dolibarr  20.0.0-alpha
DolibarrModules.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2003-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
4  * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
5  * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
6  * Copyright (C) 2005-2013 Laurent Destailleur <eldy@users.sourceforge.net>
7  * Copyright (C) 2005-2024 Regis Houssin <regis.houssin@inodbox.com>
8  * Copyright (C) 2014 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
9  * Copyright (C) 2018 Josep Lluís Amador <joseplluis@lliuretic.cat>
10  * Copyright (C) 2019-2024 Frédéric France <frederic.france@free.fr>
11  * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program. If not, see <https://www.gnu.org/licenses/>.
25  */
26 
38 class DolibarrModules // Can not be abstract, because we need to instantiate it into unActivateModule to be able to disable a module whose files were removed.
39 {
43  public $db;
44 
49  public $numero;
50 
54  public $editor_name;
55 
59  public $editor_url;
60 
64  public $editor_squarred_logo;
65 
73  public $family;
74 
87  public $familyinfo;
88 
92  public $module_position = '50';
93 
102  public $name;
103 
109  public $dirs = array();
110 
114  public $boxes = array();
115 
119  public $const = array();
120 
124  public $cronjobs = array();
125 
129  public $rights;
130 
134  public $rights_admin_allowed;
135 
139  public $rights_class;
140 
141  const KEY_ID = 0;
142  const KEY_LABEL = 1;
143  const KEY_TYPE = 2; // deprecated
144  const KEY_DEFAULT = 3;
145  const KEY_FIRST_LEVEL = 4;
146  const KEY_SECOND_LEVEL = 5;
147  const KEY_MODULE = 6;
148  const KEY_ENABLED = 7;
149 
153  public $menu = array();
154 
182  public $module_parts = array();
183 
187  public $error;
188 
192  public $errors;
193 
204  public $version;
205 
210  public $lastVersion = '';
211 
216  public $needUpdate = false;
217 
223  public $description;
224 
231  public $descriptionlong;
232 
236  public $dictionaries;
237 
241  public $tabs;
242 
243  // For exports
244 
248  public $export_code;
249 
253  public $export_label;
254 
255  public $export_icon;
256 
260  public $export_enabled;
261  public $export_permission;
262  public $export_fields_array;
263  public $export_TypeFields_array; // Array of key=>type where type can be 'Numeric', 'Date', 'Text', 'Boolean', 'Status', 'List:xxx:fieldlabel:rowid'
264  public $export_entities_array;
265  public $export_aggregate_array;
266  public $export_examplevalues_array;
267  public $export_help_array;
268  public $export_special_array; // special or computed field
269  public $export_dependencies_array;
270  public $export_sql_start;
271  public $export_sql_end;
272  public $export_sql_order;
273 
274 
275  // For import
276 
280  public $import_code;
281 
285  public $import_label;
286 
287  public $import_icon;
288  public $import_entities_array;
289  public $import_tables_array;
290  public $import_tables_creator_array;
291  public $import_fields_array;
292  public $import_fieldshidden_array;
293  public $import_convertvalue_array;
294  public $import_regex_array;
295  public $import_examplevalues_array;
296  public $import_updatekeys_array;
297  public $import_run_sql_after_array;
298  public $import_TypeFields_array;
299  public $import_help_array;
300 
304  public $const_name;
305 
309  public $always_enabled;
310 
314  public $disabled;
315 
319  public $core_enabled;
320 
327  public $picto;
328 
335  public $config_page_url;
336 
337 
343  public $depends;
344 
349  public $requiredby;
350 
355  public $conflictwith;
356 
360  public $langfiles;
361 
367  public $warnings_activation;
368 
374  public $warnings_activation_ext;
375 
381  public $warnings_unactivation;
382 
387  public $phpmin;
388 
389  public $phpmax;
390 
395  public $need_dolibarr_version;
396 
397  public $need_javascript_ajax;
398 
399  public $enabled_bydefault;
400 
404  public $hidden = false;
405 
409  public $url_last_version;
410 
411 
417  public function __construct($db)
418  {
419  $this->db = $db;
420  }
421  // We should but can't set this as abstract because this will make dolibarr hang
422  // after migration due to old module not implementing. We must wait PHP is able to make
423  // a try catch on Fatal error to manage this correctly.
424  // We need constructor into function unActivateModule into admin.lib.php
425 
426 
427  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
438  protected function _init($array_sql, $options = '')
439  {
440  // phpcs:enable
441  global $conf;
442  $err = 0;
443 
444  $this->db->begin();
445 
446  // Insert activation module constant
447  if (!$err) {
448  $err += $this->_active();
449  }
450 
451  // Insert new pages for tabs (into llx_const)
452  if (!$err) {
453  $err += $this->insert_tabs();
454  }
455 
456  // Insert activation of module's parts. Copy website templates into doctemplates.
457  if (!$err) {
458  $err += $this->insert_module_parts();
459  }
460 
461  // Insert constant defined by modules (into llx_const)
462  if (!$err && !preg_match('/newboxdefonly/', $options)) {
463  $err += $this->insert_const(); // Test on newboxdefonly to avoid to erase value during upgrade
464  }
465 
466  // Insert boxes def (into llx_boxes_def) and boxes setup (into llx_boxes)
467  if (!$err && !preg_match('/noboxes/', $options)) {
468  $err += $this->insert_boxes($options);
469  }
470 
471  // Insert cron job entries (entry in llx_cronjobs)
472  if (!$err) {
473  $err += $this->insert_cronjobs();
474  }
475 
476  // Insert permission definitions of module into llx_rights_def. If user is admin, grant this permission to user.
477  if (!$err) {
478  $err += $this->insert_permissions(1, null, 1);
479  }
480 
481  // Insert specific menus entries into database
482  if (!$err) {
483  $err += $this->insert_menus();
484  }
485 
486  // Create module's directories
487  if (!$err) {
488  $err += $this->create_dirs();
489  }
490 
491  // Execute addons requests
492  $num = count($array_sql);
493  for ($i = 0; $i < $num; $i++) {
494  if (!$err) {
495  $val = $array_sql[$i];
496  $sql = $val;
497  $ignoreerror = 0;
498  if (is_array($val)) {
499  $sql = $val['sql'];
500  $ignoreerror = $val['ignoreerror'];
501  }
502  // Add current entity id
503  $sql = str_replace('__ENTITY__', (string) $conf->entity, $sql);
504 
505  dol_syslog(get_class($this)."::_init ignoreerror=".$ignoreerror, LOG_DEBUG);
506  $result = $this->db->query($sql, $ignoreerror);
507  if (!$result) {
508  if (!$ignoreerror) {
509  $this->error = $this->db->lasterror();
510  $err++;
511  } else {
512  dol_syslog(get_class($this)."::_init Warning ".$this->db->lasterror(), LOG_WARNING);
513  }
514  }
515  }
516  }
517 
518  // Return code
519  if (!$err) {
520  $this->db->commit();
521 
522  $moduleNameInConf = strtolower(preg_replace('/^MAIN_MODULE_/', '', $this->const_name));
523  // two exceptions to handle
524  if ($moduleNameInConf === 'propale') {
525  $moduleNameInConf = 'propal';
526  } elseif ($moduleNameInConf === 'supplierproposal') {
527  $moduleNameInConf = 'supplier_proposal';
528  }
529 
530  $conf->modules[$moduleNameInConf] = $moduleNameInConf; // Add this module in list of enabled modules so isModEnabled() will work (conf->module->enabled must no more be used)
531 
532  return 1;
533  } else {
534  $this->db->rollback();
535  return 0;
536  }
537  }
538 
539  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
548  protected function _remove($array_sql, $options = '')
549  {
550  global $conf;
551  // phpcs:enable
552  $err = 0;
553 
554  $this->db->begin();
555 
556  // Remove activation module line (constant MAIN_MODULE_MYMODULE in llx_const)
557  if (!$err) {
558  $err += $this->_unactive();
559  }
560 
561  // Remove activation of module's new tabs (MAIN_MODULE_MYMODULE_TABS_XXX in llx_const)
562  if (!$err) {
563  $err += $this->delete_tabs();
564  }
565 
566  // Remove activation of module's parts (MAIN_MODULE_MYMODULE_XXX in llx_const)
567  if (!$err) {
568  $err += $this->delete_module_parts();
569  }
570 
571  // Remove constants defined by modules
572  if (!$err) {
573  $err += $this->delete_const();
574  }
575 
576  // Remove list of module's available boxes (entry in llx_boxes)
577  if (!$err && !preg_match('/(newboxdefonly|noboxes)/', $options)) {
578  $err += $this->delete_boxes(); // We don't have to delete if option ask to keep boxes safe or ask to add new box def only
579  }
580 
581  // Remove list of module's cron job entries (entry in llx_cronjobs)
582  if (!$err) {
583  $err += $this->delete_cronjobs();
584  }
585 
586  // Remove module's permissions from list of available permissions (entries in llx_rights_def)
587  if (!$err) {
588  $err += $this->delete_permissions();
589  }
590 
591  // Remove module's menus (entries in llx_menu)
592  if (!$err) {
593  $err += $this->delete_menus();
594  }
595 
596  // Remove module's directories
597  if (!$err) {
598  $err += $this->delete_dirs();
599  }
600 
601  // Run complementary sql requests
602  $num = count((array) $array_sql);
603  for ($i = 0; $i < $num; $i++) {
604  if (!$err) {
605  dol_syslog(get_class($this)."::_remove", LOG_DEBUG);
606  $result = $this->db->query($array_sql[$i]);
607  if (!$result) {
608  $this->error = $this->db->error();
609  $err++;
610  }
611  }
612  }
613 
614  // Return code
615  if (!$err) {
616  $this->db->commit();
617 
618  // Disable modules
619  $moduleNameInConf = strtolower(preg_replace('/^MAIN_MODULE_/', '', $this->const_name));
620  // two exceptions to handle
621  if ($moduleNameInConf === 'propale') {
622  $moduleNameInConf = 'propal';
623  } elseif ($moduleNameInConf === 'supplierproposal') {
624  $moduleNameInConf = 'supplier_proposal';
625  }
626 
627  unset($conf->modules[$moduleNameInConf]); // Add this module in list of enabled modules so isModEnabled() will work (conf->module->enabled must no more be used)
628 
629  return 1;
630  } else {
631  $this->db->rollback();
632  return 0;
633  }
634  }
635 
636 
643  public function getName()
644  {
645  global $langs;
646  $langs->load("admin");
647 
648  if ($langs->transnoentitiesnoconv("Module".$this->numero."Name") != "Module".$this->numero."Name") {
649  // If module name translation exists
650  return $langs->transnoentitiesnoconv("Module".$this->numero."Name");
651  } else {
652  // If module name translation using it's unique id does not exist, we try to use its name to find translation
653  if (is_array($this->langfiles)) {
654  foreach ($this->langfiles as $val) {
655  if ($val) {
656  $langs->load($val);
657  }
658  }
659  }
660 
661  if ($langs->trans("Module".$this->name."Name") != "Module".$this->name."Name") {
662  // If module name translation exists
663  return $langs->transnoentitiesnoconv("Module".$this->name."Name");
664  }
665 
666  // Last chance with simple label
667  return $langs->transnoentitiesnoconv($this->name);
668  }
669  }
670 
671 
677  public function getDesc()
678  {
679  global $langs;
680  $langs->load("admin");
681 
682  if ($langs->transnoentitiesnoconv("Module".$this->numero."Desc") != "Module".$this->numero."Desc") {
683  // If module description translation exists
684  return $langs->transnoentitiesnoconv("Module".$this->numero."Desc");
685  } else {
686  // If module description translation does not exist using its unique id, we can use its name to find translation
687  if (is_array($this->langfiles)) {
688  foreach ($this->langfiles as $val) {
689  if ($val) {
690  $langs->load($val);
691  }
692  }
693  }
694 
695  if ($langs->transnoentitiesnoconv("Module".$this->name."Desc") != "Module".$this->name."Desc") {
696  // If module name translation exists
697  return $langs->trans("Module".$this->name."Desc");
698  }
699 
700  // Last chance with simple label
701  return $langs->trans($this->description);
702  }
703  }
704 
711  public function getDescLong()
712  {
713  global $langs;
714  $langs->load("admin");
715 
716  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
717  include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
718 
719  $content = '';
720  $pathoffile = $this->getDescLongReadmeFound();
721 
722  if ($pathoffile) { // Mostly for external modules
723  $content = file_get_contents($pathoffile, false, null, 0, 1024 * 1024); // Max size loaded 1Mb
724 
725  if ((float) DOL_VERSION >= 6.0) {
726  @include_once DOL_DOCUMENT_ROOT.'/core/lib/parsemd.lib.php';
727 
728  $content = dolMd2Html(
729  $content,
730  'parsedown',
731  array(
732  'doc/' => dol_buildpath(strtolower($this->name).'/doc/', 1),
733  'img/' => dol_buildpath(strtolower($this->name).'/img/', 1),
734  'images/' => dol_buildpath(strtolower($this->name).'/images/', 1),
735  )
736  );
737 
738  $content = preg_replace('/<a href="/', '<a target="_blank" rel="noopener noreferrer" href="', $content);
739  } else {
740  $content = nl2br($content);
741  }
742  } else {
743  // Mostly for internal modules
744  if (!empty($this->descriptionlong)) {
745  if (is_array($this->langfiles)) {
746  foreach ($this->langfiles as $val) {
747  if ($val) {
748  $langs->load($val);
749  }
750  }
751  }
752 
753  $content = $langs->transnoentitiesnoconv($this->descriptionlong);
754  }
755  }
756 
757  return $content;
758  }
759 
765  public function getDescLongReadmeFound()
766  {
767  global $langs;
768 
769  $filefound = false;
770 
771  // Define path to file README.md.
772  // First check README-la_LA.md then README-la.md then README.md
773  $pathoffile = dol_buildpath(strtolower($this->name).'/README-'.$langs->defaultlang.'.md', 0);
774  if (dol_is_file($pathoffile)) {
775  $filefound = true;
776  }
777  if (!$filefound) {
778  $tmp = explode('_', $langs->defaultlang);
779  $pathoffile = dol_buildpath(strtolower($this->name).'/README-'.$tmp[0].'.md', 0);
780  if (dol_is_file($pathoffile)) {
781  $filefound = true;
782  }
783  }
784  if (!$filefound) {
785  $pathoffile = dol_buildpath(strtolower($this->name).'/README.md', 0);
786  if (dol_is_file($pathoffile)) {
787  $filefound = true;
788  }
789  }
790 
791  return ($filefound ? $pathoffile : '');
792  }
793 
794 
800  public function getChangeLog()
801  {
802  global $langs;
803  $langs->load("admin");
804 
805  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
806  include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
807 
808  $filefound = false;
809 
810  // Define path to file README.md.
811  // First check ChangeLog-la_LA.md then ChangeLog.md
812  $pathoffile = dol_buildpath(strtolower($this->name).'/ChangeLog-'.$langs->defaultlang.'.md', 0);
813  if (dol_is_file($pathoffile)) {
814  $filefound = true;
815  }
816  if (!$filefound) {
817  $pathoffile = dol_buildpath(strtolower($this->name).'/ChangeLog.md', 0);
818  if (dol_is_file($pathoffile)) {
819  $filefound = true;
820  }
821  }
822 
823  if ($filefound) { // Mostly for external modules
824  $content = file_get_contents($pathoffile);
825 
826  if ((float) DOL_VERSION >= 6.0) {
827  @include_once DOL_DOCUMENT_ROOT.'/core/lib/parsemd.lib.php';
828 
829  $content = dolMd2Html($content, 'parsedown', array('doc/' => dol_buildpath(strtolower($this->name).'/doc/', 1)));
830  } else {
831  $content = nl2br($content);
832  }
833  } else {
834  $content = '';
835  }
836 
837  return $content;
838  }
839 
845  public function getPublisher()
846  {
847  return $this->editor_name;
848  }
849 
855  public function getPublisherUrl()
856  {
857  return $this->editor_url;
858  }
859 
868  public function getVersion($translated = 1)
869  {
870  global $langs;
871  $langs->load("admin");
872 
873  $ret = '';
874 
875  $newversion = preg_replace('/_deprecated/', '', $this->version);
876  if ($newversion == 'experimental') {
877  $ret = ($translated ? $langs->transnoentitiesnoconv("VersionExperimental") : $newversion);
878  } elseif ($newversion == 'development') {
879  $ret = ($translated ? $langs->transnoentitiesnoconv("VersionDevelopment") : $newversion);
880  } elseif ($newversion == 'dolibarr') {
881  $ret = DOL_VERSION;
882  } elseif ($newversion) {
883  $ret = $newversion;
884  } else {
885  $ret = ($translated ? $langs->transnoentitiesnoconv("VersionUnknown") : 'unknown');
886  }
887 
888  if (preg_match('/_deprecated/', $this->version)) {
889  $ret .= ($translated ? ' ('.$langs->transnoentitiesnoconv("Deprecated").')' : $this->version);
890  }
891  return $ret;
892  }
893 
899  public function getModulePosition()
900  {
901  if (in_array($this->version, array('dolibarr', 'experimental', 'development'))) { // core module
902  return $this->module_position;
903  } else { // external module
904  if ($this->module_position >= 100000) {
905  return $this->module_position;
906  } else {
907  $position = intval($this->module_position) + 100000;
908  return strval($position);
909  }
910  }
911  }
912 
920  public function isCoreOrExternalModule()
921  {
922  if ($this->version == 'dolibarr' || $this->version == 'dolibarr_deprecated') {
923  return 'core';
924  }
925  if (!empty($this->version) && !in_array($this->version, array('experimental', 'development'))) {
926  return 'external';
927  }
928  if (!empty($this->editor_name) || !empty($this->editor_url)) {
929  return 'external';
930  }
931  if ($this->numero >= 100000) {
932  return 'external';
933  }
934  return 'unknown';
935  }
936 
937 
943  public function getLangFilesArray()
944  {
945  return $this->langfiles;
946  }
947 
955  public function getExportDatasetLabel($r)
956  {
957  global $langs;
958 
959  $langstring = "ExportDataset_".$this->export_code[$r];
960  if ($langs->trans($langstring) == $langstring) {
961  // Translation not found
962  return $langs->trans($this->export_label[$r]);
963  } else {
964  // Translation found
965  return $langs->trans($langstring);
966  }
967  }
968 
969 
977  public function getImportDatasetLabel($r)
978  {
979  global $langs;
980 
981  $langstring = "ImportDataset_".$this->import_code[$r];
982  //print "x".$langstring;
983  if ($langs->trans($langstring) == $langstring) {
984  // Translation not found
985  return $langs->transnoentitiesnoconv($this->import_label[$r]);
986  } else {
987  // Translation found
988  return $langs->transnoentitiesnoconv($langstring);
989  }
990  }
991 
992 
998  public function getLastActivationDate()
999  {
1000  global $conf;
1001 
1002  $err = 0;
1003 
1004  $sql = "SELECT tms FROM ".MAIN_DB_PREFIX."const";
1005  $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
1006  $sql .= " AND entity IN (0, ".((int) $conf->entity).")";
1007 
1008  dol_syslog(get_class($this)."::getLastActiveDate", LOG_DEBUG);
1009  $resql = $this->db->query($sql);
1010  if (!$resql) {
1011  $err++;
1012  } else {
1013  $obj = $this->db->fetch_object($resql);
1014  if ($obj) {
1015  return $this->db->jdate($obj->tms);
1016  }
1017  }
1018 
1019  return '';
1020  }
1021 
1022 
1028  public function getLastActivationInfo()
1029  {
1030  global $conf;
1031 
1032  $err = 0;
1033 
1034  $sql = "SELECT tms, note FROM ".MAIN_DB_PREFIX."const";
1035  $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
1036  $sql .= " AND entity IN (0, ".$conf->entity.")";
1037 
1038  dol_syslog(get_class($this)."::getLastActiveDate", LOG_DEBUG);
1039  $resql = $this->db->query($sql);
1040  if (!$resql) {
1041  $err++;
1042  } else {
1043  $obj = $this->db->fetch_object($resql);
1044  if ($obj) {
1045  $tmp = array();
1046  if ($obj->note) {
1047  $tmp = json_decode($obj->note, true);
1048  }
1049  return array(
1050  'authorid' => empty($tmp['authorid']) ? '' : $tmp['authorid'],
1051  'ip' => empty($tmp['ip']) ? '' : $tmp['ip'],
1052  'lastactivationdate' => $this->db->jdate($obj->tms),
1053  'lastactivationversion' => (!empty($tmp['lastactivationversion']) ? $tmp['lastactivationversion'] : 'unknown'),
1054  );
1055  }
1056  }
1057 
1058  return array();
1059  }
1060 
1061 
1062  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1068  protected function _active()
1069  {
1070  // phpcs:enable
1071  global $conf, $user;
1072 
1073  $err = 0;
1074 
1075  // Common module
1076  $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
1077 
1078  $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
1079  $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
1080  $sql .= " AND entity IN (0, ".$entity.")";
1081 
1082  dol_syslog(get_class($this)."::_active delete activation constant", LOG_DEBUG);
1083  $resql = $this->db->query($sql);
1084  if (!$resql) {
1085  $err++;
1086  }
1087 
1088  $note = json_encode(
1089  array(
1090  'authorid' => (is_object($user) ? $user->id : 0),
1091  'ip' => (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']),
1092  'lastactivationversion' => $this->version,
1093  )
1094  );
1095 
1096  $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (name, value, visible, entity, note) VALUES";
1097  $sql .= " (".$this->db->encrypt($this->const_name);
1098  $sql .= ", ".$this->db->encrypt('1');
1099  $sql .= ", 0, ".((int) $entity);
1100  $sql .= ", '".$this->db->escape($note)."')";
1101 
1102  dol_syslog(get_class($this)."::_active insert activation constant", LOG_DEBUG);
1103  $resql = $this->db->query($sql);
1104  if (!$resql) {
1105  $err++;
1106  }
1107 
1108  return $err;
1109  }
1110 
1111 
1112  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1118  protected function _unactive()
1119  {
1120  // phpcs:enable
1121  global $conf;
1122 
1123  $err = 0;
1124 
1125  // Common module
1126  $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
1127 
1128  $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
1129  $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
1130  $sql .= " AND entity IN (0, ".$entity.")";
1131 
1132  dol_syslog(get_class($this)."::_unactive", LOG_DEBUG);
1133  $this->db->query($sql);
1134 
1135  return $err;
1136  }
1137 
1138 
1139  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps,PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1155  protected function _load_tables($reldir, $onlywithsuffix = '')
1156  {
1157  // phpcs:enable
1158  global $conf;
1159 
1160  $error = 0;
1161  $dirfound = 0;
1162  $ok = 1;
1163 
1164  if (empty($reldir)) {
1165  return 1;
1166  }
1167 
1168  include_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
1169 
1170  foreach ($conf->file->dol_document_root as $dirroot) {
1171  if ($ok == 1) {
1172  $dirsql = $dirroot.$reldir;
1173  $ok = 0;
1174 
1175  // We will loop on xxx/, xxx/tables/, xxx/data/
1176  $listofsubdir = array('', 'tables/', 'data/');
1177  if ($this->db->type == 'pgsql') {
1178  $listofsubdir[] = '../pgsql/functions/';
1179  }
1180 
1181  foreach ($listofsubdir as $subdir) {
1182  $dir = $dirsql.$subdir;
1183 
1184  $handle = @opendir($dir); // Dir may not exists
1185  if (is_resource($handle)) {
1186  $dirfound++;
1187 
1188  // Run llx_mytable.sql files, then llx_mytable_*.sql
1189  $files = array();
1190  while (($file = readdir($handle)) !== false) {
1191  $files[] = $file;
1192  }
1193  sort($files);
1194  foreach ($files as $file) {
1195  if ($onlywithsuffix) {
1196  if (!preg_match('/\-'.preg_quote($onlywithsuffix, '/').'\./i', $file)) {
1197  //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
1198  continue;
1199  } else {
1200  //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
1201  }
1202  }
1203  if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'llx_') {
1204  $result = run_sql($dir.$file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
1205  if ($result <= 0) {
1206  $error++;
1207  }
1208  }
1209  }
1210 
1211  rewinddir($handle);
1212 
1213  // Run llx_mytable.key.sql files (Must be done after llx_mytable.sql) then then llx_mytable_*.key.sql
1214  $files = array();
1215  while (($file = readdir($handle)) !== false) {
1216  $files[] = $file;
1217  }
1218  sort($files);
1219  foreach ($files as $file) {
1220  if ($onlywithsuffix) {
1221  if (!preg_match('/\-'.preg_quote($onlywithsuffix, '/').'\./i', $file)) {
1222  //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
1223  continue;
1224  } else {
1225  //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
1226  }
1227  }
1228  if (preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'llx_') {
1229  $result = run_sql($dir.$file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
1230  if ($result <= 0) {
1231  $error++;
1232  }
1233  }
1234  }
1235 
1236  rewinddir($handle);
1237 
1238  // Run functions-xxx.sql files (Must be done after llx_mytable.key.sql)
1239  $files = array();
1240  while (($file = readdir($handle)) !== false) {
1241  $files[] = $file;
1242  }
1243  sort($files);
1244  foreach ($files as $file) {
1245  if ($onlywithsuffix) {
1246  if (!preg_match('/\-'.preg_quote($onlywithsuffix, '/').'\./i', $file)) {
1247  //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
1248  continue;
1249  } else {
1250  //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
1251  }
1252  }
1253  if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 9) == 'functions') {
1254  $result = run_sql($dir.$file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
1255  if ($result <= 0) {
1256  $error++;
1257  }
1258  }
1259  }
1260 
1261  rewinddir($handle);
1262 
1263  // Run data_xxx.sql files (Must be done after llx_mytable.key.sql)
1264  $files = array();
1265  while (($file = readdir($handle)) !== false) {
1266  $files[] = $file;
1267  }
1268  sort($files);
1269  foreach ($files as $file) {
1270  if ($onlywithsuffix) {
1271  if (!preg_match('/\-'.preg_quote($onlywithsuffix, '/').'\./i', $file)) {
1272  //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
1273  continue;
1274  } else {
1275  //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
1276  }
1277  }
1278  if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'data') {
1279  $result = run_sql($dir.$file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
1280  if ($result <= 0) {
1281  $error++;
1282  }
1283  }
1284  }
1285 
1286  rewinddir($handle);
1287 
1288  // Run update_xxx.sql files
1289  $files = array();
1290  while (($file = readdir($handle)) !== false) {
1291  $files[] = $file;
1292  }
1293  sort($files);
1294  foreach ($files as $file) {
1295  if ($onlywithsuffix) {
1296  if (!preg_match('/\-'.preg_quote($onlywithsuffix, '/').'\./i', $file)) {
1297  //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
1298  continue;
1299  } else {
1300  //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
1301  }
1302  }
1303  if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 6) == 'update') {
1304  $result = run_sql($dir.$file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
1305  if ($result <= 0) {
1306  $error++;
1307  }
1308  }
1309  }
1310 
1311  closedir($handle);
1312  }
1313  }
1314 
1315  if ($error == 0) {
1316  $ok = 1;
1317  }
1318  }
1319  }
1320 
1321  if (!$dirfound) {
1322  dol_syslog("A module wants to load sql files from ".$reldir." but this directory was not found.", LOG_WARNING);
1323  }
1324  return $ok;
1325  }
1326 
1327 
1328  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1336  public function insert_boxes($option = '')
1337  {
1338  // phpcs:enable
1339  include_once DOL_DOCUMENT_ROOT.'/core/class/infobox.class.php';
1340 
1341  global $conf;
1342 
1343  $err = 0;
1344 
1345  if (is_array($this->boxes)) {
1346  dol_syslog(get_class($this)."::insert_boxes", LOG_DEBUG);
1347 
1348  $pos_name = InfoBox::getListOfPagesForBoxes();
1349 
1350  foreach ($this->boxes as $key => $value) {
1351  $file = isset($this->boxes[$key]['file']) ? $this->boxes[$key]['file'] : '';
1352  $note = isset($this->boxes[$key]['note']) ? $this->boxes[$key]['note'] : '';
1353  $enabledbydefaulton = isset($this->boxes[$key]['enabledbydefaulton']) ? $this->boxes[$key]['enabledbydefaulton'] : 'Home';
1354 
1355  if (empty($file)) {
1356  $file = isset($this->boxes[$key][1]) ? $this->boxes[$key][1] : ''; // For backward compatibility
1357  }
1358  if (empty($note)) {
1359  $note = isset($this->boxes[$key][2]) ? $this->boxes[$key][2] : ''; // For backward compatibility
1360  }
1361 
1362  // Search if boxes def already present
1363  $sql = "SELECT count(*) as nb FROM ".MAIN_DB_PREFIX."boxes_def";
1364  $sql .= " WHERE file = '".$this->db->escape($file)."'";
1365  $sql .= " AND entity = ".$conf->entity;
1366  if ($note) {
1367  $sql .= " AND note ='".$this->db->escape($note)."'";
1368  }
1369 
1370  $result = $this->db->query($sql);
1371  if ($result) {
1372  $obj = $this->db->fetch_object($result);
1373  if ($obj->nb == 0) {
1374  $this->db->begin();
1375 
1376  if (!$err) {
1377  $sql = "INSERT INTO ".MAIN_DB_PREFIX."boxes_def (file, entity, note)";
1378  $sql .= " VALUES ('".$this->db->escape($file)."', ";
1379  $sql .= $conf->entity.", ";
1380  $sql .= $note ? "'".$this->db->escape($note)."'" : "null";
1381  $sql .= ")";
1382 
1383  dol_syslog(get_class($this)."::insert_boxes", LOG_DEBUG);
1384  $resql = $this->db->query($sql);
1385  if (!$resql) {
1386  $err++;
1387  }
1388  }
1389  if (!$err && !preg_match('/newboxdefonly/', $option)) {
1390  $lastid = $this->db->last_insert_id(MAIN_DB_PREFIX."boxes_def", "rowid");
1391 
1392  foreach ($pos_name as $key2 => $val2) {
1393  //print 'key2='.$key2.'-val2='.$val2."<br>\n";
1394  if ($enabledbydefaulton && $val2 != $enabledbydefaulton) {
1395  continue; // Not enabled by default onto this page.
1396  }
1397 
1398  $sql = "INSERT INTO ".MAIN_DB_PREFIX."boxes (box_id, position, box_order, fk_user, entity)";
1399  $sql .= " VALUES (".((int) $lastid).", ".((int) $key2).", '0', 0, ".((int) $conf->entity).")";
1400 
1401  dol_syslog(get_class($this)."::insert_boxes onto page ".$key2."=".$val2, LOG_DEBUG);
1402  $resql = $this->db->query($sql);
1403  if (!$resql) {
1404  $err++;
1405  }
1406  }
1407  }
1408 
1409  if (!$err) {
1410  $this->db->commit();
1411  } else {
1412  $this->error = $this->db->lasterror();
1413  $this->db->rollback();
1414  }
1415  }
1416  // else box already registered into database
1417  } else {
1418  $this->error = $this->db->lasterror();
1419  $err++;
1420  }
1421  }
1422  }
1423 
1424  return $err;
1425  }
1426 
1427 
1428  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1434  public function delete_boxes()
1435  {
1436  // phpcs:enable
1437  global $conf;
1438 
1439  $err = 0;
1440 
1441  if (is_array($this->boxes)) {
1442  foreach ($this->boxes as $key => $value) {
1443  //$titre = $this->boxes[$key][0];
1444  if (empty($this->boxes[$key]['file'])) {
1445  $file = isset($this->boxes[$key][1]) ? $this->boxes[$key][1] : ''; // For backward compatibility
1446  } else {
1447  $file = $this->boxes[$key]['file'];
1448  }
1449 
1450  //$note = $this->boxes[$key][2];
1451 
1452  // TODO If the box is also included by another module and the other module is still on, we should not remove it.
1453  // For the moment, we manage this with hard coded exception
1454  //print "Remove box ".$file.'<br>';
1455  if ($file == 'box_graph_product_distribution.php') {
1456  if (isModEnabled("product") || isModEnabled("service")) {
1457  dol_syslog("We discard deleting module ".$file." because another module still active requires it.");
1458  continue;
1459  }
1460  }
1461 
1462  if ($this->db->type == 'sqlite3') {
1463  // sqlite doesn't support "USING" syntax.
1464  // TODO: remove this dependency.
1465  $sql = "DELETE FROM ".MAIN_DB_PREFIX."boxes ";
1466  $sql .= "WHERE ".MAIN_DB_PREFIX."boxes.box_id IN (";
1467  $sql .= "SELECT ".MAIN_DB_PREFIX."boxes_def.rowid ";
1468  $sql .= "FROM ".MAIN_DB_PREFIX."boxes_def ";
1469  $sql .= "WHERE ".MAIN_DB_PREFIX."boxes_def.file = '".$this->db->escape($file)."') ";
1470  $sql .= "AND ".MAIN_DB_PREFIX."boxes.entity = ".$conf->entity;
1471  } else {
1472  $sql = "DELETE FROM ".MAIN_DB_PREFIX."boxes";
1473  $sql .= " USING ".MAIN_DB_PREFIX."boxes, ".MAIN_DB_PREFIX."boxes_def";
1474  $sql .= " WHERE ".MAIN_DB_PREFIX."boxes.box_id = ".MAIN_DB_PREFIX."boxes_def.rowid";
1475  $sql .= " AND ".MAIN_DB_PREFIX."boxes_def.file = '".$this->db->escape($file)."'";
1476  $sql .= " AND ".MAIN_DB_PREFIX."boxes.entity = ".$conf->entity;
1477  }
1478 
1479  dol_syslog(get_class($this)."::delete_boxes", LOG_DEBUG);
1480  $resql = $this->db->query($sql);
1481  if (!$resql) {
1482  $this->error = $this->db->lasterror();
1483  $err++;
1484  }
1485 
1486  $sql = "DELETE FROM ".MAIN_DB_PREFIX."boxes_def";
1487  $sql .= " WHERE file = '".$this->db->escape($file)."'";
1488  $sql .= " AND entity = ".$conf->entity; // Do not use getEntity here, we want to delete only in current company
1489 
1490  dol_syslog(get_class($this)."::delete_boxes", LOG_DEBUG);
1491  $resql = $this->db->query($sql);
1492  if (!$resql) {
1493  $this->error = $this->db->lasterror();
1494  $err++;
1495  }
1496  }
1497  }
1498 
1499  return $err;
1500  }
1501 
1502  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1508  public function insert_cronjobs()
1509  {
1510  // phpcs:enable
1511  include_once DOL_DOCUMENT_ROOT.'/core/class/infobox.class.php';
1512 
1513  global $conf;
1514 
1515  $err = 0;
1516 
1517  if (is_array($this->cronjobs)) {
1518  dol_syslog(get_class($this)."::insert_cronjobs", LOG_DEBUG);
1519 
1520  foreach ($this->cronjobs as $key => $value) {
1521  $entity = isset($this->cronjobs[$key]['entity']) ? $this->cronjobs[$key]['entity'] : $conf->entity;
1522  $label = isset($this->cronjobs[$key]['label']) ? $this->cronjobs[$key]['label'] : '';
1523  $jobtype = isset($this->cronjobs[$key]['jobtype']) ? $this->cronjobs[$key]['jobtype'] : '';
1524  $class = isset($this->cronjobs[$key]['class']) ? $this->cronjobs[$key]['class'] : '';
1525  $objectname = isset($this->cronjobs[$key]['objectname']) ? $this->cronjobs[$key]['objectname'] : '';
1526  $method = isset($this->cronjobs[$key]['method']) ? $this->cronjobs[$key]['method'] : '';
1527  $command = isset($this->cronjobs[$key]['command']) ? $this->cronjobs[$key]['command'] : '';
1528  $parameters = isset($this->cronjobs[$key]['parameters']) ? $this->cronjobs[$key]['parameters'] : '';
1529  $comment = isset($this->cronjobs[$key]['comment']) ? $this->cronjobs[$key]['comment'] : '';
1530  $frequency = isset($this->cronjobs[$key]['frequency']) ? $this->cronjobs[$key]['frequency'] : '';
1531  $unitfrequency = isset($this->cronjobs[$key]['unitfrequency']) ? $this->cronjobs[$key]['unitfrequency'] : '';
1532  $priority = isset($this->cronjobs[$key]['priority']) ? $this->cronjobs[$key]['priority'] : '';
1533  $datestart = isset($this->cronjobs[$key]['datestart']) ? $this->cronjobs[$key]['datestart'] : '';
1534  $dateend = isset($this->cronjobs[$key]['dateend']) ? $this->cronjobs[$key]['dateend'] : '';
1535  $status = isset($this->cronjobs[$key]['status']) ? $this->cronjobs[$key]['status'] : '';
1536  $test = isset($this->cronjobs[$key]['test']) ? $this->cronjobs[$key]['test'] : ''; // Line must be enabled or not (so visible or not)
1537 
1538  // Search if cron entry already present
1539  $sql = "SELECT count(*) as nb FROM ".MAIN_DB_PREFIX."cronjob";
1540  //$sql .= " WHERE module_name = '".$this->db->escape(empty($this->rights_class) ?strtolower($this->name) : $this->rights_class)."'";
1541  $sql .= " WHERE label = '".$this->db->escape($label)."'";
1542  /*if ($class) {
1543  $sql .= " AND classesname = '".$this->db->escape($class)."'";
1544  }
1545  if ($objectname) {
1546  $sql .= " AND objectname = '".$this->db->escape($objectname)."'";
1547  }
1548  if ($method) {
1549  $sql .= " AND methodename = '".$this->db->escape($method)."'";
1550  }
1551  if ($command) {
1552  $sql .= " AND command = '".$this->db->escape($command)."'";
1553  }
1554  if ($parameters) {
1555  $sql .= " AND params = '".$this->db->escape($parameters)."'";
1556  }*/
1557  $sql .= " AND entity = ".((int) $entity); // Must be exact entity
1558 
1559  $now = dol_now();
1560 
1561  $result = $this->db->query($sql);
1562  if ($result) {
1563  $obj = $this->db->fetch_object($result);
1564  if ($obj->nb == 0) {
1565  $this->db->begin();
1566 
1567  if (!$err) {
1568  $sql = "INSERT INTO ".MAIN_DB_PREFIX."cronjob (module_name, datec, datestart, dateend, label, jobtype, classesname, objectname, methodename, command, params, note,";
1569  if (is_int($frequency)) {
1570  $sql .= ' frequency,';
1571  }
1572  if (is_int($unitfrequency)) {
1573  $sql .= ' unitfrequency,';
1574  }
1575  if (is_int($priority)) {
1576  $sql .= ' priority,';
1577  }
1578  if (is_int($status)) {
1579  $sql .= ' status,';
1580  }
1581  $sql .= " entity, test)";
1582  $sql .= " VALUES (";
1583  $sql .= "'".$this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class)."', ";
1584  $sql .= "'".$this->db->idate($now)."', ";
1585  $sql .= ($datestart ? "'".$this->db->idate($datestart)."'" : "'".$this->db->idate($now)."'").", ";
1586  $sql .= ($dateend ? "'".$this->db->idate($dateend)."'" : "NULL").", ";
1587  $sql .= "'".$this->db->escape($label)."', ";
1588  $sql .= "'".$this->db->escape($jobtype)."', ";
1589  $sql .= ($class ? "'".$this->db->escape($class)."'" : "null").",";
1590  $sql .= ($objectname ? "'".$this->db->escape($objectname)."'" : "null").",";
1591  $sql .= ($method ? "'".$this->db->escape($method)."'" : "null").",";
1592  $sql .= ($command ? "'".$this->db->escape($command)."'" : "null").",";
1593  $sql .= ($parameters ? "'".$this->db->escape($parameters)."'" : "null").",";
1594  $sql .= ($comment ? "'".$this->db->escape($comment)."'" : "null").",";
1595  if (is_int($frequency)) {
1596  $sql .= "'".$this->db->escape($frequency)."', ";
1597  }
1598  if (is_int($unitfrequency)) {
1599  $sql .= "'".$this->db->escape($unitfrequency)."', ";
1600  }
1601  if (is_int($priority)) {
1602  $sql .= "'".$this->db->escape($priority)."', ";
1603  }
1604  if (is_int($status)) {
1605  $sql .= ((int) $status).", ";
1606  }
1607  $sql .= $entity.",";
1608  $sql .= "'".$this->db->escape($test)."'";
1609  $sql .= ")";
1610 
1611  $resql = $this->db->query($sql);
1612  if (!$resql) {
1613  $err++;
1614  }
1615  }
1616 
1617  if (!$err) {
1618  $this->db->commit();
1619  } else {
1620  $this->error = $this->db->lasterror();
1621  $this->db->rollback();
1622  }
1623  }
1624  // else box already registered into database
1625  } else {
1626  $this->error = $this->db->lasterror();
1627  $err++;
1628  }
1629  }
1630  }
1631 
1632  return $err;
1633  }
1634 
1635 
1636  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1642  public function delete_cronjobs()
1643  {
1644  // phpcs:enable
1645  global $conf;
1646 
1647  $err = 0;
1648 
1649  if (is_array($this->cronjobs)) {
1650  $sql = "DELETE FROM ".MAIN_DB_PREFIX."cronjob";
1651  $sql .= " WHERE module_name = '".$this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class)."'";
1652  $sql .= " AND entity = ".$conf->entity;
1653  $sql .= " AND test = '1'"; // We delete on lines that are not set with a complete test that is '$conf->module->enabled' so when module is disabled, the cron is also removed.
1654  // For crons declared with a '$conf->module->enabled', there is no need to delete the line, so we don't loose setup if we reenable module.
1655 
1656  dol_syslog(get_class($this)."::delete_cronjobs", LOG_DEBUG);
1657  $resql = $this->db->query($sql);
1658  if (!$resql) {
1659  $this->error = $this->db->lasterror();
1660  $err++;
1661  }
1662  }
1663 
1664  return $err;
1665  }
1666 
1667  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1673  public function delete_tabs()
1674  {
1675  // phpcs:enable
1676  global $conf;
1677 
1678  $err = 0;
1679 
1680  $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
1681  $sql .= " WHERE ".$this->db->decrypt('name')." like '".$this->db->escape($this->const_name)."_TABS_%'";
1682  $sql .= " AND entity = ".$conf->entity;
1683 
1684  dol_syslog(get_class($this)."::delete_tabs", LOG_DEBUG);
1685  if (!$this->db->query($sql)) {
1686  $this->error = $this->db->lasterror();
1687  $err++;
1688  }
1689 
1690  return $err;
1691  }
1692 
1693  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1699  public function insert_tabs()
1700  {
1701  // phpcs:enable
1702  global $conf;
1703 
1704  $err = 0;
1705 
1706  if (!empty($this->tabs)) {
1707  dol_syslog(get_class($this)."::insert_tabs", LOG_DEBUG);
1708 
1709  $i = 0;
1710  foreach ($this->tabs as $key => $value) {
1711  if (is_array($value) && count($value) == 0) {
1712  continue; // Discard empty arrays
1713  }
1714 
1715  $entity = $conf->entity;
1716  $newvalue = $value;
1717 
1718  if (is_array($value)) {
1719  $newvalue = $value['data'];
1720  if (isset($value['entity'])) {
1721  $entity = $value['entity'];
1722  }
1723  }
1724 
1725  if ($newvalue) {
1726  $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (";
1727  $sql .= "name";
1728  $sql .= ", type";
1729  $sql .= ", value";
1730  $sql .= ", note";
1731  $sql .= ", visible";
1732  $sql .= ", entity";
1733  $sql .= ")";
1734  $sql .= " VALUES (";
1735  $sql .= $this->db->encrypt($this->const_name."_TABS_".$i);
1736  $sql .= ", 'chaine'";
1737  $sql .= ", ".$this->db->encrypt($newvalue);
1738  $sql .= ", null";
1739  $sql .= ", '0'";
1740  $sql .= ", ".((int) $entity);
1741  $sql .= ")";
1742 
1743  $resql = $this->db->query($sql);
1744  if (!$resql) {
1745  dol_syslog($this->db->lasterror(), LOG_ERR);
1746  if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1747  $this->error = $this->db->lasterror();
1748  $this->errors[] = $this->db->lasterror();
1749  $err++;
1750  break;
1751  }
1752  }
1753  }
1754  $i++;
1755  }
1756  }
1757  return $err;
1758  }
1759 
1760  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1766  public function insert_const()
1767  {
1768  // phpcs:enable
1769  global $conf;
1770 
1771  $err = 0;
1772 
1773  if (empty($this->const)) {
1774  return 0;
1775  }
1776 
1777  dol_syslog(__METHOD__, LOG_DEBUG);
1778 
1779  foreach ($this->const as $key => $value) {
1780  $name = $this->const[$key][0];
1781  $type = $this->const[$key][1];
1782  $val = $this->const[$key][2];
1783  $note = isset($this->const[$key][3]) ? $this->const[$key][3] : '';
1784  $visible = isset($this->const[$key][4]) ? $this->const[$key][4] : 0;
1785  $entity = (!empty($this->const[$key][5]) && $this->const[$key][5] != 'current') ? 0 : $conf->entity;
1786 
1787  // Clean
1788  if (empty($visible)) {
1789  $visible = '0';
1790  }
1791  if (empty($val) && $val != '0') {
1792  $val = '';
1793  }
1794 
1795  $sql = "SELECT count(*) as nb";
1796  $sql .= " FROM ".MAIN_DB_PREFIX."const";
1797  $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($name)."'";
1798  $sql .= " AND entity = ".((int) $entity);
1799 
1800  $result = $this->db->query($sql);
1801  if ($result) {
1802  $row = $this->db->fetch_row($result);
1803 
1804  if ($row[0] == 0) { // If not found
1805  $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (name,type,value,note,visible,entity)";
1806  $sql .= " VALUES (";
1807  $sql .= $this->db->encrypt($name);
1808  $sql .= ",'".$this->db->escape($type)."'";
1809  $sql .= ",".(($val != '') ? $this->db->encrypt($val) : "''");
1810  $sql .= ",".($note ? "'".$this->db->escape($note)."'" : "null");
1811  $sql .= ",'".$this->db->escape($visible)."'";
1812  $sql .= ",".$entity;
1813  $sql .= ")";
1814 
1815  if (!$this->db->query($sql)) {
1816  $err++;
1817  }
1818  } else {
1819  dol_syslog(__METHOD__." constant '".$name."' already exists", LOG_DEBUG);
1820  }
1821  } else {
1822  $err++;
1823  }
1824  }
1825 
1826  return $err;
1827  }
1828 
1829  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1835  public function delete_const()
1836  {
1837  // phpcs:enable
1838  global $conf;
1839 
1840  $err = 0;
1841 
1842  if (empty($this->const)) {
1843  return 0;
1844  }
1845 
1846  foreach ($this->const as $key => $value) {
1847  $name = $this->const[$key][0];
1848  $deleteonunactive = (!empty($this->const[$key][6])) ? 1 : 0;
1849 
1850  if ($deleteonunactive) {
1851  $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
1852  $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($name)."'";
1853  $sql .= " AND entity in (0, ".$conf->entity.")";
1854  dol_syslog(get_class($this)."::delete_const", LOG_DEBUG);
1855  if (!$this->db->query($sql)) {
1856  $this->error = $this->db->lasterror();
1857  $err++;
1858  }
1859  }
1860  }
1861 
1862  return $err;
1863  }
1864 
1865  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1874  public function insert_permissions($reinitadminperms = 0, $force_entity = null, $notrigger = 0)
1875  {
1876  // phpcs:enable
1877  global $conf, $user;
1878 
1879  $err = 0;
1880  $entity = (!empty($force_entity) ? $force_entity : $conf->entity);
1881 
1882  dol_syslog(get_class($this)."::insert_permissions", LOG_DEBUG);
1883 
1884  // Test if module is activated
1885  $sql_del = "SELECT ".$this->db->decrypt('value')." as value";
1886  $sql_del .= " FROM ".MAIN_DB_PREFIX."const";
1887  $sql_del .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
1888  $sql_del .= " AND entity IN (0,".((int) $entity).")";
1889 
1890  $resql = $this->db->query($sql_del);
1891 
1892  if ($resql) {
1893  $obj = $this->db->fetch_object($resql);
1894 
1895  if ($obj !== null && !empty($obj->value) && !empty($this->rights)) {
1896  include_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
1897 
1898  // TODO rights parameters with integer indexes are deprecated
1899  // $this->rights[$key][0] = $this->rights[$key][self::KEY_ID]
1900  // $this->rights[$key][1] = $this->rights[$key][self::KEY_LABEL]
1901  // $this->rights[$key][3] = $this->rights[$key][self::KEY_DEFAULT]
1902  // $this->rights[$key][4] = $this->rights[$key][self::KEY_FIRST_LEVEL]
1903  // $this->rights[$key][5] = $this->rights[$key][self::KEY_SECOND_LEVEL]
1904 
1905  // new parameters
1906  // $this->rights[$key][self::KEY_MODULE] // possibility to define user right for an another module (default: current module name)
1907  // $this->rights[$key][self::KEY_ENABLED] // condition to show or hide a user right (default: 1) (eg isModEnabled('anothermodule'))
1908 
1909  // If the module is active
1910  foreach ($this->rights as $key => $value) {
1911  $r_id = $this->rights[$key][self::KEY_ID]; // permission id in llx_rights_def (not unique because primary key is couple id-entity)
1912  $r_label = $this->rights[$key][self::KEY_LABEL];
1913  $r_type = $this->rights[$key][self::KEY_TYPE] ?? 'w'; // TODO deprecated
1914  $r_default = $this->rights[$key][self::KEY_DEFAULT] ?? 0;
1915  $r_perms = $this->rights[$key][self::KEY_FIRST_LEVEL] ?? '';
1916  $r_subperms = $this->rights[$key][self::KEY_SECOND_LEVEL] ?? '';
1917 
1918  // KEY_FIRST_LEVEL (perms) must not be empty
1919  if (empty($r_perms)) {
1920  continue;
1921  }
1922 
1923  // name of module (default: current module name)
1924  $r_module = (empty($this->rights_class) ? strtolower($this->name) : $this->rights_class);
1925 
1926  // name of the module from which the right comes (default: empty means same module the permission is for)
1927  $r_module_origin = '';
1928 
1929  if (isset($this->rights[$key][self::KEY_MODULE])) {
1930  // name of the module to which the right must be applied
1931  $r_module = $this->rights[$key][self::KEY_MODULE];
1932  // name of the module from which the right comes
1933  $r_module_origin = (empty($this->rights_class) ? strtolower($this->name) : $this->rights_class);
1934  }
1935 
1936  // condition to show or hide a user right (default: 1) (eg isModEnabled('anothermodule') or ($conf->global->MAIN_FEATURES_LEVEL > 0) or etc..)
1937  $r_enabled = $this->rights[$key][self::KEY_ENABLED] ?? '1';
1938 
1939  // Search if perm already present
1940  $sql = "SELECT count(*) as nb FROM ".MAIN_DB_PREFIX."rights_def";
1941  $sql .= " WHERE entity = ".((int) $entity);
1942  $sql .= " AND id = ".((int) $r_id);
1943 
1944  $resqlselect = $this->db->query($sql);
1945  if ($resqlselect) {
1946  $objcount = $this->db->fetch_object($resqlselect);
1947  if ($objcount && $objcount->nb == 0) {
1948  $sql = "INSERT INTO ".MAIN_DB_PREFIX."rights_def (";
1949  $sql .= "id";
1950  $sql .= ", entity";
1951  $sql .= ", libelle";
1952  $sql .= ", module";
1953  $sql .= ", module_origin";
1954  $sql .= ", type"; // TODO deprecated
1955  $sql .= ", bydefault";
1956  $sql .= ", perms";
1957  $sql .= ", subperms";
1958  $sql .= ", enabled";
1959  $sql .= ") VALUES (";
1960  $sql .= ((int) $r_id);
1961  $sql .= ", ".((int) $entity);
1962  $sql .= ", '".$this->db->escape($r_label)."'";
1963  $sql .= ", '".$this->db->escape($r_module)."'";
1964  $sql .= ", '".$this->db->escape($r_module_origin)."'";
1965  $sql .= ", '".$this->db->escape($r_type)."'"; // TODO deprecated
1966  $sql .= ", ".((int) $r_default);
1967  $sql .= ", '".$this->db->escape($r_perms)."'";
1968  $sql .= ", '".$this->db->escape($r_subperms)."'";
1969  $sql .= ", '".$this->db->escape($r_enabled)."'";
1970  $sql.= ")";
1971 
1972  $resqlinsert = $this->db->query($sql, 1);
1973 
1974  if (!$resqlinsert) {
1975  if ($this->db->errno() != "DB_ERROR_RECORD_ALREADY_EXISTS") {
1976  $this->error = $this->db->lasterror();
1977  $err++;
1978  break;
1979  } else {
1980  dol_syslog(get_class($this)."::insert_permissions record already exists", LOG_INFO);
1981  }
1982  }
1983 
1984  $this->db->free($resqlinsert);
1985  }
1986 
1987  $this->db->free($resqlselect);
1988  }
1989 
1990  // If we want to init permissions on admin users
1991  if (!empty($reinitadminperms)) {
1992  $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."user WHERE admin = 1";
1993  dol_syslog(get_class($this)."::insert_permissions Search all admin users", LOG_DEBUG);
1994 
1995  $resqlseladmin = $this->db->query($sql, 1);
1996 
1997  if ($resqlseladmin) {
1998  $num = $this->db->num_rows($resqlseladmin);
1999  $i = 0;
2000  while ($i < $num) {
2001  $obj2 = $this->db->fetch_object($resqlseladmin);
2002  dol_syslog(get_class($this)."::insert_permissions Add permission id ".$r_id." to user id=".$obj2->rowid);
2003 
2004  $tmpuser = new User($this->db);
2005  $result = $tmpuser->fetch($obj2->rowid);
2006  if ($result > 0) {
2007  $tmpuser->addrights($r_id, '', '', 0, 1);
2008  } else {
2009  dol_syslog(get_class($this)."::insert_permissions Failed to add the permission to user because fetch return an error", LOG_ERR);
2010  }
2011  $i++;
2012  }
2013  } else {
2014  dol_print_error($this->db);
2015  }
2016  }
2017  }
2018 
2019  if (!empty($reinitadminperms) && !empty($user->admin)) { // Reload permission for current user if defined
2020  // We reload permissions
2021  $user->clearrights();
2022  $user->getrights();
2023  }
2024  }
2025  $this->db->free($resql);
2026  } else {
2027  $this->error = $this->db->lasterror();
2028  $err++;
2029  }
2030 
2031  return $err;
2032  }
2033 
2034 
2035  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2041  public function delete_permissions()
2042  {
2043  // phpcs:enable
2044  global $conf;
2045 
2046  $err = 0;
2047 
2048  $module = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
2049 
2050  $sql = "DELETE FROM ".MAIN_DB_PREFIX."rights_def";
2051  $sql .= " WHERE (module = '".$this->db->escape($module)."' OR module_origin = '".$this->db->escape($module)."')";
2052 
2053  // Delete all entities if core module
2054  if (empty($this->core_enabled)) {
2055  $sql .= " AND entity = ".((int) $conf->entity);
2056  }
2057 
2058  dol_syslog(get_class($this)."::delete_permissions", LOG_DEBUG);
2059  if (!$this->db->query($sql)) {
2060  $this->error = $this->db->lasterror();
2061  $err++;
2062  }
2063 
2064  return $err;
2065  }
2066 
2067 
2068  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2074  public function insert_menus()
2075  {
2076  // phpcs:enable
2077  global $conf, $user;
2078 
2079  if (!is_array($this->menu) || empty($this->menu)) {
2080  return 0;
2081  }
2082 
2083  include_once DOL_DOCUMENT_ROOT.'/core/class/menubase.class.php';
2084 
2085  dol_syslog(get_class($this)."::insert_menus", LOG_DEBUG);
2086 
2087  $err = 0;
2088 
2089  // Common module
2090  $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
2091 
2092  $this->db->begin();
2093 
2094  foreach ($this->menu as $key => $value) {
2095  $menu = new Menubase($this->db);
2096  $menu->menu_handler = 'all';
2097 
2098  //$menu->module=strtolower($this->name); TODO When right_class will be same than module name
2099  $menu->module = (empty($this->rights_class) ? strtolower($this->name) : $this->rights_class);
2100 
2101  if (!$this->menu[$key]['fk_menu']) {
2102  $menu->fk_menu = 0;
2103  } else {
2104  $foundparent = 0;
2105  $fk_parent = $this->menu[$key]['fk_menu'];
2106  $reg = array();
2107  if (preg_match('/^r=/', $fk_parent)) { // old deprecated method
2108  $fk_parent = str_replace('r=', '', $fk_parent);
2109  if (isset($this->menu[$fk_parent]['rowid'])) {
2110  $menu->fk_menu = $this->menu[$fk_parent]['rowid'];
2111  $foundparent = 1;
2112  }
2113  } elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+),fk_leftmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
2114  $menu->fk_menu = -1;
2115  $menu->fk_mainmenu = $reg[1];
2116  $menu->fk_leftmenu = $reg[2];
2117  $foundparent = 1;
2118  } elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
2119  $menu->fk_menu = -1;
2120  $menu->fk_mainmenu = $reg[1];
2121  $menu->fk_leftmenu = '';
2122  $foundparent = 1;
2123  }
2124  if (!$foundparent) {
2125  $this->error = "ErrorBadDefinitionOfMenuArrayInModuleDescriptor";
2126  dol_syslog(get_class($this)."::insert_menus ".$this->error." ".$this->menu[$key]['fk_menu'], LOG_ERR);
2127  $err++;
2128  }
2129  }
2130  $menu->type = $this->menu[$key]['type'];
2131  $menu->mainmenu = isset($this->menu[$key]['mainmenu']) ? $this->menu[$key]['mainmenu'] : (isset($menu->fk_mainmenu) ? $menu->fk_mainmenu : '');
2132  $menu->leftmenu = isset($this->menu[$key]['leftmenu']) ? $this->menu[$key]['leftmenu'] : '';
2133  $menu->title = $this->menu[$key]['titre'];
2134  $menu->prefix = isset($this->menu[$key]['prefix']) ? $this->menu[$key]['prefix'] : '';
2135  $menu->url = $this->menu[$key]['url'];
2136  $menu->langs = isset($this->menu[$key]['langs']) ? $this->menu[$key]['langs'] : '';
2137  $menu->position = $this->menu[$key]['position'];
2138  $menu->perms = $this->menu[$key]['perms'];
2139  $menu->target = isset($this->menu[$key]['target']) ? $this->menu[$key]['target'] : '';
2140  $menu->user = $this->menu[$key]['user'];
2141  $menu->enabled = isset($this->menu[$key]['enabled']) ? $this->menu[$key]['enabled'] : 0;
2142  $menu->position = $this->menu[$key]['position'];
2143  $menu->entity = $entity;
2144 
2145  if (!$err) {
2146  $result = $menu->create($user); // Save menu entry into table llx_menu
2147  if ($result > 0) {
2148  $this->menu[$key]['rowid'] = $result;
2149  } else {
2150  $this->error = $menu->error;
2151  dol_syslog(get_class($this).'::insert_menus result='.$result." ".$this->error, LOG_ERR);
2152  $err++;
2153  break;
2154  }
2155  }
2156  }
2157 
2158  if (!$err) {
2159  $this->db->commit();
2160  } else {
2161  dol_syslog(get_class($this)."::insert_menus ".$this->error, LOG_ERR);
2162  $this->db->rollback();
2163  }
2164 
2165  return $err;
2166  }
2167 
2168 
2169  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2175  public function delete_menus()
2176  {
2177  // phpcs:enable
2178  global $conf;
2179 
2180  $err = 0;
2181 
2182  //$module=strtolower($this->name); TODO When right_class will be same than module name
2183  $module = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
2184 
2185  $sql = "DELETE FROM ".MAIN_DB_PREFIX."menu";
2186  $sql .= " WHERE module = '".$this->db->escape($module)."'";
2187  $sql .= " AND entity IN (0, ".$conf->entity.")";
2188 
2189  dol_syslog(get_class($this)."::delete_menus", LOG_DEBUG);
2190  $resql = $this->db->query($sql);
2191  if (!$resql) {
2192  $this->error = $this->db->lasterror();
2193  $err++;
2194  }
2195 
2196  return $err;
2197  }
2198 
2199  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2205  public function create_dirs()
2206  {
2207  // phpcs:enable
2208  global $langs, $conf;
2209 
2210  $err = 0;
2211  $name = '';
2212 
2213  if (isset($this->dirs) && is_array($this->dirs)) {
2214  foreach ($this->dirs as $key => $value) {
2215  $addtodatabase = 0;
2216 
2217  if (!is_array($value)) {
2218  $dir = $value; // Default simple mode
2219  } else {
2220  $constname = $this->const_name."_DIR_";
2221  $dir = $this->dirs[$key][1];
2222  $addtodatabase = empty($this->dirs[$key][2]) ? '' : $this->dirs[$key][2]; // Create constante in llx_const
2223  $subname = empty($this->dirs[$key][3]) ? '' : strtoupper($this->dirs[$key][3]); // Add submodule name (ex: $conf->module->submodule->dir_output)
2224  $forcename = empty($this->dirs[$key][4]) ? '' : strtoupper($this->dirs[$key][4]); // Change the module name if different
2225 
2226  if (!empty($forcename)) {
2227  $constname = 'MAIN_MODULE_'.$forcename."_DIR_";
2228  }
2229  if (!empty($subname)) {
2230  $constname = $constname.$subname."_";
2231  }
2232 
2233  $name = $constname.strtoupper($this->dirs[$key][0]);
2234  }
2235 
2236  // Define directory full path ($dir must start with "/")
2237  if (!getDolGlobalString('MAIN_MODULE_MULTICOMPANY') || $conf->entity == 1) {
2238  $fulldir = DOL_DATA_ROOT.$dir;
2239  } else {
2240  $fulldir = DOL_DATA_ROOT."/".$conf->entity.$dir;
2241  }
2242  // Create dir if it does not exists
2243  if (!empty($fulldir) && !file_exists($fulldir)) {
2244  if (dol_mkdir($fulldir, DOL_DATA_ROOT) < 0) {
2245  $this->error = $langs->trans("ErrorCanNotCreateDir", $fulldir);
2246  dol_syslog(get_class($this)."::_init ".$this->error, LOG_ERR);
2247  $err++;
2248  }
2249  }
2250 
2251  // Define the constant in database if requested (not the default mode)
2252  if (!empty($addtodatabase) && !empty($name)) {
2253  $result = $this->insert_dirs($name, $dir);
2254  if ($result) {
2255  $err++;
2256  }
2257  }
2258  }
2259  }
2260 
2261  return $err;
2262  }
2263 
2264 
2265  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2274  public function insert_dirs($name, $dir)
2275  {
2276  // phpcs:enable
2277  global $conf;
2278 
2279  $err = 0;
2280 
2281  $sql = "SELECT count(*)";
2282  $sql .= " FROM ".MAIN_DB_PREFIX."const";
2283  $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($name)."'";
2284  $sql .= " AND entity = ".$conf->entity;
2285 
2286  dol_syslog(get_class($this)."::insert_dirs", LOG_DEBUG);
2287  $result = $this->db->query($sql);
2288  if ($result) {
2289  $row = $this->db->fetch_row($result);
2290 
2291  if ($row[0] == 0) {
2292  $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (name, type, value, note, visible, entity)";
2293  $sql .= " VALUES (".$this->db->encrypt($name).", 'chaine', ".$this->db->encrypt($dir).", '".$this->db->escape("Directory for module ".$this->name)."', '0', ".((int) $conf->entity).")";
2294 
2295  dol_syslog(get_class($this)."::insert_dirs", LOG_DEBUG);
2296  $this->db->query($sql);
2297  }
2298  } else {
2299  $this->error = $this->db->lasterror();
2300  $err++;
2301  }
2302 
2303  return $err;
2304  }
2305 
2306 
2307  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2313  public function delete_dirs()
2314  {
2315  // phpcs:enable
2316  global $conf;
2317 
2318  $err = 0;
2319 
2320  $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
2321  $sql .= " WHERE ".$this->db->decrypt('name')." LIKE '".$this->db->escape($this->const_name)."_DIR_%'";
2322  $sql .= " AND entity = ".$conf->entity;
2323 
2324  dol_syslog(get_class($this)."::delete_dirs", LOG_DEBUG);
2325  if (!$this->db->query($sql)) {
2326  $this->error = $this->db->lasterror();
2327  $err++;
2328  }
2329 
2330  return $err;
2331  }
2332 
2333  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2340  public function insert_module_parts()
2341  {
2342  // phpcs:enable
2343  global $conf, $langs;
2344 
2345  $error = 0;
2346 
2347  if (is_array($this->module_parts)) {
2348  if (empty($this->module_parts['icon']) && !empty($this->picto) && preg_match('/^fa\-/', $this->picto)) {
2349  $this->module_parts['icon'] = $this->picto;
2350  }
2351 
2352  foreach ($this->module_parts as $key => $value) {
2353  if (is_array($value) && count($value) == 0) {
2354  continue; // Discard empty arrays
2355  }
2356 
2357  // If module brings website templates, we must generate the zip like we do whenenabling the website module
2358  if ($key == 'websitetemplates' && $value == 1) {
2359  $srcroot = dol_buildpath('/'.strtolower($this->name).'/doctemplates/websites');
2360 
2361  // Copy templates in dir format (recommended) into zip file
2362  $docs = dol_dir_list($srcroot, 'directories', 0, 'website_.*$');
2363  foreach ($docs as $cursorfile) {
2364  $src = $srcroot.'/'.$cursorfile['name'];
2365  $dest = DOL_DATA_ROOT.'/doctemplates/websites/'.$cursorfile['name'];
2366 
2367  dol_delete_file($dest.'.zip');
2368 
2369  // Compress it
2370  global $errormsg;
2371  $errormsg = '';
2372  $result = dol_compress_dir($src, $dest.'.zip', 'zip');
2373  if ($result < 0) {
2374  $error++;
2375  $this->error = ($errormsg ? $errormsg : $langs->trans('ErrorFailToCreateZip', $dest));
2376  $this->errors[] = ($errormsg ? $errormsg : $langs->trans('ErrorFailToCreateZip', $dest));
2377  }
2378  }
2379 
2380  // Copy also the preview website_xxx.jpg file
2381  $docs = dol_dir_list($srcroot, 'files', 0, 'website_.*\.jpg$');
2382  foreach ($docs as $cursorfile) {
2383  $src = $srcroot.'/'.$cursorfile['name'];
2384  $dest = DOL_DATA_ROOT.'/doctemplates/websites/'.$cursorfile['name'];
2385 
2386  dol_copy($src, $dest);
2387  }
2388  }
2389 
2390  $entity = $conf->entity; // Reset the current entity
2391  $newvalue = $value;
2392 
2393  // Serialize array parameters
2394  if (is_array($value)) {
2395  // Can defined other parameters
2396  // Example when $key='hooks', then $value is an array('data'=>array('hookcontext1','hookcontext2'), 'entity'=>X)
2397  if (isset($value['data']) && is_array($value['data'])) {
2398  $newvalue = json_encode($value['data']);
2399  if (isset($value['entity'])) {
2400  $entity = $value['entity'];
2401  }
2402  } elseif (isset($value['data']) && !is_array($value['data'])) {
2403  $newvalue = $value['data'];
2404  if (isset($value['entity'])) {
2405  $entity = $value['entity'];
2406  }
2407  } else { // when hook is declared with syntax 'hook'=>array('hookcontext1','hookcontext2',...)
2408  $newvalue = json_encode($value);
2409  }
2410  }
2411 
2412  if (!empty($newvalue)) {
2413  $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (";
2414  $sql .= "name";
2415  $sql .= ", type";
2416  $sql .= ", value";
2417  $sql .= ", note";
2418  $sql .= ", visible";
2419  $sql .= ", entity";
2420  $sql .= ")";
2421  $sql .= " VALUES (";
2422  $sql .= " ".$this->db->encrypt($this->const_name."_".strtoupper($key), 1);
2423  $sql .= ", 'chaine'";
2424  $sql .= ", ".$this->db->encrypt($newvalue, 1);
2425  $sql .= ", null";
2426  $sql .= ", '0'";
2427  $sql .= ", ".((int) $entity);
2428  $sql .= ")";
2429 
2430  dol_syslog(get_class($this)."::insert_module_parts for key=".$this->const_name."_".strtoupper($key), LOG_DEBUG);
2431 
2432  $resql = $this->db->query($sql, 1);
2433  if (!$resql) {
2434  if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2435  $error++;
2436  $this->error = $this->db->lasterror();
2437  } else {
2438  dol_syslog(get_class($this)."::insert_module_parts for ".$this->const_name."_".strtoupper($key)." Record already exists.", LOG_WARNING);
2439  }
2440  }
2441  }
2442  }
2443  }
2444  return $error;
2445  }
2446 
2447  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2453  public function delete_module_parts()
2454  {
2455  // phpcs:enable
2456  global $conf;
2457 
2458  $err = 0;
2459 
2460  if (is_array($this->module_parts)) {
2461  dol_syslog(get_class($this)."::delete_module_parts", LOG_DEBUG);
2462 
2463  if (empty($this->module_parts['icon']) && !empty($this->picto) && preg_match('/^fa\-/', $this->picto)) {
2464  $this->module_parts['icon'] = $this->picto;
2465  }
2466 
2467  foreach ($this->module_parts as $key => $value) {
2468  // If entity is defined
2469  if (is_array($value) && isset($value['entity'])) {
2470  $entity = $value['entity'];
2471  } else {
2472  $entity = $conf->entity;
2473  }
2474 
2475  $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
2476  $sql .= " WHERE ".$this->db->decrypt('name')." LIKE '".$this->db->escape($this->const_name)."_".strtoupper($key)."'";
2477  $sql .= " AND entity = ".((int) $entity);
2478 
2479  if (!$this->db->query($sql)) {
2480  $this->error = $this->db->lasterror();
2481  $err++;
2482  }
2483  }
2484  }
2485  return $err;
2486  }
2487 
2497  public function init($options = '')
2498  {
2499  return $this->_init(array(), $options);
2500  }
2501 
2510  public function remove($options = '')
2511  {
2512  return $this->_remove(array(), $options);
2513  }
2514 
2515 
2523  public function getKanbanView($codeenabledisable = '', $codetoconfig = '')
2524  {
2525  global $conf, $langs;
2526 
2527  // Define imginfo
2528  $imginfo = "info";
2529  if ($this->isCoreOrExternalModule() == 'external') {
2530  $imginfo = "info_black";
2531  }
2532 
2533  $const_name = 'MAIN_MODULE_'.strtoupper(preg_replace('/^mod/i', '', get_class($this)));
2534 
2535  $version = $this->getVersion(0);
2536  $versiontrans = '';
2537  if (preg_match('/development/i', $version)) {
2538  $versiontrans .= 'warning';
2539  }
2540  if (preg_match('/experimental/i', $version)) {
2541  $versiontrans .= 'warning';
2542  }
2543  if (preg_match('/deprecated/i', $version)) {
2544  $versiontrans .= 'warning';
2545  }
2546 
2547  $return = '
2548  <div class="box-flex-item info-box-module'
2549  .(getDolGlobalString($const_name) ? '' : ' --disabled')
2550  .($this->isCoreOrExternalModule() == 'external' ? ' --external' : '')
2551  .($this->needUpdate ? ' --need-update' : '')
2552  .'">
2553  <div class="info-box info-box-sm info-box-module">
2554  <div class="info-box-icon'.(!getDolGlobalString($const_name) ? '' : ' info-box-icon-module-enabled'.($versiontrans ? ' info-box-icon-module-warning' : '')).'">';
2555 
2556  $alttext = '';
2557  //if (is_array($objMod->need_dolibarr_version)) $alttext.=($alttext?' - ':'').'Dolibarr >= '.join('.',$objMod->need_dolibarr_version);
2558  //if (is_array($objMod->phpmin)) $alttext.=($alttext?' - ':'').'PHP >= '.join('.',$objMod->phpmin);
2559  if (!empty($this->picto)) {
2560  if (preg_match('/^\//i', $this->picto)) {
2561  $return .= img_picto($alttext, $this->picto, 'class="inline-block valignmiddle"', 1);
2562  } else {
2563  $return .= img_object($alttext, $this->picto, 'class="inline-block valignmiddle"');
2564  }
2565  } else {
2566  $return .= img_object($alttext, 'generic', 'class="inline-block valignmiddle"');
2567  }
2568 
2569  if ($this->isCoreOrExternalModule() == 'external' || preg_match('/development|experimental|deprecated/i', $version)) {
2570  $versionTitle = $langs->trans("Version").' '.$this->getVersion(1);
2571  if ($this->needUpdate) {
2572  $versionTitle .= '<br>'.$langs->trans('ModuleUpdateAvailable').' : '.$this->lastVersion;
2573  }
2574 
2575  $return .= '<span class="info-box-icon-version'.($versiontrans ? ' '.$versiontrans : '').' classfortooltip" title="'.dol_escape_js($versionTitle).'" >';
2576  $return .= $this->getVersion(1);
2577  $return .= '</span>';
2578  }
2579 
2580  $return .= '</div>
2581  <div class="info-box-content info-box-text-module'.(!getDolGlobalString($const_name) ? '' : ' info-box-module-enabled'.($versiontrans ? ' info-box-content-warning' : '')).'">
2582  <span class="info-box-title">'.$this->getName().'</span>
2583  <span class="info-box-desc twolinesmax opacitymedium" title="'.dol_escape_htmltag($this->getDesc()).'">'.nl2br($this->getDesc()).'</span>';
2584 
2585  $return .= '<div class="valignmiddle inline-block info-box-more">';
2586  //if ($versiontrans) print img_warning($langs->trans("Version").' '.$this->getVersion(1)).' ';
2587  $return .= '<a class="valignmiddle inline-block" href="javascript:document_preview(\''.DOL_URL_ROOT.'/admin/modulehelp.php?id='.((int) $this->numero).'\',\'text/html\',\''.dol_escape_js($langs->trans("Module")).'\')">'.img_picto(($this->isCoreOrExternalModule() == 'external' ? $langs->trans("ExternalModule").' - ' : '').$langs->trans("ClickToShowDescription"), $imginfo).'</a>';
2588  $return .= '</div><br>';
2589 
2590  $return .= '<div class="valignmiddle inline-block info-box-actions">';
2591  $return .= '<div class="valignmiddle inline-block info-box-setup">';
2592  $return .= $codetoconfig;
2593  $return .= '</div>';
2594  $return .= '<div class="valignmiddle inline-block marginleftonly marginrightonly">';
2595  $return .= $codeenabledisable;
2596  $return .= '</div>';
2597  $return .= '</div>';
2598 
2599  $return .= '
2600  </div><!-- /.info-box-content -->
2601  </div><!-- /.info-box -->
2602  </div>';
2603 
2604  return $return;
2605  }
2606 
2614  public function checkForUpdate()
2615  {
2616  require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
2617  if (!empty($this->url_last_version)) {
2618  $lastVersion = getURLContent($this->url_last_version, 'GET', '', 1, array(), array('http', 'https'), 0); // Accept http or https links on external remote server only
2619  if (isset($lastVersion['content']) && strlen($lastVersion['content']) < 30) {
2620  // Security warning : be careful with remote data content, the module editor could be hacked (or evil) so limit to a-z A-Z 0-9 _ . -
2621  $this->lastVersion = preg_replace("/[^a-zA-Z0-9_\.\-]+/", "", $lastVersion['content']);
2622  if (version_compare($this->lastVersion, $this->version) > 0) {
2623  $this->needUpdate = true;
2624  return 1;
2625  } else {
2626  $this->needUpdate = false;
2627  return 0;
2628  }
2629  } else {
2630  return -1;
2631  }
2632  }
2633  return 0;
2634  }
2635 }
run_sql($sqlfile, $silent=1, $entity=0, $usesavepoint=1, $handler='', $okerror='default', $linelengthlimit=32768, $nocommentremoval=0, $offsetforchartofaccount=0, $colspan=0, $onlysqltoimportwebsite=0, $database='')
Launch a sql file.
Definition: admin.lib.php:170
Class DolibarrModules.
delete_permissions()
Removes access rights.
getImportDatasetLabel($r)
Gives translated label of an import dataset.
_init($array_sql, $options='')
Enables a module.
getLastActivationDate()
Gives the last date of activation.
delete_cronjobs()
Removes boxes.
getKanbanView($codeenabledisable='', $codetoconfig='')
Return Kanban view of a module.
getModulePosition()
Gives the module position.
insert_const()
Adds constants.
__construct($db)
Constructor.
delete_boxes()
Removes boxes.
isCoreOrExternalModule()
Tells if module is core or external.
getDescLongReadmeFound()
Return path of file if a README file was found.
insert_permissions($reinitadminperms=0, $force_entity=null, $notrigger=0)
Adds access rights.
delete_menus()
Removes menu entries.
delete_const()
Removes constants tagged 'deleteonunactive'.
_active()
Insert constants for module activation.
_remove($array_sql, $options='')
Disable function.
getDescLong()
Gives the long description of a module.
insert_boxes($option='')
Adds boxes.
getPublisher()
Gives the publisher name.
getChangeLog()
Gives the changelog.
insert_cronjobs()
Adds cronjobs.
create_dirs()
Creates directories.
delete_module_parts()
Removes generic parts.
getVersion($translated=1)
Gives module version (translated if param $translated is on) For 'experimental' modules,...
_unactive()
Module deactivation.
getDesc()
Gives the translated module description if translation exists in admin.lang or the default module des...
$needUpdate
true indicate this module need update
delete_tabs()
Removes tabs.
getExportDatasetLabel($r)
Gives translated label of an export dataset.
getName()
Gives the translated module name if translation exists in admin.lang or into language files of module...
_load_tables($reldir, $onlywithsuffix='')
Create tables and keys required by module:
$lastVersion
Module last version.
insert_dirs($name, $dir)
Adds directories definitions.
getLangFilesArray()
Gives module related language files list.
insert_menus()
Adds menu entries.
insert_module_parts()
Save configuration for generic features.
getPublisherUrl()
Gives the publisher url.
getLastActivationInfo()
Gives the last author of activation.
delete_dirs()
Removes directories.
init($options='')
Function called when module is enabled.
static getListOfPagesForBoxes()
Name of positions (See below)
Class to manage menu entries.
Class to manage Dolibarr users.
Definition: user.class.php:50
if(isModEnabled('invoice') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') && $user->hasRight('tax', 'charges', 'lire')) if(isModEnabled('invoice') &&isModEnabled('order') && $user->hasRight("commande", "lire") &&!getDolGlobalString('WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER')) $sql
Social contributions to pay.
Definition: index.php:744
print *****$script_file(".$version.") pid cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
dol_copy($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=0)
Copy a file to another file.
Definition: files.lib.php:755
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
Definition: files.lib.php:1438
dol_is_file($pathoffile)
Return if path is a file.
Definition: files.lib.php:519
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:63
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_now($mode='auto')
Return date for now.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
if(!defined( 'CSRFCHECK_WITH_TOKEN'))
dolMd2Html($content, $parser='parsedown', $replaceimagepath=null)
Function to parse MD content into HTML.
Definition: parsemd.lib.php:32
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition: repair.php:125