Now we get to the most exciting part to blog of the JIBC project I have been working on. In this series I went from map metaphors, dabbling with a Leaflet.js interface to the WordPress site building.

Now we go where no blog has gone be… well this is not true at all. I’ve been relishing a long time to do into some of the stuff Tom Woodward has done with using the WordPress API and what the cool kids call “headless WordPress” (which sadly has nothing to do with headless ds106).

Without tossing too many acronyms your way, the WordPress REST API allows you to use WordPress to create, manage content in a way that is easy for normal humans who run and scream at the site of GitHub Markdown. But you can then tap into, access, fully grab all the data to use in anyway you that think of in sites, applications outside of WordPress.

I have only done some really minor things with the API, like the phrase loading on the front of http://arganee.world and some experiments where a SPLOT Image Collector can be used as a source for random images in outside sites (See Randy SPLOT and Glitch A SPLOT).

But it’s been ticking in the back of my mind for a time to use on a bigger scale.

That ticking got louder in the early planning of the CorrLeader project, especially as my client explained a desire for some efficient ways to keep busy managers tuned into new resources, opportunities from the web site, something more than email or making them return there. Especially something that would be efficient and useful on a mobile device.

I could see in my mind early, some kind of small footprint web site using HTML/Javascript to load the newest content from the site via the API, and having ways that it could be filtered to show items of the most interest.

It happened.

Again, all of the content originates from the full WordPress site (which because of the responsive theme does itself play well on a mobile).

WordPress version

But the CorrLeader Navigator gives it to you in a compact newest content first form factor:

In a hand is a mobile device showing the welcome page for the navigator web site, inviting one to explore new content, and featuring a bright "filter" button above the results/
The navigator opens…

When it launches it has within the JSON formatted summary of all posts and custom posts types on the site (currently 84 entries, weighing it at < 1 MB).

The default view is a text excerpt summary, showing the content categories, and the link to see the full content on the main site.

Quick summary view, good for scanning new content.

Just recently added is an option to see a full formatted view of content, images, embedded media, links, and all of the entry metadata.

The mobile device view of a fully formatted entry with featured image, and the post full content.
Full media content view, below the scroll is the full text of the item plus the meta data.

But the nifty stuff is under the filter button. Here you can select the types of resources to view (between the two content types), and then restrict by broad topic areas (Personal, Relational, and Organizational Leadership) and/or the specific topics within. I have been calling this privately Alan’s Insane Checkbox Project.

All kinds of checkboxes and buttons to customize the type of content

The results change in real time as you toggle things on and off (this is the jQuery voodoo happening inside).

But this is not everything. What I dreamt of was a way that on your own device, it could remember your selections, so each time you flipped open your device, it would show the newest content according to the selections you had saved (no need to return to the form).

This works now.. and it does this with no trackable cookies, but using Javascript Local Storage. a means to save preferences in a way that stays on a device (a web site cannot sniff it, the data is not sent back and forth with the HTTP requests).

Navigator options to view results as text summary or full media, plus the options to save the selections in the devices local storage, so they are preset whenever you return to it. And you can also clear storage at any time.

It’s allo working now, and since setting up a means of caching the json files it loads really quick as it does not need to hit the WordPress site every time it is loaded.

That’s what it does, now for the peeking under the hood (warning, code lies ahead). Are you ready, Scotty?

Promise Me Some API Data?

The first thing to do was finding out how to pull in WordPress data via the API. The most basic call to get data on all your posts (and yes, you can try this at your home blog) is tacking on wp-json/wp/v2/posts to your WordPress blog URL. Try mine https://cogdogblog.com/wp-json/wp/v2/posts

A screen full of gibberish json data. You might be better off not seeing it. Okay it looks like '{"id":68204,"date":"2019-10-03T08:25:42","date_gmt":"2019-10-03T15:25:42","guid":{"rendered":'....

It makes a bit more sense if you copy all that stuff and put it into something like JSON Pretty Print

Ugly code on the left, neatly organized into fields and values on the right.

This makes it much easier to see what kind of data you get and how it is organized.

The first basic test was a simple getting of this data and popping it into an initially empty div in the html page. For fun it uses backstretch.js for a background image and a google hosted jQuery library.

The first test site, victory is just getting data and displaying it.

I used pretty much the same ajax code as before to get the WordPress data.

You gotta start somewhere.

A few things then came up to consider. First, I have two different content types on the site to bring in as one stream; normal WordPress posts represent the Resources but also the portfolio content type is used to hold the recommended courses.

Fortunately I was thinking about this early and had requested the site setup to include the WP REST API Controller plugin – this allowed me to expose api URLs for the custom post type and it’s own taxonomy. I now have a way to get to this data,

The portfolio content type is enabled in the REST API controls, and exposes a new URL.

And now I can get this data with a second API URL, https://corrleader.jibc.ca/wp-json/wp/v2/portfolio

Here was one of many times Tom Woodward stepped in with a big help (via twitter DMs) in helping me figure out how to get the data I needed. By adding ?embed to each API URL I am able to get much more data per item (like featured image URLs) but more importantly, all the associated taxonomy data. And my including a second parameter per_page=100 I can get more than the default 20 results. I believe 100 is the limit, bit for now we are okay with 67 resources and 17 custom post types.

And the complexity grew. I had to make 2 API calls to get this data, then combine them all into one array, and also sort them reverse chronologically.

Tom again was a big help by sharing some examples of the Javascript promise constructor, which even after getting it to work I may understand about 10%. Because the fetching of the API data is asynchronous, I needed I was to wait before both were returned before doing anything (Javascript will just keep chugging along).

What I do below is first create some empty arrays to store my resources data and my courses data, then use promise to get data from both URLs, it waits until it has results to checking them and passing on to a function that displays the results. This is the basics of this portion:

I would certainly lose the 3 readers left if I went into more detail of the update_results() code- it does a lot. It has to check if we are displaying all results, or just one content type, and if we are filtering by categories (see below), it then munges through both data arrays, puts them into a new one of they are meant to be shown, then call a funky function I found that sorts them by the data value inside of them.

Anyone there?

I learned some crazy new JavaScript methods to do this stuff, like array intersections.

Oh, I had also started using the basic Bootstrap template to create the visual part of the site. It comes with a lot of built-in features like modals and grids and buttons and more that simplified the layout part greatly. I did not want to overly stylize the output, but wanted it to look good regardless.

(I also change the CDN links for the theme and jquery to local files, just cause sometimes I like to do dev work when the internet is missing).

Those Crazy Checkboxes

The next chunk of time was spent sorting out the form interface for the customization screen. I played with a few ways to make this appear in Bootstrap- cards, accordions, and settled on the button opening a modal dialog.

The challenge was making all of these work for a set up of three top level categories (leadership areas), each with 6-11 sub categories (topics), and a master switch to allow selecting all or none.

This means if you start with all of the options selected, clicking any of them needs to turn off the master select all switch (and if all were reselected, turn it back on). If one of the 3 area checkboxes were turned on or off, it would have to turn on / off all of the sub category topics. I all sub the category topics were then all turned on (or off), then the parent category would need to match.

It was a bit like playing with the holiday lights…

The key were was using a series of CSS classes to the checkbox elements, so jQuery could act on all similar boxes. These classes included:

  • leader-cat any checkbox that is a category
  • leader-area for the top level three categories (Personal, Relational, Organizational)
  • leader-topic for any of the sub categories (e.g. Adaptability, Authenticity, etc)
  • leader-personal, leader-relational, leader-organizational to group the subt topics with their parent
  • triggr - any form element that would need to have the functional called up update content displayed

Or, in the context of the form:

Diagram showing that CSS classes "leader-car leader-area triggr" are applied to top level categories, and sub categories have CSS classes 'leader-cat leader-topic leader-personal triggr"

And, as spelled out in the HTML code for the form:

It gets even trickier, because each category can be mapped to two different WordPress taxonomies- the ones for Adaptability are 16 for the Category ID for a Resource while it could be 55 for the Course Content type Taxonomy.

Crazy?

The form element for select all /select none uses a BootStrap custom-switch to change it to a slide on/off button and is marked with a CSS id of #toggle_cats.

Thos code turns all checkboxes on or off to match the #toggle_cats switch state:

So this little gem turns this item on if none of the subtopics are turned off!

And this code handles the syncing of the Personal Leadership area check box with its subcategories:

And something like this for any sub category in Personal Leadership manages to keep the parent category checkbox set correctly

Now that I am trying to explain all this, I might be even fuzzy how it all works. Believe me, I spent a lot of time checking and unchecking boxes before they even did anything!

This nifty function is used to get the values of all selected category ids, remember that there are two values in each form element, separated by a comma, which we then join then split! The .map(Number) turns out to be needed to get the values not as strings.

More helper functions to get the ids of selected categories and the names as well (extracting from the labels of checkboxes).

What About Showing Stuff?

The .triggr class on a form element has a function that calls update_results (which is also first called when the page is loaded). This is the kind of code that grow like spaghetti over time. Let’s walk through the pasta.

We start with a start value! This indicates where in the array of results to start walking through to get data. Default is 0, meaning form the beginning, but it can take a variable if we are appending more content (via the more button at the bottom).

The page loads with a spinning “loading.gif” so if we are at this point we can hide it. Then we set up some bits to create the string announcing the results.

If none of the checkboxes are de-selected, it’s easy. The data is sitting in two arrays results_posts and results_courses for the two content types. If the selection is one of them, we just use the appropriate array; if it’s all content, we stitch them together, reverse sort by date, and use that data.

The function we call for reverse sorting of the arrays by the data element is one of These Things I Found By Searching and It Works Even if I am Not Sure What it Does.

Back to the updating of results, if we are filtering by category, then it gets really interesting. Or messy.

We have to use some of those functions in the previous sections to get all of the category/taxonomy IDs represented by the checked boxes get_selected_cats_val(). Then we use a funky intersection function I found that can find out if array of taxonomy IDs for each item in the results we are walking through matches any in the array of selected categories. If we match, we add the item to the building array of ones we are to display.

Then we still have to run the sorting function again if we have mixed items from multiple taxonomy terms. And I even have a function (previous section) I can use to get the names of all selected checkbox category names as a string, so I can include that in the blue box header with the results.

By now we should have the information we have filtered out of all the data to start iterating through to display. We have one check to see the upper limit of the number of items to walk through (in case we are near the end of the array), and a call to a helper function that forms the results header.

We then iterate through the array, and display either the compact text summary form or the rich media format (for the latter I added a bit of CSS to the output to put a numbered separator between results).

And here we can now put all the results into the div where they are shown, or appending it if we are adding to ones already displayed. There are some checks at the end that determine if the more buttons show be shown.

Lastly, for the full media format, a few more classes need to be added to divs that contain and iframe video and the iframe itself, this makes the videos responsively sized.

And this was fun to figure out, how to make the more button work all ajax like (though not really) to append more results. The trick here was using some CSS/jQuery to count the number of items currently in the list of outputs, so it knows the value to send to the update_results() function to append more.

To get really fancy, I also found some code to make it so the window scrolls back to where in the page the new content was added (without we are left at the tail end).

Store Those Preferences in Local Storage

I am not sure if anyone will even notice this detail, bit given the current state of how prevalent (and some might say predatory) browser cookie tracking is, I’m excited to have implemented an approach which does what I might resorted before to cookies for.

And this is a key feature of the Navigator. If you saved all those settings from the form, each time your returned to the site, you’d have them preset for you.

Like I said, finding out about Local Storage was a bit like seeing inside the suitcase from Pulp Fiction. I perused a few how to tutorials – and I found the stated reasons not to use it as irrelevant. It acts like a cookie (and is also deleted if you delete your browser history) but as far as I can sort out, there is no way that information is transmitted back to a web site. And all I am saving are some CSS IDs for saved checkboxes.

My form has a Save and Clear button to manage the storage. I added a click to confirm just to make it more clear to someone what is happening.

I ended up storing three variables (if I was doing this less incrementally, I could have condensed them a bit more)- the CSS ids for checked boxes, as well as the ids that represent the content type selection (radio buttons) and the display type, compact or media loaded (radio buttons).

Everything is set up in an early function called as the page loads. We can test of Local Storage is available on the device and if we have one of our known variables floating around (if it is the rest should be there). Then we should restore the previous checked boxes with restore_selected_cats() otherwise we do the default settings set_default_checkboxes().

Here are the default settings (if we don’t have local storage, for extra grins we disable its buttons on the filter form).

But if we do have some saved settings, let’s set them up:

There’s a few more small bits I’ve left out, if you want to see the whole dang script, I’ve got a link for ya. It’s not your concise cool programmer code.

Cache Please?

It was not a problem hitting the WordPress site for the API data as I was testing, but we don’t really need to have every request going to the site for that, especially as the update frequency is not very high. The load time was maybe 10-15 seconds to get the two calls for JSON data back.

I knew all along I was planning to cache it. having the JSON files local on the site makes it load almost instantly. This is a crude PHP thing I wrote to call when it needs to be updated, and in production could be requested via a cron job. For the heck of it, it asks for a variable to be passed with a key code.

Anyone Left in the Building?

This is an insanely long code heavy post, but it’s my blog and I can blog what I want to. I wanted to capture the major parts and how it came together iteratively as I built it.

And this is of course, rather hard wired to the project and site I worked on. But this gives me a whole new leverage point where I could much more easily do one again, and I am seeing many more possibilities for thinking of ways to augment a WordPress site with a different means of accessing/scanning the contents.

For example, the idea to add a full media display came from a client wish to be able to have some cut and paste copy that could be used in emails with the organization, and this works really well so far in being able to do that. They could use the filters to find a category of content to promote, and use it to generate copy content.

With these tools inside my box, I am seeing all kinds of new magic ideas inside (and outside) of WordPress.

Pulp Fiction GIF - Find & Share on GIPHY
This code is… golden!

Featured Image: Added the CorrLeader logo based on Compass by Yo! Baba from the Noun Project (CC BY) and text to Captain Kirk’s Star Trek communicator flickr photo by Matt From London shared under a Creative Commons (BY) license

If this kind of stuff has any value, please support me monthly on Patreon or a one time PayPal kibble toss
Profile Picture for Alan Levine aka CogDog
An early 90s builder of the web and blogging Alan Levine barks at CogDogBlog.com on web storytelling (#ds106 #4life), photography, bending WordPress, and serendipity in the infinite internet river. He thinks it's weird to write about himself in the third person.

Leave a Reply

Your email address will not be published. Required fields are marked *