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