dolibarr 24.0.0-beta
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-2026 Frédéric France <frederic.france@free.fr>
11 * Copyright (C) 2024-2026 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
38class 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
120 public $const = array();
121
125 public $cronjobs = array();
126
130 public $rights;
131
135 public $rights_admin_allowed;
136
140 public $rights_class;
141
142 const URL_FOR_BLACKLISTED_MODULES = 'https://www.dolibarr.org/modules-blacklist.php';
143
144 const KEY_ID = 0;
145 const KEY_LABEL = 1;
146 const KEY_TYPE = 2; // deprecated
147 const KEY_DEFAULT = 3;
148 const KEY_FIRST_LEVEL = 4;
149 const KEY_SECOND_LEVEL = 5;
150 const KEY_MODULE = 6;
151 const KEY_ENABLED = 7;
152
156 public $menu = array();
157
185 public $module_parts = array();
186
190 public $error;
191
195 public $errors;
196
207 public $version;
208
212 public $version_if_core = 0;
213
218 public $lastVersion = '';
219
224 public $needUpdate = false;
225
231 public $description;
232
239 public $descriptionlong;
240
244 public $dictionaries = array();
245
249 public $tabs;
250
251 // For exports
252
256 public $export_code;
257
261 public $export_label;
262
266 public $export_icon;
267
271 public $export_enabled;
275 public $export_permission;
279 public $export_fields_array;
283 public $export_TypeFields_array; // Array of key=>type where type can be 'Numeric', 'Date', 'Text', 'Boolean', 'Status', 'List:xxx:fieldlabel:rowid'
287 public $export_entities_array;
291 public $export_aggregate_array;
295 public $export_examplevalues_array;
299 public $export_help_array;
306 public $export_special_array; // special or computed field
313 public $export_dependencies_array;
317 public $export_sql_start;
321 public $export_sql_end;
325 public $export_sql_order;
326
327
328 // For import
329
333 public $import_code;
334
338 public $import_label;
339
343 public $import_icon;
347 public $import_entities_array;
351 public $import_tables_array;
355 public $import_tables_creator_array;
359 public $import_fields_array;
363 public $import_fieldshidden_array;
367 public $import_convertvalue_array;
371 public $import_regex_array;
375 public $import_examplevalues_array;
379 public $import_updatekeys_array;
383 public $import_run_sql_after_array;
387 public $import_TypeFields_array;
391 public $import_help_array;
392
396 public $const_name;
397
401 public $always_enabled;
402
406 public $disabled;
407
411 public $automatic_activation = array();
412
416 public $core_enabled;
417
424 public $picto;
425
432 public $config_page_url;
433
434
443 public $depends;
444
449 public $requiredby;
450
455 public $conflictwith;
456
460 public $langfiles;
461
467 public $warnings_activation;
468
474 public $warnings_activation_ext;
475
481 public $warnings_unactivation;
482
487 public $phpmin;
488
492 public $phpmax;
493
498 public $need_dolibarr_version;
499
504 public $max_dolibarr_version;
505
509 public $need_javascript_ajax;
510
514 public $enabled_bydefault;
515
519 public $hidden = false;
520
524 public $url_last_version;
525
526
532 public function __construct($db)
533 {
534 $this->db = $db;
535 }
536 // We should but can't set this as abstract because this will make dolibarr hang
537 // after migration due to old module not implementing. We must wait PHP is able to make
538 // a try catch on Fatal error to manage this correctly.
539 // We need constructor into function unActivateModule into admin.lib.php
540
541
542 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
553 protected function _init($array_sql, $options = '')
554 {
555 // phpcs:enable
556 global $conf;
557 $err = 0;
558
559 $this->db->begin();
560
561 // Insert activation module constant
562 if (!$err) {
563 $err += $this->_active();
564 }
565
566 // Insert new pages for tabs (into llx_const)
567 if (!$err) {
568 $err += $this->insert_tabs();
569 }
570
571 // Insert activation of module's parts. Copy website templates into doctemplates.
572 if (!$err) {
573 $err += $this->insert_module_parts();
574 }
575
576 // Insert constant defined by modules (into llx_const) if no existing yet
577 if (!$err && !preg_match('/newboxdefonly/', $options)) {
578 $err += $this->insert_const(); // Test on newboxdefonly to avoid to erase value during upgrade
579 }
580
581 // Insert boxes def (into llx_boxes_def) and boxes setup (into llx_boxes)
582 if (!$err && !preg_match('/noboxes/', $options)) {
583 $err += $this->insert_boxes($options);
584 }
585
586 // Insert cron job entries (entry in llx_cronjobs)
587 if (!$err) {
588 $err += $this->insert_cronjobs();
589 }
590
591 // Insert permission definitions of module into llx_rights_def. If user is admin, grant this permission to user.
592 if (!$err) {
593 $err += $this->insert_permissions(1, null, 1);
594 }
595
596 // Insert specific menus entries into database
597 if (!$err) {
598 $err += $this->insert_menus();
599 }
600
601 // Create module's directories
602 if (!$err) {
603 $err += $this->create_dirs();
604 }
605
606 // Execute addons requests
607 $num = count($array_sql);
608 for ($i = 0; $i < $num; $i++) {
609 if (!$err) {
610 $val = $array_sql[$i];
611 $sql = $val;
612 $ignoreerror = 0;
613 if (is_array($val)) {
614 $sql = $val['sql'];
615 $ignoreerror = $val['ignoreerror'] ?? 0;
616 }
617 // Add current entity id
618 $sql = str_replace('__ENTITY__', (string) $conf->entity, $sql);
619
620 dol_syslog(get_class($this)."::_init ignoreerror=".$ignoreerror, LOG_DEBUG);
621 $result = $this->db->query($sql, $ignoreerror);
622 if (!$result) {
623 if (!$ignoreerror) {
624 $this->error = $this->db->lasterror();
625 $err++;
626 } else {
627 dol_syslog(get_class($this)."::_init Warning ".$this->db->lasterror(), LOG_WARNING);
628 }
629 }
630 }
631 }
632
633 // Return code
634 if (!$err) {
635 $this->db->commit();
636
637 $moduleNameInConf = strtolower(preg_replace('/^MAIN_MODULE_/', '', $this->const_name));
638 // two exceptions to handle
639 if ($moduleNameInConf === 'propale') {
640 $moduleNameInConf = 'propal';
641 } elseif ($moduleNameInConf === 'supplierproposal') {
642 $moduleNameInConf = 'supplier_proposal';
643 }
644
645 $conf->modules[$moduleNameInConf] = $moduleNameInConf; // Add this module in list of enabled modules so isModEnabled() will work (conf->module->enabled must no more be used)
646
647 return 1;
648 } else {
649 $this->db->rollback();
650 return 0;
651 }
652 }
653
654 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
663 protected function _remove($array_sql, $options = '')
664 {
665 global $conf;
666 // phpcs:enable
667 $err = 0;
668
669 $this->db->begin();
670
671 // Remove activation module line (constant MAIN_MODULE_MYMODULE in llx_const)
672 if (!$err) {
673 $err += $this->_unactive();
674 }
675
676 // Remove activation of module's new tabs (MAIN_MODULE_MYMODULE_TABS_XXX in llx_const)
677 if (!$err) {
678 $err += $this->delete_tabs();
679 }
680
681 // Remove activation of module's parts (MAIN_MODULE_MYMODULE_XXX in llx_const)
682 if (!$err) {
683 $err += $this->delete_module_parts();
684 }
685
686 // Remove constants defined by modules
687 if (!$err) {
688 $err += $this->delete_const();
689 }
690
691 // Remove list of module's available boxes (entry in llx_boxes)
692 if (!$err && !preg_match('/(newboxdefonly|noboxes)/', $options)) {
693 $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
694 }
695
696 // Remove list of module's cron job entries (entry in llx_cronjobs)
697 if (!$err) {
698 $err += $this->delete_cronjobs();
699 }
700
701 // Remove module's permissions from list of available permissions (entries in llx_rights_def)
702 if (!$err) {
703 $err += $this->delete_permissions();
704 }
705
706 // Remove module's menus (entries in llx_menu)
707 if (!$err) {
708 $err += $this->delete_menus();
709 }
710
711 // Remove module's directories
712 if (!$err) {
713 $err += $this->delete_dirs();
714 }
715
716 // Run complementary sql requests
717 $num = count((array) $array_sql);
718 for ($i = 0; $i < $num; $i++) {
719 if (!$err) {
720 dol_syslog(get_class($this)."::_remove", LOG_DEBUG);
721 $result = $this->db->query($array_sql[$i]);
722 if (!$result) {
723 $this->error = $this->db->error();
724 $err++;
725 }
726 }
727 }
728
729 // Return code
730 if (!$err) {
731 $this->db->commit();
732
733 // Disable modules
734 $moduleNameInConf = strtolower(preg_replace('/^MAIN_MODULE_/', '', $this->const_name));
735 // two exceptions to handle
736 if ($moduleNameInConf === 'propale') {
737 $moduleNameInConf = 'propal';
738 } elseif ($moduleNameInConf === 'supplierproposal') {
739 $moduleNameInConf = 'supplier_proposal';
740 }
741
742 unset($conf->modules[$moduleNameInConf]); // Add this module in list of enabled modules so isModEnabled() will work (conf->module->enabled must no more be used)
743
744 return 1;
745 } else {
746 $this->db->rollback();
747 return 0;
748 }
749 }
750
751
758 public function getName()
759 {
760 global $langs;
761 $langs->load("admin");
762
763 if ($langs->transnoentitiesnoconv("Module".$this->numero."Name") != "Module".$this->numero."Name") {
764 // If module name translation exists
765 return $langs->transnoentitiesnoconv("Module".$this->numero."Name");
766 } else {
767 // If module name translation using it's unique id does not exist, we try to use its name to find translation
768 if (is_array($this->langfiles)) {
769 foreach ($this->langfiles as $val) {
770 if ($val) {
771 $langs->load($val);
772 }
773 }
774 }
775
776 if ($langs->trans("Module".$this->name."Name") != "Module".$this->name."Name") {
777 // If module name translation exists
778 return $langs->transnoentitiesnoconv("Module".$this->name."Name");
779 }
780
781 // Last chance with simple label
782 return $langs->transnoentitiesnoconv($this->name);
783 }
784 }
785
786
793 public function getDesc($foruseinpopupdesc = 0)
794 {
795 global $langs;
796 $langs->load("admin");
797
798 if ($langs->transnoentitiesnoconv("Module".$this->numero."Desc") != "Module".$this->numero."Desc") {
799 // If module description translation exists
800 return $langs->transnoentitiesnoconv("Module".$this->numero."Desc");
801 } else {
802 // If module description translation does not exist using its unique id, we can use its name to find translation
803 if (is_array($this->langfiles)) {
804 foreach ($this->langfiles as $val) {
805 if ($val) {
806 $langs->load($val);
807 }
808 }
809 }
810
811 if ($langs->transnoentitiesnoconv("Module".$this->name."Desc") != "Module".$this->name."Desc") {
812 // If module name translation exists
813 return $langs->trans("Module".$this->name."Desc");
814 }
815
816 // Last chance with simple label
817 return $langs->trans($this->description);
818 }
819 }
820
827 public function getDescLong()
828 {
829 global $langs;
830 $langs->load("admin");
831
832 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
833 include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
834
835 $content = '';
836 $pathoffile = $this->getDescLongReadmeFound();
837
838 if ($pathoffile) { // Mostly for external modules
839 $content = file_get_contents($pathoffile, false, null, 0, 1024 * 1024); // Max size loaded 1Mb
840
841 if ((float) DOL_VERSION >= 6.0) { // @phpstan-ignore-line
842 @include_once DOL_DOCUMENT_ROOT.'/core/lib/parsemd.lib.php';
843
844 $content = dolMd2Html(
845 $content,
846 'parsedown',
847 array(
848 'doc/' => dol_buildpath(strtolower($this->name).'/doc/', 1),
849 'img/' => dol_buildpath(strtolower($this->name).'/img/', 1),
850 'images/' => dol_buildpath(strtolower($this->name).'/images/', 1),
851 )
852 );
853
854 $content = preg_replace('/<a href="/', '<a target="_blank" rel="noopener noreferrer" href="', $content);
855 } else {
856 $content = nl2br($content);
857 }
858 } else {
859 // Mostly for internal modules
860 if (!empty($this->descriptionlong)) {
861 if (is_array($this->langfiles)) {
862 foreach ($this->langfiles as $val) {
863 if ($val) {
864 $langs->load($val);
865 }
866 }
867 }
868
869 $content = $langs->transnoentitiesnoconv($this->descriptionlong);
870 }
871 }
872
873 return '<div class="moduledesclong">'.$content.'</div>';
874 }
875
881 public function getDescLongReadmeFound()
882 {
883 global $langs;
884
885 $filefound = false;
886
887 // Define path to file README.md.
888 // First check README-la_LA.md then README-la.md then README.md
889 $pathoffile = dol_buildpath(strtolower($this->name).'/README-'.$langs->defaultlang.'.md', 0);
890 if (dol_is_file($pathoffile)) {
891 $filefound = true;
892 }
893 if (!$filefound) {
894 $tmp = explode('_', $langs->defaultlang);
895 $pathoffile = dol_buildpath(strtolower($this->name).'/README-'.$tmp[0].'.md', 0);
896 if (dol_is_file($pathoffile)) {
897 $filefound = true;
898 }
899 }
900 if (!$filefound) {
901 $pathoffile = dol_buildpath(strtolower($this->name).'/README.md', 0);
902 if (dol_is_file($pathoffile)) {
903 $filefound = true;
904 }
905 }
906
907 return ($filefound ? $pathoffile : '');
908 }
909
910
916 public function getChangeLog()
917 {
918 global $langs;
919 $langs->load("admin");
920
921 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
922 include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
923
924 $filefound = false;
925
926 // Define path to file README.md.
927 // First check ChangeLog-la_LA.md then ChangeLog.md
928 $pathoffile = dol_buildpath(strtolower($this->name).'/ChangeLog-'.$langs->defaultlang.'.md', 0);
929 if (dol_is_file($pathoffile)) {
930 $filefound = true;
931 }
932 if (!$filefound) {
933 $pathoffile = dol_buildpath(strtolower($this->name).'/ChangeLog.md', 0);
934 if (dol_is_file($pathoffile)) {
935 $filefound = true;
936 }
937 }
938
939 if ($filefound) { // Mostly for external modules
940 $content = file_get_contents($pathoffile);
941
942 if ((float) DOL_VERSION >= 6.0) { // @phpstan-ignore-line
943 @include_once DOL_DOCUMENT_ROOT.'/core/lib/parsemd.lib.php';
944
945 $content = dolMd2Html($content, 'parsedown', array('doc/' => dol_buildpath(strtolower($this->name).'/doc/', 1)));
946 } else {
947 $content = nl2br($content);
948 }
949 } else {
950 $content = '';
951 }
952
953 return $content;
954 }
955
961 public function getPublisher()
962 {
963 return $this->editor_name;
964 }
965
971 public function getPublisherUrl()
972 {
973 return $this->editor_url;
974 }
975
984 public function getVersion($translated = 1)
985 {
986 global $langs;
987 $langs->load("admin");
988
989 $ret = '';
990
991 $newversion = preg_replace('/_deprecated/', '', $this->version);
992 if ($newversion == 'experimental') {
993 $ret = ($translated ? $langs->transnoentitiesnoconv("VersionExperimental") : $newversion);
994 } elseif ($newversion == 'development') {
995 $ret = ($translated ? $langs->transnoentitiesnoconv("VersionDevelopment") : $newversion);
996 } elseif ($newversion == 'dolibarr') {
997 $ret = DOL_VERSION;
998 } elseif ($newversion) {
999 $ret = $newversion;
1000 } else {
1001 $ret = ($translated ? $langs->transnoentitiesnoconv("VersionUnknown") : 'unknown');
1002 }
1003
1004 if (preg_match('/_deprecated/', $this->version)) {
1005 $ret .= ($translated ? ' ('.$langs->transnoentitiesnoconv("Deprecated").')' : $this->version);
1006 }
1007 return $ret;
1008 }
1009
1015 public function getModulePosition()
1016 {
1017 if (in_array($this->version, array('dolibarr', 'dolibarr_deprecated', 'experimental', 'development'))) { // core module
1018 return $this->module_position;
1019 } else { // external module
1020 if ($this->module_position >= 100000) {
1021 return $this->module_position;
1022 } else {
1023 $position = intval($this->module_position) + 100000;
1024 return strval($position);
1025 }
1026 }
1027 }
1028
1035 public function isCoreOrExternalModule()
1036 {
1037 if ($this->version == 'dolibarr' || $this->version == 'dolibarr_deprecated') {
1038 return 'core';
1039 }
1040 if (!empty($this->version) && !in_array($this->version, array('experimental', 'development')) && empty($this->version_if_core)) {
1041 return 'external';
1042 }
1043 if (!empty($this->editor_name) || !empty($this->editor_url) && empty($this->version_if_core)) {
1044 return 'external';
1045 }
1046 if ($this->numero >= 100000) {
1047 return 'external';
1048 }
1049 return 'unknown';
1050 }
1051
1052
1058 public function getLangFilesArray()
1059 {
1060 return $this->langfiles;
1061 }
1062
1070 public function getExportDatasetLabel($r)
1071 {
1072 global $langs;
1073
1074 $langstring = "ExportDataset_".$this->export_code[$r];
1075 if ($langs->trans($langstring) == $langstring) {
1076 // Translation not found
1077 return $langs->trans($this->export_label[$r]);
1078 } else {
1079 // Translation found
1080 return $langs->trans($langstring);
1081 }
1082 }
1083
1084
1092 public function getImportDatasetLabel($r)
1093 {
1094 global $langs;
1095
1096 $langstring = "ImportDataset_".$this->import_code[$r];
1097 //print "x".$langstring;
1098 if ($langs->trans($langstring) == $langstring) {
1099 // Translation not found
1100 return $langs->transnoentitiesnoconv($this->import_label[$r]);
1101 } else {
1102 // Translation found
1103 return $langs->transnoentitiesnoconv($langstring);
1104 }
1105 }
1106
1107
1113 public function getLastActivationDate()
1114 {
1115 global $conf;
1116
1117 $err = 0;
1118
1119 $sql = "SELECT tms FROM ".MAIN_DB_PREFIX."const";
1120 $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
1121 $sql .= " AND entity IN (0, ".((int) $conf->entity).")";
1122
1123 dol_syslog(get_class($this)."::getLastActiveDate", LOG_DEBUG);
1124 $resql = $this->db->query($sql);
1125 if (!$resql) {
1126 $err++;
1127 } else {
1128 $obj = $this->db->fetch_object($resql);
1129 if ($obj) {
1130 return $this->db->jdate($obj->tms);
1131 }
1132 }
1133
1134 return '';
1135 }
1136
1137
1143 public function getLastActivationInfo()
1144 {
1145 global $conf;
1146
1147 $err = 0;
1148
1149 $sql = "SELECT tms, note FROM ".MAIN_DB_PREFIX."const";
1150 $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
1151 $sql .= " AND entity IN (0, ".$conf->entity.")";
1152
1153 dol_syslog(get_class($this)."::getLastActiveDate", LOG_DEBUG);
1154 $resql = $this->db->query($sql);
1155 if (!$resql) {
1156 $err++;
1157 } else {
1158 $obj = $this->db->fetch_object($resql);
1159 if ($obj) {
1160 $tmp = array();
1161 if ($obj->note) {
1162 $tmp = json_decode($obj->note, true);
1163 }
1164 return array(
1165 'authorid' => empty($tmp['authorid']) ? '' : (int) $tmp['authorid'],
1166 'ip' => empty($tmp['ip']) ? '' : (string) $tmp['ip'],
1167 'lastactivationdate' => $this->db->jdate($obj->tms),
1168 'lastactivationversion' => (!empty($tmp['lastactivationversion']) ? (string) $tmp['lastactivationversion'] : 'unknown'),
1169 );
1170 }
1171 }
1172
1173 return array();
1174 }
1175
1176
1177 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1183 protected function _active()
1184 {
1185 // phpcs:enable
1186 global $conf, $user;
1187
1188 $err = 0;
1189
1190 // Common module
1191 $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
1192
1193 $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
1194 $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
1195 $sql .= " AND entity IN (0, ".$entity.")";
1196
1197 dol_syslog(get_class($this)."::_active delete activation constant", LOG_DEBUG);
1198 $resql = $this->db->query($sql);
1199 if (!$resql) {
1200 $err++;
1201 }
1202
1203 $note = json_encode(
1204 array(
1205 'authorid' => (is_object($user) ? $user->id : 0),
1206 'ip' => (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']),
1207 'lastactivationversion' => $this->version,
1208 )
1209 );
1210
1211 $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (name, value, visible, entity, note) VALUES";
1212 $sql .= " (".$this->db->encrypt($this->const_name);
1213 $sql .= ", ".$this->db->encrypt('1');
1214 $sql .= ", 0, ".((int) $entity);
1215 $sql .= ", '".$this->db->escape($note)."')";
1216
1217 dol_syslog(get_class($this)."::_active insert activation constant", LOG_DEBUG);
1218 $resql = $this->db->query($sql);
1219 if (!$resql) {
1220 $err++;
1221 }
1222
1223 return $err;
1224 }
1225
1226
1227 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1233 protected function _unactive()
1234 {
1235 // phpcs:enable
1236 global $conf;
1237
1238 $err = 0;
1239
1240 // Common module
1241 $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
1242
1243 $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
1244 $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
1245 $sql .= " AND entity IN (0, ".$entity.")";
1246
1247 dol_syslog(get_class($this)."::_unactive", LOG_DEBUG);
1248 $this->db->query($sql);
1249
1250 return $err;
1251 }
1252
1253
1254 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps,PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1273 protected function _load_tables($reldir, $onlywithsuffix = '')
1274 {
1275 // phpcs:enable
1276 global $conf;
1277
1278 $error = 0;
1279 $dirfound = 0;
1280 $ok = 1;
1281
1282 if (empty($reldir)) {
1283 return 1;
1284 }
1285
1286 include_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
1287
1288 foreach ($conf->file->dol_document_root as $dirroot) {
1289 if ($ok == 1) {
1290 $dirsql = $dirroot.$reldir;
1291 $ok = 0;
1292
1293 // We will loop on xxx/, xxx/tables/, xxx/data/
1294 $listofsubdir = array('', 'tables/', 'data/');
1295 if ($this->db->type == 'pgsql') {
1296 $listofsubdir[] = '../pgsql/functions/';
1297 }
1298
1299 foreach ($listofsubdir as $subdir) {
1300 $dir = $dirsql.$subdir;
1301
1302 $handle = @opendir($dir); // Dir may not exists
1303 if (is_resource($handle)) {
1304 $dirfound++;
1305
1306 // Run llx_mytable.sql files, then llx_mytable_*.sql
1307 $files = array();
1308 while (($file = readdir($handle)) !== false) {
1309 $files[] = $file;
1310 }
1311 sort($files);
1312 foreach ($files as $file) {
1313 if ($onlywithsuffix) {
1314 if (!preg_match('/\-'.preg_quote($onlywithsuffix, '/').'\./i', $file)) {
1315 //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
1316 continue;
1317 } else {
1318 //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
1319 }
1320 }
1321 if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'llx_') {
1322 $result = run_sql($dir.$file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, 0, 1);
1323 if ($result <= 0) {
1324 $error++;
1325 }
1326 }
1327 }
1328
1329 rewinddir($handle);
1330
1331 // Run llx_mytable.key.sql files (Must be done after llx_mytable.sql) then then llx_mytable_*.key.sql
1332 $files = array();
1333 while (($file = readdir($handle)) !== false) {
1334 $files[] = $file;
1335 }
1336 sort($files);
1337 foreach ($files as $file) {
1338 if ($onlywithsuffix) {
1339 if (!preg_match('/\-'.preg_quote($onlywithsuffix, '/').'\./i', $file)) {
1340 //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
1341 continue;
1342 } else {
1343 //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
1344 }
1345 }
1346 if (preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'llx_') {
1347 /* $result = run_sql($dir.$file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, 0, 1);
1348 if ($result <= 0) {
1349 $error++;
1350 }*/
1351 }
1352 }
1353
1354 rewinddir($handle);
1355
1356 // Run functions-xxx.sql files (Must be done after llx_mytable.key.sql)
1357 $files = array();
1358 while (($file = readdir($handle)) !== false) {
1359 $files[] = $file;
1360 }
1361 sort($files);
1362 foreach ($files as $file) {
1363 if ($onlywithsuffix) {
1364 if (!preg_match('/\-'.preg_quote($onlywithsuffix, '/').'\./i', $file)) {
1365 //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
1366 continue;
1367 } else {
1368 //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
1369 }
1370 }
1371 if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 9) == 'functions') {
1372 /* $result = run_sql($dir.$file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, 0, 1);
1373 if ($result <= 0) {
1374 $error++;
1375 }*/
1376 }
1377 }
1378
1379 rewinddir($handle);
1380
1381 // Run data_xxx.sql files (Must be done after llx_mytable.key.sql)
1382 $files = array();
1383 while (($file = readdir($handle)) !== false) {
1384 $files[] = $file;
1385 }
1386 sort($files);
1387 foreach ($files as $file) {
1388 if ($onlywithsuffix) {
1389 if (!preg_match('/\-'.preg_quote($onlywithsuffix, '/').'\./i', $file)) {
1390 //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
1391 continue;
1392 } else {
1393 //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
1394 }
1395 }
1396 if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'data') {
1397 /* $result = run_sql($dir.$file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, 0, 1);
1398 if ($result <= 0) {
1399 $error++;
1400 }*/
1401 }
1402 }
1403
1404 rewinddir($handle);
1405
1406 // Run update_xxx.sql files
1407 $files = array();
1408 while (($file = readdir($handle)) !== false) {
1409 $files[] = $file;
1410 }
1411 sort($files);
1412 foreach ($files as $file) {
1413 if ($onlywithsuffix) {
1414 if (!preg_match('/\-'.preg_quote($onlywithsuffix, '/').'\./i', $file)) {
1415 //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
1416 continue;
1417 } else {
1418 //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
1419 }
1420 }
1421 if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 6) == 'update') {
1422 /* $result = run_sql($dir.$file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, 0, 1);
1423 if ($result <= 0) {
1424 $error++;
1425 }*/
1426 }
1427 }
1428
1429 closedir($handle);
1430 }
1431 }
1432
1433 if ($error == 0) {
1434 $ok = 1;
1435 }
1436 }
1437 }
1438
1439 if (!$dirfound) {
1440 dol_syslog("A module wants to load sql files from ".$reldir." but this directory was not found.", LOG_WARNING);
1441 }
1442 return $ok;
1443 }
1444
1445
1446 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1454 public function insert_boxes($option = '')
1455 {
1456 // phpcs:enable
1457 include_once DOL_DOCUMENT_ROOT.'/core/class/infobox.class.php';
1458
1459 global $conf;
1460
1461 $err = 0;
1462
1463 if (is_array($this->boxes)) {
1464 dol_syslog(get_class($this)."::insert_boxes", LOG_DEBUG);
1465
1466 $pos_name = InfoBox::getListOfPagesForBoxes();
1467
1468 foreach ($this->boxes as $key => $value) {
1469 $file = isset($this->boxes[$key]['file']) ? $this->boxes[$key]['file'] : '';
1470 $note = isset($this->boxes[$key]['note']) ? $this->boxes[$key]['note'] : '';
1471 $enabledbydefaulton = isset($this->boxes[$key]['enabledbydefaulton']) ? $this->boxes[$key]['enabledbydefaulton'] : 'Home';
1472
1473 if (empty($file)) {
1474 $file = isset($this->boxes[$key][1]) ? $this->boxes[$key][1] : ''; // For backward compatibility
1475 }
1476 if (empty($note)) {
1477 $note = isset($this->boxes[$key][2]) ? $this->boxes[$key][2] : ''; // For backward compatibility
1478 }
1479
1480 // Search if boxes def already present
1481 $sql = "SELECT count(*) as nb FROM ".MAIN_DB_PREFIX."boxes_def";
1482 $sql .= " WHERE file = '".$this->db->escape($file)."'";
1483 $sql .= " AND entity = ".$conf->entity;
1484 if ($note) {
1485 $sql .= " AND note ='".$this->db->escape($note)."'";
1486 }
1487
1488 $result = $this->db->query($sql);
1489 if ($result) {
1490 $obj = $this->db->fetch_object($result);
1491 if ($obj->nb == 0) {
1492 $this->db->begin();
1493
1494 if (!$err) {
1495 $sql = "INSERT INTO ".MAIN_DB_PREFIX."boxes_def (file, entity, note)";
1496 $sql .= " VALUES ('".$this->db->escape($file)."', ";
1497 $sql .= $conf->entity.", ";
1498 $sql .= $note ? "'".$this->db->escape($note)."'" : "null";
1499 $sql .= ")";
1500
1501 dol_syslog(get_class($this)."::insert_boxes", LOG_DEBUG);
1502 $resql = $this->db->query($sql);
1503 if (!$resql) {
1504 $err++;
1505 }
1506 }
1507 if (!$err && !preg_match('/newboxdefonly/', $option)) {
1508 $lastid = $this->db->last_insert_id(MAIN_DB_PREFIX."boxes_def", "rowid");
1509
1510 foreach ($pos_name as $key2 => $val2) {
1511 //print 'key2='.$key2.'-val2='.$val2."<br>\n";
1512 if ($enabledbydefaulton && $val2 != $enabledbydefaulton) {
1513 continue; // Not enabled by default onto this page.
1514 }
1515
1516 $sql = "INSERT INTO ".MAIN_DB_PREFIX."boxes (box_id, position, box_order, fk_user, entity)";
1517 $sql .= " VALUES (".((int) $lastid).", ".((int) $key2).", '0', 0, ".((int) $conf->entity).")";
1518
1519 dol_syslog(get_class($this)."::insert_boxes onto page ".$key2."=".$val2, LOG_DEBUG);
1520 $resql = $this->db->query($sql);
1521 if (!$resql) {
1522 $err++;
1523 }
1524 }
1525 }
1526
1527 if (!$err) {
1528 $this->db->commit();
1529 } else {
1530 $this->error = $this->db->lasterror();
1531 $this->db->rollback();
1532 }
1533 }
1534 // else box already registered into database
1535 } else {
1536 $this->error = $this->db->lasterror();
1537 $err++;
1538 }
1539 }
1540 }
1541
1542 return $err;
1543 }
1544
1545
1546 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1552 public function delete_boxes()
1553 {
1554 // phpcs:enable
1555 global $conf;
1556
1557 $err = 0;
1558
1559 if (is_array($this->boxes)) {
1560 foreach ($this->boxes as $key => $value) {
1561 //$titre = $this->boxes[$key][0];
1562 if (empty($this->boxes[$key]['file'])) {
1563 $file = isset($this->boxes[$key][1]) ? $this->boxes[$key][1] : ''; // For backward compatibility
1564 } else {
1565 $file = $this->boxes[$key]['file'];
1566 }
1567
1568 //$note = $this->boxes[$key][2];
1569
1570 // TODO If the box is also included by another module and the other module is still on, we should not remove it.
1571 // For the moment, we manage this with hard coded exception
1572 //print "Remove box ".$file.'<br>';
1573 if ($file == 'box_graph_product_distribution.php') {
1574 if (isModEnabled("product") || isModEnabled("service")) {
1575 dol_syslog("We discard deleting module ".$file." because another module still active requires it.");
1576 continue;
1577 }
1578 }
1579
1580 if ($this->db->type == 'sqlite3') {
1581 // sqlite doesn't support "USING" syntax.
1582 // TODO: remove this dependency.
1583 $sql = "DELETE FROM ".MAIN_DB_PREFIX."boxes ";
1584 $sql .= "WHERE ".MAIN_DB_PREFIX."boxes.box_id IN (";
1585 $sql .= "SELECT ".MAIN_DB_PREFIX."boxes_def.rowid ";
1586 $sql .= "FROM ".MAIN_DB_PREFIX."boxes_def ";
1587 $sql .= "WHERE ".MAIN_DB_PREFIX."boxes_def.file = '".$this->db->escape($file)."') ";
1588 $sql .= "AND ".MAIN_DB_PREFIX."boxes.entity = ".$conf->entity;
1589 } else {
1590 $sql = "DELETE FROM ".MAIN_DB_PREFIX."boxes";
1591 $sql .= " USING ".MAIN_DB_PREFIX."boxes, ".MAIN_DB_PREFIX."boxes_def";
1592 $sql .= " WHERE ".MAIN_DB_PREFIX."boxes.box_id = ".MAIN_DB_PREFIX."boxes_def.rowid";
1593 $sql .= " AND ".MAIN_DB_PREFIX."boxes_def.file = '".$this->db->escape($file)."'";
1594 $sql .= " AND ".MAIN_DB_PREFIX."boxes.entity = ".$conf->entity;
1595 }
1596
1597 dol_syslog(get_class($this)."::delete_boxes", LOG_DEBUG);
1598 $resql = $this->db->query($sql);
1599 if (!$resql) {
1600 $this->error = $this->db->lasterror();
1601 $err++;
1602 }
1603
1604 $sql = "DELETE FROM ".MAIN_DB_PREFIX."boxes_def";
1605 $sql .= " WHERE file = '".$this->db->escape($file)."'";
1606 $sql .= " AND entity = ".$conf->entity; // Do not use getEntity here, we want to delete only in current company
1607
1608 dol_syslog(get_class($this)."::delete_boxes", LOG_DEBUG);
1609 $resql = $this->db->query($sql);
1610 if (!$resql) {
1611 $this->error = $this->db->lasterror();
1612 $err++;
1613 }
1614 }
1615 }
1616
1617 return $err;
1618 }
1619
1620 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1626 public function insert_cronjobs()
1627 {
1628 // phpcs:enable
1629 include_once DOL_DOCUMENT_ROOT . '/core/class/infobox.class.php';
1630 include_once DOL_DOCUMENT_ROOT . '/cron/class/cronjob.class.php';
1631 include_once DOL_DOCUMENT_ROOT . '/user/class/user.class.php';
1632
1633 global $conf, $user;
1634
1635 if (empty($user)) {
1636 $user = new User($this->db);
1637 }
1638
1639 $err = 0;
1640
1641 if (is_array($this->cronjobs)) {
1642 dol_syslog(get_class($this) . "::insert_cronjobs", LOG_DEBUG);
1643
1644 foreach ($this->cronjobs as $key => $value) {
1645 $now = dol_now();
1646
1647 $entity = isset($value['entity']) ? $value['entity'] : $conf->entity;
1648 $label = isset($value['label']) ? $value['label'] : '';
1649 $jobtype = isset($value['jobtype']) ? $value['jobtype'] : '';
1650 $classesname = isset($value['class']) ? $value['class'] : '';
1651 $objectname = isset($value['objectname']) ? $value['objectname'] : '';
1652 $methodename = isset($value['method']) ? $value['method'] : '';
1653 $command = isset($value['command']) ? $value['command'] : '';
1654 $params = isset($value['parameters']) ? $value['parameters'] : '';
1655 $md5params = isset($value['md5params']) ? $value['md5params'] : '';
1656 $comment = isset($value['comment']) ? $value['comment'] : '';
1657 $frequency = isset($value['frequency']) ? $value['frequency'] : '';
1658 $unitfrequency = isset($value['unitfrequency']) ? $value['unitfrequency'] : '';
1659 $priority = isset($value['priority']) ? $value['priority'] : '';
1660 $datestart = isset($value['datestart']) ? $value['datestart'] : '';
1661 $dateend = isset($value['dateend']) ? $value['dateend'] : '';
1662 $datenextrun = isset($value['datenextrun']) ? $value['datenextrun'] : $now;
1663 $status = isset($value['status']) ? $value['status'] : '';
1664 $maxrun = isset($value['maxrun']) ? $value['maxrun'] : 0;
1665 $libname = isset($value['libname']) ? $value['libname'] : '';
1666 $test = isset($value['test']) ? $value['test'] : ''; // Line must be enabled or not (so visible or not)
1667
1668 // Search if cron entry already present
1669 $sql = "SELECT count(*) as nb FROM " . MAIN_DB_PREFIX . "cronjob";
1670 //$sql .= " WHERE module_name = '" . $this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class) . "'";
1671 $sql .= " WHERE label = '".$this->db->escape($label)."'";
1672 /* unique key is on label,entity so no need for this test
1673 if ($classesname) {
1674 $sql .= " AND classesname = '" . $this->db->escape($classesname) . "'";
1675 }
1676 if ($objectname) {
1677 $sql .= " AND objectname = '" . $this->db->escape($objectname) . "'";
1678 }
1679 if ($methodename) {
1680 $sql .= " AND methodename = '" . $this->db->escape($methodename) . "'";
1681 }
1682 if ($command) {
1683 $sql .= " AND command = '" . $this->db->escape($command) . "'";
1684 }
1685 if ($params) {
1686 $sql .= " AND params = '" . $this->db->escape($params) . "'";
1687 }
1688 */
1689 $sql .= " AND entity = " . ((int) $entity); // Must be exact entity
1690
1691 $result = $this->db->query($sql);
1692 if (!$result) {
1693 $this->error = $this->db->lasterror();
1694 $err++;
1695 break;
1696 // else box already registered into database
1697 }
1698
1699 $obj = $this->db->fetch_object($result);
1700 if ($obj->nb > 0) {
1701 continue;
1702 }
1703
1704 $cronjob = new Cronjob($this->db);
1705
1706 $cronjob->entity = $entity;
1707 $cronjob->label = $label;
1708 $cronjob->jobtype = $jobtype;
1709 $cronjob->classesname = $classesname;
1710 $cronjob->objectname = $objectname;
1711 $cronjob->methodename = $methodename;
1712 $cronjob->command = $command;
1713 $cronjob->params = $params;
1714 $cronjob->md5params = $md5params;
1715 $cronjob->note_private = $comment;
1716 $cronjob->frequency = $frequency;
1717 $cronjob->unitfrequency = $unitfrequency;
1718 $cronjob->priority = $priority;
1719 $cronjob->datestart = $datestart;
1720 $cronjob->dateend = $dateend;
1721 $cronjob->datenextrun = $datenextrun;
1722 $cronjob->maxrun = $maxrun;
1723 $cronjob->status = $status;
1724 $cronjob->test = $test;
1725 $cronjob->libname = $libname;
1726 $cronjob->module_name = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
1727
1728 $retCreate = $cronjob->create($user);
1729
1730 if ($retCreate < 0) {
1731 $this->error = implode("\n", array_merge([$cronjob->error], $cronjob->errors));
1732 return -1;
1733 }
1734 }
1735 }
1736
1737 return $err;
1738 }
1739
1740 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1746 public function delete_cronjobs()
1747 {
1748 // phpcs:enable
1749 global $conf;
1750
1751 $err = 0;
1752
1753 if (is_array($this->cronjobs)) {
1754 $sql = "DELETE FROM ".MAIN_DB_PREFIX."cronjob";
1755 $sql .= " WHERE module_name = '".$this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class)."'";
1756 $sql .= " AND entity = ".$conf->entity;
1757 $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.
1758 // 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.
1759
1760 dol_syslog(get_class($this)."::delete_cronjobs", LOG_DEBUG);
1761 $resql = $this->db->query($sql);
1762 if (!$resql) {
1763 $this->error = $this->db->lasterror();
1764 $err++;
1765 }
1766 }
1767
1768 return $err;
1769 }
1770
1771 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1777 public function delete_tabs()
1778 {
1779 // phpcs:enable
1780 global $conf;
1781
1782 $err = 0;
1783
1784 $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
1785 $sql .= " WHERE ".$this->db->decrypt('name')." LIKE '".$this->db->escape($this->const_name)."_TABS_%'";
1786 $sql .= " AND entity = ".((int) $conf->entity);
1787
1788 dol_syslog(get_class($this)."::delete_tabs", LOG_DEBUG);
1789 if (!$this->db->query($sql)) {
1790 $this->error = $this->db->lasterror();
1791 $err++;
1792 }
1793
1794 return $err;
1795 }
1796
1797 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1803 public function insert_tabs()
1804 {
1805 // phpcs:enable
1806 global $conf;
1807
1808 $err = 0;
1809
1810 if (!empty($this->tabs)) {
1811 dol_syslog(get_class($this)."::insert_tabs", LOG_DEBUG);
1812
1813 $i = 0;
1814 foreach ($this->tabs as $key => $value) {
1815 if (is_array($value) && count($value) == 0) {
1816 continue; // Discard empty arrays
1817 }
1818
1819 $entity = $conf->entity;
1820 $newvalue = $value;
1821
1822 if (is_array($value)) {
1823 $newvalue = $value['data'];
1824 if (isset($value['entity'])) {
1825 $entity = $value['entity'];
1826 }
1827 }
1828
1829 if ($newvalue) {
1830 $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (";
1831 $sql .= "name";
1832 $sql .= ", type";
1833 $sql .= ", value";
1834 $sql .= ", note";
1835 $sql .= ", visible";
1836 $sql .= ", entity";
1837 $sql .= ")";
1838 $sql .= " VALUES (";
1839 $sql .= $this->db->encrypt($this->const_name."_TABS_".$i);
1840 $sql .= ", 'chaine'";
1841 $sql .= ", ".$this->db->encrypt($newvalue);
1842 $sql .= ", null";
1843 $sql .= ", '0'";
1844 $sql .= ", ".((int) $entity);
1845 $sql .= ")";
1846
1847 $resql = $this->db->query($sql);
1848 if (!$resql) {
1849 dol_syslog($this->db->lasterror(), LOG_ERR);
1850 if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1851 $this->error = $this->db->lasterror();
1852 $this->errors[] = $this->db->lasterror();
1853 $err++;
1854 break;
1855 }
1856 }
1857 }
1858 $i++;
1859 }
1860 }
1861 return $err;
1862 }
1863
1864 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1870 public function insert_const()
1871 {
1872 // phpcs:enable
1873 global $conf;
1874
1875 $err = 0;
1876
1877 if (empty($this->const)) {
1878 return 0;
1879 }
1880
1881 dol_syslog(__METHOD__, LOG_DEBUG);
1882
1883 foreach ($this->const as $key => $value) {
1884 // Property to search if entry exists
1885 $name = $this->const[$key][0];
1886 $entity = (!empty($this->const[$key][5]) && $this->const[$key][5] != 'current') ? 0 : $conf->entity;
1887
1888 // Property to update if it does not exists
1889 $type = $this->const[$key][1];
1890 $val = $this->const[$key][2];
1891 $note = isset($this->const[$key][3]) ? $this->const[$key][3] : '';
1892 $visible = isset($this->const[$key][4]) ? $this->const[$key][4] : 0;
1893
1894 // Clean
1895 if (empty($visible)) {
1896 $visible = '0';
1897 }
1898 if (empty($val) && $val != '0') {
1899 $val = '';
1900 }
1901
1902 if (!empty($name)) {
1903 $sql = "SELECT count(*) as nb";
1904 $sql .= " FROM ".MAIN_DB_PREFIX."const";
1905 $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($name)."'";
1906 $sql .= " AND entity = ".((int) $entity);
1907
1908 $result = $this->db->query($sql);
1909 if ($result) {
1910 $row = $this->db->fetch_row($result);
1911
1912 if ($row[0] == 0) { // If not found
1913 $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (name, type, value, note, visible, entity)";
1914 $sql .= " VALUES (";
1915 $sql .= $this->db->encrypt($name);
1916 $sql .= ", '".$this->db->escape($type)."'";
1917 $sql .= ", ".(($val != '') ? $this->db->encrypt($val) : "''");
1918 $sql .= ", ".($note ? "'".$this->db->escape($note)."'" : "null");
1919 $sql .= ", '".$this->db->escape($visible)."'";
1920 $sql .= ", ".((int) $entity);
1921 $sql .= ")";
1922
1923 if (!$this->db->query($sql)) {
1924 $err++;
1925 } else {
1926 // Set also the variable in running environment
1927 $conf->global->$name = $val;
1928 }
1929 } else {
1930 dol_syslog(__METHOD__." constant '".$name."' already exists", LOG_DEBUG);
1931 }
1932 } else {
1933 $err++;
1934 }
1935 }
1936 }
1937
1938 return $err;
1939 }
1940
1941 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1947 public function delete_const()
1948 {
1949 // phpcs:enable
1950 global $conf;
1951
1952 $err = 0;
1953
1954 if (empty($this->const)) {
1955 return 0;
1956 }
1957
1958 foreach ($this->const as $key => $value) {
1959 $name = $this->const[$key][0];
1960 $deleteonunactive = (!empty($this->const[$key][6])) ? 1 : 0;
1961
1962 if ($deleteonunactive) {
1963 $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
1964 $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($name)."'";
1965 $sql .= " AND entity in (0, ".$conf->entity.")";
1966 dol_syslog(get_class($this)."::delete_const", LOG_DEBUG);
1967 if (!$this->db->query($sql)) {
1968 $this->error = $this->db->lasterror();
1969 $err++;
1970 }
1971 }
1972 }
1973
1974 return $err;
1975 }
1976
1977 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1986 public function insert_permissions($reinitadminperms = 0, $force_entity = null, $notrigger = 0)
1987 {
1988 // phpcs:enable
1989 global $conf, $user;
1990
1991 $err = 0;
1992 $entity = (!empty($force_entity) ? $force_entity : $conf->entity);
1993
1994 dol_syslog(get_class($this)."::insert_permissions", LOG_DEBUG);
1995
1996 // Test if module is activated
1997 $sql_del = "SELECT ".$this->db->decrypt('value')." as value";
1998 $sql_del .= " FROM ".MAIN_DB_PREFIX."const";
1999 $sql_del .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
2000 $sql_del .= " AND entity IN (0,".((int) $entity).")";
2001
2002 $resql = $this->db->query($sql_del);
2003
2004 if ($resql) {
2005 $obj = $this->db->fetch_object($resql);
2006
2007 if ($obj !== null && !empty($obj->value) && !empty($this->rights)) {
2008 include_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
2009
2010 // If the module is active
2011 foreach ($this->rights as $key => $value) {
2012 $r_id = $this->rights[$key][self::KEY_ID]; // permission id in llx_rights_def (not unique because primary key is couple id-entity)
2013 $r_label = $this->rights[$key][self::KEY_LABEL];
2014 $r_type = $this->rights[$key][self::KEY_TYPE] ?? 'w'; // TODO deprecated
2015 $r_default = $this->rights[$key][self::KEY_DEFAULT] ?? 0;
2016 $r_perms = $this->rights[$key][self::KEY_FIRST_LEVEL] ?? '';
2017 $r_subperms = $this->rights[$key][self::KEY_SECOND_LEVEL] ?? '';
2018
2019 $r_module_position = $this->getModulePosition();
2020 $r_family = $this->family;
2021 $r_family_position = 0;
2022
2023 // KEY_FIRST_LEVEL (perms) must not be empty
2024 if (empty($r_perms)) {
2025 continue;
2026 }
2027
2028 // name of module (default: current module name)
2029 $r_module = (empty($this->rights_class) ? strtolower($this->name) : $this->rights_class);
2030
2031 // name of the module from which the right comes (default: empty means same module the permission is for)
2032 $r_module_origin = '';
2033
2034 if (isset($this->rights[$key][self::KEY_MODULE])) {
2035 // name of the module to which the right must be applied
2036 $r_module = $this->rights[$key][self::KEY_MODULE];
2037 // name of the module from which the right comes
2038 $r_module_origin = (empty($this->rights_class) ? strtolower($this->name) : $this->rights_class);
2039 }
2040
2041 // condition to show or hide a user right (default: 1) (eg isModEnabled('anothermodule') or ($conf->global->MAIN_FEATURES_LEVEL > 0) or etc..)
2042 $r_enabled = $this->rights[$key][self::KEY_ENABLED] ?? '1';
2043
2044 // Search if perm already present
2045 $sql = "SELECT count(*) as nb FROM ".MAIN_DB_PREFIX."rights_def";
2046 $sql .= " WHERE entity = ".((int) $entity);
2047 $sql .= " AND id = ".((int) $r_id);
2048
2049 $resqlselect = $this->db->query($sql);
2050 if ($resqlselect) {
2051 $objcount = $this->db->fetch_object($resqlselect);
2052 if ($objcount && $objcount->nb == 0) {
2053 $sql = "INSERT INTO ".MAIN_DB_PREFIX."rights_def (";
2054 $sql .= "id";
2055 $sql .= ", entity";
2056 $sql .= ", libelle";
2057 $sql .= ", module";
2058 $sql .= ", module_origin";
2059 $sql .= ", module_position"; // Not that module_position can be fixed eynamically when accessing page user/perms.php
2060 $sql .= ", family";
2061 $sql .= ", family_position";
2062 $sql .= ", type"; // Not used yet
2063 $sql .= ", bydefault";
2064 $sql .= ", perms";
2065 $sql .= ", subperms";
2066 $sql .= ", enabled";
2067 $sql .= ") VALUES (";
2068 $sql .= ((int) $r_id);
2069 $sql .= ", ".((int) $entity);
2070 $sql .= ", '".$this->db->escape($r_label)."'";
2071 $sql .= ", '".$this->db->escape($r_module)."'";
2072 $sql .= ", '".$this->db->escape($r_module_origin)."'";
2073 $sql .= ", '".$this->db->escape((string) $r_module_position)."'";
2074 $sql .= ", '".$this->db->escape($r_family)."'";
2075 $sql .= ", '".$this->db->escape((string) $r_family_position)."'";
2076 $sql .= ", '".$this->db->escape($r_type)."'"; // Not used yet
2077 $sql .= ", ".((int) $r_default);
2078 $sql .= ", '".$this->db->escape($r_perms)."'";
2079 $sql .= ", '".$this->db->escape($r_subperms)."'";
2080 $sql .= ", '".$this->db->escape($r_enabled)."'";
2081 $sql .= ")";
2082
2083 $resqlinsert = $this->db->query($sql, 1);
2084
2085 if (!$resqlinsert) {
2086 if ($this->db->errno() != "DB_ERROR_RECORD_ALREADY_EXISTS") {
2087 $this->error = $this->db->lasterror();
2088 $err++;
2089 break;
2090 } else {
2091 dol_syslog(get_class($this)."::insert_permissions record already exists", LOG_INFO);
2092 }
2093 }
2094
2095 $this->db->free($resqlinsert);
2096 }
2097
2098 $this->db->free($resqlselect);
2099 }
2100
2101 // If we want to init permissions on admin users
2102 if (!empty($reinitadminperms)) {
2103 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."user WHERE admin = 1";
2104 dol_syslog(get_class($this)."::insert_permissions Search all admin users", LOG_DEBUG);
2105
2106 $resqlseladmin = $this->db->query($sql, 1);
2107
2108 if ($resqlseladmin) {
2109 $num = $this->db->num_rows($resqlseladmin);
2110 $i = 0;
2111 while ($i < $num) {
2112 $obj2 = $this->db->fetch_object($resqlseladmin);
2113 dol_syslog(get_class($this)."::insert_permissions Add permission id ".$r_id." to user id=".$obj2->rowid);
2114
2115 $tmpuser = new User($this->db);
2116 $result = $tmpuser->fetch($obj2->rowid);
2117 if ($result > 0) {
2118 $tmpuser->addrights($r_id, '', '', 0, 1);
2119 } else {
2120 dol_syslog(get_class($this)."::insert_permissions Failed to add the permission to user because fetch return an error", LOG_ERR);
2121 }
2122 $i++;
2123 }
2124 } else {
2125 dol_print_error($this->db);
2126 }
2127 }
2128 }
2129
2130 if (!empty($reinitadminperms) && !empty($user->admin)) { // Reload permission for current user if defined
2131 // We reload permissions
2132 $user->clearrights();
2133 $user->loadRights();
2134 }
2135 }
2136 $this->db->free($resql);
2137 } else {
2138 $this->error = $this->db->lasterror();
2139 $err++;
2140 }
2141
2142 return $err;
2143 }
2144
2145
2146 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2152 public function delete_permissions()
2153 {
2154 // phpcs:enable
2155 global $conf;
2156
2157 $err = 0;
2158
2159 $module = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
2160
2161 $sql = "DELETE FROM ".MAIN_DB_PREFIX."rights_def";
2162 $sql .= " WHERE (module = '".$this->db->escape($module)."' OR module_origin = '".$this->db->escape($module)."')";
2163
2164 // Delete all entities if core module
2165 if (empty($this->core_enabled)) {
2166 $sql .= " AND entity = ".((int) $conf->entity);
2167 }
2168
2169 dol_syslog(get_class($this)."::delete_permissions", LOG_DEBUG);
2170 if (!$this->db->query($sql)) {
2171 $this->error = $this->db->lasterror();
2172 $err++;
2173 }
2174
2175 return $err;
2176 }
2177
2178
2179 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2185 public function insert_menus()
2186 {
2187 // phpcs:enable
2188 global $conf, $user;
2189
2190 if (!is_array($this->menu) || empty($this->menu)) {
2191 return 0;
2192 }
2193
2194 include_once DOL_DOCUMENT_ROOT.'/core/class/menubase.class.php';
2195
2196 dol_syslog(get_class($this)."::insert_menus", LOG_DEBUG);
2197
2198 $err = 0;
2199
2200 // Common module
2201 $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
2202
2203 $this->db->begin();
2204
2205 foreach ($this->menu as $key => $value) {
2206 $menu = new Menubase($this->db);
2207 $menu->menu_handler = 'all';
2208
2209 //$menu->module=strtolower($this->name); TODO When right_class will be same than module name
2210 $menu->module = (empty($this->rights_class) ? strtolower($this->name) : $this->rights_class);
2211
2212 if (!$this->menu[$key]['fk_menu']) {
2213 $menu->fk_menu = 0;
2214 } else {
2215 $foundparent = 0;
2216 $fk_parent = $this->menu[$key]['fk_menu'];
2217 $reg = array();
2218 if (preg_match('/^r=/', $fk_parent)) { // old deprecated method
2219 $fk_parent = str_replace('r=', '', $fk_parent);
2220 if (isset($this->menu[$fk_parent]['rowid'])) {
2221 $menu->fk_menu = $this->menu[$fk_parent]['rowid'];
2222 $foundparent = 1;
2223 }
2224 } elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+),fk_leftmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
2225 $menu->fk_menu = -1;
2226 $menu->fk_mainmenu = $reg[1];
2227 $menu->fk_leftmenu = $reg[2];
2228 $foundparent = 1;
2229 } elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
2230 $menu->fk_menu = -1;
2231 $menu->fk_mainmenu = $reg[1];
2232 $menu->fk_leftmenu = '';
2233 $foundparent = 1;
2234 }
2235 if (!$foundparent) {
2236 $this->error = "ErrorBadDefinitionOfMenuArrayInModuleDescriptor";
2237 dol_syslog(get_class($this)."::insert_menus ".$this->error." ".$this->menu[$key]['fk_menu'], LOG_ERR);
2238 $err++;
2239 }
2240 }
2241 $menu->type = $this->menu[$key]['type'];
2242 $menu->mainmenu = isset($this->menu[$key]['mainmenu']) ? $this->menu[$key]['mainmenu'] : (isset($menu->fk_mainmenu) ? $menu->fk_mainmenu : '');
2243 $menu->leftmenu = isset($this->menu[$key]['leftmenu']) ? $this->menu[$key]['leftmenu'] : '';
2244 $menu->title = $this->menu[$key]['titre'];
2245 $menu->prefix = isset($this->menu[$key]['prefix']) ? $this->menu[$key]['prefix'] : '';
2246 $menu->url = $this->menu[$key]['url'];
2247 $menu->langs = isset($this->menu[$key]['langs']) ? $this->menu[$key]['langs'] : '';
2248 $menu->position = $this->menu[$key]['position'];
2249 $menu->perms = $this->menu[$key]['perms'];
2250 $menu->target = isset($this->menu[$key]['target']) ? $this->menu[$key]['target'] : '';
2251 $menu->user = $this->menu[$key]['user'];
2252 $menu->enabled = isset($this->menu[$key]['enabled']) ? $this->menu[$key]['enabled'] : 0;
2253 $menu->position = $this->menu[$key]['position'];
2254 $menu->entity = $entity;
2255
2256 if (!$err) {
2257 $result = $menu->create($user); // Save menu entry into table llx_menu
2258 if ($result > 0) {
2259 $this->menu[$key]['rowid'] = $result;
2260 } else {
2261 $this->error = $menu->error;
2262 dol_syslog(get_class($this).'::insert_menus result='.$result." ".$this->error, LOG_ERR);
2263 $err++;
2264 break;
2265 }
2266 }
2267 }
2268
2269 if (!$err) {
2270 $this->db->commit();
2271 } else {
2272 dol_syslog(get_class($this)."::insert_menus ".$this->error, LOG_ERR);
2273 $this->db->rollback();
2274 }
2275
2276 return $err;
2277 }
2278
2279
2280 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2286 public function delete_menus()
2287 {
2288 // phpcs:enable
2289 global $conf;
2290
2291 $err = 0;
2292
2293 //$module=strtolower($this->name); TODO When right_class will be same than module name
2294 $module = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
2295
2296 $sql = "DELETE FROM ".MAIN_DB_PREFIX."menu";
2297 $sql .= " WHERE module = '".$this->db->escape($module)."'";
2298 $sql .= " AND menu_handler = 'all'"; // We delete only lines that were added manually or by the module activation. We keep entry added by menuhandler like 'auguria'
2299 $sql .= " AND entity IN (0, ".$conf->entity.")";
2300
2301 dol_syslog(get_class($this)."::delete_menus", LOG_DEBUG);
2302 $resql = $this->db->query($sql);
2303 if (!$resql) {
2304 $this->error = $this->db->lasterror();
2305 $err++;
2306 }
2307
2308 return $err;
2309 }
2310
2311 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2317 public function create_dirs()
2318 {
2319 // phpcs:enable
2320 global $langs, $conf;
2321
2322 $err = 0;
2323 $name = '';
2324
2325 if (isset($this->dirs) && is_array($this->dirs)) {
2326 foreach ($this->dirs as $key => $value) {
2327 $addtodatabase = 0;
2328
2329 if (!is_array($value)) {
2330 $dir = $value; // Default simple mode
2331 } else {
2332 $constname = $this->const_name."_DIR_";
2333 $dir = $this->dirs[$key][1];
2334 $addtodatabase = empty($this->dirs[$key][2]) ? '' : $this->dirs[$key][2]; // Create constante in llx_const
2335 $subname = empty($this->dirs[$key][3]) ? '' : strtoupper($this->dirs[$key][3]); // Add submodule name (ex: $conf->module->submodule->dir_output)
2336 $forcename = empty($this->dirs[$key][4]) ? '' : strtoupper($this->dirs[$key][4]); // Change the module name if different
2337
2338 if (!empty($forcename)) {
2339 $constname = 'MAIN_MODULE_'.$forcename."_DIR_";
2340 }
2341 if (!empty($subname)) {
2342 $constname = $constname.$subname."_";
2343 }
2344
2345 $name = $constname.strtoupper($this->dirs[$key][0]);
2346 }
2347
2348 // Define directory full path ($dir must start with "/")
2349 if (!getDolGlobalString('MAIN_MODULE_MULTICOMPANY') || $conf->entity == 1) {
2350 $fulldir = DOL_DATA_ROOT.$dir;
2351 } else {
2352 $fulldir = DOL_DATA_ROOT."/".$conf->entity.$dir;
2353 }
2354 // Create dir if it does not exists
2355 if (!empty($fulldir) && !file_exists($fulldir)) {
2356 if (dol_mkdir($fulldir, DOL_DATA_ROOT) < 0) {
2357 $this->error = $langs->trans("ErrorCanNotCreateDir", $fulldir);
2358 dol_syslog(get_class($this)."::_init ".$this->error, LOG_ERR);
2359 $err++;
2360 }
2361 }
2362
2363 // Define the constant in database if requested (not the default mode)
2364 if (!empty($addtodatabase) && !empty($name)) {
2365 $result = $this->insert_dirs($name, $dir);
2366 if ($result) {
2367 $err++;
2368 }
2369 }
2370 }
2371 }
2372
2373 return $err;
2374 }
2375
2376
2377 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2386 public function insert_dirs($name, $dir)
2387 {
2388 // phpcs:enable
2389 global $conf;
2390
2391 $err = 0;
2392
2393 $sql = "SELECT count(*)";
2394 $sql .= " FROM ".MAIN_DB_PREFIX."const";
2395 $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($name)."'";
2396 $sql .= " AND entity = ".$conf->entity;
2397
2398 dol_syslog(get_class($this)."::insert_dirs", LOG_DEBUG);
2399 $result = $this->db->query($sql);
2400 if ($result) {
2401 $row = $this->db->fetch_row($result);
2402
2403 if ($row[0] == 0) {
2404 $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (name, type, value, note, visible, entity)";
2405 $sql .= " VALUES (".$this->db->encrypt($name).", 'chaine', ".$this->db->encrypt($dir).", '".$this->db->escape("Directory for module ".$this->name)."', '0', ".((int) $conf->entity).")";
2406
2407 dol_syslog(get_class($this)."::insert_dirs", LOG_DEBUG);
2408 $this->db->query($sql);
2409 }
2410 } else {
2411 $this->error = $this->db->lasterror();
2412 $err++;
2413 }
2414
2415 return $err;
2416 }
2417
2418
2419 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2425 public function delete_dirs()
2426 {
2427 // phpcs:enable
2428 global $conf;
2429
2430 $err = 0;
2431
2432 $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
2433 $sql .= " WHERE ".$this->db->decrypt('name')." LIKE '".$this->db->escape($this->const_name)."_DIR_%'";
2434 $sql .= " AND entity = ".$conf->entity;
2435
2436 dol_syslog(get_class($this)."::delete_dirs", LOG_DEBUG);
2437 if (!$this->db->query($sql)) {
2438 $this->error = $this->db->lasterror();
2439 $err++;
2440 }
2441
2442 return $err;
2443 }
2444
2445 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2452 public function insert_module_parts()
2453 {
2454 // phpcs:enable
2455 global $conf, $langs;
2456
2457 $error = 0;
2458
2459 if (is_array($this->module_parts)) {
2460 if (empty($this->module_parts['icon']) && !empty($this->picto) && preg_match('/^fa\-/', $this->picto)) {
2461 $this->module_parts['icon'] = $this->picto;
2462 }
2463
2464 foreach ($this->module_parts as $key => $value) {
2465 if (is_array($value) && count($value) == 0) {
2466 continue; // Discard empty arrays
2467 }
2468
2469 // If module brings website templates, we must generate the zip like we do whenenabling the website module
2470 if ($key == 'websitetemplates' && $value == 1) {
2471 $srcroot = dol_buildpath('/'.strtolower($this->name).'/doctemplates/websites');
2472
2473 // Copy templates in dir format (recommended) into zip file
2474 $docs = dol_dir_list($srcroot, 'directories', 0, 'website_.*$');
2475 foreach ($docs as $cursorfile) {
2476 $src = $srcroot.'/'.$cursorfile['name'];
2477 $dest = DOL_DATA_ROOT.'/doctemplates/websites/'.$cursorfile['name'];
2478
2479 dol_delete_file($dest.'.zip');
2480
2481 // Compress it
2482 global $errormsg; // Used by dol_compress_dir
2483 $errormsg = '';
2484 $result = dol_compress_dir($src, $dest.'.zip', 'zip');
2485 if ($result < 0) {
2486 $error++;
2487 $this->error = ($errormsg ? $errormsg : $langs->trans('ErrorFailToCreateZip', $dest));
2488 $this->errors[] = ($errormsg ? $errormsg : $langs->trans('ErrorFailToCreateZip', $dest));
2489 }
2490 }
2491
2492 // Copy also the preview website_xxx.jpg file
2493 $docs = dol_dir_list($srcroot, 'files', 0, 'website_.*\.jpg$');
2494 foreach ($docs as $cursorfile) {
2495 $src = $srcroot.'/'.$cursorfile['name'];
2496 $dest = DOL_DATA_ROOT.'/doctemplates/websites/'.$cursorfile['name'];
2497
2498 dol_copy($src, $dest);
2499 }
2500 }
2501
2502 $entity = $conf->entity; // Reset the current entity
2503 $newvalue = $value;
2504
2505 // Serialize array parameters
2506 if (is_array($value)) {
2507 // Can defined other parameters
2508 // Example when $key='hooks', then $value is an array('data'=>array('hookcontext1','hookcontext2'), 'entity'=>X)
2509 if (isset($value['data']) && is_array($value['data'])) {
2510 $newvalue = json_encode($value['data']);
2511 if (isset($value['entity'])) {
2512 $entity = $value['entity'];
2513 }
2514 } elseif (isset($value['data']) && !is_array($value['data'])) {
2515 $newvalue = $value['data'];
2516 if (isset($value['entity'])) {
2517 $entity = $value['entity'];
2518 }
2519 } else { // when hook is declared with syntax 'hook'=>array('hookcontext1','hookcontext2',...)
2520 $newvalue = json_encode($value);
2521 }
2522 }
2523
2524 if (!empty($newvalue)) {
2525 $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (";
2526 $sql .= "name";
2527 $sql .= ", type";
2528 $sql .= ", value";
2529 $sql .= ", note";
2530 $sql .= ", visible";
2531 $sql .= ", entity";
2532 $sql .= ")";
2533 $sql .= " VALUES (";
2534 $sql .= " ".$this->db->encrypt($this->const_name."_".strtoupper($key), 1);
2535 $sql .= ", 'chaine'";
2536 $sql .= ", ".$this->db->encrypt($newvalue, 1);
2537 $sql .= ", null";
2538 $sql .= ", '0'";
2539 $sql .= ", ".((int) $entity);
2540 $sql .= ")";
2541
2542 dol_syslog(get_class($this)."::insert_module_parts for key=".$this->const_name."_".strtoupper($key), LOG_DEBUG);
2543
2544 $resql = $this->db->query($sql, 1);
2545 if (!$resql) {
2546 if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2547 $error++;
2548 $this->error = $this->db->lasterror();
2549 } else {
2550 dol_syslog(get_class($this)."::insert_module_parts for ".$this->const_name."_".strtoupper($key)." Record already exists.", LOG_WARNING);
2551 }
2552 }
2553 }
2554 }
2555 }
2556 return $error;
2557 }
2558
2559 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2565 public function delete_module_parts()
2566 {
2567 // phpcs:enable
2568 global $conf;
2569
2570 $err = 0;
2571
2572 if (is_array($this->module_parts)) {
2573 dol_syslog(get_class($this)."::delete_module_parts", LOG_DEBUG);
2574
2575 if (empty($this->module_parts['icon']) && !empty($this->picto) && preg_match('/^fa\-/', $this->picto)) {
2576 $this->module_parts['icon'] = $this->picto;
2577 }
2578
2579 foreach ($this->module_parts as $key => $value) {
2580 // If entity is defined
2581 if (is_array($value) && isset($value['entity'])) {
2582 $entity = $value['entity'];
2583 } else {
2584 $entity = $conf->entity;
2585 }
2586
2587 $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
2588 $sql .= " WHERE ".$this->db->decrypt('name')." LIKE '".$this->db->escape($this->const_name)."_".strtoupper($key)."'";
2589 $sql .= " AND entity = ".((int) $entity);
2590
2591 if (!$this->db->query($sql)) {
2592 $this->error = $this->db->lasterror();
2593 $err++;
2594 }
2595 }
2596 }
2597 return $err;
2598 }
2599
2609 public function init($options = '')
2610 {
2611 return $this->_init(array(), $options);
2612 }
2613
2622 public function remove($options = '')
2623 {
2624 return $this->_remove(array(), $options);
2625 }
2626
2627
2635 public function getKanbanView($codeenabledisable = '', $codetoconfig = '')
2636 {
2637 global $langs;
2638
2639 // Define imginfo
2640 $imginfo = "info";
2641 if ($this->isCoreOrExternalModule() == 'external') {
2642 $imginfo = "info_black";
2643 }
2644
2645 $const_name = 'MAIN_MODULE_'.strtoupper(preg_replace('/^mod/i', '', get_class($this)));
2646
2647 $version = $this->getVersion(0);
2648 $versiontrans = '';
2649 if (preg_match('/development/i', $version)) {
2650 $versiontrans .= 'warning';
2651 }
2652 if (preg_match('/experimental/i', $version)) {
2653 $versiontrans .= 'warning';
2654 }
2655 if (preg_match('/deprecated/i', $version)) {
2656 $versiontrans .= 'warning';
2657 }
2658
2659 $return = '
2660 <div class="box-flex-item info-box-module'
2661 .(getDolGlobalString($const_name) ? '' : ' --disabled')
2662 .($this->isCoreOrExternalModule() == 'external' ? ' --external' : '')
2663 .($this->needUpdate ? ' --need-update' : '')
2664 .'">
2665 <div class="info-box info-box-sm info-box-module">
2666 <div class="info-box-icon'.(!getDolGlobalString($const_name) ? '' : ' info-box-icon-module-enabled'.($versiontrans ? ' info-box-icon-module-warning' : '')).'">';
2667
2668 $alttext = '';
2669 //if (is_array($objMod->need_dolibarr_version)) $alttext.=($alttext?' - ':'').'Dolibarr >= '.join('.',$objMod->need_dolibarr_version);
2670 //if (is_array($objMod->phpmin)) $alttext.=($alttext?' - ':'').'PHP >= '.join('.',$objMod->phpmin);
2671 if (!empty($this->picto)) {
2672 if (preg_match('/^\//i', $this->picto)) {
2673 $return .= img_picto($alttext, $this->picto, 'class="inline-block valignmiddle"', 1);
2674 } else {
2675 $return .= img_object($alttext, $this->picto, 'class="inline-block valignmiddle"');
2676 }
2677 } else {
2678 $return .= img_object($alttext, 'generic', 'class="inline-block valignmiddle"');
2679 }
2680
2681 if ($this->isCoreOrExternalModule() == 'external' || preg_match('/development|experimental|deprecated/i', $version)) {
2682 $versionTitle = $langs->trans("Version").' '.$this->getVersion(1);
2683 if ($this->needUpdate) {
2684 $versionTitle .= '<br>'.$langs->trans('ModuleUpdateAvailable').' : '.$this->lastVersion;
2685 }
2686
2687 $return .= '<span class="info-box-icon-version'.($versiontrans ? ' '.$versiontrans : '').' classfortooltip" title="'.dol_escape_js($versionTitle).'" >';
2688 $return .= $this->getVersion(1);
2689 $return .= '</span>';
2690 }
2691
2692 $return .= '</div>
2693 <div class="info-box-content info-box-text-module'.(!getDolGlobalString($const_name) ? '' : ' info-box-module-enabled'.($versiontrans ? ' info-box-content-warning' : '')).'">
2694 <span class="info-box-title noopacity">'.$this->getName().'</span>
2695 <span class="info-box-desc twolinesmax opacitymedium" title="'.dol_escape_htmltag($this->getDesc()).'">'.nl2br($this->getDesc()).'</span>';
2696
2697 $return .= '<div class="valignmiddle inline-block info-box-more">';
2698 //if ($versiontrans) print img_warning($langs->trans("Version").' '.$this->getVersion(1)).' ';
2699 $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>';
2700 $return .= '</div><br>';
2701
2702 $return .= '<div class="valignmiddle inline-block info-box-actions">';
2703 $return .= '<div class="valignmiddle inline-block info-box-setup">';
2704 $return .= $codetoconfig;
2705 $return .= '</div>';
2706 $return .= '<div class="valignmiddle inline-block marginleftonly marginrightonly">';
2707 $return .= $codeenabledisable;
2708 $return .= '</div>';
2709 $return .= '</div>';
2710
2711 $return .= '
2712 </div><!-- /.info-box-content -->
2713 </div><!-- /.info-box -->
2714 </div>';
2715
2716 return $return;
2717 }
2718
2727 public function checkForUpdate()
2728 {
2729 require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
2730 if (!empty($this->url_last_version)) {
2731 $lastVersion = getURLContent($this->url_last_version, 'GET', '', 1, array(), array('http', 'https'), 0); // Accept http or https links on external remote server only
2732 if (isset($lastVersion['content']) && strlen($lastVersion['content']) < 30) {
2733 // 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 _ . -
2734 $this->lastVersion = preg_replace("/[^a-zA-Z0-9_\.\-]+/", "", $lastVersion['content']);
2735 if (version_compare($this->lastVersion, $this->version) > 0) {
2736 $this->needUpdate = true;
2737 return 1;
2738 } else {
2739 $this->needUpdate = false;
2740 return 0;
2741 }
2742 } else {
2743 return -1;
2744 }
2745 }
2746 return 0;
2747 }
2748
2756 public function checkForCompliance($nametocheck = '')
2757 {
2758 global $conf, $langs;
2759
2760 if (empty($nametocheck)) {
2761 $nametocheck = $this->name;
2762 }
2763
2764 // Get list of illegal modules name or ID
2765 if (empty($conf->cache['noncompliantmodules'])) {
2766 require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
2767
2768 $result = getURLContent(self::URL_FOR_BLACKLISTED_MODULES, 'GET', '', 1, array(), array('http', 'https'), 0); // Accept http or https links on external remote server only
2769 if (isset($result['content']) && $result['http_code'] == 200) {
2770 $langs->load("errors");
2771
2772 // 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 _ . -
2773 $arrayoflines = preg_split("/[\n,]/", $result['content']);
2774 foreach ($arrayoflines as $line) {
2775 $tmpfieldsofline = explode(';', $line);
2776 $modulekey = strtolower(trim($tmpfieldsofline[0]));
2777 if (empty($modulekey)) {
2778 continue;
2779 }
2780 $conf->cache['noncompliantmodules'][$modulekey]['name'] = trim($tmpfieldsofline[0]);
2781 $conf->cache['noncompliantmodules'][$modulekey]['id'] = (isset($tmpfieldsofline[1]) ? trim($tmpfieldsofline[1]) : '');
2782 $conf->cache['noncompliantmodules'][$modulekey]['signature'] = (isset($tmpfieldsofline[2]) ? trim($tmpfieldsofline[2]) : '');
2783 $conf->cache['noncompliantmodules'][$modulekey]['message'] = $langs->trans(empty(isset($tmpfieldsofline[3]) ? trim($tmpfieldsofline[3]) : '') ? 'WarningModuleAffiliatedToAReportedCompany' : trim($tmpfieldsofline[3]));
2784 if (!empty($tmpfieldsofline[4])) {
2785 $message2 = $langs->trans("WarningModuleAffiliatedToAPiratPlatform", '{s}');
2786 $listofillegalurl = '';
2787 foreach (explode(" ", $tmpfieldsofline[4]) as $illegalurl) {
2788 $listofillegalurl .= ($listofillegalurl ? ' '.$langs->trans("or").' ' : '').'<b>'.preg_replace('/[^a-z0-9\.\-]/', '', $illegalurl).'</b>';
2789 }
2790 $message2 = str_replace('{s}', $listofillegalurl, $message2);
2791 $conf->cache['noncompliantmodules'][$modulekey]['message2'] = $message2;
2792 }
2793 }
2794 }
2795 }
2796
2797 if (!empty($conf->cache['noncompliantmodules'])) {
2798 $modulekey = strtolower($nametocheck);
2799 if (in_array($modulekey, array_keys($conf->cache['noncompliantmodules']))) {
2800 $answer = trim($conf->cache['noncompliantmodules'][$modulekey]['message']);
2801 if (!empty($conf->cache['noncompliantmodules'][$modulekey]['message2'])) {
2802 $answer .= '<br>'.$conf->cache['noncompliantmodules'][$modulekey]['message2'];
2803 }
2804 return $answer;
2805 }
2806 }
2807
2808 return 0;
2809 }
2810
2829 protected function declareNewDictionary($dictionaryArray, $langs = '')
2830 {
2831 $fields = array('name', 'lib', 'sql', 'sqlsort', 'field', 'fieldvalue', 'fieldinsert', 'rowid', 'cond', 'help', 'fieldcheck');
2832
2833 foreach ($fields as $field) {
2834 if (isset($dictionaryArray[$field])) {
2835 // @phan-suppress-next-line PhanTypeMismatchProperty
2836 $this->dictionaries['tab'.$field][] = $dictionaryArray[$field];
2837 }
2838 }
2839 if ($langs && !in_array($langs, $this->dictionaries[$langs])) {
2840 $this->dictionaries['langs'][] = $langs;
2841 }
2842 }
2843}
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.
Cron Job class.
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.
getDesc($foruseinpopupdesc=0)
Gives the translated module description if translation exists in admin.lang or the default module des...
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.
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:
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.
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
dol_copy($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=0)
Copy a file to another file.
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.
dol_is_file($pathoffile)
Return if path is a file.
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:64
dol_now($mode='gmt')
Return date for now.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
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 a 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...
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
dolMd2Html($content, $parser='parsedown', $replaceimagepath=null)
Function to parse MD content into HTML.
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:133