Meta box : liez un pdf à votre article

Dans l’article d’hier on s’initiait aux meta boxes de WordPress. Ces petites boites que nous rajoutons dans l’admin, nous permettant d’ajouter des informations complémentaires à vos articles, pages, contenus…

Maintenant je vous propose de découvrir des metaboxes plus complexes, pour des utilisations spécifiques.

Je ne sais pas si vous avez déjà rencontré ce problème mais pour créer dans WordPress des boites contenant des champs input:file, c’est un peu la merde. Tout simplement car les formulaires de l’admin ne sont pas fait pour ça (ils n’ont pas l’attribut enctype=“multipart/form-data”), peut-être pour des raison de sécurité…

Donc pour uploader des PDFs et les associer à vos contenus il va falloir trouver une autre méthode.

un bouton pour télécharger un pdf dans WordPress
Liez des documents à vos articles

Celle que nous allons voir est amplement pompée du génialissime ouvrage « Professional WordPress Plugin Development » que je vous conseille vivement de lire.

Codons la meta box !

Tout d’abord voyons le principe.

Nous allons créer un champ texte et un bouton.
Au clic sur le bouton, un code javascript va nous permettre d’utiliser la fenêtre d’upload des medias pour envoyer notre contenu dans WordPress…
Après l’upload, un clic sur “insérer dans l’article” renseignera l’URL du fichier dans le champ texte prévu à cet effet.

Le code de base

La première étape consiste donc à créer notre metabox, avec le bouton et le champ texte.

On procède comme expliqué dans le précédent article. Il faut initialiser la metabox, la construire puis prévoir la sauvegarde de la metadonnée.

add_action( 'add_meta_boxes', 'ajout_pdf_metabox' );
function ajout_pdf_metabox() {
  add_meta_box( "url_du_pdf", "Fichier à télécharger", "pdf_metabox", "post", "normal", "high" );
}

function pdf_metabox( $post ){
  $url_pdf = get_post_meta( $post->ID, '_url_pdf', true );
  ?>
  <label for="url_pdf">Sélectionner un pdf à télécharger</label><input id="url_pdf" style="width: 450px;" type="text" name="url_pdf" value="<?php echo esc_url( $url_pdf ); ?>" />
  <input id="upload_pdf_button" class="button-secondary" type="button" value="Télécharger un document pdf" />
  <?php 
}

add_action( 'save_post', 'save_custom' );
function save_custom( $post_ID ){
  if ( isset( $_POST[ 'url_pdf' ] ) ) {
    update_post_meta( $post_ID, '_url_pdf', esc_url_raw( $_POST[ 'url_pdf' ] ) );
  }
}

Pour l’instant un clic sur le bouton ne sera suivi d’aucune action. Si on renseigne du texte dans le champ et que l’on sauvegarde l’article, les données seront sauvegardée.

Le javascript

Maintenant il va falloir créer le javascript. Son rôle sera de faire apparaitre la fenêtre d’upload des médias lorsque l’on cliquera sur “Télécharger un document pdf”.

Créez un nouveau fichier .js puis copiez-y le code suivant.

jQuery(document).ready(function($) {
  // on initialise une variable qui nous permettra de détecter le champ à remplir
  var formfield = null;
  // on détecte un clic sur le bouton
  $('#upload_pdf_button').click(function() {
    $('html').addClass('pdf');
    // on cible notre champ
    formfield = $('#url_pdf').attr('name');
    // on charge la fenêtre
    tb_show('', 'media-upload.php?type=image&TB_iframe=true');
    // on empêche toute action supplémentaire
    return false;
  });
});

Ce code ne sert (en gros) qu’à charger la fenêtre des medias, maintenant il faut ajouter un autre bout de code.

Après avoir sélectionné/envoyé un fichier, un clic sur “insérer dans l’article” envoie l’URL du fichier dans notre champ texte.

La méthode de WordPress correspondant au bouton insérer dans l’article est “send_to_editor”. C’est elle que nous allons intercepter.

Il faut ajouter à notre Javascript (avant la fermeture du jQuery) le code suivant

// on duplique la function send_to_editor
window.original_send_to_editor = window.send_to_editor;
// notre fonction
window.send_to_editor = function(html){
// la varible qui contient notre url DE FICHIER
var fileurl;
// si la fenetre est bien chargé à partir du bouton
if (formfield != null) {
  // on récupère l'url (si besoin, pour comprendre, vous pouvez faire un console.log de html)
  fileurl = $(html).filter('a').attr('href');
  // on écrit l'URL dans notre champ texte
  $('#url_pdf').val(fileurl);
  // on shoot la boite
  tb_remove();
  // on vire la classe pdf
  $('html').removeClass('pdf');
  // on vide la variable formfield
  formfield = null;
} else {
  // si la fenêtre n'est pas chargée à partir du bouton, alors comportement normal
  window.original_send_to_editor(html);
}
};

Voilà… En fait on voit dans ce code qu’il a fallu employer une ruse pour différencier notre usage de la boite d’upload de son usage normal, afin qu’elle ai toujours le comportement attendu.

Il ne nous reste qu’a enregistrer notre script et à le charger, via wp_enqueue_script(), dans l’admin. Insérez ce code dans la fonction pdf_metabox().

// mon script
wp_enqueue_script( 'willy-meta-box-sup', get_bloginfo('template_url').'/js/willy-pdf.js', array( 'jquery','media-upload','thickbox' ) );

Pour que le script fonctionne, il faut qu’il soit chargé après jQuery, media-upload et thickbox.

Vous pouvez maintenant tester notre metabox, elle doit normalement fonctionner.

Les dépendances

Ahhh… un petit détail. Si vous souhaitez utiliser cette metabox sur un type de contenu n’ayant pas d’éditeur, ça ne marchera pas. Enfin pas dans l’état…

La raison est simple, notre script a beaucoup plus de dépendances, qui sont normalement chargées en même temps que l’éditeur. Il faudra donc, avant d’appeler notre javascript, appeler tous ces javascripts et feuilles de style.

// les dépendances
wp_enqueue_script( 'media-upload' );
wp_enqueue_script( 'thickbox' );
wp_enqueue_script( 'quicktags' );
wp_enqueue_script( 'jquery-ui-resizable' );
wp_enqueue_script( 'jquery-ui-draggable' );
wp_enqueue_script( 'jquery-ui-button' );
wp_enqueue_script( 'jquery-ui-position' );
wp_enqueue_script( 'jquery-ui-dialog' );
wp_enqueue_script( 'wpdialogs' );
wp_enqueue_script( 'wplink' );
wp_enqueue_script( 'wpdialogs-popup' );
wp_enqueue_script( 'wp-fullscreen' );
wp_enqueue_script( 'editor' );
wp_enqueue_script( 'word-count' );
wp_enqueue_style( 'thickbox' );

// mon script
wp_enqueue_script( 'willy-meta-box-sup', get_bloginfo('template_url').'/js/willy-pdf.js', array( 'jquery','media-upload','thickbox' ) );

Un shortcode pour ressortir tout ça

Nous pouvons récupérer l’URL du PDF lié via la fonction get_post_meta(), dans le template. Cependant cela peut être très pratique de le récupérer directement dans le contenu également.

Voici un shortcode permettant de faire ça. L’avantage de l’utiliser est que vous n’aurez pas modifier le lien dans l’éditeur si vous mettez à jour le PDF. Et puis ça sera un peu plus clair pour vos clients.

Pour l’utiliser, il suffit d’insérer [pdf] dans votre article, ou bien encore [pdf]Téléchargez mon PDF ![/pdf].

add_shortcode( 'pdf', 'lien_pdf' );
function lien_pdf( $content = NULL ) {
  global $post;
  $pdf = get_post_meta( $post->ID, '_url_pdf', true );
  $content = ( $content != NULL ) ? $content : 'Télécharger le PDF';
  return '<a class="call_pdf" href="'.esc_url($pdf).'" target="_blank">'.$content.'</a>';
}

Pour utiliser le shortcode, il suffit de copier le code ci-dessus dans votre fichier functions.php.

Aller plus loin : un custom post type «téléchargements»

Jusqu’ici nous avons vu comment ajouter une meta box de type fichier, afin de lier un pdf à un article.
Il m’arrive souvent, en plus, de créer un Custom Post Type “Téléchargement” et d’y assigner cette seule metabox. L’intérêt de procéder ainsi est :

  • d’identifier clairement un type de contenu PDF
  • de faciliter la traduction de ces contenus, avec WPML par exemple
  • de pouvoir lier une multitude de PDFs à un article

Il faut commencer par créer ce type de contenu, dans le fichier functions.php.

add_action( 'init','mes_telechargements');
function mes_telechargements(){
//custom post type pour les téléchargements
register_post_type('telechargement', array(
  'label'         => __('Téléchargements'),
  'labels'        => array(
    'add_new_item'  => 'Ajouter un fichier à télécharger',
    'edit_item'     => 'Modifier le fichier à télécharger',
    'new_item'      => 'Ajouter un fichier à télécharger',
    'view_item'     => 'Voir le fichier à télécharger',
    'singular_name' => 'Fichier à télécharger'
  ),
  'public' => true,
  'show_ui' => true,
  'capability_type' => 'post',
  'hierarchical' => false,
  'has_archive' => true,
  'rewrite' => array('slug' => 'doc'),
  'supports' => array('title'),
  'show_in_nav_menus' => true
));
}

Pour l’instant, ce type de contenu ne supporte que le champ “titre” . Il faut aussi y associer la meta box crée plus haut, en modifiant le 4ème paramètre par telechargement.

add_action( 'add_meta_boxes', 'ajout_pdf_metabox' );
function ajout_pdf_metabox() {
  add_meta_box( "url_du_pdf", "Fichier à télécharger", "pdf_metabox", "telechargement", "normal", "high" );
}

Nos fichiers PDFs sont maintenant totalement indépendants. Il ne reste plus qu’à les re-rendre associables aux autres contenus…

La metabox pour lier plusieurs fichier relatifs

Pour ce faire on va faire une autre meta box, plutôt simple. On peut repartir du modèle des “meta-check-box” du dernier article, auquel on va simplement ajouter une boucle pour lister tous les téléchargements disponibles.

add_action('add_meta_boxes','init_metabox');
function init_metabox(){
  add_meta_box('pdf_affectes', 'Les PDFs liés à cet article', 'pdfs_lies', 'post', 'side');
}
// cette fonction me sert à inscrire checked, si jamais la valeur est coché
function check($cible,$test){
  if(in_array($test,$cible)){return 'checked';}
}
function pdfs_lies($post){
  $doc = get_post_meta($post->ID,'_docs_affectes',false);
  echo 'Indiquez les PDFs affectés à l\'article :';
  $docs = get_posts('post_type=telechargements&posts_per_page=-1');
  foreach($docs as $k=>$d){
    echo '<label><input type="checkbox" name="docs[]" '.check($d->ID,$doc).' value="'.$d->ID.'" /> '.$d->post_title.'</label>';
  }
}

add_action('save_post','save_metabox');
function save_metabox($post_id){
  if(isset($_POST['doc'])){
    // je supprime toutes les entrées pour cette meta
    delete_post_meta($post_id, '_docs_affectes');
    // et pour chaque PDF coché, j'ajoute une metadonnée
    foreach($_POST['doc'] as $c){
      add_post_meta($post_id, '_docs_affectes', intval($c) )
    }
  }
}

Ça y est, nous pouvons associer autant de fichiers PDFs que l’on veut à nos articles. À noter que c’est également plus simple pour associer le même PDF à plusieurs contenus.

Obtenir la liste

Pour afficher la liste de tous les PDFs lié au contenu, on peut là encore utiliser la function get_post_meta() ou bien nous pouvons utiliser un autre shortcode que voici :

add_shortcode('tous_pdf','liste_pdf');
function liste_pdf(){
  global $post;
  $docs = get_post_meta($post->ID,'_docs_affectes',false);
  if(!empty($docs[0]){
    echo '<ul>';
    foreach($docs as $doc){
    $url_pdf = get_posts_meta($doc,'_url_pdf',true);</ul>
    $titre = get_the_title($doc);</ul>
      echo '<li><a href="'.$url_pdf.'">'.$titre.'</a></li>';
    }
    echo '</ul>';
  }
}

Pour l’utiliser, il faut juste ajouter dans votre contenu [tous_pdf]. Simple, n’est ce pas ?

Je trouve que cette solution est très facile à utiliser sur les sites, et pas si dur à mettre en place.

Merci à Brad Williams pour le javascript !

Bon… demain on se fait une autre metabox ?

14 commentaires
Daniel Roch, il y a 5 années
Effectivement, on entre dans le vif du sujet et dans des fonctions plus complexes dès le deuxième article dédié aux métas box.

Bravo pour le boulot, et vivement les prochains articles de la série. 😉

rMn, il y a 5 années
Superbe article !

Malheusement le shortcode ne fonctionne pas pour moi (mais tous le reste fonctionne.
J’ai pourtant bien copier la fonction dans fonction.php

Respect 😉

Willy Bahuaud, il y a 5 années
Oups… Une erreur c’était glissé dans l’article !

Le shortcode il faut l’appeler avec [pdf] et non [lien_pdf]. Idem pour le second shortcode qu’il faut appeler avec [tous_pdf]

Merci de m’avoir faire remonter l’erreur 🙂

Daniel, il y a 5 années
A bah… des articles comme celui-ci on en redemande. Beau travail en effet, que je garde précieusement dans mes favoris
L’ouvrage que tu cite en introduction est effectivement un « must-have », 500 pages qui valent de l’or.
Julio Potier (BoiteAWeb), il y a 5 années
Excellent, excellent et excellent. 3 mots pour décrire l’article. Bravo Willy, je mets en favoris, rien a re-dire (moi qui aime ça ! lol)
Geoffrey, il y a 5 années
Quel article !!
Merci, ça va largement servir.
Hâte de lire les prochains de la série.
Willy Bahuaud, il y a 5 années
Merci à Tous pour les commentaires, ça donne envie de faire plus d’articles !

Dans le bouquin, Brad Williams parle comme ça de la méthode « send_to_editor » de WordPress. C’est frustrant, j’ai fouillé pour voir s’il y en avait d’autre du même genre (par exemple pour intercepter l’image à la une) mais je n’ai pas trouvé grand chose…

Mathieu, il y a 5 années
Merci pour cet article très utile.

Tu parles de sécurité en debut d’article. On utilise ici la fenêtre d’upload des médias, cela signifie que c’est parfaitement safe ?

Bonne fin de semaine,
Mathieu

Willy Bahuaud, il y a 5 années
, oui, à 100%
Cryoweb, il y a 5 années
Super tuto merci !
Petit bémol par contre: le javascript pour uploader les pdf (via le custom type téléchargements) ne fonctionne pas sur mon install de wordpress 3.4.1:

J’ai mis « editor » dans la liste des support du custom type, le javascript est bien chargé et la fenêtre d’upload s’ouvre bien mais ça n’ajoute pas le lien lorsque l’on clique sur « insérer dans l’article »
Via firebug j’ai ces erreurs là:

Syntax error: invalid label:
Notice: Undefined index: post_id in /wp-admin/includes/media.php on line 1270

et

ReferenceError: formfield is not defined

Une idée pour résoudre ça ?

Cryoweb, il y a 5 années
D’ailleurs j’en profite pour apporter une correction à ton code pour la version 3.4.1 de WP, l’appel direct à wp_enqueue_script provoque une erreur, il faut importer le javascript de cette façon:


function ajout_js_pdf() {
	wp_enqueue_script(
		'custom-script',
		get_template_directory_uri() . '/js/script-pdf.js',
		 array( 'jquery','media-upload','thickbox' )
	);
}
add_action('admin_enqueue_scripts', 'ajout_js_pdf');
Baylock, il y a 5 années
Bonjour et merci pour ce tuto.
Comment faire quand on veut lier la métabox non pas à un post classique mais à un custom-post?
luc, il y a 3 années
Bonjour,
c’est vraiment très intéressant.
Comment adapter cela pour un type d’article ou de page en particulier ?
Je pense notamment aux « custom_post »
Mak, il y a 2 années
Hi !
Merci pour tes tuto métabox elles me sont vachement utiles pour comprendre ces précieuses metabox, beau boulot !
((( Je tente à mon tour d’en coder une pour attribuer un format à mon article (image, vidéo, lien url, citation, discussion etc). ;p )))

Je suis un débutant et donc je souhaiterai quelques précisions sur celle-ci.
– les codes affichés sont corrigés pour les versions wordpress en court ?
– J’ai mis tes codes/ fichiers à la racine de mon thème pour tester cette métabox mais rien ne fonctionne. Dois-je insérer ces codes/fichiers ailleurs ? ah oui la seul chose modifiée c’est la valeur ‘post’ en ‘portfolio’ afin que cette métabox apparaisse dans la section portfolio de mon theme.

Merci.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Publié le 06 juin 2012
par Willy Bahuaud
Catégorie Développement WordPress