xref: /plugin/farmer/helper.php (revision 655fe6c1fa0e8eca0207e883f3a88be96a9a98a2)
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
71ff14bcfaSAnna Dabrowska        $protocol = is_ssl() ? 'https://' : 'http://';
72f64a85f7SAndreas Gohr        if (str_contains($animal, '.')) {
73ff14bcfaSAnna Dabrowska            return $protocol . $animal;
74c4c8e953SAndreas Gohr        } elseif ($config['base']['basedomain']) {
75ff14bcfaSAnna Dabrowska            return $protocol . $animal . '.' . $config['base']['basedomain'];
76c4c8e953SAndreas Gohr        } else {
770336ab2aSAndreas Gohr            return DOKU_URL . '!' . $animal . '/';
78c4c8e953SAndreas Gohr        }
79c4c8e953SAndreas Gohr    }
80c4c8e953SAndreas Gohr
81c4c8e953SAndreas Gohr    /**
82b96c66ccSAndreas Gohr     * List of all animals, i.e. directories within DOKU_FARMDIR without the template.
83b96c66ccSAndreas Gohr     *
84b96c66ccSAndreas Gohr     * @return array
85b96c66ccSAndreas Gohr     */
861da41c8bSAndreas Gohr    public function getAllAnimals()
871da41c8bSAndreas Gohr    {
881da41c8bSAndreas Gohr        $animals = [];
898262a4cbSAndreas Gohr        $list = glob(DOKU_FARMDIR . '*/conf/', GLOB_ONLYDIR);
90b96c66ccSAndreas Gohr        foreach ($list as $path) {
91b96c66ccSAndreas Gohr            $animal = basename(dirname($path));
92b96c66ccSAndreas Gohr            if ($animal == '_animal') continue; // old template
93b96c66ccSAndreas Gohr            $animals[] = $animal;
94b96c66ccSAndreas Gohr        }
95b96c66ccSAndreas Gohr        sort($animals);
96b96c66ccSAndreas Gohr        return $animals;
97b96c66ccSAndreas Gohr    }
98b96c66ccSAndreas Gohr
99b96c66ccSAndreas Gohr    /**
100b96c66ccSAndreas Gohr     * checks wether $path is in under $container
101b96c66ccSAndreas Gohr     *
102dfdaf33eSAndreas Gohr     * Also returns false if $path and $container are the same directory
103dfdaf33eSAndreas Gohr     *
104b96c66ccSAndreas Gohr     * @param string $path
105b96c66ccSAndreas Gohr     * @param string $container
106b96c66ccSAndreas Gohr     * @return bool
107b96c66ccSAndreas Gohr     */
1081da41c8bSAndreas Gohr    public function isInPath($path, $container)
1091da41c8bSAndreas Gohr    {
1100f0a264cSAndreas Gohr        $path = fullpath($path) . '/';
1110f0a264cSAndreas Gohr        $container = fullpath($container) . '/';
1121da41c8bSAndreas Gohr        if ($path === $container) return false;
113f64a85f7SAndreas Gohr        return (str_starts_with($path, $container));
114b96c66ccSAndreas Gohr    }
115b96c66ccSAndreas Gohr
116b96c66ccSAndreas Gohr    /**
117b96c66ccSAndreas Gohr     * Check if the farm is correctly configured for this farmer plugin
118b96c66ccSAndreas Gohr     *
119b96c66ccSAndreas Gohr     * @return bool
120b96c66ccSAndreas Gohr     */
1211da41c8bSAndreas Gohr    public function checkFarmSetup()
1221da41c8bSAndreas Gohr    {
123b96c66ccSAndreas Gohr        return defined('DOKU_FARMDIR') && isset($GLOBALS['FARMCORE']);
124b96c66ccSAndreas Gohr    }
125b96c66ccSAndreas Gohr
12649f2871cSAndreas Gohr    /**
12749f2871cSAndreas Gohr     * @param string $animalname
12849f2871cSAndreas Gohr     *
12949f2871cSAndreas Gohr     * @return bool
13049f2871cSAndreas Gohr     */
1311da41c8bSAndreas Gohr    public function validateAnimalName($animalname)
1321da41c8bSAndreas Gohr    {
133*655fe6c1SAndreas Gohr        return preg_match("/^[a-z0-9]+([\\.\\-][a-z0-9]+)*$/", $animalname) === 1;
13449f2871cSAndreas Gohr    }
135b96c66ccSAndreas Gohr
136b96c66ccSAndreas Gohr    /**
137bc461538SMichael Große     * Copy a file, or recursively copy a folder and its contents. Adapted for DokuWiki.
138bc461538SMichael Große     *
139bc461538SMichael Große     * @todo: needs tests
140bc461538SMichael Große     *
141bc461538SMichael Große     * @author      Aidan Lister <aidan@php.net>
142bc461538SMichael Große     * @author      Michael Große <grosse@cosmocode.de>
143801ebaa1SAndreas Gohr     * @author      Andreas Gohr <gohr@cosmocode.de>
144bc461538SMichael Große     * @link        http://aidanlister.com/2004/04/recursively-copying-directories-in-php/
145bc461538SMichael Große     *
146bc461538SMichael Große     * @param string $source Source path
147bc461538SMichael Große     * @param string $destination Destination path
148801ebaa1SAndreas Gohr     * @param string $exclude Regular expression to exclude files or directories (complete with delimiters)
149bc461538SMichael Große     * @return bool Returns TRUE on success, FALSE on failure
150bc461538SMichael Große     */
1511da41c8bSAndreas Gohr    public function copyDir($source, $destination, $exclude = '')
1521da41c8bSAndreas Gohr    {
153801ebaa1SAndreas Gohr        if ($exclude && preg_match($exclude, $source)) {
154801ebaa1SAndreas Gohr            return true;
155801ebaa1SAndreas Gohr        }
156801ebaa1SAndreas Gohr
157bc461538SMichael Große        if (is_link($source)) {
158bc461538SMichael Große            io_lock($destination);
159bc461538SMichael Große            $result = symlink(readlink($source), $destination);
160bc461538SMichael Große            io_unlock($destination);
161bc461538SMichael Große            return $result;
162bc461538SMichael Große        }
163bc461538SMichael Große
164bc461538SMichael Große        if (is_file($source)) {
165bc461538SMichael Große            io_lock($destination);
166bc461538SMichael Große            $result = copy($source, $destination);
167bc461538SMichael Große            io_unlock($destination);
168bc461538SMichael Große            return $result;
169bc461538SMichael Große        }
170bc461538SMichael Große
171bc461538SMichael Große        if (!is_dir($destination)) {
172bc461538SMichael Große            io_mkdir_p($destination);
173bc461538SMichael Große        }
174bc461538SMichael Große
17549f2871cSAndreas Gohr        $dir = @dir($source);
17649f2871cSAndreas Gohr        if ($dir === false) return false;
177bc461538SMichael Große        while (false !== ($entry = $dir->read())) {
178bc461538SMichael Große            if ($entry == '.' || $entry == '..') {
179bc461538SMichael Große                continue;
180bc461538SMichael Große            }
181bc461538SMichael Große
182bc461538SMichael Große            // recurse into directories
1831da41c8bSAndreas Gohr            $this->copyDir("$source/$entry", "$destination/$entry", $exclude);
184bc461538SMichael Große        }
185bc461538SMichael Große
186bc461538SMichael Große        $dir->close();
187bc461538SMichael Große        return true;
188bc461538SMichael Große    }
189bc461538SMichael Große
19016bbfe4bSMichael Große    /**
19116bbfe4bSMichael Große     * get a list of all Plugins installed in the farmer wiki, regardless whether they are active or not.
19216bbfe4bSMichael Große     *
193af1c6dd8SAndreas Gohr     * @param bool $all get all plugins, even disabled ones
19416bbfe4bSMichael Große     * @return array
19516bbfe4bSMichael Große     */
1961da41c8bSAndreas Gohr    public function getAllPlugins($all = true)
1971da41c8bSAndreas Gohr    {
198af1c6dd8SAndreas Gohr
1993536d644SAndreas Gohr        /** @var Doku_Plugin_Controller $plugin_controller */
2003536d644SAndreas Gohr        global $plugin_controller;
2013536d644SAndreas Gohr
202af1c6dd8SAndreas Gohr        $plugins = $plugin_controller->getList('', $all);
2033536d644SAndreas Gohr
2043536d644SAndreas Gohr        // filter out a few plugins
205af1c6dd8SAndreas Gohr        $plugins = array_filter(
2061da41c8bSAndreas Gohr            $plugins,
2071da41c8bSAndreas Gohr            function ($item) {
2083536d644SAndreas Gohr                if ($item == 'farmer') return false;
2093536d644SAndreas Gohr                if ($item == 'extension') return false;
210f1336aa9SAndreas Gohr                if ($item == 'upgrade') return false;
2113536d644SAndreas Gohr                if ($item == 'testing') return false;
2123536d644SAndreas Gohr                return true;
213af1c6dd8SAndreas Gohr            }
214af1c6dd8SAndreas Gohr        );
2153536d644SAndreas Gohr
2166ec1ad8fSMichael Große        sort($plugins);
2170b96e6d7SMichael Große        return $plugins;
2180b96e6d7SMichael Große    }
2190b96e6d7SMichael Große
22016bbfe4bSMichael Große    /**
221af1c6dd8SAndreas Gohr     * Get the plugin states configured locally in the given animal
22216bbfe4bSMichael Große     *
223af1c6dd8SAndreas Gohr     * Response is cached
224af1c6dd8SAndreas Gohr     *
225af1c6dd8SAndreas Gohr     * @param $animal
226af1c6dd8SAndreas Gohr     * @return array
22716bbfe4bSMichael Große     */
2281da41c8bSAndreas Gohr    public function getAnimalPluginLocalStates($animal)
2291da41c8bSAndreas Gohr    {
230af1c6dd8SAndreas Gohr        if (isset($this->animalPluginState[$animal])) return $this->animalPluginState[$animal];
231af1c6dd8SAndreas Gohr
232af1c6dd8SAndreas Gohr        $localfile = DOKU_FARMDIR . $animal . '/conf/plugins.local.php';
2331da41c8bSAndreas Gohr        $plugins = [];
234af1c6dd8SAndreas Gohr        if (file_exists($localfile)) {
235af1c6dd8SAndreas Gohr            include($localfile);
236fcbe16a4SMichael Große        }
237af1c6dd8SAndreas Gohr
238af1c6dd8SAndreas Gohr        $this->animalPluginState[$animal] = $plugins;
239af1c6dd8SAndreas Gohr        return $plugins;
240fcbe16a4SMichael Große    }
241fcbe16a4SMichael Große
242a0fc814bSMichael Große    /**
243af1c6dd8SAndreas Gohr     * Return the default state plugins would have in animals
244af1c6dd8SAndreas Gohr     *
245af1c6dd8SAndreas Gohr     * Response is cached
246af1c6dd8SAndreas Gohr     *
247af1c6dd8SAndreas Gohr     * @return array
248a0fc814bSMichael Große     */
2491da41c8bSAndreas Gohr    public function getDefaultPluginStates()
2501da41c8bSAndreas Gohr    {
251af1c6dd8SAndreas Gohr        if (!is_null($this->defaultPluginState)) return $this->defaultPluginState;
252af1c6dd8SAndreas Gohr
253af1c6dd8SAndreas Gohr        $farmconf = $this->getConfig();
254af1c6dd8SAndreas Gohr        $all = $this->getAllPlugins();
255af1c6dd8SAndreas Gohr
2561da41c8bSAndreas Gohr        $plugins = [];
257af1c6dd8SAndreas Gohr        foreach ($all as $one) {
258af1c6dd8SAndreas Gohr            if ($farmconf['inherit']['plugins']) {
259af1c6dd8SAndreas Gohr                $plugins[$one] = !plugin_isdisabled($one);
260fcbe16a4SMichael Große            } else {
261af1c6dd8SAndreas Gohr                $plugins[$one] = true; // default state is enabled
262fcbe16a4SMichael Große            }
263114a05a7SAndreas Gohr        }
264af1c6dd8SAndreas Gohr
265511d09feSAndreas Gohr        ksort($plugins);
266af1c6dd8SAndreas Gohr        $this->defaultPluginState = $plugins;
267af1c6dd8SAndreas Gohr        return $plugins;
268af1c6dd8SAndreas Gohr    }
269af1c6dd8SAndreas Gohr
270af1c6dd8SAndreas Gohr    /**
271af1c6dd8SAndreas Gohr     * Return a structure giving detailed info about the state of all plugins in an animal
272af1c6dd8SAndreas Gohr     *
273af1c6dd8SAndreas Gohr     * @param $animal
274af1c6dd8SAndreas Gohr     * @return array
275af1c6dd8SAndreas Gohr     */
2761da41c8bSAndreas Gohr    public function getAnimalPluginRealState($animal)
2771da41c8bSAndreas Gohr    {
2781da41c8bSAndreas Gohr        $info = [];
279af1c6dd8SAndreas Gohr
280af1c6dd8SAndreas Gohr        $defaults = $this->getDefaultPluginStates();
281af1c6dd8SAndreas Gohr        $local = $this->getAnimalPluginLocalStates($animal);
282af1c6dd8SAndreas Gohr
283af1c6dd8SAndreas Gohr        foreach ($defaults as $plugin => $set) {
2841da41c8bSAndreas Gohr            $current = ['name' => $plugin, 'default' => $set, 'actual' => $set, 'isdefault' => true];
285af1c6dd8SAndreas Gohr
286af1c6dd8SAndreas Gohr            if (isset($local[$plugin])) {
287af1c6dd8SAndreas Gohr                $current['actual'] = (bool) $local[$plugin];
288af1c6dd8SAndreas Gohr                $current['isdefault'] = false;
289af1c6dd8SAndreas Gohr            }
290af1c6dd8SAndreas Gohr
291511d09feSAndreas Gohr            $info[$plugin] = $current;
292af1c6dd8SAndreas Gohr        }
293af1c6dd8SAndreas Gohr
294511d09feSAndreas Gohr        ksort($info);
295af1c6dd8SAndreas Gohr        return $info;
296af1c6dd8SAndreas Gohr    }
297af1c6dd8SAndreas Gohr
298af1c6dd8SAndreas Gohr    /**
299af1c6dd8SAndreas Gohr     * Set the state of a plugin in an animal
300af1c6dd8SAndreas Gohr     *
301af1c6dd8SAndreas Gohr     * @param string $plugin
302af1c6dd8SAndreas Gohr     * @param string $animal
303af1c6dd8SAndreas Gohr     * @param int $state -1 = default, 1 = enabled, 0 = disabled
304af1c6dd8SAndreas Gohr     */
3051da41c8bSAndreas Gohr    public function setPluginState($plugin, $animal, $state)
3061da41c8bSAndreas Gohr    {
307af1c6dd8SAndreas Gohr        $state = (int) $state;
308af1c6dd8SAndreas Gohr
309af1c6dd8SAndreas Gohr        $plugins = $this->getAnimalPluginLocalStates($animal);
310af1c6dd8SAndreas Gohr        if ($state < 0) {
311af1c6dd8SAndreas Gohr            if (isset($plugins[$plugin])) unset($plugins[$plugin]);
312af1c6dd8SAndreas Gohr        } else {
313af1c6dd8SAndreas Gohr            $plugins[$plugin] = $state;
314af1c6dd8SAndreas Gohr        }
315af1c6dd8SAndreas Gohr
316fcbe16a4SMichael Große        $this->writePluginConf($plugins, $animal);
317511d09feSAndreas Gohr
318511d09feSAndreas Gohr        // clear state cache
319511d09feSAndreas Gohr        if (isset($this->animalPluginState[$animal])) unset($this->animalPluginState[$animal]);
320fcbe16a4SMichael Große    }
321fcbe16a4SMichael Große
32216bbfe4bSMichael Große    /**
32316bbfe4bSMichael Große     * Write the list of (deactivated) plugins as plugin configuration of an animal to file
32416bbfe4bSMichael Große     *
325af1c6dd8SAndreas Gohr     * updates the plugin state cache
326af1c6dd8SAndreas Gohr     *
32716bbfe4bSMichael Große     * @param array $plugins associative array with the key being the plugin name and the value 0 or 1
32816bbfe4bSMichael Große     * @param string $animal Directory of the animal within DOKU_FARMDIR
32916bbfe4bSMichael Große     */
3301da41c8bSAndreas Gohr    public function writePluginConf($plugins, $animal)
3311da41c8bSAndreas Gohr    {
332af1c6dd8SAndreas Gohr        $pluginConf = '<?php' . "\n# plugins enabled and disabled by the farmer plugin\n";
333fcbe16a4SMichael Große        foreach ($plugins as $plugin => $status) {
334af1c6dd8SAndreas Gohr            $pluginConf .= '$plugins[\'' . $plugin . '\'] = ' . $status . ";\n";
335fcbe16a4SMichael Große        }
336fcbe16a4SMichael Große        io_saveFile(DOKU_FARMDIR . $animal . '/conf/plugins.local.php', $pluginConf);
337fcbe16a4SMichael Große        touch(DOKU_FARMDIR . $animal . '/conf/local.php');
338af1c6dd8SAndreas Gohr
3393062cd8eSAndreas Gohr        if (function_exists('opcache_invalidate')) {
3403062cd8eSAndreas Gohr            opcache_invalidate(DOKU_FARMDIR . $animal . '/conf/plugins.local.php');
3413062cd8eSAndreas Gohr            opcache_invalidate(DOKU_FARMDIR . $animal . '/conf/local.php');
3423062cd8eSAndreas Gohr        }
3433062cd8eSAndreas Gohr
344af1c6dd8SAndreas Gohr        $this->animalPluginState[$animal] = $plugins;
345fcbe16a4SMichael Große    }
346bc461538SMichael Große}
347