1<?php 2 3use chrisbliss18\phpico\PHPIco; 4use dokuwiki\Extension\AdminPlugin; 5use dokuwiki\Form\Form; 6use dokuwiki\Utf8\PhpString; 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 // create a random logo and favicon 239 if (!class_exists('\splitbrain\RingIcon\RingIcon', false)) { 240 require(__DIR__ . '/../3rdparty/RingIcon.php'); 241 } 242 if (!class_exists('\chrisbliss18\phpico\PHPIco', false)) { 243 require(__DIR__ . '/../3rdparty/PHPIco.php'); 244 } 245 try { 246 if (function_exists('imagecreatetruecolor')) { 247 $logo = $animaldir . '/data/media/wiki/logo.png'; 248 if (!file_exists($logo)) { 249 $ringicon = new RingIcon(64); 250 $ringicon->createImage($animaldir, $logo); 251 } 252 253 $icon = $animaldir . '/data/media/wiki/favicon.ico'; 254 if (!file_exists($icon)) { 255 $icongen = new PHPIco($logo); 256 $icongen->saveIco($icon); 257 } 258 } 259 } catch (\Exception $ignore) { 260 // something went wrong, but we don't care. this is a nice to have feature only 261 } 262 263 // create admin user 264 if ($adminSetup === 'newAdmin') { 265 $users = "# <?php exit()?>\n" . $this->makeAdminLine($adminPassword) . "\n"; 266 } elseif ($adminSetup === 'currentAdmin') { 267 $users = "# <?php exit()?>\n" . $this->getAdminLine() . "\n"; 268 } elseif ($adminSetup === 'noUsers') { 269 if (file_exists($animaldir . '/conf/users.auth.php')) { 270 // a user file exists already, probably from animal template - don't overwrite 271 $users = ''; 272 } else { 273 // create empty user file 274 $users = "# <?php exit()?>\n"; 275 } 276 } else { 277 $users = io_readFile(DOKU_CONF . 'users.auth.php'); 278 } 279 if ($users) { 280 $ok &= io_saveFile($animaldir . '/conf/users.auth.php', $users); 281 } 282 283 if ($aclpolicy != '') { 284 $aclfile = file($animaldir . '/conf/acl.auth.php'); 285 $aclfile = array_map('trim', $aclfile); 286 array_pop($aclfile); 287 switch ($aclpolicy) { 288 case 'open': 289 $aclfile[] = "* @ALL 8"; 290 break; 291 case 'public': 292 $aclfile[] = "* @ALL 1"; 293 $aclfile[] = "* @user 8"; 294 break; 295 case 'closed': 296 $aclfile[] = "* @ALL 0"; 297 $aclfile[] = "* @user 8"; 298 break; 299 default: 300 throw new Exception('Undefined aclpolicy given'); 301 } 302 $ok &= io_saveFile($animaldir . '/conf/acl.auth.php', implode("\n", $aclfile) . "\n"); 303 304 global $conf; 305 switch ($userreg) { 306 case 'allow': 307 $disableactions = implode(',', array_diff(explode(',', $conf['disableactions']), ['register'])); 308 $ok &= io_saveFile( 309 $animaldir . '/conf/local.php', 310 "\n" . '$conf[\'disableactions\'] = \'' . $disableactions . '\';' . "\n", 311 true 312 ); 313 break; 314 case 'disable': 315 $disableactions = implode(',', array_merge(explode(',', $conf['disableactions']), ['register'])); 316 $ok &= io_saveFile( 317 $animaldir . '/conf/local.php', 318 "\n" . '$conf[\'disableactions\'] = \'' . $disableactions . '\';' . "\n", 319 true 320 ); 321 break; 322 case 'inherit': 323 case true: 324 // nothing needs to be done 325 break; 326 default: 327 $ok &= io_saveFile( 328 $animaldir . '/conf/local.php', 329 "\n" . '$conf[\'disableactions\'] = \'register\';' . "\n", 330 true 331 ); 332 } 333 } 334 335 // deactivate plugins by default FIXME this should be nicer 336 $deactivatedPluginsList = explode(',', $this->getConf('deactivated plugins')); 337 $deactivatedPluginsList = array_map('trim', $deactivatedPluginsList); 338 $deactivatedPluginsList = array_unique($deactivatedPluginsList); 339 $deactivatedPluginsList = array_filter($deactivatedPluginsList); 340 foreach ($deactivatedPluginsList as $plugin) { 341 $this->helper->setPluginState(trim($plugin), $name, 0); 342 } 343 344 return $ok; 345 } 346 347 /** 348 * Creates a new user line 349 * 350 * @param $password 351 * @return string 352 */ 353 protected function makeAdminLine($password) 354 { 355 $pass = auth_cryptPassword($password); 356 $line = implode( 357 ':', 358 ['admin', $pass, 'Administrator', 'admin@example.org', 'admin,user'] 359 ); 360 return $line; 361 } 362 363 /** 364 * Copies the current user as new admin line 365 * 366 * @return string 367 */ 368 protected function getAdminLine() 369 { 370 $currentAdmin = $_SERVER['REMOTE_USER']; 371 $masterUsers = file_get_contents(DOKU_CONF . 'users.auth.php'); 372 $masterUsers = ltrim(strstr($masterUsers, "\n" . $currentAdmin . ":")); 373 374 $newAdmin = substr($masterUsers, 0, strpos($masterUsers, "\n") + 1); 375 return $newAdmin; 376 } 377} 378