Title: Pure Demo Importer
Author: Themepure
Published: <strong>September 23, 2026</strong>
Last modified: September 23, 2026

---

Search plugins

![](https://ps.w.org/pure-demo-importer/assets/banner-772x250.png?rev=3709459)

![](https://ps.w.org/pure-demo-importer/assets/icon-256x256.png?rev=3709459)

# Pure Demo Importer

 By [Themepure](https://profiles.wordpress.org/themepure/)

[Download](https://downloads.wordpress.org/plugin/pure-demo-importer.1.0.12.zip)

 * [Details](https://wordpress.org/plugins/pure-demo-importer/#description)
 * [Reviews](https://wordpress.org/plugins/pure-demo-importer/#reviews)
 *  [Installation](https://wordpress.org/plugins/pure-demo-importer/#installation)
 * [Development](https://wordpress.org/plugins/pure-demo-importer/#developers)

 [Support](https://wordpress.org/support/plugin/pure-demo-importer/)

## Description

Pure Demo Importer adds a **Demo Importer** admin page to any WordPress theme that
registers itself with the plugin. It can:

 * Export the current site’s content (content.xml WXR), widgets (widgets.wie) and
   customizer settings (customizer.dat) into a single ZIP.
 * Import any of the above on a fresh WordPress install, with auto-install and activation
   of required plugins from the WordPress.org repo.
 * Show a live system-requirements check before import with copy-paste fix instructions.
 * Optionally reset the site (delete posts/widgets/customizer mods) before importing.

The plugin itself ships **no demo config**. Themes and core plugins register their
demos by hooking the `pure_demo_importer_register` action — see “Registering demos”
below.

### Registering demos

The plugin fires a `pure_demo_importer_register` action on `after_setup_theme:20`.
Hook into it and call `ThemePure_Demo_Importer::register()` with your config:

    ```
    add_action( 'pure_demo_importer_register', function () {
        ThemePure_Demo_Importer::register( array(
            'theme_name' => 'My Theme',
            'theme_slug' => 'my-theme',          // drives the filter name
            'menu_slug'  => 'my-theme-demo',
            'menu_title' => 'My Theme Importer',
            'menu_icon'  => 'dashicons-download',
            'demos'      => require __DIR__ . '/demo-config/demo.php',
        ) );
    } );
    ```

### Filter hook: extending the demos array

After the initial `::register()` call, the demos array is **filterable per theme**
via the filter `{theme_slug}_demo_importer_demos`. The filter name is built from
your `theme_slug` — if you registered with `'theme_slug' => 'consora'`, the filter
is `consora_demo_importer_demos`. The filter passes two args: `( array $demos, array
$config )`.

Use this filter to **add, remove, or modify** demos after the initial register call—
without touching the theme’s static `demo.php` config.

#### When the filter fires

Inside `ThemePure_Demo_Importer::get_demos()` — every time the admin page (or any
AJAX step) reads the demos array. Filter callbacks can be added anywhere that runs**
before** the user opens the Demo Importer page (e.g. `functions.php`, plugin bootstrap,`
after_setup_theme`).

#### Where to add the filter callback

 Location
 When to use

 Theme’s `demo-config/demo-import.php`
 Most demos are static — this is just a small
extension.

 A separate file (`theme/demo-config/extra-demos.php`)
 Keep dynamic / conditional
demos isolated from the static config.

 A site-specific plugin (`mu-plugins/site-demos.php`)
 The extra demos are project-
specific and shouldn’t ship with the theme.

#### Example 1: Append an extra demo (most common)

From any plugin or `functions.php`, after the theme’s register call:

    ```
    add_filter( 'consora_demo_importer_demos', function ( $demos, $config ) {
        $demos[] = array(
            'slug'               => 'home-bonus',
            'name'               => 'Bonus Homepage',
            'page_preview_image' => 'https://wp.themepure.net/consora/sample-data/preview-image/thumb-bonus.jpg',
            'preview_live_url'   => 'https://wp.themepure.net/consora/home-bonus',
            'required_plugins'   => array(
                array( 'slug' => 'elementor', 'name' => 'Elementor' ),
            ),
            'content_xml_url'    => 'https://wp.themepure.net/consora/sample-data/sample-data/bonus.xml',
            'home_page'          => 'Home Bonus',
            'source_url'         => 'https://wp.themepure.net/consora/',
        );
        return $demos;
    }, 10, 2 );
    ```

#### Example 2: Remove a demo conditionally

Hide demos that depend on a plugin which isn’t active (e.g. WooCommerce-only demos):

    ```
    add_filter( 'consora_demo_importer_demos', function ( $demos, $config ) {
        // Hide the WooCommerce demo when Woo isn't active.
        if ( ! class_exists( 'WooCommerce' ) ) {
            $demos = array_values( array_filter( $demos, function ( $d ) {
                return $d['slug'] !== 'home-05';
            } ) );
        }
        return $demos;
    }, 10, 2 );
    ```

#### Example 3: Mutate every demo at once

Switch the CDN host across every demo, or force-add a plugin to every demo’s required-
plugins list:

    ```
    add_filter( 'consora_demo_importer_demos', function ( $demos, $config ) {
        foreach ( $demos as &$demo ) {
            // Force-add a plugin to every demo.
            $demo['required_plugins'][] = array(
                'slug' => 'wordpress-seo',
                'name' => 'Yoast SEO',
            );
            // Swap the CDN host.
            if ( isset( $demo['content_xml_url'] ) ) {
                $demo['content_xml_url'] = str_replace(
                    'wp.themepure.net',
                    'cdn.themepure.net',
                    $demo['content_xml_url']
                );
            }
        }
        unset( $demo );
        return $demos;
    }, 10, 2 );
    ```

#### Replacing the demos array entirely

You can also call `::register()` with `'demos' => array()` and populate the entire
list via the filter — useful if your demos are generated dynamically (e.g. fetched
from a remote API at boot).

### Other available hooks

#### Lifecycle actions

 * `themepure_di_before_import` — fires before reset / import begins. Args: `( $
   demo )`.
 * `themepure_di_after_import` — fires once all imports complete. Args: `( $demo)`.
   Use for WooCommerce page IDs, custom kits, theme options.
 * `themepure_di_before_step` / `themepure_di_after_step` — per-step hooks. Args:`(
   $step, $demo, $arg )`.

#### Reset

 * `themepure_di_reset_preserve_plugins` — filter the list of plugin basenames to
   keep active across a reset. Default: `[ 'pure-demo-importer/pure-demo-importer.
   php' ]`. Append any plugin that registers demos so the demos remain visible after
   the post-reset page reload.

#### Downloader

 * `themepure_di_download_timeout` — remote download timeout in seconds (default
   300).
 * `themepure_di_http_bypass_args` — tweak browser-style headers used when a host
   403s the default WP user-agent.

#### WXR import

 * `themepure_di_wp_import_fetch_attachments` — skip media sideload during WXR import(
   default true).
 * `themepure_di_wp_import_allow_create_users` — allow WP_Import to create users
   from XML authors (default false).

#### Logger

 * `themepure_di_logging_enabled` — disable site-wide import logging:
 * add_filter( ‘themepure_di_logging_enabled’, ‘__return_false’ );

Logs are written to a private `wp-content/uploads/themepure-di-logs-{random}/import.
log` and rotated at ~2 MB. The random suffix is generated once per site so the log
cannot be fetched by guessing its URL.

### External services

Pure Demo Importer ships with zero demos configured out of the box (`demos => array()`)
and makes no external connections on its own. It is a drop-in engine: a WordPress
theme or plugin integrates with it by registering one or more “demo” entries (each
with its own name and URLs) via the `pure_demo_importer_register` action. Once a
theme has registered real demos, the following outbound requests can happen — always
as the direct result of the site administrator clicking a button in the Pure Demo
Importer admin screen, never automatically or in the background:

 * **Demo content download.** When the admin clicks “Import” for a specific demo,
   the plugin downloads that demo’s content files (WXR XML, widgets `.wie`/JSON,
   Customizer `.dat`, an optional Elementor kit `.zip`) from the URL(s) the integrating
   theme configured for that demo. This is a plain HTTP GET of a static file; no
   site data, personal data, or tracking parameters are sent — only a standard request
   for the file at that URL. The host is whatever demo server the theme author operates
   or points to; this plugin does not operate its own demo-content service and cannot
   provide a single Terms of Service / Privacy Policy link, since the host is defined
   entirely by the integrating theme, not by this plugin.
 * **Plugin installation.** When the admin clicks “Install” next to a recommended
   plugin, the plugin is resolved and downloaded from the official WordPress.org
   Plugin Directory using WordPress core’s own `plugins_api()` and `Plugin_Upgrader`—
   the same mechanism used by Add New Plugin in wp-admin. See https://wordpress.
   org/about/privacy/ for WordPress.org’s own privacy policy. No plugin code is 
   ever installed from any other source.
 * **WooCommerce variation-swatch image (optional).** If the integrating theme’s
   demo config includes a `variation_settings_url`, the plugin fetches that single
   URL after import to configure WooCommerce product-variation swatches. Same nature
   as the demo content download above: a static file fetch to a URL the theme author
   configured, triggered only by the admin’s own import action.

No analytics, telemetry, or usage tracking of any kind is performed by this plugin.

## Installation

 1. Upload the `pure-demo-importer` folder to `/wp-content/plugins/`.
 2. Activate “Pure Demo Importer” from the Plugins screen.
 3. From your theme’s `functions.php` (or a core plugin’s bootstrap), hook the `pure_demo_importer_register`
    action and call `ThemePure_Demo_Importer::register()` with your config. See the“
    Registering demos” section for a full example.

## FAQ

### The plugin says “No demos configured” — what now?

Either your theme/core plugin hasn’t called `ThemePure_Demo_Importer::register()`,
or it called it with an empty `demos` array. See the “Registering demos” section
above for the correct hook + call pattern. If you want to populate demos via filter
only (no `register()` array), use the `{theme_slug}_demo_importer_demos` filter 
described above.

### Will reset delete my Pure Demo Importer plugin?

No. The reset always preserves Pure Demo Importer itself — deactivating the plugin
running the reset would terminate the request mid-flight. Use the `themepure_di_reset_preserve_plugins`
filter to preserve additional plugins.

### Does this work without the WordPress Importer plugin installed?

Yes. Pure Demo Importer vendors the full WordPress Importer plugin internally at`
vendor/wordpress-importer/`. There is no runtime dependency on the upstream plugin
being installed.

### Where is the import log?

wp-content/uploads/themepure-di-logs-{random}/import.log — rotated at ~2 MB. Disable
with `add_filter( 'themepure_di_logging_enabled', '__return_false' );`.

## Reviews

There are no reviews for this plugin.

## Contributors & Developers

“Pure Demo Importer” is open source software. The following people have contributed
to this plugin.

Contributors

 *   [ Themepure ](https://profiles.wordpress.org/themepure/)

[Translate “Pure Demo Importer” into your language.](https://translate.wordpress.org/projects/wp-plugins/pure-demo-importer)

### Interested in development?

[Browse the code](https://plugins.trac.wordpress.org/browser/pure-demo-importer/),
check out the [SVN repository](https://plugins.svn.wordpress.org/pure-demo-importer/),
or subscribe to the [development log](https://plugins.trac.wordpress.org/log/pure-demo-importer/)
by [RSS](https://plugins.trac.wordpress.org/log/pure-demo-importer/?limit=100&mode=stop_on_copy&format=rss).

## Changelog

#### 1.0.12

 * Security: removed the SQL-pack import/export subsystem. It executed statements
   from a downloaded `.sql` dump verbatim, which is an arbitrary-SQL path and has
   no place in a WordPress.org release. Normal WXR / widgets / customizer importing
   is unchanged.
 * Security: removed the bundled regex-based SVG “sanitizer” and stopped allowlisting`
   svg` / `svgz` or forcing a MIME type from the filename. SVG in demo content is
   now refused during import unless a proven sanitizer library (enshrined/svg-sanitize,
   as bundled by Safe SVG) is installed.
 * Security: all XML now loads through a single hardened path — `LIBXML_NONET`, 
   no entity substitution, DOCTYPE refused, document size capped, libxml error state
   always restored. The deprecated `libxml` entity-loader toggle is gone.
 * Security: `WP_Import::import()` can no longer read arbitrary fields from the 
   original request. `$_POST` is replaced with a minimal allowlisted array for the
   duration of the call and restored in a `finally` block, along with filters, deferred
   counting, cache invalidation and output buffers.
 * Security: every outbound download now goes through one safe downloader — http/
   https only, loopback / private / link-local / unique-local blocked for IPv4 and
   IPv6, every redirect hop revalidated, redirect and response-size caps, partial
   files cleaned up on failure.
 * Security: the selective-reset payload is now limited by byte size and nesting
   depth before decoding.
 * Security: private upload directories are now named with a per-site random suffix
   and protected with Apache 2.4-compatible rules, a `web.config` and an `index.
   php` stub, instead of Apache-2.2-only `.htaccess` syntax that could 500 a directory
   on Apache 2.4.
 * Removed all `set_time_limit()` and `memory_limit` `ini_set()` calls. Imports 
   rely on bounded AJAX chunks; memory headroom is requested once via WordPress’s
   own `wp_raise_memory_limit( 'admin' )`.
 * Fixed a PHP 8.4 implicit-nullable parameter deprecation.
 * Removed the `tools/build-sql-pack.php` developer script from the distributed 
   package.

#### 1.0.11

 * Recommended-plugin installs are now resolved exclusively from the WordPress.org
   Plugin Directory — no other source (remote or local) is ever used.

#### 1.0.0

 * Initial release.
 * AJAX queue-driven import with progress UI.
 * Single-shot and chunked WXR import paths.
 * Vendored WordPress Importer (no runtime dependency on the upstream plugin).
 * Widgets, customizer, metafields, Elementor kit, URL replacement steps.
 * Reset site with self-preservation + filterable preserve-list.
 * Auto page reload after successful reset.

## Meta

 *  Version **1.0.12**
 *  Last updated **2 days ago**
 *  Active installations **30+**
 *  WordPress version ** 5.6 or higher **
 *  Tested up to **7.1.2**
 *  PHP version ** 7.4 or higher **
 * Tags
 * [demo](https://wordpress.org/plugins/tags/demo/)[demo importer](https://wordpress.org/plugins/tags/demo-importer/)
   [one click import](https://wordpress.org/plugins/tags/one-click-import/)[starter sites](https://wordpress.org/plugins/tags/starter-sites/)
   [WXR](https://wordpress.org/plugins/tags/wxr/)
 *  [Advanced View](https://wordpress.org/plugins/pure-demo-importer/advanced/)

## Ratings

No reviews have been submitted yet.

[Your review](https://wordpress.org/support/plugin/pure-demo-importer/reviews/#new-post)

[See all reviews](https://wordpress.org/support/plugin/pure-demo-importer/reviews/)

## Contributors

 *   [ Themepure ](https://profiles.wordpress.org/themepure/)

## Support

Got something to say? Need help?

 [View support forum](https://wordpress.org/support/plugin/pure-demo-importer/)