xref: /plugin/farmer/DokuWikiFarmCore.php (revision f64a85f7ef8593a387609801c962a904fe407d36)
1da0ae2c0SAndreas Gohr<?php
2da0ae2c0SAndreas Gohr
31da41c8bSAndreas Gohr// phpcs:disable PSR1.Files.SideEffects
4*c468f8caSAndreas Gohr
5a646d519SAndreas Gohr/**
6a646d519SAndreas Gohr * Core Manager for the Farm functionality
7a646d519SAndreas Gohr *
8a646d519SAndreas Gohr * This class is initialized before any other DokuWiki code runs. Therefore it is
9a646d519SAndreas Gohr * completely selfcontained and does not use any of DokuWiki's utility functions.
10a646d519SAndreas Gohr *
11b96c66ccSAndreas Gohr * It's registered as a global $FARMCORE variable but you should not interact with
12b96c66ccSAndreas Gohr * it directly. Instead use the Farmer plugin's helper component.
130a5d2da2SAndreas Gohr *
140a5d2da2SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
150a5d2da2SAndreas Gohr * @author  Andreas Gohr <gohr@cosmocode.de>
16a646d519SAndreas Gohr */
171da41c8bSAndreas Gohrclass DokuWikiFarmCore
181da41c8bSAndreas Gohr{
19da0ae2c0SAndreas Gohr    /**
20da0ae2c0SAndreas Gohr     * @var array The default config - changed by loadConfig
21da0ae2c0SAndreas Gohr     */
221da41c8bSAndreas Gohr    protected $config = [
231da41c8bSAndreas Gohr        'base' => [
24a646d519SAndreas Gohr            'farmdir' => '',
25c4c8e953SAndreas Gohr            'farmhost' => '',
261da41c8bSAndreas Gohr            'basedomain' => ''
271da41c8bSAndreas Gohr        ],
281da41c8bSAndreas Gohr        'notfound' => [
29da0ae2c0SAndreas Gohr            'show' => 'farmer',
30da0ae2c0SAndreas Gohr            'url' => ''
311da41c8bSAndreas Gohr        ],
321da41c8bSAndreas Gohr        'inherit' => [
33da0ae2c0SAndreas Gohr            'main' => 1,
34da0ae2c0SAndreas Gohr            'acronyms' => 1,
35da0ae2c0SAndreas Gohr            'entities' => 1,
36da0ae2c0SAndreas Gohr            'interwiki' => 1,
37da0ae2c0SAndreas Gohr            'license' => 1,
38da0ae2c0SAndreas Gohr            'mime' => 1,
39da0ae2c0SAndreas Gohr            'scheme' => 1,
40da0ae2c0SAndreas Gohr            'smileys' => 1,
41da0ae2c0SAndreas Gohr            'wordblock' => 1,
421272da0cSAndreas Gohr            'users' => 0,
43af1c6dd8SAndreas Gohr            'plugins' => 0,
44da0ae2c0SAndreas Gohr            'userstyle' => 0,
4505ea7625SAnna Dabrowska            'userscript' => 0,
463cee9885SAnna Dabrowska            'styleini' => 0
471da41c8bSAndreas Gohr        ]
481da41c8bSAndreas Gohr    ];
49da0ae2c0SAndreas Gohr
50a646d519SAndreas Gohr    /** @var string|false The current animal, false for farmer */
51a646d519SAndreas Gohr    protected $animal = false;
52a646d519SAndreas Gohr    /** @var bool true if an animal was requested but was not found */
53a646d519SAndreas Gohr    protected $notfound = false;
54a646d519SAndreas Gohr    /** @var bool true if the current animal was requested by host */
55a646d519SAndreas Gohr    protected $hostbased = false;
56a646d519SAndreas Gohr
57da0ae2c0SAndreas Gohr    /**
58da0ae2c0SAndreas Gohr     * DokuWikiFarmCore constructor.
59da0ae2c0SAndreas Gohr     *
60a646d519SAndreas Gohr     * This initializes the whole farm by loading the configuration and setting
61a646d519SAndreas Gohr     * DOKU_CONF depending on the requested animal
62da0ae2c0SAndreas Gohr     */
631da41c8bSAndreas Gohr    public function __construct()
641da41c8bSAndreas Gohr    {
65da0ae2c0SAndreas Gohr        $this->loadConfig();
66a646d519SAndreas Gohr        if ($this->config['base']['farmdir'] === '') return; // farm setup not complete
678262a4cbSAndreas Gohr        $this->config['base']['farmdir'] = rtrim($this->config['base']['farmdir'], '/') . '/'; // trailing slash always
6849f2871cSAndreas Gohr        define('DOKU_FARMDIR', $this->config['base']['farmdir']);
69a646d519SAndreas Gohr
70a646d519SAndreas Gohr        // animal?
71a646d519SAndreas Gohr        $this->detectAnimal();
72a646d519SAndreas Gohr
73a646d519SAndreas Gohr        // setup defines
74a646d519SAndreas Gohr        define('DOKU_FARM_ANIMAL', $this->animal);
75a646d519SAndreas Gohr        if ($this->animal) {
768262a4cbSAndreas Gohr            define('DOKU_CONF', DOKU_FARMDIR . $this->animal . '/conf/');
77a646d519SAndreas Gohr        } else {
78a646d519SAndreas Gohr            define('DOKU_CONF', DOKU_INC . '/conf/');
79a646d519SAndreas Gohr        }
80a646d519SAndreas Gohr
81a646d519SAndreas Gohr        $this->setupCascade();
82a646d519SAndreas Gohr        $this->adjustCascade();
83da0ae2c0SAndreas Gohr    }
84da0ae2c0SAndreas Gohr
85da0ae2c0SAndreas Gohr    /**
86da0ae2c0SAndreas Gohr     * @return array the current farm configuration
87da0ae2c0SAndreas Gohr     */
881da41c8bSAndreas Gohr    public function getConfig()
891da41c8bSAndreas Gohr    {
90da0ae2c0SAndreas Gohr        return $this->config;
91da0ae2c0SAndreas Gohr    }
92da0ae2c0SAndreas Gohr
93da0ae2c0SAndreas Gohr    /**
94a646d519SAndreas Gohr     * @return false|string
95a646d519SAndreas Gohr     */
961da41c8bSAndreas Gohr    public function getAnimal()
971da41c8bSAndreas Gohr    {
98a646d519SAndreas Gohr        return $this->animal;
99a646d519SAndreas Gohr    }
100a646d519SAndreas Gohr
101a646d519SAndreas Gohr    /**
102a646d519SAndreas Gohr     * @return boolean
103a646d519SAndreas Gohr     */
1041da41c8bSAndreas Gohr    public function isHostbased()
1051da41c8bSAndreas Gohr    {
106a646d519SAndreas Gohr        return $this->hostbased;
107a646d519SAndreas Gohr    }
108a646d519SAndreas Gohr
109a646d519SAndreas Gohr    /**
110a646d519SAndreas Gohr     * @return boolean
111a646d519SAndreas Gohr     */
1121da41c8bSAndreas Gohr    public function wasNotfound()
1131da41c8bSAndreas Gohr    {
114a646d519SAndreas Gohr        return $this->notfound;
115a646d519SAndreas Gohr    }
116a646d519SAndreas Gohr
117a646d519SAndreas Gohr    /**
118b330074aSAndreas Gohr     * @return string
119b330074aSAndreas Gohr     */
1201da41c8bSAndreas Gohr    public function getAnimalDataDir()
1211da41c8bSAndreas Gohr    {
1228262a4cbSAndreas Gohr        return DOKU_FARMDIR . $this->getAnimal() . '/data/';
123b330074aSAndreas Gohr    }
124b330074aSAndreas Gohr
125b330074aSAndreas Gohr    /**
126b330074aSAndreas Gohr     * @return string
127b330074aSAndreas Gohr     */
1281da41c8bSAndreas Gohr    public function getAnimalBaseDir()
1291da41c8bSAndreas Gohr    {
13004dc6bd5SSzymon Olewniczak        if ($this->isHostbased()) return '/';
131b330074aSAndreas Gohr        return getBaseURL() . '!' . $this->getAnimal();
132b330074aSAndreas Gohr    }
133b330074aSAndreas Gohr
134b330074aSAndreas Gohr    /**
135*c468f8caSAndreas Gohr     * Set the animal
136a646d519SAndreas Gohr     *
137*c468f8caSAndreas Gohr     * Checks if the animal exists and is a valid directory name.
138a646d519SAndreas Gohr     *
139*c468f8caSAndreas Gohr     * @param mixed $animal the animal name
140*c468f8caSAndreas Gohr     * @return bool returns true if the animal was set successfully, false otherwise
141a646d519SAndreas Gohr     */
142*c468f8caSAndreas Gohr    protected function setAnimal($animal)
1431da41c8bSAndreas Gohr    {
144a646d519SAndreas Gohr        $farmdir = $this->config['base']['farmdir'];
145a646d519SAndreas Gohr
146*c468f8caSAndreas Gohr        // invalid animal stuff is always a not found
147a646d519SAndreas Gohr        if (!is_string($animal) || strpbrk($animal, '\\/') !== false) {
148a646d519SAndreas Gohr            $this->notfound = true;
149*c468f8caSAndreas Gohr            return false;
150*c468f8caSAndreas Gohr        }
151a646d519SAndreas Gohr        $animal = strtolower($animal);
152a646d519SAndreas Gohr
153a646d519SAndreas Gohr        // check if animal exists
154a646d519SAndreas Gohr        if (is_dir("$farmdir/$animal/conf")) {
155a646d519SAndreas Gohr            $this->animal = $animal;
156*c468f8caSAndreas Gohr            $this->notfound = false;
157*c468f8caSAndreas Gohr            return true;
158a646d519SAndreas Gohr        } else {
159a646d519SAndreas Gohr            $this->notfound = true;
160*c468f8caSAndreas Gohr            return false;
161a646d519SAndreas Gohr        }
162a646d519SAndreas Gohr    }
163a646d519SAndreas Gohr
164*c468f8caSAndreas Gohr    /**
165*c468f8caSAndreas Gohr     * Detect the animal from the given query string
166*c468f8caSAndreas Gohr     *
167*c468f8caSAndreas Gohr     * This removes the animal parameter from the given string and sets the animal
168*c468f8caSAndreas Gohr     *
169*c468f8caSAndreas Gohr     * @param string $queryString The query string to extract the animal from, will be modified
170*c468f8caSAndreas Gohr     * @return bool true if the animal was set successfully, false otherwise
171*c468f8caSAndreas Gohr     */
172*c468f8caSAndreas Gohr    protected function detectAnimalFromQueryString(string &$queryString): bool
173*c468f8caSAndreas Gohr    {
174*c468f8caSAndreas Gohr        $params = [];
175*c468f8caSAndreas Gohr        parse_str($queryString, $params);
176*c468f8caSAndreas Gohr        if (!isset($params['animal'])) return false;
177*c468f8caSAndreas Gohr        $animal = $params['animal'];
178*c468f8caSAndreas Gohr        unset($params['animal']);
179*c468f8caSAndreas Gohr        $queryString = http_build_query($params);
180*c468f8caSAndreas Gohr
181*c468f8caSAndreas Gohr        $this->hostbased = false;
182*c468f8caSAndreas Gohr        return $this->setAnimal($animal);
183*c468f8caSAndreas Gohr    }
184*c468f8caSAndreas Gohr
185*c468f8caSAndreas Gohr    /**
186*c468f8caSAndreas Gohr     * Detect the animal from the bang path
187*c468f8caSAndreas Gohr     *
188*c468f8caSAndreas Gohr     * This is used to detect the animal from a bang path like `/!animal/my:page` or '/dokuwiki/!animal/my:page'.
189*c468f8caSAndreas Gohr     *
190*c468f8caSAndreas Gohr     * @param string $path The bang path to extract the animal from
191*c468f8caSAndreas Gohr     * @return bool true if the animal was set successfully, false otherwise
192*c468f8caSAndreas Gohr     */
193*c468f8caSAndreas Gohr    protected function detectAnimalFromBangPath(string $path): bool
194*c468f8caSAndreas Gohr    {
195*c468f8caSAndreas Gohr        $bangregex = '#^(/(?:[^/]*/)*)!([^/]+)/#';
196*c468f8caSAndreas Gohr        if (preg_match($bangregex, $path, $matches)) {
197*c468f8caSAndreas Gohr            // found a bang path
198*c468f8caSAndreas Gohr            $animal = $matches[2];
199*c468f8caSAndreas Gohr
200*c468f8caSAndreas Gohr            $this->hostbased = false;
201*c468f8caSAndreas Gohr            return $this->setAnimal($animal);
202*c468f8caSAndreas Gohr        }
203*c468f8caSAndreas Gohr        return false;
204*c468f8caSAndreas Gohr    }
205*c468f8caSAndreas Gohr
206*c468f8caSAndreas Gohr    /**
207*c468f8caSAndreas Gohr     * Detect the animal from the host name
208*c468f8caSAndreas Gohr     *
209*c468f8caSAndreas Gohr     * @param string $host The hostname
210*c468f8caSAndreas Gohr     * @return bool true if the animal was set successfully, false otherwise
211*c468f8caSAndreas Gohr     */
212*c468f8caSAndreas Gohr    protected function detectAnimalFromHostName(string $host): bool
213*c468f8caSAndreas Gohr    {
214*c468f8caSAndreas Gohr        $possible = $this->getAnimalNamesForHost($host);
215*c468f8caSAndreas Gohr        foreach ($possible as $animal) {
216*c468f8caSAndreas Gohr            if ($this->setAnimal($animal)) {
217*c468f8caSAndreas Gohr                $this->hostbased = true;
218*c468f8caSAndreas Gohr                return true;
219*c468f8caSAndreas Gohr            }
220*c468f8caSAndreas Gohr        }
221*c468f8caSAndreas Gohr        return false;
222*c468f8caSAndreas Gohr    }
223*c468f8caSAndreas Gohr
224*c468f8caSAndreas Gohr    /**
225*c468f8caSAndreas Gohr     * Detect the current animal
226*c468f8caSAndreas Gohr     *
227*c468f8caSAndreas Gohr     * Sets internal members $animal, $notfound and $hostbased
228*c468f8caSAndreas Gohr     *
229*c468f8caSAndreas Gohr     * This borrows form DokuWiki's inc/farm.php but does not support a default conf dir
230*c468f8caSAndreas Gohr     *
231*c468f8caSAndreas Gohr     * @params string|null $sapi the SAPI to use. Only changed during testing
232*c468f8caSAndreas Gohr     */
233*c468f8caSAndreas Gohr    protected function detectAnimal($sapi = null)
234*c468f8caSAndreas Gohr    {
235*c468f8caSAndreas Gohr        $sapi = $sapi ?: PHP_SAPI;
236*c468f8caSAndreas Gohr        $farmhost = $this->config['base']['farmhost'];
237*c468f8caSAndreas Gohr
238*c468f8caSAndreas Gohr        if ('cli' == $sapi) {
239*c468f8caSAndreas Gohr            if (!isset($_SERVER['animal'])) return; // no animal parameter given - we're the farmer
240*c468f8caSAndreas Gohr
241*c468f8caSAndreas Gohr            if (preg_match('#^https?://#i', $_SERVER['animal'])) {
242*c468f8caSAndreas Gohr                // CLI animal parameter is a URL
243*c468f8caSAndreas Gohr                $urlparts = parse_url($_SERVER['animal']);
244*c468f8caSAndreas Gohr                $urlparts['query'] ??= '';
245*c468f8caSAndreas Gohr
246*c468f8caSAndreas Gohr                // detect the animal from the URL
247*c468f8caSAndreas Gohr                $this->detectAnimalFromQueryString($urlparts['query']) ||
248*c468f8caSAndreas Gohr                $this->detectAnimalFromBangPath($urlparts['path']) ||
249*c468f8caSAndreas Gohr                $this->detectAnimalFromHostName($urlparts['host']);
250*c468f8caSAndreas Gohr
251*c468f8caSAndreas Gohr                // fake baseurl etc.
252*c468f8caSAndreas Gohr                $this->injectServerEnvironment($urlparts);
253*c468f8caSAndreas Gohr            } else {
254*c468f8caSAndreas Gohr                // CLI animal parameter is just a name
255*c468f8caSAndreas Gohr                $this->setAnimal(strtolower($_SERVER['animal']));
256*c468f8caSAndreas Gohr            }
257*c468f8caSAndreas Gohr        } else {
258*c468f8caSAndreas Gohr            // an animal url parameter has been set
259*c468f8caSAndreas Gohr            if (isset($_GET['animal'])) {
260*c468f8caSAndreas Gohr                $this->detectAnimalFromQueryString($_SERVER['QUERY_STRING']);
261*c468f8caSAndreas Gohr                unset($_GET['animal']);
262*c468f8caSAndreas Gohr                return;
263*c468f8caSAndreas Gohr            }
264*c468f8caSAndreas Gohr
26536282384SAndreas Gohr            // no host - no host based setup. if we're still here then it's the farmer
266*c468f8caSAndreas Gohr            if (empty($_SERVER['HTTP_HOST'])) return;
26736282384SAndreas Gohr
268a646d519SAndreas Gohr            // is this the farmer?
269a646d519SAndreas Gohr            if (strtolower($_SERVER['HTTP_HOST']) == $farmhost) {
270a646d519SAndreas Gohr                return;
271a646d519SAndreas Gohr            }
272a646d519SAndreas Gohr
273*c468f8caSAndreas Gohr            // we're in host based mode now
274a646d519SAndreas Gohr            $this->hostbased = true;
275*c468f8caSAndreas Gohr
276*c468f8caSAndreas Gohr            // we should get an animal now
277*c468f8caSAndreas Gohr            if (!$this->detectAnimalFromHostName($_SERVER['HTTP_HOST'])) {
278*c468f8caSAndreas Gohr                $this->notfound = true;
279*c468f8caSAndreas Gohr            }
280a646d519SAndreas Gohr        }
281a646d519SAndreas Gohr    }
282a646d519SAndreas Gohr
283*c468f8caSAndreas Gohr    /**
284*c468f8caSAndreas Gohr     * Create Server environment variables for the current animal
285*c468f8caSAndreas Gohr     *
286*c468f8caSAndreas Gohr     * This is called when the animal is initialized on the command line using a full URL.
287*c468f8caSAndreas Gohr     * Since the initialization is running before any configuration is loaded, we instead
288*c468f8caSAndreas Gohr     * set the $_SERVER variables that will later be used to autodetect the base URL. This
289*c468f8caSAndreas Gohr     * way a manually set base URL will still take precedence.
290*c468f8caSAndreas Gohr     *
291*c468f8caSAndreas Gohr     * @param array $urlparts A parse_url() result array
292*c468f8caSAndreas Gohr     * @return void
293*c468f8caSAndreas Gohr     * @see is_ssl()
294*c468f8caSAndreas Gohr     * @see getBaseURL()
295*c468f8caSAndreas Gohr     */
296*c468f8caSAndreas Gohr    protected function injectServerEnvironment(array $urlparts)
297*c468f8caSAndreas Gohr    {
298*c468f8caSAndreas Gohr        // prepare data for DOKU_REL
299*c468f8caSAndreas Gohr        $path = $urlparts['path'] ?? '/';
300*c468f8caSAndreas Gohr        if (($bangpos = strpos($path, '!')) !== false) {
301*c468f8caSAndreas Gohr            // strip from the bang path
302*c468f8caSAndreas Gohr            $path = substr($path, 0, $bangpos);
303*c468f8caSAndreas Gohr        }
304*c468f8caSAndreas Gohr        if (!str_ends_with($path, '.php')) {
305*c468f8caSAndreas Gohr            // make sure we have a script name
306*c468f8caSAndreas Gohr            $path = rtrim($path, '/') . '/doku.php';
307*c468f8caSAndreas Gohr        }
308*c468f8caSAndreas Gohr        $_SERVER['SCRIPT_NAME'] = $path;
309*c468f8caSAndreas Gohr
310*c468f8caSAndreas Gohr        // prepare data for is_ssl()
311*c468f8caSAndreas Gohr        if (($urlparts['scheme'] ?? '') === 'https') {
312*c468f8caSAndreas Gohr            $_SERVER['HTTPS'] = 'on';
313*c468f8caSAndreas Gohr        } else {
314*c468f8caSAndreas Gohr            $_SERVER['HTTPS'] = 'off';
315*c468f8caSAndreas Gohr        }
316*c468f8caSAndreas Gohr
317*c468f8caSAndreas Gohr        // prepare data for DOKU_URL
318*c468f8caSAndreas Gohr        $_SERVER['HTTP_HOST'] = $urlparts['host'] ?? '';
319*c468f8caSAndreas Gohr        if (isset($urlparts['port'])) {
320*c468f8caSAndreas Gohr            $_SERVER['HTTP_HOST'] .= ':' . $urlparts['port'];
321*c468f8caSAndreas Gohr        }
322a646d519SAndreas Gohr    }
323a646d519SAndreas Gohr
324a646d519SAndreas Gohr    /**
32585becf1bSAndreas Gohr     * Return a list of possible animal names for the given host
32685becf1bSAndreas Gohr     *
32785becf1bSAndreas Gohr     * @param string $host the HTTP_HOST header
32885becf1bSAndreas Gohr     * @return array
32985becf1bSAndreas Gohr     */
3301da41c8bSAndreas Gohr    protected function getAnimalNamesForHost($host)
3311da41c8bSAndreas Gohr    {
3321da41c8bSAndreas Gohr        $animals = [];
33385becf1bSAndreas Gohr        $parts = explode('.', implode('.', explode(':', rtrim($host, '.'))));
33485becf1bSAndreas Gohr        for ($j = count($parts); $j > 0; $j--) {
33585becf1bSAndreas Gohr            // strip from the end
33685becf1bSAndreas Gohr            $animals[] = implode('.', array_slice($parts, 0, $j));
33785becf1bSAndreas Gohr            // strip from the end without host part
33885becf1bSAndreas Gohr            $animals[] = implode('.', array_slice($parts, 1, $j));
33985becf1bSAndreas Gohr        }
34085becf1bSAndreas Gohr        $animals = array_unique($animals);
34185becf1bSAndreas Gohr        $animals = array_filter($animals);
3420a5d2da2SAndreas Gohr        usort(
343bfecda9bSAndreas Gohr            $animals,
344bfecda9bSAndreas Gohr            // compare by length, then alphabet
345bfecda9bSAndreas Gohr            function ($a, $b) {
346bfecda9bSAndreas Gohr                $ret = strlen($b) - strlen($a);
347bfecda9bSAndreas Gohr                if ($ret != 0) return $ret;
348a2a8c90bSAndreas Gohr                return $a <=> $b;
3490a5d2da2SAndreas Gohr            }
3500a5d2da2SAndreas Gohr        );
35185becf1bSAndreas Gohr        return $animals;
35285becf1bSAndreas Gohr    }
35385becf1bSAndreas Gohr
35485becf1bSAndreas Gohr    /**
355a646d519SAndreas Gohr     * This sets up the default farming config cascade
356a646d519SAndreas Gohr     */
3571da41c8bSAndreas Gohr    protected function setupCascade()
3581da41c8bSAndreas Gohr    {
359a646d519SAndreas Gohr        global $config_cascade;
3601da41c8bSAndreas Gohr        $config_cascade = [
3611da41c8bSAndreas Gohr            'main' => [
3621da41c8bSAndreas Gohr                'default' => [DOKU_INC . 'conf/dokuwiki.php'],
3631da41c8bSAndreas Gohr                'local' => [DOKU_CONF . 'local.php'],
3641da41c8bSAndreas Gohr                'protected' => [DOKU_CONF . 'local.protected.php']
3651da41c8bSAndreas Gohr            ],
3661da41c8bSAndreas Gohr            'acronyms' => [
3671da41c8bSAndreas Gohr                'default' => [DOKU_INC . 'conf/acronyms.conf'],
3681da41c8bSAndreas Gohr                'local' => [DOKU_CONF . 'acronyms.local.conf']
3691da41c8bSAndreas Gohr            ],
3701da41c8bSAndreas Gohr            'entities' => [
3711da41c8bSAndreas Gohr                'default' => [DOKU_INC . 'conf/entities.conf'],
3721da41c8bSAndreas Gohr                'local' => [DOKU_CONF . 'entities.local.conf']
3731da41c8bSAndreas Gohr            ],
3741da41c8bSAndreas Gohr            'interwiki' => [
3751da41c8bSAndreas Gohr                'default' => [DOKU_INC . 'conf/interwiki.conf'],
3761da41c8bSAndreas Gohr                'local' => [DOKU_CONF . 'interwiki.local.conf']
3771da41c8bSAndreas Gohr            ],
3781da41c8bSAndreas Gohr            'license' => [
3791da41c8bSAndreas Gohr                'default' => [DOKU_INC . 'conf/license.php'],
3801da41c8bSAndreas Gohr                'local' => [DOKU_CONF . 'license.local.php']
3811da41c8bSAndreas Gohr            ],
3821da41c8bSAndreas Gohr            'manifest' => [
3831da41c8bSAndreas Gohr                'default' => [DOKU_INC . 'conf/manifest.json'],
3841da41c8bSAndreas Gohr                'local' => [DOKU_CONF . 'manifest.local.json']
3851da41c8bSAndreas Gohr            ],
3861da41c8bSAndreas Gohr            'mediameta' => [
3871da41c8bSAndreas Gohr                'default' => [DOKU_INC . 'conf/mediameta.php'],
3881da41c8bSAndreas Gohr                'local' => [DOKU_CONF . 'mediameta.local.php']
3891da41c8bSAndreas Gohr            ],
3901da41c8bSAndreas Gohr            'mime' => [
3911da41c8bSAndreas Gohr                'default' => [DOKU_INC . 'conf/mime.conf'],
3921da41c8bSAndreas Gohr                'local' => [DOKU_CONF . 'mime.local.conf']
3931da41c8bSAndreas Gohr            ],
3941da41c8bSAndreas Gohr            'scheme' => [
3951da41c8bSAndreas Gohr                'default' => [DOKU_INC . 'conf/scheme.conf'],
3961da41c8bSAndreas Gohr                'local' => [DOKU_CONF . 'scheme.local.conf']
3971da41c8bSAndreas Gohr            ],
3981da41c8bSAndreas Gohr            'smileys' => [
3991da41c8bSAndreas Gohr                'default' => [DOKU_INC . 'conf/smileys.conf'],
4001da41c8bSAndreas Gohr                'local' => [DOKU_CONF . 'smileys.local.conf']
4011da41c8bSAndreas Gohr            ],
4021da41c8bSAndreas Gohr            'wordblock' => [
4031da41c8bSAndreas Gohr                'default' => [DOKU_INC . 'conf/wordblock.conf'],
4041da41c8bSAndreas Gohr                'local' => [DOKU_CONF . 'wordblock.local.conf']
4051da41c8bSAndreas Gohr            ],
4061da41c8bSAndreas Gohr            'acl' => [
4071da41c8bSAndreas Gohr                'default' => DOKU_CONF . 'acl.auth.php'
4081da41c8bSAndreas Gohr            ],
4091da41c8bSAndreas Gohr            'plainauth.users' => [
4101da41c8bSAndreas Gohr                'default' => DOKU_CONF . 'users.auth.php'
4111da41c8bSAndreas Gohr            ],
4121da41c8bSAndreas Gohr            'plugins' => [
4131da41c8bSAndreas Gohr                'default' => [DOKU_INC . 'conf/plugins.php'],
4141da41c8bSAndreas Gohr                'local' => [DOKU_CONF . 'plugins.local.php'],
4151da41c8bSAndreas Gohr                'protected' => [
416a646d519SAndreas Gohr                    DOKU_INC . 'conf/plugins.required.php',
4171da41c8bSAndreas Gohr                    DOKU_CONF . 'plugins.protected.php'
4181da41c8bSAndreas Gohr                ]
4191da41c8bSAndreas Gohr            ],
4201da41c8bSAndreas Gohr            'userstyle' => [
4211da41c8bSAndreas Gohr                'screen' => [
4221da41c8bSAndreas Gohr                    DOKU_CONF . 'userstyle.css',
4231da41c8bSAndreas Gohr                    DOKU_CONF . 'userstyle.less'
4241da41c8bSAndreas Gohr                ],
4251da41c8bSAndreas Gohr                'print' => [
4261da41c8bSAndreas Gohr                    DOKU_CONF . 'userprint.css',
4271da41c8bSAndreas Gohr                    DOKU_CONF . 'userprint.less'
4281da41c8bSAndreas Gohr                ],
4291da41c8bSAndreas Gohr                'feed' => [
4301da41c8bSAndreas Gohr                    DOKU_CONF . 'userfeed.css',
4311da41c8bSAndreas Gohr                    DOKU_CONF . 'userfeed.less'
4321da41c8bSAndreas Gohr                ],
4331da41c8bSAndreas Gohr                'all' => [
4341da41c8bSAndreas Gohr                    DOKU_CONF . 'userall.css',
4351da41c8bSAndreas Gohr                    DOKU_CONF . 'userall.less'
4361da41c8bSAndreas Gohr                ]
4371da41c8bSAndreas Gohr            ],
4381da41c8bSAndreas Gohr            'userscript' => [
4391da41c8bSAndreas Gohr                'default' => [DOKU_CONF . 'userscript.js']
4401da41c8bSAndreas Gohr            ],
4411da41c8bSAndreas Gohr            'styleini' => [
4421da41c8bSAndreas Gohr                'default' => [DOKU_INC . 'lib/tpl/%TEMPLATE%/' . 'style.ini'],
4431da41c8bSAndreas Gohr                'local' => [DOKU_CONF . 'tpl/%TEMPLATE%/' . 'style.ini']
4441da41c8bSAndreas Gohr            ]
4451da41c8bSAndreas Gohr        ];
446a646d519SAndreas Gohr    }
447a646d519SAndreas Gohr
448a646d519SAndreas Gohr    /**
449a646d519SAndreas Gohr     * This adds additional files to the config cascade based on the inheritence settings
450a646d519SAndreas Gohr     *
451a646d519SAndreas Gohr     * These are only added for animals, not the farmer
452a646d519SAndreas Gohr     */
4531da41c8bSAndreas Gohr    protected function adjustCascade()
4541da41c8bSAndreas Gohr    {
455b330074aSAndreas Gohr        // nothing to do when on the farmer:
456b330074aSAndreas Gohr        if (!$this->animal) return;
457a646d519SAndreas Gohr
458b330074aSAndreas Gohr        global $config_cascade;
459a646d519SAndreas Gohr        foreach ($this->config['inherit'] as $key => $val) {
460a646d519SAndreas Gohr            if (!$val) continue;
461a646d519SAndreas Gohr
462a646d519SAndreas Gohr            // prepare what is to append or prepend
4631da41c8bSAndreas Gohr            $append = [];
4641da41c8bSAndreas Gohr            $prepend = [];
465a646d519SAndreas Gohr            if ($key == 'main') {
4664b1aad07Satisne                $prepend = [
4674b1aad07Satisne                    'protected' => [DOKU_INC . 'conf/local.protected.php']
4684b1aad07Satisne                ];
4691da41c8bSAndreas Gohr                $append = [
4701da41c8bSAndreas Gohr                    'default' => [DOKU_INC . 'conf/local.php'],
4711da41c8bSAndreas Gohr                    'protected' => [DOKU_INC . 'lib/plugins/farmer/includes/config.php']
4721da41c8bSAndreas Gohr                ];
473a646d519SAndreas Gohr            } elseif ($key == 'license') {
4741da41c8bSAndreas Gohr                $append = [
4751da41c8bSAndreas Gohr                    'default' => [DOKU_INC . 'conf/' . $key . '.local.php']
4761da41c8bSAndreas Gohr                ];
477a646d519SAndreas Gohr            } elseif ($key == 'userscript') {
4781da41c8bSAndreas Gohr                $prepend = [
4791da41c8bSAndreas Gohr                    'default' => [DOKU_INC . 'conf/userscript.js']
4801da41c8bSAndreas Gohr                ];
481a646d519SAndreas Gohr            } elseif ($key == 'userstyle') {
4821da41c8bSAndreas Gohr                $prepend = [
4831da41c8bSAndreas Gohr                    'screen' => [
4841da41c8bSAndreas Gohr                        DOKU_INC . 'conf/userstyle.css',
4851da41c8bSAndreas Gohr                        DOKU_INC . 'conf/userstyle.less'
4861da41c8bSAndreas Gohr                    ],
4871da41c8bSAndreas Gohr                    'print' => [
4881da41c8bSAndreas Gohr                        DOKU_INC . 'conf/userprint.css',
4891da41c8bSAndreas Gohr                        DOKU_INC . 'conf/userprint.less'
4901da41c8bSAndreas Gohr                    ],
4911da41c8bSAndreas Gohr                    'feed' => [
4921da41c8bSAndreas Gohr                        DOKU_INC . 'conf/userfeed.css',
4931da41c8bSAndreas Gohr                        DOKU_INC . 'conf/userfeed.less'
4941da41c8bSAndreas Gohr                    ],
4951da41c8bSAndreas Gohr                    'all' => [
4961da41c8bSAndreas Gohr                        DOKU_INC . 'conf/userall.css',
4971da41c8bSAndreas Gohr                        DOKU_INC . 'conf/userall.less'
4981da41c8bSAndreas Gohr                    ]
4991da41c8bSAndreas Gohr                ];
50005ea7625SAnna Dabrowska            } elseif ($key == 'styleini') {
5011da41c8bSAndreas Gohr                $append = [
5021da41c8bSAndreas Gohr                    'local' => [DOKU_INC . 'conf/tpl/%TEMPLATE%/style.ini']
5031da41c8bSAndreas Gohr                ];
5041272da0cSAndreas Gohr            } elseif ($key == 'users') {
5051272da0cSAndreas Gohr                $config_cascade['plainauth.users']['protected'] = DOKU_INC . 'conf/users.auth.php';
506af1c6dd8SAndreas Gohr            } elseif ($key == 'plugins') {
5074b1aad07Satisne                $prepend = [
5084b1aad07Satisne                    'protected' => [DOKU_INC . 'conf/local.protected.php']
5094b1aad07Satisne                ];
5101da41c8bSAndreas Gohr                $append = [
5111da41c8bSAndreas Gohr                    'default' => [DOKU_INC . 'conf/plugins.local.php']
5121da41c8bSAndreas Gohr                ];
513a646d519SAndreas Gohr            } else {
5141da41c8bSAndreas Gohr                $append = [
5151da41c8bSAndreas Gohr                    'default' => [DOKU_INC . 'conf/' . $key . '.local.conf']
5161da41c8bSAndreas Gohr                ];
517a646d519SAndreas Gohr            }
518a646d519SAndreas Gohr
519a646d519SAndreas Gohr            // add to cascade
520a646d519SAndreas Gohr            foreach ($prepend as $section => $data) {
521a646d519SAndreas Gohr                $config_cascade[$key][$section] = array_merge($data, $config_cascade[$key][$section]);
522a646d519SAndreas Gohr            }
523a646d519SAndreas Gohr            foreach ($append as $section => $data) {
524a646d519SAndreas Gohr                $config_cascade[$key][$section] = array_merge($config_cascade[$key][$section], $data);
525a646d519SAndreas Gohr            }
526a646d519SAndreas Gohr        }
527de156015SAndreas Gohr
528de156015SAndreas Gohr        // add plugin overrides
529de156015SAndreas Gohr        $config_cascade['plugins']['protected'][] = DOKU_INC . 'lib/plugins/farmer/includes/plugins.php';
530a646d519SAndreas Gohr    }
531a646d519SAndreas Gohr
532a646d519SAndreas Gohr    /**
533da0ae2c0SAndreas Gohr     * Loads the farm config
534da0ae2c0SAndreas Gohr     */
5351da41c8bSAndreas Gohr    protected function loadConfig()
5361da41c8bSAndreas Gohr    {
537da0ae2c0SAndreas Gohr        $ini = DOKU_INC . 'conf/farm.ini';
538700b5633SAndreas Gohr        if (file_exists($ini)) {
539da0ae2c0SAndreas Gohr            $config = parse_ini_file($ini, true);
540da0ae2c0SAndreas Gohr            foreach (array_keys($this->config) as $section) {
541da0ae2c0SAndreas Gohr                if (isset($config[$section])) {
542da0ae2c0SAndreas Gohr                    $this->config[$section] = array_merge(
543da0ae2c0SAndreas Gohr                        $this->config[$section],
544da0ae2c0SAndreas Gohr                        $config[$section]
545da0ae2c0SAndreas Gohr                    );
546da0ae2c0SAndreas Gohr                }
547da0ae2c0SAndreas Gohr            }
548700b5633SAndreas Gohr        }
549700b5633SAndreas Gohr
550700b5633SAndreas Gohr        // farmdir setup can be done via environment
551700b5633SAndreas Gohr        if ($this->config['base']['farmdir'] === '' && isset($_ENV['DOKU_FARMDIR'])) {
552700b5633SAndreas Gohr            $this->config['base']['farmdir'] = $_ENV['DOKU_FARMDIR'];
553700b5633SAndreas Gohr        }
554da0ae2c0SAndreas Gohr
555a646d519SAndreas Gohr        $this->config['base']['farmdir'] = trim($this->config['base']['farmdir']);
556a646d519SAndreas Gohr        $this->config['base']['farmhost'] = strtolower(trim($this->config['base']['farmhost']));
557a646d519SAndreas Gohr    }
558da0ae2c0SAndreas Gohr}
559da0ae2c0SAndreas Gohr
560da0ae2c0SAndreas Gohr// initialize it globally
56185becf1bSAndreas Gohrif (!defined('DOKU_UNITTEST')) {
562da0ae2c0SAndreas Gohr    global $FARMCORE;
563da0ae2c0SAndreas Gohr    $FARMCORE = new DokuWikiFarmCore();
56485becf1bSAndreas Gohr}
565