1<?php 2 3use dokuwiki\Extension\Plugin; 4 5/** 6 * DokuWiki Plugin farmer (Helper Component) 7 * 8 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 9 * @author Michael Große <grosse@cosmocode.de> 10 * @author Andreas Gohr <gohr@cosmocode.de> 11 */ 12class helper_plugin_farmer extends Plugin 13{ 14 protected $defaultPluginState; 15 protected $animalPluginState = []; 16 17 /** 18 * Returns the name of the current animal if any, false otherwise 19 * 20 * @return string|false 21 */ 22 public function getAnimal() 23 { 24 if (!isset($GLOBALS['FARMCORE'])) return false; 25 return $GLOBALS['FARMCORE']->getAnimal(); 26 } 27 28 /** 29 * Get the farm config 30 * 31 * @return array 32 */ 33 public function getConfig() 34 { 35 if (!isset($GLOBALS['FARMCORE'])) return []; 36 return $GLOBALS['FARMCORE']->getConfig(); 37 } 38 39 /** 40 * Was the current animal requested by host? 41 * 42 * @return bool 43 */ 44 public function isHostbased() 45 { 46 if (!isset($GLOBALS['FARMCORE'])) return false; 47 return $GLOBALS['FARMCORE']->isHostbased(); 48 } 49 50 /** 51 * Was an animal requested that could not be found? 52 * 53 * @return bool 54 */ 55 public function wasNotfound() 56 { 57 if (!isset($GLOBALS['FARMCORE'])) return false; 58 return $GLOBALS['FARMCORE']->wasNotfound(); 59 } 60 61 /** 62 * Guess the URL for an animal 63 * 64 * @param $animal 65 * @return string 66 */ 67 public function getAnimalURL($animal) 68 { 69 $config = $this->getConfig(); 70 71 $protocol = is_ssl() ? 'https://' : 'http://'; 72 if (str_contains($animal, '.')) { 73 return $protocol . $animal; 74 } elseif ($config['base']['basedomain']) { 75 return $protocol . $animal . '.' . $config['base']['basedomain']; 76 } else { 77 return DOKU_URL . '!' . $animal . '/'; 78 } 79 } 80 81 /** 82 * List of all animals, i.e. directories within DOKU_FARMDIR without the template. 83 * 84 * @return array 85 */ 86 public function getAllAnimals() 87 { 88 $animals = []; 89 $list = glob(DOKU_FARMDIR . '*/conf/', GLOB_ONLYDIR); 90 foreach ($list as $path) { 91 $animal = basename(dirname($path)); 92 if ($animal == '_animal') continue; // old template 93 $animals[] = $animal; 94 } 95 sort($animals); 96 return $animals; 97 } 98 99 /** 100 * checks wether $path is in under $container 101 * 102 * Also returns false if $path and $container are the same directory 103 * 104 * @param string $path 105 * @param string $container 106 * @return bool 107 */ 108 public function isInPath($path, $container) 109 { 110 $path = fullpath($path) . '/'; 111 $container = fullpath($container) . '/'; 112 if ($path === $container) return false; 113 return (str_starts_with($path, $container)); 114 } 115 116 /** 117 * Check if the farm is correctly configured for this farmer plugin 118 * 119 * @return bool 120 */ 121 public function checkFarmSetup() 122 { 123 return defined('DOKU_FARMDIR') && isset($GLOBALS['FARMCORE']); 124 } 125 126 /** 127 * @param string $animalname 128 * 129 * @return bool 130 */ 131 public function validateAnimalName($animalname) 132 { 133 return preg_match("/^[a-z0-9]+([\\.\\-][a-z0-9]+)*$/i", $animalname) === 1; 134 } 135 136 /** 137 * Copy a file, or recursively copy a folder and its contents. Adapted for DokuWiki. 138 * 139 * @todo: needs tests 140 * 141 * @author Aidan Lister <aidan@php.net> 142 * @author Michael Große <grosse@cosmocode.de> 143 * @author Andreas Gohr <gohr@cosmocode.de> 144 * @link http://aidanlister.com/2004/04/recursively-copying-directories-in-php/ 145 * 146 * @param string $source Source path 147 * @param string $destination Destination path 148 * @param string $exclude Regular expression to exclude files or directories (complete with delimiters) 149 * @return bool Returns TRUE on success, FALSE on failure 150 */ 151 public function copyDir($source, $destination, $exclude = '') 152 { 153 if ($exclude && preg_match($exclude, $source)) { 154 return true; 155 } 156 157 if (is_link($source)) { 158 io_lock($destination); 159 $result = symlink(readlink($source), $destination); 160 io_unlock($destination); 161 return $result; 162 } 163 164 if (is_file($source)) { 165 io_lock($destination); 166 $result = copy($source, $destination); 167 io_unlock($destination); 168 return $result; 169 } 170 171 if (!is_dir($destination)) { 172 io_mkdir_p($destination); 173 } 174 175 $dir = @dir($source); 176 if ($dir === false) return false; 177 while (false !== ($entry = $dir->read())) { 178 if ($entry == '.' || $entry == '..') { 179 continue; 180 } 181 182 // recurse into directories 183 $this->copyDir("$source/$entry", "$destination/$entry", $exclude); 184 } 185 186 $dir->close(); 187 return true; 188 } 189 190 /** 191 * get a list of all Plugins installed in the farmer wiki, regardless whether they are active or not. 192 * 193 * @param bool $all get all plugins, even disabled ones 194 * @return array 195 */ 196 public function getAllPlugins($all = true) 197 { 198 199 /** @var Doku_Plugin_Controller $plugin_controller */ 200 global $plugin_controller; 201 202 $plugins = $plugin_controller->getList('', $all); 203 204 // filter out a few plugins 205 $plugins = array_filter( 206 $plugins, 207 function ($item) { 208 if ($item == 'farmer') return false; 209 if ($item == 'extension') return false; 210 if ($item == 'upgrade') return false; 211 if ($item == 'testing') return false; 212 return true; 213 } 214 ); 215 216 sort($plugins); 217 return $plugins; 218 } 219 220 /** 221 * Get the plugin states configured locally in the given animal 222 * 223 * Response is cached 224 * 225 * @param $animal 226 * @return array 227 */ 228 public function getAnimalPluginLocalStates($animal) 229 { 230 if (isset($this->animalPluginState[$animal])) return $this->animalPluginState[$animal]; 231 232 $localfile = DOKU_FARMDIR . $animal . '/conf/plugins.local.php'; 233 $plugins = []; 234 if (file_exists($localfile)) { 235 include($localfile); 236 } 237 238 $this->animalPluginState[$animal] = $plugins; 239 return $plugins; 240 } 241 242 /** 243 * Return the default state plugins would have in animals 244 * 245 * Response is cached 246 * 247 * @return array 248 */ 249 public function getDefaultPluginStates() 250 { 251 if (!is_null($this->defaultPluginState)) return $this->defaultPluginState; 252 253 $farmconf = $this->getConfig(); 254 $all = $this->getAllPlugins(); 255 256 $plugins = []; 257 foreach ($all as $one) { 258 if ($farmconf['inherit']['plugins']) { 259 $plugins[$one] = !plugin_isdisabled($one); 260 } else { 261 $plugins[$one] = true; // default state is enabled 262 } 263 } 264 265 ksort($plugins); 266 $this->defaultPluginState = $plugins; 267 return $plugins; 268 } 269 270 /** 271 * Return a structure giving detailed info about the state of all plugins in an animal 272 * 273 * @param $animal 274 * @return array 275 */ 276 public function getAnimalPluginRealState($animal) 277 { 278 $info = []; 279 280 $defaults = $this->getDefaultPluginStates(); 281 $local = $this->getAnimalPluginLocalStates($animal); 282 283 foreach ($defaults as $plugin => $set) { 284 $current = ['name' => $plugin, 'default' => $set, 'actual' => $set, 'isdefault' => true]; 285 286 if (isset($local[$plugin])) { 287 $current['actual'] = (bool) $local[$plugin]; 288 $current['isdefault'] = false; 289 } 290 291 $info[$plugin] = $current; 292 } 293 294 ksort($info); 295 return $info; 296 } 297 298 /** 299 * Set the state of a plugin in an animal 300 * 301 * @param string $plugin 302 * @param string $animal 303 * @param int $state -1 = default, 1 = enabled, 0 = disabled 304 */ 305 public function setPluginState($plugin, $animal, $state) 306 { 307 $state = (int) $state; 308 309 $plugins = $this->getAnimalPluginLocalStates($animal); 310 if ($state < 0) { 311 if (isset($plugins[$plugin])) unset($plugins[$plugin]); 312 } else { 313 $plugins[$plugin] = $state; 314 } 315 316 $this->writePluginConf($plugins, $animal); 317 318 // clear state cache 319 if (isset($this->animalPluginState[$animal])) unset($this->animalPluginState[$animal]); 320 } 321 322 /** 323 * Write the list of (deactivated) plugins as plugin configuration of an animal to file 324 * 325 * updates the plugin state cache 326 * 327 * @param array $plugins associative array with the key being the plugin name and the value 0 or 1 328 * @param string $animal Directory of the animal within DOKU_FARMDIR 329 */ 330 public function writePluginConf($plugins, $animal) 331 { 332 $pluginConf = '<?php' . "\n# plugins enabled and disabled by the farmer plugin\n"; 333 foreach ($plugins as $plugin => $status) { 334 $pluginConf .= '$plugins[\'' . $plugin . '\'] = ' . $status . ";\n"; 335 } 336 io_saveFile(DOKU_FARMDIR . $animal . '/conf/plugins.local.php', $pluginConf); 337 touch(DOKU_FARMDIR . $animal . '/conf/local.php'); 338 339 if (function_exists('opcache_invalidate')) { 340 opcache_invalidate(DOKU_FARMDIR . $animal . '/conf/plugins.local.php'); 341 opcache_invalidate(DOKU_FARMDIR . $animal . '/conf/local.php'); 342 } 343 344 $this->animalPluginState[$animal] = $plugins; 345 } 346} 347