1<?php 2/* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net> 3 * Copyright (C) 2017 Regis Houssin <regis.houssin@inodbox.com> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 */ 18 19/** 20 * \file htdocs/admin/modulehelp.php 21 * \brief Page to activate/disable all modules 22 */ 23 24if (!defined('NOREQUIREMENU')) define('NOREQUIREMENU', '1'); // If there is no need to load and show top and left menu 25if (!defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL', '1'); // Disabled because this page is into a popup on module search page and we want to avoid to have an Anti CSRF token error (done if MAIN_SECURITY_CSRF_WITH_TOKEN is on) when we make a second search after closing popup. 26 27 28require '../main.inc.php'; 29require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; 30require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 31require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; 32 33// Load translation files required by the page 34$langs->loadLangs(array('errors', 'admin', 'modulebuilder', 'exports')); 35 36$mode = GETPOST('mode', 'alpha'); 37$action = GETPOST('action', 'aZ09'); 38$id = GETPOST('id', 'int'); 39if (empty($mode)) $mode = 'desc'; 40 41if (!$user->admin) 42 accessforbidden(); 43 44 45 46/* 47 * Actions 48 */ 49 50// Nothing 51 52 53/* 54 * View 55 */ 56 57$form = new Form($db); 58 59$help_url = 'EN:First_setup|FR:Premiers_paramétrages|ES:Primeras_configuraciones'; 60llxHeader('', $langs->trans("Setup"), $help_url); 61 62print '<!-- Force style container -->'."\n".'<style> 63.id-container { 64 width: 100%; 65} 66</style>'; 67 68$arrayofnatures = array('core'=>$langs->transnoentitiesnoconv("Core"), 'external'=>$langs->transnoentitiesnoconv("External").' - '.$langs->trans("AllPublishers")); 69 70// Search modules dirs 71$modulesdir = dolGetModulesDirs(); 72 73 74$filename = array(); 75$modules = array(); 76$orders = array(); 77$categ = array(); 78$dirmod = array(); 79$i = 0; // is a sequencer of modules found 80$j = 0; // j is module number. Automatically affected if module number not defined. 81$modNameLoaded = array(); 82 83foreach ($modulesdir as $dir) 84{ 85 // Load modules attributes in arrays (name, numero, orders) from dir directory 86 //print $dir."\n<br>"; 87 dol_syslog("Scan directory ".$dir." for module descriptor files (modXXX.class.php)"); 88 $handle = @opendir($dir); 89 if (is_resource($handle)) 90 { 91 while (($file = readdir($handle)) !== false) 92 { 93 //print "$i ".$file."\n<br>"; 94 if (is_readable($dir.$file) && substr($file, 0, 3) == 'mod' && substr($file, dol_strlen($file) - 10) == '.class.php') 95 { 96 $modName = substr($file, 0, dol_strlen($file) - 10); 97 98 if ($modName) 99 { 100 if (!empty($modNameLoaded[$modName])) 101 { 102 $mesg = "Error: Module ".$modName." was found twice: Into ".$modNameLoaded[$modName]." and ".$dir.". You probably have an old file on your disk.<br>"; 103 setEventMessages($mesg, null, 'warnings'); 104 dol_syslog($mesg, LOG_ERR); 105 continue; 106 } 107 108 try { 109 $res = include_once $dir.$file; 110 if (class_exists($modName)) 111 { 112 try { 113 $objMod = new $modName($db); 114 $modNameLoaded[$modName] = $dir; 115 116 if (!$objMod->numero > 0 && $modName != 'modUser') 117 { 118 dol_syslog('The module descriptor '.$modName.' must have a numero property', LOG_ERR); 119 } 120 $j = $objMod->numero; 121 122 $modulequalified = 1; 123 124 // We discard modules according to features level (PS: if module is activated we always show it) 125 $const_name = 'MAIN_MODULE_'.strtoupper(preg_replace('/^mod/i', '', get_class($objMod))); 126 if ($objMod->version == 'development' && (empty($conf->global->$const_name) && ($conf->global->MAIN_FEATURES_LEVEL < 2))) $modulequalified = 0; 127 if ($objMod->version == 'experimental' && (empty($conf->global->$const_name) && ($conf->global->MAIN_FEATURES_LEVEL < 1))) $modulequalified = 0; 128 if (preg_match('/deprecated/', $objMod->version) && (empty($conf->global->$const_name) && ($conf->global->MAIN_FEATURES_LEVEL >= 0))) $modulequalified = 0; 129 130 // We discard modules according to property disabled 131 //if (! empty($objMod->hidden)) $modulequalified=0; 132 133 if ($modulequalified > 0) 134 { 135 $publisher = dol_escape_htmltag($objMod->getPublisher()); 136 $external = ($objMod->isCoreOrExternalModule() == 'external'); 137 if ($external) 138 { 139 if ($publisher) 140 { 141 $arrayofnatures['external_'.$publisher] = $langs->trans("External").' - '.$publisher; 142 } else { 143 $arrayofnatures['external_'] = $langs->trans("External").' - '.$langs->trans("UnknownPublishers"); 144 } 145 } 146 ksort($arrayofnatures); 147 } 148 149 // Define array $categ with categ with at least one qualified module 150 if ($modulequalified > 0) 151 { 152 $modules[$i] = $objMod; 153 $filename[$i] = $modName; 154 155 // Gives the possibility to the module, to provide his own family info and position of this family 156 if (is_array($objMod->familyinfo) && !empty($objMod->familyinfo)) { 157 if (!is_array($familyinfo)) $familyinfo = array(); 158 $familyinfo = array_merge($familyinfo, $objMod->familyinfo); 159 $familykey = key($objMod->familyinfo); 160 } else { 161 $familykey = $objMod->family; 162 } 163 164 $moduleposition = ($objMod->module_position ? $objMod->module_position : '50'); 165 if ($moduleposition == '50' && ($objMod->isCoreOrExternalModule() == 'external')) 166 { 167 $moduleposition = '80'; // External modules at end by default 168 } 169 170 $orders[$i] = $familyinfo[$familykey]['position']."_".$familykey."_".$moduleposition."_".$j; // Sort by family, then by module position then number 171 $dirmod[$i] = $dir; 172 //print $i.'-'.$dirmod[$i].'<br>'; 173 // Set categ[$i] 174 $specialstring = 'unknown'; 175 if ($objMod->version == 'development' || $objMod->version == 'experimental') $specialstring = 'expdev'; 176 if (isset($categ[$specialstring])) $categ[$specialstring]++; // Array of all different modules categories 177 else $categ[$specialstring] = 1; 178 $j++; 179 $i++; 180 } else dol_syslog("Module ".get_class($objMod)." not qualified"); 181 } catch (Exception $e) 182 { 183 dol_syslog("Failed to load ".$dir.$file." ".$e->getMessage(), LOG_ERR); 184 } 185 } else { 186 print "Warning bad descriptor file : ".$dir.$file." (Class ".$modName." not found into file)<br>"; 187 } 188 } catch (Exception $e) 189 { 190 dol_syslog("Failed to load ".$dir.$file." ".$e->getMessage(), LOG_ERR); 191 } 192 } 193 } 194 } 195 closedir($handle); 196 } else { 197 dol_syslog("htdocs/admin/modulehelp.php: Failed to open directory ".$dir.". See permission and open_basedir option.", LOG_WARNING); 198 } 199} 200 201asort($orders); 202//var_dump($orders); 203//var_dump($categ); 204//var_dump($modules); 205 206 207unset($objMod); 208$i = 0; 209foreach ($orders as $tmpkey => $tmpvalue) 210{ 211 $tmpMod = $modules[$tmpkey]; 212 if ($tmpMod->numero == $id) 213 { 214 $key = $i; 215 $modName = $filename[$tmpkey]; 216 $dirofmodule = $dirmod[$tmpkey]; 217 $objMod = $tmpMod; 218 break; 219 } 220 $i++; 221} 222$value = $orders[$key]; 223$tab = explode('_', $value); 224$familyposition = $tab[0]; $familykey = $tab[1]; $module_position = $tab[2]; $numero = $tab[3]; 225 226 227 228$head = modulehelp_prepare_head($objMod); 229 230// Check filters 231$modulename = $objMod->getName(); 232$moduledesc = $objMod->getDesc(); 233$moduleauthor = $objMod->getPublisher(); 234$moduledir = strtolower(preg_replace('/^mod/i', '', get_class($objMod))); 235 236$const_name = 'MAIN_MODULE_'.strtoupper(preg_replace('/^mod/i', '', get_class($objMod))); 237 238$text = '<span class="opacitymedium">'.$langs->trans("LastActivationDate").':</span> '; 239if (!empty($conf->global->$const_name)) $text .= dol_print_date($objMod->getLastActivationDate(), 'dayhour'); 240else $text .= $langs->trans("Disabled"); 241$tmp = $objMod->getLastActivationInfo(); 242$authorid = $tmp['authorid']; 243if ($authorid > 0) 244{ 245 $tmpuser = new User($db); 246 $tmpuser->fetch($authorid); 247 $text .= '<br><span class="opacitymedium">'.$langs->trans("LastActivationAuthor").':</span> '; 248 $text .= $tmpuser->getNomUrl(1); 249} 250$ip = $tmp['ip']; 251if ($ip) 252{ 253 $text .= '<br><span class="opacitymedium">'.$langs->trans("LastActivationIP").':</span> '; 254 $text .= $ip; 255} 256 257$moreinfo = $text; 258 259$title = ($modulename ? $modulename : $moduledesc); 260 261print '<div class="centpercent">'; 262 263$picto = 'object_'.$objMod->picto; 264 265print load_fiche_titre(($modulename ? $modulename : $moduledesc), $moreinfo, $picto, 0, '', 'titlemodulehelp'); 266print '<br>'; 267 268print dol_get_fiche_head($head, $mode, '', -1); 269 270if (!$modulename) 271{ 272 dol_syslog("Error for module ".$key." - Property name of module looks empty", LOG_WARNING); 273} 274 275// Load all lang files of module 276if (isset($objMod->langfiles) && is_array($objMod->langfiles)) 277{ 278 foreach ($objMod->langfiles as $domain) 279 { 280 $langs->load($domain); 281 } 282} 283 284 285 286 287// Version (with picto warning or not) 288$version = $objMod->getVersion(0); 289$versiontrans = ''; 290if (preg_match('/development/i', $version)) $versiontrans .= img_warning($langs->trans("Development"), 'style="float: left"'); 291if (preg_match('/experimental/i', $version)) $versiontrans .= img_warning($langs->trans("Experimental"), 'style="float: left"'); 292if (preg_match('/deprecated/i', $version)) $versiontrans .= img_warning($langs->trans("Deprecated"), 'style="float: left"'); 293$versiontrans .= $objMod->getVersion(1); 294 295// Define imginfo 296$imginfo = "info"; 297if ($objMod->isCoreOrExternalModule() == 'external') 298{ 299 $imginfo = "info_black"; 300} 301 302// Define text of description of module 303$text = ''; 304 305if ($mode == 'desc') 306{ 307 if ($moduledesc) $text .= '<br>'.$moduledesc.'<br><br><br>'; 308 309 $text .= '<span class="opacitymedium">'.$langs->trans("Version").':</span> '.$version; 310 311 $moduledescriptorfile = get_class($objMod).'.class.php'; 312 $text .= '<br><span class="opacitymedium">'.$langs->trans("DescriptorFile").':</span> '.$moduledescriptorfile; 313 314 $textexternal = ''; 315 if ($objMod->isCoreOrExternalModule() == 'external') 316 { 317 $textexternal .= '<br><span class="opacitymedium">'.$langs->trans("Origin").':</span> '.$langs->trans("ExternalModule").' - '.$langs->trans("InstalledInto", $dirofmodule); 318 if ($objMod->editor_name != 'dolibarr') $textexternal .= '<br><span class="opacitymedium">'.$langs->trans("Publisher").':</span> '.(empty($objMod->editor_name) ? $langs->trans("Unknown") : $objMod->editor_name); 319 $editor_url = $objMod->editor_url; 320 if (!preg_match('/^http/', $editor_url)) $editor_url = 'http://'.$editor_url; 321 if (!empty($objMod->editor_url) && !preg_match('/dolibarr\.org/i', $objMod->editor_url)) $textexternal .= ($objMod->editor_name != 'dolibarr' ? ' - ' : '').img_picto('', 'globe').' <a href="'.$editor_url.'" target="_blank">'.$objMod->editor_url.'</a>'; 322 $text .= $textexternal; 323 $text .= '<br>'; 324 } else { 325 $text .= '<br><span class="opacitymedium">'.$langs->trans("Origin").':</span> '.$langs->trans("Core").'<br>'; 326 } 327 328 $moduledesclong = $objMod->getDescLong(); 329 if ($moduledesclong) $text .= '<br><hr><div class="moduledesclong">'.$moduledesclong.'<div>'; 330} 331 332if ($mode == 'feature') 333{ 334 $text .= '<br><strong>'.$langs->trans("DependsOn").':</strong> '; 335 if (count($objMod->depends)) $text .= join(',', $objMod->depends); 336 else $text .= $langs->trans("None"); 337 $text .= '<br><strong>'.$langs->trans("RequiredBy").':</strong> '; 338 if (count($objMod->requiredby)) $text .= join(',', $objMod->requiredby); 339 else $text .= $langs->trans("None"); 340 341 $text .= '<br><br>'; 342 343 $text .= '<br><strong>'.$langs->trans("AddDataTables").':</strong> '; 344 $sqlfiles = dol_dir_list(dol_buildpath($moduledir.'/sql/'), 'files', 0, 'llx.*\.sql', array('\.key\.sql', '\.sql\.back')); 345 if (count($sqlfiles) > 0) 346 { 347 $text .= $langs->trans("Yes").' ('; 348 $i = 0; 349 foreach ($sqlfiles as $val) 350 { 351 $text .= ($i ? ', ' : '').preg_replace('/\.sql$/', '', preg_replace('/llx_/', '', $val['name'])); 352 $i++; 353 } 354 $text .= ')'; 355 } else $text .= $langs->trans("No"); 356 357 $text .= '<br>'; 358 359 $text .= '<br><strong>'.$langs->trans("AddDictionaries").':</strong> '; 360 if (isset($objMod->dictionaries) && isset($objMod->dictionaries['tablib']) && is_array($objMod->dictionaries['tablib']) && count($objMod->dictionaries['tablib'])) 361 { 362 $i = 0; 363 foreach ($objMod->dictionaries['tablib'] as $val) 364 { 365 $text .= ($i ? ', ' : '').$val; 366 $i++; 367 } 368 } else $text .= $langs->trans("No"); 369 370 $text .= '<br>'; 371 372 $text .= '<br><strong>'.$langs->trans("AddData").':</strong> '; 373 $filedata = dol_buildpath($moduledir.'/sql/data.sql'); 374 if (dol_is_file($filedata)) 375 { 376 $text .= $langs->trans("Yes").' ('.$moduledir.'/sql/data.sql)'; 377 } else $text .= $langs->trans("No"); 378 379 $text .= '<br>'; 380 381 $text .= '<br><strong>'.$langs->trans("AddRemoveTabs").':</strong> '; 382 if (isset($objMod->tabs) && is_array($objMod->tabs) && count($objMod->tabs)) 383 { 384 $i = 0; 385 foreach ($objMod->tabs as $val) 386 { 387 if (is_array($val)) $val = $val['data']; 388 if (is_string($val)) 389 { 390 $tmp = explode(':', $val, 3); 391 $text .= ($i ? ', ' : '').$tmp[0].':'.$tmp[1]; 392 $i++; 393 } 394 } 395 } else $text .= $langs->trans("No"); 396 397 $text .= '<br>'; 398 399 $text .= '<br><strong>'.$langs->trans("AddModels").':</strong> '; 400 if (isset($objMod->module_parts) && isset($objMod->module_parts['models']) && $objMod->module_parts['models']) 401 { 402 $text .= $langs->trans("Yes"); 403 } else $text .= $langs->trans("No"); 404 405 $text .= '<br>'; 406 407 $text .= '<br><strong>'.$langs->trans("AddSubstitutions").':</strong> '; 408 if (isset($objMod->module_parts) && isset($objMod->module_parts['substitutions']) && $objMod->module_parts['substitutions']) 409 { 410 $text .= $langs->trans("Yes"); 411 } else $text .= $langs->trans("No"); 412 413 $text .= '<br>'; 414 415 $text .= '<br><strong>'.$langs->trans("AddSheduledJobs").':</strong> '; 416 if (isset($objMod->cronjobs) && is_array($objMod->cronjobs) && count($objMod->cronjobs)) 417 { 418 $i = 0; 419 foreach ($objMod->cronjobs as $val) 420 { 421 $text .= ($i ? ', ' : '').($val['label']); 422 $i++; 423 } 424 } else $text .= $langs->trans("No"); 425 426 $text .= '<br>'; 427 428 $text .= '<br><strong>'.$langs->trans("AddTriggers").':</strong> '; 429 $moreinfoontriggerfile = ''; 430 if (isset($objMod->module_parts) && isset($objMod->module_parts['triggers']) && $objMod->module_parts['triggers']) 431 { 432 $yesno = 'Yes'; 433 } else { 434 $yesno = 'No'; 435 } 436 require_once DOL_DOCUMENT_ROOT.'/core/class/interfaces.class.php'; 437 $interfaces = new Interfaces($db); 438 $triggers = $interfaces->getTriggersList(array((($objMod->isCoreOrExternalModule() == 'external') ? '/'.$moduledir : '').'/core/triggers')); 439 foreach ($triggers as $triggercursor) 440 { 441 if ($triggercursor['module'] == $moduledir) 442 { 443 $yesno = 'Yes'; 444 $moreinfoontriggerfile = ' ('.$triggercursor['relpath'].')'; 445 } 446 } 447 448 $text .= $langs->trans($yesno).$moreinfoontriggerfile; 449 450 $text .= '<br>'; 451 452 $text .= '<br><strong>'.$langs->trans("AddBoxes").':</strong> '; 453 if (isset($objMod->boxes) && is_array($objMod->boxes) && count($objMod->boxes)) 454 { 455 $i = 0; 456 foreach ($objMod->boxes as $val) 457 { 458 $text .= ($i ? ', ' : '').($val['file'] ? $val['file'] : $val[0]); 459 $i++; 460 } 461 } else $text .= $langs->trans("No"); 462 463 $text .= '<br>'; 464 465 $text .= '<br><strong>'.$langs->trans("AddHooks").':</strong> '; 466 if (isset($objMod->module_parts) && is_array($objMod->module_parts['hooks']) && count($objMod->module_parts['hooks'])) 467 { 468 $i = 0; 469 foreach ($objMod->module_parts['hooks'] as $key => $val) 470 { 471 if ($key === 'entity') continue; 472 473 // For special values 474 if ($key === 'data') 475 { 476 if (is_array($val)) 477 { 478 foreach ($val as $value) 479 { 480 $text .= ($i ? ', ' : '').($value); 481 $i++; 482 } 483 484 continue; 485 } 486 } 487 488 $text .= ($i ? ', ' : '').($val); 489 $i++; 490 } 491 } else $text .= $langs->trans("No"); 492 493 $text .= '<br>'; 494 495 $text .= '<br><strong>'.$langs->trans("AddPermissions").':</strong> '; 496 if (isset($objMod->rights) && is_array($objMod->rights) && count($objMod->rights)) 497 { 498 $i = 0; 499 foreach ($objMod->rights as $val) 500 { 501 $text .= ($i ? ', ' : '').($val[1]); 502 $i++; 503 } 504 } else $text .= $langs->trans("No"); 505 506 $text .= '<br>'; 507 508 $text .= '<br><strong>'.$langs->trans("AddMenus").':</strong> '; 509 if (isset($objMod->menu) && !empty($objMod->menu)) // objMod can be an array or just an int 1 510 { 511 $text .= $langs->trans("Yes"); 512 } else $text .= $langs->trans("No"); 513 514 $text .= '<br>'; 515 516 $text .= '<br><strong>'.$langs->trans("AddExportProfiles").':</strong> '; 517 if (isset($objMod->export_label) && is_array($objMod->export_label) && count($objMod->export_label)) 518 { 519 $i = 0; 520 foreach ($objMod->export_label as $val) 521 { 522 $text .= ($i ? ', ' : '').($val); 523 $i++; 524 } 525 } else $text .= $langs->trans("No"); 526 527 $text .= '<br>'; 528 529 $text .= '<br><strong>'.$langs->trans("AddImportProfiles").':</strong> '; 530 if (isset($objMod->import_label) && is_array($objMod->import_label) && count($objMod->import_label)) 531 { 532 $i = 0; 533 foreach ($objMod->import_label as $val) 534 { 535 $text .= ($i ? ', ' : '').($val); 536 $i++; 537 } 538 } else $text .= $langs->trans("No"); 539 540 $text .= '<br>'; 541 542 $text .= '<br><strong>'.$langs->trans("AddOtherPagesOrServices").':</strong> '; 543 $text .= $langs->trans("DetectionNotPossible"); 544} 545 546 547if ($mode == 'changelog') 548{ 549 $changelog = $objMod->getChangeLog(); 550 if ($changelog) $text .= '<div class="moduledesclong">'.$changelog.'<div>'; 551 else $text .= '<div class="moduledesclong">'.$langs->trans("NotAvailable").'</div>'; 552} 553 554print $text; 555 556 557print dol_get_fiche_end(); 558 559print '</div>'; 560 561// End of page 562llxFooter(); 563$db->close(); 564