2

I'm trying to grab all image id's that are associated with the [gallery] shortcode that are listed as exclude. For example: if my post has [gallery exclude="1,2,3"] I'd like to get a variable that would echo like this echo $excludes; result 1,2,3 thank you for any help you can offer.

3 Answers 3

3

The task

It's not as easy, as it looks at a first glance. The main problem is, that you can define your own [gallery] shortcode and simply override the default. Actually that's what some themes are doing (blame you ThemeForest authors!). In this case, simply hooking into post_gallery won't work in every case.

Drawbacks for post_gallery

The problem is that both the shortcode, as well as the core callback can be overridden and therefore every callback will simply be knocked out.

Drawbacks for the_content

If you're switching to another filter and take the raw output, then you'll have to deal (another time) with the Regex. This is slow and mostly won't be simple.

So what to do now? Easy. Interact with the global $shortcode_tags. It holds the callback as second argument, so it's actually not too hard to determine in which case we are. Then we can simply switch on demand. This way we have a fine balance between reliability and performance.

Build a base plugin

Here's a plugin that defines an abstract class (one that must get extended in order to work). There're three things that need definition in the child class:

  • $part - the part you want to retrieve
  • $type - the type of match you need. Valid are digit/alphanumeric/alpha
  • process_atts() - the method that processes your output - whatever you want to do with the result

Just upload this via (S)FTP into your plugins folder and activate.

<?php
/** Plugin Name: (#70451) »kaiser« Get Gallery attributes (Base) */
if ( ! class_exists( 'wpse70451_extract_gallery_atts' ) )
{

abstract class wpse70451_extract_gallery_atts
{
    public $atts  = array();

    public $error;

    // Must get defined in extending class
    public $part;
    public $type;

    public static function init()
    {
        is_null( self :: $instance ) AND self :: $instance = new self;
        return self :: $instance;
    }

    public function __construct()
    {
        ! isset( $this->part ) AND new WP_Error();

        add_action( 'loop_start', array( $this, 'error_handler' ) );

        // The value of the array is the callback fn name
        // If it's the default, then we're on the safe side,
        // as the core fn is no pluggable and can't get overridden.
        if ( 'gallery_shortcode' === $GLOBALS['shortcode_tags'] )
        {
            add_filter( 'post_gallery', array( $this, 'get_gallery_atts' ), 0, 2 );
        }
        // Else we have to go with a slower regex
        else
        {
            add_filter( 'the_content', array( $this, 'get_gallery_ids' ), 0 );
        }
    }

    public function error_handler()
    {
        if (
            ! in_array(
                 $this->type
                ,array(
                     'digit'
                    ,'alphanumeric'
                    ,'alpha'
                 )
            )
            OR ! empty( $this->type )
        )
            return new WP_Error( 
                 'invalid_type'
                ,__( 'Invalid type set.', 'wpse70451_textdomain' )
                ,__FILE__
            );
    }

    public function __toString()
    {
        $is_error = $this->error;

        if ( ! is_wp_error( $is_error ) )
            return;

        // No error message for Guests or Subscribers
        // Assuming that no one has activated caching plugins when debugging
        // and not set WP_DEBUG to TRUE on a live site
        if ( 
            ! is_user_logged_in()
            AND ! current_user_can( 'edit_posts' ) 
            AND ( ! defined( 'WP_DEBUG' ) OR ! WP_DEBUG )
        )
            return '';

        // Error output for development
        return "{$is_error->get_error_message( 'invalid_type' )}: {$is_error->get_error_data()}";
    }

    public function get_gallery_ids( $content )
    {
        $pattern = get_shortcode_regex( $content );
        preg_match_all( "/{$pattern}/s", $content, $matches );
        $atts = explode( " ", array_shift( $matches[3] ) );
        foreach ( $atts as $att )
        {
            if ( strstr( $att, $this->part ) )
                break;
        }
        preg_match_all( $this->get_regex( $this->type ), trim( $att ), $atts );
        $this->atts = array_filter( $atts );

        return $content;
    }

    // Build pattern
    public function get_regex( $type )
    {
        switch ( $type )
        {
            case 'digit' :
                $pattern_atts = '/(\d*)/';
                break;

            case 'alphanumeric' :
                $pattern_atts = '/([A-Za-z0-9]*)/';
                break;

            case 'alpha' :
                $pattern_atts = '/([A-Za-z]*)/';
                break;

            default :
                // Add a method name `get_pattern()` to the extending class
                // to work with a custom regex pattern.
                if (  method_exists( $this, 'get_pattern' ) )
                {
                    $pattern_atts = $this->get_pattern();
                    break;
                }
                $pattern_atts = $this->get_regex( 'alphanumeric' );
                break;
        }

        return $pattern_atts;
    }

    public function get_gallery_atts( $content, $atts )
    {
        $this->atts = $atts[ $this->part ];

        // Allow overrides to trigger
        // at this point we already got what we need
        return $content;
    }

    // Must get defined in extending class
    public abstract function process_atts() {}
} // END Class

} // endif;

Handle a task, get a child

Here you see the actual processing plugin. First it hooks itself statically to the init hook and then runs the parents __construct() method to retrieve the attributes.

Then you need to define what attributes you want to retrieve (see the classes properties $part and $type, which already default to what you asked for).

The last two decisions you need to make are

  1. What do I want to do with my result? See process_atts()
  2. When do you want to output the result. See __construct() and where process_atts() is hooked in.

It's as easy as that.

If you need a custom regex, just add a method named get_regex() to your extending class and return your custom pattern. Then set the $type to and empty string '' and you're ready to go.

<?php
/** Plugin Name: (#70451) »kaiser« Get Gallery excludes */

if ( ! class_exists( 'wpse70451_extract_gallery_excludes' ) )
{
    add_action( 'init', array( 'wpse70451_extract_gallery_excludes', 'init' ) );

final class wpse70451_extract_gallery_excludes extends wpse70451_extract_gallery_atts
{
    public static $instance;

    public $part = 'exclude';

    public $type = 'digit';

    public static function init()
    {
        is_null( self :: $instance ) AND self :: $instance = new self;
        return self :: $instance;
    }

    public function __construct()
    {
        parent :: __construct();

        // Example hook: `loop_end`
        add_action( 'loop_end', array( $this, 'process_atts' ) );
    }

    public function process_atts()
    {
        $markup = '';
        // Do something with $this->atts;
        return print $markup;
    }
} // END Class

} // endif;

Alter what you need and then again: Just upload this via (S)FTP into your plugins folder and activate.

6
  • Thank you for your help. What if all I wanted to do was return the list in a variable I could use in an $args array? $args = array( 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order'=> 'ASC', 'posts_per_page' => 3, 'post_parent' => get_the_ID(), 'exclude' => $myvariablehere
    – sosukeinu
    Oct 25, 2012 at 15:36
  • Just add this inside process_atts() in the extending class (child Plugin), exactly as I described it in the explanation.
    – kaiser
    Oct 25, 2012 at 17:02
  • I'm getting Fatal error: Properties cannot be declared abstract in /wp-content/plugins/get_gallery_attributes_base.php on line 13
    – sosukeinu
    Oct 25, 2012 at 20:02
  • Good catch. It's not tested as I wrote both plugins off of my head - fixed. Just got no time to add a proper error handling for one of those, but this shouldn't keep you back from using it.
    – kaiser
    Oct 25, 2012 at 23:13
  • $this->atts is returning this Array ( [0] => Array ( [0] => ) [1] => Array ( [0] => ) ) and if I assign it to $excludes and echo it, I get nothing.
    – sosukeinu
    Oct 26, 2012 at 12:28
1

If you also need to display the content on the same page, what about using the post_gallery filter? You won't need any regex then

add_filter('post_gallery', 'gallery_shortcode_excludes', 10, 2);
function gallery_shortcode_excludes($content, $attr) {
    $excludes = $attr['excludes'];
    // maybe also save it in some global/class/static variable

    //  return empty string to let wordpress continue to the shortcode output
    return '';
}
0
1

It took me a while to find a solution that worked for me, but since all I was looking for was the delimited list of attachment id's associated with a certain attribute like exclude or hide, this worked for me:

# Grab the list of "hide" attribute
$regex_pattern = get_shortcode_regex();
preg_match ('/'.$regex_pattern.'/s', $post->post_content, $regex_matches);
    if ($regex_matches[2] == 'gallery') :
     $attribureStr = str_replace (" ", "&", trim ($regex_matches[3]));
     $attribureStr = str_replace ('"', '', $attribureStr);
     //  Parse the attributes
      $defaults = array (
      'hide' => '1',
     );
    $attributes = wp_parse_args ($attribureStr, $defaults);
     if (isset ($attributes["hide"])) :
      $excludeID = get_post_thumbnail_id() . ',' . $attributes["hide"];
     else :
      $excludeID = get_post_thumbnail_id();
   endif;
endif;

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.