Design across sites & services

Working within the constraints of a CMS can sometimes be … constraining. What if you’d like to include custom functionality within your site that falls outside the standard WordPress library? What if that functionality is coded in Python? Enter the mighty embed!

Iframes: the wrong way

Want to troll a developer? Ask her to put an iframe on your site. Developers have built this backdoor into Contentful, arguably the most serious CMS in the world, just so they wouldn’t ever have to have that conversation with you. Under assets, simply select create a new asset and link and drop the script into the block. This also works in WordPress.

Let’s say you want to embed two episodes of Hidden Brain into your page, like so:

Both of these are just individual iframes dropped into a Custom HTML block, like so:

<iframe src="https://omny.fm/shows/hidden-brain/laughter-the-best-medicine/embed" width="100%" height="180" frameborder="0" title="Laughter: The Best Medicine"></iframe>

Voilà: you’ve just earned your Masters in Computer Science! Or maybe not. The problem with iframes is that you’re giving someone else a piece of your website. What could possibly go wrong? Well:

  • They present a horrible user experience, where the user might interact with something in the frame that takes them to another page within the frame. As just one example, click play on the two examples above. They play over each other because they have no way of coordinating the experience on your page. Ideally, the audio of one would stop the moment you press play on the other one.
  • It’s a big honkin security hole that a nefarious actor could use to steal information from your users under the banner of your brand.
  • They compound all the bad performance of your site with the equally bad or worse performance of the framed site. Even if you’re the developer of both the wrapper page and the framed page, you’re still doubling the load time of the page.
  • There’s no version control for iframes, meaning that a change to the framed site may break your site without you (or the framed page developer) even knowing about it.
  • You get zero SEO credit for the content in the frame when the Google crawler comes around.

If you’re ready to join the 21st Century, ditch the iframe.

Scripting: the ok way

WordPress makes it easy to add vanilla javascript to any page, which can then be deployed elsewhere. Let’s say you want to add a javascript clock onto your site, like so:

You can usually just google this, steal someone else's code, and drop it on your site in a custom HTML block, as I did here:

<h2 id="time" class="text-danger pl-5"></h2>

<script>
function checkTime(i) {
  if (i < 10) {
    i = "0" + i;
  }
  return i;
}

function startTime() {
  var today = new Date();
  var h = today.getHours();
  var m = today.getMinutes();
  var s = today.getSeconds();
  // add a zero in front of numbers<10
  m = checkTime(m);
  s = checkTime(s);
  document.getElementById('time').innerHTML = h + ":" + m + ":" + s;
  t = setTimeout(function() {
    startTime()
  }, 500);
}
startTime();
</script>

So, this is fine, but it still suffers from the problem that your code is part of the content, making it hard to upgrade the design and functionality of the site unless you're carefully documenting these one-off pages. It also isn't as performant as cached code. Fortunately, there's a better way:

Custom PHP: the proper way

Don't freak out. You can do this! And you only need to do it once to embed all future webpages ever into your site using a shortcode that defines the location of the page / code to embed.

First I'll tell you the easy way, but don't do it this way! Just read to get the core concept. The easiest way is to go to Appearance Theme Editor and select Theme Functions from the box on the right side of the screen. Then just add the following code to the bottom of the php file:

add_shortcode( 'rr', 'remoteRequest');
function remoteRequest($atts){
        $route = $atts['r'];  //passed from shortcode
        $url = 'https://' . $route;
        $request = array(
                'method' => 'GET',
                'timeout' => 10,
                'redirection' => 10,
                'blocking' => true,
                'headers' => array(),
                'cookies' => array(),
        );
	$response = wp_remote_request($url, $request) or die ('request failed.');
        return $response['body'];
}

Now, whenever you want to add an embed, you'll just add a shortcode, like [rr r="www.yoursite.com/api?user=me"] and the embedded page will preload with php and get cached by your caching engine.

We're just going to make a few changes to the process above to improve it, but first pause for a moment to reflect on the power of this small bit of code. The rr shortcode is short for remote request, but you could give it your own shortcode if you don't like mine. You could call it George, for instance, and have the shortcode be g, so long as you change remoteRequest to George wherever it appears in the code above. The r= bit of the code defines the request. In other words, it allows an editor to specify a different source url for each page. With five minutes of copypasta, you've opened up the entire internet to your editors, with the same awesome performance caching you have on your own site. You're also taking all requests for the code over to your site, so you can set up your own analytics around how the code is used. You can even apply your own CSS styling and javascript to the embedded page.

What could go wrong? If you'd actually followed the instructions above, WordPress would have given you a warning when you opened the Theme Editor. You really shouldn't be editing code here. For one thing, you should use version control because if you don't, you won't be able to recover from mistakes in your code. For another, you should make these changes in your IDE and test them locally before deploying, to avoid the pesky problem of your entire site is down because of a misplaced semicolon. Also, you should first create a child theme to avoid this inevitable situation: your code is erased the next time you update your theme.

Custom Plugin: the proper, proper way

Navigate to your plugins folder and add a new folder. I'd usually name it after the domain of the site and use this as a repository for all custom code specific to your domain, but you can call it anything. You can also optionally add a file in that folder called README.md to add documentation to your custom code.

The important thing is that, in that folder you also have a file with the same name as the folder with the suffix .php to serve as the core code of your custom plugin. At the top of that plugin, add some information about your plugin that will describe your plugin to any admin-level users of your site. Here's what mine looked like:

<?php
/**
 * Plugin Name: Town Calendar Custom Functions
 * Plugin URI: https://www.towncalendar.org/
 * Description: Custom Functions for townCalendar.org
 * Version: 1.0
 * Author: Demian Perry
 * Author URI: http://www.towncalendar.org
 */

Immediately after this code, add the shortcode function we were working with earlier, with two important modifications:

add_shortcode( 'rr', 'remoteRequest');
function remoteRequest($atts){
        $route = $atts['r'];  //passed from shortcode
        $url = 'https://WWW.YOURSITE.COM' . $route;
        $request = array(
                'method' => 'GET',
                'timeout' => 10,
                'redirection' => 10,
                'blocking' => true,
                'headers' => array(),
                'cookies' => array(),
        );
	$response = wp_remote_request($url, $request) or die ('request failed.');
        if (array_key_exists('body', $response)){
                return $response['body'];
        } else {
                return print_r($response);
                //return 'Events app took too long to load.  Refresh the page to see events.';
        }
}

The two changes are:

  1. I added WWW.YOURSITE.COM to the remote request url. This has the drawback of limiting embeds to just your site. We do it because it gives you control over performance and security. And you can always create simple, custom proxies that will allow your editors to embed from another site. All they have to do is talk to you. And that's the point.
  2. I added better error handling to the bottom. The code already had error-handling for a complete failure of the script, but the more common issue is a timeout and this new code will more gracefully fail for the user.

To make this code active on your site, simply go into your admin dashboard and activate the plugin. Then add the shortcode to any page where you'd like to add an embed. Remember that, with your domain hard-coded into the plugin, you no longer need to use the url in the shortcode, so you can use a relative url to the page you want to embed: [rr r="/api?user=me"]

See the code in action:

I'm using this exact code on my website towncalendar.org to allow users to interact with events that have been selected by a python algorithm. Take a look at the embed at the link above (click the picture to see it in action). Note how quickly it loads, and how it is fully interactive. Real gear-heads will also note that the code piggy-backs both on my wordpress CSS and on the jQuery script at the top of my site, rather than loading it twice (as the two not-so-proper solutions would do).

Happy embedding!