xref: /plugin/farmer/DokuWikiFarmCore.php (revision b96c66cc018b7a6d9630acddc46fe47fbaff0136)
1<?php
2
3/**
4 * Core Manager for the Farm functionality
5 *
6 * This class is initialized before any other DokuWiki code runs. Therefore it is
7 * completely selfcontained and does not use any of DokuWiki's utility functions.
8 *
9 * It's registered as a global $FARMCORE variable but you should not interact with
10 * it directly. Instead use the Farmer plugin's helper component.
11 */
12class DokuWikiFarmCore {
13    /**
14     * @var array The default config - changed by loadConfig
15     */
16    protected $config = array(
17        'base' => array(
18            'farmdir' => '',
19            'farmhost' => ''
20        ),
21        'notfound' => array(
22            'show' => 'farmer',
23            'url' => ''
24        ),
25        'inherit' => array(
26            'main' => 1,
27            'acronyms' => 1,
28            'entities' => 1,
29            'interwiki' => 1,
30            'license' => 1,
31            'mime' => 1,
32            'scheme' => 1,
33            'smileys' => 1,
34            'wordblock' => 1,
35            'userstyle' => 0,
36            'userscript' => 0
37        )
38    );
39
40    /** @var string|false The current animal, false for farmer */
41    protected $animal = false;
42    /** @var bool true if an animal was requested but was not found */
43    protected $notfound = false;
44    /** @var bool true if the current animal was requested by host */
45    protected $hostbased = false;
46
47    /**
48     * DokuWikiFarmCore constructor.
49     *
50     * This initializes the whole farm by loading the configuration and setting
51     * DOKU_CONF depending on the requested animal
52     */
53    public function __construct() {
54        $this->loadConfig();
55        if($this->config['base']['farmdir'] === '') return; // farm setup not complete
56
57        // animal?
58        $this->detectAnimal();
59
60        // setup defines
61        define('DOKU_FARMDIR', $this->config['base']['farmdir']);
62        define('DOKU_FARM_ANIMAL', $this->animal);
63        if($this->animal) {
64            define('DOKU_CONF', DOKU_FARMDIR . '/' . $this->animal . '/conf/');
65        } else {
66            define('DOKU_CONF', DOKU_INC . '/conf/');
67        }
68
69        $this->setupCascade();
70        $this->adjustCascade();
71    }
72
73    /**
74     * @return array the current farm configuration
75     */
76    public function getConfig() {
77        return $this->config;
78    }
79
80    /**
81     * @return false|string
82     */
83    public function getAnimal() {
84        return $this->animal;
85    }
86
87    /**
88     * @return boolean
89     */
90    public function isHostbased() {
91        return $this->hostbased;
92    }
93
94    /**
95     * @return boolean
96     */
97    public function wasNotfound() {
98        return $this->notfound;
99    }
100
101    /**
102     * Detect the current animal
103     *
104     * Sets internal members $animal, $notfound and $hostbased
105     *
106     * This borrows form DokuWiki's inc/farm.php but does not support a default conf dir
107     */
108    protected function detectAnimal() {
109        $farmdir = $this->config['base']['farmdir'];
110        $farmhost = $this->config['base']['farmhost'];
111
112        // check if animal was set via parameter (rewrite or CLI)
113        $animal = '';
114        if(isset($_REQUEST['animal'])) $animal = $_REQUEST['animal'];
115        if('cli' == php_sapi_name() && isset($_SERVER['animal'])) $animal = $_SERVER['animal'];
116        if($animal) {
117            // check that $animal is a string and just a directory name and not a path
118            if(!is_string($animal) || strpbrk($animal, '\\/') !== false) {
119                $this->notfound = true;
120                return;
121            };
122            $animal = strtolower($animal);
123
124            // check if animal exists
125            if(is_dir("$farmdir/$animal/conf")) {
126                $this->animal = $animal;
127                return;
128            } else {
129                $this->notfound = true;
130                return;
131            }
132        }
133
134        // is this the farmer?
135        if(strtolower($_SERVER['HTTP_HOST']) == $farmhost) {
136            return;
137        }
138
139        // still here? check for host based
140        $this->hostbased = true;
141        $uri = explode('/', $_SERVER['SCRIPT_NAME'] ? $_SERVER['SCRIPT_NAME'] : $_SERVER['SCRIPT_FILENAME']);
142        $server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.')))));
143        for($i = count($uri) - 1; $i > 0; $i--) {
144            for($j = count($server); $j > 0; $j--) {
145                $animal = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
146                if(is_dir("$farmdir/$animal/conf/")) {
147                    $this->animal = $animal;
148                    return;
149                }
150            }
151        }
152
153        // no hit
154        $this->notfound = true;
155        return;
156    }
157
158    /**
159     * This sets up the default farming config cascade
160     */
161    protected function setupCascade() {
162        global $config_cascade;
163        $config_cascade = array(
164            'main' => array(
165                'default' => array(DOKU_INC . 'conf/dokuwiki.php',),
166                'local' => array(DOKU_CONF . 'local.php',),
167                'protected' => array(DOKU_CONF . 'local.protected.php',),
168            ),
169            'acronyms' => array(
170                'default' => array(DOKU_INC . 'conf/acronyms.conf',),
171                'local' => array(DOKU_CONF . 'acronyms.local.conf',),
172            ),
173            'entities' => array(
174                'default' => array(DOKU_INC . 'conf/entities.conf',),
175                'local' => array(DOKU_CONF . 'entities.local.conf',),
176            ),
177            'interwiki' => array(
178                'default' => array(DOKU_INC . 'conf/interwiki.conf',),
179                'local' => array(DOKU_CONF . 'interwiki.local.conf',),
180            ),
181            'license' => array(
182                'default' => array(DOKU_INC . 'conf/license.php',),
183                'local' => array(DOKU_CONF . 'license.local.php',),
184            ),
185            'mediameta' => array(
186                'default' => array(DOKU_INC . 'conf/mediameta.php',),
187                'local' => array(DOKU_CONF . 'mediameta.local.php',),
188            ),
189            'mime' => array(
190                'default' => array(DOKU_INC . 'conf/mime.conf',),
191                'local' => array(DOKU_CONF . 'mime.local.conf',),
192            ),
193            'scheme' => array(
194                'default' => array(DOKU_INC . 'conf/scheme.conf',),
195                'local' => array(DOKU_CONF . 'scheme.local.conf',),
196            ),
197            'smileys' => array(
198                'default' => array(DOKU_INC . 'conf/smileys.conf',),
199                'local' => array(DOKU_CONF . 'smileys.local.conf',),
200            ),
201            'wordblock' => array(
202                'default' => array(DOKU_INC . 'conf/wordblock.conf',),
203                'local' => array(DOKU_CONF . 'wordblock.local.conf',),
204            ),
205            'acl' => array(
206                'default' => DOKU_CONF . 'acl.auth.php',
207            ),
208            'plainauth.users' => array(
209                'default' => DOKU_CONF . 'users.auth.php',
210            ),
211            'plugins' => array( // needed since Angua
212                                'default' => array(DOKU_INC . 'conf/plugins.php',),
213                                'local' => array(DOKU_CONF . 'plugins.local.php',),
214                                'protected' => array(
215                                    DOKU_INC . 'conf/plugins.required.php',
216                                    DOKU_CONF . 'plugins.protected.php',
217                                ),
218            ),
219            'userstyle' => array(
220                'screen' => array(DOKU_CONF . 'userstyle.css', DOKU_CONF . 'userstyle.less',),
221                'print' => array(DOKU_CONF . 'userprint.css', DOKU_CONF . 'userprint.less',),
222                'feed' => array(DOKU_CONF . 'userfeed.css', DOKU_CONF . 'userfeed.less',),
223                'all' => array(DOKU_CONF . 'userall.css', DOKU_CONF . 'userall.less',),
224            ),
225            'userscript' => array(
226                'default' => array(DOKU_CONF . 'userscript.js',),
227            ),
228        );
229    }
230
231    /**
232     * This adds additional files to the config cascade based on the inheritence settings
233     *
234     * These are only added for animals, not the farmer
235     */
236    protected function adjustCascade() {
237        // FIXME check if this is an animal
238        global $config_cascade;
239
240        foreach($this->config['inherit'] as $key => $val) {
241            if(!$val) continue;
242
243            // prepare what is to append or prepend
244            $append = array();
245            $prepend = array();
246            if($key == 'main') {
247                $append = array('default' => array(DOKU_INC . 'conf/local.php'));
248            } elseif($key == 'license') {
249                $append = array('default' => array(DOKU_INC . 'conf/' . $key . '.local.php'));
250            } elseif($key == 'userscript') {
251                $prepend = array('default' => array(DOKU_INC . 'conf/userscript.js'));
252            } elseif($key == 'userstyle') {
253                $prepend = array(
254                    'screen' => array(DOKU_INC . 'conf/userstyle.css', DOKU_INC . 'conf/userstyle.less',),
255                    'print' => array(DOKU_INC . 'conf/userprint.css', DOKU_INC . 'conf/userprint.less',),
256                    'feed' => array(DOKU_INC . 'conf/userfeed.css', DOKU_INC . 'conf/userfeed.less',),
257                    'all' => array(DOKU_INC . 'conf/userall.css', DOKU_INC . 'conf/userall.less',),
258                );
259            } else {
260                $append = array('default' => array(DOKU_INC . 'conf/' . $key . '.local.conf'));
261            }
262
263            // add to cascade
264            foreach($prepend as $section => $data) {
265                $config_cascade[$key][$section] = array_merge($data, $config_cascade[$key][$section]);
266            }
267            foreach($append as $section => $data) {
268                $config_cascade[$key][$section] = array_merge($config_cascade[$key][$section], $data);
269            }
270        }
271    }
272
273    /**
274     * Loads the farm config
275     */
276    protected function loadConfig() {
277        $ini = DOKU_INC . 'conf/farm.ini';
278        if(!file_exists($ini)) return;
279        $config = parse_ini_file($ini, true);
280        foreach(array_keys($this->config) as $section) {
281            if(isset($config[$section])) {
282                $this->config[$section] = array_merge(
283                    $this->config[$section],
284                    $config[$section]
285                );
286            }
287        }
288
289        $this->config['base']['farmdir'] = trim($this->config['base']['farmdir']);
290        $this->config['base']['farmhost'] = strtolower(trim($this->config['base']['farmhost']));
291    }
292
293}
294
295// initialize it globally
296global $FARMCORE;
297$FARMCORE = new DokuWikiFarmCore();
298