web-dev-qa-db-fra.com

AJAX / Read More: plusieurs check_ajax_referer () et wp_create_nonce () ne fonctionnent pas de manière indépendante

J'ai plusieurs wp_query sur la même page. Chacune de ces requêtes a un bouton AJAX "lire plus" en bas. Le problème que je constate est que je ne peux en avoir qu'un à la fois. Quelle que soit la fonction ajoutée en premier dans le functions.php, celle-ci fonctionne - l’autre obtient une erreur 403 pour admin-ajax.php.

Je suis assez nouveau dans AJAX, alors j’en ai probablement fait un hash total, et je suis sûr qu’il existe un moyen de combiner cela en une seule fonction (de préférence!), Mais si je peux juste trouver un moyen de les utiliser tous travailler de manière indépendante, que le travail soit bien.

Voici mon code:

Les 2 WP_Queries dans un modèle page-work.php personnalisé:

Premier:

<?php
    //Find out how many posts 
    $total_posts = wp_count_posts('smart_maps');
    $total_posts = $total_posts->publish;
    $number_shown = 0;


     $the_query = new WP_Query( $args ); ?>
    <?php if ( $the_query->have_posts() ) : ?>
    <div id="smartmaps" class="smartmaps col xs-col-14 xs-offset-1 md-col-12 md-offset-2" style="display:none;">
        <div class="in-here-smartmaps">
            <?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
            <a href="<?php echo get_permalink(get_the_ID());?>">
                <div class="single-smartmap col xs-col-16 md-col-8">
                    <div class="col xs-col-8 xs-offset-4 md-col-4 md-offset-0 center">
                        <div class="image-element">
                            <img src="<?php the_field('thumbnail_image');?>" class="circle">
                            <div class="image-overlay circle"></div>
                        </div>
                    </div>
                    <div class="col xs-col-14 xs-offset-1 md-col-10 md-offset-1">
                        <h5 class="smart-map-title"><?php the_title();?></h5>
                        <?php if(get_field('icons')) :?>
                            <div class="block-icons">
                                <?php if(in_array('Heart', get_field('icons'))) :?>
                                    <span class="icon-heart"></span>
                                <?php endif;?>
                                <?php if(in_array('Plant', get_field('icons'))) :?>
                                    <span class="icon-plant"></span>
                                <?php endif; ?>
                                <?php if(in_array('Cup', get_field('icons'))) :?>
                                    <span class="icon-cup"></span>
                                <?php endif;?>
                                <?php if(in_array('Book', get_field('icons'))) :?>
                                    <span class="icon-book"></span>
                                <?php endif;?>
                            </div>
                        <?php endif;?>
                    </div>
                </div>
            </a>
                <?php $number_shown++; 
            endwhile; ?>
        <?php endif;?>
        </div>
    <?php if($number_shown != $total_posts) :?>
        <div class="loadmore smarties col xs-col-14 xs-offset-1 md-col-8 md-offset-4 center">
            <h3><a href="#">LOAD MORE</a></h3>
        </div>
    <?php endif;?>

    </div>
<div clas="clearfix"></div>

Le second 1:

<div id="strategic-events" class="strategicevents col xs-col-14 xs-offset-1 md-col-12 md-offset-2" style="display:none;">
    <div class="in-here-strats">
    <?php
    //Find out how many posts 
    $total_posts = wp_count_posts('strategic_events');
    $total_posts = $total_posts->publish;
    $number_shown = 0;

    $args = array( 'post_type' => 'strategic_events', 'posts_per_page' => 10, 'offset' => $the_offset );


     $the_query2 = new WP_Query( $args ); ?>
    <?php if ( $the_query2->have_posts() ) : 
        while ( $the_query2->have_posts() ) : $the_query2->the_post(); ?>
        <a href="<?php echo get_permalink(get_the_ID());?>">    
            <div class="single-strategicevent col xs-col-16 md-col-8">
                <div class="col xs-col-8 xs-offset-4 md-col-4 md-offset-0 center">
                    <div class="image-element">
                        <img src="<?php the_field('strategic_event_image');?>" class="circle">
                        <div class="image-overlay circle"></div>
                    </div>
                </div>
                <div class="col xs-col-14 xs-offset-1 md-col-10 md-offset-1">
                    <h5 class="strategic-event-title"><?php the_title();?></h5>
                    <?php if(get_field('subtitle')) :?>
                        <p><?php the_field('subtitle');?></p>   
                        <small><?php the_field('location_text');?></small>          
                    <?php endif;?>
                </div>
            </div>
        </a>
            <?php $number_shown++; 
        endwhile; 
    endif; ?>
    </div>
    <?php if($number_shown != $total_posts) :?>
        <div class="loadmore strats col xs-col-14 xs-offset-1 md-col-8 md-offset-4 center">
            <h3><a href="#">LOAD MORE</a></h3>
        </div>
    <?php endif;?>

</div>
<div class="clearfix"></div>

Mon code JS (les deux également dans le page-work.php pour le moment) ...

<script>
var ajaxurl = "<?php echo admin_url( 'admin-ajax.php' ); ?>";
var page = 2;
jQuery(function($) {
    $('body').on('click', '.loadmore.strats', function(e) {
        e.preventDefault();
        var data = {
            'action': 'load_posts_by_ajax',
            'page': page,
            'security': '<?php echo wp_create_nonce("load_strats_posts"); ?>',
            'max_page': '<?php global $wp_query; echo $wp_query->max_num_pages;?>'
        };

        $.post(ajaxurl, data, function(response) {
            $('.in-here-strats').append(response);
            page++;
        });
    });
});
<script>
var ajaxurl = "<?php echo admin_url( 'admin-ajax.php' ); ?>";
var page = 2;
jQuery(function($) {
    $('body').on('click', '.loadmore.smarties', function(e) {
        e.preventDefault();
        var data = {
            'action': 'load_posts_by_ajax',
            'page': page,
            'security2': '<?php echo wp_create_nonce("load_smartmaps_posts"); ?>',
            'max_page': '<?php global $wp_query; echo $wp_query->max_num_pages;?>'
        };

        $.post(ajaxurl, data, function(response) {
            $('.in-here-smartmaps').append(response);
            page++;
        });
    });
});
</script>

Et le AJAX fonctionne dans mon function.php:

//Load More Smart Maps 
add_action('wp_ajax_load_posts_by_ajax', 'load_smart_maps_by_ajax_callback');
add_action('wp_ajax_nopriv_load_posts_by_ajax', 'load_smart_maps_by_ajax_callback');
function load_smart_maps_by_ajax_callback() {
check_ajax_referer('load_smartmaps_posts', 'security2');
$paged = $_POST['page'];
$args = array(
    'post_type' => 'smart_maps',
    'post_status' => 'publish',
    'posts_per_page' => '10',
    'paged' => $paged,
);
$my_posts = new WP_Query( $args );
if ( $my_posts->have_posts() ) :
    ?>
    <?php 
        $total_posts = wp_count_posts('smart_maps');
        $total_posts = $total_posts->publish;
        $number_shown = $paged * 10 - 10;?>
    <?php while ( $my_posts->have_posts() ) : $my_posts->the_post() ?>

            <a href="<?php echo get_permalink(get_the_ID());?>">
                <div class="single-smartmap col xs-col-16 md-col-8">
                    <div class="col xs-col-8 xs-offset-4 md-col-4 md-offset-0 center">
                        <div class="image-element">
                            <img src="<?php the_field('thumbnail_image');?>" class="circle">
                            <div class="image-overlay circle"></div>
                        </div>
                    </div>
                    <div class="col xs-col-14 xs-offset-1 md-col-10 md-offset-1">
                        <h5 class="smart-map-title"><?php the_title();?></h5>
                        <?php if(get_field('icons')) :?>
                            <div class="block-icons">
                                <?php if(in_array('Heart', get_field('icons'))) :?>
                                    <span class="icon-heart"></span>
                                <?php endif;?>
                                <?php if(in_array('Plant', get_field('icons'))) :?>
                                    <span class="icon-plant"></span>
                                <?php endif; ?>
                                <?php if(in_array('Cup', get_field('icons'))) :?>
                                    <span class="icon-cup"></span>
                                <?php endif;?>
                                <?php if(in_array('Book', get_field('icons'))) :?>
                                    <span class="icon-book"></span>
                                <?php endif;?>
                            </div>
                        <?php endif;?>
                    </div>
                </div>
            </a>
            <?php $number_shown++;
                if ( $number_shown == $total_posts ) {
                    echo '<style>.loadmore.smarties {display:none;}</style>';
                }
            endwhile ?>
    <?php endif;

wp_die();
}


//Load More Strategic Events
add_action('wp_ajax_load_posts_by_ajax', 'load_strats_by_ajax_callback');
add_action('wp_ajax_nopriv_load_posts_by_ajax', 'load_strats_by_ajax_callback');
function load_strats_by_ajax_callback() {
check_ajax_referer('load_strats_posts', 'security');
$paged = $_POST['page'];
$args = array(
    'post_type' => 'strategic_events',
    'post_status' => 'publish',
    'posts_per_page' => '10',
    'paged' => $paged,
);
$my_posts = new WP_Query( $args );
if ( $my_posts->have_posts() ) :
    ?>
    <?php 
        $total_posts = wp_count_posts('strategic_events');
        $total_posts = $total_posts->publish;
        $number_shown = $paged * 10 - 10;?>
    <?php while ( $my_posts->have_posts() ) : $my_posts->the_post() ?>

            <a href="<?php echo get_permalink(get_the_ID());?>">    
            <div class="single-strategicevent col xs-col-16 md-col-8">
                <div class="col xs-col-8 xs-offset-4 md-col-4 md-offset-0 center">
                    <div class="image-element">
                        <img src="<?php the_field('strategic_event_image');?>" class="circle">
                        <div class="image-overlay circle"></div>
                    </div>
                </div>
                <div class="col xs-col-14 xs-offset-1 md-col-10 md-offset-1">
                    <h5 class="strategic-event-title"><?php the_title();?></h5>
                    <?php if(get_field('subtitle')) :?>
                        <p><?php the_field('subtitle');?></p>   
                        <small><?php the_field('location_text');?></small>          
                    <?php endif;?>
                </div>
            </div>
        </a>
            <?php $number_shown++; 
                if ( $number_shown == $total_posts ) {
                    echo '<style>.loadmore.strats {display:none;}</style>';
                }
            endwhile ?>
    <?php endif;

wp_die();
}

Je suis tout à fait en faveur de simplifier cela en une seule fonction si possible, ou sinon, au moins de le faire fonctionner de manière indépendante. J'imagine qu'il ne s'agit que d'afficher 1 wp_nonce et de le comparer aux deux appels AJAX, de sorte que le premier qu'il lit est correct, le second produit-il un 403?

1
user2115227

Le problème que je constate est que je ne peux en avoir qu'un à la fois. Quelle que soit la fonction ajoutée en premier dans le functions.php, celle-ci fonctionne - l’autre obtient une erreur 403 pour admin-ajax.php.

Oui, car vos deux fonctions PHP (ou AJAX rappels) sont liées à l'action same AJAX, qui est load_posts_by_ajax:

// #1 AJAX action = load_posts_by_ajax
add_action('wp_ajax_load_posts_by_ajax', 'load_smart_maps_by_ajax_callback');
add_action('wp_ajax_nopriv_load_posts_by_ajax', 'load_smart_maps_by_ajax_callback');

// #2 AJAX action = load_posts_by_ajax
add_action('wp_ajax_load_posts_by_ajax', 'load_strats_by_ajax_callback');
add_action('wp_ajax_nopriv_load_posts_by_ajax', 'load_strats_by_ajax_callback');

Et check_ajax_referer() par défaut quitte la page avec le statut 403 ("interdit"), lorsque, par exemple, le nonce n'est pas spécifié ou a déjà expiré:

// #1 $_REQUEST['security2'] will be checked for the load_smartmaps_posts nonce action.
check_ajax_referer('load_smartmaps_posts', 'security2');

// #2 $_REQUEST['security'] will be checked for the load_strats_posts nonce action.
check_ajax_referer('load_strats_posts', 'security');

Vous pouvez envoyer les deux nonces (bien que personne ne le fasse), ou vous pouvez utiliser le même nonce et la même clé $_REQUEST (par exemple security), mais vous obtiendrez tout de même un résultat/une réponse inattendu.

Donc, si vous voulez une "fonction unique", vous pouvez utiliser une "action" secondaire ":

  1. Partie PHP dans functions.php:

    // #1 Load More Smart Maps
    // No add_action( 'wp_ajax_...' ) calls here.
    function load_smart_maps_by_ajax_callback() {
        // No need to call check_ajax_referer()
        ...your code here...
    }
    
    
    // #2 Load More Strategic Events
    // No add_action( 'wp_ajax_...' ) calls here.
    function load_strats_by_ajax_callback() {
        // No need to call check_ajax_referer()
        ...your code here...
    }
    
    add_action( 'wp_ajax_load_posts_by_ajax', 'load_posts_by_ajax' );
    add_action( 'wp_ajax_nopriv_load_posts_by_ajax', 'load_posts_by_ajax' );
    function load_posts_by_ajax() {
        check_ajax_referer( 'load_posts_by_ajax', 'security' );
    
        switch ( filter_input( INPUT_POST, 'action2' ) ) {
            case 'load_smartmaps_posts':
                load_smart_maps_by_ajax_callback();
                break;
    
            case 'load_strats_posts':
                load_strats_by_ajax_callback();
                break;
        }
    
        wp_die();
    }
    
  2. Partie JS - l'objet data:

    // #1 On click of `.loadmore.smarties`
    var data = {
        'action': 'load_posts_by_ajax',
        'action2': 'load_smartmaps_posts',
        'security': '<?php echo wp_create_nonce( "load_posts_by_ajax" ); ?>', // same nonce
        ...other properties...
    };
    
    // #2 On click of `.loadmore.strats`
    var data = {
        'action': 'load_posts_by_ajax',
        'action2': 'load_strats_posts',
        'security': '<?php echo wp_create_nonce( "load_posts_by_ajax" ); ?>', // same nonce
        ...other properties...
    };
    

Mais pourquoi ne pas associer les rappels à leur propre action spécifique AJAX:

// #1 AJAX action = load_smart_maps_posts_by_ajax
add_action('wp_ajax_load_smart_maps_posts_by_ajax', 'load_smart_maps_by_ajax_callback');
add_action('wp_ajax_nopriv_load_smart_maps_posts_by_ajax', 'load_smart_maps_by_ajax_callback');

// #2 AJAX action = load_strats_posts_by_ajax
add_action('wp_ajax_load_strats_posts_by_ajax', 'load_strats_by_ajax_callback');
add_action('wp_ajax_nopriv_load_strats_posts_by_ajax', 'load_strats_by_ajax_callback');
1
Sally CJ

WP ajax est un peu étrange. Vous mettez le script en file d'attente pour qu'il apparaisse sur la page. Vous LOCALISEZ également votre script afin de coller des variables dans la page dont il aura besoin, des variables telles que les nonces, les adresses ajax ou même d’autres choses étranges que vous pourriez trouver utiles.

Enregistrez et localisez le script comme ceci:

    wp_enqueue_script( 'name_of_function', get_stylesheet_directory_uri() . '/js/my_special_script.js', array( 'jquery' ), '1.0', true );
    wp_localize_script( 'name_of_function',
                       'name_the_script_will_see',
                       array(
                           'ajax_url' => admin_url('admin-ajax.php'),
                           'ajax_nonce' => wp_create_nonce('your_nonce'),
                       ));

Et puis vous devez ajouter l'action ajax DEUX FOIS, une fois pour le public et encore pour les pages d'administration.

add_action('wp_ajax_this_is_the_ajax_name', 'function_name_in_php' );
add_action('wp_ajax_nopriv_this_is_the_ajax_name', 'function_name_in_php' );

Ensuite, dans votre script, vous choisissez les valeurs suivantes:

var data = {
                'action': 'this_is_the_ajax_name',
                'post_nonce': name_the_script_will_see.ajax_nonce,
                'other_value_needed': value_generated_by_script,
            };
$.post( name_the_script_will_see.ajax_url, data, function( response_data ) { 

            if ( response_data.success ) {
                alert ("Success");
            } else {
                alert ("Error");
            }

Votre script php recevra les données sous forme de données post:

function function_name_in_php(){
     $nonce = $_POST['post_nonce'];
     $other_data = $_POST['other_value_needed'];
     if ( ! wp_verify_nonce( $nonce, 'your_nonce' ) ) {
    wp_send_json_error(array(
        'message'=>'Security Token Failure',
    )); // sends json_encoded success=false
}

Je vais utiliser un nonce dans de nombreux scripts différents. Tout ce que je fais vraiment, c'est de m'assurer que mon demandeur provient bien de mon site. Il ne sert à rien de générer 17 notes différentes pour cela. Souvent, je colle le nonce dans un bouton de soumission en tant qu'attribut de données, puis je tire le nonce de celui-ci plutôt que du schéma de localisation wordpress. C'est plus pratique quand il y a plusieurs scripts sur une seule page et qu'ils accèdent à certaines des mêmes fonctions principales.

var postData = {};
 var nonce = $('#register_button').attr('data-nonce');
 postData.nonce = nonce;
 postData.action = 'this_is_the_ajax_name';
$.post( name_the_script_will_see.ajax_url, postData, function( response_data ) { 

        if ( response_data.success ) {
            alert ("success");
        } else {
            alert ("Error");
        }

J'espère que cela t'aides!

2
Elkrat