Grow your CSS skills. Land your dream job.

RSS Feed with Altered Images

  • # April 4, 2014 at 11:46 am

    My RSS feed, for the blog on this site, gets generated directly from the content of the posts I publish. There is often images right in the content, perhaps like this:

    <p>Some random sentence or whatever.</p>
    
    <figure id='post-167550 media-167550' class='align-none'>
      <img src='http://css-tricks.com/wp-content/uploads/2014/04/concept.png' alt='' />
    </figure>
    
    etc...
    

    Notice the image has no width or height attributes, meaning it will just display at it’s regular size. Older blog posts might have some <img> with those attributes though.

    On the front end, that’s no big deal, as I can get them fitting into their article container with CSS just fine.

    But when that HTML goes into an RSS feed, there is no CSS to help. That is an issue sometimes, because the image might be, say, 1000px wide. Some feed readers don’t handle that well, but more importantly I use a RSS-to-email system and those wide images tend to destroy the table-based layouts of emails, stretching them super wide and causing horizontal scroll of the text.

    Essentially this problem:

    img

    So what would be cool, is to create a special RSS feed at like /feed/email/ that would

    1. filter through the post content and find all <img>
    2. remove any height attribute
    3. remove any width attribute, but replace with like width='320'

    That way when the rss-to-email service eats it, the content has no way to break the layout.

    Easy?

    # April 4, 2014 at 12:31 pm

    My gut instinct would be to include width and height attributes on all <img> since you can do this

    img {
       height: auto;
        max-width: 100%;
    }
    

    to make them scale on your front end. That’s what we’re doing here: http://www.pewresearch.org/fact-tank/2014/04/03/among-afghan-public-mixed-support-for-womens-rights/

    But on to your question…

    Checkout the add_feed() function (https://codex.wordpress.org/Rewrite_API/add_feed) which you would call by hooking into the init action. It accepts two parameters: 1) the name of the feed that appears in the URL, 2) a callback function to generate the output for the feed.

    function css_tricks_custom_feeds() {
        add_feed( 'email', 'get_me_the_email_feed_template');
    }
    add_action( 'init', 'css_tricks_custom_feeds' );
    

    For the callback function we want to do two things: 1) apply a filter on the_content_feed, 2) include the default RSS feed template so we don’t have to fork it and we’re lazy and all that.

    function get_me_the_email_feed_template() {
        add_filter( 'the_content_feed', 'css_tricks_super_awesome_feed_image_magic', 10 );
        include( ABSPATH . '/wp-includes/feed-rss2.php' );
    }
    

    Now we just need our function to run over the_feed_content and manipulate the images….

    function css_tricks_super_awesome_feed_image_magic( $content ) {
        $doc = new DOMDocument();
        $doc-&gt;LoadHTML( $content );
        $images = $doc-&gt;getElementsByTagName('img');
        foreach ($images as $image) {
            $image-&gt;removeAttribute( 'height' );
            $image-&gt;setAttribute( 'width', '320' );
        }
        return $doc-&gt;saveHTML();
    }
    

    I haven’t tested this. You may need to apply the css_tricks_super_awesome_feed_image_magic() function after other filters that get applied to the_contented_feed. To do that change the number 10 to a higher number in the add_filter() function above.

    Here’s the whole thing:

    function css_tricks_custom_feeds() {
        add_feed( 'email', 'get_me_the_email_feed_template');
    }
    add_action( 'init', 'css_tricks_custom_feeds' );
    
    function get_me_the_email_feed_template() {
        add_filter( 'the_content_feed', 'css_tricks_super_awesome_feed_image_magic' );
        include( ABSPATH . '/wp-includes/feed-rss2.php' );
    }
    
    function css_tricks_super_awesome_feed_image_magic( $content ) {
        $doc = new DOMDocument();
        $doc-&gt;LoadHTML( $content );
        $images = $doc-&gt;getElementsByTagName('img');
        foreach ($images as $image) {
            $image-&gt;removeAttribute( 'height' );
            $image-&gt;setAttribute( 'width', '320' );
        }
        return $doc-&gt;saveHTML();
    }
    
    # April 4, 2014 at 12:49 pm

    I’m not familiar enough with WP to know how exactly to hook it up to the generation of the RSS feed, but there’s a fantastic PHP library that allows you to parse and modify HTML with selectors just like you do with jQuery.

    PHP Simple HTML DOM Parser: http://simplehtmldom.sourceforge.net/

    require_once('Simple_HTML_DOM.php');
    
    $post_html = "Some random sentence or whatever.
    <figure id='post-167550 media-167550' class='align-none'>
      <img src='http://css-tricks.com/wp-content/uploads/2014/04/concept.png' alt='' />
    </figure>
    
    Another random sentence or whatever.
    <figure id='post-167550 media-167550' class='align-none'>
      <img src='http://css-tricks.com/wp-content/uploads/2014/04/concept.png' width='500' height='200' alt='' />
    </figure>";
    
    $post_html_raw = str_get_html($post_html);
    
    foreach($post_html_raw->find('img') as $image) {
      $image_src = $image->src;
      $image->width = '320';
      $image->height = '';
    }
    
    echo $post_html_raw;
    

    And here’s what the HTML looks like after:

    <p>Some random sentence or whatever.</p> 
    <figure id='post-167550 media-167550' class='align-none'>
       <img src='http://css-tricks.com/wp-content/uploads/2014/04/concept.png' alt='' width="320" height="" /> 
    </figure> 
    <p>Another random sentence or whatever.</p> 
    <figure id='post-167550 media-167550' class='align-none'>
       <img src='http://css-tricks.com/wp-content/uploads/2014/04/concept.png' width='320' height='' alt='' /> 
    </figure>
    

    I threw up a live-working example: http://labs.jonsuh.com/chris-coyier-rss-email/

    # April 4, 2014 at 2:22 pm

    @kingkool68 / @jonsuh

    Y’all are amazing. This is super close. I had no idea that server side dom parser thing was possible.

    There are some weird side affects though… Any clues?

    img

    # April 4, 2014 at 7:22 pm

    Oh DOMDocument(), you’re so silly! Here is a workaround for stripping out the automatically-added DOCTYPE gibberish from http://www.php.net/manual/en/domdocument.savehtml.php#88273

    First we need to wrap our HTML fragment in a <div>

    $content = '<div>' . $content . '</div>';
    

    Then this line will essentially get the innerHTML of the first div…

    $content = substr($doc-&gt;saveXML($doc-&gt;getElementsByTagName('div')-&gt;item(0)), 5, -6);   
    

    I don’t know what is going on in the <description> element of the feed as my code doesn’t touch that part. I did notice in the regular RSS feed there is an extra closing </p> after the first sentence…

    &lt;![CDATA[Stripes are pretty easy to do in CSS these days. <a href="http://css-tricks.com/css3-gradients/">CSS gradients</a> via the background-image property really got our back. I thought I'd document some variations in one easy to reference place.
    </p>
    Normal Colored Diagonal Stripes
    

    Here is the full code to do what you want.

    function css_tricks_custom_feeds() {
        add_feed( 'email', 'get_me_the_email_feed_template');
    }
    add_action( 'init', 'css_tricks_custom_feeds' );
    
    function get_me_the_email_feed_template() {
        add_filter( 'the_content_feed', 'css_tricks_super_awesome_feed_image_magic' );
        include( ABSPATH . '/wp-includes/feed-rss2.php' );
    }
    
    function css_tricks_super_awesome_feed_image_magic( $content ) {
         //Weirdness we need to add to strip the doctype with later.
        $content = '<div>' . $content . '</div>';
        $doc = new DOMDocument();
        $doc-&gt;LoadHTML( $content );
        $images = $doc-&gt;getElementsByTagName('img');
        foreach ($images as $image) {
            $image-&gt;removeAttribute( 'height' );
            $image-&gt;setAttribute( 'width', '320' );
        }
    
        //Strip weird DOCTYPE that DOMDocument() adds in
        $content = substr($doc-&gt;saveXML($doc-&gt;getElementsByTagName('div')-&gt;item(0)), 5, -6);
        return $content;
    }
    

    And PHP Simple DOM is a great PHP class that lets you parse HTML using CSS selectors we all know and love. It makes scraping sites a pure delight. Didn’t need to get it involved for this task though as PHP’s built in DOMDocument() class does the job.

    # April 5, 2014 at 12:04 pm

    Done and done, thanks!

    I have MailChimp – the RSS-to-Email provider I’m using here on CSS-Tricks, set to digest this new feed instead, so we’ll see if it stops breaking email layouts. I suspect that it will. If all goes well I’ll write it up and credit you of course.

    # April 5, 2014 at 8:10 pm

    Sounds good glad it worked out.

    The add_feed() function is a great way to add different formats of your content. I’ve added special XML versions of a post that could be ingested into a library database to spitting out a JSON version to even rendering a plain text version.

    For example, take this URL: http://www.people-press.org/2014/04/02/americas-new-drug-policy-landscape/

    You can show it as json by adding /json/ to the end of it (http://www.people-press.org/2014/04/02/americas-new-drug-policy-landscape/json/) or a plain text version by adding /plain/ to the end of it (http://www.people-press.org/2014/04/02/americas-new-drug-policy-landscape/plain/) I don’t have this live anywhere but I also made a Star Wars feed to render a post just like the opening credits of every nerds favorite Sci-Fi film. This was based on this pen http://codepen.io/TimPietrusky/pen/eHGfj

Viewing 7 posts - 1 through 7 (of 7 total)

You must be logged in to reply to this topic.

*May or may not contain any actual "CSS" or "Tricks".