May was a productive month. Sixteen PRs merged across six open source WordPress projects — from Automattic’s Co-Authors Plus to Cloudflare Turnstile integration. Some fixes took three lines. One took three commits across twenty-six files. All of them made WordPress better for someone.
Here’s the story behind each one.
Co-Authors Plus: When a Fatal Error Hides in Plain Sight
The month started with a bug report that didn’t make sense at first. Users were seeing Call to undefined function get_current_screen() — a fatal error in a function that’s been part of WordPress core for years.
The catch? It only happened when another plugin (Revisionary Pro) triggered a post save during plugins_loaded, before the admin screen even existed.
PR #1260 — merged May 1.
The fix was a guard check, but a thoughtful one. Not just “does the function exist?” but “what should we return when it doesn’t?”
public function is_block_editor( $post = null ): bool {
// get_current_screen() is only available after the screen is set up.
// Guard against contexts where it has not been loaded yet (e.g. REST saves).
if ( ! function_exists( 'get_current_screen' ) ) {
return false;
}
$screen = get_current_screen();
The second fix was more subtle — handling the case where is_admin() returns true but get_current_screen() returns null (AJAX requests):
if ( ! $post_type && is_admin() && function_exists( 'get_current_screen' ) ) {
$screen = get_current_screen();
$post_type = $screen ? $screen->post_type : '';
}
Two defensive layers. Zero behavioral change for normal users. The kind of fix nobody notices — until it prevents a site from crashing.
Mailchimp for WordPress: Three PRs, One Story
The Mailchimp contributions tell a connected story across three PRs.
The Automation PR
PR #832 — merged May 5. This was the big one: 232 additions, 25 deletions across six files. The goal was to automate the Mailchimp Site Tracking Pixel connection. Instead of making users manually enter Connected Site IDs, the plugin now calls the Mailchimp API directly:
POST /connected-sites
{
"domain": "example.com"
}
One toggle. One API call. Done.
The tricky part? Detecting when the Premium e-commerce module was already providing pixel logic, and gracefully stepping aside instead of duplicating it. The guard check had to happen at the right layer — not at class instantiation (which would block everything), but at the specific method that outputs the script tag.
The Compatibility Fix
PR #840 — merged May 12. This one solved a problem the automation PR created. Premium e-commerce users were locked out of the tracking pixel feature entirely because the guard was too aggressive. The fix moved the check from bootstrap to the output method, and added a fallback for reading the script URL from the e-commerce module’s stored options:
$mcjs_url = ! empty( $mc4wp_options['mcjs_url'] )
? $mc4wp_options['mcjs_url']
: '';
The Translators Comments
PR #838 — merged May 4. Thirty-eight missing // translators: comments across twenty-six files. Not glamorous, but critical for internationalization:
// translators: %s is the URL to the plugin form settings page.
printf( __( 'You can edit your sign-up form in the Mailchimp for WordPress form settings.', 'mailchimp-for-wp' ), admin_url( 'admin.php?page=mailchimp-for-wp-forms' ) );
Without these comments, translators see a %s and have to guess what it represents. With them, the context is clear.
Newspack Theme: Four PRs for the People Who Read Your Site
Newspack powers news sites. That means accessibility isn’t optional — it’s the product. Four PRs this month, all serving readers.
Respecting Reduced Motion
PR #2687 — merged May 12. A media query that says “if the user asked for less animation, give them less animation”:
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
scroll-behavior: auto !important;
}
}
Note the 0.001ms instead of 0ms. That’s not a typo — Safari ignores zero-duration values. The kind of detail that separates “it works on my machine” from “it works for everyone.”
Screen Reader Pagination
PR #2688 — merged May 12. Pagination links had “Newer posts” / “Older posts” text hidden with display: none — which means screen readers couldn’t read them either.
The fix uses the clip-based visually-hidden pattern that’s already standard in the WordPress ecosystem:
.nav-next-text,
.nav-prev-text {
border: 0;
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
word-wrap: normal;
}
Visually invisible. Accessibility tree intact. Screen readers announce “Newer posts” — sighted users see only the arrow.
Thumbnail Loading Conflict
PR #2689 — merged May 13. WordPress 6.3+ throws a _doing_it_wrong warning when an image has both loading="lazy" and fetchpriority="high". The Newspack theme was doing exactly that on the first featured image.
// Before: conflicting attributes
'loading' => $after_first_featured_image ? 'lazy' : false,
'fetchpriority' => $after_first_featured_image ? 'auto' : 'high',
// After: mutual exclusivity
'loading' => $after_first_featured_image ? 'lazy' : 'eager',
'fetchpriority' => $after_first_featured_image ? false : 'high',
Two lines changed. Zero warnings. First image loads eagerly with high priority. Everything else loads lazily.
GDPR-Compliant Fonts
PR #2693 — merged May 14. Newspack sites serving European audiences needed a way to use web fonts without shipping user data to Google. The fix added Bunny Fonts and CoolLabs Fonts as alternatives:
$font_service_urls = array(
'google' => 'fonts.googleapis.com',
'bunny' => 'fonts.bunny.net',
'coollabs' => 'api.fonts.coollabs.io',
'fonts' => 'fast.fonts.net',
'typekit' => 'use.typekit.net',
'typenetwork' => 'cloud.typenetwork.com',
);
Three lines added. A whole class of GDPR compliance headaches eliminated.
Imagify: Six PRs for a Cleaner Admin Experience
Imagify got the most attention this month — six PRs, all merged into the 2.2.8 milestone.
The Deprecation Sweep
PR #1024 — merged May 13. jQuery Migrate warnings were flooding the browser console. Three deprecated methods replaced with native equivalents across sixteen files:
// .change() → .on('change', ...)
$('#imagify-toggle-plan').on('change', function() {
// $.isArray() → Array.isArray()
if ( ! response.data || ! ( $.isPlainObject( response.data ) || Array.isArray( response.data ) ) ) {
// $.trim() → String.prototype.trim()
context = String( contextId ).trim().match( /^(.+)_(\d+)$/ );
Each change is trivial in isolation. Together, they silence every JQMIGRATE warning in the plugin.
The Null Guard
PR #1026 — merged May 13. When the API key is invalid, plan_label stays null. Hover the admin bar → PHP 8.1+ deprecation warning. One cast fixed it:
$plan_label_value = (string) $data['plan_label'];
$pos = strpos( $plan_label_value, '_' );
The Cleanup
PR #1027 — merged May 13. Imagify was leaving transients in the database after deactivation and uninstall. Two missing delete_site_transient() calls added to the deactivation hook and uninstall.php. Clean uninstall = no database cruft.
The HTML Breakage
PR #1043 — merged May 13. An <br> tag was showing as literal text in the admin bar because esc_html() was escaping it. The fix swapped esc_html() for wp_kses() with a narrow allowlist:
// Before: <br> becomes <br>
echo esc_html( $text );
// After: <br> renders as a line break
echo wp_kses( $text, array( 'br' => array() ) );
Security preserved. Line breaks restored.
The Flexbox Migration
PR #1022 — merged May 13. The “Our Plugins” section used float: left; width: 25% — which broke on tablets, causing cards to overlap. Flexbox with responsive breakpoints fixed it:
.imagify-plugin-family-col {
width: 25%;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
Four cards per row on desktop. Two on tablet. One on mobile. No overlaps.
The Link Update
PR #1023 — merged May 13. Updated the WP Rocket promotional link to point to the new landing page for Imagify users, with correct UTM parameters. Four lines changed. Tracking works again.
Plugin Check: Making the Error Report Actually Useful
PR #1180 — merged May 16. This one went from February to May — nine commits, eight review comments, and a journey through three implementation approaches.
The problem: WordPress Plugin Check showed “Errors were found” with no count. The CLI version showed detailed numbers. The UI didn’t.
The solution counted errors from the existing aggregatedResults object:
function countResults() {
let errorCount = 0;
let warningCount = 0;
for ( const file of Object.keys( aggregatedResults.errors ) ) {
const lines = aggregatedResults.errors[ file ] || {};
for ( const line of Object.keys( lines ) ) {
const columns = lines[ line ] || {};
for ( const column of Object.keys( columns ) ) {
errorCount += ( columns[ column ] || [] ).length;
}
}
}
// ... same for warnings
return { errorCount, warningCount };
}
The message rendering handles singular/plural forms and combines them:
✅ Checks complete. No errors found.
❌ Checks complete. 3 errors and 2 warnings found.
No backend changes. No new data structures. Just better use of what was already there.
Cloudflare Turnstile: The Security Fix That Blocks Before It Happens
PR #84 — merged May 29. The last PR of the month, and a security one.
Elementor forms with Cloudflare Turnstile could be submitted without solving the challenge — either by pressing Enter or if another script re-enabled the submit button. The fix uses a document-level submit event listener in capture mode:
document.addEventListener('submit', function(event) {
if (widget) {
var responseInput = form.querySelector('[name="cf-turnstile-response"]');
if (!responseInput || !responseInput.value) {
event.preventDefault();
event.stopImmediatePropagation();
}
}
}, true); // capture mode — runs before Elementor's handlers
The true parameter is the key. Capture mode intercepts the event before jQuery’s handlers see it. No response token? No submission. Period.
The Numbers
| Project | PRs | Lines Changed | Files Touched |
|---|---|---|---|
| Co-Authors Plus | 1 | +70 / -2 | 2 |
| Mailchimp for WordPress | 3 | +348 / -54 | 32 |
| Newspack Theme | 4 | +27 / -4 | 3 |
| Imagify Plugin | 6 | +188 / -44 | 24 |
| Plugin Check | 1 | +137 / -4 | 2 |
| Simple Cloudflare Turnstile | 1 | +42 / -1 | 1 |
| Total | 16 | +812 / -109 | 64 |
What I Learned
Small fixes compound. A three-line null guard in Imagify prevents a PHP deprecation warning that affects every user with an invalid API key. A one-line wp_kses change restores line breaks for thousands of admins.
Accessibility is never done. The Newspack PRs this month — reduced motion, screen reader pagination, loading attribute conflicts — all serve readers who interact with the web differently. Every site has these users. Most sites don’t serve them well.
Context matters more than code. The Co-Authors Plus fix required understanding when get_current_screen() becomes available. The Turnstile fix required understanding event capture phase. The Plugin Check PR required understanding why JavaScript-side counting was better than backend counting. The code is simple. The reasoning isn’t.
Open source is about showing up. Sixteen PRs across six projects. Some were features. Some were bug fixes. Some were three-line changes that took longer to write the PR description than to write the code. All of them shipped because maintainers reviewed, discussed, and merged them.
That’s what May looked like. On to June.
All PRs linked above are publicly viewable. Contributions were made under my GitHub account @faisalahammad.











Leave a Reply