This page describes a special setup of DokuWiki called a wiki farm. In such a setup, one single copy of the engine of the wiki (the parent wiki or farm controller or farmer) is used to run several (read many) child instances of a wiki (i.e. individual wikis or animals). The biggest advantage is that only one instance of the wiki engine has to be maintained and upgraded, while keeping many wiki animals with completely separated contents (i.e. configuration and data) use that engine.
The older approach (originally on this page) based on symlinking can now be found on Symlink Farm.
Plugins and Templates are common to all the wikis: You have to install them manually in the farmer wiki. Templates can be chosen from each individual animal wiki. Plugins cannot be enabled or disabled in the animal wiki at the time of writing (8 Nov 2009), although there are plans to make it possible. But it is possible to configure the plugins per animal.
—
Shouldn't there be a link to the farm plugin, which apparently also allows to setup a farm?
The farm directory can theoretically be anywhere in the file system but we recommend the following structure:
/var/www/farmer – the DokuWiki instance/var/www/farm – contains all the animals/animal1/animal2The farmer is the DokuWiki instance which is used to run all the animals.
/var/www/farmer)$> touch /var/www/farmer/lib/plugins/plugin/disabled
Copy the following into /var/www/farmer/inc/preload.php and set the $farm variable to your farm directory.
<?php /** * This overwrites the DOKU_CONF. Each animal gets its own configuration and data directory. * * The farm ($farm) can be any directory and needs to be set. * Animals are direct subdirectories of the farm directory. * There are two different approaches: * * An .htaccess based setup can use any animal directory name: * http://domain.org/<path_to_farm>/subdir/ will need the subdirectory '$farm/subdir/'. * * A virtual host based setup needs animal directory names which have to reflect * the domain name: If an animal resides in http://www.domain.org:8080/mysite/test/, * directories that will match range from '$farm/8080.www.domain.org.mysite.test/' * to a simple '$farm/domain/'. */ $farm = '/var/www/farm'; // SET THIS to your farm directory if(!defined('DOKU_CONF')) define('DOKU_CONF', conf_path($farm)); if(!defined('DOKU_FARM')) define('DOKU_FARM', false); /** * Find the appropriate configuration directory. * * If the .htaccess based setup is used, the configuration directory can be * any subdirectory of the farm directory. * * Otherwise try finding a matching configuration directory by stripping the * website's hostname from left to right and pathname from right to left. The * first configuration file found will be used; the remaining will ignored. * If no configuration file is found, return the default confdir './conf'. * * @author Anika Henke <anika@selfthinker.org> * @author htaccess part based on http://www.dokuwiki.org/doku.php?id=tips:farm2#preload.php1 * @author virtual host part based on conf_path() from Drupal.org's /includes/bootstrap.inc * (see http://cvs.drupal.org/viewvc/drupal/drupal/includes/bootstrap.inc?view=markup) */ function conf_path($farm) { // htaccess based if(isset($_REQUEST['animal'])) { if(!is_dir($farm.'/'.$_REQUEST['animal'])) nice_die("Sorry! This Wiki doesn't exist!"); if(!defined('DOKU_FARM')) define('DOKU_FARM', 'htaccess'); return $farm.'/'.$_REQUEST['animal'].'/conf/'; } // virtual host based $uri = explode('/', $_SERVER['SCRIPT_NAME'] ? $_SERVER['SCRIPT_NAME'] : $_SERVER['SCRIPT_FILENAME']); $server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.'))))); for ($i = count($uri) - 1; $i > 0; $i--) { for ($j = count($server); $j > 0; $j--) { $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i)); if(is_dir("$farm/$dir/conf/")) { if(!defined('DOKU_FARM')) define('DOKU_FARM', 'virtual'); return "$farm/$dir/conf/"; } } } // default conf directory in farm if(is_dir("$farm/default/conf/")) { if(!defined('DOKU_FARM')) define('DOKU_FARM', 'default'); return "$farm/default/conf/"; } // farmer return DOKU_INC.'conf/'; } /* Use default config files and local animal config files */ $config_cascade = array( 'main' => array( 'default' => array(DOKU_INC.'conf/dokuwiki.php'), 'local' => array(DOKU_CONF.'local.php'), 'protected' => array(DOKU_CONF.'local.protected.php'), ), 'acronyms' => array( 'default' => array(DOKU_INC.'conf/acronyms.conf'), 'local' => array(DOKU_CONF.'acronyms.local.conf'), ), 'entities' => array( 'default' => array(DOKU_INC.'conf/entities.conf'), 'local' => array(DOKU_CONF.'entities.local.conf'), ), 'interwiki' => array( 'default' => array(DOKU_INC.'conf/interwiki.conf'), 'local' => array(DOKU_CONF.'interwiki.local.conf'), ), 'license' => array( 'default' => array(DOKU_INC.'conf/license.php'), 'local' => array(DOKU_CONF.'license.local.php'), ), 'mediameta' => array( 'default' => array(DOKU_INC.'conf/mediameta.php'), 'local' => array(DOKU_CONF.'mediameta.local.php'), ), 'mime' => array( 'default' => array(DOKU_INC.'conf/mime.conf'), 'local' => array(DOKU_CONF.'mime.local.conf'), ), 'scheme' => array( 'default' => array(DOKU_INC.'conf/scheme.conf'), 'local' => array(DOKU_CONF.'scheme.local.conf'), ), 'smileys' => array( 'default' => array(DOKU_INC.'conf/smileys.conf'), 'local' => array(DOKU_CONF.'smileys.local.conf'), ), 'wordblock' => array( 'default' => array(DOKU_INC.'conf/wordblock.conf'), 'local' => array(DOKU_CONF.'wordblock.local.conf'), ), );
The file will overwrite DokuWiki constants (path to the configuration settings, etc.) before DokuWiki's initialization phase according to the farm directory you specify. This allows to have a different configuration and data directory for each DokuWiki animal.
This also sets a constant (DOKU_FARM) to use in other code (e.g. plugins). And it adds the possibility to have a default config directory in the farm (in case you have trouble with the farmer config, which can happen with some rewrite settings).
This is just a minimal farm setup. You could use other resources to pull the farm directories from, like databases etc. You could automate the whole farm creation process with registering a farm etc. to your liking. Get creative, and if possible, share your ideas on this page.
Now you have the choice between two possible farm setups:
Virtual Host Based Setup:
yourdomain.org to subdomain.anotherdomain.net or even test.domain.org:8080/foo/bar/ 2)Directory (.htaccess) Based Setup:
yourdomain.org/farm/foo, yourdomain.org/farm/bar, etc.
Is this true? Is there a workaround?For this setup you have to configure the virtual host for each new animal.
NameVirtualHost * <VirtualHost *:8080> # this only works for a specific port ServerName domain.org ServerAlias *.domain.org # this works for all subdomains of domain.org DocumentRoot /var/www/farmer/ # the document root always needs to be the DokuWiki *farmer* directory </VirtualHost> <VirtualHost *> ServerName subdomain.otherdomain.org # this only works for one specific subdomain DocumentRoot /var/www/farmer/ # the document root always needs to be the DokuWiki *farmer* directory </VirtualHost>
On most shared hosts environments you only need to add the server name (your desired domain with or without subdomain) and the document root (always pointing to the farmer directory) through their GUI.
In this scenario we just use a single domain and access the animals via directories, like http://farmer.org/farm/cat and http://farmer.org/farm/dog.
This .htaccess (needed to redirect the accessed animal to the farmer) has to be put into the /var/www/farm directory which holds all the animals.
Remember to enable .htaccess in the Apache Configuration (AllowOverride All)
RewriteEngine On RewriteRule ^/?([^/]+)/(.*) /farmer/$2?animal=$1 [QSA] RewriteRule ^/?([^/]+)$ /farmer/?animal=$1 [QSA] Options +FollowSymLinks
Be careful if you have a redirection loop, your DocumentRoot needs to be /var/www/ (neither /var/www/farmer/ nor /var/www/farm/).
albin [dot] blaschka [at] standortsanalyse [dot] net, 25. November 2009: The buttons “Login” et al. do not work for me: If clicked, I get to the main “farmer” - instance of the wiki, not to the individual animal, called http://server/farm/animal. At the moment, I have no solution for the problem (
forms seem not to get re-written), working on it, it is a showstopper for me, if I am right…looking deeper, I will report back…
benjamin [dot] seclier [at] univ [dash] nancy2 [dot] fr, 7. December 2009 : With the .htaccess script above, I have a redirection loop if I call an animal (which doesn't exist) without the last slash on the URL. For instance, if I call this bad animal :
http://my-wiki.fr/badanimal/
the preload.php file send me “This wiki doesn't exist” but if I call
http://my-wiki.fr/badanimal
I have a redirection loop.
In order to fix it, I had to modify my .htacces (which I wrote in my apache's configuration file instead) like this :
RewriteEngine On RewriteRule index - [L] RewriteRule ^([^/]+)/(.*) ./dokuwiki/master/$2?animal=$1 [QSA,L] RewriteRule ^([^/]+)$ http://my-wiki.fr/$1/ [QSA,L]
Thanks to this hack, when I type an URL without the last slash, it is rewrited with this last slash and I don't have this redirection loop anymore.
As the Abysss Webserver does not feature .htaccess the URL redirection has to be configured within the server itself. The farmer is located in the subdirectory “dokuwiki” and the animals are also in own subdirectories. The following configuration worked for me (only one redirecting rule was necessary):
Virtual Path Regular Expression: /([^/]+)/(.*)
Case Sensitive: On (default)
No conditions
Apply to subrequests too: On (default)
If this rule matches: Perform an internal redirection
Redirect to: /dokuwiki/$2?animal=$1
Append Query String: On (default)
Escape Redirection Location: On (default) — Charly 2009/10/22 10:49
You can
The rules how to name the directory in which your animal stores all its files depends on the setup:
yourdomain.org or only yourdomain, subdomain.anotherdomain.net or even 8080.test.domain.org.foo.bar (for the URL example of test.domain.org:8080/foo/bar/). 3)yourdomain.org/farm/foo would need an animal directory called foo.
You can use the following shell commands to create the /data (together with all its subdirectories: /attic, /cache, /index, /locks, /media, /meta, /pages, /tmp) and /conf (together with the four files: local.php, local.protected.php, acl.auth.php, users.auth.php) directories and set the required permissions4).
$> cd /var/www/farm $> mkdir -p domain.org/{data/{attic,cache,index,locks,media,meta,pages,tmp},conf} $> cd domain.org $> find ./ -type d -exec chmod -v 777 {} \; $> touch conf/{local.php,local.protected.php,acl.auth.php,users.auth.php} $> chmod 666 conf/{local.php,acl.auth.php,users.auth.php}
Note: If you have root access on the server you could also change the owner of the respective directories and configuration files to the user/group under which your webserver is running and use more restrictive (secure) chmod settings. Assuming that the webserver user and group are www-data you could use the following commands inside the animal directory.
find ./ -exec chown www-data:www-data {} \; find ./ -type d -exec chmod 775 {} \; chown www-data:www-data conf/{local.php,acl.auth.php,users.auth.php} chmod 664 conf/{local.php,acl.auth.php,users.auth.php}
This adds a basic configuration (with title, language, superuser and activating ACLs with useacl).
$> cat << EOF >> conf/local.php > <?php > \$conf['title'] = 'Farm Title'; > \$conf['lang'] = 'en'; > \$conf['useacl'] = 1; > \$conf['superuser'] = '@admin';
This adds some configuration to the local.protected.php (savedir and updatecheck) so that the admin of the wiki instance isn't able to overwrite it.
$> cat << EOF >> conf/local.protected.php > <?php > \$conf['savedir'] = DOKU_CONF.'../data'; > \$conf['updatecheck'] = 0;
This sets some basic permissions to “all can read, members of the @admin group can do anything” which can be altered later via the ACL manager.
$> cat << EOF >> conf/acl.auth.php > # <?php exit()?> > * @admin 255 > * @ALL 1
The following adds a user 'admin' with password 'admin', the password and the user credentials should be changed via the update profile form ASAP.
$> echo "# <?php exit()?>" > conf/users.auth.php $> echo 'admin:$1$cce258b2$U9o5nK0z4MhTfB5QlKF23/:admin:admin@mail.org:admin,user' >> conf/users.auth.php
Here's a little bash script which will perform the above steps in one go inside the directory it's executed in (if you're using this with other scripts you have to make sure you cd into the designated directory before executing it). Save it as addanimal, make sure it's in your shell $PATH (i.e. by putting it into /usr/local/bin), and make it executable. It takes the domain/directory of the new animal as argument.
#!/bin/bash if [ $# -lt 1 ]; then echo "Usage: $(basename $0) [farm domain/directory]" exit 1 fi ANIMAL=${PWD}/${1} ANIMAL_TITLE=$1 if [ -d $ANIMAL ]; then echo "ERROR: $ANIMAL exists already!" exit 1 fi echo ">> adding animal $1" echo ">> creating directory structure ..." mkdir -p ${ANIMAL}/{data/{attic,cache,index,locks,media,meta,pages,tmp},conf} find ${ANIMAL}/ -type d -exec chmod 777 {} \; touch ${ANIMAL}/conf/{local.php,local.protected.php,acl.auth.php,users.auth.php} chmod 666 ${ANIMAL}/conf/{local.php,acl.auth.php,users.auth.php} echo ">> creating basic configuration ..." echo "<?php \$conf['title'] = '${ANIMAL_TITLE}'; \$conf['lang'] = 'en'; \$conf['useacl'] = 1; \$conf['superuser'] = '@admin';" > ${ANIMAL}/conf/local.php echo ">> setting fixed configuration ..." echo "<?php \$conf['savedir'] = DOKU_CONF.'../data'; \$conf['updatecheck'] = 0;" > ${ANIMAL}/conf/local.protected.php echo ">> setting basic permissions ..." echo "# <?php exit()?> * @admin 255 * @ALL 1" > ${ANIMAL}/conf/acl.auth.php echo ">> adding admin user ..." echo '# <?php exit()?> admin:$1$cce258b2$U9o5nK0z4MhTfB5QlKF23/:admin:admin@mail.org:admin,user' > ${ANIMAL}/conf/users.auth.php echo ">> IMPORTANT: Don't forget to change your admin username + password!" echo ">> finished!" echo ">> bye!" exit 0 # vim:ts=4:sw=4:noet:enc=utf-8:
Usage Example:
$> cd /var/www/farm $> addanimal cat.domain.org
$> cd /var/www/farm $> addanimal subdir
You can use nice URLs on a farm using the virtual host based setup. Just add the .htaccess file into your /var/www/farmer directory as if this were a singleton copy, and then each animal may set their URLs how they chose without further intervention from the farmer.
You cannot use URL rewriting in the directory (.htaccess) based setup or the Symlink Farm.
Is that true? Or is there a way?
If you want to get this with releases before rc2009-02-06, you could patch “init.php” like this
--- init.php +++ init.php.patched @@ -9,6 +9,9 @@ return ((float)$usec+(float)$sec)-((float)$start); } define('DOKU_START_TIME', delta_time()); + + // if available load a preload config file + @include(fullpath(dirname(__FILE__)).'/preload.php'); // define the include path if(!defined('DOKU_INC')) define('DOKU_INC',fullpath(dirname(__FILE__).'/../').'/');
Or you could use PHP's auto_prepend_file option to include the above preload.php and overwrite the configuration settings. In this case you just need to remember that in contrast to “DokuWiki preload” not even DOKU_INC is defined when your auto_prepend_file is executed. — Christian Marg
Since I set “DOKU_CONF” depending on “$_SERVER['Servername'] in my preload.php, the indexing program 'bin/indexer.php' didn't work anymore. At first I opted for a patch to “indexer.php” - it should be possible to also put that code in “preload.php” to avoid this patch. All it does is to search $argv for ”–path <path to one dokufarm-animal>”, remove the additional parameters from $argv and set DOKU_CONF accordingly:
--- indexer.php.dist 2008-09-23 13:43:00.000000000 +0200 +++ indexer.php 2008-09-23 16:54:40.000000000 +0200 @@ -2,6 +2,24 @@ <?php if ('cli' != php_sapi_name()) die(); +for($i = 1; $i < sizeof($argv); ++$i) +{ + if ($argv[$i] == "--path") { + if (!isset($argv[$i+1])) { + die("--path: no path given.\n"); + } else { + $path=$argv[$i+1]; + unset($argv[$i],$argv[$i+1]); + $argv=array_values($argv); + if (($path= realpath($path."/conf/").'/')===false) { + die("--path: path doesn't exist.\n"); + } + if(!defined('DOKU_CONF')) define('DOKU_CONF', $path ); + last; + } + } +} + if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../').'/'); require_once(DOKU_INC.'inc/init.php'); require_once(DOKU_INC.'inc/common.php');
To create an index for every farm animal, you just have to iterate over all farm animals and call the indexer script for each one.
— Christian Marg 2008/09/23 17:13
If you want to have both DokuWiki farmer and animals each located in an own subdirectory of the server you have to use a different preload.php. basically you have to set additional constants in order to have anything working. of course you still have to set URL rewriting in your webserver (see above).
<?php
/**
* This overwrites the DOKU_CONF. Each farm gets its own configuration and data directory.
* Animal is not a subdirectory with the farmer, but at same level directory level.
* Access farmer like this: http://domain.org/farmer/doku.php
* Access animal like this: http://domain.org/animal/doku.php
* Farmer on server e.g. /var/www/farmer
* Animal on server e.g. /var/www/animal
*/
// the home directory for all animals and the farmer located in subdirectories
$farmdir = DOKU_INC.'../';
// don't do anything if the animal doesn't exist
if(isset($_REQUEST['animal'])) {
if(!is_dir($farmdir . $_REQUEST['animal'])) {
nice_die("Sorry! This Wiki doesn't exist!");
}
if(!defined('DOKU_CONF')) {
define('DOKU_CONF', $farmdir . $_REQUEST['animal'] . '/conf/');
}
// correct paths according to animal and make nice looking in HTML source
if(!defined('DOKU_URL')) define('DOKU_URL',preg_replace('/(.+)\/([^\/]+)\//','$1/'.$_REQUEST['animal'].'/',getBaseURL(true)));
if(!defined('DOKU_REL')) define('DOKU_REL',preg_replace('/([^\/]+)\/\/([^\/]+)\/(.+)\//','/$3/',DOKU_URL));
} else {
// don't do anything on the farmer instance
return;
}
You can have subdirectories with the virtual host based setup (i.e. without a .htaccess file in the farm). After adding the animal directory to the farm (should be something like “domain.org.subdir”5)), you only need to create a symlink in the farmer directory like this:
ln -s . subdir
If you need to make subdirectories within a virtual directory setup work with userewrite=1, you need to add the following to the farmer's .htaccess for every subdirectory, right before RewriteRule ^$ doku.php [L]:
RewriteRule ^$ subdir/doku.php [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} subdir
RewriteRule ^(.*)$ subdir/doku.php?id=$1 [QSA,L]
RewriteRule ^subdir/index.php$ subdir/doku.php
I am no rewrite rules expert, can someone please check and correct this (if necessary)?
Be careful, this is not fully tested and is already known to cause trouble in some setups.