1

I want to add class to caption shortcode, pulled from custom attachment field. I add the field via:

function add_img_class( $form_fields, $post ) {
    $form_fields['img_class_field'] = array(
        'label' => __( 'Image class', 'text-domain' ),
        'input' => 'text',
        'value' => get_post_meta( $post->ID, 'img_class_field', true )
    );
    return $form_fields;
}

add_filter( 'attachment_fields_to_edit', 'add_img_class', 10, 2 );


function save_img_class( $post, $attachment ) {
    if( isset( $attachment['img_class_field'] ) )
        update_post_meta( $post['ID'], 'img_class_field', $attachment['img_class_field']);
    return $post;
}

add_filter( 'attachment_fields_to_save', 'save_img_class', 10, 2 );

How do I dynamically add "class" attribute with the field value to caption shortcode now?

[caption ... class="img_class_field"]<img src...>[/caption]
4
  • If the image is an attachment, why are you using shortcodes to display it instead of modifying the template?
    – inarilo
    Jun 6, 2017 at 20:55
  • @inharilo if the image doesn't have a caption, I modify the image template with the class. But if the image has a caption I need to add the class to the <figure> that contains the image.
    – Runnick
    Jun 7, 2017 at 5:46
  • i;m still not clear on your requirement but you can call shortcodes inside templates, if that helps.
    – inarilo
    Jun 7, 2017 at 7:36
  • you can use do_shortcode or call directly wordpress.stackexchange.com/questions/89764/…
    – inarilo
    Jun 7, 2017 at 7:41

2 Answers 2

1

Here's a solution that leverages the shortcode_atts_{shortcode} filter, where caption is the shortcode in this case.

This works by pulling the attachment id from $out['id'] then getting the value of the class via get_post_meta( $attachment_id, 'img_class_field', true ).

With this approach, it is not necessary to modify the output of the caption shortcode in the editor. The adding of the custom class is handled behind the scenes. It also still works if a user has manually added a class attribute to the caption shortcode.

/**
 * Adds class to caption shortcode output via img_class_field attachment meta.
 *
 * @param array  $out       The output array of shortcode attributes.
 * @param array  $pairs     The supported attributes and their defaults.
 * @param array  $atts      The user defined shortcode attributes.
 * @param string $shortcode The shortcode name.
 */
add_filter( 'shortcode_atts_caption', 'wpse_shortcode_atts_caption', 10, 4 );
function wpse_shortcode_atts_caption( $out, $pairs, $atts, $shortcode ) {
    // Get the attachment id. It should be available via $out['id'], but
    // it will be in the format 'attachment_xxxx' where xxxx is the id.
    // We'll try to get the id portion and we'll bail if this doesn't work.
    $attachment_id = isset( $out['id'] ) && ( $out['id'] ) ? $out['id'] : false;
    $attachment_id = (int) preg_replace( '/^attachment_/', '', $attachment_id );
    if ( ! $attachment_id ) {
        return $out;
    }

    // Get the custom image class and add it to the existing classes
    $extra_image_class = get_post_meta( $attachment_id, 'img_class_field', true );
    if ( $extra_image_class ) {
        $spacer = isset( $out['class'] ) && ( $out['class'] ) ? ' ' : '';
        $out['class'] .= esc_attr( $spacer . $extra_image_class );
    }

    return $out;
}

Usage

Custom classes added to attachment meta data (I used 2 for good measure):

some-class another-class

Example of unmodified caption shortcode added via media modal:

[caption id="attachment_2397" align="alignnone" width="480"]<a href="http://example.com/wp-content/uploads/2017/05/image.jpg"><img src="http://example.com/wp-content/uploads/2017/05/image.jpg" alt="" width="480" height="360" class="size-full wp-image-2397" /></a> This is the caption![/caption]

Example output 1: (theme supports HTML5 for captions):

<figure id="attachment_2397" style="width: 480px" class="wp-caption alignnone some-class another-class">
    <a href="http://example.com/">
        <img src="http://example.com/wp-content/uploads/2017/05/image.jpg" alt="" width="480" height="360" class="size-full wp-image-2397" srcset="http://example.com/wp-content/uploads/2017/05/image.jpg 480w, http://example.com/wp-content/uploads/2017/05/image-300x225.jpg 300w" sizes="(max-width: 480px) 100vw, 480px">
    </a>
    <figcaption class="wp-caption-text">This is the caption!</figcaption>
</figure>

Example output 2: (theme does not support HTML5 for captions):

<div id="attachment_2397" style="width: 490px" class="wp-caption alignnone some-class another-class">
    <a href="http://example.com/wp-content/uploads/2017/05/image.jpg">
        <img src="http://example.com/wp-content/uploads/2017/05/image.jpg" alt="" width="480" height="360" class="size-full wp-image-2397" srcset="http://example.com/wp-content/uploads/2017/05/image.jpg 480w, http://example.com/wp-content/uploads/2017/05/image-300x225.jpg 300w" sizes="(max-width: 480px) 100vw, 480px">
    </a>
    <p class="wp-caption-text">This is the caption!</p>
</div>
1

My idea behind this would be to actually use a custom field call within the shortcode itself, then add a filter shortcode_atts_{$shortcode} to process said custom field and add it to class to return it.

{shortcode} in this case would be caption since that's what we're building off.

add_filter( 'shortcode_atts_caption', 'caption_custom_field_class' );

function caption_custom_field_class( $out, $pairs, $atts, $shortcode ) {
    if ( !empty( $atts['custom_field'] ) && get_post_meta( sanitize_text( $atts['custom_field'] ), $atts['id'], true ) ) {
             $out['class'] .= ( !empty( $out['class'] ) ? ' ' : '' ) . get_post_meta( sanitize_text( $atts['custom_field'] ), $atts['id'], true );
        }
    }
    return $out;
}

So, what we're doing is utilizing another variable field called 'custom_field' to pass our custom field name.

[caption custom_field="mycustomfieldname"]<img src=".... />[/caption]

The function checks if custom_field shortcode attribute is empty or not, and checks if you can retrieve any post meta based on the passed ID. If those are both true, then adds it to the $out['class'] with a space depending on whether or not $out['class'] is empty.

I may need to update this answer, because I started writing here, and I'm going to try it in a live environment.

1
  • Looks like we were tackling this at the same time in similar ways :-) One little thing I noticed here is that you've got the two strings in your add_filter() call flipped around. Jun 6, 2017 at 19:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.