Verhindert, dass Posts veröffentlicht werden, wenn benutzerdefinierte Felder nicht ausgefüllt sind

17

Ich habe einen benutzerdefinierten Post-Typ Event, der ein benutzerdefiniertes Start- und Enddatum / -uhrzeitfeld enthält (als Metaboxen im Post-Bearbeitungsbildschirm).

Ich möchte sicherstellen, dass ein Event nicht veröffentlicht (oder geplant) werden kann, ohne dass die Daten ausgefüllt sind, da dies Probleme mit den Vorlagen zur Anzeige der Eventdaten verursacht (abgesehen davon, dass dies eine notwendige Voraussetzung ist!). Ich würde jedoch gerne Entwürfe für Veranstaltungen haben, die kein gültiges Datum enthalten, während sie in Vorbereitung sind.

Ich habe überlegt save_post, ob ich die Überprüfung durchführen soll, aber wie kann ich verhindern, dass der Status geändert wird?

EDIT1: Dies ist der Hook, mit dem ich jetzt das post_meta speichere.

// Save the Metabox Data
function ep_eventposts_save_meta( $post_id, $post ) {

if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
    return;

if ( !isset( $_POST['ep_eventposts_nonce'] ) )
    return;

if ( !wp_verify_nonce( $_POST['ep_eventposts_nonce'], plugin_basename( __FILE__ ) ) )
    return;

// Is the user allowed to edit the post or page?
if ( !current_user_can( 'edit_post', $post->ID ) )
    return;

// OK, we're authenticated: we need to find and save the data
// We'll put it into an array to make it easier to loop though

//debug
//print_r($_POST);

$metabox_ids = array( '_start', '_end' );

foreach ($metabox_ids as $key ) {
    $events_meta[$key . '_date'] = $_POST[$key . '_date'];
    $events_meta[$key . '_time'] = $_POST[$key . '_time'];
    $events_meta[$key . '_timestamp'] = $events_meta[$key . '_date'] . ' ' . $events_meta[$key . '_time'];
}

$events_meta['_location'] = $_POST['_location'];

if (array_key_exists('_end_timestamp', $_POST))
    $events_meta['_all_day'] = $_POST['_all_day'];

// Add values of $events_meta as custom fields

foreach ( $events_meta as $key => $value ) { // Cycle through the $events_meta array!
    if ( $post->post_type == 'revision' ) return; // Don't store custom data twice
    $value = implode( ',', (array)$value ); // If $value is an array, make it a CSV (unlikely)
    if ( get_post_meta( $post->ID, $key, FALSE ) ) { // If the custom field already has a value
        update_post_meta( $post->ID, $key, $value );
    } else { // If the custom field doesn't have a value
        add_post_meta( $post->ID, $key, $value );
    }
    if ( !$value ) 
                delete_post_meta( $post->ID, $key ); // Delete if blank
}

}

add_action( 'save_post', 'ep_eventposts_save_meta', 1, 2 );

EDIT2: und das ist, was ich versuche, um die Post-Daten nach dem Speichern in der Datenbank zu überprüfen.

add_action( 'save_post', 'ep_eventposts_check_meta', 99, 2 );
function ep_eventposts_check_meta( $post_id, $post ) {
//check that metadata is complete when a post is published
//print_r($_POST);

if ( $_POST['post_status'] == 'publish' ) {

    $custom = get_post_custom($post_id);

    //make sure both dates are filled
    if ( !array_key_exists('_start_timestamp', $custom ) || !array_key_exists('_end_timestamp', $custom )) {
        $post->post_status = 'draft';
        wp_update_post($post);

    }
    //make sure start < end
    elseif ( $custom['_start_timestamp'] > $custom['_end_timestamp'] ) {
        $post->post_status = 'draft';
        wp_update_post($post);
    }
    else {
        return;
    }
}
}

Das Hauptproblem dabei ist ein Problem, das tatsächlich in einer anderen Frage beschrieben wurde : Die Verwendung wp_update_post()eines save_postHakens löst eine Endlosschleife aus.

EDIT3: dachte ich einen Weg , es zu tun, durch Einhaken wp_insert_post_datastatt save_post. Das einzige Problem ist, dass jetzt die post_statuszurückgesetzt wird, aber jetzt eine irreführende Meldung mit der Aufschrift "Beitrag veröffentlicht" angezeigt wird (durch Hinzufügen &message=6zur umgeleiteten URL), der Status jedoch auf "Entwurf" gesetzt wird.

add_filter( 'wp_insert_post_data', 'ep_eventposts_check_meta', 99, 2 );
function ep_eventposts_check_meta( $data, $postarr ) {
//check that metadata is complete when a post is published, otherwise revert to draft
if ( $data['post_type'] != 'event' ) {
    return $data;
}
if ( $postarr['post_status'] == 'publish' ) {
    $custom = get_post_custom($postarr['ID']);

    //make sure both dates are filled
    if ( !array_key_exists('_start_timestamp', $custom ) || !array_key_exists('_end_timestamp', $custom )) {
        $data['post_status'] = 'draft';
    }
    //make sure start < end
    elseif ( $custom['_start_timestamp'] > $custom['_end_timestamp'] ) {
        $data['post_status'] = 'draft';
    }
    //everything fine!
    else {
        return $data;
    }
}

return $data;
}
englebip
quelle

Antworten:

16

Wie m0r7if3r betonte, gibt es keine Möglichkeit zu verhindern, dass ein Beitrag mit dem save_postHook veröffentlicht wird, da der Beitrag zum Zeitpunkt, zu dem der Hook ausgelöst wird, bereits gespeichert ist. Mit dem folgenden Befehl können Sie den Status jedoch wiederherstellen, wp_insert_post_dataohne eine Endlosschleife auszulösen.

Folgendes wird nicht getestet, sollte aber funktionieren.

<?php
add_action('save_post', 'my_save_post');
function my_save_post($post_id) {
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
         return;

    if ( !isset( $_POST['ep_eventposts_nonce'] ) )
         return;

    if ( !wp_verify_nonce( $_POST['ep_eventposts_nonce'], plugin_basename( __FILE__ ) ) )
         return;

    // Is the user allowed to edit the post or page?
     if ( !current_user_can( 'edit_post', $post->ID ) )
         return;

   // Now perform checks to validate your data. 
   // Note custom fields (different from data in custom metaboxes!) 
   // will already have been saved.
    $prevent_publish= false;//Set to true if data was invalid.
    if ($prevent_publish) {
        // unhook this function to prevent indefinite loop
        remove_action('save_post', 'my_save_post');

        // update the post to change post status
        wp_update_post(array('ID' => $post_id, 'post_status' => 'draft'));

        // re-hook this function again
        add_action('save_post', 'my_save_post');
    }
}
?>

Ich habe nicht überprüft, aber im Code wird in der Feedback-Nachricht die falsche Meldung angezeigt, dass der Beitrag veröffentlicht wurde. Dies liegt daran, dass WordPress uns zu einer URL weiterleitet, bei der die messageVariable jetzt falsch ist.

Um es zu ändern, können wir den redirect_post_locationFilter verwenden:

add_filter('redirect_post_location','my_redirect_location',10,2);
function my_redirect_location($location,$post_id){
    //If post was published...
    if (isset($_POST['publish'])){
        //obtain current post status
        $status = get_post_status( $post_id );

        //The post was 'published', but if it is still a draft, display draft message (10).
        if($status=='draft')
            $location = add_query_arg('message', 10, $location);
    }

    return $location;
}

Um den obigen Umleitungsfilter zusammenzufassen: Wenn ein Beitrag veröffentlicht werden soll, aber noch ein Entwurf ist, ändern wir die Nachricht entsprechend (was ist message=10). Auch dies ist ungetestet, sollte aber funktionieren. Der Codex von add_query_argschlägt vor, dass, wenn eine Variable bereits gesetzt ist, die Funktion diese ersetzt (aber wie gesagt, ich habe dies nicht getestet).

Stephen Harris
quelle
Anders als die Vermissten; In Ihrer Zeile add_query_arg ist dieser Filtertrick redirect_post_location genau das, was ich brauchte. Vielen Dank!
MadtownLems
@MadtownLems behoben :)
Stephen Harris
9

OK, so habe ich es endlich gemacht: ein Ajax-Aufruf einer PHP-Funktion, die die Prüfung durchführt, die irgendwie von dieser Antwort inspiriert ist und einen cleveren Tipp aus einer Frage verwendet, die ich bei StackOverflow gestellt habe . Wichtig ist, ich stelle sicher, dass nur dann, wenn wir die Prüfung veröffentlichen möchten, erfolgt, damit ein Entwurf immer ohne die Prüfung gespeichert werden kann. Dies war letztendlich die einfachere Lösung, um die Veröffentlichung des Beitrags tatsächlich zu verhindern . Es könnte jemand anderem helfen, also habe ich es hier geschrieben.

Fügen Sie zunächst das erforderliche Javascript hinzu:

//AJAX to validate event before publishing
//adapted from /wordpress/15546/dont-publish-custom-post-type-post-if-a-meta-data-field-isnt-valid
add_action('admin_enqueue_scripts-post.php', 'ep_load_jquery_js');   
add_action('admin_enqueue_scripts-post-new.php', 'ep_load_jquery_js');   
function ep_load_jquery_js(){
global $post;
if ( $post->post_type == 'event' ) {
    wp_enqueue_script('jquery');
}
}

add_action('admin_head-post.php','ep_publish_admin_hook');
add_action('admin_head-post-new.php','ep_publish_admin_hook');
function ep_publish_admin_hook(){
global $post;
if ( is_admin() && $post->post_type == 'event' ){
    ?>
    <script language="javascript" type="text/javascript">
        jQuery(document).ready(function() {
            jQuery('#publish').click(function() {
                if(jQuery(this).data("valid")) {
                    return true;
                }
                var form_data = jQuery('#post').serializeArray();
                var data = {
                    action: 'ep_pre_submit_validation',
                    security: '<?php echo wp_create_nonce( 'pre_publish_validation' ); ?>',
                    form_data: jQuery.param(form_data),
                };
                jQuery.post(ajaxurl, data, function(response) {
                    if (response.indexOf('true') > -1 || response == true) {
                        jQuery("#post").data("valid", true).submit();
                    } else {
                        alert("Error: " + response);
                        jQuery("#post").data("valid", false);

                    }
                    //hide loading icon, return Publish button to normal
                    jQuery('#ajax-loading').hide();
                    jQuery('#publish').removeClass('button-primary-disabled');
                    jQuery('#save-post').removeClass('button-disabled');
                });
                return false;
            });
        });
    </script>
    <?php
}
}

Dann die Funktion, die die Prüfung übernimmt:

add_action('wp_ajax_ep_pre_submit_validation', 'ep_pre_submit_validation');
function ep_pre_submit_validation() {
//simple Security check
check_ajax_referer( 'pre_publish_validation', 'security' );

//convert the string of data received to an array
//from /wordpress//a/26536/10406
parse_str( $_POST['form_data'], $vars );

//check that are actually trying to publish a post
if ( $vars['post_status'] == 'publish' || 
    (isset( $vars['original_publish'] ) && 
     in_array( $vars['original_publish'], array('Publish', 'Schedule', 'Update') ) ) ) {
    if ( empty( $vars['_start_date'] ) || empty( $vars['_end_date'] ) ) {
        _e('Both Start and End date need to be filled');
        die();
    }
    //make sure start < end
    elseif ( $vars['_start_date'] > $vars['_end_date'] ) {
        _e('Start date cannot be after End date');
        die();
    }
    //check time is also inputted in case of a non-all-day event
    elseif ( !isset($vars['_all_day'] ) ) {
        if ( empty($vars['_start_time'] ) || empty( $vars['_end_time'] ) ) {
            _e('Both Start time and End time need to be specified if the event is not an all-day event');
            die();              
        }
        elseif ( strtotime( $vars['_start_date']. ' ' .$vars['_start_time'] ) > strtotime( $vars['_end_date']. ' ' .$vars['_end_time'] ) ) {
            _e('Start date/time cannot be after End date/time');
            die();
        }
    }
}

//everything ok, allow submission
echo 'true';
die();
}

Diese Funktion gibt zurück, trueob alles in Ordnung ist, und sendet das Formular, um den Beitrag auf dem normalen Kanal zu veröffentlichen. Andernfalls gibt die Funktion eine Fehlermeldung zurück, die als angezeigt wird alert(), und das Formular wird nicht gesendet.

englebip
quelle
Ich bin dem gleichen Ansatz gefolgt und habe den Beitrag als "Entwurf" anstelle von "Veröffentlichen" gespeichert, wenn die Validierungsfunktion true zurückgibt. Sie wissen nicht, wie Sie das beheben können !!! <br/> Erhalten Sie während des Ajax-Aufrufs auch nicht die Daten für ein Textfeld (z. B. post_content, ein anderes benutzerdefiniertes Textfeld)?
Mahmudur
1
Ich habe diese Lösung etwas anders angewendet: Bei Erfolg habe ich zunächst den folgenden Code im Javascript verwendet: delayed_autosave(); //get data from textarea/tinymce field jQuery('#publish').data("valid", true).trigger('click'); //publish postVielen Dank.
Mahmudur,
3

Ich denke, dass der beste Weg, dies zu tun, nicht darin besteht, die Statusänderung so sehr zu verhindern, wie dies der Fall ist, um sie rückgängig zu machen, wenn dies der Fall ist. Zum Beispiel: Sie haken save_postmit einer wirklich hohen Priorität (so dass der Haken sehr spät ausgelöst wird, nämlich nachdem Sie Ihre Meta-Einfügung durchgeführt haben), überprüfen dann post_statusden gerade gespeicherten Beitrag und aktualisieren ihn auf ausstehend (oder Entwurf oder Was auch immer), wenn es nicht Ihren Kriterien entspricht.

Eine alternative Strategie besteht darin, den Haken wp_insert_post_datazu setzen, um den post_status direkt zu setzen. Der Nachteil dieser Methode ist meines Erachtens, dass Sie das Postmeta noch nicht in die Datenbank eingefügt haben, sodass Sie es usw. vor Ort verarbeiten müssen, um Ihre Überprüfungen durchzuführen, und es dann erneut verarbeiten müssen, um es einzufügen es in die Datenbank ... die entweder in Leistung oder in Code eine Menge Aufwand werden könnte.

mor7ifer
quelle
Ich bin gerade save_postmit Priorität 1 verbunden, um die Metafelder aus den Metaboxen zu speichern. was Sie dann vorschlagen, ist, einen zweiten Haken save_postmit Priorität zu haben, sagen wir 99? Würde dies die Integrität sicherstellen? Was passiert, wenn aus irgendeinem Grund der erste Hook ausgelöst wird, die Metadaten eingefügt und der Beitrag veröffentlicht werden, der zweite Hook jedoch nicht, sodass Sie ungültige Felder erhalten?
12.
Ich kann mir keine Situation vorstellen, in der der erste Haken ausgelöst würde, aber nicht der zweite. Was für ein Szenario könnte das bewirken? Wenn Sie sich darüber Sorgen machen, können Sie das Post-Meta einfügen, das Post-Meta überprüfen und dann die post_statusAll-in-One-Funktion, die von einem einzelnen Anruf ausgeht, auf einen Hook aktualisieren, wenn Sie dies vorziehen.
Mor7ifer
Ich habe meinen Code als Bearbeitung für meine Frage veröffentlicht. Ich habe versucht, einen zweiten Haken zu verwenden, save_postaber das löst eine Endlosschleife aus.
Englebip
Ihr Problem ist, dass Sie den erstellten Beitrag überprüfen sollten. Dies if( get_post_status( $post_id ) == 'publish' )ist auch das, was Sie verwenden möchten, da Sie die Daten in neu definieren $wpdb->postsund nicht die Daten in $_POST[].
Mor7ifer
0

Die beste Methode könnte JAVASCRIPT sein:

<script type="text/javascript">
var field_id =  "My_field_div__ID";    // <----------------- CHANGE THIS

var SubmitButton = document.getElementById("save-post") || false;
var PublishButton = document.getElementById("publish")  || false; 
if (SubmitButton)   {SubmitButton.addEventListener("click", SubmCLICKED, false);}
if (PublishButton)  {PublishButton.addEventListener("click", SubmCLICKED, false);}
function SubmCLICKED(e){   
  var passed= false;
  if(!document.getElementById(field_id)) { alert("I cant find that field ID !!"); }
  else {
      var Enabled_Disabled= document.getElementById(field_id).value;
      if (Enabled_Disabled == "" ) { alert("Field is Empty");   }  else{passed=true;}
  }
  if (!passed) { e.preventDefault();  return false;  }
}
</script>
T.Todua
quelle
-1

Tut mir leid, dass ich Ihnen keine klare Antwort geben kann, aber ich erinnere mich, dass ich in letzter Zeit etwas Ähnliches gemacht habe. Ich kann mich nur nicht genau erinnern, wie. Ich denke , dass ich es vielleicht hätte um über Art und Weise - so etwas wie ich es ein Default - Wert hatte und wenn die Person es nicht geändert hatte nahm ich das so in einer if - Anweisung nach oben -> if(category==default category) {echo "You didn't pick a category!"; return them to the post creation page; }leider ist dies nicht eine gerade nach oben Antwort , aber die Hoffnung es hilft ein bisschen.

MIINIIM
quelle