xref: /plugin/farmer/helper.php (revision f64a85f7ef8593a387609801c962a904fe407d36)
1bc461538SMichael Große<?php
21da41c8bSAndreas Gohr
31da41c8bSAndreas Gohruse dokuwiki\Extension\Plugin;
41da41c8bSAndreas 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 */
121da41c8bSAndreas Gohrclass helper_plugin_farmer extends Plugin
131da41c8bSAndreas Gohr{
141da41c8bSAndreas Gohr    protected $defaultPluginState;
151da41c8bSAndreas 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     */
221da41c8bSAndreas Gohr    public function getAnimal()
231da41c8bSAndreas 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     */
331da41c8bSAndreas Gohr    public function getConfig()
341da41c8bSAndreas Gohr    {
351da41c8bSAndreas 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     */
441da41c8bSAndreas Gohr    public function isHostbased()
451da41c8bSAndreas 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     */
551da41c8bSAndreas Gohr    public function wasNotfound()
561da41c8bSAndreas 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     */
671da41c8bSAndreas Gohr    public function getAnimalURL($animal)
681da41c8bSAndreas Gohr    {
69c4c8e953SAndreas Gohr        $config = $this->getConfig();
70c4c8e953SAndreas Gohr
71*f64a85f7SAndreas Gohr        if (str_contains($animal, '.')) {
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     */
851da41c8bSAndreas Gohr    public function getAllAnimals()
861da41c8bSAndreas Gohr    {
871da41c8bSAndreas 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     */
1071da41c8bSAndreas Gohr    public function isInPath($path, $container)
1081da41c8bSAndreas Gohr    {
1090f0a264cSAndreas Gohr        $path = fullpath($path) . '/';
1100f0a264cSAndreas Gohr        $container = fullpath($container) . '/';
1111da41c8bSAndreas Gohr        if ($path === $container) return false;
112*f64a85f7SAndreas Gohr        return (str_starts_with($path, $container));
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     */
1201da41c8bSAndreas Gohr    public function checkFarmSetup()
1211da41c8bSAndreas 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     */
1301da41c8bSAndreas Gohr    public function validateAnimalName($animalname)
1311da41c8bSAndreas 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     */
1501da41c8bSAndreas Gohr    public function copyDir($source, $destination, $exclude = '')
1511da41c8bSAndreas 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
1821da41c8bSAndreas 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     */
1951da41c8bSAndreas Gohr    public function getAllPlugins($all = true)
1961da41c8bSAndreas 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(
2051da41c8bSAndreas Gohr            $plugins,
2061da41c8bSAndreas 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     */
2271da41c8bSAndreas Gohr    public function getAnimalPluginLocalStates($animal)
2281da41c8bSAndreas Gohr    {
229af1c6dd8SAndreas Gohr        if (isset($this->animalPluginState[$animal])) return $this->animalPluginState[$animal];
230af1c6dd8SAndreas Gohr
231af1c6dd8SAndreas Gohr        $localfile = DOKU_FARMDIR . $animal . '/conf/plugins.local.php';
2321da41c8bSAndreas 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     */
2481da41c8bSAndreas Gohr    public function getDefaultPluginStates()
2491da41c8bSAndreas 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
2551da41c8bSAndreas 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     */
2751da41c8bSAndreas Gohr    public function getAnimalPluginRealState($animal)
2761da41c8bSAndreas Gohr    {
2771da41c8bSAndreas Gohr        $info = [];
278af1c6dd8SAndreas Gohr
279af1c6dd8SAndreas Gohr        $defaults = $this->getDefaultPluginStates();
280af1c6dd8SAndreas Gohr        $local = $this->getAnimalPluginLocalStates($animal);
281af1c6dd8SAndreas Gohr
282af1c6dd8SAndreas Gohr        foreach ($defaults as $plugin => $set) {
2831da41c8bSAndreas 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     */
3041da41c8bSAndreas Gohr    public function setPluginState($plugin, $animal, $state)
3051da41c8bSAndreas 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     */
3291da41c8bSAndreas Gohr    public function writePluginConf($plugins, $animal)
3301da41c8bSAndreas 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