MHM Rentiva v4.27.2: Settings Testing Pollution Hotfix + Duplicate Addon Notice Dedupe
After the v4.27.1 i18n hotfix, a user reported three more bugs — all originating from the same fresh-install scenario, all triggered by pressing "Run All Diagnostics" once on the Settings Testing page. v4.27.2 breaks the chain.
🐛 Bug 1: All Text Fields Show "1" on Fresh Install
Scenario:
- Plugin is freshly installed.
- User goes to Settings → Settings Testing tab and presses "Run All Diagnostics".
- Returns to General Settings — Brand Name = "1", Cancellation Deadline = 1, Payment Deadline = 1, Support Email, Brand Logo URL, etc. all show "1".
- Pressing "Reset to Defaults" fixes it.
Root Cause
SettingsTester::test_settings_save() was converting empty strings to '1' to trigger a "change detected save":
// Fresh install: $current_str == ''
if ( in_array( $current_str, array( '0', '1', '' ) ) ) {
$test_value = ( $current_str === '1' ) ? '0' : '1'; // → '1'
}
This '1' payload was then passed to the real sanitizer. The sanitizer rewrote ALL fields for the target tab (not just the key under test). The restore mechanism only reverted the explicitly tested keys → collateral writes to other tab fields persisted in the DB.
The critical point: On a fresh install, when the General test runs:
brand_name→ '1' (test flip, then restored) ✓currency→ '1' (flip + restore) ✓dark_mode→ sanitizer wrote it, test never touched it → stayed in DBsupport_email→ sanitizer wroteadmin@...as default, then flipped to '1', not restored → stayed in DB- ... and more
Fix
1. Harness fix — full snapshot/restore
// NEW v4.27.2: snapshot the ENTIRE option, restore the ENTIRE option.
$option_existed_before = false !== get_option( $option_name, false );
$full_option_snapshot = (array) get_option( $option_name, array() );
// ... test runs, sanitizer pollutes DB ...
if ( $option_existed_before ) {
update_option( $option_name, $full_option_snapshot );
} else {
delete_option( $option_name );
}
The test now leaves the DB truly read-only. Collateral writes are automatically undone.
2. Migration — clean up existing installs
SettingsCore::migrate_clean_test_pollution() — one-time flagged migration (mhm_rentiva_v4272_test_pollution_cleaned):
$polluted_keys = array(
'mhm_rentiva_brand_name', 'mhm_rentiva_email_from_name',
'mhm_rentiva_email_from_address', 'mhm_rentiva_support_email',
'mhm_rentiva_booking_url', 'mhm_rentiva_login_url',
'mhm_rentiva_currency', /* ... */
);
foreach ($polluted_keys as $key) {
$value = $settings[$key] ?? null;
if ('0' === $value || '1' === $value) {
unset($settings[$key]); // fallback to default
}
}
Only cleans text, email, URL, currency fields that carry the pollution signature ('0' / '1'). Numeric fields (deadlines) are not touched — the user may genuinely have entered 1 (rare). Legitimate string values are preserved.
🐛 Bug 2: Duplicate Limit Notice on Add-ons Page
The Lite limit warning was appearing twice on the Additional Services admin page — once above and once below the stats cards.
Root Cause
AddonMenu::add_addon_page_title() emitted the notice inside a nested <div class="wrap"> block. The WordPress core admin-notice relocator JS then:
- Placed the standard copy after the
wp-header-endmarker - Left the original copy inside the inner wrap
- → Two copies
Fix
The notice is now emitted in a separate admin_notices callback — AddonMenu::render_addon_limit_notice() — at priority 20. It fires AFTER the inner wrap is closed, and WordPress places it exactly once.
🐛 Bug 3: Settings Testing "Defaults Set" Phantom Key FAILs
The Settings Testing page reported "Defaults Set: FAIL" for setting keys that do not exist anywhere:
mhm_rentiva_timezone(not in General)mhm_rentiva_db_auto_optimize(not in Core)mhm_rentiva_wp_optimization_enabled(not in Core)mhm_rentiva_my_account_url(not in Frontend)
Fix
Removed these phantom keys from the check list. Additionally, the "Email Address Valid" and "Email Validation Works" FAILs were a side-effect of Bug 1 ('1' → is_email('1') = false) — they clear automatically once the pollution migration runs.
🧪 Tests
tests/Migration/SettingsTestPollutionMigrationTest.php — 5 new tests, 19 assertions:
- Text/email/URL/currency pollution is cleaned
- Legitimate user values are preserved
- Numeric fields (deadlines) are not touched
- Post-migration legitimate "1" rename is not overwritten (idempotent)
- Flag is set on fresh install
✅ Verification
| Check | Result |
|---|---|
| PHPCS release | 0 error / 0 warning |
| PHPUnit | 737/737 (+5 new, 732→737), 6 skipped, 0 regressions |
| Plugin Check (PCP) | 0 error, 3 trivial warnings (unchanged) |
⚠️ Upgrade Note
Users who pressed "Run All Diagnostics" and saw Brand Name = 1: the migration runs automatically on the next admin page load. You do NOT need to press "Reset to Defaults". Text/email/URL/currency fields will revert to their defaults.
Numeric deadlines (Cancellation / Payment) are not touched — the migration cannot distinguish these from a deliberate user choice. Manually set the deadlines in the Booking tab to 24 hours / 30 minutes (or your preferred values).
📦 Installation
mhm-rentiva.4.27.2.zip → Plugins → Add New → Upload Plugin, or use the Rentiva Pro auto-updater.
Requirements: PHP 8.1+, WordPress 6.7+.
🎓 Lesson
All three of these bugs — like the v4.27.1 i18n hotfix — are in the runtime DB-state class. Neither PHPCS, PCP, nor the unit test suite can catch them. They only surface when testing a fresh-install scenario through the real admin UI. The feedback_runtime_smoke_test.md memory note now reads "fresh-install UI smoke test mandatory before every major release" — the next major release will integrate this pattern into the checklist.
In context: we shipped 3 releases in one day — v4.27.0 (WPCS submission prep) → v4.27.1 (i18n locale leak) → v4.27.2 (Settings Testing pollution + duplicate Addon notice). Each with zero regressions and new regression tests. The agile patch pipeline works.
🔜 Next
- v4.28.0: Popular Routes shortcode/block/widget (new feature)
- v4.29.0: Vendor Report / Appeal System (new subsystem)
- The v4.27.x roadmap is closed — the next umbrella is v4.28.0
GitHub Release: v4.27.2
