1

This is both a WordPress and a PHP question, since a PHP programmer may know a technique that the WP group doesn't.

I have been attempting to write a shortcode that fires the Foundation Reveal code for use inline with the WP Editor. I envision having it work like this:

[reveal size="medium" bg="true" slug="page-slug"]Open a modal with this link![/reveal]

The shortcode specifies the modal size, whether or not to use the black background, and which page slug to incorporate content from in a custom single-page loop (which allows us to use a non-public custom post type to hold the modal data). There are additional attributes but I haven't written them in yet in the interest of simplicity.

Here's where it gets tricky.

A modal comes in two parts, the link and the hidden DIV. The DIV typically goes at the footer of the site. I could put it inline and solve my problem, but that would play havoc on Google's webcache. No, the modal needs to be an aside in the footer.

So I got a bit clever. I had the short code return the target link, but I also had it register a hook (placed in footer.php) to which a content DIV is attached. Everything was working fine until I realized a problem with my variable scope. First, here's my current code:

/**
 * Foundation Reveal Shortcode
 */

// Create an action to be placed in the site footer. We will target this with the second half of the function
function foundation_reveal() {
    do_action('foundation_reveal');
}

// Set up the shortcode for the modal
function reveal_setup($atts, $content) {

    // Extract the attributes
    extract(shortcode_atts(array(
        'size'  =>  'medium',
        'slug'  =>  'dummy',
        'bg'    =>  'reveal-modal'
    ), $atts));

    $modal_link  = '<a href="#" data-reveal-id="' . $slug . '">';
    $modal_link .= $content;
    $modal_link .= '</a>';

    if ($slug == 'dummy') {
        $the_modal = '<div id="' . $slug . '" class="' . $size . ' ' . $bg . '" data-reveal>' . 'reminder/dummy content goes here' . '</div>';
    } else {
        $the_modal = '<div id="' . $slug . '" class="' . $size . ' ' . $bg . '" data-reveal>' . 'post loop based on page slug goes here' . '</div>';
    }

    function reveal_content($the_modal) {
        echo $the_modal;
    }

    add_action('foundation_reveal', 'reveal_content');

    return $modal_link;
}


/**
 * Setup shortcodes for this theme
 */

// Register all shortcodes
function gmfi_shortcodes() {
    add_shortcode('reveal', 'reveal_setup');
}

// Add shortcodes to the init hoook
add_action( 'init', 'gmfi_shortcodes');

And since it's kind of helpful to see this whole thing color coded by my IDE, here's a link to that image.

The problem will be evident to any PHP programmer right away. In order to pass something like $slug to the secondary function, which gets hooked into the footer, I would need to pass the variable by reference.

i.e., I would have to do this:

add_action('foundation_reveal', 'reveal_content($slug)');

That, unfortunately, is not how WordPress works.

So here's the question then... can you think of another way to pass that variable by reference? Because if not, I may have to suck it up and write a custom hook/action script. That option seems wasteful if there is a way to do it using the available WordPress components. But, then again, if there was another way, one of the WordPress/Foundation plugin suites probably would have done it already.

Either way, I need you all to help me figure out my next step.

Thanks so much for sticking with me as I explained all of that!

Additional note: The foundation_reveal() hook in the footer definitely gets fired, it just doesn't get any of the class or ID data passed to it.

2

There are different ways to get the result. You can use a class, store the div content in a class or instance variable and output it when needed.

As alternative you can use a function with a static variable, to hold the content.

I'll use this second alternative, converting it in a class is an exercise for you ;)

In addiction you can use the core 'wp_footer' hook to output the content, in this way you don't have to add an additional hook, nor use a template tag.

function reveal_setup($atts = array(), $content = '') {
  // Setup the static variable. Use an array to allow multiple calls per page
  static $the_modals = array();
  // if the function is called from wp_footer hook
  if ( current_filter() === 'wp_footer' ) {
    if ( is_array($the_modals) && ! empty($the_modals) ) {
      foreach( $the_modals as $amodal ) {
        echo $amodal;
      }
    }
  // if the function is called from shortcode
  } else {
    // Get the attributes
    $atts = shortcode_atts(
      array( 'size' => 'medium', 'slug' => 'dummy','bg' => 'reveal-modal' ),
      $atts,
      'reveal' // enable filtering
    );
    // prepare the_modal link
    $modal_link  = '<a href="#" data-reveal-id="' . $atts['slug'] . '">';
    $modal_link .= $content;
    $modal_link .= '</a>';
    // prepare the_modal content
    $modal_format = '<div id="%s" class="%s %s" data-reveal>';
    $the_modal = sprintf( $modal_format, $atts['slug'], $atts['size'], $atts['bg'] );
    if ( $atts['slug'] == 'dummy' ) {
      $the_modal .= 'reminder/dummy content goes here';
    } else {
      $the_modal .= 'post loop based on page slug goes here';
    }
    $the_modal .= '</div>';
    // save the modal content in the static modals array
    $the_modals[] = $the_modal;
    // add the present function to wp_footer hook if it is not already added
    if ( ! has_action('wp_footer', __FUNCTION__) ) {
      add_action( 'wp_footer', __FUNCTION__ );
    }
    // return the modal link
    return $modal_link;
  }
}

add_shortcode('reveal', 'reveal_setup');

That's all. Untested.

PS: extract usage is a bad practice, (although I know it's largely used in core code and in Codex examples...)

  • Oh, this is fantastic. thank you GM! I'll get to trying this right away. I have been running through my courses on class structure but having this - a bonafide example within my own project - is PERFECT. I'll get back to you the minute I've had a chance to put this to work! – Imperative Ideas Jan 4 '14 at 2:28
  • Interesting regarding extract. You're right - EVERYTHING I've ever read uses it. As for this code, it works perfectly. Time to write my page loop into the class. Any advice on the best way to go about doing that? I'm decent with inline PHP but as I said - still learning OOP. I'm guessing a static function should render the loop and it should be called with a passed reference from the page slug. – Imperative Ideas Jan 6 '14 at 19:11
  • 1
    @ImperativeIdeas you are not forced to converted this function in OOP. You can just put it in your functions.php and use just like it is. If you want to convert in a class, write 2 methods, one is called from shortcode: output the link and save the modal in a object variable (it's wha is done in my function in else block). The second method is called to output the the divs on wp_footer hook (this part is what is done inside the if block). – gmazzap Jan 6 '14 at 19:34
  • Yeah I figured that out after posting. This is the non-oop version, as I worked it out. I simplified the output a bit for the time being but it seems to be functional: pastebin.com/mzDSGeLa. I'd like to try to convert this to a class next, just because it would be a good practice piece. – Imperative Ideas Jan 6 '14 at 23:18
  • Thank you again for the assist. I've learned a lot today! Chiefly what a static variable does. I'm not sure how I had missed that lesson along the way but I suspect it has something to do with the fact I always get distracted by writing stuff about 2/3 of the way through a PHP course and this variable type is often introduced late, so I end up never seeing it :) – Imperative Ideas Jan 6 '14 at 23:27
0

You could also use an anonymous function and let it access outside variables via the use keyword (if you got PHP 5.3+ ).

Examples:

add_action( 'init', function() {

    // Example 1: Append stuff to the titles    
    $stuff = '...';    
    add_filter( 'the_title', function( $title ) use ( $stuff ){
        return $title . $stuff ;
    });

    // Example 2: Display message in the footer   
    $msg = 'The End';
    add_action( 'wp_footer', function() use ( $msg ){
        echo $msg ;
    });

    // Example 3: Run an action only once  
    add_action( 'the_content', $callback = function( $content ) use ( &$callback ){
        remove_filter( current_filter(), $callback ); // __FUNCTION__ doesn't work here.
        return strtolower( $content );
    });

});

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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