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