Bloc-note d'un développeur web
Dans : Outils, trucs et astuces
23 sept 2010J’ai récemment eu besoin d’afficher de manière élégante les pièces jointes à des articles et des pages. Seulement, avec un WordPress qui autorise l’ajout de documents autres que des images/vidéos et rien pour les insérer proprement dans un contenu, la tâche est loin d’être aisée.
Un coup de XHTML et roule ma poule !
Oui ! Si j’étais le rédacteur du site (comme ici). Mais il n’en est rien. Il fallait une solution utilisable depuis l’éditeur WYSIWYG, sans avoir à intervenir sur la source XHTML (genre pour ajouter un div, une image, des classes CSS, etc).
Voici donc un shortcode WordPress qui affiche la liste des pièces jointes.

Le shortcode en action
Le shortcode présenté dans ce billet nécessite la présence de la classe abstraite d’aide à la création de shortcode PPM_Shortcode. Si ce n’est pas déjà fait, récupérez-là et rendez-la disponible pour la classe AttachmentsListShortCode.
Copiez le code qui suit dans le fichier functions.php de votre thème1.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 | <?php /** * Add the new shortcode `attachments`. */ $attachments_list = new AttachmentsListShortCode('attachments'); $attachments_list->register(); /** * Shortcode to display a list of attachments of the current post. * * Attributes to use as follows: * 'exclude' - Default is none. List of IDs of the attachments to exclude of search. * 'orderby' - Default is 'title ASC'. How to order the attachments. * 'style' - Default is 'large'. Presentation style to use. * * @author Mehdi Kabab <http://pioupioum.fr/> * @copyright Copyright (C) 2010 Mehdi Kabab * @license http://www.gnu.org/licenses/gpl.html GNU GPL version 3 or later * @version 1.0.0 * @link http://pioupioum.fr/outils-astuces/wordpress-shortcode-afficher-fichiers-joints.html */ class AttachmentsListShortCode extends PPM_Shortcode { /** * Default options. * * @var array **/ protected $_default_options = array( 'exclude' => array(), 'orderby' => 'title ASC', 'style' => null, ); /** * Keys allowed in the query ORDER BY clause. * * @param array */ protected $_allowed_orderby = array( 'mime_type' ); /** * Directory of MIME icons. * The path is relative to the current theme. * * @var string */ private $_icons_dir; /** * Clause ORDER BY to be applied to sorting. * * @var string */ private $_orderby = null; /** * Stack of the presentation styles for the attachments. * * @var ArrayObject **/ private $_styles; /** * Constructor. * * @param string $tag Shortcode tag to be searched in post content. * @param string $icons_dir Directory of MIME icons. */ public function __construct($tag, $icons_dir = 'images/mime') { parent::__construct($tag); $this->setIconsDirectory($icons_dir); $this->_styles = new ArrayObject(array(), ArrayObject::ARRAY_AS_PROPS); foreach ($this->_getDefaultStyles() as $tpl => $data) { if (isset($data['default'])) { $default = (boolean) $data['default']; unset($data['default']); } else { $default = false; } $this->setStyle($tpl, $data, $default); } } /** * Retrieve directory of MIME icons. * * @return string */ public function getIconsDirectory() { return $this->_icons_dir; } /** * Set the directory of MIME icons. * * @return PPM_Shortcode */ public function setIconsDirectory($dir) { $this->_icons_dir = trim($dir, '/') . '/'; return $this; } /** * Retrieve the stack of the presentation styles. * * @return ArrayObject */ public function getStyles() { return $this->_styles; } /** * Set a presentation style of the attachments. * * @param string $name Presentation style name. * @param array $data Style data. * @param boolean $default True for set this style as default style. * @return PPM_Shortcode * @throws Exception If the data are not valid. */ public function setStyle($name, $data, $default = false) { if (!$this->_isValidStyle($data)) { throw new Exception(sprintf('Data for the style `%s` are not valid!', $name)); } $this->_styles[$name] = new ArrayObject($data, ArrayObject::ARRAY_AS_PROPS); if (true === $default) { $this->setStyleByDefault($name); } return $this; } /** * Retrieve name of the presentation style used by default. * * @return string */ public function getStyleByDefault() { return $this->_default_options['style']; } /** * Set name of the presentation style to use by default. * * @param string $name * @return PPM_Shortcode */ public function setStyleByDefault($name) { $this->_default_options['style'] = $name; return $this; } /** * [filter] Edit the clause ORDER BY to allow sorting by mime_type. * * @param string $orderby The ORDER BY clause. * @return string The new ORDER BY clause. */ public function filterOrderby($orderby) { if (null !== $this->_orderby && in_array($this->_orderby, $this->_allowed_orderby)) { $orderby = preg_replace('#^([^\.]+\.)(\w+)(\s+ASC|DESC)$#i', '$1post_' . $this->_orderby . '$3', $orderby); } return $orderby; } /** * @see PPM_Shortcode::_process() */ protected function _process($attributes, $content = null) { if ($this->getStyles()->offsetExists($this->getOption('style'))) { $style = $this->getOption('style'); } else { $style = $this->getStyleByDefault(); } // Order and Order by clauses list($orderby, $order) = explode(' ', $this->_default_options['orderby']); if (false !== strpos($this->getOption('orderby'), ' ')) { list($orderby, $order) = explode(' ', $this->getOption('orderby')); } else { $orderby = $this->getOption('orderby'); } $this->_orderby = $orderby; // Retrieve the attachments $attachments = get_children(array( 'post_parent' => get_the_ID(), 'post_type' => 'attachment', 'numberposts' => -1, 'exclude' => $this->getOption('exclude'), 'orderby' => $this->_orderby, 'order' => ('ASC' === strtoupper($order)) ? 'ASC' : 'DESC', 'exclude' => $this->getOption('exclude'), 'suppress_filters' => false // Allows to edit the query )); if (false === $attachments) { return ''; } // Edit the query add_filter('posts_orderby', array($this, 'filterOrderby')); $result = ''; $tpl = $this->getStyles()->$style; foreach ($attachments as $attachment) { // Do not display image attachments if (0 === strpos($attachment->post_mime_type, 'image/')) { continue; } // Determine the filename $file_type = strtr($attachment->post_mime_type, '/-', '__'); $file_name = $this->getIconsDirectory() . $file_type . '.png'; if (file_exists(get_stylesheet_directory() . '/' . $file_name)) { $icon_file = get_bloginfo('stylesheet_directory', false) . '/' . $file_name; } else { $icon_file = wp_mime_type_icon($attachment->ID); } if (function_exists('getimagesize')) { $size_attr = getimagesize($icon_file); $size_attr = $size_attr[3]; } else { $size_attr = ''; } $file_path = get_attached_file($attachment->ID); $context = array( 'ID' => $attachment->ID, 'filename' => basename($attachment->guid), 'file_date' => mysql2date(get_option('date_format'), $attachment->post_date), 'file_size' => esc_attr(size_format(filesize($file_path))), 'file_type' => $attachment->post_mime_type, 'file_url' => esc_attr($attachment->guid), 'icon_atts' => $size_attr, 'icon_path' => esc_attr($icon_file), 'title' => esc_attr(get_the_title($attachment->ID)), 'label_filename' => __('File name:'), 'label_file_date' => __('Upload date:'), 'label_file_size' => __('Size:') ); // Hook for customize the context $context = apply_filters('attachments_list_context', $context, $attachment, $this->getStyles()->$style); $result .= $this->_compileMarkup($tpl, $context); } return $tpl->before . do_shortcode($content) . $result . $tpl->after; } /** * Performs substitutions in the markup of the selected presentation style. * * @param ArrayObject $tpl * @param array $context * @return string The markup after substitutions. */ protected function _compileMarkup($tpl, $context) { $markup = $tpl->markup; foreach ($context as $key => $value) { $markup = str_replace('%' . $key . '%', $value, $markup); } return $markup; } /** * Retrieve the default stack of the presentation styles. * * Declare the `medium`, `small` and `large` presentation styles. * * @return array */ protected function _getDefaultStyles() { return array( 'medium' => array( 'before' => '<div class="attachments-list-medium">', 'after' => '</div>', 'markup' => '<div class="attachment-item attachment-%ID%">' . '<a href="%file_url%" title="%title% (%file_size%)" rel="attachment">' . '<img src="%icon_path%"%icon_atts% alt=""/><small>%title%</small></a>' . '</div>' ), 'small' => array( 'before' => '<div class="attachments-list-small">', 'after' => '</div>', 'markup' => '<div class="attachment-small-icon attachment-%ID%">' . '<img src="%icon_path%"%icon_atts% alt=""/>' . '<a href="%file_url%" title="%title%" rel="attachment">' . '%title%</a> (%file_size%)' . '</div>' ), 'large' => array( 'before' => '<div class="attachments-list-large">', 'after' => '</div>', 'markup' => '<dl class="attachment-item attachment-%ID%">' . '<dt><a href="%file_url%" title="%title%" rel="attachment">' . '<img class="alignleft" src="%icon_path%"%icon_atts% alt=""/>' . '%title%</a></dt>' . '<dd><span>%label_filename%</span> %filename%</dd>' . '<dd><span>%label_file_date%</span> %file_date%</dd>' . '<dd><span>%label_file_size%</span> %file_size%</dd>' . '</dl>', 'default' => true ) ); } /** * Test if the data of a presentation style are well formed. * * @param array $data * @return boolean */ protected function _isValidStyle($data) { $ok = 3 === count($data) && isset($data['before']) && isset($data['after']) && isset($data['markup']); return $ok; } } ?> |
Il vous faut également intégrer à votre thème les styles CSS qui suivent. Ceux-ci demeurant être une suggestion.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | .attachments-list-large, .attachments-list-medium, .attachments-list-small { margin-bottom: 1em; } .attachments-list-large .attachment-item img, .attachments-list-medium .attachment-item img, .attachments-list-small .attachment-item img { border: none; } .attachments-list-large .attachment-item dl { margin: 0 0 1.5em 1em; } .attachments-list-large .attachment-item dt { font-weight: bold; } .attachments-list-large .attachment-item dd { font-size: 0.85em; margin-left: 1.5em; } .attachments-list-large .attachment-item img { display: inline; float: left; } .attachments-list-large .attachment-item img { margin-right: 1em; } .attachments-list-medium .attachment-item { display: inline-table; margin-bottom: 1.5em; padding: 0 0.15em; text-align: center; vertical-align: top; width: 10em; } .attachments-list-medium .attachment-item a { text-decoration: none; } .attachments-list-medium .attachment-item small { display: block; font-size: 0.85em; } .attachments-list-small img { margin-right: 5px; vertical-align: text-bottom; height: 16px; width: 16px; } |
Ou si vous utilisez SCSS :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | .attachments-list-large, .attachments-list-medium, .attachments-list-small { margin-bottom: 1em; .attachment-item { img { border: none; } } } .attachments-list-large { .attachment-item { $space: 1em; dl { margin: 0 0 ($space + 0.5em) $space; } dt { font-weight: bold; } dd { font-size: 0.85em; margin-left: ($space + 0.5em); } img { display: inline; float: left; margin: { right: $space; } } } } .attachments-list-medium { .attachment-item { display: inline-table; margin-bottom: 1.5em; padding: 0 0.15em; text-align: center; width: 10em; a { text-decoration: none; } small { display: block; font-size: 0.85em; } } } .attachments-list-small { img { margin-right: 5px; vertical-align: text-bottom; height: 16px; width: 16px; } } |
Le shortcode attachments propose trois formats d’affichage des pièces jointes : medium, large (par défaut) et small.

[attachments style="small"]

[attachments style="medium"]

[attachments style="large"]
[attachments]
Ici, nous excluons de la liste les documents ayant les identifiants 53 et 56.
[attachments exclude="53,56"]
[attachments orderby="date"]
[attachments orderby="date DESC"]
[attachments orderby="mime_type"]
Les paramètres peuvent être cumulés, sans ordre précis.
[attachments style="large" exclude="53" orderby="date"]
Le shortcode attachments existe aussi en forme englobante. Tout contenu englobé préfixera la liste des pièces jointes générées.

[attachments]
<h2>Avec un contenu</h2>
<p>Ce shortcode englobe le titre qui précède et ce paragraphe.</p>
[/attachments]
Voici la liste des paramètres acceptés par le shortcode attachments :
exclude
(optionnel) La liste des ID séparés par des virgules des documents à exclure de la liste. Vide par défaut. Toutes les pièces jointes au contenu sont affichées.
orderby
(optionnel) Le type de tri appliqué à la liste. Par défaut title ASC, les document sont triés par titre et par ordre alphabétique.
style
(optionnel) Le type de présentation de la liste. Trois formats sont disponibles : small, medium et large (par défaut). Se reporter aux exemples pour avoir des aperçus.
Afin de correspondre aux besoins du plus grand nombre, le shortcode attachments offre diverses possibilités de personnalisation.
Si vous souhaitez afficher une simple liste ordonnée des pièces jointes, déclarez un nouveau style au shortcode comme suit :
1 2 3 4 5 6 7 8 9 10 | <?php $attachments_list = new AttachmentsListShortCode('attachments'); $ol_style = array( 'before' => '<ol class="attachments-list-ol">', 'after' => '</ol>', 'markup' => '<li><a href="%file_url%" rel="attachment">%title%</a></li>' ); $attachments_list->setStyle('ol', $ol_style) ->register(); ?> |
Puis, appelez-le dans un billet :
[attachments style="ol"]

Notez que la déclaration d’un style peut se faire après l’enregistrement du shortcode. La seule contrainte étant de la faire avant l’appel au shortcode par WordPress.
Si vous souhaitez définir un style de présentation à utiliser par défaut, deux possibilités :
'default' => true.setStyleByDefault($name) : $attachments_list->setStyleByDefault('ol);.Ci-après, la liste des différentes tags disponibles pour vos styles :
<img/>.Par défaut le shortcode affiche les icônes disponibles dans WordPress. Le shortcode permet d’utiliser un set d’icône personnalisé.
Il suffit pour cela de déclarer un chemin d’accès aux icônes des différents types MIME. Par défaut images/mime. Ce chemin doit être relatif au dossier du thème.
1 2 3 4 5 | <?php $attachments_list = new AttachmentsListShortCode('pièces jointes'); $attachments_list->setIconsDirectory('img/icons') ->register(); ?> |
Afin que les icônes soient récupérées, les fichiers images des types MIME doivent être de la forme type_mime.png.
C’est à dire qu’un document PDF de type MIME application/pdf aura pour icône le fichier application_pdf.png. Un document ODT de type MIME application/vnd.oasis.opendocument.text se référera quand à lui à l’image application_vnd.oasis.opendocument.text.png.
J’utilise le pack d’icônes Oxygen dans les exemples présentés de cette page.
attachments_list_contextLe filtre attachments_list_context permet de modifier le contexte qui sera utilisé pour réaliser les substitions dans le style de présentation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?php function filter_attachments_list_context($context, $attachment, $size) { // WordPress ne comportant pas de chaîne 'Size:', cette // dernière ne peut être localisée automatiquement. // Travaillant sur un site exclusivement localisé en français // on peut se permettre les deux traitements qui suivent. $context['label_file_size'] = 'Taille :'; $context['file_size'] = strtr($context['file_size'], 'B', 'o'); return $context; } add_filter('attachments_list_context', 'filter_attachments_list_context', 10, 3); ?> |
Tu méga-gères, j’en avais besoin en plus :3 Merci Mehdi !
Génial, merci beaucoup !
Excellent ce petit tuto ! je vais adopter direct
Merci à toi
Un shortcode de luxe !
C’est pas vraiment un shortcode vu la longueur, mais merci beaucoup, ça m’évitera de le coder !
Trop classe \o/. super bien pour des articles type tutoriel avec des fichiers attachés !
ps : Et moi qui croyait qu’un shortcode c’est petit bout de code…
Vraiment utile et très bien expliqué : bravo et merci !
@iDorian et @yoy la raison pour laquelle je n’ai pas empaqueté ce shortcode dans un plugin est simple : pourquoi alourdir le temps de traitement d’une page (recherche du plugin, parsing de son en-tête, ajout dans la pile des plugins alors que le shortcode sera lui-même dans une pile, etc) pour une fonctionnalité qui n’offre pas d’options depuis le backend et qui est, au final, liée au thème courant par l’ajout d’icônes et de styles de présentation ?
@pioupioum loin de moi l’idée de dire qu’il l’empaqueter dans un plugin, perso j’y connais rien a Wordpress
Après lecture du billet je pensais qu’un shortcode c’était une fonctionnalité de wordress pour insérer des balises perso… mais c’est bien censé être un petit bout de code donc… c’est quoi
la différence avec snippet ?
@yoy te connaissant je savais que tu ne parlais pas à mal
J’en ai profité pour m’étaler un peu sur le sujet. Après il est vrai qu’un shortcode (qui permet bien d’insérer des balises perso) est généralement court. Ici il est long parce que j’utilise un modèle objet avec une API de getter/setter relativement complète au lieu d’une simple fonction. Le choix du tout objet se justifie par un pari sur l’avenir : l’équipe de développeurs de WordPress finira bien par pondre une API complète en POO. Ce jour là je n’aurai qu’à adapter ma classe abstraite PPM_Shortcode pour que tous mes shortcodes personnels continuent à fonctionner
Un snippet est, à mon sens, un code copié/collé qui pourra resservir. J’évite également de fournir des explications détaillées sur les cas d’utilisation, autrement je me retrouverai avec des billets de snippet aussi longs que celui-ci
Merci infiniment, ça manquait
ou je dois coller ce code exactement, je commence avec wp.
classe abstraite d’aide à la création de shortcode PPM_Shortcode
Merci
Vous pouvez coller les deux codes dans le fichier
functions.phpde votre thème WordPress.merci bcp pour cet outil !
est ce possible de le voir un jour sous forme de plug ?
l’insertion dans functions.php implique qu’a chaque maj ca saute non ?
merci par avance
sebastien
super ce tutorial c’est ce que je cherchais depuis longtemps par contre j’ai au-dessus de mes fichiers pdf un message d’erreur “Warning: getimagesize() [function.getimagesize]: URL file-access is disabled in the server configuration in /homepages/13/d313814125/htdocs/julyos/Julyos/wp-content/themes/contrast-test/functions.php on line 445” et je ne trouve pas la solution, pourriez-vous m’aider?
@manu il vous faut activer l’option allow_url_fopen depuis le fichier
php.ini, sinon depuis la configuration Apache de votre site à l’aide de la directive `php_admin_flag :Si cela vous est impossible — ce qui suit n’a pas été testé — modifiez dans la méthode
AttachmentsListShortCode::process()les lignes 237 et 238par
@sebastien à chaque changement de thème uniquement. Je ne le publierai pas sous la forme de plugin pour les raisons avancées.
Bonjour,
“Si ce n’est pas déjà fait, récupérez-là et rendez-la disponible pour la classe AttachmentsListShortCode”
Comment la rendre disponible ?
@Anita il y a deux manières de procéder :
PPM_Shortcodedans le même fichier que celui contenant la classeAttachmentsListShortCode;PPM_Shortcode.class.php) situé dans votre thème puis de l’inclure dans le fichier qui contient la classeAttachmentsListShortCodeà l’aide de l’instruction require, comme suit :require __DIR__ . '/PPM_Shortcode.class.php';.Je comprend ton avis de développeur sur l’intégration d’une fonctionnalité à un thème, moi même, c’est ce que je fais.
Seulement, un utilisateur lambda va (si il veut vraiment utiliser cette fonctionnalité) copier tes classes (et CSS), ça ok. Mais lorsque son thème va se mettre à jour, il devra ré-inserer ces lignes de code dans son thème, si il se souvient encore de l’endroit ou il avait trouvé cette fonctionnalité. Exit aussi les mises à jour, un autre avantage de la pluginisation. Et la visibilité de son plugin sur le site de Wordpress, par exemple.
Bref, c’est pour cela que les fonctionnalités sont proposées généralement sous forme de plugin.
Vraiment génial. C’est parfaitement présenté et c’est complet. J’approuve totalement et je compte utiliser ce shortcode pour mes projets WordPress. Merci.
En fait, je ne parviens pas à le faire fonctionner correctement avec WPML. Mon langage par défaut (fr) permet de créer correctement la liste des pièces jointes. Par contre, dans le deuxième langage, ca ne fonctionne pas. L’article est indiqué comme n’ayant aucune pièce jointe dans le TinyMCE. Insérer un code comme [attachments docid=272] ne génère rien non plus, je pense que c’est normal si le TinyMCE considère l’article sans pièce jointe.