Skip to content
You are reading the docs for ShopClass 7.1.0.dev1, still in development. Read the stable docs.

Customiser

Appearance → Customise is built into core. It appears for any theme that declares the customiser theme support. Sections are ordinary widgets at a widget location; look values are ordinary settings page fields. The theme adds no table and no admin code.

osc_add_theme_support('customiser', array(
'locations' => array(
'home' => array(
'label' => 'Home page',
'types' => array('mytheme.hero', 'mytheme.products', 'core.rich_text'),
'preview' => osc_base_url(),
),
),
'settings' => array('page' => 'mytheme', 'fields' => array('brand', 'scheme', 'font', 'logo')),
'swatches' => array('brand' => array('raspberry' => '#c81f5e', 'ocean' => '#0f6fbf')),
'live' => array('brand' => 'data-brand', 'scheme' => 'data-theme', 'font' => 'data-font'),
));

Core checks the declaration with osc_customiser_spec() and drops anything invalid:

  • locations must be widget locations the theme declares, types registered widget types, and preview a URL on this site.
  • settings.fields must be fields of the named settings page. Secret, custom, category-mode, rich text and translated fields are not allowed; core drops them.
  • swatches are printed as a background colour only when they match #rrggbb. A select field with swatches is drawn as colour circles.
  • live names fields the preview applies without a reload, as attributes on <html>.

If nothing usable is left, osc_customiser_spec() returns null and the screen does not appear.

A section can be hidden without being deleted. Core stores this as the reserved config key _enabled. osc_render_widget() prints nothing when it is false; a missing key or true renders. Your render callback never sees a hidden row. Editing a widget keeps the key.

Changes are saved as a draft in the key-value store, one per administrator and theme, for seven days. Publish writes each drafted location in one transaction, saves the look values, clears the page cache and runs customiser_published( $theme ).

The preview is the location’s preview URL plus ?osc_preview=1. It is honoured only for a signed-in administrator. Otherwise the parameter does nothing. With no draft for the active theme, the preview shows the published page. In a preview:

  • osc_show_widgets( $location ) renders the drafted rows. An unsaved row has id 0.
  • osc_customiser_preview() is true.
  • osc_customiser_draft_value( $page, $name ) returns the drafted look value, or null when it is not drafted. Read it before the saved setting.
  • The response is not cacheable and sends X-Robots-Tag: noindex, and osc_head() prints <meta name="osc-customiser" content="ADMIN ORIGIN">.
  • osc_customiser_preview_rows( $location ) gives the drafted rows if you render a location yourself.

Never print a drafted value into CSS or a URL without checking it. Put owner choices on <html> as attributes and check them against a list in PHP.

Print these only while osc_customiser_preview() is true:

<section data-sec="<?php echo osc_esc_html($rowKey); ?>">
<h2 data-field="title"><?php echo osc_esc_html($config['title']); ?></h2>
</section>

data-sec is the row id, or the draft key of an unsaved row. data-field is the config field name. Also print the allowed values of every live attribute on <html>, for example data-osc-allow-data-brand="raspberry ocean".

The admin screen and the preview talk with postMessage. Both sides check event.origin === location.origin. Every message has type: 'osc-customiser'.

Core ships no preview-side script, only the admin side. The theme must write the preview side: a script it enqueues only while osc_customiser_preview() is true. That script must:

  • listen for message events of type osc-customiser from its own origin;
  • apply set, text and select as in the table below;
  • send { ready: true } to window.parent once loaded;
  • on a click inside a [data-sec] element, stop the click and send { picked: <its data-sec> }, so links do not navigate while editing;
  • send { missed: true } when a text message finds no matching node.

Aisle’s js/aisle-preview.js is a working example.

Admin to preview:

Message Effect
{ set: { attr: 'data-brand', value: 'ocean' } } Sets the attribute, only if the value is in the matching data-osc-allow-* list. A value of '' or 'system' removes the attribute instead.
{ text: { section, field, value } } Sets textContent of [data-sec=section] [data-field=field].
{ select: section, name } Outlines the section. The optional name is the label shown on the outline; send the section’s display name.

Preview to admin:

Message When
{ ready: true } The page has loaded.
{ picked: section } The owner clicked a section.
{ missed: true } A text message had no node to change. The admin reloads the preview after saving.

Any other change (order, hide, add, remove, images) saves the draft and reloads the preview.

osc_customiser_spec(), osc_customiser_preview(), osc_customiser_draft_value(), osc_customiser_preview_rows(), osc_customiser_preview_url(). See helpers.

customiser_published ( $theme ) runs after a publish. Use it to rebuild anything derived from the sections or look.