Every client of an agency is unique. Unique hosts, unique hosting control panels, unique acceptance of “just use a plugin for that,” unique budget constraints, unique opinions about technical stuff from whomever signs off on the project. In time, you learn to stop reaching for a plugin every time you want a simple, unique functionality – plugins require updates, potential conflicts with other plugins, one more thing to break when you move the website to another host, one more settings page nobody will remember two years from now.
And, realistically, by the time you inherit a site at an agency, it will have 40 plugins installed – 31 active, 9 inactive but not removed, and 3 of those active plugins quietly duplicating each other’s functionality because no one bothered checking whether that was the case before installation of the third. No one needs help piling on to that. If your job is “remove this meta tag” or “add this header,” it can be done with an include file in the child theme and no additions to the plugins table.
In addition to all that, most of the things that I kept needing weren’t even client-specific at all – they were just the things that WordPress core had never bothered doing natively, period, every single time, no matter who the client was. Scheduling a post to go back up again. Reasonable size limits by file type for uploads. Proper security headers being sent. Avoiding sending out a generator tag to everyone who asked. These aren’t corner cases, these are universal deficiencies, and I’m a developer – I don’t need an entire plugin’s settings page and update cycle in order to do something that WordPress itself should have shipped with in the first place. I can do it myself. And that’s where this collection of code snippets comes from.
So instead, I write functions. PHP, dumped straight into a child theme’s functions.php (or organized into includes if you’re looking at the stuff below). No plugin header, no settings page bulkiness unless the client really wants it, no library to keep up-to-date. Functions, not plugins. Suck on it.
I’ll be uploading them one at a time as I check they still work 🙂
Admin To-Do List
To-do list that works directly from the WordPress dashboard, accessible only to super admins. Powered by per-user meta, meaning each administrator has his/her own private list – no sharing of state, no stepping on toes of other administrators. Each task item gets priority (high/medium/low), completion flag, timestamps, and the widget filters uncompleted tasks to the top using AJAX add/toggle/delete functionality with nonces verified on every call. In addition, there are also a few helper functions – tracking the number of uses, removing completed tasks, dumping the list into JSON format – along with a management page callback, just in case I ever wanted to set up a proper settings page rather than the dashboard widget.
Backend WordPress Search
The admin search in WordPress is subpar by default, and customers are aware. There are two improvements in the way this is done. Firstly, it introduces a live-search widget into the dashboard that searches every public post type as you type and gives an edit/view link along with a highlighted excerpt, taking care to respect capabilities, meaning that when a Contributor uses the search functionality from their own dashboard, they will not see other authors’ private drafts. Secondly, it expands the Posts/Pages list-table search to also match slugs and postmeta values, as well as pushing title matches to the top of the list.
Clean Up WordPress
The time-tested “strip the cruft” file. This strips the generator meta tag, DNS-prefetch, RSD/wlwmanifest links, shortlinks, emoji scripts and styles (both front-end and admin), adjacent-post links, and the WooCommerce generator tag from wp_head. Additionally, it has a double-duty output buffer to remove any lingering <meta name="generator"> tag just in case one makes it past the other steps. It also removes the ?ver= parameter from enqueued scripts/styles, the Visual Composer/WPBakery generator tag if that plugin is being used, the dashboard Welcome panel, and adds a cat-{id}-id class to body_class() for the post’s primary category. None of this is required, but it is the type of cleaning that will keep your markup clean and not tell anyone who is looking exactly what version of WordPress you are using.
Secure HTTP Headers
There is one function in the send_headers hook that adds basic security headers to all front end responses: HSTS, initial Content Security Policy (frame-ancestors by default, designed to be extended according to each client), X-Frame-Options, X-Content-Type-Options, Referrer-Policy, X-XSS-Protection, and Permissions-Policy which sets geolocation/sync-xhr/fullscreen to same-origin. There is also a removal of X-Powered-By header to prevent leaking PHP version via response. This is something that I always install during initial setup of new client website – basic security protection with very cheap price, and it is known to increase scores of security scanners that clients like to take screenshots from.
SEO Assistant
The largest one within the folder. This provides an administrator with a full-fledged UI – tabs, filters, bulk actions, import/export – for excluding certain posts/pages, entire categories or tags (any publicly available taxonomy, not just the default ones), or raw URLs from three distinct things: search on-site functionality of WordPress itself, XML sitemap provided by the core, and Google indexing (through the use of wp_robots filter, which means that this one builds up correctly, not printing a conflicting meta tag). The important caveat, which I explicitly pointed out to myself within the docblock of the file: being excluded from the sitemap does not prevent Google from indexing something through some other way, such as internal linking. Making something completely invisible for Google requires using the noindex option, which is a completely different checkbox in the UI. In case if a client uses Yoast SEO or Rank Math along with this plugin, then it is definitely useful to check which sitemap is actually served.
Sitemap Shortcode
The small and uninteresting one. The [mh-sitemap] shortcode that simply returns an unordered <ul> of all pages published in the site, in menu order. There are no attributes that you need to set or any nesting logic, just a simple “list my pages” shortcode for the non-technical client who needs a human-readable sitemap page available from their footer. It only works with pages, intentionally, since most clients know a sitemap page to be one of page post type.
Auto-Unpublish Scheduler
WordPress enables you to set your post to be published in the future but nothing out of the box that would enable you to schedule your post to be taken down later – something frequently requested by users doing time-limited promotions, seasonal pages, or recruitment postings. The feature will allow you to add an Auto-unpublish button right beneath the Schedule button in the post editor, implementing the same datepicker as WordPress itself does and scheduling just one wp_cron event via wp_schedule_single_event() function – the way the core schedules future dated publication, only working in reverse. Once scheduled, the post is marked as a draft, private, or even deleted depending on what user selected in the post editor.
File Upload Limits Manager
Enables an administrator to set upload size limits for each individual file-type category – such as images, documents, video, audio, archives, code, or even custom file categories that the user may specify – using wp_handle_upload_prefilter on the server side. The key restriction in all of this is simple – no matter what limit the administrator sets, it is bound by wp_max_upload_size(), the maximum upload capacity for the server itself based on its upload_max_filesize/post_max_size limit. You can decrease the capacity below what the server permits, but you can’t increase it above it – and that becomes a very significant consideration for an agency running sites for multiple clients, since the capacity for each individual host will vary, and this prevents the user from setting up an upload capacity that the host will simply ignore anyway.
That is all there is to that. It might be worthwhile to simply write the function if you are working as an agency developer and are constantly installing the same three functions on each and every client website just to remove the “generator tag” or to add a “security header.”
Comments (0)