Skip to content

feat: Add admin UI meta box for editing provider_listings in community-listings plugin #1

Description

@miguelcolmenares

Background

The community-listings plugin registers a community custom post type with a communityMeta field group exposed through WPGraphQL. One of those fields — provider_listings — is a JSON string containing an array of senior care facility listings for each city page.

This data was originally imported from Contentful CMS. Contentful is no longer in use — the WordPress database is now the source of truth. However, provider_listings is currently a hidden programmatic meta field with no admin UI, making it impossible to edit content without running direct database commands via WP-CLI on the production ECS container.

Data Structure

Each city-level community post stores provider_listings as post meta (key: provider_listings). The value is a raw JSON string — an array of provider objects:

[
  {
    "slug": "palm-coast-fl-facilities",
    "provider_name": "Ormond Manor",
    "num_rooms": "20",
    "room_string": "Private Rooms",
    "care_type_string": "Memory Care",
    "base_pricing_string": "$4,500/month",
    "description": "Ormond Manor is a specialized memory care community...",
    "resident_review": "Families are delighted...",
    "award": "",
    "reviews_string": "4.8 stars (42 reviews)",
    "phone_number": "386-555-0100",
    "address": "495 Sterthaus Dr., Ormond Beach, FL 32174",
    "url": "https://www.memorycare.com/ormond-manor",
    "url_display": "memorycare.com/ormond-manor"
  }
]

In production, a city page can have up to 19+ providers (~34KB of JSON). This data is consumed by the headless Next.js frontend via WPGraphQL as communityMeta { providerListings }.

Critical: $wpdb->update() Required — NOT update_post_meta()

WordPress's update_metadata() calls wp_unslash() on the input value, which strips backslashes — corrupting the \" escape sequences inside the JSON string. Always use $wpdb->update() directly:

global $wpdb;
$wpdb->update(
    $wpdb->postmeta,
    [ 'meta_value' => $json_string ],
    [ 'post_id' => $post_id, 'meta_key' => 'provider_listings' ],
    [ '%s' ],
    [ '%d', '%s' ]
);

Goal

Add a WordPress admin meta box to the community post type editor so content editors can view and update provider_listings without CLI access.


Acceptance Criteria

AC1 — Meta box registration

  • A meta box titled "Provider Listings" is added to the community post type edit screen
  • The meta box only appears on city-level posts (where listing_type meta equals 'city'). State-level posts do not show it.
  • The meta box is positioned in the normal context at high priority

AC2 — View/Edit UI

  • The meta box renders a <textarea> pre-populated with the current JSON value, formatted with JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
  • A byte count is displayed below the textarea (e.g. "34,938 bytes")
  • A provider count indicator shows how many providers are in the array (e.g. "19 providers")
  • The textarea has a minimum height of 20 rows

AC3 — Save behavior

  • On save_post, the submitted textarea value is validated as JSON via json_decode()
  • If invalid JSON: save is skipped and an admin notice is shown: "Provider Listings not saved: invalid JSON."
  • If valid JSON: the value is saved using $wpdb->update() directly to preserve \" escape sequences
  • A nonce field is included and verified on save

AC4 — Client-side JSON validation

  • A JS snippet validates the textarea on blur/change
  • Invalid JSON: textarea border turns red + inline error message appears
  • Error clears when the content becomes valid JSON again

AC5 — Security

  • Capability check: only users with edit_posts can save
  • Nonce generated with wp_create_nonce() and verified with wp_verify_nonce()
  • The raw JSON string is stored without HTML sanitization (sanitization would corrupt JSON escapes)

AC6 — Individual provider fields (v2, optional)

  • An expandable section renders each provider as individual labeled fields (provider_name, description, resident_review, address, etc.)
  • Improves UX for non-technical editors who should not need to touch raw JSON
  • Nice-to-have — can be a follow-up PR

Implementation Notes

  • This change belongs in the community-listings plugin, not in any theme
  • The plugin already registers the community CPT and the WPGraphQL field providerListings
  • Meta key in wp_postmeta: provider_listings (snake_case) → WPGraphQL field: providerListings (camelCase)
  • Applies to all sites using this plugin (memorycare.com, etc.)
  • Table prefix for memorycare.com: wp_memorycare_

Related

  • Jira WEB-1072 — Ormond Manor content update on memorycare.com required manual DB editing via WP-CLI + AWS ECS SSH because no admin UI exists for this field
  • Downstream consumer: memory-nextjs headless frontend — community city pages at /communities/[state]/[city]

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions