Multiple sites

You can run multiple independent sites from one play-cms installation. Each site has its own frontend and shares the common backend with all sites (however, each site also can have site-specific backend entries).

Required version: play-cms:2.5+

 

Configuration

Sites are configured in application.conf: key cms.sites[{site1}{site2}{..}]

name

The display name of a site.

key The key used in the database - don't change after a site was initialized.
hosts

A comma-separated lists of hosts that are assigned to this site. They're matched against the request's "Host:" header. To allow all hosts, use the wildcard "*". 

Note: The port is part of the host header if it's not 80 - meaning you can run multiple sites on different ports of the same host.

 langs The language config to support multilanguage feature.
 langs Path to site specific logo(usually it appears in /uploads directory)

 

The default site config looks like this:

cms.sites = [
  {
    name: default
    key:""
    hosts: ["*"]
  }
]

  

In this example, two sites are configured, with no default:

cms.sites = [
  {
    name: site1.ch
    key: site1
    hosts: [site1.ch, www.site1.ch]
    langs: [...]
    logoPath: ""
  }
  
  {
    name: site2.ch
    key: site2
    hosts: [site2.ch, www.site2.ch]
    langs: [...]
    logoPath: ""
  }  
]

 

 From code, access the site configuration like this:

// Get the sites configuration
CMS.getSites();

// Get the current site
CMS.getSites().current();

// Get all configured sites
CMS.getSites().all();

// Resolve the site for a given host
CMS.getSites().forHost("site1.ch");

// Get the TypeSafe config object for the current site 
// (This will let you use arbitrary site-specific config values)
CMS.getSites().current().config;

(Note: These are all immutable config values) 

 

Adding a new site

First, add the configuration entry for your new site. After that, access the backend of your new site (newsite.com/admin).

In the "Navigation" screen, you should see a red note which asks for initializing the new site. Click on it. This will add the required structural nodes and some demo content to your new site.

 

Internals

The AbstractBlock (table cms_blocks) has a new attribute site which contains the site key the block belongs to. Blocks that belong to all sites, such as the backend, have site set to the wildcard (*). 

The site is inherited from the parent block, except for the site root nodes (those nodes that connect directly to the single root parent) - these site roots define the site starting point.

When retrieving blocks, the cms tries first to find a specialized block (matching the current site), then a default (wildcard) block.

If assigning a key, then a block's site+key must be unique.

 

Handling non-web requests

The cms requires always a valid site. To write tests or other code running from localhost, you should ensure the Host header is set to localhot and have a configuration that can handle localhost. In any case, CMS.getSites().current() needs to be set correctly when interacting with cms content!

 [TODO: Allow to set / mock the current site from code]

 

Site-specific actions

With a multi-site configuration there are often cases, where different logic has to be implemented for the different sites. The cms provides the SiteSpecific utility class to make this easier and with less boilerplate code.

Rendering different layouts per site:

return SiteSpecific.forSite(CMS.getSites().current(), views.html.notFound.render())
        .onSiteKey(MySites.SITE_A.key, () -> views.html.showSiteA.render())
        .onSiteKey(MySites.SITE_B.key, () -> views.html.showSiteB.render())
        .get();

The above code returns the template notFound by default and returns showSiteA if the current site is SITE_A and showSiteB if the current site is SITE_B

 

SiteSpecific uses generics to determine the result type of the suppliers. This way you can either return a Result, a rendered Template or something entirely different.

Configuration configuration = ...
return SiteSpecific.forSite(CMS.getSites().current(), Results::notFound)
        .onSiteKey(MySites.SITE_A.key, () -> {
            boolean useSomeFeature = configuration.getBoolean("config.someFeature.siteA.enabled");
            return ok(views.html.showPage.render(useSomeFeature));
        })
        .onSiteKey(MySites.SITE_B.key, () -> {
            boolean useSomeFeature = configuration.getBoolean("config.someFeature.siteB.enabled");
            return ok(views.html.showPage.render(useSomeFeature));
        })
        .get();

This code renders the same template for SITE_A and SITE_B but first reads the configuration about whether a certain feature is enabled or not. By default, it returns the Play Framework's notFound result.

Migration from earlier versions

1. Add the default site configuration to application.conf (see above)

2. Update the existing db:

CREATE UNIQUE INDEX cms_blocks_KEY_SITE_uindex ON `play-cms-demo`.cms_blocks (`KEY`, SITE);

UPDATE cms_blocks SET SITE=''
UPDATE cms_blocks SET SITE='*' WHERE `KEY`='_root'
UPDATE cms_blocks SET SITE='*' WHERE `KEY`='_backend'