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