Mutualisation: sharing a single SPIP kernel amongst several sites

Procedure for sharing the SPIP kernel amongst several sites.

Sharing SPIP files between several sites, which is known as mutualisation, saves a lot of disk space, as well as making it possible to update SPIP easily on all the sites by updating only the core.

SPIP allows for its files to be shared . As new versions are released, the procedures for doing so are becoming clearer and more robust .

The concept...

The folders required for the SPIP kernel to run (ecrire, prive, squelettes-dist ), and those tracking the activities of a site (config, IMG, tmp, local) have been clearly identified and separated from one another. It is this separation which makes it possible to have several autonomous SPIP sites all running on the same SPIP kernel.

This autonomy relies on four directories for each site, into which SPIP will write the data and results from activities on the site. We distinguish between temporary and permanent data on the first hand, and between data which is or is not accessible via HTTP on the second hand. The two directories for non-HTTP accessible data will be protected by a .htaccess file automatically installed by SPIP (the server administrators can also locate these directories outside of the directory structure that is serviced by HTTP).

These four directories are quite distinct and are named using the following PHP constants:

define('_NOM_TEMPORAIRES_INACCESSIBLES', "tmp/");
define('_NOM_TEMPORAIRES_ACCESSIBLES', "local/");
define('_NOM_PERMANENTS_INACCESSIBLES', "config/");
define('_NOM_PERMANENTS_ACCESSIBLES', "IMG/");

In an ordinary installation of SPIP, these directories are created at the root of the site using the default directory names shown above.
The pooling of the SPIP sources relies on the mapping of these four specific directories for each site as determined by their URLs.

Initialising resource pooling

This mapping is carried out by the spip_initialisation function. It accepts four arguments indicating the location of these four fundamental directories, and uses them to construct the constants that are required for SPIP to operate properly.
For a standard installation, these four arguments are simply those four constants illustrated above. The spip_initialisation function is called just after the mes_options.php file has been loaded, and will silently refuse to execute if it has already been called.
As a consequence, if that file applies this function on the four arguments deduced from the site URL, then we will be using the SPIP sources from the same location as the site URL.
We could also define the constants using for SPIP’s operations in this file, with these definitions thereby taking priority over those that spip_initialisation would try to define.

The code below, when inserted into the config/mes_options.php file, takes the domain name and performs resource pooling for the four directories tmp, local, config and IMG located in the sites/domain_name/ folder.
Previously, the definition of a few constants made it possible to centralise the logging files for all of the sites into a single directory, log, and not into several sites/domain_name/tmp/ directories for each of them. This centralisation is certainly not compulsory but does prove to be useful; it can also be applied for the help and back-up/restore directories.

<?php
$rep = 'sites/';
$site = $_SERVER['HTTP_HOST'];
$path = _DIR_RACINE . $rep . $site . '/';

// path search order
define('_SPIP_PATH',
	$path . ':' .
	_DIR_RACINE .':' . 
	_DIR_RACINE .'squelettes-dist/:' .
	_DIR_RACINE .'prive/:' .
	_DIR_RESTREINT);

// add the squelettes template folder
if (is_dir($path . 'squelettes'))
	$GLOBALS['dossier_squelettes'] = $rep . $site . '/squelettes';

// example of logs at the root for all sites
define('_FILE_LOG_SUFFIX', '_' . $site . '.log');
define('_DIR_LOG',  _DIR_RACINE . 'log/');

// prefixes for cookies and tables:
$cookie_prefix = str_replace('.', '_', $site); 
$table_prefix = 'spip';

// execute the config/mes_option.php file for the mutualised site
if (is_readable($f = $path . _NOM_PERMANENTS_INACCESSIBLES . _NOM_CONFIG . '.php')) 
	include($f); 

// start the site
spip_initialisation(
	($path . _NOM_PERMANENTS_INACCESSIBLES),
	($path . _NOM_PERMANENTS_ACCESSIBLES),
	($path . _NOM_TEMPORAIRES_INACCESSIBLES),
	($path . _NOM_TEMPORAIRES_ACCESSIBLES)
);
?>

Resource pooling for domains or sub-domains

To pool the resources for several domains or sub-domains, you must redirect them all to the physical directory that contains SPIP. For example, in order to resource pool http://example.org/, http://example.net/ and http://subdomain.example.net/, they should all point to the same common directory, such as /var/www/spip/.

So this folder contains SPIP as well as a config/mes_options.php file for activating the resource pooling. Taking the code example above, we must then create the sites and log folders as well as sites/example.org, sites/example.net and sites/subdomain.example.net, and within each of these also create the config, local, IMG and tmp directories with write access. And that’s it! By then accessing any one of these sites, SPIP should proceed to offer you its normal installation routine.

If you want to be able to activate different types of URLs, such as the "url arborescentes" (hierarchical URLs) or "url propres" (personal URLs), then it will be necessary to create an HTTP access file, a template of which is included in the standard distribution with the name of htaccess.txt: just rename it as .htaccess to power it up. It will apply to all of the pooled sites, but it is possible to have differentiated access to the various sites by carefully configuring the server (see more on this below). In both cases, this will all only work if your server accepts to execute the directives included in this file, especially those relating to URL rewriting (which is not necessarily available for all ISP hosts - be sure to check with your host before attempting to use such rewriting rules).

Sharing the directories of a domain

Every (virtual) folder in a domain, when its URL is correctly processed, can also become a pooled SPIP site. For this to happen, it is necessary that the folder’s URL be redirected transparently to the SPIP root. This is the role played by the modified .htaccess file. In this way, http://example.com/first_site/ and http://example.com/second_site/ can both be pooled SPIP sites (and http://example.com/ at the same time too).

First off, the htaccess.txt must be renamed as .htaccess, and then be modified so that the virtual directories point to the SPIP root. To do this, add into the "Réglages personnalisés" section (custom settings) either the generic code which handles all of the virtual directories together (but where http://example.com/first_site without its closing "/" will return an error), or specific code indicating the directory names explicitly (no errors if there is no closing "/"):

// generic code
RewriteCond %{REQUEST_URI} !^/(config|ecrire|IMG|prive|plugins|plugins-dist|sites|squelettes-dist|squelettes|tmp|lib|local|mutualisation)/(.*)
RewriteRule ^[^/]+/(.*) /$1 [QSA,L]

// OR specific code
RewriteRule ^(first_site|second_site)$ /$1/ [R,L]
RewriteRule ^(first_site|second_site)/(.*) /$2 [QSA,L]

Secondly, a resource pooling must be created by analysing the the URL passed in the config/mes_options.php file. Here is an example for having the pooled sites at sites/example.com, sites/first_site and sites/second_site:

<?php
if ( 
	(
		preg_match(',^/([\.a-zA-Z0-9_-]+)/,', $_SERVER['REQUEST_URI'], $r)
		AND !is_dir(_DIR_RACINE . $r[1])
	)
) {
	$site = $r[1];
} else {
	$site = $_SERVER['HTTP_HOST'];
}

$rep = 'sites/';
$path = _DIR_RACINE . $rep . $site . '/';

// path name search order
define('_SPIP_PATH',
	$path . ':' .
	_DIR_RACINE .':' . 
	_DIR_RACINE .'squelettes-dist/:' .


	_DIR_RACINE .'prive/:' .
	_DIR_RESTREINT);

// add the squelettes template folder
if (is_dir($path . 'squelettes'))
	$GLOBALS['dossier_squelettes'] = $rep . $site . '/squelettes';

// prefixes for cookies tables:
$cookie_prefix = str_replace('.', '_', $site); 
$table_prefix = 'spip';

// execute the config/mes_option.php file for the pooled site
if (is_readable($f = $path . _NOM_PERMANENTS_INACCESSIBLES . _NOM_CONFIG . '.php')) 
	include($f); 

// start the site
spip_initialisation(
	($path . _NOM_PERMANENTS_INACCESSIBLES),
	($path . _NOM_PERMANENTS_ACCESSIBLES),
	($path . _NOM_TEMPORAIRES_INACCESSIBLES),
	($path . _NOM_TEMPORAIRES_ACCESSIBLES)
);
?>

Table prefixing

The examples shown above require as many database instances as there are resource pooled sites. However, it is possible to install all of the sites on a single database by prefixing the names of their tables with a unique prefix for each site.

The example below creates a prefix of 8 characters: one prefix with the first 4 letters of the site, followed by another 4 different "random" characters.

$table_prefix = substr(str_replace('.', '_', $site),0,4) . substr(md5($site),0,4);

The config/mes_options.php file

Any modification made to the config/mes_options.php file for the SPIP kernel will affect the options for all of the sites being hosted.

For example, inserting this code into that file:
$type_urls = ’propres’ ;

will make all of the sites use that type of URL by default... But each of these sites is free to change that in its own "private" /sites/repertoire_du_site/config/mes_options.php file.

Configuring Apache for the domains and sub-domains

The tasks for resource pooling on the server side remain quite simple in respect of managing the sub-domains and domains. All you need is to create a redirection between the domain name and the physical directory where SPIP is stored.

Shown below is a (minimal) configuration example for a server called ’example.tld’ using some sub-domains. The configuration file is to be created in /etc/apache2/sites_availables/example.tld, which must then be activated

sudo a2ensite example.tld
sudo /etc/init.d/apache2 reload

SPIP is installed in this example in ’/var/www/spip/’.

<VirtualHost *>
	ServerName example.tld
	ServerAdmin webmaster@localhost
	ServerAlias *.example.tld
	
	DocumentRoot /var/www/spip/
	<Directory /var/www/spip/>
		Options Indexes FollowSymLinks MultiViews
		AllowOverride All
		Order allow,deny
		allow from all
	</Directory>

</VirtualHost>

If you additionally want to have differentiated access files, then use the AccessFileName directive inside the VirtualHost directive, so that the access file for site S is named, for example, as .htaccess-S.

Note about back-ups and restores

By default, each site copies its back-up files into the
/sites/first_site/tmp/dump (or /sites/first_site/tmp/upload/login directories for back-ups for a restricted admin).

The restores are made from the same folder. The path name for image files is also correctly taken into account.

Author Mark Published : Updated : 20/04/23

Translations : català, English, français, Nederlands