Preveniți publicarea unui articol dacă câmpurile personalizate nu sunt completate

12 feb. 2012, 01:31:44
Vizualizări: 26K
Voturi: 21

Am un tip de articol personalizat Event care conține câmpuri personalizate (metaboxes în ecranul de editare) pentru data și ora de început și sfârșit.

Aș dori să mă asigur că un Eveniment nu poate fi publicat (sau programat) fără ca datele să fie completate, deoarece acest lucru va cauza probleme cu șabloanele care afișează datele Evenimentului (pe lângă faptul că este o cerință necesară!). Cu toate acestea, aș dori să pot avea Evenimente în starea Ciornă care nu conțin o dată validă în timp ce sunt în pregătire.

Mă gândeam să folosesc hook-ul save_post pentru verificare, dar cum pot preveni schimbarea stării?

EDIT1: Acesta este hook-ul pe care îl folosesc acum pentru a salva post_meta.

// Salvează datele din Metabox
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;

// Utilizatorul are permisiunea de a edita postarea sau pagina?
if ( !current_user_can( 'edit_post', $post->ID ) )
    return;

// OK, suntem autentificați: trebuie să găsim și să salvăm datele
// Le vom pune într-un array pentru a fi mai ușor de parcurs

//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'];

// Adaugă valorile din $events_meta ca câmpuri personalizate

foreach ( $events_meta as $key => $value ) { // Parcurge array-ul $events_meta!
    if ( $post->post_type == 'revision' ) return; // Nu stoca datele personalizate de două ori
    $value = implode( ',', (array)$value ); // Dacă $value este un array, transformă-l în CSV (puțin probabil)
    if ( get_post_meta( $post->ID, $key, FALSE ) ) { // Dacă câmpul personalizat are deja o valoare
        update_post_meta( $post->ID, $key, $value );
    } else { // Dacă câmpul personalizat nu are o valoare
        add_post_meta( $post->ID, $key, $value );
    }
    if ( !$value ) 
                delete_post_meta( $post->ID, $key ); // Șterge dacă este gol
}

}

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

EDIT2: și acesta este codul pe care încerc să-l folosesc pentru a verifica datele articolului după salvarea în baza de date.

add_action( 'save_post', 'ep_eventposts_check_meta', 99, 2 );
function ep_eventposts_check_meta( $post_id, $post ) {
//verifică dacă metadatele sunt complete când un articol este publicat
//print_r($_POST);

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

    $custom = get_post_custom($post_id);

    //asigură-te că ambele date sunt completate
    if ( !array_key_exists('_start_timestamp', $custom ) || !array_key_exists('_end_timestamp', $custom )) {
        $post->post_status = 'draft';
        wp_update_post($post);

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

Principala problemă cu aceasta este o problemă care a fost de fapt descrisă în altă întrebare: folosirea wp_update_post() în cadrul unui hook save_post declanșează o buclă infinită.

EDIT3: Am găsit o modalitate de a face acest lucru, folosind hook-ul wp_insert_post_data în loc de save_post. Singura problemă este că acum post_status este resetat, dar apare un mesaj înșelător care spune "Articol publicat" (prin adăugarea &message=6 la URL-ul redirecționat), deși starea este setată la Ciornă.

add_filter( 'wp_insert_post_data', 'ep_eventposts_check_meta', 99, 2 );
function ep_eventposts_check_meta( $data, $postarr ) {
//verifică dacă metadatele sunt complete când un articol este publicat, altfel revine la ciornă
if ( $data['post_type'] != 'event' ) {
    return $data;
}
if ( $postarr['post_status'] == 'publish' ) {
    $custom = get_post_custom($postarr['ID']);

    //asigură-te că ambele date sunt completate
    if ( !array_key_exists('_start_timestamp', $custom ) || !array_key_exists('_end_timestamp', $custom )) {
        $data['post_status'] = 'draft';
    }
    //asigură-te că start < end
    elseif ( $custom['_start_timestamp'] > $custom['_end_timestamp'] ) {
        $data['post_status'] = 'draft';
    }
    //totul e în regulă!
    else {
        return $data;
    }
}

return $data;
}
0
Toate răspunsurile la întrebare 5
3
21

După cum a subliniat m0r7if3r, nu există nicio modalitate de a împiedica publicarea unui articol folosind hook-ul save_post, deoarece până când acest hook este declanșat, articolul este deja salvat. Următorul cod, totuși, vă va permite să reveniți la starea anterioară fără a utiliza wp_insert_post_data și fără a provoca o buclă infinită.

Următorul cod nu a fost testat, dar ar trebui să funcționeze.

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

    // Are utilizatorul permisiunea de a edita articolul sau pagina?
     if ( !current_user_can( 'edit_post', $post->ID ) )
         return;

   // Acum efectuați verificări pentru a valida datele. 
   // Rețineți că câmpurile personalizate (diferite de datele din metabox-uri personalizate!) 
   // vor fi deja salvate.
    $prevent_publish= false;//Setează la true dacă datele au fost invalide.
    if ($prevent_publish) {
        // dezactivează această funcție pentru a preveni o buclă infinită
        remove_action('save_post', 'my_save_post');

        // actualizează articolul pentru a schimba starea
        wp_update_post(array('ID' => $post_id, 'post_status' => 'draft'));

        // reactivează această funcție din nou
        add_action('save_post', 'my_save_post');
    }
}
?>

Nu am verificat, dar uitându-mă la cod, mesajul de feedback va afișa incorect că articolul a fost publicat. Acest lucru se întâmplă deoarece WordPress ne redirecționează către un URL unde variabila message este acum incorectă.

Pentru a o schimba, putem folosi filtrul redirect_post_location:

add_filter('redirect_post_location','my_redirect_location',10,2);
function my_redirect_location($location,$post_id){
    // Dacă articolul a fost publicat...
    if (isset($_POST['publish'])){
        // obține starea curentă a articolului
        $status = get_post_status( $post_id );

        // Articolul a fost 'publicat', dar dacă este încă un draft, afișează mesajul de draft (10).
        if($status=='draft')
            $location = add_query_arg('message', 10, $location);
    }

    return $location;
}

Pentru a rezuma filtrul de redirecționare de mai sus: Dacă un articol este setat să fie publicat, dar este încă un draft, atunci modificăm mesajul în consecință (care este message=10). Din nou, acest lucru nu a fost testat, dar ar trebui să funcționeze. Codex-ul funcției add_query_arg sugerează că atunci când o variabilă este deja setată, funcția o înlocuiește (dar, după cum am spus, nu am testat acest lucru).

13 feb. 2012 18:04:17
Comentarii

În afară de lipsa punctului și virgulă pe linia ta cu add_query_arg, acest truc cu filtrul redirect_post_location este exact ceea ce aveam nevoie. Mulțumesc!

MadtownLems MadtownLems
2 apr. 2014 22:04:21

@MadtownLems rezolvat :)

Stephen Harris Stephen Harris
3 apr. 2014 03:18:09

Aceasta a fost singura soluție care a funcționat pentru mine (nu voiam niciun hack cu javascript). Ești cel mai bun!

Cogicero Cogicero
21 ian. 2021 05:59:12
2

OK, asta este în final cum am reușit să implementez: un apel Ajax către o funcție PHP care face verificarea, inspirat parțial de acest răspuns și folosind un sfat inteligent de la o întrebare pe care am pus-o pe StackOverflow. Important este să mă asigur că verificarea se face doar când dorim să Publicăm postarea, astfel încât un Draft poate fi mereu salvat fără verificare. Aceasta s-a dovedit a fi soluția mai ușoară pentru a împiedica publicarea postării. Poate ajuta pe altcineva, așa că am scris-o aici.

Mai întâi, adăugați codul JavaScript necesar:

//AJAX pentru validarea evenimentului înainte de publicare
//adaptat după https://wordpress.stackexchange.com/questions/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("Eroare: " + response);
                        jQuery("#post").data("valid", false);

                    }
                    //ascunde icon-ul de încărcare, readuce butonul Publică la normal
                    jQuery('#ajax-loading').hide();
                    jQuery('#publish').removeClass('button-primary-disabled');
                    jQuery('#save-post').removeClass('button-disabled');
                });
                return false;
            });
        });
    </script>
    <?php
}
}

Apoi, funcția care se ocupă de verificare:

add_action('wp_ajax_ep_pre_submit_validation', 'ep_pre_submit_validation');
function ep_pre_submit_validation() {
//verificare simplă de Securitate
check_ajax_referer( 'pre_publish_validation', 'security' );

//convertește string-ul de date primit într-un array
//din https://wordpress.stackexchange.com/a/26536/10406
parse_str( $_POST['form_data'], $vars );

//verifică dacă încercăm să publicăm o postare
if ( $vars['post_status'] == 'publish' || 
    (isset( $vars['original_publish'] ) && 
     in_array( $vars['original_publish'], array('Publică', 'Programează', 'Actualizează') ) ) ) {
    if ( empty( $vars['_start_date'] ) || empty( $vars['_end_date'] ) ) {
        _e('Atât data de început cât și cea de sfârșit trebuie completate');
        die();
    }
    //asigură-te că începutul < sfârșit
    elseif ( $vars['_start_date'] > $vars['_end_date'] ) {
        _e('Data de început nu poate fi după data de sfârșit');
        die();
    }
    //verifică dacă ora este introdusă în cazul unui eveniment care nu durează toată ziua
    elseif ( !isset($vars['_all_day'] ) ) {
        if ( empty($vars['_start_time'] ) || empty( $vars['_end_time'] ) ) {
            _e('Atât ora de început cât și cea de sfârșit trebuie specificate dacă evenimentul nu durează toată ziua');
            die();              
        }
        elseif ( strtotime( $vars['_start_date']. ' ' .$vars['_start_time'] ) > strtotime( $vars['_end_date']. ' ' .$vars['_end_time'] ) ) {
            _e('Data/ora de început nu poate fi după data/ora de sfârșit');
            die();
        }
    }
}

//totul este în regulă, permite trimiterea
echo 'true';
die();
}

Această funcție returnează true dacă totul este în regulă și trimite formularul pentru publicarea postării prin canalul normal. În caz contrar, funcția returnează un mesaj de eroare care este afișat ca un alert(), iar formularul nu este trimis.

17 feb. 2012 19:31:32
Comentarii

Am urmat aceeași abordare, dar postarea este salvată ca "Ciornă" în loc de "Publicare" când funcția de validare returnează true. Nu sunt sigur cum să rezolv acest lucru!!!<br/>De asemenea, nu primesc datele pentru un câmp textarea (ex. post_content sau orice alt câmp text personalizat) în timpul apelului ajax?

Mahmudur Mahmudur
10 aug. 2012 06:47:00

Am aplicat această soluție ușor diferit: în primul rând am folosit codul de mai jos în javascript în caz de succes: delayed_autosave(); //obține datele din câmpul textarea/tinymce jQuery('#publish').data("valid", true).trigger('click'); //publică postarea Mulțumesc mult.

Mahmudur Mahmudur
13 aug. 2012 05:25:05
4

Cred că cea mai bună abordare nu este să PREVII schimbarea stării, ci mai degrabă să o REVERSEZI dacă se întâmplă. De exemplu: poți folosi hook-ul save_post, cu o prioritate foarte mare (astfel încât hook-ul să se declanșeze foarte târziu, mai precis după ce ai inserat metadatele), apoi verifici post_status al postării care tocmai a fost salvată și o actualizezi la "pending" (sau "draft" sau orice altceva) dacă nu îndeplinește criteriile tale.

O strategie alternativă ar fi să folosești hook-ul wp_insert_post_data pentru a seta direct post_status. Dezavantajul acestei metode, după părerea mea, este că încă nu ai inserat metadatele în baza de date, așa că va trebui să le procesezi etc. pe loc pentru a face verificările, apoi să le procesezi din nou pentru a le insera în baza de date... ceea ce ar putea crea o povară semnificativă, fie din punct de vedere al performanței, fie al codului.

12 feb. 2012 01:53:44
Comentarii

În prezent folosesc hook-ul save_post cu prioritatea 1 pentru a salva câmpurile meta din metabox-uri; ceea ce propui este să am un al doilea hook pentru save_post cu prioritatea, să zicem, 99? Asta va asigura integritatea? Dar dacă din anumite motive primul hook se execută, metadata este introdusă și postul este publicat, dar al doilea hook nu se execută, așa că ajungi cu câmpuri invalide?

englebip englebip
12 feb. 2012 14:00:03

Nu îmi pot imagina o situație în care primul hook s-ar executa dar al doilea nu... ce scenariu te face să crezi că asta s-ar putea întâmpla? Dacă ești atât de îngrijorat, poți insera post meta, verifica post meta, și apoi actualiza post_status într-o singură funcție care rulează dintr-un singur apel la un hook, dacă preferi.

mor7ifer mor7ifer
12 feb. 2012 15:36:52

Am postat codul meu ca edit la întrebarea mea; am încercat să folosesc un al doilea hook pentru save_post dar asta declanșează o buclă infinită.

englebip englebip
12 feb. 2012 21:58:49

Problema ta este că ar trebui să verifici postarea creată. Deci if( get_post_status( $post_id ) == 'publish' ) este ceea ce ar trebui să folosești, deoarece vei redefini datele din $wpdb->posts, nu datele din $_POST[].

mor7ifer mor7ifer
12 feb. 2012 23:05:39
0

Cea mai bună metodă poate fi JAVASCRIPT:

<script type="text/javascript">
var field_id =  "My_field_div__ID";    // <----------------- SCHIMBĂ ACESTA

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("Nu pot găsi acel ID de câmp !!"); }
  else {
      var Enabled_Disabled= document.getElementById(field_id).value;
      if (Enabled_Disabled == "" ) { alert("Câmpul este gol");   }  else{passed=true;}
  }
  if (!passed) { e.preventDefault();  return false;  }
}
</script>
26 iun. 2015 13:56:55
0
-2

Îmi pare rău că nu pot să îți dau un răspuns direct, dar îmi amintesc că am făcut ceva similar foarte recent. Doar că nu mai rețin exact cum. Cred că am abordat problema într-un mod ocolit - ceva de genul setând o valoare implicită și dacă utilizatorul nu a modificat-o, am detectat acest lucru într-un if. De exemplu -> if(category==default category) {echo "Nu ai selectat o categorie!"; return them to the post creation page; } Îmi pare rău că nu pot să îți ofer un răspuns concret, dar sper că te ajută măcar puțin.

12 feb. 2012 03:20:36