Create WordPress Demo Site with Multisite

Create WordPress Demo Site with Multisite - Featured image

Creating demo sites with WordPress Multisite is a quick way to let your customer to try your theme or plugin in a true-to-life environment.

Setting up a demo site for WordPress plugins or themes can be as simple as installing WordPress and giving everyone a demo account. This has several drawbacks:

  • anyone can overwrite what the other testers did and will lead to a messy demo site
  • you can limit what the demo account can do, crippling the effectiveness of the demo
  • you have to reset / clean the demo site every few weeks

Alternatively, you could use a plugin like Ninja Demo, however we decided not to approach the subject this way. Not due to the price (~ $200), but simply because we figured out this can be a really fun project to play with.

With WordPress MultiSite you can let them modify, delete or upload on your demo without interacting with other demo accounts by simply allowing your users to create a brand new demo site.

In this tutorial we’ll go through:

To see the finished product, just check out Profile Builder’s demo site. You’ll get a new WordPress demo site where you can make any changes as an administrator. You can also log out and test Profile Builder’s features as a subscriber or as a non-logged in user.

Creating a demo subdomain to the main domain

Since this is fairly dependent on the hosting you currently have, I’m not going into much detail here. Basically, for cpanel users it should be something like this:

  • Open cPanel and create a new sub-domain
  • Give it the name ‘demo’
  • Create a database and a database user
  • Update the DNS to demo.yoursitename.com

Installing WordPress Multisite

The Multisite Network feature comes with each WordPress install. All you need to do is install and setup WordPress like you normally would and then enable Multisite.

To enable the Network Setup menu item, you must first define MultiSite in the wp-config.php file.

1
2
/* MultiSite */
define( 'WP_ALLOW_MULTISITE', true );

After that, you will be able to create a Network out of your WordPress Install. Check out the Codex Network Documentation for more info regarding this.

Setting up available plugins and themes

For our Profile Builder Demo, besides the plugin, we also installed all available add-ons (since users won’t be able to install their own plugins even though they are administrators in the demo sites), as well as other plugins that Profile Builder is compatible with like WooCommerce or Members.

Except Profile Builder, these plugins won’t be activated by default, however the WordPress demo site users will be able to choose what features they want to use in a true-to-life environment.

multisite_plugins

Replace the WP signup with a “Create Demo” button

The default WordPress MultiSite sign-up functionality is a bit too “safe” for our demo site. Basically, you have to:

  • Enter a username and email
  • Enter the site title you want and choose if you want it to be indexable by search engines
  • Get a notification email with the confirmation link
  • Finally be able to login into the newly created WordPress Demo Site

While the above makes perfect sense in the general context of WordPress Multisite, it’s just not good enough for a WordPress demo site. So we want to replace all of that with a simple “Create Demo” button.

Replace the WP signup with a “Create Demo” button

The “Create Demo Sub-Sites” Plugin

If you don’t want to go through all the hassle of creating a custom plugin and copy pasting the code, then simply download the plugin by clicking the button bellow and install it site-wide.

Download Create Demo Sub-Sites

You can then add the [cds_register] shortcode in your main network site.

Brakedown of the code.
The plugin contains two main parts:

  • The register shortcode where we create the new user and subsite
  • Automatically login in the user in the new website
  • The math-CAPTCHA library and a few small functions to glue it all together

Code for the “Create Demo Site” shortcode

The main functions we’re relying here are the wpmu_create_user and wpmu_create_blog, which are specific WordPress MultiSite functions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// Add a Simple Math CAPTCHA
include_once('MathCaptcha.php');
 
// Need sessions for the Math CAPTCHA
add_action('init', 'cds_start_session');
function cds_start_session(){
	session_start();
}
 
// Create the shortcode that will output the Create Demo button.
add_shortcode('cds_register', 'cds_register');
function cds_register(){
 
	$cpa = new MathCaptcha();
	if(( 'POST' == $_SERVER['REQUEST_METHOD'] ) && ( $_POST['cbs_action'] == 'cbs_demo' )){
		$captcha_val = $_REQUEST['captcha'];
 
		if( ! $cpa->validate($captcha_val) ){
			return '<p>Incorect Captcha. Please try again. You will be redirected in 3 seconds <meta http-equiv="refresh" content="3"></p>';
		}
 
		// MultiSite domain
		$main_site = DOMAIN_CURRENT_SITE;
 
		do {
			$rand = 'demo' . cbs_rand_string();
			if( is_subdomain_install() ) {
				$newdomain = "{$rand}.$main_site";
				$path = '/';
			} else {
				$newdomain = $main_site;
				$path = PATH_CURRENT_SITE . "{$rand}/";
			}
		} while( username_exists($rand) && domain_exists($newdomain, $path) );
 
		// Create the new user
		$username = $rand ;
		$email = $rand . '@mailinator.com';
		$password =  'demo';
		$user_id = wpmu_create_user( $username, $password, $email );
 
		if( is_wp_error( $user_id ) ){
			return '<p>The new demo account could not be created. Please try again. (You will be redirected in 5 seconds) <meta http-equiv="refresh" content="5"></p>';
		}
 
		// Create the new sub-site
		$title = 'Demo';
		$blog_id = wpmu_create_blog( $newdomain, $path, $title, $user_id , array( 'public' => 1 ) );
 
		if( is_wp_error( $blog_id ) ){
			return '<p>The new demo account could not be created. Please try again. (You will be redirected in 5 seconds) <meta http-equiv="refresh" content="5"></p>';
		}
 
		$location =  get_site_url( $blog_id );
		$nonce = wp_create_nonce( 'cds_autologin-'.$user_id.'-'.(int)( time() / 60 ) );
		$location .= "/?cds_autologin=true&uid=$user_id&_wpnonce=$nonce";
 
        return "<script> window.location.replace('$location'); </script>";
 
	} else {
		$cpa->reset_captcha();
		ob_start();
		?>
		<form enctype="multipart/form-data" method="post" id="cbs-register" action="">
			<p><?php
			echo 'Solve this simple Math: ' . $cpa->get_captcha_text() . " = ?";
			?>
			<input name="captcha" type="text" value="" />
			</p>
			<p class="form-submit">
				<input name="cbs_register" type="submit" id="cbs_register" class="submit button" value="Create Demo" />
				<input name="cbs_action" type="hidden" id="cbs_action" value="cbs_demo" />
			</p><!-- .form-submit -->
		</form>
		<?php
		$output = ob_get_contents();
		ob_end_clean();
 
		return $output;
	}
}

What we’re doing here is:

  • Check to see if the button has been clicked ($_POST[‘cbs_action’] exists and it’s cbs_demo)
  • Randomly generate the username and the domain name by verifying again and again until we find a pair that doesn’t exist in our install
  • Create the username. The email of the demo admin account, is a mailinator.com account so it’s easy to test emails sent by the website
  • Create the WordPress demo site
  • If everything is successful (the CAPTCHA is correct and we could create both the user and the domain), redirect the user to the newly created demo site. Here we’re constructing a nonce that’s based on the new user ID and contains the current unix time in minutes.

Automatically login in users

We’re doing this by using the wp_set_auth_cookie that takes as a parameter the user ID we’re sending in the $_GET request as a parameter.

Now, we don’t want to let everyone log in just by adding ?$uid=2134 in the url, so we’re relying on the nonce setup after a successful user and demo site creation.

1
2
3
4
if ( ! ( wp_verify_nonce( $nonce , 'cds_autologin-'.$uid.'-'.(int)( time() / 60 ) ) || wp_verify_nonce( $nonce , 'cds_autologin-'.$uid.'-'.(int)( time() / 60 ) - 1 ) ) ){
	wp_redirect( $current_page_url );
	exit;
}

Basically, if there’s been more then 1 minute since the initial nonce creation in the shortcode, we’re not logging in the user. Since nonces are unique, it’s practically impossible for somebody else to randomly login to the site.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
 * Setup Autologin after creation of demo site
 */
 
/* Set action for automatic login after registration */
add_action( 'init', 'cds_autologin_after_creation' );
function cds_autologin_after_creation(){
	if( isset( $_GET['cds_autologin'] ) && isset( $_GET['uid'] ) ){
		$uid = $_GET['uid'];
		$nonce  = $_REQUEST['_wpnonce'];
 
		$arr_params = array( 'cds_autologin', 'uid', '_wpnonce' );
		$current_page_url = remove_query_arg( $arr_params, cds_curpageurl() );
 
		if ( ! ( wp_verify_nonce( $nonce , 'cds_autologin-'.$uid.'-'.(int)( time() / 60 ) ) || wp_verify_nonce( $nonce , 'cds_autologin-'.$uid.'-'.(int)( time() / 60 ) - 1 ) ) ){
			wp_redirect( $current_page_url );
			exit;
		} else {
			wp_set_auth_cookie( $uid );
			wp_redirect( $current_page_url . 'wp-admin/' ) ;
			exit;
		}
	}
}

A few small functions to glue it all together

The rest of the code consists of the function that generates the random username / domain name and a function that finds the current URL so we can redirect to it in order for the login cookie to take effect and the user to be logged in.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// A simple and almost random string generator
// We don't really need 100% random strings, so SHA1 will do just fine 
function cbs_rand_string($length = 8)
{
	return substr(sha1(rand()), 0, $length);
}
 
 
function cds_curpageurl() {
	$pageURL = 'http';
 
	if ((isset($_SERVER["HTTPS"])) && ($_SERVER["HTTPS"] == "on"))
		$pageURL .= "s";
 
	$pageURL .= "://";
 
	if ($_SERVER["SERVER_PORT"] != "80")
		$pageURL .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"];
 
	else
		$pageURL .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
 
	if ( function_exists('apply_filters') ) apply_filters('wppb_curpageurl', $pageURL);
 
	return $pageURL;
}

That’s it for the code!

There’s actually less then 160 lines of code in total, including the comments and WordPress plugin headers. It’s a really simple and straight forward mini-plugin that now allows us to :

  • Create a new user and WordPress demo site
  • Automatically logs in the new user and redirects him/her to the dashboard

Download Create Demo Sub-Sites

Further improving our WordPress site demo setup

We now have a very nice setup where users can create WordPress demo sites on the fly, without having to enter username / passwords / emails etc. Just fill a simple math CAPTCHA.

Creating a site demo template

UPDATE:We recommend you also have a look at the Multisite Cloner plugin from wordpress.org that is working and actively developed.

WordPress Replicator Plugin Backend

All this is great, but it would be even greater if we would have a site template that will get applied every time a new demo site is created.

For this we’ve used the Replicator plugin by Ron Renneck. It hasn’t been updated in quite some time, however it still works great for what we need like copying the settings, pages, posts, widgets and menus to every new WordPress demo site created.

This is not a free plugin, however we already had a license for it from another project and I knew it was going to work great for our Profile Builder demo site.

Alternatively to this plugin, you can setup a lightweight template in WordPress MultiSite settings by:

  • Setting up the first page content
  • First post content
  • Network activating the plugins you want the user to have access to

It’s not ideal, but depending on the demo site you’re building it could work just fine.

Hide backend menus

WordPress MultiSite Menus

Finally, we can now hide a few menu items like the My Sites admin bar link, the tools or the comment pages in the backend if they won’t be needed for your WordPress demo site.

For this we used the free Menus plugin. It simply adds a list of menus you can show / hide for all sub-sites, as simple as that.

Conclusions

Building a WordPress demo site is not really complicated, however if you want it done right, a few things need to happen like:

  • fast and easy demo site creation (with the help of the custom plugin explained above)
  • new demo sites should be based on a template (using the Replicator plugin)
  • hide admin menus you don’t want / need to be seen (using the Menus plugin)

Also, there are a few things that we haven’t done but plan to integrate later on like:

  • automatic demo site removal after a few days since creation (using a cron most likely)
  • create a dashboard widget where we can display certain information like the current username and it’s mailinator email, the demo password, information on how to use the new WordPress demo site, etc.

All of the above is possible with the use of the Ninja Demo plugin, so if you’re on a tight schedule, honestly go for that.

Otherwise, if you want to mess a bit with WordPress MultiSite and consider this as a chance to learn something new, the DIY route is not that hard.

Update: Delete sites after 24 hours

It’s been almost a year since we introduced demo sites for our plugins and it seems that they have fulfilled their purpose. Right now, they are accessed by tens of users each day.

With such a high rate of usage, it becomes inevitable to address the automatic site removal issue. Otherwise, over time you will end up with an enormous amount of sites that could reach your hosting’s storage limits.

For our solution, we have set up an hourly cron that will call a function to delete subsites older than 24 hours. The cron is registered on plugin activation, and removed on plugin deactivation. (Keep this mind when debugging. If your plugin is already active when you are adding this code, then deactivate the plugin and activate it back.)

The function that actually does the deleting, makes two checks for each subsite in order to decide if it should be removed:

  • If 24 hours have already passed since the site creation.
  • If super admin is not a member of the site.
    This will ensure that the sites created by super admin, such as the main blog or any other subsites you wish to keep alive, are safe from removal.

If these 2 conditions are true, the subsite is deleted.

Let’s see the code!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
 * Schedule event hourly to delete old subsites
 */
register_activation_hook( __FILE__, 'cds_cron_to_delete_subsites_activation' );
function cds_cron_to_delete_subsites_activation() {
	wp_schedule_event( time(), 'hourly', 'cds_check_subsites_hourly_event' );
}
 
register_deactivation_hook ( __FILE__, 'cds_cron_to_delete_subsites_deactivation' );
function cds_cron_to_delete_subsites_deactivation() {
	wp_clear_scheduled_hook( 'cds_check_subsites_hourly_event' );
}
 
 
/**
 * Delete subsites registered for more than 24 hours
 *
 * Only deletes subsites where super-admin is not part of the site's users
 */
function cds_check_subsites_for_deletion() {
 
	require_once( ABSPATH . 'wp-admin/includes/admin.php' );
	// get sites
	global $wpdb;
	$args = array(
	    'network_id' => $wpdb->siteid,
	);
	$sites_array = wp_get_sites( $args );
	$current_time = current_time( 'timestamp' );
 
	foreach ( $sites_array as $site ){
		// get each blog's users and check if super-admin is the creator so that it will not be deleted
		$user_args = array(
			'blog_id'		=> $site[ 'blog_id' ],
			'fields'		=> 'ID',
		);
		$users = get_users( $user_args );
 
		$delete_this_site = true;
		foreach ( $users as $user ){
			if ( is_super_admin( $user ) ) {
				$delete_this_site = false;
				break;
			}
		}
 
		$register_time = strtotime( $site[ 'registered' ], current_time( 'mysql' ) );
		$time_since_registration = $current_time - $register_time;
 
		// if the site has been created for more than 86400 seconds = 24 hours, delete it
		if ( $delete_this_site && ( $time_since_registration > 86400 ) ) {
   			if( ! function_exists( 'wpmu_delete_blog' ) ) return;
			wpmu_delete_blog( $site[ 'blog_id' ], true );
		}
	}
}
add_action( 'cds_check_subsites_hourly_event', 'cds_check_subsites_for_deletion' );

Subscribe to get early access

to new plugins, discounts and brief updates about what's new with Cozmoslabs!

27 thoughts on “Create WordPress Demo Site with Multisite

  1. Is it possible to create a demo user that can see what an administrator sees but all changed they made get deleted from the database after say 60mins?

  2. Hello Cristian, thanks for the article and plug-in. Because Replicator plug-in does not work anymore is there another plug-in which works together with your plug-in?
    Because now on my multisite there is a new demo user/site created with your plug-in but without all the settings from the original site. Hope this is clear to you what I mean. Thanks!

  3. thanks for this article!
    I just don’t understand one thing. In your http://demo.cozmoslabs.com/ you have three products to demo. Do you have a multisite installation for each of those?
    Or you just have one multisite installation (with subdomain ‘demo.’) and then three pages from where to create the demo websites for each product? In the latter case how do you set WordPress to create new sites with different templates for each of these three products?

    I set up everything but I miss this bit, basically how to get the following scenario:
    demo.wp.com
    demo.wp.com/plugin-a/ (will create based on template `plugin-a`)
    demo.wp.com/plugin-a/demo121a
    demo.wp.com/plugin-a/demo122a
    demo.wp.com/plugin-b/ (will create based on template `plugin-a`)
    demo.wp.com/plugin-b/demo123b
    demo.wp.com/plugin-b/demo124b
    demo.wp.com/plugin-c/ (will create based on template `plugin-a`)
    demo.wp.com/plugin-c/demo125c
    demo.wp.com/plugin-c/demo126c

    Thanks again anyway!

      1. Hi Cristian, thanks for replying!
        I see, after I wrote you I started changing everything tweaking your plugin to have each demo domain prefixed the product slug, such as:
        demo.wp.com/plugin-b/ (will create based on template `plugin-b`)
        demo.wp.com/plugin-b–demo123b
        demo.wp.com/plugin-b–demo124b
        demo.wp.com/plugin-c/ (will create based on template `plugin-c`)
        demo.wp.com/plugin-c–demo125c
        demo.wp.com/plugin-c–demo126c

        What do you think? DO you see any benefit of having one multisite (like in this case) or having one multisite for each product (like in your current website)?

        When I realised I needed a ‘network of networks’ I thought to go for the ‘–‘ slug solution to organize the demos, and then in the homepage demo.wp.com I just list each products.

        I would be curious to hear your thoughts!
        best

        1. If you can make it work so that one demo site works for all of your products, great! Honestly I haven’t thought of that. Might have been easier this way to manage then 3 multisites.

          The bottom line is, figure out what works for you best. We’ve added demos to our products in sequence, so it was easy to just duplicate a multisite in another folder then to write code to take care of different demos in the same instance.

  4. Hi,

    Nice article. I am going to try this. Just have one question, this will go on creating multiple sites. Is there a way to auto delete the new demo site and user ? Can not find that info here.

    1. Hello Pallavi,

      I have updated the article with the full code on how to automatically delete demo sites after 24 hours. Check out the appended update about this topic!

  5. Hi,

    Is there a way to direct the user to the home page instead of the admin section? I want to demo a page builder so it makes more sense if you go straight to home page.

    1. Hello Luis,

      In the cds_autologin_after_creation() function, if you remove the ” . ‘wp-admin/’ ” part from the line ” wp_redirect( $current_page_url . ‘wp-admin/’ ) ; “, it should keep you on the homepage after the demo site is created.

  6. Great article. I have used your method. 1 drawback, images. What are your suggestions on replicating media of site? Does replicator do that, or maybe my hosting service WP Engine is pointing to right directory.

  7. Thanks for this very useful article!
    I have one question: why the autologin of the new user is done after a redirection (create user and blog –> redirection –> autologin –> redirection to wp-admin) and not directly after that the new blog has been created (create user and blog, autologin –> redirection to wp-admin).

    And for your information: in order to use a template site to create the demo sites, this plugin can be used: https://wordpress.org/plugins/multisite-cloner/. It is free 😉

    1. I guess it could work just fine, doing the autologin immediately after redirection. It might be an oversight on the logic in the article. I think the autologin functionality is borrowed from our other plugins are doing auto-login in a similar fashion for reasons that are not really needed in this application.

      There’s also the possibility if I remember correclty, but I don’t have time to test this right now, that the autologin exactly after blog creation fails with caching in place, even though it’s unlikely we’ll have any sort of caching enabled on a demo site.

      Unfortunately I don’t think we have time to rethink this, so it will stay as this for the foreseeable future, as it is working.

      As for the Multisite Cloner, it does look like it’s the right plugin for the job and actively supported. So I’ve updated the article with it as a suggested alternative. Thanks a lot!

    2. Now that I think about it, this is the only way it would work as the wp_set_auth_cookie( $user_id ) function needs to setup headers from PHP, and if you do this immediately after blog creation, you’ll get the Headers Already Sent PHP fatal error. So it just won’t work any other way.

  8. Hi,
    Recently, I’ve seen your article and read it carefully. You’ve explained very good. I really liked it. So, keep up the good work.

  9. Hi, thanks for the article but I have a problem with the Replicator plugin, I can’t access the site. I there any other plugin i can use to replace the Replicator plugin?

  10. Hello Cristian,

    Thank you for this great article.

    Just one question, I saw that in your demo site some plugins are not available to site admins like “Create Demo Sub-Sites” plugin, please let me know how you do it?

    I want to do not allow site admins to see “Create Demo Sub-Sites” and “Multisite Cloner” plugins.

    Thanks.

Leave a Reply

Your email address will not be published. Required fields are marked *