Améliorer le choix des dispositions dans un contenu flexible ACF

Je pense que tout est parti d’un screenshot que j’ai vu passer sur le slack WordPress-fr et où je me suis dit que ça serait génial d’avoir la même chose pour nos contributeurs 😍

Au boulot, nous adorons les contenus flexibles ACF mais il faut bien se l’avouer, la popin pour choisir les dispositions n’est pas très « sexy » pour l’utilisateur final.

Je pensais que cela serait assez simple car comme nous le savons tous, ACF est très extensible et bien codé, sauf qu’il a fallu réfléchir un peu plus que prévu 😅 
ACF ne propose pas encore la possibilité d’ajouter une option à une disposition d’un contenu flexible. Pour les curieux, ça se passe ici : plugins/advanced-custom-fields-pro/pro/fields/class-acf-field-flexible-content.php#L564-648 et vraiment aucun hook dans les parages… De toute façon, en y réfléchissant un peu, rajouter une image depuis l’interface qu’il faudrait ensuite stocker dans la médiathèque (alors que c’est déjà assez le bordel) ou ailleurs n’est pas une idée si simple que ça.

Alors regardons du côté de la popin qui affiche cette fameuse liste des dispositions :

<script type="text-html" class="tmpl-popup"><?php 
	?><div class="acf-fc-popup"><ul><?php foreach( $layouts as $layout ): 
		
		$atts = array(
			'href'			=> '#',
			'data-layout'	=> $layout['name'],
			'data-min' 		=> $layout['min'],
			'data-max' 		=> $layout['max'],
		);
		
		?><li><a <?php acf_esc_attr_e( $atts ); ?>><?php echo $layout['label']; ?></a></li><?php 
	
	endforeach; ?></ul></div>
</script>

Toujours pas de hook mais nous savons maintenant qu’il va falloir bidouiller le nom de nos dispositions 😂 Mais attention cette opération n’est pas si simple que ça. La documentation d’ACF acf/fields/flexible_content/layout_title nous dit que cette donnée $layout['label'] est aussi utilisée pour composer le titre principal pour nos dispositions…

Alors comment fait-on ?

Code

Dans un premier temps, nous amenons tout le luxe dont nous aurons besoin pour la personnalisation des dispositions de nos contenus flexibles. Nous pourrons donc choisir :

  • le répertoire qui contient les images pour tous les champs ou pour chaque champ individuellement ;
  • l’extension de l’ensemble des images (on ne mélange pas les formats) ;
  • l’URL spécifique pour une disposition en particulier.

Et nous disposerons de données supplémentaires sur chaque disposition comme l’url de l’image de remplacement et le titre.

/**
 * Improve client’s experience by providing placeholder images instead of basic texts
 * to choose layouts in flexible content fields.
 *
 * @param array $field The field settings array. This can be modified and then returned
 * @return array $field The field settings array
 */
function swp_acf_override_flexible_content_layout_labels( $field ) {
    if ( $field['type'] != 'flexible_content' || (isset( $field['layout'] ) && empty( $field['layout'] )) ) {
        return $field;
    }

    $placeholders_rel_path = apply_filters( "swp_acf/fields/flexible_content/placeholders_rel_path", '', $field );
    $placeholders_rel_path = apply_filters( "swp_acf/fields/flexible_content/placeholders_rel_path/name={$field['_name']}", $placeholders_rel_path, $field );
    $placeholders_rel_path = apply_filters( "swp_acf/fields/flexible_content/placeholders_rel_path/key={$field['key']}", $placeholders_rel_path, $field );
    $placeholders_rel_path = rtrim( $placeholders_rel_path, '/' );

    if ( $placeholders_rel_path == '' ) {
        return $field;
    }

    $placeholders_extension = apply_filters( "swp_acf/fields/flexible_content/placeholders_extension", 'jpg', $field );
    $placeholders_extension = trim( $placeholders_extension, '.  \t\n\r\0\x0B' );
    $theme_file_uri = get_theme_file_uri();

    foreach ( $field['layouts'] as $key => $layout ) {
        $old_label = $field['layouts'][ $key ]['label'];

        $palceholder_url = $theme_file_uri . '/' . $placeholders_rel_path . '/' . $layout['name'] . '.' . $placeholders_extension;
        $palceholder_url = apply_filters( "swp_acf/fields/flexible_content/placeholder_url/name={$field['_name']}", $palceholder_url, $layout['name'], $layout, $field );
        $palceholder_url = apply_filters( "swp_acf/fields/flexible_content/placeholder_url/key={$field['key']}", $palceholder_url, $layout['name'], $layout, $field );

        $field['layouts'][ $key ]['title'] = $old_label;
        $field['layouts'][ $key ]['placeholder'] = $palceholder_url;
        $field['layouts'][ $key ]['label'] = '<img src="' . $palceholder_url . '">';
    }

    return $field;

}
add_filter( 'acf/prepare_field/type=flexible_content', 'swp_acf_override_flexible_content_layout_labels', 9, 1 );

Sans risque, nous rendons son titre à la disposition si notre nouvelle donnée existe. Pas question d’aller afficher une image à la place du titre 😉

/**
 * Reset the text (HTML) displayed at the top of each flexible content layout
 * with the first textual label instead of keeping the image placeholder.
 *
 * @param string $title  The layout title text. Defaults to the layout title
 * @param array  $field  The flexible content field settings array
 * @param array  $layout The current layout settings array
 * @param int    $i      The current layout index. The first layout index is 0
 * @return string $title The layout title text
 */
function swp_acf_reset_flexible_content_layout_title( $title, $field, $layout, $i ) {
    if ( isset( $layout['title'] ) && ! empty( $layout['title'] ) ) {
        return $layout['title'];
    }

    return $title;
}
add_filter( 'acf/fields/flexible_content/layout_title', 'swp_acf_reset_flexible_content_layout_title', 9, 4 );

Et pour finir avec quelques styles « inline », nous adaptons l’affichage des nouveaux contenus de la popin pour qu’il colle au fonctionnement par défaut établi par ACF ☺️

/**
 * Add some inline style to correctly display layout's image placeholder 
 * and follow the default ACF behaviour in the flexible content field popup.
 *
 * @param void
 * @return void
 */
function swp_acf_add_flexible_content_layout_placeholder_styles() {
    $inline_styles = <<<CSS
.acf-fc-popup a.disabled {
  cursor: default;
  pointer-events: none;
}
.acf-fc-popup img {
  display: block;
  height: auto;
  margin: -6px -5px;
  max-width: 135px;
  width: auto;
}
.acf-fc-popup /*a*/.disabled img {
  opacity: .5;
  filter: grayscale(100%);
}
.acf-fc-popup a:hover {
  background: 0;
}
.acf-fc-popup a:hover img {
  outline: 6px solid #0073aa;
}
CSS;

    wp_add_inline_style( 'acf-input', $inline_styles );
}
add_action( 'admin_enqueue_scripts', 'swp_acf_add_flexible_content_layout_placeholder_styles', PHP_INT_MAX );

Usage

function tc_acf_set_font_page_placeholders_rel_path( $rel_path ) {
    return '/assets/media/placeholders/acf/front-page/';
}
add_filter( 'swp_acf/fields/flexible_content/placeholders_rel_path/key=field_5a83f8cc4b135', 'tc_acf_set_font_page_placeholders_rel_path' );

Voila, c’est fini 😁

Edit 6 juin 2018

En écrivant ce post, je n’avais pas connaissance de ressources similaires mais je viens d’en trouver deux en même temps 😄

Ma méthode aborde le problème différemment mais au final l’utilisateur sera ravi 😘