webentwicklung-frage-antwort-db.com.de

Wie wird der Status von geöffneten/geschlossenen und ausgeblendeten/angezeigten Metaboxen pro Post gespeichert?

Mein eigentliches Problem ist etwas komplex, daher werde ich hier versuchen, es zu abstrahieren und einfach zu halten.

Ich arbeite an einer benutzerdefinierten App, die auf WordPress basiert. Ich habe einen benutzerdefinierten Beitragstyp registriert. Nennen wir ihn "Personen", in dem ich Informationen über ... Personen speichere.

Das CPT unterstützt nur Standardfelder für Post-Titel und -Inhalte, es gibt jedoch einige Metaboxen zum Speichern von Personeninformationen (meine App als Adressbuch betrachten).

Es gibt also eine Metabox zum Speichern persönlicher Daten, eine zum Speichern sozialer Netzwerke und eine zum Speichern arbeitsbezogener Daten, d. H., Wenn diese Person für mich ein Kunde oder ein Lieferant ist, wenn wir Gutschriften oder Belastungen haben ...

Ich habe hier vereinfacht, aber es gibt eine konsistente Anzahl von Metaboxen, sagen wir 12.

Mein Problem ist, dass einige Leute, für die ich Informationen speichern möchte, nur zufällige Kontakte sind und ich nur persönliche Informationen speichern möchte, andere Freunde sind und ich persönliche Informationen und Informationen über soziale Netzwerke speichern möchte, andere Kunden oder Lieferanten sind und ich arbeitsbezogene Informationen speichern möchten.

Wenn ich beim Bearbeiten eines Beitrags (über Bildschirmoptionsmenü ) ausgeblendet oder eine nicht benötigte Metabox geschlossen habe, muss ich sie beim Öffnen eines anderen Beitrags dort anzeigen oder erneut öffnen, wo ich sie benötige. Das weil Metaboxen Position/Status/Reihenfolge auf Benutzerbasis als Benutzermetadaten gespeichert werden .

Wenn Sie sich in einigen Posts vorstellen, dass ich 2 Metaboxen benötige, in einigen 10 und in einigen 5, verstehen Sie, dass dies ärgerlich ist, weil das Anzeigen/Öffnen des Bearbeitungsbildschirms den Zugriff auf den Bildschirm einschränkt (die Bildlaufleiste scheint endlos zu sein), und manchmal sind es die Informationen, nach denen ich suche am ende der seite nach ein paar metaboxen ohne info ...

Frage:

Ist es möglich, die Position/den Status/die Bestellung der Metabox pro Post für einen bestimmten Post-Typ zu speichern?


PS: Ich weiß, dass einige js/jQuery das Problem lösen können, aber wenn möglich, würde ich Javascript-Lösungen vermeiden.

9
gmazzap

Wie von birgire in seiner Antwort hervorgehoben, verwendet WordPress AJAX, um den Status der Metaboxen zu aktualisieren, und die in der AJAX -Anforderung übergebenen Daten enthalten keine Beitrags-ID , und das macht es schwierig, den Status der Boxen nachträglich zu aktualisieren.

Nachdem ich festgestellt habe, dass die von WordPress verwendete Aktion AJAX 'closed-postboxes' ist, habe ich im Ordner admin js nach dieser Zeichenfolge gesucht, um herauszufinden, wie WordPress die Anforderung AJAX ausführt.

Ich fand es passiert auf postbox.js in Zeile # 118 .

Es sieht so aus:

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

Im Wesentlichen betrachtet WordPress DOM-Elemente mit der Klasse 'postbox' und der Klasse 'closed' und erstellt eine durch Kommas getrennte Liste ihrer IDs. Gleiches gilt für versteckte DOM-Objekte mit der Klasse 'postbox'.

Mein Gedanke war also: Ich kann eine gefälschte Metabox erstellen, die die richtigen Klassen hat und die ausgeblendet ist, und ihre ID so einstellen, dass sie die Beitrags-ID enthält. Auf diese Weise kann ich sie in einem AJAX Anfrage.

Das habe ich getan:

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

Auf diese Weise habe ich eine Metabox erstellt, die immer geschlossen und verborgen ist, sodass WordPress ihre ID als $_POST var in der AJAX -Anforderung sendet, und sobald die gefälschte Box-ID die Post-ID auf vorhersehbare Weise enthält, I bin in der lage den post zu erkennen.

Danach habe ich mir angesehen, wie WordPress die Aufgabe AJAX ausführt.

In admin-ajax.php in Zeile 72 hakt WordPress 'wp_ajax_closed-postboxes' mit Priorität 1 ein.

Um vor WordPress zu handeln, könnte ich dieselbe Aktion mit der Priorität 0 einbinden.

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

Das Speichern von Daten in einem Post-Meta ermöglichte das Filtern von get_user_option_closedpostboxes_mycpt und get_user_option_metaboxhidden_mycpt (beide Variationen des get_user_option_{$option} -Filters), um das Laden von WordPress-Optionen aus Post-Meta zu erzwingen:

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

und

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

Das Hauptproblem:

Das Hauptproblem hierbei ist, dass bei den Ajax-Anrufen Schließen, Verstecken und Bestellen keine Post-ID mit der Nutzlast gesendet wird. Hier sind zwei Beispiele für Formulardaten:

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

Wir könnten dies umgehen, indem wir einen anderen benutzerdefinierten Ajax-Aufruf verwenden.

Wir könnten uns natürlich einfach in den save_post-Hook einhängen und die Daten jedes Mal ändern, wenn der Beitrag gespeichert wird. Dies ist jedoch nicht die normale Benutzeroberfläche, daher wird dies hier nicht berücksichtigt

Es gibt eine andere nicht elegante Lösung mit PHP, die hier unten beschrieben wird:

Eine Nicht-Javascript-Lösung:

Die Frage ist, wo die Daten gespeichert werden sollen? Als Benutzer Metadaten, Beitrag Metadaten oder vielleicht in einer benutzerdefinierten Tabelle?

Hier speichern wir es als Benutzer-Metadaten und nehmen die schließend von postend Meta-Boxen als Beispiel.

Wenn der closedpostboxes_post-Metawert aktualisiert wird, speichern wir ihn auch im closedpostboxes_post_{post_id}-Metawert.

Dann hijacken wir den Abruf von closedpostboxes_post, um ihn mit dem entsprechenden Metawert basierend auf Benutzer-ID und Beitrags-ID zu überschreiben.

a) Aktualisierung während der Ajax-Aktion closed-postboxes:

Über die Funktion wp_get_referer() können wir die Beitrags-ID abrufen und dann die praktische Funktion url_to_postid() verwenden. Ich wusste zum ersten Mal von dieser "lustigen" Funktion, nachdem ich vor ein paar Monaten die Antwort von @s_ha_dum gelesen hatte ;-) Leider erkennt die Funktion ?post=123 GET Variablen nicht, aber wir können einen kleinen Trick machen, indem wir sie einfach auf ändern p=123 um es zu umgehen.

Wir können uns in updated_user_meta einklinken, der ausgelöst wird, nachdem die Benutzer-Metadaten für closedpostboxes_post aktualisiert wurden:

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) Daten abrufen:

Wir können uns in den get_user_option_closedpostboxes_post-Hook einhängen, um die aus dem closedpostboxes_post-Benutzer-Meta abgerufenen Daten zu ändern:

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

Möglicherweise möchten wir auch an den Fall denken, dass kein postbasierter closedpostboxes_post_{post_id} verfügbar ist. Es werden also die zuletzt gespeicherten Einstellungen von closedpostboxes_post verwendet. Vielleicht möchten Sie, dass in diesem Standardfall alles offen oder geschlossen ist. Es wäre einfach, dieses Verhalten zu ändern.

Für andere benutzerdefinierte Beitragstypen können wir den entsprechenden closedpostboxes_{post_type}-Hook verwenden.

Gleiches sollte für die Bestellung und Verstecken von Metaboxen mit den Benutzer-Meta metaboxhidden_{post_type} und meta-box-order_{post_data} möglich sein.

ps: entschuldigung für diese zu lange wochenende antwort, da sie immer kurz und lustig sein sollten ;-)

8
birgire