1da0ae2c0SAndreas Gohr<?php 2da0ae2c0SAndreas Gohr 3a646d519SAndreas Gohr/** 4a646d519SAndreas Gohr * Core Manager for the Farm functionality 5a646d519SAndreas Gohr * 6a646d519SAndreas Gohr * This class is initialized before any other DokuWiki code runs. Therefore it is 7a646d519SAndreas Gohr * completely selfcontained and does not use any of DokuWiki's utility functions. 8a646d519SAndreas Gohr * 9b96c66ccSAndreas Gohr * It's registered as a global $FARMCORE variable but you should not interact with 10b96c66ccSAndreas Gohr * it directly. Instead use the Farmer plugin's helper component. 11a646d519SAndreas Gohr */ 12da0ae2c0SAndreas Gohrclass DokuWikiFarmCore { 13da0ae2c0SAndreas Gohr /** 14da0ae2c0SAndreas Gohr * @var array The default config - changed by loadConfig 15da0ae2c0SAndreas Gohr */ 16da0ae2c0SAndreas Gohr protected $config = array( 17a646d519SAndreas Gohr 'base' => array( 18a646d519SAndreas Gohr 'farmdir' => '', 19a646d519SAndreas Gohr 'farmhost' => '' 20a646d519SAndreas Gohr ), 21da0ae2c0SAndreas Gohr 'notfound' => array( 22da0ae2c0SAndreas Gohr 'show' => 'farmer', 23da0ae2c0SAndreas Gohr 'url' => '' 24da0ae2c0SAndreas Gohr ), 25da0ae2c0SAndreas Gohr 'inherit' => array( 26da0ae2c0SAndreas Gohr 'main' => 1, 27da0ae2c0SAndreas Gohr 'acronyms' => 1, 28da0ae2c0SAndreas Gohr 'entities' => 1, 29da0ae2c0SAndreas Gohr 'interwiki' => 1, 30da0ae2c0SAndreas Gohr 'license' => 1, 31da0ae2c0SAndreas Gohr 'mime' => 1, 32da0ae2c0SAndreas Gohr 'scheme' => 1, 33da0ae2c0SAndreas Gohr 'smileys' => 1, 34da0ae2c0SAndreas Gohr 'wordblock' => 1, 35da0ae2c0SAndreas Gohr 'userstyle' => 0, 36da0ae2c0SAndreas Gohr 'userscript' => 0 37da0ae2c0SAndreas Gohr ) 38da0ae2c0SAndreas Gohr ); 39da0ae2c0SAndreas Gohr 40a646d519SAndreas Gohr /** @var string|false The current animal, false for farmer */ 41a646d519SAndreas Gohr protected $animal = false; 42a646d519SAndreas Gohr /** @var bool true if an animal was requested but was not found */ 43a646d519SAndreas Gohr protected $notfound = false; 44a646d519SAndreas Gohr /** @var bool true if the current animal was requested by host */ 45a646d519SAndreas Gohr protected $hostbased = false; 46a646d519SAndreas Gohr 47da0ae2c0SAndreas Gohr /** 48da0ae2c0SAndreas Gohr * DokuWikiFarmCore constructor. 49da0ae2c0SAndreas Gohr * 50a646d519SAndreas Gohr * This initializes the whole farm by loading the configuration and setting 51a646d519SAndreas Gohr * DOKU_CONF depending on the requested animal 52da0ae2c0SAndreas Gohr */ 53da0ae2c0SAndreas Gohr public function __construct() { 54da0ae2c0SAndreas Gohr $this->loadConfig(); 55a646d519SAndreas Gohr if($this->config['base']['farmdir'] === '') return; // farm setup not complete 5649f2871cSAndreas Gohr define('DOKU_FARMDIR', $this->config['base']['farmdir']); 57a646d519SAndreas Gohr 58a646d519SAndreas Gohr // animal? 59a646d519SAndreas Gohr $this->detectAnimal(); 60a646d519SAndreas Gohr 61a646d519SAndreas Gohr // setup defines 62a646d519SAndreas Gohr define('DOKU_FARM_ANIMAL', $this->animal); 63a646d519SAndreas Gohr if($this->animal) { 64a646d519SAndreas Gohr define('DOKU_CONF', DOKU_FARMDIR . '/' . $this->animal . '/conf/'); 65a646d519SAndreas Gohr } else { 66a646d519SAndreas Gohr define('DOKU_CONF', DOKU_INC . '/conf/'); 67a646d519SAndreas Gohr } 68a646d519SAndreas Gohr 69a646d519SAndreas Gohr $this->setupCascade(); 70a646d519SAndreas Gohr $this->adjustCascade(); 71da0ae2c0SAndreas Gohr } 72da0ae2c0SAndreas Gohr 73da0ae2c0SAndreas Gohr /** 74da0ae2c0SAndreas Gohr * @return array the current farm configuration 75da0ae2c0SAndreas Gohr */ 76da0ae2c0SAndreas Gohr public function getConfig() { 77da0ae2c0SAndreas Gohr return $this->config; 78da0ae2c0SAndreas Gohr } 79da0ae2c0SAndreas Gohr 80da0ae2c0SAndreas Gohr /** 81a646d519SAndreas Gohr * @return false|string 82a646d519SAndreas Gohr */ 83a646d519SAndreas Gohr public function getAnimal() { 84a646d519SAndreas Gohr return $this->animal; 85a646d519SAndreas Gohr } 86a646d519SAndreas Gohr 87a646d519SAndreas Gohr /** 88a646d519SAndreas Gohr * @return boolean 89a646d519SAndreas Gohr */ 90a646d519SAndreas Gohr public function isHostbased() { 91a646d519SAndreas Gohr return $this->hostbased; 92a646d519SAndreas Gohr } 93a646d519SAndreas Gohr 94a646d519SAndreas Gohr /** 95a646d519SAndreas Gohr * @return boolean 96a646d519SAndreas Gohr */ 97a646d519SAndreas Gohr public function wasNotfound() { 98a646d519SAndreas Gohr return $this->notfound; 99a646d519SAndreas Gohr } 100a646d519SAndreas Gohr 101a646d519SAndreas Gohr /** 102b330074aSAndreas Gohr * @return string 103b330074aSAndreas Gohr */ 104b330074aSAndreas Gohr public function getAnimalDataDir() { 105b330074aSAndreas Gohr return DOKU_FARMDIR . '/' . $this->getAnimal() . '/data/'; 106b330074aSAndreas Gohr } 107b330074aSAndreas Gohr 108b330074aSAndreas Gohr /** 109b330074aSAndreas Gohr * @return string 110b330074aSAndreas Gohr */ 111b330074aSAndreas Gohr public function getAnimalBaseDir() { 112b330074aSAndreas Gohr if($this->isHostbased()) return ''; 113b330074aSAndreas Gohr return getBaseURL() . '!' . $this->getAnimal(); 114b330074aSAndreas Gohr } 115b330074aSAndreas Gohr 116b330074aSAndreas Gohr /** 117a646d519SAndreas Gohr * Detect the current animal 118a646d519SAndreas Gohr * 119a646d519SAndreas Gohr * Sets internal members $animal, $notfound and $hostbased 120a646d519SAndreas Gohr * 121a646d519SAndreas Gohr * This borrows form DokuWiki's inc/farm.php but does not support a default conf dir 122a646d519SAndreas Gohr */ 123a646d519SAndreas Gohr protected function detectAnimal() { 124a646d519SAndreas Gohr $farmdir = $this->config['base']['farmdir']; 125a646d519SAndreas Gohr $farmhost = $this->config['base']['farmhost']; 126a646d519SAndreas Gohr 127a646d519SAndreas Gohr // check if animal was set via parameter (rewrite or CLI) 128a646d519SAndreas Gohr $animal = ''; 129a646d519SAndreas Gohr if(isset($_REQUEST['animal'])) $animal = $_REQUEST['animal']; 130a646d519SAndreas Gohr if('cli' == php_sapi_name() && isset($_SERVER['animal'])) $animal = $_SERVER['animal']; 131a646d519SAndreas Gohr if($animal) { 132a646d519SAndreas Gohr // check that $animal is a string and just a directory name and not a path 133a646d519SAndreas Gohr if(!is_string($animal) || strpbrk($animal, '\\/') !== false) { 134a646d519SAndreas Gohr $this->notfound = true; 135a646d519SAndreas Gohr return; 136a646d519SAndreas Gohr }; 137a646d519SAndreas Gohr $animal = strtolower($animal); 138a646d519SAndreas Gohr 139a646d519SAndreas Gohr // check if animal exists 140a646d519SAndreas Gohr if(is_dir("$farmdir/$animal/conf")) { 141a646d519SAndreas Gohr $this->animal = $animal; 142a646d519SAndreas Gohr return; 143a646d519SAndreas Gohr } else { 144a646d519SAndreas Gohr $this->notfound = true; 145a646d519SAndreas Gohr return; 146a646d519SAndreas Gohr } 147a646d519SAndreas Gohr } 148a646d519SAndreas Gohr 149a646d519SAndreas Gohr // is this the farmer? 150a646d519SAndreas Gohr if(strtolower($_SERVER['HTTP_HOST']) == $farmhost) { 151a646d519SAndreas Gohr return; 152a646d519SAndreas Gohr } 153a646d519SAndreas Gohr 154*85becf1bSAndreas Gohr // still here? check for host based 155a646d519SAndreas Gohr $this->hostbased = true; 156*85becf1bSAndreas Gohr $possible = $this->getAnimalNamesForHost($_SERVER['HTTP_HOST']); 157*85becf1bSAndreas Gohr foreach($possible as $animal) { 158a646d519SAndreas Gohr if(is_dir("$farmdir/$animal/conf/")) { 159a646d519SAndreas Gohr $this->animal = $animal; 160a646d519SAndreas Gohr return; 161a646d519SAndreas Gohr } 162a646d519SAndreas Gohr } 163a646d519SAndreas Gohr 164a646d519SAndreas Gohr // no hit 165a646d519SAndreas Gohr $this->notfound = true; 166a646d519SAndreas Gohr return; 167a646d519SAndreas Gohr } 168a646d519SAndreas Gohr 169a646d519SAndreas Gohr /** 170*85becf1bSAndreas Gohr * Return a list of possible animal names for the given host 171*85becf1bSAndreas Gohr * 172*85becf1bSAndreas Gohr * @param string $host the HTTP_HOST header 173*85becf1bSAndreas Gohr * @return array 174*85becf1bSAndreas Gohr */ 175*85becf1bSAndreas Gohr protected function getAnimalNamesForHost($host) { 176*85becf1bSAndreas Gohr $animals = array(); 177*85becf1bSAndreas Gohr $parts = explode('.', implode('.', explode(':', rtrim($host, '.')))); 178*85becf1bSAndreas Gohr for($j = count($parts); $j > 0; $j--) { 179*85becf1bSAndreas Gohr // strip from the end 180*85becf1bSAndreas Gohr $animals[] = implode('.', array_slice($parts, 0, $j)); 181*85becf1bSAndreas Gohr // strip from the end without host part 182*85becf1bSAndreas Gohr $animals[] = implode('.', array_slice($parts, 1, $j)); 183*85becf1bSAndreas Gohr } 184*85becf1bSAndreas Gohr $animals = array_unique($animals); 185*85becf1bSAndreas Gohr $animals = array_filter($animals); 186*85becf1bSAndreas Gohr usort($animals, function($a, $b) { 187*85becf1bSAndreas Gohr return strlen($b) - strlen($a); 188*85becf1bSAndreas Gohr }); 189*85becf1bSAndreas Gohr return $animals; 190*85becf1bSAndreas Gohr } 191*85becf1bSAndreas Gohr 192*85becf1bSAndreas Gohr /** 193a646d519SAndreas Gohr * This sets up the default farming config cascade 194a646d519SAndreas Gohr */ 195a646d519SAndreas Gohr protected function setupCascade() { 196a646d519SAndreas Gohr global $config_cascade; 197a646d519SAndreas Gohr $config_cascade = array( 198a646d519SAndreas Gohr 'main' => array( 199a646d519SAndreas Gohr 'default' => array(DOKU_INC . 'conf/dokuwiki.php',), 200a646d519SAndreas Gohr 'local' => array(DOKU_CONF . 'local.php',), 201a646d519SAndreas Gohr 'protected' => array(DOKU_CONF . 'local.protected.php',), 202a646d519SAndreas Gohr ), 203a646d519SAndreas Gohr 'acronyms' => array( 204a646d519SAndreas Gohr 'default' => array(DOKU_INC . 'conf/acronyms.conf',), 205a646d519SAndreas Gohr 'local' => array(DOKU_CONF . 'acronyms.local.conf',), 206a646d519SAndreas Gohr ), 207a646d519SAndreas Gohr 'entities' => array( 208a646d519SAndreas Gohr 'default' => array(DOKU_INC . 'conf/entities.conf',), 209a646d519SAndreas Gohr 'local' => array(DOKU_CONF . 'entities.local.conf',), 210a646d519SAndreas Gohr ), 211a646d519SAndreas Gohr 'interwiki' => array( 212a646d519SAndreas Gohr 'default' => array(DOKU_INC . 'conf/interwiki.conf',), 213a646d519SAndreas Gohr 'local' => array(DOKU_CONF . 'interwiki.local.conf',), 214a646d519SAndreas Gohr ), 215a646d519SAndreas Gohr 'license' => array( 216a646d519SAndreas Gohr 'default' => array(DOKU_INC . 'conf/license.php',), 217a646d519SAndreas Gohr 'local' => array(DOKU_CONF . 'license.local.php',), 218a646d519SAndreas Gohr ), 219a646d519SAndreas Gohr 'mediameta' => array( 220a646d519SAndreas Gohr 'default' => array(DOKU_INC . 'conf/mediameta.php',), 221a646d519SAndreas Gohr 'local' => array(DOKU_CONF . 'mediameta.local.php',), 222a646d519SAndreas Gohr ), 223a646d519SAndreas Gohr 'mime' => array( 224a646d519SAndreas Gohr 'default' => array(DOKU_INC . 'conf/mime.conf',), 225a646d519SAndreas Gohr 'local' => array(DOKU_CONF . 'mime.local.conf',), 226a646d519SAndreas Gohr ), 227a646d519SAndreas Gohr 'scheme' => array( 228a646d519SAndreas Gohr 'default' => array(DOKU_INC . 'conf/scheme.conf',), 229a646d519SAndreas Gohr 'local' => array(DOKU_CONF . 'scheme.local.conf',), 230a646d519SAndreas Gohr ), 231a646d519SAndreas Gohr 'smileys' => array( 232a646d519SAndreas Gohr 'default' => array(DOKU_INC . 'conf/smileys.conf',), 233a646d519SAndreas Gohr 'local' => array(DOKU_CONF . 'smileys.local.conf',), 234a646d519SAndreas Gohr ), 235a646d519SAndreas Gohr 'wordblock' => array( 236a646d519SAndreas Gohr 'default' => array(DOKU_INC . 'conf/wordblock.conf',), 237a646d519SAndreas Gohr 'local' => array(DOKU_CONF . 'wordblock.local.conf',), 238a646d519SAndreas Gohr ), 239a646d519SAndreas Gohr 'acl' => array( 240a646d519SAndreas Gohr 'default' => DOKU_CONF . 'acl.auth.php', 241a646d519SAndreas Gohr ), 242a646d519SAndreas Gohr 'plainauth.users' => array( 243a646d519SAndreas Gohr 'default' => DOKU_CONF . 'users.auth.php', 244a646d519SAndreas Gohr ), 245a646d519SAndreas Gohr 'plugins' => array( // needed since Angua 246a646d519SAndreas Gohr 'default' => array(DOKU_INC . 'conf/plugins.php',), 247a646d519SAndreas Gohr 'local' => array(DOKU_CONF . 'plugins.local.php',), 248a646d519SAndreas Gohr 'protected' => array( 249a646d519SAndreas Gohr DOKU_INC . 'conf/plugins.required.php', 250a646d519SAndreas Gohr DOKU_CONF . 'plugins.protected.php', 251a646d519SAndreas Gohr ), 252a646d519SAndreas Gohr ), 253a646d519SAndreas Gohr 'userstyle' => array( 254a646d519SAndreas Gohr 'screen' => array(DOKU_CONF . 'userstyle.css', DOKU_CONF . 'userstyle.less',), 255a646d519SAndreas Gohr 'print' => array(DOKU_CONF . 'userprint.css', DOKU_CONF . 'userprint.less',), 256a646d519SAndreas Gohr 'feed' => array(DOKU_CONF . 'userfeed.css', DOKU_CONF . 'userfeed.less',), 257a646d519SAndreas Gohr 'all' => array(DOKU_CONF . 'userall.css', DOKU_CONF . 'userall.less',), 258a646d519SAndreas Gohr ), 259a646d519SAndreas Gohr 'userscript' => array( 260a646d519SAndreas Gohr 'default' => array(DOKU_CONF . 'userscript.js',), 261a646d519SAndreas Gohr ), 262a646d519SAndreas Gohr ); 263a646d519SAndreas Gohr } 264a646d519SAndreas Gohr 265a646d519SAndreas Gohr /** 266a646d519SAndreas Gohr * This adds additional files to the config cascade based on the inheritence settings 267a646d519SAndreas Gohr * 268a646d519SAndreas Gohr * These are only added for animals, not the farmer 269a646d519SAndreas Gohr */ 270a646d519SAndreas Gohr protected function adjustCascade() { 271b330074aSAndreas Gohr // nothing to do when on the farmer: 272b330074aSAndreas Gohr if(!$this->animal) return; 273a646d519SAndreas Gohr 274b330074aSAndreas Gohr global $config_cascade; 275a646d519SAndreas Gohr foreach($this->config['inherit'] as $key => $val) { 276a646d519SAndreas Gohr if(!$val) continue; 277a646d519SAndreas Gohr 278a646d519SAndreas Gohr // prepare what is to append or prepend 279a646d519SAndreas Gohr $append = array(); 280a646d519SAndreas Gohr $prepend = array(); 281a646d519SAndreas Gohr if($key == 'main') { 282b330074aSAndreas Gohr $append = array( 283b330074aSAndreas Gohr 'default' => array(DOKU_INC . 'conf/local.php'), 284b330074aSAndreas Gohr 'protected' => array(DOKU_INC . 'lib/plugins/farmer/includes/config.php') 285b330074aSAndreas Gohr ); 286a646d519SAndreas Gohr } elseif($key == 'license') { 287a646d519SAndreas Gohr $append = array('default' => array(DOKU_INC . 'conf/' . $key . '.local.php')); 288a646d519SAndreas Gohr } elseif($key == 'userscript') { 289a646d519SAndreas Gohr $prepend = array('default' => array(DOKU_INC . 'conf/userscript.js')); 290a646d519SAndreas Gohr } elseif($key == 'userstyle') { 291a646d519SAndreas Gohr $prepend = array( 292a646d519SAndreas Gohr 'screen' => array(DOKU_INC . 'conf/userstyle.css', DOKU_INC . 'conf/userstyle.less',), 293a646d519SAndreas Gohr 'print' => array(DOKU_INC . 'conf/userprint.css', DOKU_INC . 'conf/userprint.less',), 294a646d519SAndreas Gohr 'feed' => array(DOKU_INC . 'conf/userfeed.css', DOKU_INC . 'conf/userfeed.less',), 295a646d519SAndreas Gohr 'all' => array(DOKU_INC . 'conf/userall.css', DOKU_INC . 'conf/userall.less',), 296a646d519SAndreas Gohr ); 297a646d519SAndreas Gohr } else { 298a646d519SAndreas Gohr $append = array('default' => array(DOKU_INC . 'conf/' . $key . '.local.conf')); 299a646d519SAndreas Gohr } 300a646d519SAndreas Gohr 301a646d519SAndreas Gohr // add to cascade 302a646d519SAndreas Gohr foreach($prepend as $section => $data) { 303a646d519SAndreas Gohr $config_cascade[$key][$section] = array_merge($data, $config_cascade[$key][$section]); 304a646d519SAndreas Gohr } 305a646d519SAndreas Gohr foreach($append as $section => $data) { 306a646d519SAndreas Gohr $config_cascade[$key][$section] = array_merge($config_cascade[$key][$section], $data); 307a646d519SAndreas Gohr } 308a646d519SAndreas Gohr } 309a646d519SAndreas Gohr } 310a646d519SAndreas Gohr 311a646d519SAndreas Gohr /** 312da0ae2c0SAndreas Gohr * Loads the farm config 313da0ae2c0SAndreas Gohr */ 314da0ae2c0SAndreas Gohr protected function loadConfig() { 315da0ae2c0SAndreas Gohr $ini = DOKU_INC . 'conf/farm.ini'; 316da0ae2c0SAndreas Gohr if(!file_exists($ini)) return; 317da0ae2c0SAndreas Gohr $config = parse_ini_file($ini, true); 318da0ae2c0SAndreas Gohr foreach(array_keys($this->config) as $section) { 319da0ae2c0SAndreas Gohr if(isset($config[$section])) { 320da0ae2c0SAndreas Gohr $this->config[$section] = array_merge( 321da0ae2c0SAndreas Gohr $this->config[$section], 322da0ae2c0SAndreas Gohr $config[$section] 323da0ae2c0SAndreas Gohr ); 324da0ae2c0SAndreas Gohr } 325da0ae2c0SAndreas Gohr } 326da0ae2c0SAndreas Gohr 327a646d519SAndreas Gohr $this->config['base']['farmdir'] = trim($this->config['base']['farmdir']); 328a646d519SAndreas Gohr $this->config['base']['farmhost'] = strtolower(trim($this->config['base']['farmhost'])); 329a646d519SAndreas Gohr } 330da0ae2c0SAndreas Gohr 331da0ae2c0SAndreas Gohr} 332da0ae2c0SAndreas Gohr 333da0ae2c0SAndreas Gohr// initialize it globally 334*85becf1bSAndreas Gohrif(!defined('DOKU_UNITTEST')) { 335da0ae2c0SAndreas Gohr global $FARMCORE; 336da0ae2c0SAndreas Gohr $FARMCORE = new DokuWikiFarmCore(); 337*85becf1bSAndreas Gohr} 338