Maintainer overview of authoring CPTs, HTML conversion, and delivery integrations.
Content model
| Object | Role |
|---|---|
prc_email_campaign | Mailchimp-linked campaigns |
prc_email_txn | Mandrill bulk + dynamic system emails |
prc_newsletter_list | Product taxonomy; optional audience/segment, From, accent, campaign pattern, and archive preview campaign term meta |
prc_newsletter_list also attaches to block_module for archive block areas. List archives still query prc_email_campaign only (Rewrites::restrict_list_archive_to_campaigns). On a list archive, Latest_Campaign_Query uses prc_newsletter_list_preview_campaign_id when that campaign is still previewable and assigned to the list. Empty or invalid meta keeps the latest published campaign.
_post_visibility and workflow-status bind to campaigns (and workflow-status to transactional emails) so Hide on Publications Archive and pipeline stage persist through the block editor REST API. Dynamic system emails are keyed by post_name (slug); legacy prc_email_system_email_key migrates via wp prc email migrate-system-keys.
Admin DataViews lists
Campaigns and Transactional lists register on the shared prc-wp-admin-dataview shell. They keep shell Edit, View, and Trash. Transactional lists hide View because that post type is not public. Domain code adds fields, header actions, and query mapping. It does not replace the shell action array.
HTML pipeline
Block content converts through the deterministic Email_Block_Converter stack (includes/email/), with style/preset resolution, merge tags, dark-mode registry, and cached HTML. Templates live under templates/ (default, transactional, newsletter shell).
Audience builders
Mandrill recipient lists are built as async Firebase jobs. Email Builder owns the hub UI and a generic job runner (Audience_Job). Quiz Builder and Datasets register builders on prc_email_builder_register_audience_builders (init priority 5).
WordPress POSTs to an enqueue Cloud Function (HTTP 202). Firebase writes audienceBuildJobs/{jobId}, scans Auth or Realtime Database, and stores a Cloud Storage artifact. WordPress polls the ledger, then imports emails into prc_email_audience_* options. REST job views never include recipient addresses.
| Builder | Form | Option prefix | Job id |
|---|---|---|---|
| Email domain | domain-query | prc_email_audience_auth_domain_ | ad_ |
| Quiz group creators | source-entity (quiz) | prc_email_audience_quiz_group_owners_{id}_{verification} | qz_ |
| Dataset downloaders | source-entity (dataset) | prc_email_audience_dataset_{id}_{verification} | ds_ |
| CSV upload | csv-upload | prc_email_audience_csv_ | cs_ |
CSV upload is local. WordPress parses the file and writes the audience option immediately. It does not call Firebase.
Sync HTTP functions (buildDatasetAudience, buildQuizGroupOwnersAudience, buildEmailDomainAudience) stay deployed for rollback. Admin, inspector, and quiz/dataset CLIs use the enqueue path. The auth-domain CLI still calls buildEmailDomainAudience.
Delivery
| Path | Integration |
|---|---|
| Campaign publish / send / draft recovery | Mailchimp (includes/mailchimp/) |
| Bulk transactional send | Mandrill (includes/mandrill/). prc_email_mandrill_message filters the payload before messages/send. |
| Dynamic system send | System_Email_Sender + form action / REST; durable recipient log table |
Engagement reports for Mailchimp campaigns sync on a daily Action Scheduler schedule and on demand into the Engagement sidebar. Mandrill bulk and system emails stamp reserved message metadata (email_post_id) after prc_email_mandrill_message, then store webhook events in {prefix}prc_email_mandrill_events (salted recipient hash, no raw email). The ledger is the full-audience source. CRM still stores per-contact history and only gap-fills the report envelope when that ledger has no engagement, labelled crm_contacts.
Slack follow-ups
When Campaign_Status_Sync caches Mailchimp status as sent, two Slack replies go to the campaign's publish announcement thread (notify_in_post_thread):
- An immediate "campaign has been sent" notice (
Campaign_Status_Sync). - A first-day Mailchimp stats snapshot 24 hours later (
First_Day_Campaign_Stats). The job pulls a fresh aggregate report (opens, clicks, bounces, top links), writes it toReport_Store, and posts in the same thread.
Both handlers are production-gated and idempotent per Mailchimp campaign ID. Unlink clears both Slack markers. Ops can run wp prc email first-day-stats <post_id> without waiting 24 hours.
Source: includes/mailchimp/class-campaign-status-sync.php, includes/mailchimp/class-first-day-campaign-stats.php.
Automations
Dynamic system emails store prc_email_automation_config (send window + steps). Enrollment hooks prc_email_builder_system_email_sent. A recurring dispatcher (prc_email_automation_process_due, every 15 minutes) processes due enrollments from {prefix}prc_email_automation_enrollments.
Retired Newsletter Glue path
NGL Pro and the automated NGL → prc_email_* migration engine were removed. Archive posts with _migrated_from_ngl_id remain read-only via Migration::is_migrated().
Key files
| Path | Role |
|---|---|
includes/class-post-type.php | CPT registration |
includes/class-rest-api.php | Editor / library / send / audience-job routes |
includes/audiences/ | Registry, generic job runner, auth-domain adapter |
includes/email/ | Converter and templates |
includes/mailchimp/ | Campaign linkage, status sync, first-day Slack stats |
includes/automations/ | Drip enrollment and scheduler |
includes/ai/ | Subject, preview, links abilities |
src/sidebar/ | Campaign and transactional sidebars |
src/admin-dataview/ | Shell provider: fields, action compose, SlotFills |
src/library/ | Shared library fields, types, and header UI |