Version 8.10.1.4 - May 14, 2026 - Upgrade Notice Refresh for Legacy 6.x Updaters Version-bump release whose only purpose is to push a new readme.txt to WordPress.org so legacy users still on the 6.x line see a clear "MAJOR UPGRADE" notice the first time they click Update. WordPress only renders the Upgrade Notice block whose header matches the new version exactly, so the previous notice (which described only the 8.10.1.3 SVN-compatibility fix) was never going to reach a 6.x user. No PHP, schema, asset, or template changes - readme.txt copy and version strings only. + Item 1: README.txt - new = 8.10.1.4 = upgrade notice block * "MAJOR UPGRADE from 6.x" message: full modernization summary, "back up your site first" advisory, pointer to the Description tab for the long-form rundown. * Previous = 8.10.1.3 = block restored to its original WP.org SVN compatibility wording (kept for historical accuracy since that release already shipped). * Stable tag header bumped 8.10.1.3 -> 8.10.1.4. + Item 2: pet-match-pro.php - version strings bumped * Plugin header "Version:" 8.10.1.3 -> 8.10.1.4 (line 19). * Constants::VERSION '8.10.1.3' -> '8.10.1.4' (line 74). + Item 3: No code paths touched * No PHP, JS, CSS, template, or schema modifications. * No new constants, no new options, no new hooks. * No operator action required on update. ---- Version 8.10.1.3 - May 13, 2026 - WP.org SVN Compatibility (Trait Constant Removal) Single-file packaging fix discovered while pushing 8.10.1.2 to the WordPress.org SVN repository. The WP.org pre-commit hook lints every PHP file with an older PHP build that does not yet recognize trait constants (PHP 8.2+ feature). The 8.10.x line introduced one `private const` inside `SearchTemplateTrait`, which blocked the commit with `Fatal error: Traits cannot have constants in Standard input code on line 1604`. Resolved by converting the constant to a private static method - same data, same lookup pattern, accepted by the WP.org lint. + Item 1: SearchTemplateTrait - replaced `private const SORT_FILTER_FIELD_ALIASES` with `private static function getSortFilterFieldAliases(): array` * public/templates/includes/class-pet-match-pro-search-template-trait.php line 1604: the partner-specific sort/filter alias map (Constants::PETPOINT => ['breed' => 'primarybreed', 'name' => 'animalname', 'id' => 'animalid']) is now returned from a private static method instead of being declared as a trait constant. * Two internal call sites updated to use the method form: - line 1691 buildSortFilterAliasMirrorAttrs(): self::SORT_FILTER_FIELD_ALIASES[$partner] -> self::getSortFilterFieldAliases()[$partner] - line 1886 (active-sort detection logic): same rewrite. * Comment block at line 1604 retained verbatim - the alias map's rationale, shape contract, and "add new aliases here" guidance still apply unchanged. + Item 2: Codebase audit - no other PHP 8.2+ syntax found * Verified no other traits contain `const` declarations (poster-template-trait, field-filter-trait, field-exclusion-filter-trait all clean). * No `readonly class` declarations. * No DNF (disjunctive normal form) types - e.g., `(A&B)|C` in type positions. * No standalone `true` / `false` types in return-type or parameter-type positions. * No `#[\AllowDynamicProperties]` attribute usage. * Plugin remains PHP 8.1+ compatible. + Item 3: Version constants * pet-match-pro.php Version header and Constants::VERSION bumped 8.10.1.2 -> 8.10.1.3. * Constants::ANALYTICS_SCHEMA_VERSION stays at '2.1' (no schema change). * README.txt Stable tag bumped to 8.10.1.3; new Upgrade Notice entry above the 8.10.1.2 entry. * Operator action items: none. * Verification: * `svn commit` to https://plugins.svn.wordpress.org/petmatchpro/ succeeds without the trait-constant lint failure. * Sort buttons (Breed, Name, ID) on PetPoint search results still render with the correct active state after page load and after a sort change. * Filter dropdown using `breed` against a PetPoint result set still matches `data-primarybreed` cards. ---------------------------------------- Version 8.10.1.2 - May 10, 2026 - PetPoint XML Diagnostics, Foster-Location Warning, AF lost_found Combo Field Display Three operator-facing fixes uncovered during partner-by-partner testing on the demo sites. No schema change. No write-path change. No new shortcode params. Operator action items: none. + Item 1: PetPoint - Failed-XML logs now include libxml errors and a body snippet * includes/pp/class-pet-match-pro-pp-api.php animalDetail(): replaced `@simplexml_load_string()` with the standard libxml-internal-errors pattern. On parse failure the log entry now carries the actual libxml messages and the first 500 chars of the response body alongside animal_id and method. * Previously the warning "[PetMatchPro] PetPoint: Failed to parse animal detail XML" was unactionable - operators saw the animal_id but had no insight into what PP actually returned (HTML error page? truncated XML? BOM? Wrong content-type?). With the new fields a single log line tells us whether it's an authentication failure, a gone-animal celebration redirect from PP's side, or a malformed XML payload. * The `libxml_use_internal_errors()` toggle and `libxml_clear_errors()` calls are wrapped around the parse so the diagnostic does not leak libxml state into the rest of the request. + Item 2: AllApi - Foster-location check no longer emits "Array to string conversion" * includes/class-pet-match-pro-all-api.php isAnimalInFoster() line 2703: getAnimalProperty() can return an array when the partner field is an empty XML element (PetPoint's SimpleXML parses `` to `[]`) or a nested object without a 'value' key. Casting that array to string with `(string)` was emitting a PHP warning to the error log on every detail-page render and search-result card. * Fix: extract the raw value first, then `is_scalar() ? (string) : ''`. Arrays and objects degrade to empty string rather than throwing. Foster detection is unchanged for the scalar case; previously-warning paths now silently return false (animal not in foster) when the location is structurally invalid. + Item 3: AnimalsFirst lost_found combo type - admin-configured fields now display * Reported symptom: with `[pmp-search type="lost_found"]`, combo method set to "found", and "type" / "intake_type" admin checkboxes enabled under Found search results, neither field rendered on the cards even after hard refresh and cache purge. * Root cause: AllApi::callMethod_Parameters() assigns Settings::METHOD_TYPE_PREFERRED to any non-standard shortcode type. `lost_found` is non-standard, so `apiInstance->methodValue` became 'preferred'. In the three universal AF search templates, getMethodValue() short-circuited to `apiInstance->methodValue` before checking the resolved methodCall, so downstream admin-field lookup keyed off `preferred_search_details` (empty) instead of `found_search_details` (where the operator's checkboxes lived). * Fix: re-ordered getMethodValue() in three AF templates so combo-type detection runs BEFORE the apiInstance->methodValue short-circuit. When the shortcode `type` contains `lost_found`, the template now resolves via `methodCall` (foundSearch -> found, lostSearch -> lost) instead of trusting the synthetic 'preferred' assignment. * Files changed: public/templates/af/universal-search-default.php, public/templates/af/universal-search-no-filter.php, public/templates/af/universal-search-filter-widget.php. * Templates intentionally not touched: featured-search-{default,carousel,compact} (hardcoded to METHOD_TYPE_ADOPT - cannot hit combo path), universal-search-structured (already resolves via resolveMethodTypeFromCall() correctly), adopt-celebration-similar (adopt-only by design). + Item 4: Version constants * pet-match-pro.php Version header and Constants::VERSION bumped 8.10.1.1 -> 8.10.1.2. Constants::ANALYTICS_SCHEMA_VERSION stays at '2.1' (no schema change). * README.txt Stable tag bumped to 8.10.1.2; new Upgrade Notice entry above the 8.10.1.1 entry. * Operator action items: none. * Verification on a deployed site: * PetPoint detail page on a gone or invalid animal ID: error log entry includes `libxml` array and `body_snippet` showing what PP returned. * AnimalsFirst lost_found combo search with `type` and `intake_type` checked under Found in admin: both fields now display on every card. * PetPoint or AF detail render: no recurring "Array to string conversion in class-pet-match-pro-all-api.php on line 2703" warnings in the PHP error log. ---------------------------------------- Version 8.10.1.1 - May 8, 2026 - Detail Template Fixes (PP filter labels, currency, side-by-side video column) Bug-fix patch on top of 8.10.1. Three fixes covering the PetPoint adopt-details-navigation-similar template and the side-by-side detail layouts shared by AnimalsFirst and RescueGroups. No schema change. No write-path change. No new shortcode params. Operator action items: none. + Item 1: PetPoint adopt-details-navigation-similar - location filter label resolution * public/templates/pp/adopt-details-navigation-similar.php: getFieldValue() was returning the raw API location value (e.g. "Kennel - Greenhill") instead of running it through the admin-configured filter group mapping. With Filter Values configured under General > Filter Values (Filter 1 = "Kennel - Greenhill" / Filter 1 Label = "Main Kennel"), the search results showed the label correctly but the detail page showed the raw value. Brought to parity with af/adopt-details-navigation-similar by adding a getFilterGroupConfigs() lookup at the top of getFieldValue() that calls resolveDetailFieldDisplayValue() for any field a filter config covers (location, kennel, site, etc.). * The new lookup runs before the common-field switch and the keyMappings table, so admin-configured labels always win over raw API values without breaking the existing field resolution chain. + Item 2: PetPoint adopt-details-navigation-similar - currency formatting on all render paths * Same file: getFieldValue() was returning the price field unformatted ("250" instead of "$250.00") because formatCurrencyValue() was only invoked from renderQuickFields() and renderTitleSection(), not from renderStatsRow() or renderStatsFull(). Moved the formatCurrencyValue() call into getFieldValue() itself so every consumer benefits from a single chokepoint. * Added an explicit 'price' key mapping to the keyMappings table - previously price was reachable only via the 'fee' alias chain (price -> Price -> fee -> Fee -> AdoptionFee). Direct lookup is faster and more readable. * universal-details-navigation.php was already correct - this fix only applies to the navigation-similar variant. + Item 3: Side-by-side detail layout - videos no longer hijack a third column * Templates affected: rg/adopt-similar.php, rg/adopt-default.php, af/adopt-default.php, af/adopt-conversion.php, af/adopt-conversion-no-app.php, af/adopt-conversion-similar.php (6 templates). * Symptom: when an animal had video URLs, the .pmp-details-videos block rendered as a direct sibling of .pmp-details-image-column inside .pmp-details-image-row. With width:100% on the videos block competing against a 90px thumbnails block in the same flex row, the main image got squeezed out and three vertical columns appeared (image / thumbs / videos). * Fix: wrap renderThumbnails() and renderVideos() inside a new
so they share one right-side column. Videos now stack vertically below thumbnails inside that column instead of forming their own. * public/css/pet-match-pro-styles.css: added .pmp-details-thumbs-column rule (flex: 0 0 90px; flex-direction: column) plus child overrides forcing the nested .pmp-details-thumbnails and .pmp-details-videos to flex:0 0 auto with width:100% and column direction. The min file (pet-match-pro-styles.min.css) regenerated from source. * PetPoint detail templates were not modified - PP detail templates do not currently call renderVideos(), so the bug never manifested on PP. * Templates already using the correct pattern (universal-details-navigation, adopt-details-navigation-similar, adopt-wide, adopt-profile-3-column, rg/adopt-cpa) were not touched. + Item 4: Version constants * pet-match-pro.php Version header and Constants::VERSION bumped 8.10.1 -> 8.10.1.1. Constants::ANALYTICS_SCHEMA_VERSION stays at '2.1' (no schema change). * README.txt Stable tag bumped to 8.10.1.1; new Upgrade Notice entry above the 8.10.1 entry. * Operator action items: none. * Verification on a deployed site: * PetPoint adopt-details-navigation-similar template with Filter Values configured: location displays the admin label, not the raw API value. * Same template with price in stats_row or stats_full: value renders with the configured currency symbol (e.g. "$250.00"), matching the rendering in quick_fields and title_fields. * RescueGroups adopt-similar (and the other 5 fixed templates) on an animal with video URLs: main image renders at full size, thumbnails column on the right contains photo thumbs followed by video play tiles stacked vertically. No third column. * Templates not in the fix list render unchanged. ---------------------------------------- Version 8.10.1 - May 8, 2026 - Quality Pass Follow-Through Closes the items deferred from 8.10.0: the error_log REMOVE sweep, identifier quoting in the analytics ALTER and rollup queries, and a refreshed i18n new-strings inventory ready for the next Claude Cowork translation pass. No schema change. No write-path change. No public-facing UI change. Operator action items: none. + Item 1: Developer-leftover error_log sweep * Project-wide error_log count reduced from 93 to 34. Every remaining call now follows the wpdb-failure mandate or the structured [PMP Analytics] / [PMP Rollup] / [PetMatchPro] operational format. * pet-match-pro.php: 18 commented //error_log lines removed across loadDependencies(), defineAdminHooks(), and initializeAnalytics(). Empty-after-removal else branches collapsed. The unreachable "Try alternate path" license-file lookup that only existed to host commented logs was deleted. * admin/class-pet-match-pro-admin-settings.php: 4 commented checkpoint logs (PMP Settings constructor, Logo Path) removed. * admin/class-pet-match-pro-functions.php: 10 commented field-debug + input-callback logs removed. * includes/class-pet-match-pro-all-api.php: 2 commented fieldExclusions debug logs removed. * includes/cache/class-pet-match-pro-api-cache.php: the entire logDebug() helper removed (1 wrapped error_log + 3 callsites in get/set). Per-cache-check chatter with no caller value. * includes/rg/class-pet-match-pro-rg-api.php: 5 WP_DEBUG-gated chatter logs removed (init checkpoint, cURL retry, request dump, missing-apikey/orgID checkpoints). Doubly-redundant `if (defined('WP_DEBUG') && WP_DEBUG) { if (defined("WP_DEBUG") && WP_DEBUG) { error_log(...); } }` pattern eliminated. * public/templates/includes/class-pet-match-pro-search-template-trait.php: 1 commented labels-debug var_export removed. * 9 search templates (pp/found, pp/lost, pp/universal, af/featured-{compact,carousel,default}, af/universal-{default,filter-widget,no-filter}): 18 template-exception logs without [PMP] prefix removed; catch blocks preserved with "Silently degrade - template falls back to non-API rendering." comment for the apiFunction reflection path, and `$this->fieldLevels = [];` retained for the field-levels path. * Audit B row 55 (deactivator.php:206) intentionally kept: the surrounding commented code block is a documented example demonstrating the wpdb-failure logging pattern future devs should follow. + Item 2: Analytics SQL identifier quoting * includes/analytics/class-pet-match-pro-analytics-db.php (schema 2.0 -> 2.1 daily UNIQUE-key migration ALTER, lines 346-349): the table identifier, the index identifier, and all 8 column identifiers in the UNIQUE KEY definition are now wrapped in backticks. Disambiguates from MySQL reserved words and matches the standard identifier-quoting convention. * includes/analytics/class-pet-match-pro-analytics-daily-rollup.php (rollupDay() DELETE at :215 and INSERT at :271): every {$dailyTable}, {$eventsTable}, and column-name identifier interpolated from class-property constants now uses backticks. User-driven values (date, dayStart, dayEnd) were already routed through $wpdb->prepare() in earlier 8.10.x work; this release only adds the identifier-quoting layer. No behavior change today, but future column-name additions that collide with reserved words will work without further changes. * Audit D escape-3 spot-fix in admin-settings.php skipped after review: every echo $html / echo $var site sampled (settings builders, SEO diag block, color CSS output, do_settings_sections body capture) already wraps user data with esc_attr / esc_html / esc_html__ at the sprintf or concat layer. Per the 8.10.0 plan: don't refactor existing-correct code. + Item 3: i18n new-strings inventory refreshed * docs/superpowers/audits/2026-05-06-a-i18n-new-strings.md replaced with a 144-msgid catalog of every translatable string present in 8.10.1 source but not yet in the frozen 8.6.4 pet-match-pro.pot. Each entry lists first-observed file:line so Cowork can spot-check context before translating. * Local environment lacks WP-CLI / gettext, so the inventory was extracted via grep + awk diff; Cowork's `wp i18n make-pot` run remains the authoritative final list. All _n() plural pairs in current code (Month/Months, Year/Years, minute/minutes, animal/animals, two analytics-insights long-form plurals) are already in the 8.6.4 .pot - no new pairs in 8.7.0 - 8.10.1. * Translation notes added: placeholder preservation (%s, %d, %1$s reordering rules), HTML anchor tags inside msgids, the " -- " em-dash sentinel from feedback_no_em_dash.md, the do-not-translate PetMatchPro / PMP brand rule, and a 6-step Cowork workflow ending in the .pot/.po/.mo commit. * The .pot regeneration, .po msgmerge, and .mo recompile happen in the Cowork session so the translation pass lands as one focused commit. + Item 4: Version constants * pet-match-pro.php Version header and Constants::VERSION bumped 8.10.0 -> 8.10.1. Constants::ANALYTICS_SCHEMA_VERSION stays at '2.1' (no schema change). * README.txt Stable tag bumped to 8.10.1; new Upgrade Notice entry above the 8.10.0 entry. * Operator action items: none. The release is internal cleanup and audit follow-through. * Verification on a deployed site: * Reload search results, detail pages, and admin Settings: no PHP errors in debug.log. Intentional logs (analytics rollup, wpdb-failure mandate) still fire when their conditions are met. * Trigger a search with field exclusions configured: debug.log still shows `[PetMatchPro] [DEBUG] Field-Exclusion: ...` entries (PetPoint via the inline filter, AF/RG via the trait); the chatter that previously fired on every successful request is gone. * Visit the Analytics tab: dashboard widgets still render (rollup query identifier quoting is transparent under MySQL). * grep -rc error_log --include='*.php' from project root returns ~34, all wpdb-mandate or structured operational logs. ---------------------------------------- Version 8.10.0 - May 8, 2026 - Quality Pass + Audit Remediation External 5-track audit (i18n / error logging / license gating / WP coding standards / WP.org compliance) drove a code-quality and security-hardening pass across admin, public, and partner-API surfaces. No data-model or schema changes. No operator action required for upgrade. The single visible behavior change is on broken API keys: a previously-misleading "Parse Error: Unable to parse response" public message is now an actionable "Authentication Error: Verify your API key in general Settings." + Item 1: Security - unserialize() called on decrypted license payloads now passes ['allowed_classes' => false] * admin/license/class-pet-match-pro-license.php (2 sites, lines 498 and 881): forbids arbitrary object instantiation. If the licensing server is ever compromised or a MITM intercepts, gadget-chain code execution is no longer reachable from the deserialization paths. Test path: Free / Premium / Preferred all survive deserialization unchanged. + Item 2: Security - color values validated on save AND on render * admin/class-pet-match-pro-admin-settings.php sanitizeColorOptions(): each non-empty submitted value is validated against an allowlist of CSS color formats (hex 3/4/6/8-digit, the 147 named CSS colors plus transparent/currentColor, rgb/rgba/hsl/hsla, and the inherit/initial/unset/revert keywords). Invalid values are silently reverted to the previously-saved value (empty if never set, preserving cascade-default behavior) and a settings_error notice is queued so the admin sees the rejection. The new isValidCssColor() helper is private and reusable. * public/partials/pet-match-pro-public-color-css.php getColor(): blocks CSS-injection breakout chars (semicolon, brace, angle bracket, quote) at the read chokepoint. All ~30 echoes of color values get the protection automatically. Hex/named/functional CSS color formats all pass. + Item 3: Security - escaping fix in license-activation form * admin/index.php: replaced esc_attr_e(purchaseEmail, $this->slug) with echo esc_attr($purchaseEmail). The bareword 'purchaseEmail' was being treated as an undefined PHP constant (deprecation warning + literal string output); the form's email value now populates the configured PMP_lic_email correctly. + Item 4: License gating - admin and wizard hide-when-gated instead of showing locked UI * Tabs / accordions / partner method registers / wizard preset cards / wizard method types / wizard vanity URLs all reflect the active license tier. The surface shrinks to what the operator can actually use; matches WP.org repo team conventions for fewer locked-state hints. * Coordinated changes across admin/class-pet-match-pro-admin-settings.php, the admin/partials/* level files, admin/css/pet-match-pro-admin.css (lock-glyph removal from disabled-field labels), correct license-key for General Exclusions sub-accordion, per-method gating for Display > Empty Fields checkboxes, plugin menu position aligned across tiers. + Item 5: License gating - Free-tier shortcode params + admin features unblocked * admin/partials/pmp-option-levels-general.php: shortcode_thumbs param downgraded from PREMIUM to FREE to match the existing Settings::THUMBS admin checkbox tier. The admin always offered the feature; the param strip path was silently removing it on Free. * public/templates/includes/class-pet-match-pro-base-detail-template.php getMaxThumbs(): removed the redundant inner license check at line 695 that was ignoring the (now-Free) shortcode param. License gating is enforced upstream by stripPaidDetailParams(). * Settings::PAGE_DETAILS . '_' . Settings::POSTER bumped from PREMIUM to FREE so the "Poster Details Page" admin selector is configurable on Free (the print-poster feature itself was already free). + Item 6: Public-facing UX - silent template fallback to admin-configured (P1.10) * AllApi::getSafeFallbackTemplate(template, methodType, isSearch): when a Free-tier visitor requests a paid template, the renderer substitutes the admin-configured template name instead of showing "Template Upgrade Required" to the visitor. AllApi::getTemplateLicenseError() is deprecated to always return ''. Six callers updated (resolveSearchTemplate plus 4 direct callers in PP/AF/RG). + Item 7: Public-facing UX - audience-gating for admin notices * AllApi::buildUpgradeButton() / buildConfigurationNotice() and the inline poster notices in BaseDetailTemplate now wrap output in current_user_can('manage_options'). Visitors get '' (silent); admins still see the in-place "Not Configured" / "Upgrade to Use" hints. Pattern should extend to any other admin-domain UX leaking to public visitors. + Item 8: Public-facing UX - clearer errors and structured diagnostic logging across all 3 API clients * PetPoint, AnimalsFirst, RescueGroups now share the same error-message taxonomy: Connection Error (network/wp_error), Authentication Error (HTTP 401/403 with actionable "Verify your API key in " hint), API Error (other 4xx/5xx with HTTP code shown), Parse Error (200 OK + malformed body, with the partner-specific parser error included). * Each path emits a structured ErrorLogger entry with http_code + method + 200-char response_excerpt. Pre-8.10.0 the broken-key public message was the misleading "Parse Error: Unable to parse response" and the debug log was an empty {"context":""}. * RescueGroups postJson() now exposes http_code via curl_getinfo so the wrapper-based RG flow gets the same HTTP-status check as PP/AF. * AllApi::buildErrorMessage() drops the empty `context` field from log entries when the caller didn't supply one. + Item 9: Public-facing UX - PetPoint XML-level field-exclusion logging * includes/pp/class-pet-match-pro-pp-api.php applyFieldFiltering(): admin-configured field exclusions (Admin > General > Exclusions per method) are applied at the XML layer in PetPoint before the template renders. Added matching ErrorLogger::debug() entries (configuration, per-excluded-animal, summary) so the audit trail is consistent with what AF/RG produce via the FieldExclusionFilterTrait route. + Item 10: i18n wraps for previously-hardcoded translatable strings * Filter-AJAX success messages ("Filter group %d added", "Copied %d filter group(s) from %s to %s") * Meta titles in includes/class-pet-match-pro-all-api.php * Admin Labels-tab section header * PetPoint adoption share-button text (later removed in dead-code pass; AF/RG delegate share UI to Monarch) * Similar-template subtitles across PP/AF/RG (adopt-conversion-similar, adopt-details-navigation-similar, adopt-profile-3-column-similar, adopt-similar) * Title-case for visible button labels ("Not Configured", "Upgrade to Use Print Poster") - tooltips/title attrs stay sentence case. * .pot regeneration with the full set of new strings is deferred to 8.10.1 so the i18n catch-up is one focused commit. + Item 11: Logging consistency - operational error_log calls now route through ErrorLogger * 20 plain error_log() calls across the deactivator (12), RG API (3), color-CSS (1), field-exclusion-filter-trait (3), and AF celebration template (1) now use ErrorLogger::warning/error/debug. Format matches the existing structured logger: [PetMatchPro] [iso-timestamp] [LEVEL] : | Context: {...} * Subsystem prefixes: Deactivator, RG API, Color-CSS, Field-Exclusion, Celebration Template. Status messages (deactivated / uninstalled at