1<?php 2 3/** 4 * DokuWiki Plugin doxycode (Admin Component) 5 * 6 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 7 * @author Lukas Probsthain <lukas.probsthain@gmail.com> 8 */ 9 10use dokuwiki\Extension\AdminPlugin; 11use dokuwiki\Form\Form; 12 13/** 14 * Class admin_plugin_doxycode 15 * 16 * This admin plugin implements the management of tag files from differen doxygen 17 * documentations for building cross referenced code snippets. 18 * 19 * It lists all currently configured tag files and all tag files present in the file system 20 * of the plugin. The user can add new tag file configurations via upload or by defining a new configuration. 21 * 22 * The admin interface uses the helper_plugin_doxycode_tagmanager helper plugin for loading the current tag file 23 * list. On save it also uses the helper for storing the configuration in a json file. 24 * 25 * On save and update it also checks if a configuration is valid and can stay enabled. 26 * 27 * If a new remote config was defined, the action component of this plugin tries to download the tag file. 28 * 29 * @author Lukas Probsthain <lukas.probsthain@gmail.com> 30 */ 31class admin_plugin_doxycode extends AdminPlugin 32{ 33 /** @var helper_plugin_doxycode_tagmanager $helper */ 34 private $helper; 35 private $tag_config; 36 37 // TODO: these should be minimum widths 38 private $conf_column_widths = array( 39 'local_name' => 16, 40 'docu_url' => 35, 41 'remote_url' => 35, 42 'update_period' => 5, 43 'description' => 40 44 ); 45 46 public function __construct() 47 { 48 $this->helper = plugin_load('helper', 'doxycode_tagmanager'); 49 50 // load files 51 $tag_files = $this->helper->listTagFiles(); 52 53 // load tag_config 54 $tag_config = $this->helper->loadTagFileConfig(); 55 56 // merge both arrays for rendering 57 // prioritize the tag_config and overwrite elements from files! 58 $this->tag_config = array_merge($tag_files, $tag_config); 59 } 60 61 /** 62 * handle user request 63 */ 64 public function handle() 65 { 66 global $INPUT; 67 global $_FILES; 68 69 if (!$INPUT->has('cmd')) return; // first time - nothing to do 70 71 if (!checkSecurityToken()) return; 72 if (!is_array($INPUT->param('cmd'))) return; 73 74 $cmd = $INPUT->arr('cmd'); 75 76 $new_tag_config = $INPUT->arr('tag_config'); 77 78 // handle upload command 79 // if a new file was added, we move the file to the tagfile directory 80 // and add the file to the tagfile configuration for rendering 81 // on the next load of the page the tag file will be loaded to configuration 82 // from the tag file list from the directory 83 if ($cmd['update'] && isset($_FILES['upload']) && $_FILES['upload']['error'] != UPLOAD_ERR_NO_FILE) { 84 if ($_FILES['upload']['error'] == 0) { 85 if ('xml' != pathinfo($_FILES['upload']['name'], PATHINFO_EXTENSION)) { 86 msg(sprintf($this->getLang('admin_err_no_xml_file'), $_FILES['upload']['name']), 2); 87 } else { 88 // if tag file directory does not exist, create it! 89 $this->helper->createTagFileDir(); 90 91 move_uploaded_file( 92 $_FILES['upload']['tmp_name'], 93 DOKU_PLUGIN . 'doxycode/tagfiles/' . $_FILES['upload']['name'] 94 ); 95 msg(sprintf($this->getLang('admin_info_upload_success'), $_FILES['upload']['name']), 1); 96 $this->tag_config[pathinfo($_FILES['upload']['name'], PATHINFO_FILENAME)] = []; 97 } 98 } else { 99 msg($this->getLang('admin_err_upload'), 2); 100 } 101 } 102 103 // add new element from form 104 if (isset($new_tag_config['new'])) { 105 // do we have a valid new entry && is this entry name not already set? 106 if ( 107 strlen($new_tag_config['new']['new_name']) > 0 108 && !isset($this->tag_config[$new_tag_config['new']['new_name']]) 109 ) { 110 $newKey = $new_tag_config['new']['new_name']; 111 112 // unset the temporary new name that otherwise would mean a rename/move 113 unset($new_tag_config['new']['new_name']); 114 115 // add new tag_config element to global config 116 $this->tag_config[$newKey] = $new_tag_config['new']; 117 msg(sprintf($this->getLang('admin_info_new_tag_config'), $newKey), 1); 118 } 119 unset($new_tag_config['new']); // Remove the 'new' placeholder 120 } 121 122 // update our configuration from the input data 123 if ($cmd['save'] || $cmd['update']) { 124 foreach ($new_tag_config as $key => $tag_conf) { 125 $this->tag_config[$key] = $tag_conf; 126 } 127 } 128 129 // check if settings are valid for the enabled state 130 // TODO: implement tagmanager functions for checking if a config can be enabled! 131 if ($cmd['save'] || $cmd['update']) { 132 foreach ($this->tag_config as $key => &$tag_conf) { 133 // if element is disable continue 134 if (!isset($tag_conf['enabled']) || !$tag_conf['enabled']) continue; 135 136 // if docu_url is missing 137 if (strlen($tag_conf['docu_url']) <= 0) { 138 $tag_conf['enabled'] = false; 139 continue; 140 } 141 142 if (strlen($tag_conf['remote_url']) > 0 && strlen($tag_conf['update_period']) <= 0) { 143 $tag_conf['enabled'] = false; 144 continue; 145 } 146 } 147 148 // TODO: really necessary here? 149 unset($tag_conf); 150 } 151 152 if ($cmd['save']) { 153 // delete entries that are marked for deletion 154 foreach ($this->tag_config as $key => $tag_conf) { 155 if (isset($tag_conf['delete']) && $tag_conf['delete']) { 156 unset($this->tag_config[$key]); 157 158 // delete the tag file if it exists! 159 $filename = $this->helper->getTagFileDir() . $key . '.xml'; 160 if (file_exists($filename)) { 161 unlink($filename); 162 msg(sprintf( 163 $this->getLang('admin_info_tag_deleted'), 164 pathinfo($filename, PATHINFO_BASENAME) 165 ), 1); 166 } 167 } 168 } 169 170 // handle renames 171 foreach ($this->tag_config as $key => $tag_conf) { 172 if (isset($tag_conf['new_name']) && $key !== $tag_conf['new_name']) { 173 // TODO: check if an entry with this newName already exists! 174 // if it already exists we can't rename it -> show msg to user! 175 $newName = $tag_conf['new_name']; 176 unset($this->tag_config[$key]); 177 $this->tag_config[$newName] = $tag_conf; 178 unset($this->tag_config[$newName]['new_name']); 179 180 rename($this->helper->getTagFileDir() 181 . $key . 'xml', $this->helper->getTagFileDir() . $newName . '.xml'); 182 183 // TODO: rename tag in page! 184 // I looked into the move plugin 185 // it might be possible to handle renaming 186 // if the move plugin supports custom types (currently only media and pages) 187 188 // TODO: notify user through msg that the tag file was renamed! 189 } 190 } 191 192 $this->helper->saveTagFileConfig($this->tag_config); 193 } 194 } 195 196 /** 197 * output appropriate html 198 */ 199 public function html() 200 { 201 global $ID;# 202 global $conf; 203 global $lang; 204 205 // form header 206 echo '<div id="doxycode__tagmanager">'; 207 208 $form = new Form(['enctype' => 'multipart/form-data']); 209 210 // new file 211 $form->addElement(new dokuwiki\Form\InputElement('file', 'upload', $this->getLang('admin_upload'))); 212 213 $form->addHTML('<br>'); 214 $form->addHTML('<br>'); 215 216 // start table for existing configurations 217 $form->addTagOpen('div')->addClass('table'); 218 $form->addTagOpen('table')->addClass('inline'); 219 220 // add header 221 $form->addTagOpen('thead'); 222 223 $form->addTagOpen('tr'); 224 225 $form->addHTML('<th>' . $this->getLang('admin_conf_delete') . '</th>'); 226 $form->addHTML('<th>' . $this->getLang('admin_conf_enabled') . '</th>'); 227 $form->addHTML('<th>' . $this->getLang('admin_conf_force_runner') . '</th>'); 228 $form->addHTML('<th>' . $this->getLang('admin_conf_local_name') . '</th>'); 229 $form->addHTML('<th>' . $this->getLang('admin_conf_mtime') . '</th>'); 230 $form->addHTML('<th>' . $this->getLang('admin_conf_docu_url') . '</th>'); 231 $form->addHTML('<th>' . $this->getLang('admin_conf_remote_url') . '</th>'); 232 $form->addHTML('<th>' . $this->getLang('admin_conf_update_period') . '</th>'); 233 $form->addHTML('<th>' . $this->getLang('admin_conf_description') . '</th>'); 234 235 $form->addTagClose('tr'); 236 237 $form->addTagClose('thead'); 238 239 // add body 240 $form->addTagOpen('tbody'); 241 242 foreach ($this->tag_config as $key => $tag_conf) { 243 $form->addTagOpen('tr'); 244 245 $form->addTagOpen('td'); 246 $checkbox = $form->addCheckbox('tag_config[' . $key . '][delete]') 247 ->useInput(false); 248 if ($tag_conf['delete']) $checkbox->attrs(['checked' => 'checked']); 249 $form->addTagClose('td'); 250 251 $form->addTagOpen('td'); 252 $checkbox = $form->addCheckbox('tag_config[' . $key . '][enabled]') 253 ->useInput(false); 254 if ($tag_conf['enabled']) $checkbox->attrs(['checked' => 'checked']); 255 $form->addTagClose('td'); 256 257 $form->addTagOpen('td'); 258 $checkbox = $form->addCheckbox('tag_config[' . $key . '][force_runner]') 259 ->useInput(false); 260 if ($tag_conf['force_runner']) $checkbox->attrs(['checked' => 'checked']); 261 $form->addTagClose('td'); 262 263 $form->addTagOpen('td'); 264 $new_name = $form->addTextInput('tag_config[' . $key . '][new_name]') 265 ->attrs(['size' => $this->conf_column_widths['local_name']]) 266 ->useInput(false); 267 268 // add red highlight if this file does not exist 269 if (file_exists($this->helper->getFileName($key))) { 270 $new_name->attrs(['style' => 'background-color: LightGreen']); 271 } else { 272 $new_name->attrs(['style' => 'background-color: LightCoral']); 273 } 274 275 if (isset($tag_conf['new_name'])) { 276 $new_name->val($tag_conf['new_name']); 277 } else { 278 $new_name->val($key); 279 } 280 $form->addTagClose('td'); 281 282 // print file mtime for better understanding of update mechanism by admin 283 $form->addTagOpen('td'); 284 if (file_exists($this->helper->getFileName($key))) { 285 $form->addLabel(dformat(@filemtime($this->helper->getFileName($key)))); 286 } 287 $form->addTagClose('td'); 288 289 $form->addTagOpen('td'); 290 $form->addTextInput('tag_config[' . $key . '][docu_url]') 291 ->attrs(['size' => $this->conf_column_widths['docu_url']]) 292 ->useInput(false) 293 ->val($tag_conf['docu_url']); 294 $form->addTagClose('td'); 295 296 $form->addTagOpen('td'); 297 $form->addTextInput('tag_config[' . $key . '][remote_url]') 298 ->attrs(['size' => $this->conf_column_widths['remote_url']]) 299 ->useInput(false) 300 ->val($tag_conf['remote_url']); 301 $form->addTagClose('td'); 302 303 $form->addTagOpen('td'); 304 $period = $form->addTextInput('tag_config[' . $key . '][update_period]') 305 ->attrs(['size' => $this->conf_column_widths['update_period']]) 306 ->useInput(false) 307 ->val($tag_conf['update_period']); 308 309 if ($tag_conf['update_period'] > 0) { 310 $timestamp = $conf['last_update'] ? $conf['last_update'] : 0; 311 $now = time(); 312 313 if ($now - $tag_conf['update_period'] >= $timestamp) { 314 $period->attrs(['style' => 'background-color: LightGreen']); 315 } else { 316 $period->attrs(['style' => 'background-color: LightCoral']); 317 } 318 } 319 $form->addTagClose('td'); 320 321 $form->addTagOpen('td'); 322 $form->addTextInput('tag_config[' . $key . '][description]') 323 ->attrs(['size' => $this->conf_column_widths['description']]) 324 ->useInput(false) 325 ->val($tag_conf['description']); 326 $form->addTagClose('td'); 327 328 $form->addTagClose('tr'); 329 } 330 331 // add 'create new' entry 332 333 // TODO: break table so 'create new' stands out more clearly 334 335 $form->addTagOpen('tr'); 336 $form->addTagOpen('td') 337 ->attrs(['colspan' => 6]); 338 $form->addHTML($this->getLang('admin_new_entry')); 339 $form->addTagClose('td'); 340 $form->addTagClose('tr'); 341 342 $form->addTagOpen('tr'); 343 $form->addHTML('<td></td>'); 344 345 $form->addTagOpen('td'); 346 $form->addCheckbox('tag_config[new][enabled]') 347 ->useInput(false); 348 $form->addTagClose('td'); 349 350 $form->addTagOpen('td'); 351 $form->addCheckbox('tag_config[new][force_runner]') 352 ->useInput(false); 353 $form->addTagClose('td'); 354 355 $form->addTagOpen('td'); 356 $form->addTextInput('tag_config[new][new_name]') 357 ->attrs(['size' => $this->conf_column_widths['local_name']]) 358 ->useInput(false); 359 $form->addTagClose('td'); 360 361 $form->addTagOpen('td'); 362 $form->addTagClose('td'); 363 364 $form->addTagOpen('td'); 365 $form->addTextInput('tag_config[new][docu_url]') 366 ->attrs(['size' => $this->conf_column_widths['docu_url']]) 367 ->useInput(false); 368 $form->addTagClose('td'); 369 370 $form->addTagOpen('td'); 371 $form->addTextInput('tag_config[new][remote_url]') 372 ->attrs(['size' => $this->conf_column_widths['remote_url']]) 373 ->useInput(false); 374 $form->addTagClose('td'); 375 376 $form->addTagOpen('td'); 377 $form->addTextInput('tag_config[new][update_period]') 378 ->attrs(['size' => $this->conf_column_widths['update_period']]) 379 ->useInput(false); 380 $form->addTagClose('td'); 381 382 $form->addTagOpen('td'); 383 $form->addTextInput('tag_config[new][description]') 384 ->attrs(['size' => $this->conf_column_widths['description']]) 385 ->useInput(false); 386 $form->addTagClose('td'); 387 $form->addTagClose('tr'); 388 389 390 $form->addTagClose('tbody'); 391 392 // end table 393 $form->addTagClose('table'); 394 $form->addTagClose('div'); 395 396 $form->addButton('cmd[save]', $lang['btn_save'])->attrs(['accesskey' => 's']); 397 $form->addButton('cmd[update]', $lang['btn_update']); 398 399 echo $form->toHTML(); 400 401 402 echo '</div>'; // id=doxycode__tagmanager 403 } 404 405 /** 406 * Return true for access only by admins (config:superuser) or false if managers are allowed as well 407 * 408 * @return bool 409 */ 410 public function forAdminOnly() 411 { 412 return false; 413 } 414} 415