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