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