Skip to main content

Importers & demo system

The Unyson+ marketing site and the demo network are generated by a small build → import pipeline: Node scripts (build-*.mjs) emit page-builder JSON, and PHP importers (import.php, the numbered demos/*.php scripts) write that JSON into WordPress and activate the builder.

This page documents how those importers work and, most importantly, the manual-edit guard that keeps a re-import from clobbering pages you hand-edited in the builder. It's developer / maintainer reference, not something a site builder needs day to day.

The page importer

wordpress/import.php rebuilds the marketing pages from source JSON (pages/<slug>.json). For each page it:

  1. Reads the source builder tree (pages/<slug>.json).
  2. Stores it as the page's page-builder option and activates the builder (fw_set_db_post_option( …, array( 'json' => …, 'builder_active' => true ) )).
  3. Regenerates post_content exactly the way the plugin does, by converting the tree to shortcodes and rendering them:
    $sc = $ot->json_to_shortcodes( $tree_json ); // builder JSON → shortcode string
    $html = do_shortcode( $sc ); // → rendered HTML
  4. Fingerprints what it just wrote, so a later manual edit is detectable.

This is the same json_to_shortcodes()do_shortcode() path the front end uses, covered in How the Page Builder works.

The manual-edit guard

You hand-edit live pages in the builder between imports. The guard makes sure a re-run never silently overwrites those edits. On every import the importer stores an _upw_import_hash post meta: the md5() of the builder JSON it wrote. On a later run, before overwriting a page, it compares the page's current builder JSON against that fingerprint:

// --- guard: skip hand-edited / unfingerprinted pages unless forced ---
if ( $post_id && ! $FORCE ) {
$cur = $pb_json( $post_id ); // current builder JSON
$stored = (string) get_post_meta( $post_id, '_upw_import_hash', true );
$edited = ( $cur !== '' && md5( $cur ) !== $stored ); // diverged from last import
if ( $edited ) {
// SKIP: manually edited since last import — preserved.
continue;
}
}

A page is skipped when:

  • its current builder JSON differs from the stored fingerprint (you edited it since the last import), or
  • it has no fingerprint yet (a pre-guard page, preserved to be safe).
Overwriting is opt-in, and only after folding edits back to source

Set UPW_FORCE=1 to overwrite anyway. But only do so after you've pulled the user's manual edits back into the source pages/<slug>.json (DB → source first), or the edits are lost. The guard exists precisely so a routine re-import is safe.

Environment flags

Env varEffect
UPW_FORCE=1Overwrite even hand-edited pages (bypass the guard).
UPW_ONLY=<slug>Import a single page/demo and leave all others untouched. e.g. UPW_ONLY=countdown re-imports just that demo.
# Re-import everything (skips hand-edited pages):
php import.php

# Re-import just one page:
UPW_ONLY=features php import.php

# Force-overwrite (only after folding manual edits back into source):
UPW_FORCE=1 php import.php
Give every new importer the same guard

This guard isn't only in import.php. The demos importers carry it too: demos/7-shortcode-demos.php (the per-shortcode demo pages) and demos/6-demos-home.php (the demos home). When you add any new importer, fingerprint _upw_import_hash and honor UPW_FORCE the same way, so re-runs never clobber manual work.

The demos network

The demo network at localhost/demos/ is a WordPress multisite of demo subsites (Coastal, Felix Mercer, Volta, an accordion shop, …), each its own child theme. It's assembled by a numbered pipeline so a fresh build is reproducible:

demos/1-install.php install WordPress
demos/2-network.php enable multisite
demos/3-activate.php activate plugin + parent theme
demos/3b-extensions.php activate the needed extensions
demos/4*-*-subsite.php create each demo subsite
demos/5*-*-content.php import each subsite's content
demos/6-demos-home.php build the demos home grid
demos/7-shortcode-demos.php the 59 per-shortcode demo pages
demos/8-menu.php menus
demos/9-animation-demos.php animation showcase

Each subsite's content is generated by a matching build-*.mjs (e.g. build-coastal.mjs) that emits builder JSON, then imported by the numbered PHP script. The per-shortcode demos are generated by build-shortcode-demos.mjs and imported by 7-shortcode-demos.php, which also injects each element's real generated markup into a Code Block "The markup" panel (see the Code Block toggle).

The demos home grid

The landing grid at localhost/demos/ is built by demos/build-demos-home.mjs (a demos array of cards) and imported by demos/6-demos-home.php:

// build-demos-home.mjs → const demos = [ … ]
{ name: 'WooCommerce', cat: 'Shop',
desc: 'A storefront built on the WooCommerce integration.',
live: DEMO + 'shop/',
grad: 'linear-gradient(135deg,rgba(0,178,149,.4),rgba(47,116,230,.35))' },
// omit `live` to render a "Coming soon" card instead of a "View demo" button.
node build-demos-home.mjs # regenerate demos-home.json
php 6-demos-home.php # import it to the demos main site
Every demo gets a card

Whenever you add a demo (a subsite or a standalone page), add its card to the demos array and rebuild the home, so the demo is discoverable and never orphaned behind a direct URL. A card with a live URL renders a "View demo" button; without one, a "Coming soon" card.