WordPress – closingtags </>
Docker Plugins WordPress

WordPress + Docker v2

I updated my #Docker + #WordPress containers to build a custom 🔌 #plugin for a client

CSS HTML5 Javascript Programming SvelteKit WordPress

Accessibility for Lazy Developers

I’m lazy and you probably are too. This post talks about a few easy-to-use tools that even the laziest of developers can use to ensure their application is accessible to all.

#a11y #accessibility #automation

Automation CSS HTML5 Javascript Linux Mobile PHP Programming Security Server Svelte WordPress


I’ve been writing on this blog for nearly 9 years and I’ve learned so much since I started. The style and the content have come a long ways and I cringe every time I read old posts thoroughly enjoy seeing how I’ve grown as a developer. I’ve interacted with people that I never would have had the chance to otherwise. It’s been a wonderful learning experience.
While my intentions have always been for this site to exist as a sort of journal/wiki/knowledgebase/playground, I’ve always secretly wanted to become a billionaire tech influencer. And now you can help me achieve that goal by buying my merchandise!
By purchasing merchandise from my shop, you can support this site financially, by giving me real money that you’ve earned for your “hard work.” While donations are always appreciated, I understand that you may want something in return; something tangible, something you can see and smell, something to keep you comfortable while you cry yourself to sleep. And since nobody actually donates to strangers on the internet, I opened a shop.

All of the designs are completely original and there are many, many more to come. The pricing is affordable for all budgets and will only expand with more options. Be sure to check posts here often by following the social media channels or the RSS feed. There may just be coupon codes hidden in future posts 😉.
So if you’re ready to showcase the fact that you know what HTML is and like the look of monospaced fonts, then you should go checkout the new closingtags merch shop. Once you’ve got the closingtags swag (closingswag 🤔), be prepared to have people you barely know ask if you “work with computers” or to tell you about their genius new app idea.
Don’t forget to buy, buy, buy!

Plugins Programming Themes WordPress

WordPress + Docker

Dear Vagrant,
You’ll always hold a special place in my heart but there comes a time when we have to put the past behind us. We grow, change, and you and I aren’t what we used to be. We’ve grown apart and become so different. There is no doubt that someone out there will love you, but for me; well I’m done with the days of an upgrade to VirtualBox breaking my virtual environment. I’m saying goodbye to your virtual machines taking a whopping 7 seconds to start. Vagrant, I’ve found someone else, and yes; it is Docker.
If you’ve read any recent posts of mine, you’ll have noticed a distinct lack of information regarding WordPress. It’s not that I dislike WordPress now but given the option of developing with WordPress or not-WordPress, I’d choose not-WordPress. Remembering all of the hooks, the cluttered functions.php files, and bloated freemium plugins has all become so tiresome. That’s not to say that I’ll never work with it again, after all; this blog is powered by WordPress as is more than 1/3 of the web as we know it. Since I’ll never officially be done with WordPress, I should at least find some more modern ways to manage development with it.
Dockerize It
To get WordPress development environment up and running quickly, I use docker-compose. I’ve written about it before over here. It’s incredibly simple since the docker community maintains a WordPress image.
image: mariadb:10.6.4-focal
command: ‘–default-authentication-plugin=mysql_native_password’
– db_data:/var/lib/mysql
restart: always
– MYSQL_ROOT_PASSWORD=somewordpress
– MYSQL_DATABASE=wordpress
– MYSQL_USER=wordpress
– MYSQL_PASSWORD=wordpress
– 3306
– 33060
– db
image: wordpress:latest
– “8000:80”
restart: always
– ./:/var/www/html
– ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
For the most part, this file is the same as the one taken from the docker quick start example. Here are couple directives that I made changes to:

ports: I changed these around as I had some other services running from different programs
depends_on: Telling WordPress not to start until the DB has started also prevented some issues where I was seeing the famous “white screen of death”
volumes: set the current working directory to be the WordPress instance as well as created a custom PHP ini file to fix upload size restrictions

file_uploads = On
memory_limit = 1024M
upload_max_filesize = 10M
post_max_size = 10M
max_execution_time = 600
Placing this uploads.ini file in a directory accessible by the docker-compose.yml let me fix problems with large file uploads.
I got 99 problems and they’re all permissions
This is all well and good but there are some issues when trying to develop locally. For instance, after running docker-compose up -d, I noticed that all files belong to www-data:www-data. This is necessary for the web server in the container to serve the files in the browser at http://localhost:8000. But then I didn’t have write permission on those files so how could I manage them?
I came across a couple of solutions but they each have their drawbacks. For instance, if I set the entire WordPress instance to be owned by my user, then the web server won’t have permission to read or write to files. I could also add my user to the group www-data but even then, I won’t have write permissions until I run something akin to sudo chmod 764 entire_wordpress_dir which isn’t desirable (but is probably the best option so far). The compromise I came up with was setting myself as the owner for all of the WordPress install, and giving WordPress ownership of the uploads directory. It seems that for now, I’ll just have to flip permissions via the CLI.
If you have ideas on how to resolve the permissions issue with WordPress and docker-compose, leave a comment below!

PHP Programming Security WordPress

I Gave a Talk

I recently had the opportunity to give a presentation in front of a live audience with real human beings at the WP Omaha meetup group. For my first technical talk, I thought things went pretty well. There were some minor hiccups with my connection to the live stream cutting out (and poor audio quality), but most of it was the talk was recorded and uploaded to the WP Omaha YouTube page.
The talk itself was a security talk aimed at developers where we hacked a site installed on my computer in real time, analyzed the vulnerability within the code, and discussed how this could be prevented in the future. If you’re interested, the presentation can be downloaded here.


WordCamp Omaha 2018 Great Success

It was all worth it to see my face on the big screen for the morning announcements

This year, I had the pleasure of giving back to the WordPress community by helping organize WordCamp Omaha 2018.  Not only did I lend a hand with the website, I also planned and managed volunteer activities. I learned a lot about putting an event like this together and had a great time doing it with awesome people. I won’t lie; there were times that I considered backing out of it and giving up but in the end, I’m glad I stuck with as the whole event turned out really well.

Our venue, Mammel Hall at University of Nebraska at Omaha, was awesome. Aside from a couple technical hiccups with speaker laptops not having the proper ports and scrambling to find the correct adapter, everything went smoothly.

The atrium was huge with oh-so-cozy seating. Seriously. Look at those chairs!

Zac Gordon (https://javascriptforwp.com/) gave an excellent talk about Gutenberg and its role in the future of WordPress. I’ll be honest, I was skeptical before his talk but afterwards, I’m a little more comfortable with the change.

After the event, WCO18 treated speakers and sponsors to dinner at Taco Co!

The Taco Co building is an old bank so when you go to use the restrooms, you’re standing where the vault used to be.

We hosted an after-party at Beercade in Benson which was a hit with the WordCamp crowd. In their basement, the games are free (if you reserve the room, otherwise it’s a $3 cover charge to get into the basement)!

The basement just before the WordCamp crowd showed up. There’s also a Wii hooked up to a projector to the right and a fully stocked bar to the left.

I always make it a point to grab as much swag as possible at WordCamps. My goal is to have a complete wardrobe full of free WordCamp shirts.

If you’re a WordPress user of any skill level; complete newbie, expert admin, or a rockstar developer, I would highly encourage you to find a WordCamp near you and attend it. Better yet, volunteer at it and you’ll probably get in for free! There’s a little something for everyone, it’s a great opportunity to meet interesting people, and it’s incredibly fun.

Linux Security Server WordPress

WordPress Backup Bash Script

I threw together a simple bash script to be run via cron jobs that will backup an entire WordPress site. Technically, it will work for a lot more than that assuming the site you’re backing up has a single directory to be backed up and a MySQL/MariaDB database. It’s a fairly simple script and it is easy to expand to work with multiple directories. Just take a look at the source for yourself!
To use, fill in the information at the top of the script (site name, database name, database user + password, path to site, and backup path), upload the script to `/etc/cron.daily/` on your server and you’ll be good to go!
Take note of the `SITE_PATH` variable at the top of the script. For whatever reason, there needs to be a space between the first “/” and the rest of the path.

jQuery PHP Plugins WordPress

Restricting Dates of jQuery UI Datepicker with custom WordPress plugin

Recently, I was tasked with adding a custom field to a WooCommerce order details section. This itself is not a major task and is well documented online, but this particular field had some special requirements. It had to

Allow users to select a date for delivery/pickup
Restrict possible order dates to days when the facility is open

Sounds simple enough, right? Just toss a calendar widget in there and limit the days to weekdays since weekends and holidays are the only days when this particular facility is closed. Weekends are easy, but holidays? That’s where it got a little bit weird. Take Labor Day for instance. It always falls on the first Monday in September. President’s Day falls on the third Monday of February each year. These wouldn’t be difficult to code for the current year, but we don’t want our calendar widget to only work for this year. It should work for every year, so we don’t have to remember to go update our code every December 31st.
This company also has special rules regarding holidays. For example, if the 4th of July falls on a Saturday, then they are closed the Friday before. If the 4th is on a Sunday, then employees get the following Monday off. All in all, there are 11 different holidays we need to prevent orders from happening on.
I wasn’t sure how this could all be done with jQuery, so I ended up doing it in PHP and then serving up an array of dates to be blocked off in the widget with an AJAX call.
In our PHP code, I went through the normal suggested steps of enabling AJAX calls in a plugin and ended up with the function `company_holidays_callback`. This function gets our current year, and pushes each holiday onto an array. It does this for the current year, and the following year. Figuring out the date for each holiday wouldn’t have been so simple without the PHP strtotime function. That was a life saver! And the  function `fri_or_mon` was the logic used to mark the previous Friday or the following Monday off of the calendar as per special rule of this particular company.

To break down the jQuery, we create the instance of our datepicker on the element `#order_fulfillment_date`. If that element exists on the page, we perform our AJAX call to get the list of dates that need to be restricted. The function `noWeekendsOrHolidays` checks to if the date is a weekend. If it is, it gets disabled. If it isn’t, it passes the day onto `nationalDays` where we check if the date is one of our holidays that needs to be disabled as well.

In all of this code there isn’t really anything special or groundbreaking, but I thought the logic that determined the holidays was useful and maybe someone else could save themselves a little bit of time and headaches. Thanks for reading!

Plugins Themes WordPress

Deployments w/Git

FTP sucks. I mean, it’s fine. Whatever. But moving code with FTP sucks. You have to make sure you’ve downloaded the latest version, and you have to be careful not to overwrite or delete other files. One missed click, and you can have a long evening ahead of yourself. Because of the issues I’ve had with FTP in the past, I’ve been on the lookout for better ways of getting my code to my applications. You might remember that I previously posted about deploying a web app using Capistrano and while Capistrano is really cool, I still suck at Ruby. So when confronted with yet another deployment problem, I wanted to use Capistrano, but I didn’t want to have to butcher someone else’s deployment recipe again. And I really, really didn’t want to FTP my code up to the server again.
In comes Git; not just for version control. You’re already using it to keep your code safe. Why not use it to securely push your code to your server? In my case, I needed to push a plugin that contains custom features. So how do you use Git to deploy your code?
I’m going to operate off of the assumption that you’ve already got your code in a repository. If you haven’t done that, do it now. Once you have done that, then SSH into your server and create directory to store a remote repository.
ssh [email protected]
mkdir repo_title && cd repo_title
git –bare init
You’ve now got a bare repository to host your code. The next step deals with the hooks in the repo.nano hooks/post-receiveIn that file, drop this code:
GIT_WORK_TREE=/var/www/public_html/wp-content/plugins/custom_plugin git checkout -f master
Obviously, change that path to whatever the path is to your plugin location on the server, save it, and then run:chmod +x hooks/post-receiveso that your code has execute rights.
Now exit your SSH connection, and go back to your local repository. You’ll now need to create a remote in your local repository for your server.
cd your-project
git remote add myserver ssh://[email protected]/~/repo_title
git push myserver +master:refs/heads/master
To deploy your code, just rungit push myserverAnd that’s it! Now, you can move code without ever having to write a Ruby script or open an FTP client.
Thanks to Matt Banks over at mattbanks.me for his excellent write-up. I recommend you go check that out seeing as how that’s where I found this.
Source: Managing WordPress Theme Deployments with Git – Matt Banks
Update: It’s become popular to rename the master branch to main. If that is the case for you, simply replace all instances of “master” in the code with the branch you would like to use.

PHP Security WordPress Yii2

Deployments w/Capistrano

For so long, developers have been moving code with FTP/SFTP. It’s time consuming, and comes with its faults. One false click, and you could easily overwrite a file that you haven’t downloaded yet. And that’s not good. Especially if there’s no version control in place. I know; it’s 2015 and if that happens to you, you kind of deserve it. But the fact of the matter is that a lot of developers at smaller organizations are still doing things this way.
This is where Capistrano comes in. Capistrano is a Ruby gem that makes deployment a one command process. Seriously.
cap production deploy
That command is all you need to push code to your production server.
Capistrano lets you use a git repo to launch your application. Instead of opening up FileZilla, finding a directory, and uploading everything manually, Capistrano will go to your repository, and move that code via a secured connection (SSH) to your server. The Capistrano website has everything you need to get setup so I won’t go into that here, but I will drop in my configuration to show how I’m using it to deploy a Yii2 app.
set :application, ‘PROJECT_NAME_HERE’
set :repo_url, ‘https://GIT_REPO_HERE.git’

# Default deploy_to directory is /var/www/my_app_name
set :deploy_to, ‘DIR_TO_DEPLOY_TO_ON_SERVER’

# Default value for linked_dirs is []
set :linked_dirs, fetch(:linked_dirs, []).push(‘web/uploads’, ‘vendor’, ‘runtime’)

# Default value for default_env is {}
# set :default_env, { path: “/opt/ruby/bin:$PATH” }

# Default value for keep_releases is 5
# set :keep_releases, 5


namespace :deploy do

after :restart, :clear_cache do
on roles(:app), in: :groups, limit: 3, wait: 10 do
# # Here we can do anything such as:
# within release_path do
# execute :rake, ‘cache:clear’
# end

namespace :symlink do
desc ‘Symlink release to current’
task :release do
on release_roles :all do
tmp_current_path = release_path.parent.join(current_path.basename)
execute :ln, ‘-s’, release_path.relative_path_from(current_path.dirname), tmp_current_path
execute :mv, tmp_current_path, current_path.parent

desc ‘Symlink files and directories from shared to release’
task :shared do
invoke ‘deploy:symlink:linked_files’
invoke ‘deploy:symlink:linked_dirs’

desc ‘Symlink linked directories’
task :linked_dirs do
next unless any? :linked_dirs
on release_roles :all do
execute :mkdir, ‘-p’, linked_dir_parents(release_path)

fetch(:linked_dirs).each do |dir|
target = release_path.join(dir)
source = shared_path.join(dir)
unless test “[ -L #{target} ]”
if test “[ -d #{target} ]”
execute :rm, ‘-rf’, target
execute :ln, ‘-s’, source.relative_path_from(target.dirname), target

desc ‘Symlink linked files’
task :linked_files do
next unless any? :linked_files
on release_roles :all do
execute :mkdir, ‘-p’, linked_file_dirs(release_path)

fetch(:linked_files).each do |file|
target = release_path.join(file)
source = shared_path.join(file)
unless test “[ -L #{target} ]”
if test “[ -f #{target} ]”
execute :rm, target
execute :ln, ‘-s’, source.relative_path_from(target.dirname), target

task :composer do
on roles(:app) do
within release_path do
execute “cd #{release_path} &amp;&amp; php-latest ~/composer.phar install”

task :setup do
on roles(:app) do
within release_path do
execute “cd #{release_path} &amp;&amp; php-latest yii.php setup”
# execute :php, “yii setup”

after :updated, “deploy:composer”
after :updated, “deploy:setup”

server ‘closingtags.com’,
roles: %w{app},
branch: ‘master’,
ssh_options: {
keys: %w(~/.ssh/id_rsa),
# forward_agent: false,
auth_methods: %w(publickey)
# password: ‘please use keys’
Now, I’m no Ruby developer, so if my code could be cleaned up, which I know it can be, let me know. But let’s take a quick walk through with what we have here. Firstly, anything starting with # shows a commented line. I’ve removed most of mine, but left a few I thought might be helpful. You’ll notice the first three uncommented lines of the deployment file are just setting some variables for the configuration.
The fourth line, set :linked_dirs, is doing something really interesting. When Capistrano deploys my app, it creates a git repo on the server and will keep the last 5 versions. The linked_dirs variable is basically creating a symlink on my server, so that it doesn’t recreate those files every time. This was necessary for my uploads directory, which needs to be have the same location each time I deploy. If I created a new directory each time I deployed, users would get 404’d every time I deployed.
The next few segments actually contain a description so there isn’t much to say about those, other than they were necessary to deploy to MediaTemple’s GridServer. Most of the time, this stuff isn’t necessary, but I had a special case with our server.
task :composer installs all of my dependencies and task :setup runs a custom yii2 command that upgrades the database, if necessary.
Now like I said earlier, I’m using this to deploy a Yii2 app, but really, it could be used for anything. It’s mostly used to deploy Ruby applications, but there really isn’t anything stopping you from deploying something built WordPress or Laravel. If you have some suggestions on how I could streamline my process, let me know!