Développer avec et sans Ajax : Compatibilité NOJS avec admin-post.php

Développer avec et sans Ajax : Compatibilité NOJS avec admin-post.php

Ajax, sujet toujours discuté à droite à gauche, ici même encore la semaine dernière avec un article sur la navigation ajax made in wabeo par notre ami Willy.

À mon tour de vous parler d’Ajax, plus précisément de la façon d’utiliser Ajax dans WordPress afin de rendre vos développements compatible NOJS, ou si vous préférez « Sans JavaScript« .

Dans le même temps je vais devoir vous parler d’un fichier un peu plus méconnu que le admin-ajax.php, il s’agit de admin-post.php, son jumeau synchrone.

JavaScript Head

Vous avez développé un plugin, avec une belle interface, des actions bien stylées, les pages n’ont même pas besoin de se recharger. Que ce soit en back-end ou en front-end, l’administrateur et l’utilisateur final gagnent en confort de navigation. Félicitations.

Vous désactivez le JavaScript afin de tester le site, puisqu’avant le développement de ce plugin, le site était bel et bien fonctionnel. Même chose pour le back-end, WordPress a géré pour toutes les pages, toutes les options, tous les cas que tout fonctionne même sans le JavaScript. Vous rafraîchissez et …

drama

Peinard dans votre fauteuil, vous regardez flambez le site, plus rien ne fonctionne, vous qui aviez déjà le champagne à la main, en train de vous auto-congratuler de votre chef-d’oeuvre JavaScripto-contemporaino-2013, c’est raté.

HEY ! Pourquoi désactiver JavaScript ? Qui navigue sans JavaScript ? Quel pourcentage de personnes cela représente ? Je vous laisse lire l’article chez AlsaCreations qui s’est demandé Pourquoi certains naviguent sans JavaScript.

y-u-no-have-javascript-enabled
C’est vrai quoi …. y ?

Convaincus ? J’espère !

JavaScript Heat

En fait, la vraie bonne solution n’est pas de développer en pensant JavaScript en premier lieu, même si c’est le besoin recherché, il ne faut donc pas le faire basé sur admin-ajax.php avec un envoi de donnée arbitraire comme « id=123,action=like » etc.

Vous devez faire en sorte que le développement fonctionne sans JavaScript, basé sur admin-post.php, puis ensuite, nous pourrons ajouter la couche Ajax, ce qui ne prendra certainement pas le double de temps.

JavaScript Junkie

Exit admin-ajax.php, hello admin-post.php. Ces 2 fichiers fonctionnent quasiment de la même façon à la différence que Ajax sera asynchrone comme son nom l’indique, et Post sera synchrone, un rechargement de la page sera nécessaire, c’est le but : ne pas gérer de suite le JavaScript.

Nous allons voir le plus simplement possible comment faire. Prenons l’exemple d’un lien à cliquer qui serait sous la forme :

<a href="#" data-ID="<?php get_the_ID(); ?>" class="clicme">Like</a>

Puis vous avez certainement un code JavaScript du genre :

$('.clicme').on('click', function(e){
	e.preventDefault();
	$.get( '<?php echo admin_url( 'admin-ajax.php' ); ?>', { id: $(this).data('ID'), action:'like' } )
		.done( function(){ alert('Merci d\'avoir liké'); } );
});

Et côté PHP vous avez (j’espère) utilisé les hooks wp_ajax_nopriv_like et wp_ajax_like pour gérer ce like.

Code PHP du like très simplifié :

add_action( 'wp_ajax_nopriv_like', 'manage_callback_like' );
add_action( 'wp_ajax_like', 'manage_callback_like' );
function manage_callback_like() {
	if ( isset( $_GET['id'] ) && $id=$_GET['id'] && $post == get_post( $id ) ) {
		$nb = (int)get_post_meta( $id, '_likes', true );
		update_post_meta( $id, '_likes', ++$nb );
		wp_send_json_success();
	}
}

Hot JavaScript

« Ça devient chaud ! » Mais nooon … il faut d’abord penser à le faire de suite sans JS, voici comment procéder :

<a href="<?php echo admin_url( 'admin-post.php?action=like&id=' . get_the_ID() ); ?>">Like</a>

Plus besoin du data-ID. Et côté PHP j’utilise maintenant en plus les hooks admin_post_nopriv_like et admin_post_like.

Je modifie un peu la fonction de callback en prime :

add_action( 'wp_ajax_nopriv_like', 'manage_callback_like' );
add_action( 'wp_ajax_like', 'manage_callback_like' );
add_action( 'admin_post_nopriv_like', 'manage_callback_like' );
add_action( 'admin_post_like', 'manage_callback_like' );
function manage_callback_like() {
	if ( isset( $_GET['id'] ) && $id=$_GET['id'] && $post == get_post( $id ) ) {
		$nb = (int)get_post_meta( $id, '_likes', true );
		update_post_meta( $id, '_likes', ++$nb );
		// pas d'Ajax = redirection sur la page de provenance
		if ( !defined( 'DOING_AJAX' ) ) {
			wp_safe_redirect( wp_get_referer() );
			die();
		}
		wp_send_json_success();
	}
}

Et mon JavaScript se simplifie :

$('.clicme').on('click', function(e){
	e.preventDefault();
	$.get( $(this).attr('href').replace('admin-post.php', 'admin-ajax.php') )
		.done( function(){ alert('Merci d\'avoir liké'); } );
});

Je prends l’url du lien et remplace le nom du fichier post par celui de l’Ajax ! Plus besoin des data.

Franchement, c’est plus difficile là ? Plus long à coder ? Non. Si je clic sur le lien sans JavaScript je vais déclencher l’action de admin-post.php, mettre à jour un compteur de like, et être redirigé sur ma page.

Si le JavaScript est possible, le preventDefault empêchera alors le clic et lancera à la place la requête Ajax sur admin-ajax.php. Libre à vous d’ajouter un loader/spinner pour indiquer qu’une requête est en cours, si vous souhaitez mettre à jour le compteur en live, je vous laisse faire un wp_send_json() avec la valeur à retourner, valeur que vous remplacerez, ça, c’est aussi un autre sujet, je vous laisse faire :D

Epic JavaScript

Vous venez de faire du développement Ajax compatible NOJS en 3 lignes de code de plus et en modifiant un lien href.

Boy-That-Escalated-Quickly-Anchorman

Cette solution fonctionne aussi avec les formulaires qui seraient sérialisés puis envoyés en Ajax, si vous l’utilisez dans le back-end, n’oubliez pas d’ajouter un nonce !

Aussi elle est simple à mettre en place et respecte les bonnes pratiques en termes d’accessibilité, en auriez-vous déjà eu besoin ?

Lire la suite

Vous aimez ? Partagez !

Abonnement gratuit à 0€


17 thoughts on “Développer avec et sans Ajax : Compatibilité NOJS avec admin-post.php”

  • 1
    Sébastien on 10 décembre 2013 Répondre
    Bonjour, je trouve cette solution très simple à mettre en place, je vais essayer ça dès que possible.
    Merci !!!!
  • 2
    Gilles on 10 décembre 2013 Répondre
    Pourquoi naviguer avec JS ? Car par exemple FF a caché la possibilité de le désactiver dans le about:config ?
    C’est vraiment militant de naviguer sans JS.
    Même si ce n’est pas stupide…
    • 3
      Greg on 17 décembre 2013
      Ce n’est pas parce que JavaScript n’est plus désactivable dans les options de Firefox qu’on ne peut plus le désactiver. https://addons.mozilla.org/fr/firefox/addon/noscript/ Perso, je ne sors jamais sans :)
      Utilité ?
      – Supprimer les popups,
      – Supprimer les scripts lourds qui ralentissent les sites,
      – Supprimer certaines pubs et certains trackeurs (intérêt moindre si on a installé un plugin anti-pub bien sûr),
      – etc.
      Bref, virer tout ce qui pollue le web.
      C’est pour ça que je trouve outrageant que Firefox ait supprimé cette option, mais heureusement qu’il y a des plugins pour ça, et qui font mieux le travail.
  • 4
    Willy Bahuaud on 10 décembre 2013 Répondre
    C’est vrai que je ne pense pas systématiquement au no ajax… Pas forcément envie de perde du temps à cautionner les actions délirantes d’un administrateur réseau parano ^^

    Mais là, ton astuce tient en 3 LIGNES !
    Chapeau donc ! Je vais l’utiliser partout :-)
    (et aussi quand il ne s’agit pas de palier de l’ajax, bien sûr…)

    Merci pour l’astuce !

    • 5
      Willy Bahuaud on 13 décembre 2013
      Tiens, je me posait une question… Est ce qu’admin-post peut, tout comme admin-ajax, retourner des données ?

      L’intérêt serait par exemple d’informer l’utilisateur que l’action a été accomplit, ou renvoyer les données erronées saisies en cas d’échec, pour les remettre dans le formulaire.

      Et si l’on peut, sais-tu sous quelle forme les données seraient retournées ?

    • 6
      Julio Potier on 13 décembre 2013
      Je vois ce que tu veux dire. J’envoie un formulaire, mais j’ai merdé et je reviens sur la page : vide !
      Du coup il faut soit :
      – renvoyer sur la page en GET, super on voit les valeurs dans l’historique …
      – poser un cookie ou local storage, juste pour ça -_-
      – renvoyer en post donc faire un wp_remote_post sur le referer, ça me parait mieux ça.

      Qu’en dis tu ?

    • 7
      Willy Bahuaud on 13 décembre 2013
      Oui, si on reviens c’est vide, et même pire : aucune info.
      Comment le mec’ sait que son action a été prise en compte ?

      Le problème si on renvoie en post, c’est que l’on va retomber sur le problème des variables post renvoyés au rechargement de la page…

      Du coup ça laisse le choix entre session, cookies au local storage (en prenant soin de détruire les variables lorsque l’on en a plus besoin). Mais… c’est un peu crade non :-/ ?

      Qu’en dis-tu ?

    • 8
      Julio Potier on 13 décembre 2013
      Ha ouiii le refresh !
      Du coup, faut-il selon les cas éviter admin-post ?
    • 9
      Greg on 17 décembre 2013
      Ha ces n00bs, faut tout leur montrer :)

      // !What to do on form submit
      add_action( ‘admin_post_my_get-respond’, ‘my_respond’ );
      function my_respond() {
      
      	if ( empty($_REQUEST[‘my_wpnonce’]) || !check_admin_referer( ‘my_get-respond’, ‘my_wpnonce’ ) )
      		wp_die(__(‘Cheatin’ uh?’));
      
      	// Check if the current user can perform the action
      	if ( !current_user_can( ‘manage_options’ ) )
      		wp_die(__(‘Cheatin’ uh?’));
      
      	// Check if we have a value
      	if ( empty($_REQUEST[‘my-option’]) )
      		add_settings_error( ‘my-option-group’, ‘my_empty’, __(‘Hey! Fill the field!’), ‘error’ );
      	else {
      		$option = esc_attr( $_REQUEST[‘my-option’] );
      		
      		// Do stuff with $option
      	}
      
      	/**
      	 * Handle settings errors and return to options page
      	 */
      	// If no settings errors were registered add a general ‘updated’ message.
      	if ( !count( get_settings_errors() ) )
      		add_settings_error(‘general’, ‘settings_updated’, __(‘Action performed.’), ‘updated’);
      
      	set_transient(‘settings_errors’, get_settings_errors(), 30);
      
      	/**
      	 * Redirect back to the settings page that was submitted
      	 */
      	$goback = add_query_arg( ‘settings-updated’, ‘true’, wp_get_referer() );
      	wp_redirect( $goback );
      	exit;
      }

    • 10
      Julio Potier on 17 décembre 2013
      ho pardon, on parlait en front-end nous, si je mets à dispo une formulaire de style « inscription maison en front » et que la personne mets du caca dedans. Le but serait de revenir sur le formulaire sans avoir perdu toutes les infos des 150 champs.
      Sinon en backend le transient EST la solution. J’aime « get_settings_errors() » #pointexpert
      petit ps :

      if ( empty($_REQUEST[‘my_wpnonce’]) || !check_admin_referer( ‘my_get-respond’, ‘my_wpnonce’ ) )
          wp_die(__(‘Cheatin’ uh?’));

      égal :

      check_admin_referer( ‘my_get-respond’, ‘my_wpnonce’ ) );

      #noobtoimeme

    • 11
      Greg on 17 décembre 2013
      petit ps aussi :
      Ce n’est pas exactement pareil, dans ton cas tu as droit à un FPD si $_REQUEST['my_wpnonce'] n’est pas set.
      #pong
    • 12
      Julio Potier on 17 décembre 2013
      Je vois mal comment
      https://github.com/WordPress/WordPress/blob/master/wp-includes/pluggable.php#L804

      $result = isset($_REQUEST[$query_arg]) ? wp_verify_nonce($_REQUEST[$query_arg], $action) : false;

      #noube

    • 13
      Greg on 17 décembre 2013
      J’étais certain que ça déclenchait un FPD, mais j’ai eu beau remonter jusqu’à la version 3.0, ce isset() semble pourtant avoir toujours été là.
      Je dois confondre avec autre chose mais ça m’étonne vraiment, c’est très bizarre.
    • 14
      Julio Potier on 17 décembre 2013
      T’as oublié ton tag x)
    • 15
      Greg on 17 décembre 2013
      #ticon
  • 16
    chahine@women on 31 mars 2014 Répondre
    Pourquoi naviguer avec JS ? Car par exemple FF a caché la possibilité de le désactiver dans le about:config ?
    C’est vraiment militant de naviguer sans JS.
    Même si ce n’est pas stupide
  • 17
    Christian Louboutin on 18 octobre 2014 Répondre
    Just wish to say your article is as astounding. The clarity for your submit is simply cool and that i could assume you are an expert on this subject. Well together with your permission allow me to grasp your RSS feed to keep updated with forthcoming post. Thank you 1,000,000 and please carry on the enjoyable work.

Laisser un commentaire

Avant de parler, merci de lire la charte des commentaires.

Utiliser le tag [php][/php] pour ajouter du code ou utilisez un service comme pastebin.com.
Cibler un commentateur avec un "@", merci à Mention Comments Authors !