Recent Work
Day-to-Day Work
Day to day, most of what I do is this kind of thing:
Security, Speed and Servers
- Security, speed and database clean-ups
- VPS updates and tuning for site speed
- Removing redirect malware
- PHP and WordPress upgrades
Technical SEO
- Ongoing updates, security and technical SEO
- Technical SEO for local search
- Technical SEO and UX improvements
- Removing URLs from Google Search Console
Fixes and Audits
- Fixing an API conflict between Amazon plugins
- Theme changes and site audits
Builds and Migrations
- Setting up staging sites
- HTML to WordPress conversions
- Converting WordPress sites to multisite and merging sites
- New WordPress sites for small businesses and organisations
- An interactive map for a client's site
Case Studies
Irish Competition Site on WooCommerce
10'000tickets in the largest competitions
2numbering modes, switchable mid-competition
A custom Irish competition site on WordPress and WooCommerce, built during Covid lockdown. People answer a skill question, buy tickets and a winner is drawn from the valid entries.
More detail
I wrote the plugin behind it: how tickets are given out against an order, how the winner is picked, and the reports on who entered.
Each competition keeps a range of ticket numbers and a list of the ones already issued. Numbers can be allocated randomly or in sequence, set per competition, and a competition can be switched from one to the other part-way through: the plugin works out what has been issued so far and carries on. Allocation runs inside a per-product database lock, so two orders arriving together queue up instead of colliding.
A wrong answer to the skill question earns no tickets and doesn't count as a sale. If an order is cancelled, fails or is refunded, its numbers go back into the pool, and the competition page is purged from the LiteSpeed and Cloudflare caches so the tickets-left count stays right. Per-order and per-customer limits are enforced when someone adds to the cart and checked again when they log in. The entrant reports pull orders, quiz answers, coupons and ticket numbers into one list.
By the time the site was sold in 2025 it was running competitions of up to 10'000 tickets.
Auto-Populating Course Calendars for a Training Company
190course pages
4'200dates generated
1'250dates still to come
The company has about 190 course pages, and none of them has a date typed in by hand. I built a page element that works out each course's upcoming dates from a few settings: the length in days, which weeks of the month it runs, and how many months ahead to show. UK and US dates are generated separately, they recalculate when the month rolls over, and any date that lands on a public holiday is dropped. US federal holidays come from the government's calendar feed with a local copy as a fallback, and UK holidays come from a local file.
More detail
The page also shows each visitor the dates that suit them. A Cloudflare Worker reads the visitor's location at the edge, before the page reaches the browser. It tags the page with their continent and sets a cookie to remember it. People in the UK, Europe, Africa and Asia see the UK dates, and everyone else sees the US dates. Both lists are in the page, and a little CSS shows the right one. The same switch picks the contact numbers in the site header: the UK and European numbers for those regions, a North American number for everyone else. It fails safe: if anything goes wrong, the visitor just gets the normal page.
The site currently holds about 4'200 generated dates, 1'250 of them still to come, and the same dates feed the course data Google reads. I also added testimonial and review post types, and sent Contact Form 7 leads into Salesforce.
Automated AI Content Rewrite Workflow
400+pages to rewrite
3rewritten per night
1command to roll back any page
A cat content site with more than 400 bulk-generated pages from 2023 that need rewriting and fact-checking. I built a workflow that rewrites three per night and publishes only what survives review.
More detail
One model researches and drafts, and has to cite pages it actually opened. A script checks the draft for stock phrases, broken links, spelling and citations. A second model checks every claim against the pages the writer opened. If the reviewer isn't satisfied, the writer sends back exact edits instead of a new draft, and the script applies them so the rest of the text stays identical. If the reviewer is not satisfied after three rounds, the edits are marked for human review.
When a page does publish, the script re-reads the live page and restores the original from a snapshot if it doesn't match. Every change is logged, any page can be rolled back with one command, and the whole process pauses itself if too many pages fail, with a notification to the site administrator.
In week one it had published 5 pages, and the checks had refused 6, mostly over sources that didn't check out. That's the safeguards working. The one real fault was the live-page check treating straight quotes in a draft as different from the curly ones WordPress displays, so it rolled back a good edit that should have passed. The mistake was caught, the script adjusted, and the page published.
A WhatsApp AI Assistant for Staff
170documents searched
02:00conversation reset, daily
30days, then history is deleted
The staff of a Children's Home work from a large body of policies (~200 pages) and manuals (~350 pages). I built a WhatsApp AI assistant limited to a whitelist of numbers. Any numbers not on the whitelist receives no reply. It searches a database of about 170 of the Children's Home's own documents and answers from them, pitched to the person's job: a social worker and a child and youth care worker need different detail on the same rule. It responds in the language the person messages in: English, Afrikaans or isiXhosa.
More detail
To comply with privacy laws relating to children, it doesn't accept anything that identifies a child beyond a first name and logs breaches by category of breach and person who breached, notifies the person of the breach, and reports the third and further breaches to the manager. A child mentioned is searched in a database to determine pronouns and age-band, ensuring a response is appropriate (speaking about a girl if it's a girl, or a boy if it's a boy, and not mentioning anything about - for example - nappies when speaking about a teenager). It gives no legal, medical or disciplinary advice. Each conversation resets at 02:00, and history is deleted after 30 days, as the Children's Home's records policy requires.
Server Administration
13sites on one VPS
Nightlybackups to Google Drive
Weeklyautomated restore test
I administer my own VPS with more than a dozen sites on it. The stack is OpenLiteSpeed, PHP, MariaDB tuned for the load, Redis object caching and LiteSpeed Cache, behind Cloudflare. The origin only answers Cloudflare, and the admin console and database only listen on the server itself.
More detail
Every night WordPress sites are automatically backed up and copied to Google Drive. An automated script restores those backups into a temporary location and checks that the result confirms the backup, reporting any deviances.
API Integrations
Integrations are the part of my work I enjoy most. A few I've built, all running on live sites:
- Google Places reviews. A plugin fetches a business's rating and latest reviews every week and shows them through shortcodes, so the reviews on the site stay current without anyone touching it.
- TheCatAPI, API Ninjas and Petfinder. Breed data from several APIs is imported into a custom post type, in scheduled batches so the API limits and timeouts don't break the import. Petfinder needs an OAuth token, which is cached until it expires.
- Facebook page auto-posting. Twice a day a site picks an article it hasn't shared yet, builds the post from its meta description and tags, publishes it to the Facebook page through the Graph API and records what went out. If Facebook returns an error, the site administrator gets an email.
- Cloudflare Turnstile. Bot protection on contact forms, wired into Contact Form 7 and the Enfold form element, replacing reCAPTCHA.
- Cloudflare cache purging. When the site cache is cleared, Cloudflare's cache is cleared with it, so visitors never see a stale page.
I also write custom Enfold (Avia) builder elements and child themes, so clients get their own elements in the page builder instead of shortcodes they have to remember. The course-dates element above is one.