web-dev-qa-db-fra.com

obtenir des métaboxes enregistrés par type ou identifiant de poste

Je recherche une méthode pour obtenir les métaboxes enregistrées pour un type de publication spécifique (ou ID de publication) lorsque vous utilisez un type de publication distinct et non associé. Je travaille actuellement à la création d'un type de publication "d'édition" personnalisé pour un besoin client spécifique et je souhaite pouvoir charger les métaboxes sans que ceux-ci soient enregistrés dans ce type "modifiable".

Clarification:

Ce que j'essaie d'accomplir est l'affichage réel des métaboxes d'un autre type de message. Donc, si mon type de publication "événements" a une métabox avec la date, l'heure et le lieu, je souhaite afficher ces mêmes cases sur un type de publication distinct sans enregistrer à nouveau la métabox.

4
Norcross

Mauvaises hypothèses

@Rarst ou @ Dan-Cameron answer peut fonctionner, mais supposons que lorsque vous affichez une page de modification de type de publication, les métaboxes de l'autre type de publication sont tous enregistrés.

Cela peut arriver dans différents cas:

  • Si les métaboxes sont enregistrés à l'aide de "add_meta_boxes_{$post_type}" hook, au lieu du plus commun, générique "add_meta_boxes", ils ne seront pas disponibles dans la page d'administration pour le type de message stealer post
  • Même la publication générique hook "add_meta_boxes" pass to hooking rappelle le type de publication en cours et l’objet de publication en cours en tant qu’arguments. Cela signifie que l'enregistrement de callbacks peut utiliser ces arguments dans certaines conditions if, il est donc possible que nous nous trouvions dans la même situation que dans le point précédent

En bref, à moins que vous ne contrôliez la façon dont les métaboxes sont enregistrés pour un type de message A, vous ne pouvez pas être sûr qu'ils pourront le faire dans la page de modification d'un message de type B.

Si vous avez ce contrôle, @Rarst answer ferait l'affaire.

Au cas où vous n’auriez pas ce contrôle, la seule solution serait un peu délicate.

Dans ce cas, votre seule chance est d'envoyer une requête HTTP (AJAX ou via WP API HTTP) à la page de modification du CPT à partir duquel vous voulez voler les boîtes. Vous devez ajouter une variable de requête qui fera que la page renvoie le tableau de boîtes (probaly sérialisé ou encrypté JSON).

Après cela, vous pouvez fusionner les cases retournées avec les cases de la publication en cours et vous avez terminé.

Le fait est que ce code

  • est lent (car demande supplémentaire)
  • n'est pas trivial

Donc, si vous avez le contrôle sur toutes les boîtes enregistrées pour un type de publication, il est beaucoup plus simple et rapide de les enregistrer à nouveau pour le type de message {stealer} _.

Si vous ne savez pas exactement quelles sont toutes les cases de ce type de publication, alors la requête HTTP supplémentaire est votre seule chance.

Je vais poster ici une classe qui implémente ce workflow ( ici en tant que Gist pour une meilleure lisibilité):

class MetaboxStealer
{
    private static $stealing;

    private $post_type;
    private $boxes = [];

    /**
     * When the request contain special variable, this function will make it
     * return a serialized version of $wp_meta_boxes array and die.
     */
    public static function init()
    {
        add_filter('post_updated_messages', function ($messages) {
            if (MetaboxStealer::stealing()) {
                ob_start();
                return [];
            }
            return $messages;
        });

        add_action('do_meta_boxes', function () {
            if (MetaboxStealer::stealing()) {
                ob_end_clean();
                global $wp_meta_boxes;
                echo serialize($wp_meta_boxes);
                die();
            }
        });
    }

    /**
     * Checks that the request contain a special variable that will make request
     * return a serialized version of $wp_meta_boxes array and die.
     *
     * @return bool
     */
    public static function stealing()
    {
        if (is_null(self::$stealing)) {
            $screen = function_exists('get_current_screen') ? get_current_screen() : null;
            $stealing = filter_input(INPUT_GET, 'stealing-boxes', FILTER_SANITIZE_STRING);
            self::$stealing =
                $screen instanceof \WP_Screen
                && $stealing
                && wp_verify_nonce($stealing, $screen->post_type);
        }
        return self::$stealing;
    }

    /**
     * @param string $post_type Current post type
     */
    public function __construct($post_type)
    {
        $this->post_type = $post_type;
    }

    /**
     * Send a HTTP request to post edit page of a given CPT setting a special
     * variable that will make that page return serialized $wp_meta_boxes array.
     * After that, so obtained boxes are merged with the boxes for current post type.
     *
     * @param string $cpt CPT to steal metaboxes from
     */
    public function steal($cpt)
    {
        $vars = [
            'post_type'      => $cpt,
            'stealing-boxes' => wp_create_nonce($cpt),
        ];
        $url = add_query_arg($vars, admin_url('/post-new.php'));
        $cookies = [];
        foreach ($_COOKIE as $name => $value) {
            if ('PHPSESSID' !== strtoupper($name)) {
                $cookies[] = new \WP_Http_Cookie([
                    'name'  => $name,
                    'value' => $value,
                ]);
            }
        }
        $response = wp_remote_get($url, ['cookies' => $cookies]);
        if (! is_wp_error($response)) {
            $body = wp_remote_retrieve_body($response);
            if (is_serialized($body)) {
                $boxes = unserialize($body);
                $this->boxes = isset($boxes[$cpt]) ? $boxes[$cpt] : [];
                empty($this->boxes) or $this->merge();
            }
        }
    }

    /**
     * Merge metaboxes for current post type with boxes obtained with 
     * a HTTP request to another CPT post edit page.
     */
    private function merge()
    {
        global $wp_meta_boxes;
        isset($wp_meta_boxes[$this->post_type]) or $wp_meta_boxes[$this->post_type] = [];
        foreach ($this->boxes as $context => $priorities) {
            foreach ($priorities as $priority => $boxes) {
                if (! isset($wp_meta_boxes[$this->post_type][$context])) {
                    $wp_meta_boxes[$this->post_type][$context] = [];
                }
                if (! isset($wp_meta_boxes[$this->post_type][$context][$priority])) {
                    $wp_meta_boxes[$this->post_type][$context][$priority] = [];
                }
                $wp_meta_boxes[$this->post_type][$context][$priority] = array_merge(
                    $wp_meta_boxes[$this->post_type][$context][$priority],
                    $boxes
                );
            }
        }
    }
}

Remarques:

  • la méthode merge() est fortement dérivée de la réponse Rarst
  • J'ai utilisé WP API HTTP pour envoyer une demande supplémentaire afin de tout conserver au même endroit, mais une implémentation de AJAX serait plus agréable

Comment utiliser

Assez simple.

// init the class
add_action('admin_init', ['MetaboxStealer', 'init']);

// use the class to merge boxes for current CPT with boxes for another CPT
add_action('edit_form_after_editor', function ($post) {
    $stealer_cpt = 'stealer-cpt';
    $steal_from = 'events';
    if ($post->post_type === $stealer_cpt) {
        $stealer = new MetaboxStealer($post->post_type);
        $stealer->steal($steal_from);
        // note that you can steal from different CPTs
        // $stealer->steal($another_steal_from);
    }
});

La question de l'épargne

Quelle que soit la façon dont vous parvenez à afficher les boîtes d’un CPT dans la page de post-édition d’un autre CPT, il est possible que la routine de sauvegarde vérifie le type de post avant d’enregistrer les méta-post en elle.

Dans ce cas, les métaboxes de l'autre CPT, même s'ils sont affichés, ne seront pas sauvegardés et vous devrez probablement écrire une autre routine de sauvegarde si vous n'avez pas accès à celle d'origine.

3
gmazzap

Créez une metabox supplémentaire (case à cocher, je pense) qui offre la possibilité de créer un type de message "modifier" à partir de ce message. Dans l'intérêt de ce code, nous l'appellerons 'transfer_data'.

Remplacez la valeur de $ post_type par le type de publication de la publication d'origine à partir de laquelle nous allons récupérer les données.

J'ai ajouté la fonctionnalité permettant d'enregistrer l'ID de publication d'origine dans une méta-valeur pour le type de publication "modifier". Afin de sauvegarder les métaboxes "edit" dans la publication d'origine, vous devriez pouvoir utiliser cette valeur et cette fonction (avec update_post_meta(), bien sûr) pour procéder au reverse engineering d'une autre fonction à laquelle se connecter.

function create_edit_page($data){
    $post_type = 'the_post_type_to_work_with';
    // Grab this post's ID
    $orig_id = $_POST['post_ID'];
    // Grab the value of the 'transfer_data' field
    $is_transfer_val = get_post_meta( $orig_id, 'transfer_data');
    if($data['post_type'] == $post_type && $is_transfer_val == TRUE && 
            isset($data['guid']) && strlen($data['guid'])>0 ){

        $post_id = wp_insert_post(
          array(
            'comment_status'  => 'closed',
            'ping_status'   => 'closed',
            'post_author'   => $data['post_author'],
            'post_name'   => $slug,
                'post_content'  =>  $data['post_content'],
            'post_title'    => $data['post_title'],
            'post_status'   => 'publish',
                // The custom post type 'editing'
            'post_type'   => 'editing'
          )
        );

        // create the meta fields
        $all_meta_boxes = get_post_meta( $orig_id );
        if(isset( $all_meta_boxes ) && is_array( $all_meta_boxes )){
            foreach($all_meta_boxes as $metakey => $metavalue){
                add_post_meta($post_id, $metakey, $metavalue);
            }
        }
        // add a meta field that points to original post (for editing purposes, etc.)
        add_post_meta($post_id, 'original_post_id', $orig_id);

        // If you want to redirect the user after saving use the filter below
        // add_filter('redirect_post_location', 'my_post_redirect_filter', '99');

        return $data;
    }
    return $data;
}
add_action( 'wp_insert_post', 'create_edit_page', '99' );
// Or, call BEFORE updating the database with below action
//add_action( 'wp_insert_post_data', 'create_edit_page', '99' );
0
Mickey

Je ne ferai aucune réclamation, elle est fiable, mais c'est à peu près tout ce que j'ai pu faire:

add_action( 'add_meta_boxes', function () {
    global $wp_meta_boxes;

    foreach ( $wp_meta_boxes['steal-from'] as $context => $priorities ) {

        foreach ( $priorities as $priority => $boxes ) {

            if ( ! isset( $wp_meta_boxes['metabox-stealer'][ $context ] ) ) {
                $wp_meta_boxes['metabox-stealer'][ $context ] = [ ];
            }

            if ( ! isset( $wp_meta_boxes['metabox-stealer'][ $context ][ $priority ] ) ) {
                $wp_meta_boxes['metabox-stealer'][ $context ][ $priority ] = [ ];
            }

            $wp_meta_boxes['metabox-stealer'][ $context ][ $priority ] = array_merge(
                $wp_meta_boxes['metabox-stealer'][ $context ][ $priority ],
                $boxes
            );
        }
    }
}, 11 );

steal-from et metabox-stealer sont des types de publication à utiliser.

0
Rarst

Cela insérera les métaboxes manuellement dans des emplacements suffisamment proches de ceux où les méta-boîtes doivent être placées. Le problème est qu'ils seront insérés avant les boîtes aux lettres standard pour ce type de publication.

Je suis sûr que vous pourriez en pirater davantage si nécessaire, mais cela vous donne au moins les bases de ce dont vous avez besoin.

function monkey_advanced_meta_boxes() {
    $post = get_post( 2457 );
    do_meta_boxes( 'post_type_to_take_mboxes_from', 'normal', $post );
    do_meta_boxes( 'post_type_to_take_mboxes_from', 'advanced', $post );
}
add_action( 'edit_form_advanced', 'monkey_advanced_meta_boxes' );

function monkey_sidebar_meta_boxes() {
    $post = get_post( 2457 );
    do_meta_boxes( 'post_type_to_take_mboxes_from', 'side', $post );
}
add_action( 'submitpost_box', 'monkey_sidebar_meta_boxes' );

Bien sûr, vous devrez vérifier screen-> id et vous assurer que vous ajoutez les mbox à l'écran correct. Cela ne va pas non plus enregistrer les méta-boîtes de la même manière, donc des choses comme les supprimer des options d’écran ne fonctionneront pas.


MISE À JOUR: Après avoir réfléchi un peu à cela, il semblerait que ce ne soit pas aussi facile que je l'ai décrit ci-dessus. Je suppose que vous voudrez que les méta-boîtes sauvegardent la méta, ce que vous devrez peut-être faire manuellement si l'action de sauvegarde d'origine vérifie le post_type (ce qui devrait être le cas) et que la méta-boîte risque de ne pas être compatible.

Je pense qu'il doit y avoir un moyen de contourner cela, comme je l'avais mentionné à l'origine avec la modification de la variable globale. ????

0
Dan Cameron