Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: shortcode

  • Don’t Reinvent the Wheel

    Don’t Reinvent the Wheel

    In WordPress, I punt plugins now and then for doing weird things that can best be described as reinventing the wheel.

    Any time a plugin replicates functionality found in WordPress (i.e. the uploader, jquery), it is frowned upon. It presents a possible security risk since the features in WordPress have been tested by many more people than use most plugins. Simply put, the built in tools are less likely to have issues.

    This was always something a little theoretical. I hadn’t yet run into someone who had broken their code with an upgrade of WordPress just because we updated the core wheel. Until November 20th.

    The shortcode API in WordPress was updated. It was decided that in order to stop breaking on PHP 5.4.8 and under, we needed to apply wptexturize() to shortcodes. This had a fun side effect when people didn’t properly register shortcodes, which of course brought up the logical question … why wouldn’t you register your shortcode?

    What I generally see in plugins is someone’s using a filter to look for their shortcode instead of registering it, so the post content is, in it’s entirety, parsed. That always struck me as foolish, since posts can get pretty long if Chris Lema or I are writing. And the reason people would do it was also odd. Either they were being lazy, they didn’t know about shortcodes (which I can understand, you can’t know everything), or they were trying to get around an ‘issue’ with nested shortcodes.

    For what it’s worth, the shortcode parser correctly deals with nested shortcode macros.

    [tag-a]
       [tag-b]
          [tag-c]
       [/tag-b]
    [/tag-a]
    

    That works fine. This won’t:

    [tag-a]
       [tag-a]
       [/tag-a]
    [/tag-a]
    

    Now I want to note, I’m using a php shortcode around those tags. So yes, it works great. But the problem would be if I wanted to show you the php code in a php shortcode… Doesn’t work that way, and it’s a limitation of the context-free regexp parser used by do_shortcode(). We went for speed over levels, and it can’t match each opening tag with its correct closing tag.

    Obviously the ‘right’ answer is ‘don’t nest same-named shortcodes’ but instead, some plugin authors have chosen the strategy of not registering shortcode names. That way the parser doesn’t try to mess with it. Sounds great, right?

    Not with wptexturize() running.

    [tag-a unit="north"]
       [tag-b size="24"]
          [tag-c color="red"]
       [/tag-b]
    [/tag-a]
    

    That turns into this:

    [tag-a unit="north"]
       [tag-b size=”24”]
          [tag-c color=”red”]
       [/tag-b]
    [/tag-a]
    

    Basically the code is understood to be code and not a quote. That means it would simply output the tags as code and not the tag content.

    And you see why this is a problem.

    There are two answers to fix this. One is to turn off wptexturize for your pseudo-shortcodes (via the no_texturize_shortcodes filters which are not complete, sadly, if you need to unfilter shortcode variables) and the other is to use the Shortcode API as it was intended. I would, personally, suggest you use the API since that prevents your code from breaking like this if another security update happens.

    Which brings us back to why reinventing the wheel is generally foolhardy in robust, well maintained systems. WordPress is constantly being improved, fixed, patched, and secured. The more you work around it by making your own way, the harder it is to fix things when WordPress makes a change. You’d think that people who make their own wheel would be attentive to anything that might break it, but they rarely are. They make their way, forget about it, and get annoyed when their code breaks and blame WordPress for not warning them. That’s a dirty secret about Open Source. No one’s going to tell you that a change will break your code. They expect you to be paying attention.

    The absolutely worst part about all this is that your users will have to decide to either stop using your code (which isn’t always an option) or not to apply an upgrade. In the case of 4.0.1, this is dangerous. This is why I will keep telling you not to reinvent the wheel, unless the wheel really needs it. And if the wheel needs a reinvention, you should consider submitting a patch to WordPress core, because if it’s that bad, everyone should know.

  • The Wheel Is Fine

    The Wheel Is Fine

    “I have an idea for a plugin!” he said to me. Everyone does, but I encouraged this question. “I want to make the media embeds in WordPress more responsive.” I paused. There are already a lot of those, like Responsive Video Shortcodes. So I asked what made his different. “I’m going to make NEW shortcodes!”

    Bender (from Futurama): I'm going to make a new plugin. With shortcodes and jquery.

    I see a lot of plugins. Many times I see plugins where people want to tweak a normal part of WordPress, like the gallery or the embeds. The problem I see is that instead of extending those, they decide it’s better to make a plugin that recreates all the shortcodes and embeds, but with a new name, so users have to remember to use (which is default to WordPres) or [youtube] (which is in Jetpack) or [someothervideotool] (which I made up for an example).

    Now I’m guilty of this myself! I have a private plugin which is HTML5 video embeds and it gives the video in HTML5 format, which I could do in the default embed code, but it also plunks a link underneath that says “Can’t see the video? Click here.” And the Click Here goes right to the mp4 video. Because I have some rather dim people. But. I don’t have to reinvent the wheel here. I could instead filter that shortcode just to put my little blurb on the end! Doesn’t that seem smarter?

    The first time I thought about this, I said “What I need is to filter and slap something on the end!”

    function halfelf_oembed_filter($html, $url, $attr) {
        $html .= "<p>I am a monkey.</p>";
        return $html;
    }
    add_filter( 'embed_oembed_html', 'halfelf_oembed_filter', 10, 3 );
    add_filter( 'video_embed_html', 'halfelf_oembed_filter', 10, 3 );
    

    This puts “I am a monkey” below every embed, which is great for stuff like YouTube and Twitter. The second filter is for videos when you’re using Jetpack. And this works great, I could even wrap the HTML in something, like a div or js code, and enforce responsiveness. You can check the URLs, see if it’s youtube or whatever, and apply different settings per URL. Works nicely. I actually have my just div-wrapping for padding reasons.

        $html = "<div style='padding: 5px;'>".$html."</div>";
    

    This works brilliantly, however … I’ll get to that in a second. Now, for my friend’s initial idea, once we banged on this, he said he was just going to use the other plugin, but embed-filtering was way easier than trying to over-ride the shortcodes and reinvent the wheel. It also makes it easier to upgrade.

    But then there’s my problem with my site and the need for that “Hey, get the MP4 here!” message. You see, 90% of the videos I have on this particular site are locally uploaded, and since I always upload an mp4 and a webm (and sometimes an ogg), how do I parse it?

    Wheel gears from a tractor

    First of all, I’m using the Video Shortcode, so I know there’s a simple filter for wp_video_shortcode_override (it works roughly the same as img_caption_shortcode but that’s not what I want to use. I don’t want to replace everything, that’s my whole point here. I just want to make a link! In addition, I know I’m only going to want this when I have a video with a defined mp4, so why not just filter wp_video_shortcode instead?

    So that’s what I did:

    function halfelf_video_shortcode($html, $attr) {
    	
    	if ( !empty( $attr['mp4'] ) )
    	{
    		$html .= "<p>Can't see the whole video? Click <a href='".$attr&#91;'mp4'&#93;."'>here</a>.</p>";
    	}
    	
    	return $html;
    }
    add_filter( 'wp_video_shortcode', 'halfelf_video_shortcode', 10, 2);
    

    This slaps a link for my tech challenged friends on browsers that refuse to tolerate videos.

  • Shortcode: MLB.TV

    Shortcode: MLB.TV

    I like baseball, I like the Indians. I like embedding content. Why MLB.tv likes to make their stuff not easily embeddable is beyond me. I think [mlbtv id=28142247] is way easier to deal with if I’m using the visual editor. I grabbed the default sizes from their settings.

    <?php 
    // MLB.TV Shortcode - Better code by Konstantin Kovshenin
    // &#91;mlbtv id="###" width="480" height="270"&#93;
    function mlbtv_shortcode( $attr, $content = '' ) {
        $attr = shortcode_atts( array(
            'id' => null,
            'width' => 400,
            'height' => 224,
        ), $attr );
     
        $attr = array_map( 'absint', $attr );
        $url = add_query_arg( array(
            'content_id' => $attr['id'],
            'width' => $attr['width'],
            'height' => $attr['height'],
            'property' => 'mlb',
        ), 'http://wapc.mlb.com/shared/video/embed/embed.html' );
     
        return sprintf( '<iframe src="%s" width="%d" height="%d" frameborder="0">Your browser does not support iframes.</iframe>', esc_url( $url ), $attr['width'], $attr['height'] );
    }
    add_shortcode( 'mlbtv', 'mlbtv_shortcode' );
    

    [mlbtv id=28142247]

    Looks just fine.

  • DreamHost Logo ala CSS

    This was done by Clee, my coworker. I tweaked it, prefacing things with dh to make it more portable and then slapping it into a shortcode so I could embed it here:

    The code is as follows (you can see his original code at codepen):

    &lt;div id=&quot;dhbackground&quot;&gt;
      &lt;div id=&quot;dhmoon&quot;&gt;
        &lt;div id=&quot;dheclipse&quot;&gt;
          &lt;div id=&quot;dhchevron&quot;&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    
    @dhbg: rgba(27, 53, 100, 1);
    @size: 240px;
    @corner: (0.16 * @size);
    @offset: (@corner - 6px);
    @chevron: (0.36 * @size);
    
    div#background {
      background-color: @dhbg;
      width: @size;
      height: @size;
      border-radius: @corner;
      margin: auto;
    }
    
    div#moon {
      background-color: #fff;
      width: @size - (2 * @corner);
      height: @size - (2 * @corner);
      position: relative;
      top: @corner;
      margin: auto auto;
      border-radius: 50%;
    }
    
    div#eclipse {
      background-color: @dhbg;
      position: relative;
      width: @size - (2 * @corner);
      height: @size - (2 * @corner);
      top: -@offset;
      left: -@offset;
      border-radius: 50%;
    }
    
    div#chevron {
      background-color: rgba(51, 104, 156, 1);
      position: relative;
      width: @chevron;
      height: @chevron;
      left: @offset;
      top: @offset;
      border-radius: @corner 0px (2 * @corner) 0px;
    }
    

    Now actually that’s not CSS, it’s LESS, which I can’t inline apparently (or maybe I just don’t know how). Either way, it’s a funky cool CSS trick!

  • World Time Event Shortcode

    World Time Event Shortcode

    I had a need. A need for timezones. I happened to post about an event happening at 2pm ET, under the mistaken assumption that people knew how to convert timezones, or at least go to a website for it. Instead, after a weekend full of emails and me snapping at them for being under educated (don’t schools teach this stuff anymore?) I linked them to a page.

    Then I thought “Well heck, they won’t click that. Why not embed it.” Alas, TimeAndDate.com didn’t let you embed, but worldtimebuddy.com does. With a hat tip to Rarst for the link, I pulled this shortcode out:

    // World Time Buddy Event Time
    // Usage: [eventtime time="14:00" length="1" date="2/20/2013" tz="ET"]
    function eventtime_func( $atts ) {
    	extract( shortcode_atts( array(
    		'time' => '14:00',
    		'length' => '1',
    		'date' => '2/20/2013',
    		'tz' => 'ET',
    	), $atts ) );
    	
    	if ( $tz == 'ET') $timezone = '5128581';
    	if ( $tz == 'PT') $timezone = '5368361';
    		
    	return '<span class="wtb-ew-v1" style="width: 560px; display:inline-block;padding-bottom:5px;"><script src="http://www.worldtimebuddy.com/event_widget.js?h='.$timezone.'&amp;md='.$date.'&amp;mt='.$time.'&amp;ml='.$length.'&amp;sts=0&amp;sln=0&amp;wt=ew-lt"></script><i><a target="_blank" href="http://www.worldtimebuddy.com/">Time converter</a> at worldtimebuddy.com</i><noscript><a href="http://www.worldtimebuddy.com/">Time converter</a> at worldtimebuddy.com</noscript><script>window[wtb_event_widgets.pop()].init()</script></span>';
    	}
    add_shortcode( 'eventtime', 'eventtime_func' );
    

    I can’t make this a plugin because it has a powered-by link, and while I could remove it, I won’t. If the link was put in by the script itself and not my return, it’d be fine for the repo, but since I’m only going to use this a few times, I’m leaving it be.

  • Display Videos Shortcode

    Display Videos Shortcode

    This is a one-off, but it’s interesting to me so I’m sharing. I have a site with very pretty archives. It came with a video Custom Post-Type, but no archives for that type. Now I could have edited the theme, or overwritten the CPT, but I decided instead to embrace what I had and add on. What if I made a shortcode for [recent-videos] that showed me the recent videos?

    This code was specifically designed for the custom post-type ‘Videos’ in the News Theme by Theme Hybrid.

    add_shortcode('recent-videos', 'recent_videos_shortcode');
    function recent_videos_shortcode($atts) {
    
            extract( shortcode_atts( array(
                    'posts_per_page' => '10',
            ), $atts ) );
    
            $args = array(
                    'post_type' => 'video',
                    'posts_per_page' => $posts_per_page,
            );
    
            $vidlist = new WP_Query($args);
            if ( $vidlist->have_posts() ):
                    $return .= '<div class="display-vidlist archive" style="margin: 0 0 0 -20px!important;">';
                    while ( $vidlist->have_posts() ): $vidlist->the_post(); global $post;
    
                            $image = '<a class="image" href="'. get_permalink() .'">'. get_the_post_thumbnail($post->ID, thumbnail, array('class' => 'news-thumbnail')).'</a> ';
                            $title = '<h2 class="entry-title"><a class="title" href="'. get_permalink() .'">'. get_the_title() .'</a></h2>';
                            $date = '<div class="byline"><abbr class="published" title="'. get_the_date('l, F jS, Y, g:i a') .'">'. get_the_date('F j, Y') .'</abbr></span></div>';
                            $excerpt = '<div class="entry-summary"><p>' . get_the_excerpt() . '</p></div>';
                            $output = '<div id="post-'. get_the_ID() .'" class="hentry videos publish author-'. get_the_author_meta( 'user_login' ) .' has-excerpt">' . $image . $title . $date . $excerpt . '</div>';
                            $return .= apply_filters( 'display_posts_shortcode_output', $output, $atts );
    
                    endwhile;
    
                    $return .= '</div>';
            endif; wp_reset_query();
    
            if (!empty($return)) return $return;
    }
    

    The heavy lifting was formatting it to look right, and I’m not happy about my hack in class="display-vidlist archive" style="margin: 0 0 0 -20px!important;" but I also wasn’t 100% sure I wanted to separate the css just yet.

    Most people will need to change 'post_type' => 'video', to their CPT, and remove the style hack.

    I can already see where I’d extend this if I wanted to allow more arguments. The only one I put in was for the number of posts: [recent-videos posts_per_page=10] — You could easily add in one to allow ANY post type:

    function recent_videos_shortcode($atts) {
    
            extract( shortcode_atts( array(
                    'posts_per_page' => '10',
                    'post_type' => 'post',
            ), $atts ) );
    
            $args = array(
                    'post_type' => $post_type,
                    'posts_per_page' => $posts_per_page,
            );
    

    Then call [recent-videos post_type="video"] — Of course, if you do that, you should probably fork this into ‘Recent Posts Shortcode’ and rebrand recent-video to something else.

    Which you totally can do (this, as with all my code, is licensed GPL2).