web-dev-qa-db-fra.com

Wordpress 4.4+: Comment réviser les métadonnées CPT +

Dans WordPress 4.4+, comment activer les révisions pour les types de publication personnalisés avec leurs métadonnées? Comment pouvez-vous faire en sorte que ces informations apparaissent dans la vue diff?

J'ai tenté d'implémenter ce qui était décrit dans les articles de blog suivants, mais le résultat a été que mon implémentation a échoué en silence:

http://carolandrews.co.uk/saving-revisions-for-custom-post-type-meta-data/https://johnblackbourn.com/post-meta-revisions-wordpress

Le plug-in WP-Post-Meta-Revisions ne gère pas le cas de l'écran de révision et ne semble fonctionner que partiellement. Il est répertorié comme compatible jusqu'à la version 4.3.3, donc je ne peux pas être trop surpris, je suppose. Le ticket WordPress associé (20564) n’a pas non plus été mis à jour depuis 6 mois.

Toutes les solutions ci-dessus sont-elles pourries ou est-ce une erreur de ma part?

Ci-dessous ma tentative:


Mes types de publication personnalisés sont enregistrés avec supports => array('revisions') et fonctionnent comme prévu pour les champs WordPress natifs.

J'utilise une approche orientée objet pour le développement de mon plugin/thème et une classe abstraite représentant mes types de publication personnalisés. Chaque sous-classe représente un CPT spécifique.

Les parties pertinentes sont les suivantes:

abstract class Foo_Post {
//...

    public function __construct(){
        $post_type = $this->get_post_type();
        add_action('init', array($this,'init'));
        //...
        add_filter('_wp_post_revision_fields', array($this, 'extend_revision_screen_keys'),10,1);
        add_action('wp_restore_post_revision', array('restore_revision', 10, 2 ) );
        add_action('admin_head', array($this,'render_revision_fields') );
    }

    public function init() {
        $post_type = $this->get_post_type();
        //...
        add_action("save_post_$post_type", array($this,'save_post'), 10, 2);
    }

    public function extend_revision_screen_keys($fields) {
        foreach($this->fields() as $field_name => $field_value) {
            if (!isset($field_value['field_type']) || Foo_Post::is_native_field($field_value))
                continue;
            $fields[$field_name] = $field_name;
        }
        return $fields;
    }

    public function render_revision_fields() {
        foreach($this->fields() as $field_name=>$field_value){
            if (!isset($field_value['field_type']) || Foo_Post::is_native_field($field_value))
                continue;
            add_filter( '_wp_post_revision_field_'.$field_name, array($this,'render_revision_field'), 10, 4 );
        }
    }

    public function render_revision_field($value, $field_name, $post, $context) {
        foreach($this->fields() as $field_key => $field_value) {
            if (!isset($field_value['field_type']) || Foo_Post::is_native_field($field_value) || $field_key != $field_name)
                continue;

            switch($field_value['field_type']) {
                case Foo_FieldType::checkbox :
                    $value = get_post_meta(get_the_ID(),$field_key,true);
                break;
                case Foo_FieldType::date :
                    $value = get_post_meta(get_the_ID(),$field_key,true);
                break;
                case Foo_FieldType::email :
                    $value = get_post_meta(get_the_ID(),$field_key,true);
                break;
                case Foo_FieldType::number :
                    $value = get_post_meta(get_the_ID(),$field_key,true);
                break;
                case Foo_FieldType::multi_text :
                    $value = get_post_meta(get_the_ID(),$field_key,true);
                break;
                //native types don't need to be rendered as it is managed by wordpress
                case Foo_FieldType::rel_many :
                    $value = get_post_meta(get_the_ID(),$field_key,true);
                    $value = join(',',$value);
                break;
                case Foo_FieldType::rel_single :
                    $value = get_post_meta(get_the_ID(),$field_key,true);
                    $value = join(',',$value);
                break;
                case Foo_FieldType::rich_text :
                    $value = get_post_meta(get_the_ID(),$field_key,true);
                break;
                case Foo_FieldType::select :
                    $value = get_post_meta(get_the_ID(),$field_key,true);
                    $value = join(',',$value);
                break;
                case Foo_FieldType::single_text :
                    $value = get_post_meta(get_the_ID(),$field_key,true);
                break;
                case Foo_FieldType::tel :
                    $value = get_post_meta(get_the_ID(),$field_key,true);
                break;
                case Foo_FieldType::url :
                    $value = get_post_meta(get_the_ID(),$field_key,true);
                break;
            }
        }
        return $value;
    }

    public function restore_revision($post_id, $revision_id){
        $revision = get_post( $revision_id );
        $meta     = get_post_meta( $post_id );

        foreach($meta as $field_key => $field_value) {
            //only want the metadata fields I've defined. (prefixed with _foo_)
            $pos = strpos($field_key,'_foo_');
            if($pos !== false && $pos == 0) {
                $meta = get_metadata( 'post', $revision->ID, $field_key, true );
                if ( false !== $meta )
                    update_post_meta( $post_id, $field_key, $meta );
                //TODO: Why the following?
                //else
                //    delete_post_meta( $post_id, 'my_meta' );
            }
        }
    }

    public function save_post($post_id, $post) {
        if(!$this->can_save($post_id))
            return;

        $parent_id = wp_is_post_revision($post_id);
        $revision  = $parent_id ? get_post( $parent_id ) : null;

        foreach($this->fields() as $field_key => $field_value) {
            $value = NULL;
            switch($field_value['field_type']) {
                case Foo_FieldType::checkbox :
                    $value = isset($_POST[$field_key]) ? ($_POST[$field_key] == 'true' ? 'checked' : '') : false;
                break;
                case Foo_FieldType::date :
                    $value = isset($_POST[$field_key]) ? trim($_POST[$field_key]) : date('Y-m-d H:i:s');
                break;
                case Foo_FieldType::email :
                    $value = isset($_POST[$field_key]) ? trim($_POST[$field_key]) : '';
                break;
                case Foo_FieldType::multi_text :
                    $value = isset($_POST[$field_key]) ? trim($_POST[$field_key]) : '';
                break;
                case Foo_FieldType::number :
                    $value = isset($_POST[$field_key]) ? trim($_POST[$field_key]) : '0';
                break;
                //skipping native type as it is managed by wordpress
                case Foo_FieldType::rel_many :
                    $value = isset($_POST[$field_key]) ? array_map('intval', (array) $_POST[$field_key]) : array();
                break;
                case Foo_FieldType::rel_single :
                    $value = isset($_POST[$field_key]) ? $_POST[$field_key] : -1;
                break;
                case Foo_FieldType::rich_text :
                    $value = isset($_POST[$field_key]) ? trim($_POST[$field_key]) : '';
                break;
                case Foo_FieldType::select :
                    $value = isset($_POST[$field_key]) ? $_POST[$field_key] : '';
                break;
                case Foo_FieldType::single_text :
                    $value = isset($_POST[$field_key]) ? trim($_POST[$field_key]) : '';
                break;
                case Foo_FieldType::tel :
                    $value = isset($_POST[$field_key]) ? trim($_POST[$field_key]) : '';
                break;
                case Foo_FieldType::url :
                    $value = isset($_POST[$field_key]) ? trim($_POST[$field_key]) : '';
                break;
            }

            if(!is_null($value))
                $this->save_post_field_value($post_id, $revision, $field_key, $value);
        }
    }

    //...
}

Y a-t-il quelque chose d'évident que j'ai négligé, ou existe-t-il enfin une approche officielle pour mettre en œuvre une telle chose? La documentation à ce sujet semble au mieux clairsemée. Tous les commentaires sont bien sûr appréciés.


Modifier

Il semble que add_filter( "_wp_post_revision_field_$field_name", array($this,'render_revision_field'), 10, 4 ); n'a pas pu être enregistré. Ainsi, lorsque le fichier includes/revision.php de wodpress tente de l'appliquer, il n'existe pas.

Il semble également que add_filter('_wp_post_revision_fields', array($this, 'extend_revision_screen_keys'), 10, 1); ne soit pas ajouté.

Ma classe ci-dessus est incluse et instanciée dans le fichier racine index.php du plugin.

Peut-être que le problème est l'ordre des événements?


Éditer 2 (2016-03-31)

Pour faire un suivi sur la preuve de concept de @ialocin :

En ce qui concerne la question des échecs en silence, il semble que le problème était que la suggestion du premier lien était incorrecte. Il a ajouté le filtre render_revision_fields APRES le traitement du fichier revision.php.

J'ai déplacé cet enregistrement sur admin_init et il semble être correctement pris en charge maintenant.

global $revision correctif: pris en compte.

_wp_post_revision_field_foo de 3 paramètres contre 4: acquitté

get_the_ID(): Bonne prise. Une victime de copier-coller

Ce que je ne comprends pas maintenant, c’est la justification de sauvegarde/restauration. Avant, quand je voulais sauvegarder meta_data sur un post, je faisais ce qui suit:

update_post_meta($post_id,$field_key,$field_value)

IIUC: Maintenant, avec la version de révision, la publication souhaitée est le parent de la révision, mais que se passe-t-il si wp_is_post_revision( $post_id ) échoue? Qu'est-ce que ça veut dire? Est-ce que cela signifie qu'il n'y a pas de révision actuellement et que je devrais mettre à jour $ post dans la branche else manquante de votre exemple? Cela ne ferait pas automatiquement une nouvelle révision, n'est-ce pas?

Pourquoi le add_metadata/get_metadata serait-il associé à 'post' au lieu de la valeur CPT (par exemple, 'product')? Ou devrait-il dans ce cas? Les révisions sont-elles toujours stockées sous le type "post" et juste associées au parent du CPT souhaité?

4
mlhaufe

Je ne suis pas sûr à cent pour cent de ce qui vous manque, mais je peux vous dire que cela devrait généralement fonctionner. La preuve suit sous troisièmement. Avant d’avoir quelques remarques à vous faire:

Tout d’abord, j’imagine que cela n’échoue pas en silence, mais lors d’un processus AJAX. Utilisez donc un outil comme FireBug pour examiner cela de plus près.

Deuxièmement, je suis à peu près sûr que get_the_ID() - tel qu'il est utilisé dans votre code - vous donnera l'ID de publication de parent, ce qui n'est certainement pas ce que vous voulez. Si je le vois correctement, vous pouvez alors remplacer toutes les occurrences par $post->ID - ce qui est assez facile à changer.

Troisièmement, alors que vous suivez plus ou moins le "John Blackbourn" -approach , j’ai pris son exemple de code comme plugin pour le regarder de plus près. Moins de travail de nettoyage à faire que de travailler avec votre code. Quoi qu'il en soit, vous voyez ci-dessous le code avec les petites modifications nécessaires, voir les commentaires pour plus de détails, pour que cela fonctionne.

/*
Plugin Name: Post Meta Revisions
Description: Revisions for the 'foo' post meta field
Version:     http://wordpress.stackexchange.com/questions/221946
Author:      John Blackbourn
Plugin URI:  http://lud.icro.us/post-meta-revisions-wordpress
*/

function pmr_fields( $fields ) {
    $fields['foo'] = 'Foo';
    return $fields;
}

// global $revision doesn't work, using third parameter $post instead
function pmr_field( $value, $field, $post ) {
    return get_metadata( 'post', $post->ID, $field, true );
}

function pmr_restore_revision( $post_id, $revision_id ) {
    $post     = get_post( $post_id );
    $revision = get_post( $revision_id );
    $meta     = get_metadata( 'post', $revision->ID, 'foo', true );

    if ( false === $meta )
        delete_post_meta( $post_id, 'foo' );
    else
        update_post_meta( $post_id, 'foo', $meta );
}

function pmr_save_post( $post_id, $post ) {
    if ( $parent_id = wp_is_post_revision( $post_id ) ) {
        $parent = get_post( $parent_id );
        $meta = get_post_meta( $parent->ID, 'foo', true );

        if ( false !== $meta )
            add_metadata( 'post', $post_id, 'foo', $meta );
    }
}

// we are using three parameters
add_filter( '_wp_post_revision_field_foo', 'pmr_field', 10, 3 );
add_action( 'save_post',                   'pmr_save_post', 10, 2 );
add_action( 'wp_restore_post_revision',    'pmr_restore_revision', 10, 2 );
add_filter( '_wp_post_revision_fields',    'pmr_fields' );

Après un rapide test, il semble bien fonctionner.

7
Nicolai