Wiring WordPress Media Uploader Into Your Web App

As predicted, the “premium” theme I purchased to make things happen on the HereMusic.Live site needs more tweaking.  Today I am diving into the proper way to wire the WordPress media uploader into a plugin after learning the theme not only cannot handle a media file with parenthesis in the name but it also is dropping the media title into the URL field on the form.  Ugh.

Most of the magic for the WordPress Media interface is managed via JavaScript.    I’m going to focus on the JavaScript portion of the code, assuming an app already has a basic form on an admin page that needs media uploaded and gets the URL put into a form element.   I’m also going to assume you know how to attach basic “on click do this” to an HTML element using jQuery or inline onClick methods.

Rip Out Thickbox

The old-school method of attaching to the WordPress media uploader was to tie into the Thickbox-driven media interface.   If you look into an existing WordPress plugin or theme and see the JavaScript littered with tb_show() and window.send_to_editor references there is a very good chance it is using the outdated Thickbox model.

I’m not sure where this model is breaking down on my WordPress 4.7 site but somehow the html parameter on the send_to_editor() call is only receiving a string containing the file title.

Time to rip it out and replace it with the thinner and generally better-designed wp.media model.  It was introduced eons ago (in modern web app terms) and should be the go-to method for implementing this feature.

Insert wp.media

As with many of the “black magic” features of WordPress, wp.media is not well documented on the new developer guides and function references.   Most of the knowledge we’ll need has to come from the “old” Codex wp.media pages.

The basic concept is simple:  When the user clicks the add button create a new wp.media JavaScript object and open it.

wp.media Details

We start by defining a media_frame variable in JavaScript at a global scope so we can make it a singleton; in essence don’t fire up a new popup wp.media form every time someone clicks the button.

Next we attach an anonymous function to the click event on the button that will do some general maintenance, set some properties for the wp.media object, and tell that object what we want to do when the user selects a file.   Remember that whether the user has just uploaded a new file or picked from an existing file the application considers this a ‘select’ action in either case.    The last step of this click function is to open the wp.media object which fires up the standard WordPress upload/select window.

Example Details

The initial implementation:

Always preventDefault() on these actions to prevent conflicts with other things that may be attached to the click event stack in  your web app or browser.

Check if the media_frame global variable is active.  If it is then our media form is open somewhere.   Re-open it (show it) and leave.  This cuts down on the overhead of re-creating the window object every time we click the button.

Create the wp.media object and attach it to the media_frame variable.  This is a single instance of the media selector.  Pass in some attributes for the window title, button label, and multiple selection options.   Later we’ll use localize_script() in the PHP side of the code so we have an app that is gettext() , and thus i18n/l10n, compatible.

Before we get into the frame  processing ,  grab a sibling field on the HTML form that contains the add button we clicked to fire up the form.  My example HTML form has an input text field with the class mea_box_upload_file where we put the URL for the file we are selecting.

The select is processed with an .on( ‘select’ … ) trigger.    When the user clicks the “Use This” button (our label for the select button) it will fire the anonymous function defined here.   The details of the file they selected is retrieve in the media_frame.state().get(‘selection’).first().toJSON() call.    I then use the variable I set earlier that points to my URL field on my HTML form and set the value to the file’s url property.

With all the triggers and properties set for the media window I can now open the media selector as the last step of the button click process.

 

Follow Up Feb 1 2017

Additional notes and a complete code example.

First – wp.media has a frames property where you can (should?) attach your active frame instead of managing it “by hand” within your JavaScript.

Second – return false from the function that you invoke the media from.   Depending on where you call your modal open() you may get the entire stack of media windows opening.  In my plugin I was getting my custom modal plus a default modal provided by WordPress on an admin setting page.

Third – I’ve refactored the default code you see on nearly EVERY example of doing the frame open where it first checks to see if the frame exists, if it does return frame.open() and if it does not do the setup then return frame.open().   Nearly every time you call the same method twice within a few lines of code it can be refactored to be more efficient.  That is included in my example.

New Example

This is from my Store Locator Plus code.    I use a block scope variable that I change every time a user clicks a button so I know which setting to update with the image URL the user selected.    This allows the on(‘select’) code for the window frame itself to remain static.  Better caching and code compression can happen with this setup.

The class the creates the HTML code.  Obviously a partial example as this web app uses all kinds of objects and inheritance to manage oft-repeated code segments.

 

JavaScript: wp.media

The Codex says… JavaScript Reference wp.media.

wp_enqueue_media()

This magic WordPress function will enqueue all of the JavaScript elements needed to work with the media uploader provided by WordPress core.

Needs to be fired in/after admin_enqueue_scripts in the call stack.

The Codex says… wp_enqueue_media() 

The Code Reference says… wp_enqueue_media().

Hacking WordPress – Articles About Coding Plugins and Themes

Recently I’ve begun building a music directory site.    I purchased a premium theme and the accompanying premium plugins.    Sadly, premium does not necessarily mean well-written.   Turns out this theme and the associate plugins do a few good things, look great on the surface and do a lot of really horrid things in the background.   If I were launching a personal site, like this bio blog, I’d not worry about it.   However the long term goal is to build a music directory that handles hundreds-of-thousands of page loads every month.   It needs to be fast without having to run it on IBM’s Watson to do so.

I’ve decided to fork the theme and the plugins and start hacking them into shape.    Coming soon will be the first in a series of “hacking WordPress” articles.   The first article will be about how to now load your plugin code on every single admin page on the site.

If you are writing WordPress Themes or plugins and have a question or an idea for a post, please let me know.   If I can’t answer the question myself I will try to find a guru that can.

 

Stop Loading Your Code On Every Admin Page

I see it every day.   It comes with the territory of supporting thousands of Store Locator Plus customers around the world.   We login to a customer’s site to find the reason our plugin is not working is because some other plugin is pissing their code all over our admin pages.

There is seldom, if ever, a reason that your plugin should be loading your code on my pages.   But it is far easier to load your code everywhere on every page.    It is far, far easier to write something like “if this is an admin page, load my code” than “if this is one of MY admin pages, load my code”.    Sadly it is really not much more work to do the second option and every single WordPress site your plugin runs on will be that  much faster.    Now if the other dozen plugins and the theme itself would be that smart WordPress sites everywhere would be a lot happier.

This video blog discusses how the plugin “HMLive Extensions” that I’ve forked and renamed to protect the guilty is “doing it wrong”.    It is loading a huge block of HTML code on EVERY SINGLE ADMIN PAGE then hiding it when it can with CSS trickery.  That only makes it slower and more bloated.

The take-aways from this video should be:

  • Use classes and object oriented programming techniques.
  • Do not use is_admin() by itself to decide whether or not to load your huge chunks of code.
  • Use class_exists() to test a class exists before defining it.
  • Use a simple array with slugs , in this case $_REQUEST[‘post_type’] variable values, to determine which admin pages to run your code on.

No this is not the “be-all-end-all” fix for this type of issue.    The posts and videos in this series are meant to be a general guide to shed some light on ways to improve code implementation and hopefully make your plugins and themes better citizens within WordPress.   Incur overhead only when needed and don’t stomp on other people’s code.

Some jQuery Foo I Learned While “Leveling Up” This Weekend

I am the first to admit that I got on the JavaScript bandwagon a little late.   I was a bit hesitant because of my work with government projects a half-decade ago.   When you work with the US Government you quickly forget the “best of” when coding web apps and instead use their default protocol of using “the oldest crap possible”.   I would not be surprised if they are still using Internet Explorer 6 as their go-to standard.   For the non-geeks, that is the equivalent of setting the standard vehicle fleet to a Ford Model T.  Sure, it is a car that runs on petroleum but it sure as hell isn’t going to get you and your family ANYWHERE safely.

Just 2 years ago I started adding some JavaScript to my locator web app.    It helped bring my 2013 app up to 2001 web interface standards.  A little.   Then I learned about jQuery, a library of features and functions that does a lot of the heavy lifting for you.   It is like going from sawing your own lumber from trees to going down to the lumber yard and picking up 2x4s to build your home.   Much easier.

The Slightly Newer But Old Way

Then I learned jQuery and many of the pre-built “nice to have frills” come shipped with WordPress Core.  What?!?!  Why do 90% of the plugins and themes, from which I snarf a lot of code to make it look like I know what I’m doing, not know this?    During the past year I’ve been learning a lot of new code tricks from my friends at DevriX and teaching myself more by learning new things like advanced jQuery trickery.

New Themes

So now, way down here after my rambling, are my notes on what I learned about jQuery this weekend where I felt myself “level up” on that particular skill.

As you read these tips you’ll notice that I use jQuery “long form” vs. $ which is common practice. I have a good reason for that; lots of WordPress plugins are poorly written and assign no-conflict mode with the $ shorthand improperly and break my application.  When you have 15,000 installations you tend to do things “long form but less prone to others breaking it”.  When I write jQuery… in my examples you most likely see $ instead in “real world” code.

Cache Your Queries

When you want to work with an element on a page you can use jQuery to help find the element and make your changes.    You tend to see stuff like this:

That is not very efficient. This jQuery( <selector> ) reads the entire web page each time and makes a whole lot of JavaScript code run EVERY TIME it is processed UNLESS <selector> is a JavaScript object instead of a string.   Lots of code running = slower web apps.

Instead make jQuery “cache” the objects that it finds the first time around by assigning the selection to a variable.  The “lots of code” runs once in the example here and in the examples below it will create a subset of elements to look through versus your entire web page stack of elements.

In this mode jQuery reads the entire web page ONCE and stores the matching objects in the_dash.   It then can quickly modify just those elements at requested.

Extend Your Cached Queries

Now that you are caching your queries and making your site visitor’s laptop or mobile device work a lot less , which believe it or not can extend their battery life by a whole microsecond, you can extend those caches without doing the “whole read the page thing” again.

Here is how I used to find the sidebar, modify it, then find all the images in the sidebar and hide them:

Nice short code which is a little easier, maybe, to read, but this is horribly inefficient in the “350px” mode.   In this mode JavaScript is reading the entire web page, seeking the sidebar, and changing it.   Then it goes and reads the entire web page again, finds the sidebar, then reads everything in the sidebar and finds the images and changes them.    That is a lot of JavaScript code executing.  Executing code takes time. Time is money as they say.

And here is the far more efficient version:

In this mode it reads the entire page once, and keeps track of what it found in “help_sidebar”.   It then changes what it found without searching again because jQuery is working on help_sidebar which is an object.  If that were all we were doing with it  this would actually be a bit slower since we take the overhead of storing the object with an assigned memory pointer (variable assignment) as noted above.

However when we do the second “change all the images inside that object” we gain back that lost microsecond one-hundred fold.     The second jQuery(help_images)… that is used to modify the image within no longer has to search the entire web page.

BUT… there was a problem.    How do you add “extended selectors” to the cached jQuery?

Above we had ‘.dashboard-aside-secondary > IMG’  to find our images.  This is MORE than just the ‘.dashboard-aside-secondary‘ that we stored in our cache.    Uggh.

Find() To The Rescue

Luckily jQuery has a number of methods that extend your selectors and help you traverse the DOM.  You can find this under the Traversing jQuery docs page.

find() can take any selector or OBJECT, like the one we have containing our sidebar, and then look for any elements inside of it.   As a jQuery padawan I had only ever seen this used to find stuff within the entire DOM.   Being a slow-learner it never dawned on me that this could be extended to ANY part of the DOM not just the entire DOM.

jQuery(help_sidebar).find(‘IMG’) looks within the sidebar only to find images.  This is far faster than reading the entire page.   It then changes those images within.

Children() Is One Level of Find()

One of the incorrect paths I went down, but is very useful to know, is the use of children() in jQuery.    This finds only the matching elements just one level deep in the object stack.   Since you’ve read this far you are a code geek like me so I know that you understand that most web pages are many levels of nested elements and often you want something “deeper down” where you need your great-great-great grandson to be involved.    However there are plenty of cases where I can utilize children() to impact just the next level of menu divs, for example.

Summary

Truly understanding how jQuery selectors and “caching”works and how to modify those cached selections with the jQuery traversal methods is going to bring the efficiency of my apps up to a whole new level.    It may only save a half-second of processing time per page interaction, but it all adds up when you have 15,000 websites hosting millions of page views every day.

For my fellow code geeks out there I hope you learned something new and I’ve given you a shortcut to reaching the next level of your jQuery skill.s

Sidebar: Why “caching” in quotes? Because this doesn’t seem like caching to me but rather object-passing, but maybe I’m missing something I’ll learn at level 3.

A WordPress Plugin Coding Adventure – A Love Hate Relationship

Over the past 48 hours I spent time between numerous meetings writing another WordPress Plugin, a WooCommerce Report Modifier that we need for our Store Locator Plus store.   I’ve not written a “new from scratch” plugin that was not related to the Store Locator Plus system for quite some time.    It gave me a new perspective on the entire WordPress plugin coding adventure.

Open Source Is Great

One of the biggest things I re-affirmed about coding plugins for WordPress is that I LOVE open source.   As a developer this love for open source has NOTHING to do with price.    I don’t love it because the prices of open source are often lower than “regular” software; in face I think that is on of the biggest problems with open source is the stigma … or general idea that users THINK… the products should be free or darn-near it.    What I love is that I can both FIX things and SEE HOW THEY WORK.     The fixing and the seeing were both key elements in being able to create a plugin that did what I needed in just over a day versus weeks or months waiting for the people at WooCommerce to do something for me.

Starting A  Plugin Is Easy

I also realized that despite my difficulties in writing my first plugin years ago, the entire process is actually fairly easy and does not to be as complicated as many people make it out to be – even when using well defined classes in place of procedural code (please, please , please do NOT write your plugins using “inline” or procedural code).   For a private plugin you don’t even need to create a readme.txt file.   Create a directory in the plugins folder of WordPress, name a file the same thing as the directory with a .php extension and add a few lines of code.

Since I could read the original source of WooCommerce I knew exactly which hook I needed to invoke and whipped up the first function in a few minutes.

Hooks and Filters Are Paramount

Speaking of hooks, learn about them and USE THEM.   Implementing WordPress hooks and filters is critical to properly augmenting anything in WordPress.   Since most well-crafted plugins, including my Store Locator Plus base plugin and add on packs,  and themes also sprinkle hooks liberally throughout their code.    In my case I could find a WooCommerce hook that allowed me to modify the product report without messing with the original code from WooCommerce.      Since  WooCommerce was open source I could read the report-generating code and find the exact hook I needed within minutes.   Very nice.

Plugins and Plugin Components Are Not Created Equal

In the bad category, I was reminded almost immediately that not all plugins or plugin modules are created equal.  WooCommerce is a beast that has lots of code with lots of modules written by different authors and includes obsolete elements and dead-ends; much like Store Locator Plus.   This means that outdated practices are built into the code as are some algorithms that were created during the “hey, I’m learning WordPress coding” phase of plugin development.   Typically that means little-or-no ability to modify that piece of code and change how it works because there are NO hooks or filters to tap into.   Boo!

Inconsistent Plugin Architecture = More Plugins

The lack of hooks and filters is true with MANY of the WooCommerce native elements like the Customer List report or Orders search interface.     That meant recreating an exact copy the Customer List I wanted to modify and adding in hooks, filters, and feature to my “Extended Customer List” report.   That means I have a plugin that shares 70% of the Customer List coding from the WooCommerce product which in turn means my live site now has over 100 lines of code installed that should never have been necessary.    Multiply that by the 48 plugins I need to run my site properly.

More plugins = more bloat = more maintenance and security issues.

The lack of a true coding standard for plugin developers , or maybe it is a training issue, causes a LOT of repeat work in the WordPress plugin ecosystem.

Documentation Is Inconsistent

As you delve deeper into the WordPress plugin coding world you soon realize not all documentation is created equal.   Some WordPress functions are well documented internally and externally with full phpDoc comments a presence on the “old and outdated” Codex and on the “new and improved” WordPress Code Reference.  Many functions, however are incomplete at best with no examples and little-or-no community knowledge on the WordPress Code Reference site.   Often you end up jumping back to the older Codex site.    In some cases, rather infrequently with WordPress Core, you find functions or classes that are not even listed or documented on EITHER site or if they do appear through the coding “fu” on the WCR they have zero useful information thanks to the lack of proper phpDocs.    In the end you read code and trace execution (thank you again Open Source).

Plugins are exactly like that but worse.  Many, including the super-popular WooCommerce plugin, have sparse-if-any online documentation on CODING for the product.   Almost NONE have any easy way to ferret out those hooks and filters.  You always end up spending time tracing code execution to find those “nuggets of knowledge”.

Contributing Code Is Futile

Well, mostly futile.    If you code plugins or themes for long enough you will find a bug in SOMEONE ELSE’S CODE.    Most organizations have a way to report the bug and contribute your patches to fix the bug back to them so they can improve their products.   Most organizations take their sweet time getting around to it.  Often your patches never get put in place.

My personal experiences: Store Locator Plus was born from over 100 patches to a plugin that were submitted to the author but never integrated despite a few-dozen emails to the author.  In fact the response was “I’m on haitus”.  For over a YEAR.      My original fixes to dbDelta, admittedly a critical component of WordPress that warrants extra care-and-attention, took OVER TWO YEARS to finally make it into core.   Patches to bbPress and over a dozen other lesser-known plugins were never even looked at.   WooCommerce?  We’ll wait and see.

It would sure be nice if plugins, especially those hosted on the WordPress Plugin Directory, had a formal and consistent patch submission system.   Even better, a community “vote” to allow a “master of all plugins” to accept the patches on behalf of a MIA plugin author.   I’d check off the “allow WordPress Plugin Team to integrate patches” on my repo.

Internal Use Only Components Are Bad

WP_List_Table.    It has been clearly marked as “for internal use only”.    Yet nearly any significant plugin out there is running a class that extends it.     Which is fine except you are NOT SUPPOSED TO DO THAT according to WordPress.  You are supposed to clone the entire class (more duplicate code – see above) and build off of that.

Yes, I get why that is, but that kills one of the key concepts that makes WordPress great – the ability to extend and augment with hooks and filters not copies-and-hacks.

PHP Versions Are  A Pain

The WordPress Plugin Team announced, just yesterday, that you should run PHP 7 lint against your code.  VVV runs PHP 5.5.  WordPress itself says PHP 5.2 is fine.   Most hosting companies are running PHP 5.5 or lower.

When you code using a version of PHP newer than 5.2, as I do on my VVV box, you have a high risk of creating a plugin that passes all testing but as soon as someone runs it on PHP 5.4 it crashes.  Hard.

<!– start sidebar –>
WordPress must set a baseline standard for PHP.  If it is 5.2 that’s fine.   You cannot have your plugin team recommending PHP7, your core team recommending a development environment that defaults to 5.5, and  tell hosting companies PHP 5.2/3/4 are fine.

    
IMO WordPress needs to set the standard to PHP5.6 which they ‘recommend’ but do not require.   When 28% of the Internet is using your technology you can dictate what version of PHP is running nearly everywhere.
<!– end sidebar–>

Overall It Is A Good System

In general the system WordPress has  in place for plugin development is pretty darn good.  I think it is one of the biggest reasons WordPress has gained market share and has so many plugins available.     The system needs work.  The code needs work.    But in reality, what big world-changing products don’t need to evolve on a continual basis.

At the end of the day I could get what I wanted to accomplish done in just over a day.    I have my new report of customers that I can export to a CSV file, sort, hide columns and do all of the things I needed to get a project done.  (BTW, I’ve posted my WooCommerce Report Modifier on my store in case anyone else wants to check it out.)

Open source made that possible.  At the end of the day that is the BEST argument for why I think Open Source is the best option for releasing software.   Now if we can just convince the consumers that it is worth MORE TO THEM than closed-source system.   Open Source software should be priced HIGHER than other solutions not “Nearly Free”.

%d bloggers like this: