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

9

Mein eigentliches Problem ist etwas komplex, deshalb 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 denen ich Informationen über ... Personen speichere.

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

Es gibt also eine Metabox zum Speichern persönlicher Informationen, eine zum Speichern von Informationen in sozialen Netzwerken und eine zum Speichern von arbeitsbezogenen Informationen, dh wenn diese Person für mich ein Kunde, 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 Personen, 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 möchte arbeitsbezogene Informationen speichern.

Wenn ich beim Bearbeiten eines Beitrags (über das Menü mit den Bildschirmoptionen ) eine nicht benötigte Metabox ausblende oder schließe, muss ich sie beim Öffnen eines anderen Beitrags dort, wo ich sie benötige, anzeigen oder erneut öffnen. Das weil Metaboxen Position / status / um auf einzelne Benutzer als Benutzer - Metadaten gespeichert werden .

Wenn Sie sich vorstellen, dass ich in einigen Posts 2 Metaboxen benötige, in einigen 10 und in einigen 5, verstehen Sie, dass dies ärgerlich ist, weil das Anzeigen / Öffnen aller Elemente den Bearbeitungsbildschirm niedrig zugänglich macht (die Bildlaufleiste scheint endlos zu sein), und manchmal sind es die Informationen, nach denen ich suche am Ende der Seite nach einer Reihe von Metaboxen ohne Info ...

Frage:

Ist es möglich, die Position / den Status / die Bestellung von Metaboxen 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.

gmazzap
quelle

Antworten:

8

Das Hauptproblem:

Das Hauptproblem hierbei ist, dass beim Schließen , Verstecken und Bestellen von Ajax-Anrufen 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_postHaken einhaken 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, Metadaten posten oder vielleicht in einer benutzerdefinierten Tabelle?

Hier speichern wir es als Benutzer-Metadaten und nehmen als Beispiel das Schließen von Post- Meta-Boxen.

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

Dann entführen wir das Abrufen von closedpostboxes_post, um es mit dem entsprechenden Metawert zu überschreiben, der auf der Benutzer-ID und der Post-ID basiert.

a) Aktualisierung während der closed-postboxesAjax-Aktion:

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

Wir können uns einbinden updated_user_meta, das wird ausgelöst, sobald die Benutzer-Metadaten für closedpostboxes_postaktualisiert 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_postHook einbinden, um die aus dem closedpostboxes_postBenutzer-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 );

Wir möchten vielleicht auch über den Fall nachdenken, in dem kein Beitrag closedpostboxes_post_{post_id}verfügbar ist. Es werden also die zuletzt gespeicherten Einstellungen von verwendet closedpostboxes_post. Vielleicht möchten Sie in diesem Standardfall alles offen oder geschlossen haben. Es wäre einfach, dieses Verhalten zu ändern.

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

Gleiches sollte für das Bestellen und Ausblenden von Metaboxen mit dem Meta metaboxhidden_{post_type}und meta-box-order_{post_data}user möglich sein.

ps: Entschuldigung für diese zu lange Antwort am Wochenende, da sie immer kurz und lustig sein sollten ;-)

Birgire
quelle
Großartig +1. N / P für lange Antwort, ich würde keine kurzen erwarten. Um ehrlich zu sein, habe ich am Wochenende keine erwartet :) Zwei Dinge, die mir sehr gut gefallen haben: 1. Die Idee, Daten pro Benutzer und pro Post zu speichern: Meine Idee war, in Post-Meta zu speichern, aber auf diese Weise alles Benutzer haben den gleichen Status. 2. die Idee, 'get_user_option_*_post'damit WP benutzerdefinierte Daten erkennt. Ich denke nur, dass ich nicht zu sehr mag, ist die Verwendung von wp_get_refererwirklich auf $_SERVERvar, die nicht wirklich zuverlässig ist, aber ich denke, ich habe eine Idee, um das "Hauptproblem" zu überwinden;)
gmazzap
Vielen Dank, ich denke, es hängt von der Anzahl der Benutzer und Beiträge ab, in denen die Daten am besten gespeichert werden. Vielleicht sollten diese Daten eine TTL haben und zB einmal im Monat gelöscht werden? Ja, ich stimme Ihnen in Bezug auf die wp_get_referer()Methode zu, deshalb habe ich sie als nicht elegante PHP-Lösung bezeichnet ;-) Ich habe zuerst darüber nachgedacht, die aktuelle Post-ID für jeden Benutzer zu speichern, aber das funktioniert nicht, wenn ein Benutzer zwei oder mehr bearbeitet Beiträge im Browser.
Freuen
Nach 43 Tagen erinnere mich eine Gegenstimme daran, dies zu beantworten. Nochmals vielen Dank für Ihre Antwort.
gmazzap
6

Wie Birgire in seiner Antwort hervorhob , verwendet WordPress AJAX, um den Status von Metaboxen zu aktualisieren, und die in der AJAX-Anfrage übergebenen Daten enthalten keine Post-ID. Dies macht es schwierig, den Box-Status pro Post zu aktualisieren.

Nachdem ich die von WordPress verwendete AJAX-Aktion gefunden hatte 'closed-postboxes', suchte ich im Ordner admin js nach dieser Zeichenfolge, um herauszufinden, wie WordPress die AJAX-Anfrage stellt.

Ich fand es postbox.jsin 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 den Klassen "Postfach" und "Geschlossen" und erstellt eine durch Kommas getrennte Liste ihrer IDs. Gleiches gilt für versteckte DOM-Elemente mit der Klasse 'postbox'.

Mein Gedanke war also: Ich kann eine gefälschte Metabox erstellen , die die richtigen Klassen hat und versteckt ist, indem ich ihre ID so einstelle, dass sie die Post-ID enthält, und auf diese Weise kann ich sie in einer AJAX-Anfrage abrufen.

Folgendes 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 immer ausgeblendet ist, sodass WordPress seine ID in $_POSTder AJAX-Anfrage als var einsendet. Sobald die gefälschte Box-ID die Post-ID auf vorhersehbare Weise enthält, kann ich den Post erkennen.

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

In admin-ajax.phpZeile 72 hakt WordPress 'wp_ajax_closed-postboxes'mit Priorität 1.

Um vor WordPress zu handeln, könnte ich dieselbe Aktion mit Priorität 0 verknüpfen.

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 get_user_option_closedpostboxes_mycptund get_user_option_metaboxhidden_mycpt(beide Varianten des get_user_option_{$option}Filters) das Erzwingen von WordPress-Ladeoptionen aus dem Post-Meta:

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 );
gmazzap
quelle
Was für eine großartige Idee mit einer versteckten Metabox mit den relevanten Informationen +1
Birgire
danke @birgire und nochmals danke für dein A, die Idee, Daten sowohl pro Benutzer als auch pro Post zu
speichern,