xref: /plugin/farmer/admin/new.php (revision c609f1dcc91a56df760d51ba92f6e25b7289002c)
1<?php
2
3use dokuwiki\Extension\AdminPlugin;
4use dokuwiki\Form\Form;
5use dokuwiki\Utf8\PhpString;
6use splitbrain\phpico\PhpIco;
7use splitbrain\RingIcon\RingIcon;
8
9/**
10 * DokuWiki Plugin farmer (Admin Component)
11 *
12 * Create new animals
13 *
14 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
15 * @author  Michael Große <grosse@cosmocode.de>
16 */
17class admin_plugin_farmer_new extends AdminPlugin
18{
19    /** @var helper_plugin_farmer $helper */
20    protected $helper;
21
22    /**
23     * admin_plugin_farmer_new constructor.
24     */
25    public function __construct()
26    {
27        $this->helper = plugin_load('helper', 'farmer');
28    }
29
30    /** @inheritdoc */
31    public function showInMenu()
32    {
33        return false;
34    }
35
36    /**
37     * Should carry out any processing required by the plugin.
38     */
39    public function handle()
40    {
41        global $INPUT;
42        global $ID;
43        if (!$INPUT->has('farmer__submit')) return;
44
45        $data = $this->validateAnimalData();
46        if (!$data) return;
47        if (
48            $this->createNewAnimal(
49                $data['name'],
50                $data['admin'],
51                $data['pass'],
52                $data['template'],
53                $data['aclpolicy'],
54                $data['allowreg']
55            )
56        ) {
57            $url = $this->helper->getAnimalURL($data['name']);
58            $link = '<a href="' . $url . '">' . hsc($data['name']) . '</a>';
59
60            msg(sprintf($this->getLang('animal creation success'), $link), 1);
61            $link = wl($ID, ['do' => 'admin', 'page' => 'farmer', 'sub' => 'new'], true, '&');
62            send_redirect($link);
63        }
64    }
65
66    /**
67     * Render HTML output, e.g. helpful text and a form
68     */
69    public function html()
70    {
71        global $lang;
72        $farmconfig = $this->helper->getConfig();
73
74        $form = new Form();
75        $form->addClass('plugin_farmer')->id('farmer__create_animal_form');
76
77        $form->addFieldsetOpen($this->getLang('animal configuration'));
78        $form->addTextInput('animalname', $this->getLang('animal'));
79        $form->addFieldsetClose();
80
81        $animals = $this->helper->getAllAnimals();
82        array_unshift($animals, '');
83        $form->addFieldsetOpen($this->getLang('animal template'));
84        $form->addDropdown('animaltemplate', $animals)
85            ->addClass('farmer_chosen_animals');
86        $form->addFieldsetClose();
87
88        $form->addFieldsetOpen($lang['i_policy'])->attr('id', 'aclPolicyFieldset');
89        $policyOptions = ['open' => $lang['i_pol0'], 'public' => $lang['i_pol1'], 'closed' => $lang['i_pol2']];
90        $form->addDropdown('aclpolicy', $policyOptions)
91            ->addClass('acl_chosen');
92        if ($farmconfig['inherit']['main']) {
93            $form->addRadioButton('allowreg', $this->getLang('inherit user registration'))
94                ->val('inherit')
95                ->attr('checked', 'checked');
96            $form->addRadioButton('allowreg', $this->getLang('enable user registration'))
97                ->val('allow');
98            $form->addRadioButton('allowreg', $this->getLang('disable user registration'))
99                ->val('disable');
100        } else {
101            $form->addCheckbox('allowreg', $lang['i_allowreg'])
102                ->attr('checked', 'checked');
103        }
104
105        $form->addFieldsetClose();
106
107        $form->addFieldsetOpen($this->getLang('animal administrator'));
108
109        $btn = $form->addRadioButton('adminsetup', $this->getLang('noUsers'))
110            ->val('noUsers');
111        if ($farmconfig['inherit']['users']) {
112            $btn->attr('checked', 'checked');  // default when inherit available
113        } else {
114            // no user copying when inheriting
115            $form->addRadioButton('adminsetup', $this->getLang('importUsers'))
116                ->val('importUsers');
117            $form->addRadioButton('adminsetup', $this->getLang('currentAdmin'))
118                ->val('currentAdmin');
119        }
120        $btn = $form->addRadioButton('adminsetup', $this->getLang('newAdmin'))
121            ->val('newAdmin');
122        if (!$farmconfig['inherit']['users']) {
123            $btn->attr('checked', 'checked'); // default when inherit not available
124        }
125        $form->addPasswordInput('adminPassword', $this->getLang('admin password'));
126        $form->addFieldsetClose();
127
128        $form->addButton('farmer__submit', $this->getLang('submit'))
129            ->attr('type', 'submit')
130            ->val('newAnimal');
131        echo $form->toHTML();
132    }
133
134    /**
135     * Validate the data for a new animal
136     *
137     * @return array|bool false on errors, clean data otherwise
138     */
139    protected function validateAnimalData()
140    {
141        global $INPUT;
142
143        $animalname = $INPUT->filter('trim')->filter([PhpString::class, 'strtolower'])->str('animalname');
144        $adminsetup = $INPUT->str('adminsetup');
145        $adminpass = $INPUT->filter('trim')->str('adminPassword');
146        $template = $INPUT->filter('trim')->str('animaltemplate');
147        $aclpolicy = $INPUT->filter('trim')->str('aclpolicy');
148        $allowreg = $INPUT->str('allowreg');
149
150        $errors = [];
151
152        if ($animalname === '') {
153            $errors[] = $this->getLang('animalname_missing');
154        } elseif (!$this->helper->validateAnimalName($animalname)) {
155            $errors[] = $this->getLang('animalname_invalid');
156        }
157
158        if ($adminsetup === 'newAdmin' && $adminpass === '') {
159            $errors[] = $this->getLang('adminPassword_empty');
160        }
161
162        if ($animalname !== '' && file_exists(DOKU_FARMDIR . '/' . $animalname)) {
163            $errors[] = $this->getLang('animalname_preexisting');
164        }
165
166        if (!is_dir(DOKU_FARMDIR . $template) && !in_array($aclpolicy, ['open', 'public', 'closed'])) {
167            $errors[] = $this->getLang('aclpolicy missing/bad');
168        }
169
170        if ($errors) {
171            foreach ($errors as $error) {
172                msg($error, -1);
173            }
174            return false;
175        }
176
177        if (!is_dir(DOKU_FARMDIR . $template)) {
178            $template = '';
179        }
180        if ($template != '') {
181            $aclpolicy = '';
182        }
183
184        return [
185            'name' => $animalname,
186            'admin' => $adminsetup,
187            'pass' => $adminpass,
188            'template' => $template,
189            'aclpolicy' => $aclpolicy,
190            'allowreg' => $allowreg
191        ];
192    }
193
194    /**
195     * Create a new animal
196     *
197     * @param string $name name/title of the animal, will be the directory name for htaccess setup
198     * @param string $adminSetup newAdmin, currentAdmin or importUsers
199     * @param string $adminPassword required if $adminSetup is newAdmin
200     * @param string $template name of animal to copy
201     * @param $aclpolicy
202     * @param $userreg
203     * @return bool true if successful
204     * @throws Exception
205     */
206    protected function createNewAnimal($name, $adminSetup, $adminPassword, $template, $aclpolicy, $userreg)
207    {
208        $animaldir = DOKU_FARMDIR . $name;
209
210        // copy basic template
211        $ok = $this->helper->copyDir(__DIR__ . '/../_animal', $animaldir);
212        if (!$ok) {
213            msg($this->getLang('animal creation error'), -1);
214            return false;
215        }
216
217        // copy animal template
218        if ($template != '') {
219            foreach (['conf', 'data/pages', 'data/media', 'data/meta', 'data/media_meta', 'index'] as $dir) {
220                $templatedir = DOKU_FARMDIR . $template . '/' . $dir;
221                if (!is_dir($templatedir)) continue;
222                // do not copy changelogs in meta
223                if (str_ends_with($dir, 'meta')) {
224                    $exclude = '/\.changes$/';
225                } else {
226                    $exclude = '';
227                }
228                if (!$this->helper->copyDir($templatedir, $animaldir . '/' . $dir, $exclude)) {
229                    msg(sprintf($this->getLang('animal template copy error'), $dir), -1);
230                    // we go on anyway
231                }
232            }
233        }
234
235        // append title to local config
236        $ok &= io_saveFile($animaldir . '/conf/local.php', "\n" . '$conf[\'title\'] = \'' . $name . '\';' . "\n", true);
237
238        try {
239            if (function_exists('imagecreatetruecolor')) {
240                $logo = $animaldir . '/data/media/wiki/logo.png';
241                if (!file_exists($logo)) {
242                    $ringicon = new RingIcon(64);
243                    $ringicon->createImage($animaldir, $logo);
244                }
245
246                $icon = $animaldir . '/data/media/wiki/favicon.ico';
247                if (!file_exists($icon)) {
248                    $icongen = new PhpIco($logo);
249                    $icongen->saveIco($icon);
250                }
251            }
252        } catch (\Exception $ignore) {
253            // something went wrong, but we don't care. this is a nice to have feature only
254        }
255
256        // create admin user
257        if ($adminSetup === 'newAdmin') {
258            $users = "# <?php exit()?>\n" . $this->makeAdminLine($adminPassword) . "\n";
259        } elseif ($adminSetup === 'currentAdmin') {
260            $users = "# <?php exit()?>\n" . $this->getAdminLine() . "\n";
261        } elseif ($adminSetup === 'noUsers') {
262            if (file_exists($animaldir . '/conf/users.auth.php')) {
263                // a user file exists already, probably from animal template - don't overwrite
264                $users = '';
265            } else {
266                // create empty user file
267                $users = "# <?php exit()?>\n";
268            }
269        } else {
270            $users = io_readFile(DOKU_CONF . 'users.auth.php');
271        }
272        if ($users) {
273            $ok &= io_saveFile($animaldir . '/conf/users.auth.php', $users);
274        }
275
276        if ($aclpolicy != '') {
277            $aclfile = file($animaldir . '/conf/acl.auth.php');
278            $aclfile = array_map('trim', $aclfile);
279            array_pop($aclfile);
280            switch ($aclpolicy) {
281                case 'open':
282                    $aclfile[] = "* @ALL 8";
283                    break;
284                case 'public':
285                    $aclfile[] = "* @ALL 1";
286                    $aclfile[] = "* @user 8";
287                    break;
288                case 'closed':
289                    $aclfile[] = "* @ALL 0";
290                    $aclfile[] = "* @user 8";
291                    break;
292                default:
293                    throw new Exception('Undefined aclpolicy given');
294            }
295            $ok &= io_saveFile($animaldir . '/conf/acl.auth.php', implode("\n", $aclfile) . "\n");
296
297            global $conf;
298            switch ($userreg) {
299                case 'allow':
300                    $disableactions = implode(',', array_diff(explode(',', $conf['disableactions']), ['register']));
301                    $ok &= io_saveFile(
302                        $animaldir . '/conf/local.php',
303                        "\n" . '$conf[\'disableactions\'] = \'' . $disableactions . '\';' . "\n",
304                        true
305                    );
306                    break;
307                case 'disable':
308                    $disableactions = implode(',', array_merge(explode(',', $conf['disableactions']), ['register']));
309                    $ok &= io_saveFile(
310                        $animaldir . '/conf/local.php',
311                        "\n" . '$conf[\'disableactions\'] = \'' . $disableactions . '\';' . "\n",
312                        true
313                    );
314                    break;
315                case 'inherit':
316                case true:
317                    // nothing needs to be done
318                    break;
319                default:
320                    $ok &= io_saveFile(
321                        $animaldir . '/conf/local.php',
322                        "\n" . '$conf[\'disableactions\'] = \'register\';' . "\n",
323                        true
324                    );
325            }
326        }
327
328        // deactivate plugins by default FIXME this should be nicer
329        $deactivatedPluginsList = explode(',', $this->getConf('deactivated plugins'));
330        $deactivatedPluginsList = array_map('trim', $deactivatedPluginsList);
331        $deactivatedPluginsList = array_unique($deactivatedPluginsList);
332        $deactivatedPluginsList = array_filter($deactivatedPluginsList);
333        foreach ($deactivatedPluginsList as $plugin) {
334            $this->helper->setPluginState(trim($plugin), $name, 0);
335        }
336
337        return $ok;
338    }
339
340    /**
341     * Creates a new user line
342     *
343     * @param $password
344     * @return string
345     */
346    protected function makeAdminLine($password)
347    {
348        $pass = auth_cryptPassword($password);
349        $line = implode(
350            ':',
351            ['admin', $pass, 'Administrator', 'admin@example.org', 'admin,user']
352        );
353        return $line;
354    }
355
356    /**
357     * Copies the current user as new admin line
358     *
359     * @return string
360     */
361    protected function getAdminLine()
362    {
363        $currentAdmin = $_SERVER['REMOTE_USER'];
364        $masterUsers = file_get_contents(DOKU_CONF . 'users.auth.php');
365        $masterUsers = ltrim(strstr($masterUsers, "\n" . $currentAdmin . ":"));
366
367        $newAdmin = substr($masterUsers, 0, strpos($masterUsers, "\n") + 1);
368        return $newAdmin;
369    }
370}
371