dolibarr 23.0.3
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-2025 Frédéric France <frederic.france@free.fr>
11 * Copyright (C) 2024-2025 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://ping.dolibarr.org/modules-blacklist.txt';
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
213 public $lastVersion = '';
214
219 public $needUpdate = false;
220
226 public $description;
227
234 public $descriptionlong;
235
239 public $dictionaries = array();
240
244 public $tabs;
245
246 // For exports
247
251 public $export_code;
252
256 public $export_label;
257
261 public $export_icon;
262
266 public $export_enabled;
270 public $export_permission;
274 public $export_fields_array;
278 public $export_TypeFields_array; // Array of key=>type where type can be 'Numeric', 'Date', 'Text', 'Boolean', 'Status', 'List:xxx:fieldlabel:rowid'
282 public $export_entities_array;
286 public $export_aggregate_array;
290 public $export_examplevalues_array;
294 public $export_help_array;
301 public $export_special_array; // special or computed field
308 public $export_dependencies_array;
312 public $export_sql_start;
316 public $export_sql_end;
320 public $export_sql_order;
321
322
323 // For import
324
328 public $import_code;
329
333 public $import_label;
334
338 public $import_icon;
342 public $import_entities_array;
346 public $import_tables_array;
350 public $import_tables_creator_array;
354 public $import_fields_array;
358 public $import_fieldshidden_array;
362 public $import_convertvalue_array;
366 public $import_regex_array;
370 public $import_examplevalues_array;
374 public $import_updatekeys_array;
378 public $import_run_sql_after_array;
382 public $import_TypeFields_array;
386 public $import_help_array;
387
391 public $const_name;
392
396 public $always_enabled;
397
401 public $disabled;
402
406 public $automatic_activation = array();
407
411 public $core_enabled;
412
419 public $picto;
420
427 public $config_page_url;
428
429
438 public $depends;
439
444 public $requiredby;
445
450 public $conflictwith;
451
455 public $langfiles;
456
462 public $warnings_activation;
463
469 public $warnings_activation_ext;
470
476 public $warnings_unactivation;
477
482 public $phpmin;
483
487 public $phpmax;
488
493 public $need_dolibarr_version;
494
499 public $max_dolibarr_version;
500
504 public $need_javascript_ajax;
505
509 public $enabled_bydefault;
510
514 public $hidden = false;
515
519 public $url_last_version;
520
521
527 public function __construct($db)
528 {
529 $this->db = $db;
530 }
531 // We should but can't set this as abstract because this will make dolibarr hang
532 // after migration due to old module not implementing. We must wait PHP is able to make
533 // a try catch on Fatal error to manage this correctly.
534 // We need constructor into function unActivateModule into admin.lib.php
535
536
537 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
548 protected function _init($array_sql, $options = '')
549 {
550 // phpcs:enable
551 global $conf;
552 $err = 0;
553
554 $this->db->begin();
555
556 // Insert activation module constant
557 if (!$err) {
558 $err += $this->_active();
559 }
560
561 // Insert new pages for tabs (into llx_const)
562 if (!$err) {
563 $err += $this->insert_tabs();
564 }
565
566 // Insert activation of module's parts. Copy website templates into doctemplates.
567 if (!$err) {
568 $err += $this->insert_module_parts();
569 }
570
571 // Insert constant defined by modules (into llx_const) if no existing yet
572 if (!$err && !preg_match('/newboxdefonly/', $options)) {
573 $err += $this->insert_const(); // Test on newboxdefonly to avoid to erase value during upgrade
574 }
575
576 // Insert boxes def (into llx_boxes_def) and boxes setup (into llx_boxes)
577 if (!$err && !preg_match('/noboxes/', $options)) {
578 $err += $this->insert_boxes($options);
579 }
580
581 // Insert cron job entries (entry in llx_cronjobs)
582 if (!$err) {
583 $err += $this->insert_cronjobs();
584 }
585
586 // Insert permission definitions of module into llx_rights_def. If user is admin, grant this permission to user.
587 if (!$err) {
588 $err += $this->insert_permissions(1, null, 1);
589 }
590
591 // Insert specific menus entries into database
592 if (!$err) {
593 $err += $this->insert_menus();
594 }
595
596 // Create module's directories
597 if (!$err) {
598 $err += $this->create_dirs();
599 }
600
601 // Execute addons requests
602 $num = count($array_sql);
603 for ($i = 0; $i < $num; $i++) {
604 if (!$err) {
605 $val = $array_sql[$i];
606 $sql = $val;
607 $ignoreerror = 0;
608 if (is_array($val)) {
609 $sql = $val['sql'];
610 $ignoreerror = $val['ignoreerror'] ?? 0;
611 }
612 // Add current entity id
613 $sql = str_replace('__ENTITY__', (string) $conf->entity, $sql);
614
615 dol_syslog(get_class($this)."::_init ignoreerror=".$ignoreerror, LOG_DEBUG);
616 $result = $this->db->query($sql, $ignoreerror);
617 if (!$result) {
618 if (!$ignoreerror) {
619 $this->error = $this->db->lasterror();
620 $err++;
621 } else {
622 dol_syslog(get_class($this)."::_init Warning ".$this->db->lasterror(), LOG_WARNING);
623 }
624 }
625 }
626 }
627
628 // Return code
629 if (!$err) {
630 $this->db->commit();
631
632 $moduleNameInConf = strtolower(preg_replace('/^MAIN_MODULE_/', '', $this->const_name));
633 // two exceptions to handle
634 if ($moduleNameInConf === 'propale') {
635 $moduleNameInConf = 'propal';
636 } elseif ($moduleNameInConf === 'supplierproposal') {
637 $moduleNameInConf = 'supplier_proposal';
638 }
639
640 $conf->modules[$moduleNameInConf] = $moduleNameInConf; // Add this module in list of enabled modules so isModEnabled() will work (conf->module->enabled must no more be used)
641
642 return 1;
643 } else {
644 $this->db->rollback();
645 return 0;
646 }
647 }
648
649 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
658 protected function _remove($array_sql, $options = '')
659 {
660 global $conf;
661 // phpcs:enable
662 $err = 0;
663
664 $this->db->begin();
665
666 // Remove activation module line (constant MAIN_MODULE_MYMODULE in llx_const)
667 if (!$err) {
668 $err += $this->_unactive();
669 }
670
671 // Remove activation of module's new tabs (MAIN_MODULE_MYMODULE_TABS_XXX in llx_const)
672 if (!$err) {
673 $err += $this->delete_tabs();
674 }
675
676 // Remove activation of module's parts (MAIN_MODULE_MYMODULE_XXX in llx_const)
677 if (!$err) {
678 $err += $this->delete_module_parts();
679 }
680
681 // Remove constants defined by modules
682 if (!$err) {
683 $err += $this->delete_const();
684 }
685
686 // Remove list of module's available boxes (entry in llx_boxes)
687 if (!$err && !preg_match('/(newboxdefonly|noboxes)/', $options)) {
688 $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
689 }
690
691 // Remove list of module's cron job entries (entry in llx_cronjobs)
692 if (!$err) {
693 $err += $this->delete_cronjobs();
694 }
695
696 // Remove module's permissions from list of available permissions (entries in llx_rights_def)
697 if (!$err) {
698 $err += $this->delete_permissions();
699 }
700
701 // Remove module's menus (entries in llx_menu)
702 if (!$err) {
703 $err += $this->delete_menus();
704 }
705
706 // Remove module's directories
707 if (!$err) {
708 $err += $this->delete_dirs();
709 }
710
711 // Run complementary sql requests
712 $num = count((array) $array_sql);
713 for ($i = 0; $i < $num; $i++) {
714 if (!$err) {
715 dol_syslog(get_class($this)."::_remove", LOG_DEBUG);
716 $result = $this->db->query($array_sql[$i]);
717 if (!$result) {
718 $this->error = $this->db->error();
719 $err++;
720 }
721 }
722 }
723
724 // Return code
725 if (!$err) {
726 $this->db->commit();
727
728 // Disable modules
729 $moduleNameInConf = strtolower(preg_replace('/^MAIN_MODULE_/', '', $this->const_name));
730 // two exceptions to handle
731 if ($moduleNameInConf === 'propale') {
732 $moduleNameInConf = 'propal';
733 } elseif ($moduleNameInConf === 'supplierproposal') {
734 $moduleNameInConf = 'supplier_proposal';
735 }
736
737 unset($conf->modules[$moduleNameInConf]); // Add this module in list of enabled modules so isModEnabled() will work (conf->module->enabled must no more be used)
738
739 return 1;
740 } else {
741 $this->db->rollback();
742 return 0;
743 }
744 }
745
746
753 public function getName()
754 {
755 global $langs;
756 $langs->load("admin");
757
758 if ($langs->transnoentitiesnoconv("Module".$this->numero."Name") != "Module".$this->numero."Name") {
759 // If module name translation exists
760 return $langs->transnoentitiesnoconv("Module".$this->numero."Name");
761 } else {
762 // If module name translation using it's unique id does not exist, we try to use its name to find translation
763 if (is_array($this->langfiles)) {
764 foreach ($this->langfiles as $val) {
765 if ($val) {
766 $langs->load($val);
767 }
768 }
769 }
770
771 if ($langs->trans("Module".$this->name."Name") != "Module".$this->name."Name") {
772 // If module name translation exists
773 return $langs->transnoentitiesnoconv("Module".$this->name."Name");
774 }
775
776 // Last chance with simple label
777 return $langs->transnoentitiesnoconv($this->name);
778 }
779 }
780
781
788 public function getDesc($foruseinpopupdesc = 0)
789 {
790 global $langs;
791 $langs->load("admin");
792
793 if ($langs->transnoentitiesnoconv("Module".$this->numero."Desc") != "Module".$this->numero."Desc") {
794 // If module description translation exists
795 return $langs->transnoentitiesnoconv("Module".$this->numero."Desc");
796 } else {
797 // If module description translation does not exist using its unique id, we can use its name to find translation
798 if (is_array($this->langfiles)) {
799 foreach ($this->langfiles as $val) {
800 if ($val) {
801 $langs->load($val);
802 }
803 }
804 }
805
806 if ($langs->transnoentitiesnoconv("Module".$this->name."Desc") != "Module".$this->name."Desc") {
807 // If module name translation exists
808 return $langs->trans("Module".$this->name."Desc");
809 }
810
811 // Last chance with simple label
812 return $langs->trans($this->description);
813 }
814 }
815
822 public function getDescLong()
823 {
824 global $langs;
825 $langs->load("admin");
826
827 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
828 include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
829
830 $content = '';
831 $pathoffile = $this->getDescLongReadmeFound();
832
833 if ($pathoffile) { // Mostly for external modules
834 $content = file_get_contents($pathoffile, false, null, 0, 1024 * 1024); // Max size loaded 1Mb
835
836 if ((float) DOL_VERSION >= 6.0) { // @phpstan-ignore-line
837 @include_once DOL_DOCUMENT_ROOT.'/core/lib/parsemd.lib.php';
838
839 $content = dolMd2Html(
840 $content,
841 'parsedown',
842 array(
843 'doc/' => dol_buildpath(strtolower($this->name).'/doc/', 1),
844 'img/' => dol_buildpath(strtolower($this->name).'/img/', 1),
845 'images/' => dol_buildpath(strtolower($this->name).'/images/', 1),
846 )
847 );
848
849 $content = preg_replace('/<a href="/', '<a target="_blank" rel="noopener noreferrer" href="', $content);
850 } else {
851 $content = nl2br($content);
852 }
853 } else {
854 // Mostly for internal modules
855 if (!empty($this->descriptionlong)) {
856 if (is_array($this->langfiles)) {
857 foreach ($this->langfiles as $val) {
858 if ($val) {
859 $langs->load($val);
860 }
861 }
862 }
863
864 $content = $langs->transnoentitiesnoconv($this->descriptionlong);
865 }
866 }
867
868 return '<div class="moduledesclong">'.$content.'</div>';
869 }
870
876 public function getDescLongReadmeFound()
877 {
878 global $langs;
879
880 $filefound = false;
881
882 // Define path to file README.md.
883 // First check README-la_LA.md then README-la.md then README.md
884 $pathoffile = dol_buildpath(strtolower($this->name).'/README-'.$langs->defaultlang.'.md', 0);
885 if (dol_is_file($pathoffile)) {
886 $filefound = true;
887 }
888 if (!$filefound) {
889 $tmp = explode('_', $langs->defaultlang);
890 $pathoffile = dol_buildpath(strtolower($this->name).'/README-'.$tmp[0].'.md', 0);
891 if (dol_is_file($pathoffile)) {
892 $filefound = true;
893 }
894 }
895 if (!$filefound) {
896 $pathoffile = dol_buildpath(strtolower($this->name).'/README.md', 0);
897 if (dol_is_file($pathoffile)) {
898 $filefound = true;
899 }
900 }
901
902 return ($filefound ? $pathoffile : '');
903 }
904
905
911 public function getChangeLog()
912 {
913 global $langs;
914 $langs->load("admin");
915
916 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
917 include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
918
919 $filefound = false;
920
921 // Define path to file README.md.
922 // First check ChangeLog-la_LA.md then ChangeLog.md
923 $pathoffile = dol_buildpath(strtolower($this->name).'/ChangeLog-'.$langs->defaultlang.'.md', 0);
924 if (dol_is_file($pathoffile)) {
925 $filefound = true;
926 }
927 if (!$filefound) {
928 $pathoffile = dol_buildpath(strtolower($this->name).'/ChangeLog.md', 0);
929 if (dol_is_file($pathoffile)) {
930 $filefound = true;
931 }
932 }
933
934 if ($filefound) { // Mostly for external modules
935 $content = file_get_contents($pathoffile);
936
937 if ((float) DOL_VERSION >= 6.0) { // @phpstan-ignore-line
938 @include_once DOL_DOCUMENT_ROOT.'/core/lib/parsemd.lib.php';
939
940 $content = dolMd2Html($content, 'parsedown', array('doc/' => dol_buildpath(strtolower($this->name).'/doc/', 1)));
941 } else {
942 $content = nl2br($content);
943 }
944 } else {
945 $content = '';
946 }
947
948 return $content;
949 }
950
956 public function getPublisher()
957 {
958 return $this->editor_name;
959 }
960
966 public function getPublisherUrl()
967 {
968 return $this->editor_url;
969 }
970
979 public function getVersion($translated = 1)
980 {
981 global $langs;
982 $langs->load("admin");
983
984 $ret = '';
985
986 $newversion = preg_replace('/_deprecated/', '', $this->version);
987 if ($newversion == 'experimental') {
988 $ret = ($translated ? $langs->transnoentitiesnoconv("VersionExperimental") : $newversion);
989 } elseif ($newversion == 'development') {
990 $ret = ($translated ? $langs->transnoentitiesnoconv("VersionDevelopment") : $newversion);
991 } elseif ($newversion == 'dolibarr') {
992 $ret = DOL_VERSION;
993 } elseif ($newversion) {
994 $ret = $newversion;
995 } else {
996 $ret = ($translated ? $langs->transnoentitiesnoconv("VersionUnknown") : 'unknown');
997 }
998
999 if (preg_match('/_deprecated/', $this->version)) {
1000 $ret .= ($translated ? ' ('.$langs->transnoentitiesnoconv("Deprecated").')' : $this->version);
1001 }
1002 return $ret;
1003 }
1004
1010 public function getModulePosition()
1011 {
1012 if (in_array($this->version, array('dolibarr', 'dolibarr_deprecated', 'experimental', 'development'))) { // core module
1013 return $this->module_position;
1014 } else { // external module
1015 if ($this->module_position >= 100000) {
1016 return $this->module_position;
1017 } else {
1018 $position = intval($this->module_position) + 100000;
1019 return strval($position);
1020 }
1021 }
1022 }
1023
1030 public function isCoreOrExternalModule()
1031 {
1032 if ($this->version == 'dolibarr' || $this->version == 'dolibarr_deprecated') {
1033 return 'core';
1034 }
1035 if (!empty($this->version) && !in_array($this->version, array('experimental', 'development'))) {
1036 return 'external';
1037 }
1038 if (!empty($this->editor_name) || !empty($this->editor_url)) {
1039 return 'external';
1040 }
1041 if ($this->numero >= 100000) {
1042 return 'external';
1043 }
1044 return 'unknown';
1045 }
1046
1047
1053 public function getLangFilesArray()
1054 {
1055 return $this->langfiles;
1056 }
1057
1065 public function getExportDatasetLabel($r)
1066 {
1067 global $langs;
1068
1069 $langstring = "ExportDataset_".$this->export_code[$r];
1070 if ($langs->trans($langstring) == $langstring) {
1071 // Translation not found
1072 return $langs->trans($this->export_label[$r]);
1073 } else {
1074 // Translation found
1075 return $langs->trans($langstring);
1076 }
1077 }
1078
1079
1087 public function getImportDatasetLabel($r)
1088 {
1089 global $langs;
1090
1091 $langstring = "ImportDataset_".$this->import_code[$r];
1092 //print "x".$langstring;
1093 if ($langs->trans($langstring) == $langstring) {
1094 // Translation not found
1095 return $langs->transnoentitiesnoconv($this->import_label[$r]);
1096 } else {
1097 // Translation found
1098 return $langs->transnoentitiesnoconv($langstring);
1099 }
1100 }
1101
1102
1108 public function getLastActivationDate()
1109 {
1110 global $conf;
1111
1112 $err = 0;
1113
1114 $sql = "SELECT tms FROM ".MAIN_DB_PREFIX."const";
1115 $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
1116 $sql .= " AND entity IN (0, ".((int) $conf->entity).")";
1117
1118 dol_syslog(get_class($this)."::getLastActiveDate", LOG_DEBUG);
1119 $resql = $this->db->query($sql);
1120 if (!$resql) {
1121 $err++;
1122 } else {
1123 $obj = $this->db->fetch_object($resql);
1124 if ($obj) {
1125 return $this->db->jdate($obj->tms);
1126 }
1127 }
1128
1129 return '';
1130 }
1131
1132
1138 public function getLastActivationInfo()
1139 {
1140 global $conf;
1141
1142 $err = 0;
1143
1144 $sql = "SELECT tms, note FROM ".MAIN_DB_PREFIX."const";
1145 $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
1146 $sql .= " AND entity IN (0, ".$conf->entity.")";
1147
1148 dol_syslog(get_class($this)."::getLastActiveDate", LOG_DEBUG);
1149 $resql = $this->db->query($sql);
1150 if (!$resql) {
1151 $err++;
1152 } else {
1153 $obj = $this->db->fetch_object($resql);
1154 if ($obj) {
1155 $tmp = array();
1156 if ($obj->note) {
1157 $tmp = json_decode($obj->note, true);
1158 }
1159 return array(
1160 'authorid' => empty($tmp['authorid']) ? '' : (int) $tmp['authorid'],
1161 'ip' => empty($tmp['ip']) ? '' : (string) $tmp['ip'],
1162 'lastactivationdate' => $this->db->jdate($obj->tms),
1163 'lastactivationversion' => (!empty($tmp['lastactivationversion']) ? (string) $tmp['lastactivationversion'] : 'unknown'),
1164 );
1165 }
1166 }
1167
1168 return array();
1169 }
1170
1171
1172 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1178 protected function _active()
1179 {
1180 // phpcs:enable
1181 global $conf, $user;
1182
1183 $err = 0;
1184
1185 // Common module
1186 $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
1187
1188 $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
1189 $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
1190 $sql .= " AND entity IN (0, ".$entity.")";
1191
1192 dol_syslog(get_class($this)."::_active delete activation constant", LOG_DEBUG);
1193 $resql = $this->db->query($sql);
1194 if (!$resql) {
1195 $err++;
1196 }
1197
1198 $note = json_encode(
1199 array(
1200 'authorid' => (is_object($user) ? $user->id : 0),
1201 'ip' => (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']),
1202 'lastactivationversion' => $this->version,
1203 )
1204 );
1205
1206 $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (name, value, visible, entity, note) VALUES";
1207 $sql .= " (".$this->db->encrypt($this->const_name);
1208 $sql .= ", ".$this->db->encrypt('1');
1209 $sql .= ", 0, ".((int) $entity);
1210 $sql .= ", '".$this->db->escape($note)."')";
1211
1212 dol_syslog(get_class($this)."::_active insert activation constant", LOG_DEBUG);
1213 $resql = $this->db->query($sql);
1214 if (!$resql) {
1215 $err++;
1216 }
1217
1218 return $err;
1219 }
1220
1221
1222 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1228 protected function _unactive()
1229 {
1230 // phpcs:enable
1231 global $conf;
1232
1233 $err = 0;
1234
1235 // Common module
1236 $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
1237
1238 $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
1239 $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
1240 $sql .= " AND entity IN (0, ".$entity.")";
1241
1242 dol_syslog(get_class($this)."::_unactive", LOG_DEBUG);
1243 $this->db->query($sql);
1244
1245 return $err;
1246 }
1247
1248
1249 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps,PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1265 protected function _load_tables($reldir, $onlywithsuffix = '')
1266 {
1267 // phpcs:enable
1268 global $conf;
1269
1270 $error = 0;
1271 $dirfound = 0;
1272 $ok = 1;
1273
1274 if (empty($reldir)) {
1275 return 1;
1276 }
1277
1278 include_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
1279
1280 foreach ($conf->file->dol_document_root as $dirroot) {
1281 if ($ok == 1) {
1282 $dirsql = $dirroot.$reldir;
1283 $ok = 0;
1284
1285 // We will loop on xxx/, xxx/tables/, xxx/data/
1286 $listofsubdir = array('', 'tables/', 'data/');
1287 if ($this->db->type == 'pgsql') {
1288 $listofsubdir[] = '../pgsql/functions/';
1289 }
1290
1291 foreach ($listofsubdir as $subdir) {
1292 $dir = $dirsql.$subdir;
1293
1294 $handle = @opendir($dir); // Dir may not exists
1295 if (is_resource($handle)) {
1296 $dirfound++;
1297
1298 // Run llx_mytable.sql files, then llx_mytable_*.sql
1299 $files = array();
1300 while (($file = readdir($handle)) !== false) {
1301 $files[] = $file;
1302 }
1303 sort($files);
1304 foreach ($files as $file) {
1305 if ($onlywithsuffix) {
1306 if (!preg_match('/\-'.preg_quote($onlywithsuffix, '/').'\./i', $file)) {
1307 //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
1308 continue;
1309 } else {
1310 //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
1311 }
1312 }
1313 if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'llx_') {
1314 $result = run_sql($dir.$file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, 0, 1);
1315 if ($result <= 0) {
1316 $error++;
1317 }
1318 }
1319 }
1320
1321 rewinddir($handle);
1322
1323 // Run llx_mytable.key.sql files (Must be done after llx_mytable.sql) then then llx_mytable_*.key.sql
1324 $files = array();
1325 while (($file = readdir($handle)) !== false) {
1326 $files[] = $file;
1327 }
1328 sort($files);
1329 foreach ($files as $file) {
1330 if ($onlywithsuffix) {
1331 if (!preg_match('/\-'.preg_quote($onlywithsuffix, '/').'\./i', $file)) {
1332 //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
1333 continue;
1334 } else {
1335 //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
1336 }
1337 }
1338 if (preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'llx_') {
1339 $result = run_sql($dir.$file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, 0, 1);
1340 if ($result <= 0) {
1341 $error++;
1342 }
1343 }
1344 }
1345
1346 rewinddir($handle);
1347
1348 // Run functions-xxx.sql files (Must be done after llx_mytable.key.sql)
1349 $files = array();
1350 while (($file = readdir($handle)) !== false) {
1351 $files[] = $file;
1352 }
1353 sort($files);
1354 foreach ($files as $file) {
1355 if ($onlywithsuffix) {
1356 if (!preg_match('/\-'.preg_quote($onlywithsuffix, '/').'\./i', $file)) {
1357 //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
1358 continue;
1359 } else {
1360 //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
1361 }
1362 }
1363 if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 9) == 'functions') {
1364 $result = run_sql($dir.$file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, 0, 1);
1365 if ($result <= 0) {
1366 $error++;
1367 }
1368 }
1369 }
1370
1371 rewinddir($handle);
1372
1373 // Run data_xxx.sql files (Must be done after llx_mytable.key.sql)
1374 $files = array();
1375 while (($file = readdir($handle)) !== false) {
1376 $files[] = $file;
1377 }
1378 sort($files);
1379 foreach ($files as $file) {
1380 if ($onlywithsuffix) {
1381 if (!preg_match('/\-'.preg_quote($onlywithsuffix, '/').'\./i', $file)) {
1382 //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
1383 continue;
1384 } else {
1385 //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
1386 }
1387 }
1388 if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'data') {
1389 $result = run_sql($dir.$file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, 0, 1);
1390 if ($result <= 0) {
1391 $error++;
1392 }
1393 }
1394 }
1395
1396 rewinddir($handle);
1397
1398 // Run update_xxx.sql files
1399 $files = array();
1400 while (($file = readdir($handle)) !== false) {
1401 $files[] = $file;
1402 }
1403 sort($files);
1404 foreach ($files as $file) {
1405 if ($onlywithsuffix) {
1406 if (!preg_match('/\-'.preg_quote($onlywithsuffix, '/').'\./i', $file)) {
1407 //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
1408 continue;
1409 } else {
1410 //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
1411 }
1412 }
1413 if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 6) == 'update') {
1414 $result = run_sql($dir.$file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, 0, 1);
1415 if ($result <= 0) {
1416 $error++;
1417 }
1418 }
1419 }
1420
1421 closedir($handle);
1422 }
1423 }
1424
1425 if ($error == 0) {
1426 $ok = 1;
1427 }
1428 }
1429 }
1430
1431 if (!$dirfound) {
1432 dol_syslog("A module wants to load sql files from ".$reldir." but this directory was not found.", LOG_WARNING);
1433 }
1434 return $ok;
1435 }
1436
1437
1438 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1446 public function insert_boxes($option = '')
1447 {
1448 // phpcs:enable
1449 include_once DOL_DOCUMENT_ROOT.'/core/class/infobox.class.php';
1450
1451 global $conf;
1452
1453 $err = 0;
1454
1455 if (is_array($this->boxes)) {
1456 dol_syslog(get_class($this)."::insert_boxes", LOG_DEBUG);
1457
1458 $pos_name = InfoBox::getListOfPagesForBoxes();
1459
1460 foreach ($this->boxes as $key => $value) {
1461 $file = isset($this->boxes[$key]['file']) ? $this->boxes[$key]['file'] : '';
1462 $note = isset($this->boxes[$key]['note']) ? $this->boxes[$key]['note'] : '';
1463 $enabledbydefaulton = isset($this->boxes[$key]['enabledbydefaulton']) ? $this->boxes[$key]['enabledbydefaulton'] : 'Home';
1464
1465 if (empty($file)) {
1466 $file = isset($this->boxes[$key][1]) ? $this->boxes[$key][1] : ''; // For backward compatibility
1467 }
1468 if (empty($note)) {
1469 $note = isset($this->boxes[$key][2]) ? $this->boxes[$key][2] : ''; // For backward compatibility
1470 }
1471
1472 // Search if boxes def already present
1473 $sql = "SELECT count(*) as nb FROM ".MAIN_DB_PREFIX."boxes_def";
1474 $sql .= " WHERE file = '".$this->db->escape($file)."'";
1475 $sql .= " AND entity = ".$conf->entity;
1476 if ($note) {
1477 $sql .= " AND note ='".$this->db->escape($note)."'";
1478 }
1479
1480 $result = $this->db->query($sql);
1481 if ($result) {
1482 $obj = $this->db->fetch_object($result);
1483 if ($obj->nb == 0) {
1484 $this->db->begin();
1485
1486 if (!$err) {
1487 $sql = "INSERT INTO ".MAIN_DB_PREFIX."boxes_def (file, entity, note)";
1488 $sql .= " VALUES ('".$this->db->escape($file)."', ";
1489 $sql .= $conf->entity.", ";
1490 $sql .= $note ? "'".$this->db->escape($note)."'" : "null";
1491 $sql .= ")";
1492
1493 dol_syslog(get_class($this)."::insert_boxes", LOG_DEBUG);
1494 $resql = $this->db->query($sql);
1495 if (!$resql) {
1496 $err++;
1497 }
1498 }
1499 if (!$err && !preg_match('/newboxdefonly/', $option)) {
1500 $lastid = $this->db->last_insert_id(MAIN_DB_PREFIX."boxes_def", "rowid");
1501
1502 foreach ($pos_name as $key2 => $val2) {
1503 //print 'key2='.$key2.'-val2='.$val2."<br>\n";
1504 if ($enabledbydefaulton && $val2 != $enabledbydefaulton) {
1505 continue; // Not enabled by default onto this page.
1506 }
1507
1508 $sql = "INSERT INTO ".MAIN_DB_PREFIX."boxes (box_id, position, box_order, fk_user, entity)";
1509 $sql .= " VALUES (".((int) $lastid).", ".((int) $key2).", '0', 0, ".((int) $conf->entity).")";
1510
1511 dol_syslog(get_class($this)."::insert_boxes onto page ".$key2."=".$val2, LOG_DEBUG);
1512 $resql = $this->db->query($sql);
1513 if (!$resql) {
1514 $err++;
1515 }
1516 }
1517 }
1518
1519 if (!$err) {
1520 $this->db->commit();
1521 } else {
1522 $this->error = $this->db->lasterror();
1523 $this->db->rollback();
1524 }
1525 }
1526 // else box already registered into database
1527 } else {
1528 $this->error = $this->db->lasterror();
1529 $err++;
1530 }
1531 }
1532 }
1533
1534 return $err;
1535 }
1536
1537
1538 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1544 public function delete_boxes()
1545 {
1546 // phpcs:enable
1547 global $conf;
1548
1549 $err = 0;
1550
1551 if (is_array($this->boxes)) {
1552 foreach ($this->boxes as $key => $value) {
1553 //$titre = $this->boxes[$key][0];
1554 if (empty($this->boxes[$key]['file'])) {
1555 $file = isset($this->boxes[$key][1]) ? $this->boxes[$key][1] : ''; // For backward compatibility
1556 } else {
1557 $file = $this->boxes[$key]['file'];
1558 }
1559
1560 //$note = $this->boxes[$key][2];
1561
1562 // TODO If the box is also included by another module and the other module is still on, we should not remove it.
1563 // For the moment, we manage this with hard coded exception
1564 //print "Remove box ".$file.'<br>';
1565 if ($file == 'box_graph_product_distribution.php') {
1566 if (isModEnabled("product") || isModEnabled("service")) {
1567 dol_syslog("We discard deleting module ".$file." because another module still active requires it.");
1568 continue;
1569 }
1570 }
1571
1572 if ($this->db->type == 'sqlite3') {
1573 // sqlite doesn't support "USING" syntax.
1574 // TODO: remove this dependency.
1575 $sql = "DELETE FROM ".MAIN_DB_PREFIX."boxes ";
1576 $sql .= "WHERE ".MAIN_DB_PREFIX."boxes.box_id IN (";
1577 $sql .= "SELECT ".MAIN_DB_PREFIX."boxes_def.rowid ";
1578 $sql .= "FROM ".MAIN_DB_PREFIX."boxes_def ";
1579 $sql .= "WHERE ".MAIN_DB_PREFIX."boxes_def.file = '".$this->db->escape($file)."') ";
1580 $sql .= "AND ".MAIN_DB_PREFIX."boxes.entity = ".$conf->entity;
1581 } else {
1582 $sql = "DELETE FROM ".MAIN_DB_PREFIX."boxes";
1583 $sql .= " USING ".MAIN_DB_PREFIX."boxes, ".MAIN_DB_PREFIX."boxes_def";
1584 $sql .= " WHERE ".MAIN_DB_PREFIX."boxes.box_id = ".MAIN_DB_PREFIX."boxes_def.rowid";
1585 $sql .= " AND ".MAIN_DB_PREFIX."boxes_def.file = '".$this->db->escape($file)."'";
1586 $sql .= " AND ".MAIN_DB_PREFIX."boxes.entity = ".$conf->entity;
1587 }
1588
1589 dol_syslog(get_class($this)."::delete_boxes", LOG_DEBUG);
1590 $resql = $this->db->query($sql);
1591 if (!$resql) {
1592 $this->error = $this->db->lasterror();
1593 $err++;
1594 }
1595
1596 $sql = "DELETE FROM ".MAIN_DB_PREFIX."boxes_def";
1597 $sql .= " WHERE file = '".$this->db->escape($file)."'";
1598 $sql .= " AND entity = ".$conf->entity; // Do not use getEntity here, we want to delete only in current company
1599
1600 dol_syslog(get_class($this)."::delete_boxes", LOG_DEBUG);
1601 $resql = $this->db->query($sql);
1602 if (!$resql) {
1603 $this->error = $this->db->lasterror();
1604 $err++;
1605 }
1606 }
1607 }
1608
1609 return $err;
1610 }
1611
1612 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1618 public function insert_cronjobs()
1619 {
1620 // phpcs:enable
1621 include_once DOL_DOCUMENT_ROOT . '/core/class/infobox.class.php';
1622 include_once DOL_DOCUMENT_ROOT . '/cron/class/cronjob.class.php';
1623 include_once DOL_DOCUMENT_ROOT . '/user/class/user.class.php';
1624
1625 global $conf, $user;
1626
1627 if (empty($user)) {
1628 $user = new User($this->db);
1629 }
1630
1631 $err = 0;
1632
1633 if (is_array($this->cronjobs)) {
1634 dol_syslog(get_class($this) . "::insert_cronjobs", LOG_DEBUG);
1635
1636 foreach ($this->cronjobs as $key => $value) {
1637 $now = dol_now();
1638
1639 $entity = isset($value['entity']) ? $value['entity'] : $conf->entity;
1640 $label = isset($value['label']) ? $value['label'] : '';
1641 $jobtype = isset($value['jobtype']) ? $value['jobtype'] : '';
1642 $classesname = isset($value['class']) ? $value['class'] : '';
1643 $objectname = isset($value['objectname']) ? $value['objectname'] : '';
1644 $methodename = isset($value['method']) ? $value['method'] : '';
1645 $command = isset($value['command']) ? $value['command'] : '';
1646 $params = isset($value['parameters']) ? $value['parameters'] : '';
1647 $md5params = isset($value['md5params']) ? $value['md5params'] : '';
1648 $comment = isset($value['comment']) ? $value['comment'] : '';
1649 $frequency = isset($value['frequency']) ? $value['frequency'] : '';
1650 $unitfrequency = isset($value['unitfrequency']) ? $value['unitfrequency'] : '';
1651 $priority = isset($value['priority']) ? $value['priority'] : '';
1652 $datestart = isset($value['datestart']) ? $value['datestart'] : '';
1653 $dateend = isset($value['dateend']) ? $value['dateend'] : '';
1654 $datenextrun = isset($value['datenextrun']) ? $value['datenextrun'] : $now;
1655 $status = isset($value['status']) ? $value['status'] : '';
1656 $maxrun = isset($value['maxrun']) ? $value['maxrun'] : 0;
1657 $libname = isset($value['libname']) ? $value['libname'] : '';
1658 $test = isset($value['test']) ? $value['test'] : ''; // Line must be enabled or not (so visible or not)
1659
1660 // Search if cron entry already present
1661 $sql = "SELECT count(*) as nb FROM " . MAIN_DB_PREFIX . "cronjob";
1662 //$sql .= " WHERE module_name = '" . $this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class) . "'";
1663 $sql .= " WHERE label = '".$this->db->escape($label)."'";
1664 /* unique key is on label,entity so no need for this test
1665 if ($classesname) {
1666 $sql .= " AND classesname = '" . $this->db->escape($classesname) . "'";
1667 }
1668 if ($objectname) {
1669 $sql .= " AND objectname = '" . $this->db->escape($objectname) . "'";
1670 }
1671 if ($methodename) {
1672 $sql .= " AND methodename = '" . $this->db->escape($methodename) . "'";
1673 }
1674 if ($command) {
1675 $sql .= " AND command = '" . $this->db->escape($command) . "'";
1676 }
1677 if ($params) {
1678 $sql .= " AND params = '" . $this->db->escape($params) . "'";
1679 }
1680 */
1681 $sql .= " AND entity = " . ((int) $entity); // Must be exact entity
1682
1683 $result = $this->db->query($sql);
1684 if (!$result) {
1685 $this->error = $this->db->lasterror();
1686 $err++;
1687 break;
1688 // else box already registered into database
1689 }
1690
1691 $obj = $this->db->fetch_object($result);
1692 if ($obj->nb > 0) {
1693 continue;
1694 }
1695
1696 $cronjob = new Cronjob($this->db);
1697
1698 $cronjob->entity = $entity;
1699 $cronjob->label = $label;
1700 $cronjob->jobtype = $jobtype;
1701 $cronjob->classesname = $classesname;
1702 $cronjob->objectname = $objectname;
1703 $cronjob->methodename = $methodename;
1704 $cronjob->command = $command;
1705 $cronjob->params = $params;
1706 $cronjob->md5params = $md5params;
1707 $cronjob->note_private = $comment;
1708 $cronjob->frequency = $frequency;
1709 $cronjob->unitfrequency = $unitfrequency;
1710 $cronjob->priority = $priority;
1711 $cronjob->datestart = $datestart;
1712 $cronjob->dateend = $dateend;
1713 $cronjob->datenextrun = $datenextrun;
1714 $cronjob->maxrun = $maxrun;
1715 $cronjob->status = $status;
1716 $cronjob->test = $test;
1717 $cronjob->libname = $libname;
1718 $cronjob->module_name = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
1719
1720 $retCreate = $cronjob->create($user);
1721
1722 if ($retCreate < 0) {
1723 $this->error = implode("\n", array_merge([$cronjob->error], $cronjob->errors));
1724 return -1;
1725 }
1726 }
1727 }
1728
1729 return $err;
1730 }
1731
1732 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1738 public function delete_cronjobs()
1739 {
1740 // phpcs:enable
1741 global $conf;
1742
1743 $err = 0;
1744
1745 if (is_array($this->cronjobs)) {
1746 $sql = "DELETE FROM ".MAIN_DB_PREFIX."cronjob";
1747 $sql .= " WHERE module_name = '".$this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class)."'";
1748 $sql .= " AND entity = ".$conf->entity;
1749 $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.
1750 // 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.
1751
1752 dol_syslog(get_class($this)."::delete_cronjobs", LOG_DEBUG);
1753 $resql = $this->db->query($sql);
1754 if (!$resql) {
1755 $this->error = $this->db->lasterror();
1756 $err++;
1757 }
1758 }
1759
1760 return $err;
1761 }
1762
1763 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1769 public function delete_tabs()
1770 {
1771 // phpcs:enable
1772 global $conf;
1773
1774 $err = 0;
1775
1776 $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
1777 $sql .= " WHERE ".$this->db->decrypt('name')." LIKE '".$this->db->escape($this->const_name)."_TABS_%'";
1778 $sql .= " AND entity = ".((int) $conf->entity);
1779
1780 dol_syslog(get_class($this)."::delete_tabs", LOG_DEBUG);
1781 if (!$this->db->query($sql)) {
1782 $this->error = $this->db->lasterror();
1783 $err++;
1784 }
1785
1786 return $err;
1787 }
1788
1789 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1795 public function insert_tabs()
1796 {
1797 // phpcs:enable
1798 global $conf;
1799
1800 $err = 0;
1801
1802 if (!empty($this->tabs)) {
1803 dol_syslog(get_class($this)."::insert_tabs", LOG_DEBUG);
1804
1805 $i = 0;
1806 foreach ($this->tabs as $key => $value) {
1807 if (is_array($value) && count($value) == 0) {
1808 continue; // Discard empty arrays
1809 }
1810
1811 $entity = $conf->entity;
1812 $newvalue = $value;
1813
1814 if (is_array($value)) {
1815 $newvalue = $value['data'];
1816 if (isset($value['entity'])) {
1817 $entity = $value['entity'];
1818 }
1819 }
1820
1821 if ($newvalue) {
1822 $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (";
1823 $sql .= "name";
1824 $sql .= ", type";
1825 $sql .= ", value";
1826 $sql .= ", note";
1827 $sql .= ", visible";
1828 $sql .= ", entity";
1829 $sql .= ")";
1830 $sql .= " VALUES (";
1831 $sql .= $this->db->encrypt($this->const_name."_TABS_".$i);
1832 $sql .= ", 'chaine'";
1833 $sql .= ", ".$this->db->encrypt($newvalue);
1834 $sql .= ", null";
1835 $sql .= ", '0'";
1836 $sql .= ", ".((int) $entity);
1837 $sql .= ")";
1838
1839 $resql = $this->db->query($sql);
1840 if (!$resql) {
1841 dol_syslog($this->db->lasterror(), LOG_ERR);
1842 if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1843 $this->error = $this->db->lasterror();
1844 $this->errors[] = $this->db->lasterror();
1845 $err++;
1846 break;
1847 }
1848 }
1849 }
1850 $i++;
1851 }
1852 }
1853 return $err;
1854 }
1855
1856 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1862 public function insert_const()
1863 {
1864 // phpcs:enable
1865 global $conf;
1866
1867 $err = 0;
1868
1869 if (empty($this->const)) {
1870 return 0;
1871 }
1872
1873 dol_syslog(__METHOD__, LOG_DEBUG);
1874
1875 foreach ($this->const as $key => $value) {
1876 // Property to search if entry exists
1877 $name = $this->const[$key][0];
1878 $entity = (!empty($this->const[$key][5]) && $this->const[$key][5] != 'current') ? 0 : $conf->entity;
1879
1880 // Property to update if it does not exists
1881 $type = $this->const[$key][1];
1882 $val = $this->const[$key][2];
1883 $note = isset($this->const[$key][3]) ? $this->const[$key][3] : '';
1884 $visible = isset($this->const[$key][4]) ? $this->const[$key][4] : 0;
1885
1886 // Clean
1887 if (empty($visible)) {
1888 $visible = '0';
1889 }
1890 if (empty($val) && $val != '0') {
1891 $val = '';
1892 }
1893
1894 if (!empty($name)) {
1895 $sql = "SELECT count(*) as nb";
1896 $sql .= " FROM ".MAIN_DB_PREFIX."const";
1897 $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($name)."'";
1898 $sql .= " AND entity = ".((int) $entity);
1899
1900 $result = $this->db->query($sql);
1901 if ($result) {
1902 $row = $this->db->fetch_row($result);
1903
1904 if ($row[0] == 0) { // If not found
1905 $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (name, type, value, note, visible, entity)";
1906 $sql .= " VALUES (";
1907 $sql .= $this->db->encrypt($name);
1908 $sql .= ", '".$this->db->escape($type)."'";
1909 $sql .= ", ".(($val != '') ? $this->db->encrypt($val) : "''");
1910 $sql .= ", ".($note ? "'".$this->db->escape($note)."'" : "null");
1911 $sql .= ", '".$this->db->escape($visible)."'";
1912 $sql .= ", ".((int) $entity);
1913 $sql .= ")";
1914
1915 if (!$this->db->query($sql)) {
1916 $err++;
1917 } else {
1918 // Set also the variable in running environment
1919 $conf->global->$name = $val;
1920 }
1921 } else {
1922 dol_syslog(__METHOD__." constant '".$name."' already exists", LOG_DEBUG);
1923 }
1924 } else {
1925 $err++;
1926 }
1927 }
1928 }
1929
1930 return $err;
1931 }
1932
1933 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1939 public function delete_const()
1940 {
1941 // phpcs:enable
1942 global $conf;
1943
1944 $err = 0;
1945
1946 if (empty($this->const)) {
1947 return 0;
1948 }
1949
1950 foreach ($this->const as $key => $value) {
1951 $name = $this->const[$key][0];
1952 $deleteonunactive = (!empty($this->const[$key][6])) ? 1 : 0;
1953
1954 if ($deleteonunactive) {
1955 $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
1956 $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($name)."'";
1957 $sql .= " AND entity in (0, ".$conf->entity.")";
1958 dol_syslog(get_class($this)."::delete_const", LOG_DEBUG);
1959 if (!$this->db->query($sql)) {
1960 $this->error = $this->db->lasterror();
1961 $err++;
1962 }
1963 }
1964 }
1965
1966 return $err;
1967 }
1968
1969 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1978 public function insert_permissions($reinitadminperms = 0, $force_entity = null, $notrigger = 0)
1979 {
1980 // phpcs:enable
1981 global $conf, $user;
1982
1983 $err = 0;
1984 $entity = (!empty($force_entity) ? $force_entity : $conf->entity);
1985
1986 dol_syslog(get_class($this)."::insert_permissions", LOG_DEBUG);
1987
1988 // Test if module is activated
1989 $sql_del = "SELECT ".$this->db->decrypt('value')." as value";
1990 $sql_del .= " FROM ".MAIN_DB_PREFIX."const";
1991 $sql_del .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
1992 $sql_del .= " AND entity IN (0,".((int) $entity).")";
1993
1994 $resql = $this->db->query($sql_del);
1995
1996 if ($resql) {
1997 $obj = $this->db->fetch_object($resql);
1998
1999 if ($obj !== null && !empty($obj->value) && !empty($this->rights)) {
2000 include_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
2001
2002 // If the module is active
2003 foreach ($this->rights as $key => $value) {
2004 $r_id = $this->rights[$key][self::KEY_ID]; // permission id in llx_rights_def (not unique because primary key is couple id-entity)
2005 $r_label = $this->rights[$key][self::KEY_LABEL];
2006 $r_type = $this->rights[$key][self::KEY_TYPE] ?? 'w'; // TODO deprecated
2007 $r_default = $this->rights[$key][self::KEY_DEFAULT] ?? 0;
2008 $r_perms = $this->rights[$key][self::KEY_FIRST_LEVEL] ?? '';
2009 $r_subperms = $this->rights[$key][self::KEY_SECOND_LEVEL] ?? '';
2010
2011 $r_module_position = $this->getModulePosition();
2012 $r_family = $this->family;
2013 $r_family_position = 0;
2014
2015 // KEY_FIRST_LEVEL (perms) must not be empty
2016 if (empty($r_perms)) {
2017 continue;
2018 }
2019
2020 // name of module (default: current module name)
2021 $r_module = (empty($this->rights_class) ? strtolower($this->name) : $this->rights_class);
2022
2023 // name of the module from which the right comes (default: empty means same module the permission is for)
2024 $r_module_origin = '';
2025
2026 if (isset($this->rights[$key][self::KEY_MODULE])) {
2027 // name of the module to which the right must be applied
2028 $r_module = $this->rights[$key][self::KEY_MODULE];
2029 // name of the module from which the right comes
2030 $r_module_origin = (empty($this->rights_class) ? strtolower($this->name) : $this->rights_class);
2031 }
2032
2033 // condition to show or hide a user right (default: 1) (eg isModEnabled('anothermodule') or ($conf->global->MAIN_FEATURES_LEVEL > 0) or etc..)
2034 $r_enabled = $this->rights[$key][self::KEY_ENABLED] ?? '1';
2035
2036 // Search if perm already present
2037 $sql = "SELECT count(*) as nb FROM ".MAIN_DB_PREFIX."rights_def";
2038 $sql .= " WHERE entity = ".((int) $entity);
2039 $sql .= " AND id = ".((int) $r_id);
2040
2041 $resqlselect = $this->db->query($sql);
2042 if ($resqlselect) {
2043 $objcount = $this->db->fetch_object($resqlselect);
2044 if ($objcount && $objcount->nb == 0) {
2045 $sql = "INSERT INTO ".MAIN_DB_PREFIX."rights_def (";
2046 $sql .= "id";
2047 $sql .= ", entity";
2048 $sql .= ", libelle";
2049 $sql .= ", module";
2050 $sql .= ", module_origin";
2051 $sql .= ", module_position"; // Not that module_position can be fixed eynamically when accessing page user/perms.php
2052 $sql .= ", family";
2053 $sql .= ", family_position";
2054 $sql .= ", type"; // Not used yet
2055 $sql .= ", bydefault";
2056 $sql .= ", perms";
2057 $sql .= ", subperms";
2058 $sql .= ", enabled";
2059 $sql .= ") VALUES (";
2060 $sql .= ((int) $r_id);
2061 $sql .= ", ".((int) $entity);
2062 $sql .= ", '".$this->db->escape($r_label)."'";
2063 $sql .= ", '".$this->db->escape($r_module)."'";
2064 $sql .= ", '".$this->db->escape($r_module_origin)."'";
2065 $sql .= ", '".$this->db->escape((string) $r_module_position)."'";
2066 $sql .= ", '".$this->db->escape($r_family)."'";
2067 $sql .= ", '".$this->db->escape((string) $r_family_position)."'";
2068 $sql .= ", '".$this->db->escape($r_type)."'"; // Not used yet
2069 $sql .= ", ".((int) $r_default);
2070 $sql .= ", '".$this->db->escape($r_perms)."'";
2071 $sql .= ", '".$this->db->escape($r_subperms)."'";
2072 $sql .= ", '".$this->db->escape($r_enabled)."'";
2073 $sql .= ")";
2074
2075 $resqlinsert = $this->db->query($sql, 1);
2076
2077 if (!$resqlinsert) {
2078 if ($this->db->errno() != "DB_ERROR_RECORD_ALREADY_EXISTS") {
2079 $this->error = $this->db->lasterror();
2080 $err++;
2081 break;
2082 } else {
2083 dol_syslog(get_class($this)."::insert_permissions record already exists", LOG_INFO);
2084 }
2085 }
2086
2087 $this->db->free($resqlinsert);
2088 }
2089
2090 $this->db->free($resqlselect);
2091 }
2092
2093 // If we want to init permissions on admin users
2094 if (!empty($reinitadminperms)) {
2095 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."user WHERE admin = 1";
2096 dol_syslog(get_class($this)."::insert_permissions Search all admin users", LOG_DEBUG);
2097
2098 $resqlseladmin = $this->db->query($sql, 1);
2099
2100 if ($resqlseladmin) {
2101 $num = $this->db->num_rows($resqlseladmin);
2102 $i = 0;
2103 while ($i < $num) {
2104 $obj2 = $this->db->fetch_object($resqlseladmin);
2105 dol_syslog(get_class($this)."::insert_permissions Add permission id ".$r_id." to user id=".$obj2->rowid);
2106
2107 $tmpuser = new User($this->db);
2108 $result = $tmpuser->fetch($obj2->rowid);
2109 if ($result > 0) {
2110 $tmpuser->addrights($r_id, '', '', 0, 1);
2111 } else {
2112 dol_syslog(get_class($this)."::insert_permissions Failed to add the permission to user because fetch return an error", LOG_ERR);
2113 }
2114 $i++;
2115 }
2116 } else {
2117 dol_print_error($this->db);
2118 }
2119 }
2120 }
2121
2122 if (!empty($reinitadminperms) && !empty($user->admin)) { // Reload permission for current user if defined
2123 // We reload permissions
2124 $user->clearrights();
2125 $user->loadRights();
2126 }
2127 }
2128 $this->db->free($resql);
2129 } else {
2130 $this->error = $this->db->lasterror();
2131 $err++;
2132 }
2133
2134 return $err;
2135 }
2136
2137
2138 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2144 public function delete_permissions()
2145 {
2146 // phpcs:enable
2147 global $conf;
2148
2149 $err = 0;
2150
2151 $module = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
2152
2153 $sql = "DELETE FROM ".MAIN_DB_PREFIX."rights_def";
2154 $sql .= " WHERE (module = '".$this->db->escape($module)."' OR module_origin = '".$this->db->escape($module)."')";
2155
2156 // Delete all entities if core module
2157 if (empty($this->core_enabled)) {
2158 $sql .= " AND entity = ".((int) $conf->entity);
2159 }
2160
2161 dol_syslog(get_class($this)."::delete_permissions", LOG_DEBUG);
2162 if (!$this->db->query($sql)) {
2163 $this->error = $this->db->lasterror();
2164 $err++;
2165 }
2166
2167 return $err;
2168 }
2169
2170
2171 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2177 public function insert_menus()
2178 {
2179 // phpcs:enable
2180 global $conf, $user;
2181
2182 if (!is_array($this->menu) || empty($this->menu)) {
2183 return 0;
2184 }
2185
2186 include_once DOL_DOCUMENT_ROOT.'/core/class/menubase.class.php';
2187
2188 dol_syslog(get_class($this)."::insert_menus", LOG_DEBUG);
2189
2190 $err = 0;
2191
2192 // Common module
2193 $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
2194
2195 $this->db->begin();
2196
2197 foreach ($this->menu as $key => $value) {
2198 $menu = new Menubase($this->db);
2199 $menu->menu_handler = 'all';
2200
2201 //$menu->module=strtolower($this->name); TODO When right_class will be same than module name
2202 $menu->module = (empty($this->rights_class) ? strtolower($this->name) : $this->rights_class);
2203
2204 if (!$this->menu[$key]['fk_menu']) {
2205 $menu->fk_menu = 0;
2206 } else {
2207 $foundparent = 0;
2208 $fk_parent = $this->menu[$key]['fk_menu'];
2209 $reg = array();
2210 if (preg_match('/^r=/', $fk_parent)) { // old deprecated method
2211 $fk_parent = str_replace('r=', '', $fk_parent);
2212 if (isset($this->menu[$fk_parent]['rowid'])) {
2213 $menu->fk_menu = $this->menu[$fk_parent]['rowid'];
2214 $foundparent = 1;
2215 }
2216 } elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+),fk_leftmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
2217 $menu->fk_menu = -1;
2218 $menu->fk_mainmenu = $reg[1];
2219 $menu->fk_leftmenu = $reg[2];
2220 $foundparent = 1;
2221 } elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
2222 $menu->fk_menu = -1;
2223 $menu->fk_mainmenu = $reg[1];
2224 $menu->fk_leftmenu = '';
2225 $foundparent = 1;
2226 }
2227 if (!$foundparent) {
2228 $this->error = "ErrorBadDefinitionOfMenuArrayInModuleDescriptor";
2229 dol_syslog(get_class($this)."::insert_menus ".$this->error." ".$this->menu[$key]['fk_menu'], LOG_ERR);
2230 $err++;
2231 }
2232 }
2233 $menu->type = $this->menu[$key]['type'];
2234 $menu->mainmenu = isset($this->menu[$key]['mainmenu']) ? $this->menu[$key]['mainmenu'] : (isset($menu->fk_mainmenu) ? $menu->fk_mainmenu : '');
2235 $menu->leftmenu = isset($this->menu[$key]['leftmenu']) ? $this->menu[$key]['leftmenu'] : '';
2236 $menu->title = $this->menu[$key]['titre'];
2237 $menu->prefix = isset($this->menu[$key]['prefix']) ? $this->menu[$key]['prefix'] : '';
2238 $menu->url = $this->menu[$key]['url'];
2239 $menu->langs = isset($this->menu[$key]['langs']) ? $this->menu[$key]['langs'] : '';
2240 $menu->position = $this->menu[$key]['position'];
2241 $menu->perms = $this->menu[$key]['perms'];
2242 $menu->target = isset($this->menu[$key]['target']) ? $this->menu[$key]['target'] : '';
2243 $menu->user = $this->menu[$key]['user'];
2244 $menu->enabled = isset($this->menu[$key]['enabled']) ? $this->menu[$key]['enabled'] : 0;
2245 $menu->position = $this->menu[$key]['position'];
2246 $menu->entity = $entity;
2247
2248 if (!$err) {
2249 $result = $menu->create($user); // Save menu entry into table llx_menu
2250 if ($result > 0) {
2251 $this->menu[$key]['rowid'] = $result;
2252 } else {
2253 $this->error = $menu->error;
2254 dol_syslog(get_class($this).'::insert_menus result='.$result." ".$this->error, LOG_ERR);
2255 $err++;
2256 break;
2257 }
2258 }
2259 }
2260
2261 if (!$err) {
2262 $this->db->commit();
2263 } else {
2264 dol_syslog(get_class($this)."::insert_menus ".$this->error, LOG_ERR);
2265 $this->db->rollback();
2266 }
2267
2268 return $err;
2269 }
2270
2271
2272 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2278 public function delete_menus()
2279 {
2280 // phpcs:enable
2281 global $conf;
2282
2283 $err = 0;
2284
2285 //$module=strtolower($this->name); TODO When right_class will be same than module name
2286 $module = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
2287
2288 $sql = "DELETE FROM ".MAIN_DB_PREFIX."menu";
2289 $sql .= " WHERE module = '".$this->db->escape($module)."'";
2290 $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'
2291 $sql .= " AND entity IN (0, ".$conf->entity.")";
2292
2293 dol_syslog(get_class($this)."::delete_menus", LOG_DEBUG);
2294 $resql = $this->db->query($sql);
2295 if (!$resql) {
2296 $this->error = $this->db->lasterror();
2297 $err++;
2298 }
2299
2300 return $err;
2301 }
2302
2303 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2309 public function create_dirs()
2310 {
2311 // phpcs:enable
2312 global $langs, $conf;
2313
2314 $err = 0;
2315 $name = '';
2316
2317 if (isset($this->dirs) && is_array($this->dirs)) {
2318 foreach ($this->dirs as $key => $value) {
2319 $addtodatabase = 0;
2320
2321 if (!is_array($value)) {
2322 $dir = $value; // Default simple mode
2323 } else {
2324 $constname = $this->const_name."_DIR_";
2325 $dir = $this->dirs[$key][1];
2326 $addtodatabase = empty($this->dirs[$key][2]) ? '' : $this->dirs[$key][2]; // Create constante in llx_const
2327 $subname = empty($this->dirs[$key][3]) ? '' : strtoupper($this->dirs[$key][3]); // Add submodule name (ex: $conf->module->submodule->dir_output)
2328 $forcename = empty($this->dirs[$key][4]) ? '' : strtoupper($this->dirs[$key][4]); // Change the module name if different
2329
2330 if (!empty($forcename)) {
2331 $constname = 'MAIN_MODULE_'.$forcename."_DIR_";
2332 }
2333 if (!empty($subname)) {
2334 $constname = $constname.$subname."_";
2335 }
2336
2337 $name = $constname.strtoupper($this->dirs[$key][0]);
2338 }
2339
2340 // Define directory full path ($dir must start with "/")
2341 if (!getDolGlobalString('MAIN_MODULE_MULTICOMPANY') || $conf->entity == 1) {
2342 $fulldir = DOL_DATA_ROOT.$dir;
2343 } else {
2344 $fulldir = DOL_DATA_ROOT."/".$conf->entity.$dir;
2345 }
2346 // Create dir if it does not exists
2347 if (!empty($fulldir) && !file_exists($fulldir)) {
2348 if (dol_mkdir($fulldir, DOL_DATA_ROOT) < 0) {
2349 $this->error = $langs->trans("ErrorCanNotCreateDir", $fulldir);
2350 dol_syslog(get_class($this)."::_init ".$this->error, LOG_ERR);
2351 $err++;
2352 }
2353 }
2354
2355 // Define the constant in database if requested (not the default mode)
2356 if (!empty($addtodatabase) && !empty($name)) {
2357 $result = $this->insert_dirs($name, $dir);
2358 if ($result) {
2359 $err++;
2360 }
2361 }
2362 }
2363 }
2364
2365 return $err;
2366 }
2367
2368
2369 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2378 public function insert_dirs($name, $dir)
2379 {
2380 // phpcs:enable
2381 global $conf;
2382
2383 $err = 0;
2384
2385 $sql = "SELECT count(*)";
2386 $sql .= " FROM ".MAIN_DB_PREFIX."const";
2387 $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($name)."'";
2388 $sql .= " AND entity = ".$conf->entity;
2389
2390 dol_syslog(get_class($this)."::insert_dirs", LOG_DEBUG);
2391 $result = $this->db->query($sql);
2392 if ($result) {
2393 $row = $this->db->fetch_row($result);
2394
2395 if ($row[0] == 0) {
2396 $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (name, type, value, note, visible, entity)";
2397 $sql .= " VALUES (".$this->db->encrypt($name).", 'chaine', ".$this->db->encrypt($dir).", '".$this->db->escape("Directory for module ".$this->name)."', '0', ".((int) $conf->entity).")";
2398
2399 dol_syslog(get_class($this)."::insert_dirs", LOG_DEBUG);
2400 $this->db->query($sql);
2401 }
2402 } else {
2403 $this->error = $this->db->lasterror();
2404 $err++;
2405 }
2406
2407 return $err;
2408 }
2409
2410
2411 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2417 public function delete_dirs()
2418 {
2419 // phpcs:enable
2420 global $conf;
2421
2422 $err = 0;
2423
2424 $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
2425 $sql .= " WHERE ".$this->db->decrypt('name')." LIKE '".$this->db->escape($this->const_name)."_DIR_%'";
2426 $sql .= " AND entity = ".$conf->entity;
2427
2428 dol_syslog(get_class($this)."::delete_dirs", LOG_DEBUG);
2429 if (!$this->db->query($sql)) {
2430 $this->error = $this->db->lasterror();
2431 $err++;
2432 }
2433
2434 return $err;
2435 }
2436
2437 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2444 public function insert_module_parts()
2445 {
2446 // phpcs:enable
2447 global $conf, $langs;
2448
2449 $error = 0;
2450
2451 if (is_array($this->module_parts)) {
2452 if (empty($this->module_parts['icon']) && !empty($this->picto) && preg_match('/^fa\-/', $this->picto)) {
2453 $this->module_parts['icon'] = $this->picto;
2454 }
2455
2456 foreach ($this->module_parts as $key => $value) {
2457 if (is_array($value) && count($value) == 0) {
2458 continue; // Discard empty arrays
2459 }
2460
2461 // If module brings website templates, we must generate the zip like we do whenenabling the website module
2462 if ($key == 'websitetemplates' && $value == 1) {
2463 $srcroot = dol_buildpath('/'.strtolower($this->name).'/doctemplates/websites');
2464
2465 // Copy templates in dir format (recommended) into zip file
2466 $docs = dol_dir_list($srcroot, 'directories', 0, 'website_.*$');
2467 foreach ($docs as $cursorfile) {
2468 $src = $srcroot.'/'.$cursorfile['name'];
2469 $dest = DOL_DATA_ROOT.'/doctemplates/websites/'.$cursorfile['name'];
2470
2471 dol_delete_file($dest.'.zip');
2472
2473 // Compress it
2474 global $errormsg; // Used by dol_compress_dir
2475 $errormsg = '';
2476 $result = dol_compress_dir($src, $dest.'.zip', 'zip');
2477 if ($result < 0) {
2478 $error++;
2479 $this->error = ($errormsg ? $errormsg : $langs->trans('ErrorFailToCreateZip', $dest));
2480 $this->errors[] = ($errormsg ? $errormsg : $langs->trans('ErrorFailToCreateZip', $dest));
2481 }
2482 }
2483
2484 // Copy also the preview website_xxx.jpg file
2485 $docs = dol_dir_list($srcroot, 'files', 0, 'website_.*\.jpg$');
2486 foreach ($docs as $cursorfile) {
2487 $src = $srcroot.'/'.$cursorfile['name'];
2488 $dest = DOL_DATA_ROOT.'/doctemplates/websites/'.$cursorfile['name'];
2489
2490 dol_copy($src, $dest);
2491 }
2492 }
2493
2494 $entity = $conf->entity; // Reset the current entity
2495 $newvalue = $value;
2496
2497 // Serialize array parameters
2498 if (is_array($value)) {
2499 // Can defined other parameters
2500 // Example when $key='hooks', then $value is an array('data'=>array('hookcontext1','hookcontext2'), 'entity'=>X)
2501 if (isset($value['data']) && is_array($value['data'])) {
2502 $newvalue = json_encode($value['data']);
2503 if (isset($value['entity'])) {
2504 $entity = $value['entity'];
2505 }
2506 } elseif (isset($value['data']) && !is_array($value['data'])) {
2507 $newvalue = $value['data'];
2508 if (isset($value['entity'])) {
2509 $entity = $value['entity'];
2510 }
2511 } else { // when hook is declared with syntax 'hook'=>array('hookcontext1','hookcontext2',...)
2512 $newvalue = json_encode($value);
2513 }
2514 }
2515
2516 if (!empty($newvalue)) {
2517 $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (";
2518 $sql .= "name";
2519 $sql .= ", type";
2520 $sql .= ", value";
2521 $sql .= ", note";
2522 $sql .= ", visible";
2523 $sql .= ", entity";
2524 $sql .= ")";
2525 $sql .= " VALUES (";
2526 $sql .= " ".$this->db->encrypt($this->const_name."_".strtoupper($key), 1);
2527 $sql .= ", 'chaine'";
2528 $sql .= ", ".$this->db->encrypt($newvalue, 1);
2529 $sql .= ", null";
2530 $sql .= ", '0'";
2531 $sql .= ", ".((int) $entity);
2532 $sql .= ")";
2533
2534 dol_syslog(get_class($this)."::insert_module_parts for key=".$this->const_name."_".strtoupper($key), LOG_DEBUG);
2535
2536 $resql = $this->db->query($sql, 1);
2537 if (!$resql) {
2538 if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2539 $error++;
2540 $this->error = $this->db->lasterror();
2541 } else {
2542 dol_syslog(get_class($this)."::insert_module_parts for ".$this->const_name."_".strtoupper($key)." Record already exists.", LOG_WARNING);
2543 }
2544 }
2545 }
2546 }
2547 }
2548 return $error;
2549 }
2550
2551 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2557 public function delete_module_parts()
2558 {
2559 // phpcs:enable
2560 global $conf;
2561
2562 $err = 0;
2563
2564 if (is_array($this->module_parts)) {
2565 dol_syslog(get_class($this)."::delete_module_parts", LOG_DEBUG);
2566
2567 if (empty($this->module_parts['icon']) && !empty($this->picto) && preg_match('/^fa\-/', $this->picto)) {
2568 $this->module_parts['icon'] = $this->picto;
2569 }
2570
2571 foreach ($this->module_parts as $key => $value) {
2572 // If entity is defined
2573 if (is_array($value) && isset($value['entity'])) {
2574 $entity = $value['entity'];
2575 } else {
2576 $entity = $conf->entity;
2577 }
2578
2579 $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
2580 $sql .= " WHERE ".$this->db->decrypt('name')." LIKE '".$this->db->escape($this->const_name)."_".strtoupper($key)."'";
2581 $sql .= " AND entity = ".((int) $entity);
2582
2583 if (!$this->db->query($sql)) {
2584 $this->error = $this->db->lasterror();
2585 $err++;
2586 }
2587 }
2588 }
2589 return $err;
2590 }
2591
2601 public function init($options = '')
2602 {
2603 return $this->_init(array(), $options);
2604 }
2605
2614 public function remove($options = '')
2615 {
2616 return $this->_remove(array(), $options);
2617 }
2618
2619
2627 public function getKanbanView($codeenabledisable = '', $codetoconfig = '')
2628 {
2629 global $langs;
2630
2631 // Define imginfo
2632 $imginfo = "info";
2633 if ($this->isCoreOrExternalModule() == 'external') {
2634 $imginfo = "info_black";
2635 }
2636
2637 $const_name = 'MAIN_MODULE_'.strtoupper(preg_replace('/^mod/i', '', get_class($this)));
2638
2639 $version = $this->getVersion(0);
2640 $versiontrans = '';
2641 if (preg_match('/development/i', $version)) {
2642 $versiontrans .= 'warning';
2643 }
2644 if (preg_match('/experimental/i', $version)) {
2645 $versiontrans .= 'warning';
2646 }
2647 if (preg_match('/deprecated/i', $version)) {
2648 $versiontrans .= 'warning';
2649 }
2650
2651 $return = '
2652 <div class="box-flex-item info-box-module'
2653 .(getDolGlobalString($const_name) ? '' : ' --disabled')
2654 .($this->isCoreOrExternalModule() == 'external' ? ' --external' : '')
2655 .($this->needUpdate ? ' --need-update' : '')
2656 .'">
2657 <div class="info-box info-box-sm info-box-module">
2658 <div class="info-box-icon'.(!getDolGlobalString($const_name) ? '' : ' info-box-icon-module-enabled'.($versiontrans ? ' info-box-icon-module-warning' : '')).'">';
2659
2660 $alttext = '';
2661 //if (is_array($objMod->need_dolibarr_version)) $alttext.=($alttext?' - ':'').'Dolibarr >= '.join('.',$objMod->need_dolibarr_version);
2662 //if (is_array($objMod->phpmin)) $alttext.=($alttext?' - ':'').'PHP >= '.join('.',$objMod->phpmin);
2663 if (!empty($this->picto)) {
2664 if (preg_match('/^\//i', $this->picto)) {
2665 $return .= img_picto($alttext, $this->picto, 'class="inline-block valignmiddle"', 1);
2666 } else {
2667 $return .= img_object($alttext, $this->picto, 'class="inline-block valignmiddle"');
2668 }
2669 } else {
2670 $return .= img_object($alttext, 'generic', 'class="inline-block valignmiddle"');
2671 }
2672
2673 if ($this->isCoreOrExternalModule() == 'external' || preg_match('/development|experimental|deprecated/i', $version)) {
2674 $versionTitle = $langs->trans("Version").' '.$this->getVersion(1);
2675 if ($this->needUpdate) {
2676 $versionTitle .= '<br>'.$langs->trans('ModuleUpdateAvailable').' : '.$this->lastVersion;
2677 }
2678
2679 $return .= '<span class="info-box-icon-version'.($versiontrans ? ' '.$versiontrans : '').' classfortooltip" title="'.dol_escape_js($versionTitle).'" >';
2680 $return .= $this->getVersion(1);
2681 $return .= '</span>';
2682 }
2683
2684 $return .= '</div>
2685 <div class="info-box-content info-box-text-module'.(!getDolGlobalString($const_name) ? '' : ' info-box-module-enabled'.($versiontrans ? ' info-box-content-warning' : '')).'">
2686 <span class="info-box-title noopacity">'.$this->getName().'</span>
2687 <span class="info-box-desc twolinesmax opacitymedium" title="'.dol_escape_htmltag($this->getDesc()).'">'.nl2br($this->getDesc()).'</span>';
2688
2689 $return .= '<div class="valignmiddle inline-block info-box-more">';
2690 //if ($versiontrans) print img_warning($langs->trans("Version").' '.$this->getVersion(1)).' ';
2691 $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>';
2692 $return .= '</div><br>';
2693
2694 $return .= '<div class="valignmiddle inline-block info-box-actions">';
2695 $return .= '<div class="valignmiddle inline-block info-box-setup">';
2696 $return .= $codetoconfig;
2697 $return .= '</div>';
2698 $return .= '<div class="valignmiddle inline-block marginleftonly marginrightonly">';
2699 $return .= $codeenabledisable;
2700 $return .= '</div>';
2701 $return .= '</div>';
2702
2703 $return .= '
2704 </div><!-- /.info-box-content -->
2705 </div><!-- /.info-box -->
2706 </div>';
2707
2708 return $return;
2709 }
2710
2719 public function checkForUpdate()
2720 {
2721 require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
2722 if (!empty($this->url_last_version)) {
2723 $lastVersion = getURLContent($this->url_last_version, 'GET', '', 1, array(), array('http', 'https'), 0); // Accept http or https links on external remote server only
2724 if (isset($lastVersion['content']) && strlen($lastVersion['content']) < 30) {
2725 // 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 _ . -
2726 $this->lastVersion = preg_replace("/[^a-zA-Z0-9_\.\-]+/", "", $lastVersion['content']);
2727 if (version_compare($this->lastVersion, $this->version) > 0) {
2728 $this->needUpdate = true;
2729 return 1;
2730 } else {
2731 $this->needUpdate = false;
2732 return 0;
2733 }
2734 } else {
2735 return -1;
2736 }
2737 }
2738 return 0;
2739 }
2740
2748 public function checkForCompliance($nametocheck = '')
2749 {
2750 global $conf, $langs;
2751
2752 if (empty($nametocheck)) {
2753 $nametocheck = $this->name;
2754 }
2755
2756 // Get list of illegal modules name or ID
2757 if (empty($conf->cache['noncompliantmodules'])) {
2758 require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
2759
2760 $result = getURLContent(self::URL_FOR_BLACKLISTED_MODULES, 'GET', '', 1, array(), array('http', 'https'), 0); // Accept http or https links on external remote server only
2761 if (isset($result['content']) && $result['http_code'] == 200) {
2762 $langs->load("errors");
2763
2764 // 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 _ . -
2765 $arrayoflines = preg_split("/[\n,]/", $result['content']);
2766 foreach ($arrayoflines as $line) {
2767 $tmpfieldsofline = explode(';', $line);
2768 $modulekey = strtolower(trim($tmpfieldsofline[0]));
2769 if (empty($modulekey)) {
2770 continue;
2771 }
2772 $conf->cache['noncompliantmodules'][$modulekey]['name'] = trim($tmpfieldsofline[0]);
2773 $conf->cache['noncompliantmodules'][$modulekey]['id'] = (isset($tmpfieldsofline[1]) ? trim($tmpfieldsofline[1]) : '');
2774 $conf->cache['noncompliantmodules'][$modulekey]['signature'] = (isset($tmpfieldsofline[2]) ? trim($tmpfieldsofline[2]) : '');
2775 $conf->cache['noncompliantmodules'][$modulekey]['message'] = $langs->trans(empty(isset($tmpfieldsofline[3]) ? trim($tmpfieldsofline[3]) : '') ? 'WarningModuleAffiliatedToAReportedCompany' : trim($tmpfieldsofline[3]));
2776 if (!empty($tmpfieldsofline[4])) {
2777 $message2 = $langs->trans("WarningModuleAffiliatedToAPiratPlatform", '{s}');
2778 $listofillegalurl = '';
2779 foreach (explode(" ", $tmpfieldsofline[4]) as $illegalurl) {
2780 $listofillegalurl .= ($listofillegalurl ? ' '.$langs->trans("or").' ' : '').'<b>'.preg_replace('/[^a-z0-9\.\-]/', '', $illegalurl).'</b>';
2781 }
2782 $message2 = str_replace('{s}', $listofillegalurl, $message2);
2783 $conf->cache['noncompliantmodules'][$modulekey]['message2'] = $message2;
2784 }
2785 }
2786 }
2787 }
2788
2789 if (!empty($conf->cache['noncompliantmodules'])) {
2790 $modulekey = strtolower($nametocheck);
2791 if (in_array($modulekey, array_keys($conf->cache['noncompliantmodules']))) {
2792 $answer = trim($conf->cache['noncompliantmodules'][$modulekey]['message']);
2793 if (!empty($conf->cache['noncompliantmodules'][$modulekey]['message2'])) {
2794 $answer .= '<br>'.$conf->cache['noncompliantmodules'][$modulekey]['message2'];
2795 }
2796 return $answer;
2797 }
2798 }
2799
2800 return 0;
2801 }
2802
2821 protected function declareNewDictionary($dictionaryArray, $langs = '')
2822 {
2823 $fields = array('name', 'lib', 'sql', 'sqlsort', 'field', 'fieldvalue', 'fieldinsert', 'rowid', 'cond', 'help', 'fieldcheck');
2824
2825 foreach ($fields as $field) {
2826 if (isset($dictionaryArray[$field])) {
2827 // @phan-suppress-next-line PhanTypeMismatchProperty
2828 $this->dictionaries['tab'.$field][] = $dictionaryArray[$field];
2829 }
2830 }
2831 if ($langs && !in_array($langs, $this->dictionaries[$langs])) {
2832 $this->dictionaries['langs'][] = $langs;
2833 }
2834 }
2835}
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.
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...
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:128