Things You Almost Certainly Will Never Need to Know: WDS-Shortcodes

I work on a plugin that has WDS-Shortcodes as a dependency. Attempting to run composer install on this plugin will fail with the error message:

Could not scan for classes inside "/public_html/wp-content/plugins/lqd-messages/vendor/webdevstudios/wds-shortcodes/vendor/tgmpa/tgm-plugin-activation/class-tgm-plugin-activation.php" which does not appear to be a file nor a folder

This is because the WDS-Shortcodes on packagist has the following in its composer.json:

"autoload": {
    "classmap": [ "includes", "vendor/tgmpa/tgm-plugin-activation/class-tgm-plugin-activation.php" ],

The problem is that this file doesn’t yet exist. Once this failure has occurred it can be remedied by going to: vendor/webdevstudios/wds-shortcodes/ and running composer install. After that you can go back up to the main project directory and run composer install again and it should succeed.

The patch for this has been made on the github repository, unfortunately it hasn’t made its way to packagist. So lets try using the repository code directly by adding the repo as a repository to our composer.json like so:

"repositories": [
      "type": "vcs",
      "url": ""

We’ll then update our require in composer.json to point to dev-master. The line "webdevstudios/wds-shortcodes": "^1.0.7", has the version number swapped out for dev-master. For kicks and giggles lets delete our vendor directory, composer.lock, and run composer clear-cache.

This time everything installs without issue (huzzah!). We pull up a page on our dev site and it crashes. Looking at the debug.log again we see:

PHP Warning: require(/public_html/wp-content/plugins/lqd-messages/vendor/composer/../webdevstudios/wds-shortcodes/vendor/jtsternberg/shortcode-button/shortcode-button.php): failed to open stream: No such file or directory in /public_html/wp-content/plugins/lqd-messages/vendor/composer/autoload_real.php on line 69

This issue arises because WDS-Shortcodes is attempting to load the shortcode-button dependency from within its own vendor folder, which doesn’t exist. The shortcode-button dependency does exist in the main plugin’s vendor folder but the composer.json for WDS-Shortcodes is hard coded to use its own vendor directory.

Okay, this is an easy fix. Run composer install from within the WDS-Shortcodes folder. Refresh our page and we are still crashing. What now?

PHP Fatal error: Class 'WDS_Shortcodes' not found in /public_html/wp-content/plugins/lqd-messages/includes/shortcodes/class-shortcodes-run-base.php on line 7

Here the problem is that Composer when it creates the autoload files doesn’t add any autoloading of WDS-Shortcodes’ files. Why not? Because WDS-Shortcodes doesn’t have any autoloading setup in its composer.json. There are two ways we can resolve this. One is to edit WDS-Shortcode’s composer.json and add to the "files" portion of "autoload": wds-shortcodes.php. But, for whatever reason, this won’t be picked up by Composer even if we do a composer dump-autoload -o, manually delete the autoload files, and run composer dump-autoload -o again. So lets leave this option for the moment.

The other way we can handle this is by editing our main plugin’s composer.json to include the autoloading specifications. This involves adding "vendor/webdevstudios/wds-shortcodes/wds-shortcodes.php" under the "files" section of "autoload".

Welp, that doesn’t work either. Lets try instead adding this file and all the files in /includes to the classmapping:

  "autoload": {
    "classmap": [
    "files": [

Huzzah! That works! But there was a lot of manual work to get things going. Lets try removing the path specification for shortcode-button in WDS-Shortcodes composer.json to see if we can simplify things a bit (we already have shortcode-button installed as a dependency of the main plugin). After making this edit we run composer dump-autoload -o and get an error:

PHP Parse error: syntax error, unexpected ''4cfe55868654b2ea9298a46af9d2b' (T_ENCASULATED_AND_WHITESPACE), expecting end of file in /public_html/wp-content/plugins/lqd-messages/vendor/webdevstudios/wds-shortcoes/vendor/composer/autoload_files.php in line 11

If someone wants to clue me in on exactly why this happens, I’d appreciate it. The quick way around this issue is to manually delete the composer autoload files. Once this is done one can rerun composer dump-autoload -o and composer will complete without error.

If we refresh our page that seems to have gone okay. But what if we try to actually remove the vendor/jtsternberg/shortcode_button in wds-shortcodes? I’m a glutton for punishment, let’s find out. Reloading the page seems to work!

What if we forked our own version of WDS-Shortcodes and attempted to make the changes we’ve made above to composer.json and see if we can’t avoid all this manual work? I’m game.

We update our main composer.json to point to our new repo instead of WDS’:

"repositories": [
    "type": "vcs",
    "url": ""

We’ll also delete our composer.lock and vendor files/directories and run composer clear-cache. Then, with fingers crossed, composer install. The install goes smoothly, reloading the page not as much.

PHP Fatal error: Class 'Shortcode_Button' not found in /public_html/wp-content/plugins/lqd-messages/vendor/webdevstudios/wds-shortcodes/includes/shortcode-admin.php on line 3

Ahh, that’s right, I need to add the shortcode_button as a dependency to the main composer.json file, under "require" I add: "jtsternberg/shortcode_button": "^1.0.7", just like it was in WDS-Shortcode’s composer.json previously. I go through my regular rigamarole (delete composer related files, run clear-cache) and do composer install again. Its successful but I’m still getting the same error as above.

Ahh, that’s right. I need to tell Composer to autoload the shortcode-button code, so I add the same "autoload" "files" statement from WDS-Shortcode’s composer.json (okay, so I did try adding the statement to "classmap" first, but it didn’t work, so…): "vendor/jtsternberg/shortcode-button/shortcode-button.php".

Regular rigamarole, refresh, and the page loads! So this is better but still not perfect. Ideally I wouldn’t need to manually add short-button as a dependency to my plugin or tell composer to autoload the files…but for the moment, it works.

DevKinsta: Initial Thoughts and Feedback

I’ve used a variety of local development environments over the years and some of them have done a decent job – but none of them have been the one. Back in January when I first saw DevKinsta announced over on WP Tavern I was excited to give it a try. I’ve finally tried it and wanted to share some thoughts and feedback on DevKinsta.

Installation (on Windows)

DevKinsta uses Docker to containerize the various services DevKinsta launches. Docker utilizes Windows Subsystem for Linux 2 (WSL2) under the hood which is part of the Windows Operating System. You’ll need to install it and Docker if you haven’t already.

The official documentation suggests you need to have Hyper-V and Containers enabled in Windows Features (Settings–>Control Panel–>Programs and Fetaures–>Turn Windows features on or off) but I have neither enabled on my system. All I have installed on my system is the Virtual Machine Platform and Windows Subsystem for Linux.

Once you have WSL2 and Docker installed and enabled you can actually install Kinsta. The Kinsta portion is straightforward.

First Installation Screen

Kinsta installs itself in Users\{user}\AppData\Local\Programs\DevKinsta which still seems a bit weird to me. But I’ve seen other apps doing this as well. I’m not a huge fan of having so many places to look for installation applications (above, Program Files, Program Files (x86), AppData\Roaming, etc.) but I’m guessing this is the new and Microsoft sanctioned way of doing things?

Choose the location for DevKinsta to install.

Once the install is complete you see the completed screen and can run DevKinsta. Woohoo!

The Completed Install Screen for DevKinsta

Running DevKinsta for the First Time

The first time you launch DevKinsta it will take a while to launch (I’m guessing at least several minutes for most folks). This is because DevKinsta is downloading the images it uses with Docker…and I do mean images plural: devkinsta_fpm, devkinsta_db, devkinsta_nginx, devkinsta_mailhog, and devkinsta_adminer (that’s five images if you weren’t counting).

DevKinsta Downloading Images. 1 / 5 indicates it is on the first of the 5 images.

After this first time things move much more quickly. The normal loading screen you’ll see is gone in a flash. I expect there will still be occasional slowish starts as Kinsta makes changes to the underlying images and these changes need to be applied to the containers running on each of our local systems.

Your Day-to-Day DevKinsta Loading Screen

Creating a New Site

Once DevKinsta is loaded you are provided with a slick UI and three big options: New WordPress site, Import from Kinsta, and Custom site.

The Create New Site Screen

Adding a new site is extremely simple. Give the site a name and provide a username and password and Kinsta takes care of the rest.

New WordPress Site Screen

Note that Kinsta is making all the decisions for you if you take this route. If you need to customize your install you’ll use the Custom Install option. Using this option you can choose a PHP version, enable HTTPS, select a specific WordPress version, create a Multisite and so on. Still pretty simple and straightforward.

Custom Site Screen in DevKinsta

The Docker Warning

At some point you’ll be warned by Docker that Docker has detected Windows files being shared into a WSL 2 container and that this may result in poor performance.

Lets break that down a little. Windows Subsystem for Linux 2 is a Windows component that provides a light Linux installation. Docker uses WSL 2 when creating containers. If you mount files from your Windows filesystem to the container (which is utilizing WSL 2) the speeds deteriorate. You really want to have your files mounted inside of the container.

I usually spin up containers from within WSL to avoid this problem but Kinsta didn’t. Why not? Because Kinsta wants you to be able to browse and edit those files in your native Windows filesystem and when they are inside the container they are available on the Linux filesystem and not the Windows filesystem.

Now technically that isn’t entirely true. You can setup a network share from which you can access files within your Linux container, but that all is a bit messy. Maybe someday they’ll add an option that allows you to have the files reside in the container rather than on the Windows side and we’ll see some performance boost, but I understand why they did it this way.

Docker Warning About Using Windows Files in WSL 2 Container

The Individual Site Dashboard

Once the setup of your new site is complete you get a nice dashboard that allows you to manage your site.

Most of the Site Dashboard Screen
The rest of the Site Dashboard Screen

You can see the features yourself so let me just point out a few of the highlights:

  • You can quickly open the site frontend or backend (WP Admin).
  • The Database Manager (Adminer) is easily accessible.
  • The site host provides the URL and the site path provides the file system location of your site.
  • You can toggle SSL/HTTPS on.
  • Information about the database is clearly presented allowing for easy remote access.
  • You can easily toggle WP_DEBUG on and off.

There is one glaring omission – there is no one click access to the terminal. Instead you’ll need to whip out some command-line expertise – or read the DevKinsta documentation.


The Settings are rather sparse – two that I think deserve attention here. First there is the option to enter a site path – this is the local filesystem location for projects. This isn’t for a specific project but for all projects overall.

The Settings Screen
The Rest of the Setting Screen

The second is configuring what you want the default username and password to be for WordPress installs. It’s a minor convenience but still nice.

Visual Studio Code Dilemma

VSC has this killer extension (err extensions) that allow one to work on remote (or virtual) systems as if they were local. Unfortunately, when you attempt to connect the kinsta_fpm container to VSC you are dropped in the root directory and the Kinsta files are nowhere to be found.

You can break out by using File–>Open Folder and navigating to /www/kinsta/public/some-site but it isn’t ideal. I’d love to see a tighter VSC integration.

How Does It Compare to Local?

Overall I’d say it compares pretty favorably to Local. Personally, I don’t like having my dev servers installed on my local host, I want them virtualized or containerized. This reduces the surface area open for attack on my local machine and helps avoid munging of my system which happens on occasion by even the best intentioned software.

How Does It Compare to VVV?

DevKinsta is simple to use and its reliance on Docker rather than Vagrant/VirtualBox is attractive to me. However, VVV still comes with more tools – PHPUnit, Xdebug, Composer, NodeJS, git, and Subversion being the most notable in my opinion.

I’d love to see these tools included with DevKinsta as well…NodeJS especially needs to be there for folks to do Gutenberg development.

Summarize/Expand on Missing

I like a lot about DevKinsta but there are some features that are missing of varying importance. I’m going to summarize up those I’ve already listed as well as add a few more below.

  • High Priority
    • Addition of button on Site Info to launch shell.
    • Addition of NodeJS.
    • Addition of PHPUnit, xdebug, and Composer.
    • Addition of git and subversion.
    • Be open source.
  • Medium Priority
    • Addition of Apache as a web server option.
    • Better integration with Visual Studio Code.
    • Provide the option to use the GUI to manage wp-env.
  • Low Priority
    • Provide option to use WSL 2 filesystem rather than native Windows filesystem to improve performance.
  • Low Low Priority
    • I’d like to see more logging detail on what DevKinsta is doing while updating my Docker containers. Not because I actually need to know but it soothes me to see the logging scrolling past indicating that no, this software has not frozen.
    • I’d like to see more logging detail in general without having to dig for logs.


DevKinsta is a solid option for a local dev environment. I’d like to see them open source it and think this would facilitate faster innovation. The lack of Gutenberg support built-in and the lack of native tooling is a big bummer for me. I’m leaning towards using wp-env at least for the moment.

Your thoughts?

Migrating a WordPress Site to SpinupWP the Easy Way

The wonderful folks at Delicious Brains made SpinupWP available to the general public in May of 2019. Since everything that comes out of Delicious Brains is top notch, SpinupWP caught my eye – but there is a certain inertia that affects some of us (me) when it comes to moving sites between hosts (bit of a misnomer in this case)…so I put it off until I couldn’t bear the poor performance and support of my current web host anymore and thus I gained some ertia.

Launching a new site using SpinupWP was fairly simple and the performance was impressive! My ertia was ready to migrate my existing sites until I saw the official migration guide from SpinupWP. The steps are sysadmin’ish (nothing wrong with that), involve a number of manual and time consuming actions, and I would need to repeat them for each site I wanted to move.

My ertia inertia’ed again and I decided to stick with my current host – until their Varnish cache kept failing (even after they “fixed” it) every few minutes resulting in every site visit hitting the database. I still wasn’t ready to go through the monotonous toil involved in following the official migration instructions repeatedly, so I struck out to find a simpler way–and I’m here today to share it with you!

When To Use This Guide

The SpinupWP site migration guide offers a number of best practices which should be utilized by high traffic / high importance sites. This guide is best utilized by individuals running small or medium traffic sites where a brief period of downtime will not be an issue and where time is a more precious commodity than robust process.

I hope this guide will be particularly useful to individuals without a technical background who are running / managing WordPress sites. To this end, I have included a copious number of screenshots and have attempted to be meticulous in recording even the smallest steps involved in this process.


You will already need to have a SpinupWP account created and have spun up a server, see the official Getting Started documentation for information on how to do so.

You will also need to follow SpinupWP’s directions for setting up transactional emails assuming you do not already have a solution in-place.

Warning: Many web hosts offer email servers as part of their platform. SpinupWP does not (for good reason). You must setup a transactional email solution if you want any emails to be sent by WordPress (e.g registration, comment notification, password reset).

High-Level Overview

Before we dive into the weeds, lets take a quick look at the major steps you’ll be undertaking to migrate your existing site to SpinupWP using my “expedited” process:

  1. Create a New Site on SpinupWP.
    1. Update TXT records with your Domain Name Server (DNS).
  2. Use the freemium plugin UpdraftPlus to create a backup of your existing site.
    1. Sidebar: Using Local Storage.
    2. Sidebar: Using Remote Storage.
    3. Sidebar: Cleaning Up WP.
  3. Modify your computer’s host file to use your new site.
  4. Restore the backup taken earlier using UpdraftPlus to your new site.
  5. Add SpinupWP and Limit Login Attempts Reloaded plugins to your new site.
  6. Update DNS records with your Domain Name Server (DNS) to point to your new server.

Create a New Site on SpinupWP.

Login to your existing SpinupWP account.

Screen Shot of SpinupWP Login Page

Choose “Add Site”

Note: If you are like me, your eyes are drawn to the big pinkish button “Spin Up a New Server” and don’t immediately see an option to “Add Site”. Look underneath the pink button to find the white “Add Site” button.

SpinupWP Dashboard Screenshot

You are now in the “New Site Wizard” and will setup your “Domains”.

Screenshot of SpinupWP Add Site Domain Wizard

Ensure that “Enable HTTPS (SSL/TLS certificates)” is checked and enter the domain name of the site you are migrating to SpinupWP. Usually you’ll be making two entries on this page – and – where domain is the name of your site and .com is the top-level domain (TLD, e.g. .com, .net, .org).

Click Next. You will are now asked “Are you ready to point your domains at this server?” The answer is “No” – for the time being we want visitors to to continue going to the old host and not to SpinupWP.

Screenshot of SpinupWP Add Site DNS Wizard

Click Next. You’ll be shown two sets of TXT records you need to add to your domain name server (DNS). Oftentimes your DNS is the same as your Domain Registrar. For the sake of this example (and because I think they are awesome) we’ll be using Cloudflare as our DNS.

Sidebar: Configuring DNS Records with Cloudflare

Open a new tab in your browser and go to Cloudflare’s site. In the header menu you should see a “Log In” option to the left of “Sign Up” and a big orange “Under Attack?” button, click on “Log In”.

Screenshot of Cloudflare Login Window

After logging in you will be presented with the domains you have registered with Cloudflare.

Screenshot of Cloudflare Dashboard

Click on the appropriate domain and you’ll be brought to the Cloudflare Domain Dashboard.

Cloudflare Domain Dashboard Screenshot

Towards the top of this page you will see a number of blue and white buttons – Overview, Analytics, DNS – ahh, that’s the one! Click!

Cloudflare Domain Dashboard DNS Screenshot

You’ll want to add two records. Click on the blue “Add record” button.

Cloudflare DNS Add Record Screenshot

You’ll change the Type of the record to TXT and then copy from your SpinupWP tab the top left column’s content into Name. Leave TTL as is and copy into Content from the SpinupWP tab top-right column. Click Save.

Repeat this step again for the second TXT record, this will be on the SpinupWP tab and you’ll be using the bottom left column for Name and the bottom right column for Content. Click Save.

Once you’ve added these records they should now appear in Cloudflare, something like this:

Cloudflare DNS Records Added Screenshot

End Sidebar, Resume SpinupWP Site Setup

Great work! Go back to the SpinupWP tab and click the pink “I’ve Updated the DNS” button.

Screenshot of SpinupWP Add Site TXT Records Wizard

SpinupWP will check the new records have been added and show a successful confirmation message:

SpinupWP Add Site Wizard DNS TXT Records Confirmed Screenshot

Click Next. You’ve now completed the Domains step of the New Site wizard and moved on to Installation.

SpinupWP Add Site Wizard Install Software Screenshot

Click on WordPress and ensure the checkmark appears next to it. Then click on Next.

You’ll now need to provide some information to SpinupWP about this particular instance of WordPress. Note that the Title, Admin Username, and Admin Password will all be overwritten once you restore your site to this WordPress instance later in this process.

Note: Make sure you copy down the username and password, you’ll need these until you restore your site.

SpinupWP Add Site Wizard WordPress Prompts Screenshot

Click Next.

Congratulations! You’ve left the Installation section of the New Site Wizard and are now working with the Database section.

SpinupWP Add Site Wizard Database Screen Screenshot

You’ll be creating a new database. Note that the database name, username, password, and table prefix are not overwritten when you restore your site later with UpdraftPlus, so make sure you use sensible names and save them somewhere safe!

Note: If the database you restore uses the wp_ prefix then SpinupWP retains their unique prefix, but this is only if you are using the wp_ prefix.

Click Next. Almost done!

SpinupWP Add Site Wizard Settings Screenshot

Choose a logical user name for your site. This is the Linux user name that runs the processes and owns the files on your server.

Unless you have a reason to do otherwise, choose the latest stable release of PHP (currently 7.4) and enable the full page cache. Then Click Next!

You’ll be presented with the Confirmation Wizard page. I’ve split it into two screenshots below.

SpinupWP Add Site Top of Confirmation Screenshot

Review the listed settings. They should match what you’ve entered throughout the wizard.

SpinupWP Add Site Wizard Bottom of Confirmation Screenshot

Ignore the Site Migration Guide note and click “Add Site”.

Wait a second, what is this? Did I fail to setup the site? It seems as if I’ve been dropped into a similar site setup wizard?

SpinupWP Settings Page After Using Add Site Wizard

No, you’ve successfully created the site, unfortunately the SpinupWP dashboard for your new site is confusingly presented. Click on Back to Server on the top left to feel better.

Using UpdraftPlus to Create a Backup

Now that we have a brand new WordPress install setup on SpinupWP we can actually get to work. Log in to your existing WordPress site and install the freemium UpdraftPlus plugin:

Plugins –> Add New –> Search for “UpdraftPlus” –> Install –> Activate

Once the plugin is activated it can be accessed under:

Settings –> UpdraftPlus Backups –> Backup/Restore Tab.

UpdraftPlus Dashboard Screenshot
  1. Click on the Big Blue “Backup Now” button. A modal window will open.
  2. Make sure you have checked off “Include your database in the backup”, “Include your files in the backup”
  3. If you have configured (see sidebar below) remote storage, ensure “Send this backup to remote storage” is checked.
  4. Click “Backup Now” at the bottom right of the modal window.

You’ll be returned to the main UpdraftPlus page and you’ll be able to watch the progress of the backup. It should look something like this:

UpdraftPlus Backing Up Screenshot

Once the backup has completed you’ll see the Existing Backups section has files relating to your recent backup:

UpdraftPlus Existing Backups Screenshot

If you are using remote storage you are all set!

Sidebar: Using Local Storage

If you don’t want to setup remote storage, UpdraftPlus will save the backup files to your current host. You can then download your backup files to your computer and then upload them later to the new site.

In order to download the files you need to:

  1. Click on the button representing each type of backup data: Database, Plugins, Themes, Uploads, Others.
  2. On the same page will appear an option to download files of a specific data type. You need to click on each file and download it to your local computer.

Note: If your site is large UpdraftPlus will break the data across multiple files. For each file listed you’ll need to click on it to download. If there are three files listed under Database you’ll need to click on each individually (another perk to using remote storage).

Sidebar: Using Remote Storage

UpdraftPlus can save your backups to your local web host or a number of remote storage options. Click on the Settings tab to the right of the Backup/Restore tab to configure remote storage.

UpdraftPlus Settings for Storage Screenshot

Consumer level storage options include Dropbox, Google Drive, and Microsoft OneDrive. If you use one of these services I’d recommend using an account dedicated only to backups, separate from the account you use daily. If your site is compromised (hacked) in the future you don’t want the hackers to gain access to your files as well!

For more business oriented options Amazon S3, Microsoft Azure, and Google Cloud are available.

Select your desired remote storage provider and then Save at the bottom of the page.

You’ll be prompted to authenticate (login) to your storage provider. Once you’ve done so, UpdraftPlus will be able to backup files to and restore files from your remote storage.

Sidebar: Cleaning Up WP

Over time WP has a tendency to collect garbage. Themes we’ve forgotten to delete, plugins we no longer use, database tables or entries that are irrelevant. To speed up your site migration you can eliminate some of these files/data and thus decrease the time it takes to backup and restore.

Warning: I’d recommend taking a bloated backup first (before you make any changes!). It is far too easy to accidentally delete something you actually need.

Go through the following steps to clean things up:

  1. Check under Appearance –> Themes for unused themes and remove.
  2. Check under Plugins –> Installed Plugins for unused plugins and remove.
  3. Install the Advanced Database Cleaner (ADC) plugin and use it to scan for garbage entries in your database and remove.
  4. Use ADC’s Table tab to remove garbage tables.
  5. Use ADC’s Table tab Optimize option to reduce the size of your db tables.
  6. If you are looking for a nightmare, try cleaning up the media library.

Modifying Host Files

Next we need to “restore” our site to the new WP instance we’ve created on SpinupWP. To make this easy lets change our host file so that the IP address of our SpinupWP site is set as the location of our

I’ve included brief instructions on modifying your host file below. If the instructions don’t work for you, see this more exhaustive article on configuring hosts files by How-To Geek.


  1. Click on Start
  2. Begin typing “notepad”
  3. When the “Notepad” app appears as an option, right-click on it and choose “Run as administrator”.
  4. When prompted, allow it to run as administrator.
  5. In Notepad go to File –> Open
  6. Navigate to and open: c:\windows\system32\drivers\etc\hosts


Open Terminal and enter the command: sudo nano /etc/hosts

General Instructions

You’ll add a record to this text file that looks something like this:

The IP address is on the left and the domain of the site you are moving is on the right. You can find your site’s new IP address in SpinupWP.

Save the File.

What Is Happening Here?

Normally when we visit a website our browser contacts a DNS server which provides the IP address for the site we want to visit. When we add a record to our hosts file we tell our computer not to use the DNS record but instead to use the information in our host file.

If you’ve made this update to your host file, when you navigate to you should now see your new WordPress instance and not your existing site.

IMPORTANT: You will probably need to close your browser and reopen it before this will work. It seems browsers cache the IP address.

Restore the UpdraftPlus Backup to Your New Site

With your host file updated, use your browser to visit your new sites administration page (e.g.

Note: You’ll login with the credentials you created while adding the new site (via SpinupWP’s Add Site Wizard), not the credentials for you use regularly on your site.

Once logged in you need to install UpdraftPlus on this site:

Plugins –> Add New –> Search for “UpdraftPlus” –> Install –> Activate

Next we’ll navigate to the UpdraftPlus dashboard:

Settings –> UpdraftPlus Backups

Using Remote Storage

If you used remote storage to perform the backup on your old site you can restore from this remote storage to the new site.

  1. Click on the Settings tab.
  2. Under Remote Storage choose the same storage option you used previously.
  3. Click Save and then authenticate with the same credentials you used previously.
  4. Click on the Backup / Restore Tab.
  5. Under Existing Backups click on “Rescan remote storage”.
Updraft Plus Existing Backups Screenshot

After a minute or two you should see the backup you created earlier listed under Existing Backups. (Wife’s Editorial Comment: this would be a good time to get a snack).

Using Local Storage

If you used local storage and downloaded the backup files to your local computer you’ll need to upload them to the new site before they can be restored.

  1. Under Existing Backups choose Upload backup files
  2. Select all the backup files you want to upload.

After a moment your backup should appear under Existing Backups.

UpdraftPlus Existing Backups Screenshot

Performing the Actual Restore

Once you have your backup files loaded into UpdraftPlus you can click the Restore button under Actions. This will launch a restore wizard:

UpdraftPlus Restore Wizard Screenshot
  1. Ensure that all the available components are checked: Plugins, Themes, Uploads, Others, and Database.
  2. Click Next.
  3. You’ll be presented with the option to exclude some tables from the restore – don’t.
  4. Click the Restore button.

Rare Problem: If, for some reason, your WordPress database tables don’t have a prefix, UpdraftPlus will restore the tables but they will not be accessible. You either need to add a prefix to your database tables before making the transfer or after. I hope to write a separate article explaining how to accomplish this soon.

UpdraftPlus Performing Restore Activity Log Screenshot

UpdraftPlus will present a nice window informing you of all the activities it is taking. If successful you should eventually see a big green checkmark next to “Restore successful!”.

  1. Click on Return to UpdraftPlus configuration.
  2. You may be booted out to the WordPress login page.
  3. If you aren’t automatically booted, log out of WordPress.
  4. Log back in ( using your old site’s username and password.

Remember: When you restored the old site to your new site you overwrote the username and password you created while adding the site to SpinupWP. You’ll use your username/password from the old site going forward.

Add SpinupWP and Limit Logins Reloaded Plugins

SpinupWP automatically adds the SpinupWP and Limit Logins Reloaded plugins when you add a new site. By restoring an UpdraftPlus backup you have deleted these plugins.

  1. Go to Plugins –> Add New –> Search for “SpinupWP”
  2. Install –> Activate.
  3. Go back to Plugins –> Add New –> Search for “Limit Logins Reloaded”
  4. Install –> Activate.

That was easy!

Update Your DNS Server

Okay, to be honest, we aren’t just updating the DNS server. There are a few related steps we’ll be taking as well, but I promise they’ll only take a minute!

  1. You want to visit your site and make sure everything looks/works as expected. You could spend a lot of time here, that’s up to you.
  2. Once you’ve confirmed the site is working properly you should comment out the line we added earlier to your hosts file. The result should look something like this: #
  3. Now you can go update your DNS records. You’ll be replacing the IP address in your and records with that of your new site’s IP.
  4. Close out your browser and reopen it (or use another browser). You want to make sure the browser is not still using the IP you set in the host file.

Load up your site, it should be nice and snappy on SpinupWP’s service. Congratulations! You’ve successfully migrated your site to SpinupWP.

Remember: You need to setup transactional emails separately.

Tip: Make sure you remove any unnecessary plugins once you’ve migrated to SpinupWP, this includes any caching plugins you may have been using on your previous host.

When You Get an Invalid Default Value Error While Changing Your MySQL Engine on a Table from MyISAM to InnoDB

Instructions on overcoming a default value error when attempting to convert a MyISAM table to InnoDB.

Recently I noticed one of my older WP site still had some MyISAM tables. These days most folks are running InnoDB due to its advantages over MyISAM. If your WP database is using MyISAM you should probably update to InnoDB as well (ummm, make sure there isn’t some reason you need to stay on MyISAM first!).

Changing the engine for a table should be fairly simple:

ALTER TABLE dbname.prefix_users ENGINE=InnoDB;

But when I tried this I received an unfriendly error about an “Invalid Default Value” in a datetime column.

Ends up MySQL is now more strict in what it allows in a datetime field and it doesn’t like values of all zeroes. An easy way to fix this is to temporarily disable this strictness when running our query. This is controlled in our sql_mode. To get the current values of your MySQL sql_mode:

SELECT @@sql_mode

You want to remove from this list NO_ZERO_IN_DATE and NO_ZERO_DATE. To do this we will use

SET sql_mode = '<listofvalues>';

Replace <listofvalues> with the results of your SELECT @@sql_mode above making sure to remove NO_ZERO_IN_DATE and NO_ZERO_DATE from the list.

You should end up with something like:


Put this before your ALTER TABLE statement and this time when you execute the command it should succeed. In sum, you should be running a query that looks something like this:

ALTER TABLE dbname.prefix_users ENGINE=InnoDB;

Hope this helps someone else who finds themselves in the same situation!

Selected Bibliography

Headless WordPress

For step-by-step instructions on using WordPress as a headless back-end with Gatsby as the front-end see the GitHub repo

Interesting Note: We’ll be looking particularly at static site generators (SSGs) of which Matt Mullenweg is not a fan.

What is Headless?

  • Some confusion regarding terms due to inconsistent use within community (which tends to happen):
    • In a generic sense, “head” is the front-end (UI) of a site (do we actually use it that way?) so “headless” is a site without a front-end.
    • “Headless” sites have existed in the WordPress ecosystem using the REST API.
    • More recently, the term has been used in association with Static Site Generators (SSG’s) such as Gatsby, where WP serves as the back-end (“headless”) and Gatsby serves as the front-end (“head”).
    • While other languages (Go: Hugo, Ruby: Jekyll) provide SSG’s, the most popular are in JS and these are often referred to as using the Jamstack (JavaScript, API, Markdown).

Why Use Headless?

  • Performance Gains
  • Scalability
  • Reduced Cost (at scale)
  • Security Improvements
  • Decoupled UI
  • Use Latest JS Frameworks
  • Separation of Concerns (SoC)

Downsides of Headless

  • Now maintaining two separate sites – WP back-end and front-end.
  • The complexity of front-end frameworks (e.g. React) reduces productivity in the short-term.
  • Loss of plugin functionality (most WP plugins won’t work in a static environment).
  • Must be rebuilt every time a change occurs.
  • Takes a long time to build (depends).

WP Plugins for Generating Static Sites

WP Static Hosts


Learning More



  • WP Search? wp-search

Things I’ve Learned Using Flywheel Local

Where Local Lives on Windows

All sites are installed in a Local Sites folder at a path something like C:\Users\username\Local Sites.

The software itself is installed at:

C:\Program Files (x86)\Local\

You can find the component server software used by Local in the extraResources subdirectory. nginx, php, mysql, mariadb, and mailhog are yet one level deeper inside lighting-services.

Within the specific server software folder you’ll probably need to find the desired binary (e.g. if you are confusing a code editor). For me this looks like: \php-7.3.5+6\bin\win64\php.exe.

Note, however that you’ll want to edit the configuration files inside of your specific Project within Local Sites. For example, php.ini can be edited at \Local Sites\nameofsite\conf\php\php.ini.hbs.

Why .hbs? This seems to be the extension for Handlebars Templates and while I normally think of Handlebars being for templating code it apparently can also be used in config files, thus allowing for dynamic values in Local’s config files.

Turning Off PHP Debugging for Local

~/Local Sites/nameofsite/conf/php/php.ini.hbs

Change display_errors = On to display_errors = Off.

Configuring Local to Work with Composer

  • Composer won’t detect Local’s PHP by default, need to navigate to it manually. Path should be something like: C:\Program Files (x86)\Local\resources\extraResources\lightning-services\php-7.3.5+6\bin\win64\php.exe.
    • Don’t allow Composer to setup php.ini file.
  • I’ve been unable to get Composer running with Local with either the Windows Composer installer (see towards bottom of page) or the manual method. The problem appears to be that the local provided PHP binary was compiled without SSL support.
    • Unfortunately, it isn’t as easy as passing an o

What Local Is

  • Local, under the hood, appears to be a fairly straightforward XAMP setup (where X is Windows, Linux, or MacOS). Don’t get me wrong, the UI layer is beautiful and overall its wonderful to worth with.
  • The UI is an Electron app (similar to Slack).
  • It uses the Handlebars Templating language in config files (as mentioned above).
  • It utilizes Google’s SwiftShader, a “high-performance CPU-based implementation of the Vulkan, OpenGL ES, and Direct3D 9 graphics APIs.”
  • In addition to the fundamental packages of nginx (web server), PHP (language server), and MySQL/MariaDB (database servers) it bundles the following with the install:
    • 7-Zip – For managing compressed files
    • Adminer – A database management interface (not sure why phpmyadmin isn’t included instead, I find the latter more intuitive/powerful)
    • WP-CLI – for command-line WP management
    • WP Engine DevKit – not exactly sure how this factors in yet, its tooling from WP Engine for working with WP
    • Mailhog – catches all mail sent by WP and allows you to review it
    • ngrok – allows for sharing of one’s local dev server quickly and easily
      • This worked very well for me!
    • Cygwin – allows one to run some generally Linux native applications
      • rsync – copying/syncing/moving files
      • ssh – SSH duh 🙂

Why I’ve Decided to Stay with VVV…for now…

I’ve used a wide variety of local dev environment solutions. From WAMP to Docker to Vagrant they have all had their benefits and limitations. I don’t expect to find the perfect solution.

The one I’ve been using for perhaps a year now is VVV, which uses Vagrant under the hood. There are challenges with using it but I’m fairly comfortable with it and at the moment the limitations of Local are too significant for me to make the switch.

FWIW, I’ve outlined the salient issues below:

  1. Closed Source – Local, unlike most other local dev solutions, is closed source. I’m not against using commercial software but being oss is generally a boon, imho.
  2. Premium – Most other local dev solutions are also free. Local’s basic version comes for free but they have a premium version that costs $20/mo. This makes more uncomfortable than a solution that is premium only. freemium’s have a tendency to move basic functionality from the free edition to the premium edition when they don’t see the desired uptake of their premium edition. I’m not saying that will happen with Local, only that it makes me nervous.
  3. Speed – While theoretically Local should be faster than virtual options I haven’t found this to be significantly true. VVV is no speed demon in my experience, but neither is Local. Whatever small amount of performance I might gain isn’t worth the cost in migrating to Local.
  4. Native – In general I prefer my tools to be as portable and self-contained as possible. This means I favor solutions that use virtualization technologies under the hood…Local used to but has now adopted a native approach. Since 3 doesn’t seem to be a huge win (at least for me), 4 becomes a bigger problem.
  5. Compatibility – This is actually the nail in the coffin for me. All of the others I could probably live with but the inability to use certain core development tools with Local (on Windows) just doesn’t work. Specifically, I have been unable to discover how to get Composer working with Local.

Making an Easy Mistake with the gatsby-source-wordpress Plugin

Maybe no one is like me, but I’ve tripped over this same mistake several times now and hope I can spare you the trouble.

In order to configure Gatsby to work with WordPress you use a Gatsby (not WordPress) plugin called gatsby-source-wordpress. You can then write a GraphQL query to pull data from your WordPress site.

Try running gatsby develop and you may run into the same errors I encountered.

There was an error in your GraphQL query:
Cannot query field "allWordPressPost" on type "Query".

Generating development JavaScript bundle failed...
Cannot query field "allWordPressPost" on type "Query".

The likely reason Gatsby is throwing these errors is because you used allWordPressPost in your query instead of allWordpressPost. See the difference? In Gatsby you DON’T capitalize the letter p.

Its that simple…and yet that makes it all the more easy to overlook.

What’s Changing with Directories Pro Beta?

A Brief Introduction

Sabai Apps makes a Directories Pro development version available. The current version (as of 9/24/18 EST) is 1.2.0-dev77.

They provide the commits from git, but this can be a bit messy to read…and I find writing things down helps me solidify things in my memory. Since I’m already writing it down for my own purposes (to solidify in memory, to familiarize with code, to be able to return to later), I figured I’d share it with anyone else who might be interested (yes, is that 1 other person I see?!).

I should note that while I think these are cleaner/easier to read that the original git commit logs they are also limited in their own ways:

  • I’ve probably mixed things up somewhere, maybe multiple somewheres.
  • I don’t provide full file paths and sometimes there are multiple files with the same name in different paths (I assume from context which one is being referred to can be discerned, or that one can refer to the git commit log).
  • I sometimes choose to skip over items (e.g., spelling corrections, but also things I find for whatever reason less relevant).
  • I often don’t list JS files that have changed, only the PHP files.
  • I’m only providing notes on some of the plugin components in Directories Pro (e.g., no payments), so notes are incomplete.
  • Sometimes (like with dev72) not all of the commit logs are loading, so I can only provide partial updates.

Understanding these limitations, I still think you can get a pretty good idea pretty quickly of what is going on. For those interested in reading the original git commit logs:



  • 23rd
    • Added BuddyPress activity support to listings/reviews and its comments. (ContentTypesHelper)
    • Moved BNFW related functions to a helper class. (BnfwHelper)
  • 22nd
    • Prefix Directories admin notice messages with [directories]. (Platform)
    • Add rel=”noopener” to links with target=”_blank”. (UserIdentityHtmlHelper, DefaultRenderer, WhatsAppRenderer)
    • Added check if directory exists. (TemplateElement)
    • Refactoring code. (OptionsField)
    • Add link to location API registration sites. (AlgoliaAutocompleteApi, GeoNamesTimezoneApi, MapboxGeocodingApi)
  • 21st
    • Output log messages while importing. (WPAllImport/Importer)
    • Fixed global Exception instances not being caught (LocationCSVImporter).
    • Refactored code. (LocationCSVImporter, ApiHelper)
    • Ensure geocoding API returns 2-character uppercase country code. (GoogleMapsGeocodingApi, MapboxGeocodingApi, NominatimGeocodingApi)
    • Fixed invalid timezone API request URL. (GeoNamesTimezoneApi)
    • Fixed CSV importer/exporter not handling Timezone column. (LocationCSVExporter, LocationCSVImporter)
  • 20th
    • More code for WP All Import add-on. (EntityImporter, FieldImporter, IWpAllImporter, MapImporter, SocialImporter, WPImporter, Import, functions.php, LocationCSVImporter, LocationComponent)
    • No longer stops processing video field when fetching thumbail has failed. (VideoType)
    • Refactoring. (RangeType)
    • Do not show time selection field. (FeaturedFieldWidget)
    • Moved payment CSV import/export classes to directories-payments plugin. (CSVComponent, PaymentExporter, PaymentImporter)
  • 19th
    • More code for WP All Import add-on. (FieldImporter, MapImporter, WordPressContentComponent, EntityImporter, SocialImporter, WPImporter, WPAllImport/Importer)
    • Allow passing custom setting value for each search form field. (FormHelper).
    • Bump version. (FieldImporter)
    • Fixed taxonomy term page not showing correct content on certain occasion. (Platform)
  •  18th
    • Refactoring Display_Render helper $attr parameter, merging with $options. (RenderHelper, entity_display.html.php, FilterFormHelper)
    • Bump version. (FieldImporter)
    • Some changes on how HTML classes are generated for each post/term. (WordPress/Template, EntityComponent, WordPressContentComponent)
    • Changed filter entity_[bundle_name]_html_classes to entity_html_classes. (EntityComponent)
  • 17th
    • Refactoring ViewEntities controller. (ViewEntities)
    • Fixed fatal error in Manage Displays section in the backend. (AdminElementHelper)
    • Added support for Algolia Places API autocomplete library. (location-algolia-autocomplete.js, AlgoliaAutocompleteApi, LocationComponent)
    • Show Powered by Google only when Google Maps API is enabled. (TextFormField).
    • Removed redundant function call. (location-googlemaps-autocomplete.js, location-textfield.js)
  • 16th
    • Fixed issues upgrading from version 1.1.x to 1.2.0. (schema/1.2.0-dev.55.php, schema/latest.php, phing/model.yml)
    • Refactor how custom single term/post pages are generated. (Platform, EntityComponent, PageSettingsFormHelper, ShortcodesHelper)
    • Removed unused shortcode. (DirectoryProComponent)
  • 15th
    • Remove unused variable (ViewEntities).
    • Fix category term and child item search not working at times (KeywordField, PostFieldQuery).
    • Allow skipping entering guest info when submitting content as guest (AbstractAddEntity, LoginOrRegister, FrontendSubmitComponent, SettingsFormHelper).
  • 13th
    • Allow sorting terms by custom SQL (TaxonomyTermsHelper, Storage, Query).
    • Add support for Category Order and Taxonomy Terms Order plugin (WordPressContentComponent).
    • Fix undefined variable PHP error (RenderHelper).
    • Fix guest author post always showing Guest as author (UserIdentityHtmlHelper, UserIdentityFetcher, AuthorFieldRenderer, FrontendSubmitComponent).
    • Fix issues with get_post_custom() called by other plugins (WordPressContentComponent).
  • 12th
    • Refactoring Display component IElement classes (large number of files).
    • Additional changes to [drts-entity] shortcode (ShortcodesHelper, WordPressComponent).
    • Bump version (ShortcodesHelper).
    • Do not show 0 in lat/lng input fields when no coordinates (LatLngFormField, AddressFormField).
    • Show shortcode syntax for Detailed display only under Manage Displays (WordPressComponent).
    • Refactoring display element methods. (FormDisplayElement)
    • Disabled suggest place option if no autocomplete API library is set. (TextFormField, AddressSearchField)
  •  11th
    • Bump version on a number of files.
    • Refactoring display elements methods (EditPost, CaptchaDisplayElement, RatingsDisplayElement).
    • Accept “separator” and “key” parameters with [drts-entity] shrotcode (WordPressComponent).
    • Add new column to hold permanent element ID for each display element (1.2.0-dev.55.php, CreateHelper, DisplayHelper, ElementGateway).
    • Refactoring display elements methods (large number of files).
    • Disable suggest place option if no autocomplete API library is set (TextFormField, AddressSearchField)/
  •  10th
    • Added subscriptions dashboard panel (VotesDashboardPanel).
    • Hide empty dashboard panel links (PostsPanel).
    • Do not set current user as author by default when importing (Import, PostEntityType).
    • Fixed contact form with Gravity Forms not showing confirmation message (FormHelper).
    • Make sure additional features are unapplied when order is canceled (FormPaymentFeature, PhotosPaymentFeature, LocationsPaymentFeature).
  • 9th
    • Fixed some issues with & in URL (LinkToHelper, UrlHelper).
    • Changed “Visit Frontend” to “Visit Directory” (Directories, DirectoryComponent).
    • Added plan type parameter (FormPaymentFeature, PhotosPaymentFeature, LocationsPaymentFeature).
  • 8th
    • Added filter to customize map marker content (MarkerHelper).
    • Fixed method parameter declarations (DisplayComponent, ViewComponent, VotingComponent, and WordPressContentComponent).
    • Fixed undefined variable PHP notice level error (AdminContent).
    • Allow city/province values to be entered through input text fields (AddressFieldWidget, AddressFormField).
    • Fixed address column not always being populated (AddressFieldTYpe).
  • 6th
    • Fixed array to string conversion PHP error (AdminElementHelper).
    • Now allows other map/location fields to be used to display listings on map (MapViewMode, location_entities.html.php, location_entities_container.json.php, LocationComponent).
    • Removed debug code (AdminContent).
    • Removed unused method parameters (CustomButton, IButton, AbstractElement, ButtonElement, ColumnElement, ColumnsElement, GroupElement, HtmlElement, IElement, JavascriptElement, LabelsElement, SeparatorElement, StatisticsElement, TabElement, TabsElement, TemplateElement, TextElement, RenderHelper, ChildTermsDisplayElement, FieldDisplayElement, FieldListDisplayElement, FormDisplayElement, ParentFieldDisplayElement, AbstractEntitiesDisplayElement, ChildEntitiesDisplayElement, FilterDisplayElement, DisplayButton, CommentsDisplayElement, AbstractPostDisplayButton, AddEntityDisplayButton, CaptchaDisplayElement, RatingsDisplayElement, ClaimEntityDisplayButton, FormDisplayElement).
    • Fixed FontAwesome icon for categories not showing up on category pages (PageTitleHelper).
    • Fixed invalid namespace for Responsive Lightbox class (PhotoSliderFieldRenderer).
    • Added Responsive Lightbox & Gallery plugin support for lightbox effect (PhotoSliderFieldRenderer, SliderHelper).
  • 5th
    • Moved form related CSS to global CSS files (Form, ScriptsHelper).
    • Passed assoc array as filter param for modifying taxonomy term order  (TaxonomyTermsHelper).
    • Fixed forced reloading taxonomy term cache not working (ToolsHelper).
    • Added/refactored more code for theming (AbstractPlatform, Platform, SliderField, TextFormField, AddressSearchField).
    • Added/refactored more code for new Map view (map_entities.html.php, map_map.html.php, MapComponent, MapViewMode, location_entities.html.php, location_entities_container.json.php, location_entities_googlemap.html.php, location_map.html.php, LocationComponent).
    • Removed unused CSS class (TextFormField).
  • 4th
    • Added getOptions() method to map-googlepmaps.js, location-leaflet-map.js.
    • Removed unused method parameter (MarkerHelper).
    • Fixed invalid assignment to array (GoogleMapsGeocodingApi).
  • 3rd
    • Refactored code (DisplaysHelper and AbstractMode).
    • Added new Map view mode and Map\FieldType\ICoordinates interface (ICoordinates, map_entities.html.php, MapFieldType, MapComponent, MapViewMode, AddressFieldType).
    • Added filter to modify sort order of cached taxonomy terms (TaxonomyTermsHelper).
    • Refactored [drts-entity] shortcode (WordPressComponent).
    • Fixed incorrect method declaration in FacebookMessengerLinkFieldRenderer.
    • Added content modification plugin for Yoast SEO (wordpress-admin-yoastseo.js, AdminContent).
    • Remove unused “public” attribute of view info (DashboardViewMode, PhotoSliderViewMode).
  • 2nd
    • Added option to display logout button on frontend dashboard (DashboardComponent, DashboardViewMode, NavHelper).
    • Added FB messenger link field renderer (AccountsFieldRenderer, FacebookMessengerLinkFieldRenderer, FacebookPageFieldRenderer, TwitterFeedFieldRenderer, SocialComponent).
    • Added autocomplete=”off” to search form fields (KeywordField, TextFormField).
    • Now shows time selection field in 24hr format by default (form-field-datepicker.js).
    • Added more translatable phrases (DateHelper, AddressFieldFilter).
    • Changed error level from critical to warning in UninstallHelper.
    • Enabled theming. Available themes: default, dark, orange (Platform).
  • 1st
    • Add thumbnail_scaled image size (PostsSystemWidget, AbstractRenderer, AbstractMode, ImageFieldType, WordPressContentComponent, photoslider_entities.html.php, PhotoSliderFieldRenderer)).
    • Fixed some issues with privatable option (DirectoryType, Bundle, FeatureSettingsFormHelper).


  • 31st
    • Fixed excerpt_more filters referencing wrong post (PostContentFieldRenderer)
    • Fixed undefined variable error in map-googlemaps.js.
  • 30th
    • Added add-on for WP All Import (IWpAllImportImporter, Importer, rapid-addon).
  • 28th
    • Refactored Display_Render() helper.
    • Removed unused element_id template variable (view_entity.html.php, ViewEntity).
    • Added element_id parameter for rendering single element with [drts-entity] (WordPressComponent).
    • Fix compatibility issues with FontAwesome in themes or other plugins (Dashboard).
  • 26th
    • Exclude internal bundles from system slugs (FrontendSubmitComponent).
    • Refactoring Display_Render() helper (RenderHelper).
  • 23rd
    • Allow empty string in field query for DateType (DateType).
    • Removed unused code from Platform.
    • Added Entity_Path helper (EnetityComponent, PermalinkUrlHelper).
    • Added [drts-entity] shortcode (WordPressComponent).
    • Redraw map only when array of markers are returned (locaiton-map.js).
  • 22nd
    • Show map on directory index page when map is positioned at top on mobile (location-map.js).
  • 21st
    • Make sure AddDirectory controller is overridden (DirectoryProComponent).
  • 19th
    • Added [drts-field] shortcode (WordPressComponent).
    • Moved Display_ElementTypes helper to Display_Elements_types (ListElements, ElementsFormField, AdminElementHelper, ElementTypesHelper, ElementsHelper).
    • Fix fatal error trying to fetch non existing directory type (FiltersSystemWidget).
  • 15th
    • Use radius setting value for search my location radius (AddressFieldFilter).
    • Show map on directory index page when map is positioned at top (location_entities.html.php, location_entities_googlemap.html.php).
  • 13th
    • Renamed select2 css file (ScriptsHelper).
    • Fixed undefined variable JS error (GoogleMapsApiHelper).
    • Let user specify location search radius on search (SliderField, FormHelper, TextFormField, AddressSearchField).
  • 12th
    • Changed language phrase (AddressFieldFilter).
    • Use radius setting value for search my location radius (AddressFieldFilter).
  • 11th
    • Fixed open now label not showing correctly (OpenNowDisplayLabel, OpeningHoursFieldRenderer).
    • Removed duplicate code (AddressFieldFilter).
    • Change language phrase (AddressSearchField).
    • Set radius to 1 km/mi by default for Current Location search/filter (TextFormField, FilterFieldHelper.
  • 10th
    • Fixed filter form widget not showing when using shortcode on default page (FiltersSystemWidget).
  •  9th
    • AbstractUpdater now logs error message only.
    • Updated responsive settings (WordPressHomePage).
  • 7th
    • Added search filter feature to icon picker form field (form.css, form-field-iconpicker.js, form-field-picker.js).
  • 3rd
    • Added permalink_url token (TokensHelper).
    • Allow showing custom label instead of original title (TitleFieldRenderer).
    • Added new sync taxonomy terms tool (ToolsHelper).
    • Added option to specify table join type (Storage, PostFieldQuery).
    • Allow users with delete others claim to approve/reject claims (StatusFieldType, StatusFieldWidget).
  • 2nd
    • Version bump (Assets, AbstractPlatform, Platform, MapFieldRenderer, GoogleMapsApiHelper, AdminContent, among others)
    • Remove git comment (Platform).
  • 1st
    • Remove unused Use statements (DeleteDirectory, AbstractDisplays).
    • Added more checks for internal type bundles (DashboardViewMode, EntityComponent, DisplaysHelper, Bundle, ContactComponent).
    • Fixed routes being deleted (DisplayComponent).
    • Fixed displays admin path issue (ElementsFormField).
    • Refactored label settings page (Edit).
    • Fixed status dropdown not showing up on dashboard (NavHelper).
    • Added new no_content bundle info key (Bundle).
    • Set default admin bundle path if not set (AbstractBundleType).
    • Do not include internal type bundles in content type options (ReferenceFieldType).
    • Sync reference fields on create entity success event (EntityComponent).
    • Fetch values for structured data properties from taxonomy terms (AddressFieldType).


  • 30th
    • Use template to customize single post type page (Platform).
    • Fixed hidden address fields not being filled (AddressFormField).
  • 29th
    • Added filter to force is_page on category archive page (Platform).
  • 28th
    • Enabled import/export of voting_vote type fields (VotingExporter, VotingImporter).
    • Imported vote value must always be a PHP array (VotingImporter).
    • Customize Slick to prevent conflict with themes (photoslider.js, slick.custom.js, slick.js, SliderHelper, PhotoSliderViewMode).
    • Updated leaflet JS library files.
  • 27th
    • Fixed missing argument warning error (Platform).
  • 26th
    • Fixed unmerged code (DropdownButtonLinksHelper).
    • Prefix data-toggle attribute with app name (Dashboard, DropDownButtonLinksHelper, TabsElement, ButtonsField, CheckboxField, ColorPaletteField, EditorField, RadiosField, MapFieldRenderer, dashboard_dashbaord.html.php).
    • Change prefix of data-toggle from app name to BS prefix (Dashboard, dashboard_dashboard.html.php).
  • 24th
    • Load scripts in footer by default (GoogleMapsApi, ApiHelper, AbstractGoogleMapsApi, GeoNamesTimezoneApi, MapboxGeocodingApi, NominatimGeocodingApi, LeafletMapApi).
    • Fix bundle info referencing current value instead of default info (Edit).
    • Fix icon not being imported for each option through CSV import (OptionsField).
    • Moved Fieldset component to new directories-fieldset project (AddFieldset, Fieldsets, FieldsetEntityBundleType, FieldsetEntity, FieldsetEntityType, FieldsetComponent).
  • 23rd
    • Use -sabai_app_fullname- placeholder instead of hardcoded “directories” (AddDirectory, DeleteDirectory, Directories, EditDirectory, DirectoryComponent, TypesHelper, ElementsFormField, AdminContent, WordPressContentComponent, DirectoryProComponent).
    • Add check for internal type bundles (ContentTypes, ContentTypesHelper).
    • Make sure BundlesHelper always returns an array (BundlesHelper).
    • Skip filtering by entity type on bundles since names are always unique (EntityComponent).
    • Accept URL for any social account field (SocialComponent).
    • Fixed review body field being displayed at top (ReviewEntityBundleType).
    • Added new Fieldset compoennt (AddFieldset, Fieldsets, FieldsetEntityBundleType, FieldsetEntity, FieldsetEntityType, FieldsetComponent).
  • 21st
    • Added link target option to screenshot field renderer (FieldDisplayelement, AbstractRenderer, ImageRenderer, ScreenshotFieldRenderer).
    • Fixed setting is_singular to true on archive page causing SEO issues (Platform).
    • Fixed parent entity name/link not appearing when posting child entity (AddChildEntity).
    • Load scripts in footer by default (AbstractPostDisplayButton, ApiHelper, SliderHelper).
  • 20th
    • Fixed fatal error where 7th parameter is not being an array (ElementsFormField, AdminElementHelper).
    • Fixed streetview not working (map-googlemaps.js).
  • 19th
    • Properly close file opened with finfo_open() (FileTypeHelper).
    • Refactor screenshot renderer (AbstractPlatform, Platform, ImageRenderer, ScreenshotFieldRenderer).
    • Fixed fatal error when rendering screenshot image as background (ImageRenderer).
  • 18th
    • Added link to URL option (ImageRenderer, ScreenshotFieldRenderer).
    • Added filter to customize default No Image image (NoImageHelper).
    • Force check un-updated components on plugin update (Platform).
    • Added fix for single term page not rendering properly with some themes (Platform).
    • Removed console debug data (location-field.js).
    • Fixed invalid method param type (ScreenshotFieldRenderer).
  • 17th
    • Fixed custom bundle info value disappearing on directory update (ReviewComponent, EntityComponent, Bundle, WordPressContentComponent, ContactComponent, LocationComponent).
  • 16th
    • Fixed render as background option not working in certain case (ImageRenderer); later this change was reverted.
    • Refactored component upgrade() method (AbstractComponent, ComponentHelper).
    • Replaced bootstrap-growl with sweetalert2 JS library (AbstractPlatform).
    • Added code for properly upgrading to version 1.2.0 (ApiHelper, MapComponent).
    • Fixed undefined index notice error causing JS error (MapComponent, GoogleMapsAutocompleteApi, GoogleMapsGeocodingApi).
    • Show loading indicator when “Find on map” button is clicked (location-field.js).
    • Do not load location JS if no map provider (AddressFormField).
  • 15th
    • Added downloadUrl() method (AbstractPlatform, Platform).
    • Refactored “cacheable” display element setting (AddElement, EditElement, ElementsFormField, AdminElementHelper, AdvacnedSettingsFormHelper, FieldDisplayElement, ReferenceFieldRenderer, AbstractType, AbstractEntitiesDisplayElement).
    • Refactored color settings form (IconRenderer, UtilHelper).
    • Refactored Image field renderer (ImageRenderer).
    • Added post thumbnail settings (ContentTypesHelper, PermalinkSettingsFormHelper, WordPressContentComponent).
    • Added ScreenshotFieldRenderer (DirectoryProComponent, ScreenshotFieldRender).
  • 14th
    • Added links to plugin page (main.php).
    • Refactoring Form.php.
    • Do not shown error when map provider is not selected (MapFieldRenderer).
    • Refactored AbstractFileRenderer.php.
    • Added link to documentation under map library settings section (MapComponent).
    • Fixed files with mp4 extension not being inserted (MediaManagerFormField).
    • Fixed required indicator not showing (PageSetingsFormHelper).
    • Display video/audio files using shortcode (FileFieldRenderer, FileFieldType, FileFieldWidget).
    • Added option to specify countries to restrict geocoding results (GoogleMapsGeocodingApi, MapboxGeocodingApi).
  • 13th
    • Added fix for Customizr theme (Platform).
  • 12th
    • Fixed exception not being caught (ExportDirectory).
  • 11th
    • Bump version (IsSearchRequestedHelper).
  • 9th
    • Allow view image field setting to be required (AbstractMode).
    • Fixed single category page not showing correct page title with some themes (Platform).
    • Forced upgrade components on reload all components (SystemComponent).
    • Fixed photo sldier image fiedl options always required (PhotoSliderViewMode).
    • Refactoring (LocationComponent).
  • 8th
    • Add new Recalculate Review Ratings tool (RecalculateRatingsHelper, ReviewComponent).
    • No caching reviews on listing page by default (directory_listing_displays.php).
    • Fix custom formatted address not showing when no location term assigned (AddressFieldRenderer).
    • Fixed photoslider zoom setting overriding photoslider link setting (photoslider_entities.html.php, PhotoSliderViewMode).
    • Added option to add country restriction to geocoding API (GeoNamesTimezoneApi, GoogleMapsAutocompelteApi, GoogleMapsGeocodingApi).
  • 6th
    • Made changes to parameters for Entity_Field_options helper (FieldHelper, PersonalDataHelper, AbstractMode, ContactComponent, OpenNowDisplayLabel, PhotoSliderFieldRenderer).
    • Changes to Entity_Image helper (EntityComponent, TermsFieldType, PageTitleHelper, PermalinkHelper, MarkerHelper, AdminContent, photoslider_entities.html.php).
    • Changes on handling entity image/icon fields (PostsSystemWidegt, Edit, QueryEntities, ChildTermsDisplayElement, TitleFieldRenderer, ImageHelper, TaxonomyTermsHelper, UtilHelper).
    • Fixed fatal error on merging component config (ComponentHelper).
    • Fixed icon field not showing (IconWidget).
    • Fixed bundles with no entity permissions (PermissionsHelper, WordPressContentComponent).
    • Added is_user bundle info variable (DashboardViewMode, Bundle, ContactComponent).
    • Allow multiple countries for Google Autocomplete component restriction (GoogleMapsAutocompleteApi).
    • Some changes on handling entity image/icon fields (location_entities.html.php, location_entities_container.json.php, PhotoSliderViewMode).
    • Refactoring (LocationComponent).
  • 4th
    • Fix PHP documentor comment error (FieldHelper).
    • Fixed undefined variable error (DashboardComponent).
    • Do not add accordion CSS class if only 1 panel (dashbaord_dashboard.html.php).
  • 3rd
    • Disable directions API, show error if no map provider configured.
    • Display better form error messages in form header (AddElement, EditElement, AdminElementsHelper, Form, AddView, EditView).
    • Disable directions API, show error if no map provider configured. (MapFieldRenderer, MapFieldWidget).
    • Added class to froce overflow visible (directory_listing_displays.php).
    • Fixed undefined variable error (locaiton-leaflet-map.js).
    • Cache geocoding results up to maximum of thirty days (ApiHelper).
  •  2nd
    • Attachments are now associated with entity on creation/update.
    • Use Attr application helper to render form data attributes (Application, AbstractField, Form).
    • Escape form error messages (AdminContent).
    • Display better form error messages in form header (Form).
    • Require Google Maps API key (AbstractGoogleMapsApi).
    • Do not show map if no API configured (ApiHelper, AddressFormField, LocationComponent).
    • Refactoring AdminContent.
    • Associate attachments with entity on create/update (WordPressContentComponent).
  • 1st
    • Add WordPressUser component to directories-user.
    • Process post and term entity types only (ContentTypesHelper).
    • Remove div.fluid-width-video-wrapper added by jquery.fitvids.js (photoslider.js).


  • 29th
    • Refactoring new location API (MapComponent, GeoNamesTimezoneApi, GoogleMapsAutocompleteApi, GoogleMapsGeocodingApi, GoogleMapsTimezoneApi, MapboxGeocodingApi, NominatimGeocodingApi, LocationComponent).
    • Fixed lat/lng values not being retrieved correctly (location-textfield.js).
  • 28th
    • Do not rquire Google Maps API keys (AbstractGoogleMapsApi)..
    • Set Google Maps API as initial default (LocationComponent).
    • Add more variables to be passed to entity_field_render filter hooks.
    • Add hook to allow customization of content rendered for each file field (AbstractFileRenderer).
    • Bump version (AbstractSystem).
    • File titles can now be imported if name and title are separated by pipe “|” (WPImporter).
    • Allow non alphanum characters to be used as choice option values (ChoiceType).
    • Show log in the console instead of throwing an error for each API (location-api.js).
  • 23rd
    • Fixed custom address format option not being displayed (AddressFieldRenderer).
  • 22nd
    • Remove option to render OSM titles on Google Maps.
    • Removed unused code from PostEntityType, TermEntity, TermEntityType.
    • Added new experimental WordPressUser component.
  • 19th
    • Removed unused method from UploadHelper.
    • Fixed fatal error on fetching non-existing directory type (AddDirectory, PostsSystemWidget).
  • 17th
    • Added option to throw exception on error (FieldHelper).
    • Changed parameter name (AbstractEntity).
    • Removed redundant code from IsRoutableHelper.
    • Removed unused code from ReferencingEntitiesFormDisplayElement.php.
    • Added entity bundle to params passed to fieldTypeSettingsForm() method.
    • Added option to sync entity reference fields (EntityComponent, ReferenceFieldType).
  • 16th
    • Shared node_modules among all projects.
  • 15th
    • Allow grouping by extra fields (Query.php).
    • Fix attributes not being processed properly (Link.php).
    • Fix undefined index PHP error (OptionsField).
    • Refactoring NavHelper.
    • Let view mode instance modify query on view entities.
    • Refactoring SearchComponent.
    • Removed debugging code from core.js.
    • Added new Buttons form field.
    • Added new Glossary view filter.
    • Removed unused file (GridClass).
    • Fixed filter labels not being escaped properly (ColorFilter, GlossaryFieldFilter, AddressFieldFilter).
    • Added getTimeZone() method to Platform.
    • Properly required Map/Location API settings (GoogleMapsApi, AbstractGoogleMapsApi).
    • Ignored #max_length setting for input field if #separator is set (TextField).
    • Fixed missing argument fatal error (RenderHelper).
    • Fixed undefined variable error (location-googlemaps-timezone.js).
    • Fixed address being overwritten on “Find on map” (location-field.js).
    • Do not modify address input fields on “Find on Map” button click (location-field.js).
    • Properly namespace location API setting parameters passed to JS GoogleMapsAutoCompleteApi).
    • Rename geocoding provider name (GoogleMapsGeocodingApi, NominatimGeocodingApi).
    • Added viewport() method to Map API helper (LocationFakerGenerator, ApiHelper).
    • Use the timezone ID returned by the Platform as default (AddressFormField).
    • Added Mapbox geocoding API (MapboxGeocodingApi, LocationComponent).
    • Use center lat/lng and radius to filter if no viewport (FilterFieldHelper).
    • Removed unused code from location-mapbox-geocoding.js.
    • Fixed country setting not passed to JS (MapboxGeocodingApi).
    • Changed language phrase (MapboxGeocodingApi).
  • 14th
    • Fix phrase not being translated (FrontendSubmit).
  • 12th
    • Added new icon picker form field and JS library.
    • Refactoring core.js.
  • 9th
    • Bump version (PostsPanel).
    • Open edit post form with normal page instead of in modal window (EditPost, DashboardComponent, AbstractPostDisplayButton, PostsPanel)
    • Fix redundant call to Entity_Tokens_replace() helper.
    • Fix redundant calls to generate cache ID (RenderHelper).
    • Allow specific display to be rendered (ViewEntity).
    • Allow specific element to be displayed with [drts-directory-listing].
    • Fix specified element not being displayed with [drts-directory-listing] (RenderHelper).
    • Refactoring (ReferencingEntitiesFormDisplayElement).
    • Do not load autocomplete JS files if not required (AddressFormField).
  • 7th
    • Return two letter country code (location-nominatim-geocoding.js).
    • Populate address column with other address components if empty (AddressFieldRenderer, AddressFieldType).
    • Bump version (DirectoryProComponent).
    • Add missing slash to shortcode path atts (DirectoryProComponent).
  • 6th
    • Refactoring Map/Location API (ApiHelper, LocationComponent).
    • Require GeoNames user naem if GeoNames timezone API is selected (GeoNamesTimezoneApi).
    • Fixed Map/Location API loading issues (AddressFormField, TextFormField, ApiHelper, LocationComponent).
  • 5th
    • Added more location API providers, pass settings to API JavaScript (AbstractGoogleMapsApi, GeoNamesTimezoneApi, GoogleMapsAutocompleteApi, GoogleMapsGeocodingApi, GoogleMapsTimeZoneApi, IApi, IAutocompleteApi, IGeocodingApi, ITimezoneApi, NominatimGeocodingApi, ApiHelper, LocationComponent).
    • Refactoring Location API (similar to above; DirectoryType, GoogleMapsAutocompleteApi, TextFormField, AddressSearchField).
  • 4th
    • Change helper name (LocationCSVImporter, LocationFakeGenerator, AddressFieldType, ToolsHelper, FilterFieldHelper).
    • Add new Location API changes (AbstractGoogleMapsApi, GoogleMapsAutocompleteApi, GoogleMapsGeocodingApi, GoogleMapsTimezoneApi, IGeocodingApi, NominatimGeocodingApi, AddressFieldWidget, AddressFormField, ApiHelper, LocationComponent, LeafletMapApi).
    • Changed geocoding provider name (NominatimGeocodingApi).
  • 3rd
    • Removed Leaflet component (ApiHelper, LeafletComponent).
    • Added suport for other map API libraries (AddressFieldType, AddressFieldWidget, AddressFormField, ApiHelper, DrawMapOptionsHelper, LocationComponent, AbstractGoogleMapsApi, GoogleMapsAutocompleteApi, GoogleMapsGeocodingApi, GoogleMapsTimezoneApi, IAutoCompleteApi, ITimezoneApi, IAutocompleteApis, ITimezoneApis, LeafletMapApi).


  • 31st
    • Added Leaflet component (but removed Jun 3) (ApiHelper, LeafletComponent).
  •  30th
    • Refactoring (FrontendSubmitComponent, ReviewComponent, LocationComponent).
  • 29th
    • Bump version (ApiHelper, LeafletComponent, location_entities_html.php).
    • Removed Leaflet component (ApiHelper, LeafletComponent, location_entities.html.php).
  • 28th
    • Add option to enable/disable frontend submission per directory (ReviewEntityBundleType, AddEntity, LoginOrRegister, FrontendSubmitComponent, SubmittableBundlesHelper).
  • 26th
    • Anonymize on erasure only when current data is not empty (GuestFieldType).
  • 25th
    • Make sure rating criteria slugs are not more than 40 characters (ReviewEntityBundleType).
  • 24th
    • Add more address component values for better geocoding results (ToolsHelper).
    • Set colorable info key to false (StatusDisplayLabel, OpenNowDisplayLabel).
  • 22nd
    • Enabled AJAX submit for login/register forms (frontendsubmit_login_register_form.html.php, LoginOrRegister).
    • Fixed dashboard panel settings not saving (DashboardComponent).
    • Fixed dashboard panels not appearing when using dashboard shortcode (DashboardComponent).
    • Fixed dashboard panel issues (dashbaord_dashboard.html.php, DashboardComponent).
    • Make sure child bundle exists (AddEntityDisplayButton)
    • Re-init reCAPTCHA fields after submit fail (CaptchaHelper, reCAPTCHAComponent).
    • More fixes to have reCAPTCHA field work with AJAX submission (frontendsubmit_login_register_form.html.php, CaptchaHelper).
  • 21st
    • Removed code for deprecated cookie (LoginOrRegister).
    • Removed form build ID from form to skip rendering shortcode (LoginOrRegister).
    • Refactoring (AddDirectory).
  • 19th
    • Implemented IPersonalData and IPersonalDataAuthor interfaces (GuestFieldType).
    • Removed use of guest info cookie (AbstractAddEntity, AddChildEntity, AddEntity, LoginOrRegister).
    • Added check to make sure Directories plugin is already activated (main.php).
    • Bumped version (AddressFieldWidget).
  • 18th
    • Fixed redirection error after guest login to add a child entity (LoginOrRegister).
  • 17th
    • Fixed call to undefined Countries helper fatal error (AddressFormField).
  • 15th
    • Fixed reCAPTCHA field option not displaying for registration form (reCAPTCHAComponent).
    • Added privacy policy consent checkbox to forms for GDPR compliance (LoginOrRegister, SettingsFormHelper).
  • 14th
    • Prevent warrning error message (SliderHelper).
  • 5th
    • Fixed add child entity controller not checking permission (AddChildEntity).
    • Added location address component class to select location fields (AddressFieldWidget).
    • Moved all date/time helpers to System Component (OpeningHoursFieldRenderer).
  • 4th
    • Redirect to referenced item page if any on submit success (AbstractAddEntity).
    • Fixed trying to load map settings from Location instead of Map component (AddressFieldWidget).
  • 1st
    • Change attr name from data-value-name to data-alt-value (AddressFieldWidget).


  • 30th
    • Fixed frontend dashboard panel labels always being empty in backend settings.
    • Updated dashboard view settings (PostsPanel).
    • Passed entity reference ID and field name to add entity from (AbstractAddEntity, AbstractSubmitEnttiy).
    • Update view settings (directory_listing_views.php).
  • 29th
    • Updated gulp files.
  •  28th
    • Initial Commit.
    • Moving Files.