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:
- Reads the source builder tree (
pages/<slug>.json). - Stores it as the page's
page-builderoption and activates the builder (fw_set_db_post_option( …, array( 'json' => …, 'builder_active' => true ) )). - Regenerates
post_contentexactly 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 - 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).
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 var | Effect |
|---|---|
UPW_FORCE=1 | Overwrite 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
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
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.