WHA Docs

Plugin System

Composer-managed plugins, yax mu-plugin architecture, custom WHA plugins, and the update workflow.

Plugin Management Strategy

Plugins come from four sources, each managed differently:

SourceLocationManaged byExamples
Composer (wpackagist)src/wp-content/plugins/composer.jsonYoast, Redirection, User Switching
Composer (private)src/wp-content/mu-plugins/composer.jsonAll yax-* mu-plugins
Manual (proprietary)src/wp-content/plugins/Committed to gitACF Pro, Schema Pro, BrightEdge
Custom (WHA)src/wp-content/mu-plugins/Committed to gitAll wha-* mu-plugins

composer.json is the source of truth for WordPress core and plugin versions. The private Composer repository at composer.joeyyax.app hosts the yax-* packages.

Composer-Managed Plugins

Third-party plugins are installed via wpackagist and pinned in composer.lock. These cover SEO (Yoast), redirects, content management utilities, form protection (Cloudflare Turnstile), and dev tools (Query Monitor, Debug Bar). See composer.json under the wpackagist-plugin/* entries for the current list.

Dev-only plugins like debug-bar and query-monitor are blocked in production via the WORDPRESS_BLOCK_PLUGINS environment variable.

Yax MU-Plugin System

The yax-* mu-plugins are a shared framework used across WHA and other projects. They're installed via Composer from the private repository and auto-discovered by the loader.

How the Loader Works

  1. 000yax-loader.php is the bootstrap file (auto-generated by the entrypoint script, gitignored)
  2. It loads yax-loader/yax-loader.php which scans mu-plugins/ for yax-* directories
  3. Each plugin declares a Priority header — lower numbers load first
  4. Dependencies declared via Requires Plugins header are auto-validated
  5. Append .disabled to a directory name to skip it

Service Lifecycle

Yax plugins follow a three-phase lifecycle:

  1. boot() — Load dependencies, initialize early features (runs on muplugins_loaded)
  2. register() — Register with WordPress, configure hooks (runs on yax/register)
  3. freeze() — Lock configuration, prevent changes (runs on plugins_loaded)

Core Yax Plugins

PluginPriorityPurpose
yax-common0Service container ($yax), shared utilities (text, CSS, HTML, data formatting, heading manager, nav menu extensions, asset versioning, SVG support)
yax-model1Code-first model system — fluent builder API for register_post_type(), register_taxonomy(), metaboxes, options pages
yax-query2Fluent WP_Query builder — yax_query(), yax_posts(), yax_terms(), yax_page(), yax_post()
yax-components2Component rendering — get_component(), set_props(), get_components(), pluggable field providers (ACF, Null, custom)
yax-loaderAuto-discovery and priority-based loading of all yax-* plugins

Other Yax Plugins

The remaining yax-* plugins handle infrastructure concerns: media offloading (yax-offload-media), Composer version sync (yax-version-sync), security hardening (yax-security), performance (yax-performance), search (yax-meilisearch), analytics, content sync, cleanup, and more. See composer.json (project root) under the joeyyax/* entries for the current list.

Custom WHA Plugins

The wha-* mu-plugins are committed directly to the repo (not Composer-managed). Most register a custom post type with its associated taxonomies and business logic. A few handle integrations and specialized functionality. See src/wp-content/mu-plugins/wha-* for the full list and Custom Post Types for the data model.

Doctor.com Sync (wha-doctorcom-sync)

Syncs provider and location data from Doctor.com's API into WordPress. This is how provider profile data (bios, photos, specialties, locations) stays in sync with the Doctor.com directory.

  • Architecture: API client, FieldMap (maps Doctor.com fields to WordPress/ACF fields), ProviderSync, LocationSync, Scheduler
  • Admin UI: Settings page for API credentials and sync configuration, plus provider and location management pages for manual sync
  • Sync modes: Individual provider/location sync via AJAX, or batch sync via WP-Cron
  • Cron schedules: Configurable auto-sync intervals for both providers and locations (wha_sync_providers, wha_sync_locations)
  • Audit logging: All sync operations are logged via yax-audit-log (synced, failed, batch start/complete, paused, resumed)
  • Dependencies: yax-common, yax-model, yax-audit-log

Provider Ratings (wha-provider-ratings)

Fetches and stores PressGaney patient satisfaction ratings. This is a data-only plugin — it queries the ratings API, stores the results, and exposes getters. The theme handles display.

  • Data stored: Aggregate rating (overall score + response count), individual question scores, and patient comments per provider
  • Display control: Configurable thresholds determine whether ratings show for a given provider. Providers below the threshold don't display ratings at all.
  • Theme functions: wha_get_ratings($id), wha_get_ratings_questions($id), wha_get_ratings_comments($id), wha_should_display_ratings($id)
  • Sync: Scheduled via WP-Cron, manual sync available in wp-admin
  • Components: The stars.php UI component and provider-ratings section component consume this data
  • Dependencies: yax-common, yax-model, yax-audit-log

Appointments (wha-appointments)

Appointment scheduling wizard with REST API, custom rewrite rules, and MyHealth patient portal integration.

  • MyHealth integration: Links appointment types to the MyHealth patient portal for online scheduling
  • Query helpers: Custom queries for appointment type lookups
  • ACF fields: Registered via yax-model for appointment type configuration

Protected Content (wha-protected-content)

Page-level, component-level, and CTA-level authentication with role-based access control.

  • Granular visibility: Content can be restricted at the page, section component, or individual CTA level — not just entire pages
  • Role management: Supports role self-assignment, allowing users to select their role (e.g., patient vs. provider) which controls what content they see
  • Theme integration: yax-components checks visibility before rendering each section, so protected sections are invisible to unauthorized users without any theme code changes

Non-Composer Plugins

These proprietary plugins are committed to git and updated manually:

PluginNotes
ACF ProAdvanced Custom Fields Pro — core dependency for all flexible content. Not available on wpackagist.
Schema ProStructured data / schema.org markup
BrightEdgeSEO platform integration
SendGridEmail delivery

These are plugins that aren't available through Composer (proprietary, paid, or not on wpackagist). They're committed directly to the repo and updated manually: download the new version, replace the plugin folder, commit.

Other Committed Files

FilePurpose
healthz.phpHealth check endpoint at /healthz for uptime monitoring
redirects.phpCustom redirect rules (mu-plugin)
carbon-fields-stub.phpStub for legacy theme compatibility
yax-loader.wha.phpWHA-specific loader customizations

Updating Plugins

Composer-Managed (wpackagist)

Two paths:

From wp-admin (preferred):

  1. Click "Update" in the WordPress admin plugins page
  2. yax-version-sync automatically runs composer require wpackagist-plugin/plugin-name:X.Y.Z
  3. Commit the updated composer.json and composer.lock

From CLI:

composer require wpackagist-plugin/plugin-name:^X.Y
git add composer.json composer.lock
git commit -m "Update plugin-name to X.Y"

WordPress Core

Same as plugins — click "Update" in wp-admin, yax-version-sync handles the Composer manifest update:

# Or manually:
composer require johnpbloch/wordpress-core:X.Y.Z

Adding a New Plugin

If available on wpackagist (most free plugins):

composer require wpackagist-plugin/new-plugin

From wp-admin: Install the plugin normally. An admin notice with "Add to composer manifest" button appears — click it and commit.

If not available via Composer (paid, proprietary, or not on wpackagist): Install or download the plugin, add it to src/wp-content/plugins/, and commit it to the repo. This is how ACF Pro, Schema Pro, BrightEdge, and SendGrid are managed.

Disabling MU-Plugins

Append .disabled to any mu-plugin directory name to prevent it from loading. The yax-loader skips directories with this suffix. Currently disabled:

  • wha-national-provider-identifier.disabled — NPI lookup functionality
  • wha-photo-sync.disabled — Photo sync from external source

Blocking Plugins in Production

Set the WORDPRESS_BLOCK_PLUGINS environment variable to a space-separated list of plugin slugs. The entrypoint script deletes these on every container start:

WORDPRESS_BLOCK_PLUGINS="debug-bar query-monitor rewrite-rules-inspector"

On this page