1bc461538SMichael Große<?php 2*1da41c8bSAndreas Gohr 3*1da41c8bSAndreas Gohruse dokuwiki\Extension\Plugin; 4*1da41c8bSAndreas Gohr 5bc461538SMichael Große/** 6bc461538SMichael Große * DokuWiki Plugin farmer (Helper Component) 7bc461538SMichael Große * 8bc461538SMichael Große * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 9bc461538SMichael Große * @author Michael Große <grosse@cosmocode.de> 100a5d2da2SAndreas Gohr * @author Andreas Gohr <gohr@cosmocode.de> 11bc461538SMichael Große */ 12*1da41c8bSAndreas Gohrclass helper_plugin_farmer extends Plugin 13*1da41c8bSAndreas Gohr{ 14*1da41c8bSAndreas Gohr protected $defaultPluginState; 15*1da41c8bSAndreas Gohr protected $animalPluginState = []; 16fcbe16a4SMichael Große 17bc461538SMichael Große /** 18632c5618SAndreas Gohr * Returns the name of the current animal if any, false otherwise 19632c5618SAndreas Gohr * 20632c5618SAndreas Gohr * @return string|false 21632c5618SAndreas Gohr */ 22*1da41c8bSAndreas Gohr public function getAnimal() 23*1da41c8bSAndreas Gohr { 24b96c66ccSAndreas Gohr if (!isset($GLOBALS['FARMCORE'])) return false; 25b96c66ccSAndreas Gohr return $GLOBALS['FARMCORE']->getAnimal(); 26632c5618SAndreas Gohr } 27632c5618SAndreas Gohr 28632c5618SAndreas Gohr /** 29b96c66ccSAndreas Gohr * Get the farm config 30b96c66ccSAndreas Gohr * 31b96c66ccSAndreas Gohr * @return array 32b96c66ccSAndreas Gohr */ 33*1da41c8bSAndreas Gohr public function getConfig() 34*1da41c8bSAndreas Gohr { 35*1da41c8bSAndreas Gohr if (!isset($GLOBALS['FARMCORE'])) return []; 36b96c66ccSAndreas Gohr return $GLOBALS['FARMCORE']->getConfig(); 37b96c66ccSAndreas Gohr } 38b96c66ccSAndreas Gohr 39b96c66ccSAndreas Gohr /** 40b96c66ccSAndreas Gohr * Was the current animal requested by host? 41b96c66ccSAndreas Gohr * 42b96c66ccSAndreas Gohr * @return bool 43b96c66ccSAndreas Gohr */ 44*1da41c8bSAndreas Gohr public function isHostbased() 45*1da41c8bSAndreas Gohr { 46b96c66ccSAndreas Gohr if (!isset($GLOBALS['FARMCORE'])) return false; 47b96c66ccSAndreas Gohr return $GLOBALS['FARMCORE']->isHostbased(); 48b96c66ccSAndreas Gohr } 49b96c66ccSAndreas Gohr 50b96c66ccSAndreas Gohr /** 51b96c66ccSAndreas Gohr * Was an animal requested that could not be found? 52b96c66ccSAndreas Gohr * 53b96c66ccSAndreas Gohr * @return bool 54b96c66ccSAndreas Gohr */ 55*1da41c8bSAndreas Gohr public function wasNotfound() 56*1da41c8bSAndreas Gohr { 57b96c66ccSAndreas Gohr if (!isset($GLOBALS['FARMCORE'])) return false; 58b96c66ccSAndreas Gohr return $GLOBALS['FARMCORE']->wasNotfound(); 59b96c66ccSAndreas Gohr } 60b96c66ccSAndreas Gohr 61b96c66ccSAndreas Gohr /** 62c4c8e953SAndreas Gohr * Guess the URL for an animal 63c4c8e953SAndreas Gohr * 64c4c8e953SAndreas Gohr * @param $animal 65c4c8e953SAndreas Gohr * @return string 66c4c8e953SAndreas Gohr */ 67*1da41c8bSAndreas Gohr public function getAnimalURL($animal) 68*1da41c8bSAndreas Gohr { 69c4c8e953SAndreas Gohr $config = $this->getConfig(); 70c4c8e953SAndreas Gohr 71c4c8e953SAndreas Gohr if (strpos($animal, '.') !== false) { 72c4c8e953SAndreas Gohr return 'http://' . $animal; 73c4c8e953SAndreas Gohr } elseif ($config['base']['basedomain']) { 74c4c8e953SAndreas Gohr return 'http://' . $animal . '.' . $config['base']['basedomain']; 75c4c8e953SAndreas Gohr } else { 760336ab2aSAndreas Gohr return DOKU_URL . '!' . $animal . '/'; 77c4c8e953SAndreas Gohr } 78c4c8e953SAndreas Gohr } 79c4c8e953SAndreas Gohr 80c4c8e953SAndreas Gohr /** 81b96c66ccSAndreas Gohr * List of all animals, i.e. directories within DOKU_FARMDIR without the template. 82b96c66ccSAndreas Gohr * 83b96c66ccSAndreas Gohr * @return array 84b96c66ccSAndreas Gohr */ 85*1da41c8bSAndreas Gohr public function getAllAnimals() 86*1da41c8bSAndreas Gohr { 87*1da41c8bSAndreas Gohr $animals = []; 888262a4cbSAndreas Gohr $list = glob(DOKU_FARMDIR . '*/conf/', GLOB_ONLYDIR); 89b96c66ccSAndreas Gohr foreach ($list as $path) { 90b96c66ccSAndreas Gohr $animal = basename(dirname($path)); 91b96c66ccSAndreas Gohr if ($animal == '_animal') continue; // old template 92b96c66ccSAndreas Gohr $animals[] = $animal; 93b96c66ccSAndreas Gohr } 94b96c66ccSAndreas Gohr sort($animals); 95b96c66ccSAndreas Gohr return $animals; 96b96c66ccSAndreas Gohr } 97b96c66ccSAndreas Gohr 98b96c66ccSAndreas Gohr /** 99b96c66ccSAndreas Gohr * checks wether $path is in under $container 100b96c66ccSAndreas Gohr * 101dfdaf33eSAndreas Gohr * Also returns false if $path and $container are the same directory 102dfdaf33eSAndreas Gohr * 103b96c66ccSAndreas Gohr * @param string $path 104b96c66ccSAndreas Gohr * @param string $container 105b96c66ccSAndreas Gohr * @return bool 106b96c66ccSAndreas Gohr */ 107*1da41c8bSAndreas Gohr public function isInPath($path, $container) 108*1da41c8bSAndreas Gohr { 1090f0a264cSAndreas Gohr $path = fullpath($path) . '/'; 1100f0a264cSAndreas Gohr $container = fullpath($container) . '/'; 111*1da41c8bSAndreas Gohr if ($path === $container) return false; 112dfdaf33eSAndreas Gohr return (strpos($path, $container) === 0); 113b96c66ccSAndreas Gohr } 114b96c66ccSAndreas Gohr 115b96c66ccSAndreas Gohr /** 116b96c66ccSAndreas Gohr * Check if the farm is correctly configured for this farmer plugin 117b96c66ccSAndreas Gohr * 118b96c66ccSAndreas Gohr * @return bool 119b96c66ccSAndreas Gohr */ 120*1da41c8bSAndreas Gohr public function checkFarmSetup() 121*1da41c8bSAndreas Gohr { 122b96c66ccSAndreas Gohr return defined('DOKU_FARMDIR') && isset($GLOBALS['FARMCORE']); 123b96c66ccSAndreas Gohr } 124b96c66ccSAndreas Gohr 12549f2871cSAndreas Gohr /** 12649f2871cSAndreas Gohr * @param string $animalname 12749f2871cSAndreas Gohr * 12849f2871cSAndreas Gohr * @return bool 12949f2871cSAndreas Gohr */ 130*1da41c8bSAndreas Gohr public function validateAnimalName($animalname) 131*1da41c8bSAndreas Gohr { 13278c63d53SAndreas Gohr return preg_match("/^[a-z0-9]+([\\.\\-][a-z0-9]+)*$/i", $animalname) === 1; 13349f2871cSAndreas Gohr } 134b96c66ccSAndreas Gohr 135b96c66ccSAndreas Gohr /** 136bc461538SMichael Große * Copy a file, or recursively copy a folder and its contents. Adapted for DokuWiki. 137bc461538SMichael Große * 138bc461538SMichael Große * @todo: needs tests 139bc461538SMichael Große * 140bc461538SMichael Große * @author Aidan Lister <aidan@php.net> 141bc461538SMichael Große * @author Michael Große <grosse@cosmocode.de> 142801ebaa1SAndreas Gohr * @author Andreas Gohr <gohr@cosmocode.de> 143bc461538SMichael Große * @link http://aidanlister.com/2004/04/recursively-copying-directories-in-php/ 144bc461538SMichael Große * 145bc461538SMichael Große * @param string $source Source path 146bc461538SMichael Große * @param string $destination Destination path 147801ebaa1SAndreas Gohr * @param string $exclude Regular expression to exclude files or directories (complete with delimiters) 148bc461538SMichael Große * @return bool Returns TRUE on success, FALSE on failure 149bc461538SMichael Große */ 150*1da41c8bSAndreas Gohr public function copyDir($source, $destination, $exclude = '') 151*1da41c8bSAndreas Gohr { 152801ebaa1SAndreas Gohr if ($exclude && preg_match($exclude, $source)) { 153801ebaa1SAndreas Gohr return true; 154801ebaa1SAndreas Gohr } 155801ebaa1SAndreas Gohr 156bc461538SMichael Große if (is_link($source)) { 157bc461538SMichael Große io_lock($destination); 158bc461538SMichael Große $result = symlink(readlink($source), $destination); 159bc461538SMichael Große io_unlock($destination); 160bc461538SMichael Große return $result; 161bc461538SMichael Große } 162bc461538SMichael Große 163bc461538SMichael Große if (is_file($source)) { 164bc461538SMichael Große io_lock($destination); 165bc461538SMichael Große $result = copy($source, $destination); 166bc461538SMichael Große io_unlock($destination); 167bc461538SMichael Große return $result; 168bc461538SMichael Große } 169bc461538SMichael Große 170bc461538SMichael Große if (!is_dir($destination)) { 171bc461538SMichael Große io_mkdir_p($destination); 172bc461538SMichael Große } 173bc461538SMichael Große 17449f2871cSAndreas Gohr $dir = @dir($source); 17549f2871cSAndreas Gohr if ($dir === false) return false; 176bc461538SMichael Große while (false !== ($entry = $dir->read())) { 177bc461538SMichael Große if ($entry == '.' || $entry == '..') { 178bc461538SMichael Große continue; 179bc461538SMichael Große } 180bc461538SMichael Große 181bc461538SMichael Große // recurse into directories 182*1da41c8bSAndreas Gohr $this->copyDir("$source/$entry", "$destination/$entry", $exclude); 183bc461538SMichael Große } 184bc461538SMichael Große 185bc461538SMichael Große $dir->close(); 186bc461538SMichael Große return true; 187bc461538SMichael Große } 188bc461538SMichael Große 18916bbfe4bSMichael Große /** 19016bbfe4bSMichael Große * get a list of all Plugins installed in the farmer wiki, regardless whether they are active or not. 19116bbfe4bSMichael Große * 192af1c6dd8SAndreas Gohr * @param bool $all get all plugins, even disabled ones 19316bbfe4bSMichael Große * @return array 19416bbfe4bSMichael Große */ 195*1da41c8bSAndreas Gohr public function getAllPlugins($all = true) 196*1da41c8bSAndreas Gohr { 197af1c6dd8SAndreas Gohr 1983536d644SAndreas Gohr /** @var Doku_Plugin_Controller $plugin_controller */ 1993536d644SAndreas Gohr global $plugin_controller; 2003536d644SAndreas Gohr 201af1c6dd8SAndreas Gohr $plugins = $plugin_controller->getList('', $all); 2023536d644SAndreas Gohr 2033536d644SAndreas Gohr // filter out a few plugins 204af1c6dd8SAndreas Gohr $plugins = array_filter( 205*1da41c8bSAndreas Gohr $plugins, 206*1da41c8bSAndreas Gohr function ($item) { 2073536d644SAndreas Gohr if ($item == 'farmer') return false; 2083536d644SAndreas Gohr if ($item == 'extension') return false; 209f1336aa9SAndreas Gohr if ($item == 'upgrade') return false; 2103536d644SAndreas Gohr if ($item == 'testing') return false; 2113536d644SAndreas Gohr return true; 212af1c6dd8SAndreas Gohr } 213af1c6dd8SAndreas Gohr ); 2143536d644SAndreas Gohr 2156ec1ad8fSMichael Große sort($plugins); 2160b96e6d7SMichael Große return $plugins; 2170b96e6d7SMichael Große } 2180b96e6d7SMichael Große 21916bbfe4bSMichael Große /** 220af1c6dd8SAndreas Gohr * Get the plugin states configured locally in the given animal 22116bbfe4bSMichael Große * 222af1c6dd8SAndreas Gohr * Response is cached 223af1c6dd8SAndreas Gohr * 224af1c6dd8SAndreas Gohr * @param $animal 225af1c6dd8SAndreas Gohr * @return array 22616bbfe4bSMichael Große */ 227*1da41c8bSAndreas Gohr public function getAnimalPluginLocalStates($animal) 228*1da41c8bSAndreas Gohr { 229af1c6dd8SAndreas Gohr if (isset($this->animalPluginState[$animal])) return $this->animalPluginState[$animal]; 230af1c6dd8SAndreas Gohr 231af1c6dd8SAndreas Gohr $localfile = DOKU_FARMDIR . $animal . '/conf/plugins.local.php'; 232*1da41c8bSAndreas Gohr $plugins = []; 233af1c6dd8SAndreas Gohr if (file_exists($localfile)) { 234af1c6dd8SAndreas Gohr include($localfile); 235fcbe16a4SMichael Große } 236af1c6dd8SAndreas Gohr 237af1c6dd8SAndreas Gohr $this->animalPluginState[$animal] = $plugins; 238af1c6dd8SAndreas Gohr return $plugins; 239fcbe16a4SMichael Große } 240fcbe16a4SMichael Große 241a0fc814bSMichael Große /** 242af1c6dd8SAndreas Gohr * Return the default state plugins would have in animals 243af1c6dd8SAndreas Gohr * 244af1c6dd8SAndreas Gohr * Response is cached 245af1c6dd8SAndreas Gohr * 246af1c6dd8SAndreas Gohr * @return array 247a0fc814bSMichael Große */ 248*1da41c8bSAndreas Gohr public function getDefaultPluginStates() 249*1da41c8bSAndreas Gohr { 250af1c6dd8SAndreas Gohr if (!is_null($this->defaultPluginState)) return $this->defaultPluginState; 251af1c6dd8SAndreas Gohr 252af1c6dd8SAndreas Gohr $farmconf = $this->getConfig(); 253af1c6dd8SAndreas Gohr $all = $this->getAllPlugins(); 254af1c6dd8SAndreas Gohr 255*1da41c8bSAndreas Gohr $plugins = []; 256af1c6dd8SAndreas Gohr foreach ($all as $one) { 257af1c6dd8SAndreas Gohr if ($farmconf['inherit']['plugins']) { 258af1c6dd8SAndreas Gohr $plugins[$one] = !plugin_isdisabled($one); 259fcbe16a4SMichael Große } else { 260af1c6dd8SAndreas Gohr $plugins[$one] = true; // default state is enabled 261fcbe16a4SMichael Große } 262114a05a7SAndreas Gohr } 263af1c6dd8SAndreas Gohr 264511d09feSAndreas Gohr ksort($plugins); 265af1c6dd8SAndreas Gohr $this->defaultPluginState = $plugins; 266af1c6dd8SAndreas Gohr return $plugins; 267af1c6dd8SAndreas Gohr } 268af1c6dd8SAndreas Gohr 269af1c6dd8SAndreas Gohr /** 270af1c6dd8SAndreas Gohr * Return a structure giving detailed info about the state of all plugins in an animal 271af1c6dd8SAndreas Gohr * 272af1c6dd8SAndreas Gohr * @param $animal 273af1c6dd8SAndreas Gohr * @return array 274af1c6dd8SAndreas Gohr */ 275*1da41c8bSAndreas Gohr public function getAnimalPluginRealState($animal) 276*1da41c8bSAndreas Gohr { 277*1da41c8bSAndreas Gohr $info = []; 278af1c6dd8SAndreas Gohr 279af1c6dd8SAndreas Gohr $defaults = $this->getDefaultPluginStates(); 280af1c6dd8SAndreas Gohr $local = $this->getAnimalPluginLocalStates($animal); 281af1c6dd8SAndreas Gohr 282af1c6dd8SAndreas Gohr foreach ($defaults as $plugin => $set) { 283*1da41c8bSAndreas Gohr $current = ['name' => $plugin, 'default' => $set, 'actual' => $set, 'isdefault' => true]; 284af1c6dd8SAndreas Gohr 285af1c6dd8SAndreas Gohr if (isset($local[$plugin])) { 286af1c6dd8SAndreas Gohr $current['actual'] = (bool) $local[$plugin]; 287af1c6dd8SAndreas Gohr $current['isdefault'] = false; 288af1c6dd8SAndreas Gohr } 289af1c6dd8SAndreas Gohr 290511d09feSAndreas Gohr $info[$plugin] = $current; 291af1c6dd8SAndreas Gohr } 292af1c6dd8SAndreas Gohr 293511d09feSAndreas Gohr ksort($info); 294af1c6dd8SAndreas Gohr return $info; 295af1c6dd8SAndreas Gohr } 296af1c6dd8SAndreas Gohr 297af1c6dd8SAndreas Gohr /** 298af1c6dd8SAndreas Gohr * Set the state of a plugin in an animal 299af1c6dd8SAndreas Gohr * 300af1c6dd8SAndreas Gohr * @param string $plugin 301af1c6dd8SAndreas Gohr * @param string $animal 302af1c6dd8SAndreas Gohr * @param int $state -1 = default, 1 = enabled, 0 = disabled 303af1c6dd8SAndreas Gohr */ 304*1da41c8bSAndreas Gohr public function setPluginState($plugin, $animal, $state) 305*1da41c8bSAndreas Gohr { 306af1c6dd8SAndreas Gohr $state = (int) $state; 307af1c6dd8SAndreas Gohr 308af1c6dd8SAndreas Gohr $plugins = $this->getAnimalPluginLocalStates($animal); 309af1c6dd8SAndreas Gohr if ($state < 0) { 310af1c6dd8SAndreas Gohr if (isset($plugins[$plugin])) unset($plugins[$plugin]); 311af1c6dd8SAndreas Gohr } else { 312af1c6dd8SAndreas Gohr $plugins[$plugin] = $state; 313af1c6dd8SAndreas Gohr } 314af1c6dd8SAndreas Gohr 315fcbe16a4SMichael Große $this->writePluginConf($plugins, $animal); 316511d09feSAndreas Gohr 317511d09feSAndreas Gohr // clear state cache 318511d09feSAndreas Gohr if (isset($this->animalPluginState[$animal])) unset($this->animalPluginState[$animal]); 319fcbe16a4SMichael Große } 320fcbe16a4SMichael Große 32116bbfe4bSMichael Große /** 32216bbfe4bSMichael Große * Write the list of (deactivated) plugins as plugin configuration of an animal to file 32316bbfe4bSMichael Große * 324af1c6dd8SAndreas Gohr * updates the plugin state cache 325af1c6dd8SAndreas Gohr * 32616bbfe4bSMichael Große * @param array $plugins associative array with the key being the plugin name and the value 0 or 1 32716bbfe4bSMichael Große * @param string $animal Directory of the animal within DOKU_FARMDIR 32816bbfe4bSMichael Große */ 329*1da41c8bSAndreas Gohr public function writePluginConf($plugins, $animal) 330*1da41c8bSAndreas Gohr { 331af1c6dd8SAndreas Gohr $pluginConf = '<?php' . "\n# plugins enabled and disabled by the farmer plugin\n"; 332fcbe16a4SMichael Große foreach ($plugins as $plugin => $status) { 333af1c6dd8SAndreas Gohr $pluginConf .= '$plugins[\'' . $plugin . '\'] = ' . $status . ";\n"; 334fcbe16a4SMichael Große } 335fcbe16a4SMichael Große io_saveFile(DOKU_FARMDIR . $animal . '/conf/plugins.local.php', $pluginConf); 336fcbe16a4SMichael Große touch(DOKU_FARMDIR . $animal . '/conf/local.php'); 337af1c6dd8SAndreas Gohr 3383062cd8eSAndreas Gohr if (function_exists('opcache_invalidate')) { 3393062cd8eSAndreas Gohr opcache_invalidate(DOKU_FARMDIR . $animal . '/conf/plugins.local.php'); 3403062cd8eSAndreas Gohr opcache_invalidate(DOKU_FARMDIR . $animal . '/conf/local.php'); 3413062cd8eSAndreas Gohr } 3423062cd8eSAndreas Gohr 343af1c6dd8SAndreas Gohr $this->animalPluginState[$animal] = $plugins; 344fcbe16a4SMichael Große } 345bc461538SMichael Große} 346