With spring waking up and it starting to get really nice outside (even though it was snowing last week and been raining every other day) I’m finding any excuse to get out there and enjoy it. One thing that came to mind was writing these blogs out in the fresh air. Having the ability to take a nice bike ride out to the path, over to a forest preserve, and hash an article out using WordPress Offline. Nobody is allowed in Star Bucks right now, nor any other places that offer free WiFi, anyway.
And yes I could turn my phone into a hot brick, using its hot spot feature and have no battery left for the return trip, but what if I go visit Glacier National Park again where there is no 4G?! Before I went off to go reinvent the wheel, I did do a dive into if there was an out of the box solution to allow you to make WordPress changes offline.
It’s 2020! How does WordPress offline not exist yet?!
Technically it does, just not for the way I want to use it. I found several methods that help developers code offline and sync their changes later using Git with a dash of CI/CD. But that only covers code changes, I’m looking for something that also takes care of content! Changing up my search I found another article of someone asking around for the same thing. However, the answers were not really good answers. Many of them boiled down to, “you’re stuck writing this in Word offline, then copy and paste your stuff in”. The problem with that is sometimes writing content in an editor vs in the WP editor, doesn’t translate. Making you do double work getting the format right before you can publish.
So, when finding a solution gets hard, what do you do next? Keep digging?? No! Roll up your sleeves and make your own solution!
Making WordPress Offline
First things I needed to figure out. How was I going to setup my infrastructure, find what WordPress components needed to get moved, and how to automate the process to make it as easy as possible.
The Offline Sandbox
My infrastructure is setup as such. Locally I run several docker containers. One for the WordPress front end, one to run the database engine, and one for database management. I chose docker because when you learn how to properly containerize, they blow virtual machines out of the water.
I also created a docker file that includes a few more things that are nice to have in SSH. Nano, a maria-db client, and installing WP CLI. I decided to go with maria DB because of dirty Oracle sticking its dirty fingers in MySQL. OK the real reason is I didn’t feel like dealing with MySQL v8.0 and making it work with WordPress. Yes I could have gone with MySQL v5.7, but again Oracle…
With my WordPress container, I setup a special folder bind, linking my host machine to my container through a folder. This is my way of pushing or pulling files of my container without having to get too fancy. Obviously my site is off in the cloud somewhere, so as long as I have SSH/SFTP abilities I’m good.
Getting the WordPress files
For figuring out what WordPress files that need to sync. Well, turns out it more than files, the database also needs move as well. Since my Docker container files are already synced to my host machine, moving those files are very simple. All I need to do is run rsync -av /path/to/local/files user@host:/path/to/server/files/. and then just run the reverse to get changes from the server.
Of course, for a push or a pull, I would need to then correct the file ownership and permissions. Otherwise, WordPress panics a little. Also in wp-config.php, if my database name, user, or password are different, those will need to be updated as well. Files easy, getting the database, not so much.
Getting the WordPress database
There is a couple of different ways I could have tackled this. Originally I tried scripting up a mysqldump script to pull the DB. But I stumbled on WPCLI. This tool lets you take on various WordPress tasks that you can do now on the command line. Anytime something can be done via the command line is great for automation, but I’m getting ahead of myself. Using wp db export I sync the DB dump to the other side and then run wp db import, easy!
Well, one last thing I forgot about was scrubbing the domain name inside the database. Again, there are a few ways to go about this, and yes again WP CLI to the rescue! The importing side would need to run wp search-replace /sarnelli3D.com /localhost:1234. That will run a search replace and switch all instances of your domain name from localhost or whatever you have it set to. With the DB scrubbed and WordPress working, it was time to automate this thing!
WordPress Offline: Now with more Robots!
Yes, yes I know, scripts are not robots, but someone needs to stand up for them! My weapon of choice for script writing is python. It’s really bash with python makeup on, since most of the time I run os.popen(‘bash script).read() and then use python to decipher the output and figure out what to do next. I like to approach this by breaking down the steps into functions and answering the questions I need to know ahead of time before the script runs. Like, is this a push or a pull? Are you really sure you want to do this?
Once that’s figured out, the script pretty much writes itself. Mimicking exactly what I would do, hence why when things can be done in the command line is a blessing. So with that I present the logical layout of this script:
WordPress Offline Sync Logic
- Pulling
- Set Docker bind folder permissions for the host to gain write access
- Extract the WordPress database on the server
- Rsync all files in wp-content down to localhost (including the extracted database)
- Run an internal Docker Script that does all the internal configuration
- Set file ownership for WordPress access
- Import the database
- scrub sarnelli3d.com for localhost:1234
- Push
- Extract the database from docker to the bind folder
- Rsync all files in bind folder to the server wp-content
- Setup file ownership for server access
- Run WP CLI and import the database
- Scrub localhost:1234 for sarnelli3d.com
Room for Improvement
I won’t lie, this script is super custom and cobbled together with less duct tape and spit than a union Chicago plumber. So I do have some serious refactoring ahead of me. But more importantly is sharing this with the world. I’d like to package this up in a more simplified manner and shape it to work out of the box. Instead of this massive laundry list of pre-configurations to get it all to work on someone else’s system. Even then, it still might not work!
Less importantly, at least for now, is controlling sync conflicts if they arise. Say I go off on a trip and write a bunch of content offline, and sync when I get back. Unknowingly, I forgot to pull my most recent server changes, causing my latest push to blow away the un-pulled content. I’ve played around with the idea of a version control to use for backups or handling conflicts. The problem I kept running into was applying that technique with the database. It’s always the database’s fault!
Welp, that’s all I got for now, until next time!