dolibarr 20.0.5
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 '<div class="moduledesclong">'.$content.'</div>';
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 $datenextrun = isset($this->cronjobs[$key]['datenextrun']) ? $this->cronjobs[$key]['datenextrun'] : '';
1538
1539 // Search if cron entry already present
1540 $sql = "SELECT count(*) as nb FROM ".MAIN_DB_PREFIX."cronjob";
1541 //$sql .= " WHERE module_name = '".$this->db->escape(empty($this->rights_class) ?strtolower($this->name) : $this->rights_class)."'";
1542 $sql .= " WHERE label = '".$this->db->escape($label)."'";
1543 /*if ($class) {
1544 $sql .= " AND classesname = '".$this->db->escape($class)."'";
1545 }
1546 if ($objectname) {
1547 $sql .= " AND objectname = '".$this->db->escape($objectname)."'";
1548 }
1549 if ($method) {
1550 $sql .= " AND methodename = '".$this->db->escape($method)."'";
1551 }
1552 if ($command) {
1553 $sql .= " AND command = '".$this->db->escape($command)."'";
1554 }
1555 if ($parameters) {
1556 $sql .= " AND params = '".$this->db->escape($parameters)."'";
1557 }*/
1558 $sql .= " AND entity = ".((int) $entity); // Must be exact entity
1559
1560 $now = dol_now();
1561
1562 $result = $this->db->query($sql);
1563 if ($result) {
1564 $obj = $this->db->fetch_object($result);
1565 if ($obj->nb == 0) {
1566 $this->db->begin();
1567
1568 if (!$err) {
1569 $sql = "INSERT INTO ".MAIN_DB_PREFIX."cronjob (module_name, datec, datestart, dateend, label, jobtype, classesname, objectname, methodename, command, params, note,";
1570 if (is_int($frequency)) {
1571 $sql .= ' frequency,';
1572 }
1573 if (is_int($unitfrequency)) {
1574 $sql .= ' unitfrequency,';
1575 }
1576 if (is_int($priority)) {
1577 $sql .= ' priority,';
1578 }
1579 if (!empty($datenextrun)) {
1580 $sql .= ' datenextrun,';
1581 }
1582 if (is_int($status)) {
1583 $sql .= ' status,';
1584 }
1585 $sql .= " entity, test)";
1586 $sql .= " VALUES (";
1587 $sql .= "'".$this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class)."', ";
1588 $sql .= "'".$this->db->idate($now)."', ";
1589 $sql .= ($datestart ? "'".$this->db->idate($datestart)."'" : "'".$this->db->idate($now)."'").", ";
1590 $sql .= ($dateend ? "'".$this->db->idate($dateend)."'" : "NULL").", ";
1591 $sql .= "'".$this->db->escape($label)."', ";
1592 $sql .= "'".$this->db->escape($jobtype)."', ";
1593 $sql .= ($class ? "'".$this->db->escape($class)."'" : "null").",";
1594 $sql .= ($objectname ? "'".$this->db->escape($objectname)."'" : "null").",";
1595 $sql .= ($method ? "'".$this->db->escape($method)."'" : "null").",";
1596 $sql .= ($command ? "'".$this->db->escape($command)."'" : "null").",";
1597 $sql .= ($parameters ? "'".$this->db->escape($parameters)."'" : "null").",";
1598 $sql .= ($comment ? "'".$this->db->escape($comment)."'" : "null").",";
1599 if (is_int($frequency)) {
1600 $sql .= "'".$this->db->escape($frequency)."', ";
1601 }
1602 if (is_int($unitfrequency)) {
1603 $sql .= "'".$this->db->escape($unitfrequency)."', ";
1604 }
1605 if (is_int($priority)) {
1606 $sql .= "'".$this->db->escape($priority)."', ";
1607 }
1608 if (!empty($datenextrun)) {
1609 $sql .= "'".$this->db->idate($datenextrun)."', ";
1610 }
1611 if (is_int($status)) {
1612 $sql .= ((int) $status).", ";
1613 }
1614 $sql .= $entity.",";
1615 $sql .= "'".$this->db->escape($test)."'";
1616 $sql .= ")";
1617
1618 $resql = $this->db->query($sql);
1619 if (!$resql) {
1620 $err++;
1621 }
1622 }
1623
1624 if (!$err) {
1625 $this->db->commit();
1626 } else {
1627 $this->error = $this->db->lasterror();
1628 $this->db->rollback();
1629 }
1630 }
1631 // else box already registered into database
1632 } else {
1633 $this->error = $this->db->lasterror();
1634 $err++;
1635 }
1636 }
1637 }
1638
1639 return $err;
1640 }
1641
1642
1643 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1649 public function delete_cronjobs()
1650 {
1651 // phpcs:enable
1652 global $conf;
1653
1654 $err = 0;
1655
1656 if (is_array($this->cronjobs)) {
1657 $sql = "DELETE FROM ".MAIN_DB_PREFIX."cronjob";
1658 $sql .= " WHERE module_name = '".$this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class)."'";
1659 $sql .= " AND entity = ".$conf->entity;
1660 $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.
1661 // 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.
1662
1663 dol_syslog(get_class($this)."::delete_cronjobs", LOG_DEBUG);
1664 $resql = $this->db->query($sql);
1665 if (!$resql) {
1666 $this->error = $this->db->lasterror();
1667 $err++;
1668 }
1669 }
1670
1671 return $err;
1672 }
1673
1674 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1680 public function delete_tabs()
1681 {
1682 // phpcs:enable
1683 global $conf;
1684
1685 $err = 0;
1686
1687 $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
1688 $sql .= " WHERE ".$this->db->decrypt('name')." like '".$this->db->escape($this->const_name)."_TABS_%'";
1689 $sql .= " AND entity = ".$conf->entity;
1690
1691 dol_syslog(get_class($this)."::delete_tabs", LOG_DEBUG);
1692 if (!$this->db->query($sql)) {
1693 $this->error = $this->db->lasterror();
1694 $err++;
1695 }
1696
1697 return $err;
1698 }
1699
1700 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1706 public function insert_tabs()
1707 {
1708 // phpcs:enable
1709 global $conf;
1710
1711 $err = 0;
1712
1713 if (!empty($this->tabs)) {
1714 dol_syslog(get_class($this)."::insert_tabs", LOG_DEBUG);
1715
1716 $i = 0;
1717 foreach ($this->tabs as $key => $value) {
1718 if (is_array($value) && count($value) == 0) {
1719 continue; // Discard empty arrays
1720 }
1721
1722 $entity = $conf->entity;
1723 $newvalue = $value;
1724
1725 if (is_array($value)) {
1726 $newvalue = $value['data'];
1727 if (isset($value['entity'])) {
1728 $entity = $value['entity'];
1729 }
1730 }
1731
1732 if ($newvalue) {
1733 $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (";
1734 $sql .= "name";
1735 $sql .= ", type";
1736 $sql .= ", value";
1737 $sql .= ", note";
1738 $sql .= ", visible";
1739 $sql .= ", entity";
1740 $sql .= ")";
1741 $sql .= " VALUES (";
1742 $sql .= $this->db->encrypt($this->const_name."_TABS_".$i);
1743 $sql .= ", 'chaine'";
1744 $sql .= ", ".$this->db->encrypt($newvalue);
1745 $sql .= ", null";
1746 $sql .= ", '0'";
1747 $sql .= ", ".((int) $entity);
1748 $sql .= ")";
1749
1750 $resql = $this->db->query($sql);
1751 if (!$resql) {
1752 dol_syslog($this->db->lasterror(), LOG_ERR);
1753 if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1754 $this->error = $this->db->lasterror();
1755 $this->errors[] = $this->db->lasterror();
1756 $err++;
1757 break;
1758 }
1759 }
1760 }
1761 $i++;
1762 }
1763 }
1764 return $err;
1765 }
1766
1767 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1773 public function insert_const()
1774 {
1775 // phpcs:enable
1776 global $conf;
1777
1778 $err = 0;
1779
1780 if (empty($this->const)) {
1781 return 0;
1782 }
1783
1784 dol_syslog(__METHOD__, LOG_DEBUG);
1785
1786 foreach ($this->const as $key => $value) {
1787 $name = $this->const[$key][0];
1788 $type = $this->const[$key][1];
1789 $val = $this->const[$key][2];
1790 $note = isset($this->const[$key][3]) ? $this->const[$key][3] : '';
1791 $visible = isset($this->const[$key][4]) ? $this->const[$key][4] : 0;
1792 $entity = (!empty($this->const[$key][5]) && $this->const[$key][5] != 'current') ? 0 : $conf->entity;
1793
1794 // Clean
1795 if (empty($visible)) {
1796 $visible = '0';
1797 }
1798 if (empty($val) && $val != '0') {
1799 $val = '';
1800 }
1801
1802 $sql = "SELECT count(*) as nb";
1803 $sql .= " FROM ".MAIN_DB_PREFIX."const";
1804 $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($name)."'";
1805 $sql .= " AND entity = ".((int) $entity);
1806
1807 $result = $this->db->query($sql);
1808 if ($result) {
1809 $row = $this->db->fetch_row($result);
1810
1811 if ($row[0] == 0) { // If not found
1812 $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (name,type,value,note,visible,entity)";
1813 $sql .= " VALUES (";
1814 $sql .= $this->db->encrypt($name);
1815 $sql .= ",'".$this->db->escape($type)."'";
1816 $sql .= ",".(($val != '') ? $this->db->encrypt($val) : "''");
1817 $sql .= ",".($note ? "'".$this->db->escape($note)."'" : "null");
1818 $sql .= ",'".$this->db->escape($visible)."'";
1819 $sql .= ",".$entity;
1820 $sql .= ")";
1821
1822 if (!$this->db->query($sql)) {
1823 $err++;
1824 }
1825 } else {
1826 dol_syslog(__METHOD__." constant '".$name."' already exists", LOG_DEBUG);
1827 }
1828 } else {
1829 $err++;
1830 }
1831 }
1832
1833 return $err;
1834 }
1835
1836 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1842 public function delete_const()
1843 {
1844 // phpcs:enable
1845 global $conf;
1846
1847 $err = 0;
1848
1849 if (empty($this->const)) {
1850 return 0;
1851 }
1852
1853 foreach ($this->const as $key => $value) {
1854 $name = $this->const[$key][0];
1855 $deleteonunactive = (!empty($this->const[$key][6])) ? 1 : 0;
1856
1857 if ($deleteonunactive) {
1858 $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
1859 $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($name)."'";
1860 $sql .= " AND entity in (0, ".$conf->entity.")";
1861 dol_syslog(get_class($this)."::delete_const", LOG_DEBUG);
1862 if (!$this->db->query($sql)) {
1863 $this->error = $this->db->lasterror();
1864 $err++;
1865 }
1866 }
1867 }
1868
1869 return $err;
1870 }
1871
1872 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1881 public function insert_permissions($reinitadminperms = 0, $force_entity = null, $notrigger = 0)
1882 {
1883 // phpcs:enable
1884 global $conf, $user;
1885
1886 $err = 0;
1887 $entity = (!empty($force_entity) ? $force_entity : $conf->entity);
1888
1889 dol_syslog(get_class($this)."::insert_permissions", LOG_DEBUG);
1890
1891 // Test if module is activated
1892 $sql_del = "SELECT ".$this->db->decrypt('value')." as value";
1893 $sql_del .= " FROM ".MAIN_DB_PREFIX."const";
1894 $sql_del .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
1895 $sql_del .= " AND entity IN (0,".((int) $entity).")";
1896
1897 $resql = $this->db->query($sql_del);
1898
1899 if ($resql) {
1900 $obj = $this->db->fetch_object($resql);
1901
1902 if ($obj !== null && !empty($obj->value) && !empty($this->rights)) {
1903 include_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
1904
1905 // TODO rights parameters with integer indexes are deprecated
1906 // $this->rights[$key][0] = $this->rights[$key][self::KEY_ID]
1907 // $this->rights[$key][1] = $this->rights[$key][self::KEY_LABEL]
1908 // $this->rights[$key][3] = $this->rights[$key][self::KEY_DEFAULT]
1909 // $this->rights[$key][4] = $this->rights[$key][self::KEY_FIRST_LEVEL]
1910 // $this->rights[$key][5] = $this->rights[$key][self::KEY_SECOND_LEVEL]
1911
1912 // new parameters
1913 // $this->rights[$key][self::KEY_MODULE] // possibility to define user right for an another module (default: current module name)
1914 // $this->rights[$key][self::KEY_ENABLED] // condition to show or hide a user right (default: 1) (eg isModEnabled('anothermodule'))
1915
1916 // If the module is active
1917 foreach ($this->rights as $key => $value) {
1918 $r_id = $this->rights[$key][self::KEY_ID]; // permission id in llx_rights_def (not unique because primary key is couple id-entity)
1919 $r_label = $this->rights[$key][self::KEY_LABEL];
1920 $r_type = $this->rights[$key][self::KEY_TYPE] ?? 'w'; // TODO deprecated
1921 $r_default = $this->rights[$key][self::KEY_DEFAULT] ?? 0;
1922 $r_perms = $this->rights[$key][self::KEY_FIRST_LEVEL] ?? '';
1923 $r_subperms = $this->rights[$key][self::KEY_SECOND_LEVEL] ?? '';
1924
1925 // KEY_FIRST_LEVEL (perms) must not be empty
1926 if (empty($r_perms)) {
1927 continue;
1928 }
1929
1930 // name of module (default: current module name)
1931 $r_module = (empty($this->rights_class) ? strtolower($this->name) : $this->rights_class);
1932
1933 // name of the module from which the right comes (default: empty means same module the permission is for)
1934 $r_module_origin = '';
1935
1936 if (isset($this->rights[$key][self::KEY_MODULE])) {
1937 // name of the module to which the right must be applied
1938 $r_module = $this->rights[$key][self::KEY_MODULE];
1939 // name of the module from which the right comes
1940 $r_module_origin = (empty($this->rights_class) ? strtolower($this->name) : $this->rights_class);
1941 }
1942
1943 // condition to show or hide a user right (default: 1) (eg isModEnabled('anothermodule') or ($conf->global->MAIN_FEATURES_LEVEL > 0) or etc..)
1944 $r_enabled = $this->rights[$key][self::KEY_ENABLED] ?? '1';
1945
1946 // Search if perm already present
1947 $sql = "SELECT count(*) as nb FROM ".MAIN_DB_PREFIX."rights_def";
1948 $sql .= " WHERE entity = ".((int) $entity);
1949 $sql .= " AND id = ".((int) $r_id);
1950
1951 $resqlselect = $this->db->query($sql);
1952 if ($resqlselect) {
1953 $objcount = $this->db->fetch_object($resqlselect);
1954 if ($objcount && $objcount->nb == 0) {
1955 $sql = "INSERT INTO ".MAIN_DB_PREFIX."rights_def (";
1956 $sql .= "id";
1957 $sql .= ", entity";
1958 $sql .= ", libelle";
1959 $sql .= ", module";
1960 $sql .= ", module_origin";
1961 $sql .= ", type"; // TODO deprecated
1962 $sql .= ", bydefault";
1963 $sql .= ", perms";
1964 $sql .= ", subperms";
1965 $sql .= ", enabled";
1966 $sql .= ") VALUES (";
1967 $sql .= ((int) $r_id);
1968 $sql .= ", ".((int) $entity);
1969 $sql .= ", '".$this->db->escape($r_label)."'";
1970 $sql .= ", '".$this->db->escape($r_module)."'";
1971 $sql .= ", '".$this->db->escape($r_module_origin)."'";
1972 $sql .= ", '".$this->db->escape($r_type)."'"; // TODO deprecated
1973 $sql .= ", ".((int) $r_default);
1974 $sql .= ", '".$this->db->escape($r_perms)."'";
1975 $sql .= ", '".$this->db->escape($r_subperms)."'";
1976 $sql .= ", '".$this->db->escape($r_enabled)."'";
1977 $sql.= ")";
1978
1979 $resqlinsert = $this->db->query($sql, 1);
1980
1981 if (!$resqlinsert) {
1982 if ($this->db->errno() != "DB_ERROR_RECORD_ALREADY_EXISTS") {
1983 $this->error = $this->db->lasterror();
1984 $err++;
1985 break;
1986 } else {
1987 dol_syslog(get_class($this)."::insert_permissions record already exists", LOG_INFO);
1988 }
1989 }
1990
1991 $this->db->free($resqlinsert);
1992 }
1993
1994 $this->db->free($resqlselect);
1995 }
1996
1997 // If we want to init permissions on admin users
1998 if (!empty($reinitadminperms)) {
1999 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."user WHERE admin = 1";
2000 dol_syslog(get_class($this)."::insert_permissions Search all admin users", LOG_DEBUG);
2001
2002 $resqlseladmin = $this->db->query($sql, 1);
2003
2004 if ($resqlseladmin) {
2005 $num = $this->db->num_rows($resqlseladmin);
2006 $i = 0;
2007 while ($i < $num) {
2008 $obj2 = $this->db->fetch_object($resqlseladmin);
2009 dol_syslog(get_class($this)."::insert_permissions Add permission id ".$r_id." to user id=".$obj2->rowid);
2010
2011 $tmpuser = new User($this->db);
2012 $result = $tmpuser->fetch($obj2->rowid);
2013 if ($result > 0) {
2014 $tmpuser->addrights($r_id, '', '', 0, 1);
2015 } else {
2016 dol_syslog(get_class($this)."::insert_permissions Failed to add the permission to user because fetch return an error", LOG_ERR);
2017 }
2018 $i++;
2019 }
2020 } else {
2021 dol_print_error($this->db);
2022 }
2023 }
2024 }
2025
2026 if (!empty($reinitadminperms) && !empty($user->admin)) { // Reload permission for current user if defined
2027 // We reload permissions
2028 $user->clearrights();
2029 $user->getrights();
2030 }
2031 }
2032 $this->db->free($resql);
2033 } else {
2034 $this->error = $this->db->lasterror();
2035 $err++;
2036 }
2037
2038 return $err;
2039 }
2040
2041
2042 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2048 public function delete_permissions()
2049 {
2050 // phpcs:enable
2051 global $conf;
2052
2053 $err = 0;
2054
2055 $module = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
2056
2057 $sql = "DELETE FROM ".MAIN_DB_PREFIX."rights_def";
2058 $sql .= " WHERE (module = '".$this->db->escape($module)."' OR module_origin = '".$this->db->escape($module)."')";
2059
2060 // Delete all entities if core module
2061 if (empty($this->core_enabled)) {
2062 $sql .= " AND entity = ".((int) $conf->entity);
2063 }
2064
2065 dol_syslog(get_class($this)."::delete_permissions", LOG_DEBUG);
2066 if (!$this->db->query($sql)) {
2067 $this->error = $this->db->lasterror();
2068 $err++;
2069 }
2070
2071 return $err;
2072 }
2073
2074
2075 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2081 public function insert_menus()
2082 {
2083 // phpcs:enable
2084 global $conf, $user;
2085
2086 if (!is_array($this->menu) || empty($this->menu)) {
2087 return 0;
2088 }
2089
2090 include_once DOL_DOCUMENT_ROOT.'/core/class/menubase.class.php';
2091
2092 dol_syslog(get_class($this)."::insert_menus", LOG_DEBUG);
2093
2094 $err = 0;
2095
2096 // Common module
2097 $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
2098
2099 $this->db->begin();
2100
2101 foreach ($this->menu as $key => $value) {
2102 $menu = new Menubase($this->db);
2103 $menu->menu_handler = 'all';
2104
2105 //$menu->module=strtolower($this->name); TODO When right_class will be same than module name
2106 $menu->module = (empty($this->rights_class) ? strtolower($this->name) : $this->rights_class);
2107
2108 if (!$this->menu[$key]['fk_menu']) {
2109 $menu->fk_menu = 0;
2110 } else {
2111 $foundparent = 0;
2112 $fk_parent = $this->menu[$key]['fk_menu'];
2113 $reg = array();
2114 if (preg_match('/^r=/', $fk_parent)) { // old deprecated method
2115 $fk_parent = str_replace('r=', '', $fk_parent);
2116 if (isset($this->menu[$fk_parent]['rowid'])) {
2117 $menu->fk_menu = $this->menu[$fk_parent]['rowid'];
2118 $foundparent = 1;
2119 }
2120 } elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+),fk_leftmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
2121 $menu->fk_menu = -1;
2122 $menu->fk_mainmenu = $reg[1];
2123 $menu->fk_leftmenu = $reg[2];
2124 $foundparent = 1;
2125 } elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
2126 $menu->fk_menu = -1;
2127 $menu->fk_mainmenu = $reg[1];
2128 $menu->fk_leftmenu = '';
2129 $foundparent = 1;
2130 }
2131 if (!$foundparent) {
2132 $this->error = "ErrorBadDefinitionOfMenuArrayInModuleDescriptor";
2133 dol_syslog(get_class($this)."::insert_menus ".$this->error." ".$this->menu[$key]['fk_menu'], LOG_ERR);
2134 $err++;
2135 }
2136 }
2137 $menu->type = $this->menu[$key]['type'];
2138 $menu->mainmenu = isset($this->menu[$key]['mainmenu']) ? $this->menu[$key]['mainmenu'] : (isset($menu->fk_mainmenu) ? $menu->fk_mainmenu : '');
2139 $menu->leftmenu = isset($this->menu[$key]['leftmenu']) ? $this->menu[$key]['leftmenu'] : '';
2140 $menu->title = $this->menu[$key]['titre'];
2141 $menu->prefix = isset($this->menu[$key]['prefix']) ? $this->menu[$key]['prefix'] : '';
2142 $menu->url = $this->menu[$key]['url'];
2143 $menu->langs = isset($this->menu[$key]['langs']) ? $this->menu[$key]['langs'] : '';
2144 $menu->position = $this->menu[$key]['position'];
2145 $menu->perms = $this->menu[$key]['perms'];
2146 $menu->target = isset($this->menu[$key]['target']) ? $this->menu[$key]['target'] : '';
2147 $menu->user = $this->menu[$key]['user'];
2148 $menu->enabled = isset($this->menu[$key]['enabled']) ? $this->menu[$key]['enabled'] : 0;
2149 $menu->position = $this->menu[$key]['position'];
2150 $menu->entity = $entity;
2151
2152 if (!$err) {
2153 $result = $menu->create($user); // Save menu entry into table llx_menu
2154 if ($result > 0) {
2155 $this->menu[$key]['rowid'] = $result;
2156 } else {
2157 $this->error = $menu->error;
2158 dol_syslog(get_class($this).'::insert_menus result='.$result." ".$this->error, LOG_ERR);
2159 $err++;
2160 break;
2161 }
2162 }
2163 }
2164
2165 if (!$err) {
2166 $this->db->commit();
2167 } else {
2168 dol_syslog(get_class($this)."::insert_menus ".$this->error, LOG_ERR);
2169 $this->db->rollback();
2170 }
2171
2172 return $err;
2173 }
2174
2175
2176 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2182 public function delete_menus()
2183 {
2184 // phpcs:enable
2185 global $conf;
2186
2187 $err = 0;
2188
2189 //$module=strtolower($this->name); TODO When right_class will be same than module name
2190 $module = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
2191
2192 $sql = "DELETE FROM ".MAIN_DB_PREFIX."menu";
2193 $sql .= " WHERE module = '".$this->db->escape($module)."'";
2194 $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'
2195 $sql .= " AND entity IN (0, ".$conf->entity.")";
2196
2197 dol_syslog(get_class($this)."::delete_menus", LOG_DEBUG);
2198 $resql = $this->db->query($sql);
2199 if (!$resql) {
2200 $this->error = $this->db->lasterror();
2201 $err++;
2202 }
2203
2204 return $err;
2205 }
2206
2207 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2213 public function create_dirs()
2214 {
2215 // phpcs:enable
2216 global $langs, $conf;
2217
2218 $err = 0;
2219 $name = '';
2220
2221 if (isset($this->dirs) && is_array($this->dirs)) {
2222 foreach ($this->dirs as $key => $value) {
2223 $addtodatabase = 0;
2224
2225 if (!is_array($value)) {
2226 $dir = $value; // Default simple mode
2227 } else {
2228 $constname = $this->const_name."_DIR_";
2229 $dir = $this->dirs[$key][1];
2230 $addtodatabase = empty($this->dirs[$key][2]) ? '' : $this->dirs[$key][2]; // Create constante in llx_const
2231 $subname = empty($this->dirs[$key][3]) ? '' : strtoupper($this->dirs[$key][3]); // Add submodule name (ex: $conf->module->submodule->dir_output)
2232 $forcename = empty($this->dirs[$key][4]) ? '' : strtoupper($this->dirs[$key][4]); // Change the module name if different
2233
2234 if (!empty($forcename)) {
2235 $constname = 'MAIN_MODULE_'.$forcename."_DIR_";
2236 }
2237 if (!empty($subname)) {
2238 $constname = $constname.$subname."_";
2239 }
2240
2241 $name = $constname.strtoupper($this->dirs[$key][0]);
2242 }
2243
2244 // Define directory full path ($dir must start with "/")
2245 if (!getDolGlobalString('MAIN_MODULE_MULTICOMPANY') || $conf->entity == 1) {
2246 $fulldir = DOL_DATA_ROOT.$dir;
2247 } else {
2248 $fulldir = DOL_DATA_ROOT."/".$conf->entity.$dir;
2249 }
2250 // Create dir if it does not exists
2251 if (!empty($fulldir) && !file_exists($fulldir)) {
2252 if (dol_mkdir($fulldir, DOL_DATA_ROOT) < 0) {
2253 $this->error = $langs->trans("ErrorCanNotCreateDir", $fulldir);
2254 dol_syslog(get_class($this)."::_init ".$this->error, LOG_ERR);
2255 $err++;
2256 }
2257 }
2258
2259 // Define the constant in database if requested (not the default mode)
2260 if (!empty($addtodatabase) && !empty($name)) {
2261 $result = $this->insert_dirs($name, $dir);
2262 if ($result) {
2263 $err++;
2264 }
2265 }
2266 }
2267 }
2268
2269 return $err;
2270 }
2271
2272
2273 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2282 public function insert_dirs($name, $dir)
2283 {
2284 // phpcs:enable
2285 global $conf;
2286
2287 $err = 0;
2288
2289 $sql = "SELECT count(*)";
2290 $sql .= " FROM ".MAIN_DB_PREFIX."const";
2291 $sql .= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($name)."'";
2292 $sql .= " AND entity = ".$conf->entity;
2293
2294 dol_syslog(get_class($this)."::insert_dirs", LOG_DEBUG);
2295 $result = $this->db->query($sql);
2296 if ($result) {
2297 $row = $this->db->fetch_row($result);
2298
2299 if ($row[0] == 0) {
2300 $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (name, type, value, note, visible, entity)";
2301 $sql .= " VALUES (".$this->db->encrypt($name).", 'chaine', ".$this->db->encrypt($dir).", '".$this->db->escape("Directory for module ".$this->name)."', '0', ".((int) $conf->entity).")";
2302
2303 dol_syslog(get_class($this)."::insert_dirs", LOG_DEBUG);
2304 $this->db->query($sql);
2305 }
2306 } else {
2307 $this->error = $this->db->lasterror();
2308 $err++;
2309 }
2310
2311 return $err;
2312 }
2313
2314
2315 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2321 public function delete_dirs()
2322 {
2323 // phpcs:enable
2324 global $conf;
2325
2326 $err = 0;
2327
2328 $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
2329 $sql .= " WHERE ".$this->db->decrypt('name')." LIKE '".$this->db->escape($this->const_name)."_DIR_%'";
2330 $sql .= " AND entity = ".$conf->entity;
2331
2332 dol_syslog(get_class($this)."::delete_dirs", LOG_DEBUG);
2333 if (!$this->db->query($sql)) {
2334 $this->error = $this->db->lasterror();
2335 $err++;
2336 }
2337
2338 return $err;
2339 }
2340
2341 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2348 public function insert_module_parts()
2349 {
2350 // phpcs:enable
2351 global $conf, $langs;
2352
2353 $error = 0;
2354
2355 if (is_array($this->module_parts)) {
2356 if (empty($this->module_parts['icon']) && !empty($this->picto) && preg_match('/^fa\-/', $this->picto)) {
2357 $this->module_parts['icon'] = $this->picto;
2358 }
2359
2360 foreach ($this->module_parts as $key => $value) {
2361 if (is_array($value) && count($value) == 0) {
2362 continue; // Discard empty arrays
2363 }
2364
2365 // If module brings website templates, we must generate the zip like we do whenenabling the website module
2366 if ($key == 'websitetemplates' && $value == 1) {
2367 $srcroot = dol_buildpath('/'.strtolower($this->name).'/doctemplates/websites');
2368
2369 // Copy templates in dir format (recommended) into zip file
2370 $docs = dol_dir_list($srcroot, 'directories', 0, 'website_.*$');
2371 foreach ($docs as $cursorfile) {
2372 $src = $srcroot.'/'.$cursorfile['name'];
2373 $dest = DOL_DATA_ROOT.'/doctemplates/websites/'.$cursorfile['name'];
2374
2375 dol_delete_file($dest.'.zip');
2376
2377 // Compress it
2378 global $errormsg;
2379 $errormsg = '';
2380 $result = dol_compress_dir($src, $dest.'.zip', 'zip');
2381 if ($result < 0) {
2382 $error++;
2383 $this->error = ($errormsg ? $errormsg : $langs->trans('ErrorFailToCreateZip', $dest));
2384 $this->errors[] = ($errormsg ? $errormsg : $langs->trans('ErrorFailToCreateZip', $dest));
2385 }
2386 }
2387
2388 // Copy also the preview website_xxx.jpg file
2389 $docs = dol_dir_list($srcroot, 'files', 0, 'website_.*\.jpg$');
2390 foreach ($docs as $cursorfile) {
2391 $src = $srcroot.'/'.$cursorfile['name'];
2392 $dest = DOL_DATA_ROOT.'/doctemplates/websites/'.$cursorfile['name'];
2393
2394 dol_copy($src, $dest);
2395 }
2396 }
2397
2398 $entity = $conf->entity; // Reset the current entity
2399 $newvalue = $value;
2400
2401 // Serialize array parameters
2402 if (is_array($value)) {
2403 // Can defined other parameters
2404 // Example when $key='hooks', then $value is an array('data'=>array('hookcontext1','hookcontext2'), 'entity'=>X)
2405 if (isset($value['data']) && is_array($value['data'])) {
2406 $newvalue = json_encode($value['data']);
2407 if (isset($value['entity'])) {
2408 $entity = $value['entity'];
2409 }
2410 } elseif (isset($value['data']) && !is_array($value['data'])) {
2411 $newvalue = $value['data'];
2412 if (isset($value['entity'])) {
2413 $entity = $value['entity'];
2414 }
2415 } else { // when hook is declared with syntax 'hook'=>array('hookcontext1','hookcontext2',...)
2416 $newvalue = json_encode($value);
2417 }
2418 }
2419
2420 if (!empty($newvalue)) {
2421 $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (";
2422 $sql .= "name";
2423 $sql .= ", type";
2424 $sql .= ", value";
2425 $sql .= ", note";
2426 $sql .= ", visible";
2427 $sql .= ", entity";
2428 $sql .= ")";
2429 $sql .= " VALUES (";
2430 $sql .= " ".$this->db->encrypt($this->const_name."_".strtoupper($key), 1);
2431 $sql .= ", 'chaine'";
2432 $sql .= ", ".$this->db->encrypt($newvalue, 1);
2433 $sql .= ", null";
2434 $sql .= ", '0'";
2435 $sql .= ", ".((int) $entity);
2436 $sql .= ")";
2437
2438 dol_syslog(get_class($this)."::insert_module_parts for key=".$this->const_name."_".strtoupper($key), LOG_DEBUG);
2439
2440 $resql = $this->db->query($sql, 1);
2441 if (!$resql) {
2442 if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2443 $error++;
2444 $this->error = $this->db->lasterror();
2445 } else {
2446 dol_syslog(get_class($this)."::insert_module_parts for ".$this->const_name."_".strtoupper($key)." Record already exists.", LOG_WARNING);
2447 }
2448 }
2449 }
2450 }
2451 }
2452 return $error;
2453 }
2454
2455 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2461 public function delete_module_parts()
2462 {
2463 // phpcs:enable
2464 global $conf;
2465
2466 $err = 0;
2467
2468 if (is_array($this->module_parts)) {
2469 dol_syslog(get_class($this)."::delete_module_parts", LOG_DEBUG);
2470
2471 if (empty($this->module_parts['icon']) && !empty($this->picto) && preg_match('/^fa\-/', $this->picto)) {
2472 $this->module_parts['icon'] = $this->picto;
2473 }
2474
2475 foreach ($this->module_parts as $key => $value) {
2476 // If entity is defined
2477 if (is_array($value) && isset($value['entity'])) {
2478 $entity = $value['entity'];
2479 } else {
2480 $entity = $conf->entity;
2481 }
2482
2483 $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
2484 $sql .= " WHERE ".$this->db->decrypt('name')." LIKE '".$this->db->escape($this->const_name)."_".strtoupper($key)."'";
2485 $sql .= " AND entity = ".((int) $entity);
2486
2487 if (!$this->db->query($sql)) {
2488 $this->error = $this->db->lasterror();
2489 $err++;
2490 }
2491 }
2492 }
2493 return $err;
2494 }
2495
2505 public function init($options = '')
2506 {
2507 return $this->_init(array(), $options);
2508 }
2509
2518 public function remove($options = '')
2519 {
2520 return $this->_remove(array(), $options);
2521 }
2522
2523
2531 public function getKanbanView($codeenabledisable = '', $codetoconfig = '')
2532 {
2533 global $langs;
2534
2535 // Define imginfo
2536 $imginfo = "info";
2537 if ($this->isCoreOrExternalModule() == 'external') {
2538 $imginfo = "info_black";
2539 }
2540
2541 $const_name = 'MAIN_MODULE_'.strtoupper(preg_replace('/^mod/i', '', get_class($this)));
2542
2543 $version = $this->getVersion(0);
2544 $versiontrans = '';
2545 if (preg_match('/development/i', $version)) {
2546 $versiontrans .= 'warning';
2547 }
2548 if (preg_match('/experimental/i', $version)) {
2549 $versiontrans .= 'warning';
2550 }
2551 if (preg_match('/deprecated/i', $version)) {
2552 $versiontrans .= 'warning';
2553 }
2554
2555 $return = '
2556 <div class="box-flex-item info-box-module'
2557 .(getDolGlobalString($const_name) ? '' : ' --disabled')
2558 .($this->isCoreOrExternalModule() == 'external' ? ' --external' : '')
2559 .($this->needUpdate ? ' --need-update' : '')
2560 .'">
2561 <div class="info-box info-box-sm info-box-module">
2562 <div class="info-box-icon'.(!getDolGlobalString($const_name) ? '' : ' info-box-icon-module-enabled'.($versiontrans ? ' info-box-icon-module-warning' : '')).'">';
2563
2564 $alttext = '';
2565 //if (is_array($objMod->need_dolibarr_version)) $alttext.=($alttext?' - ':'').'Dolibarr >= '.join('.',$objMod->need_dolibarr_version);
2566 //if (is_array($objMod->phpmin)) $alttext.=($alttext?' - ':'').'PHP >= '.join('.',$objMod->phpmin);
2567 if (!empty($this->picto)) {
2568 if (preg_match('/^\//i', $this->picto)) {
2569 $return .= img_picto($alttext, $this->picto, 'class="inline-block valignmiddle"', 1);
2570 } else {
2571 $return .= img_object($alttext, $this->picto, 'class="inline-block valignmiddle"');
2572 }
2573 } else {
2574 $return .= img_object($alttext, 'generic', 'class="inline-block valignmiddle"');
2575 }
2576
2577 if ($this->isCoreOrExternalModule() == 'external' || preg_match('/development|experimental|deprecated/i', $version)) {
2578 $versionTitle = $langs->trans("Version").' '.$this->getVersion(1);
2579 if ($this->needUpdate) {
2580 $versionTitle .= '<br>'.$langs->trans('ModuleUpdateAvailable').' : '.$this->lastVersion;
2581 }
2582
2583 $return .= '<span class="info-box-icon-version'.($versiontrans ? ' '.$versiontrans : '').' classfortooltip" title="'.dol_escape_js($versionTitle).'" >';
2584 $return .= $this->getVersion(1);
2585 $return .= '</span>';
2586 }
2587
2588 $return .= '</div>
2589 <div class="info-box-content info-box-text-module'.(!getDolGlobalString($const_name) ? '' : ' info-box-module-enabled'.($versiontrans ? ' info-box-content-warning' : '')).'">
2590 <span class="info-box-title">'.$this->getName().'</span>
2591 <span class="info-box-desc twolinesmax opacitymedium" title="'.dol_escape_htmltag($this->getDesc()).'">'.nl2br($this->getDesc()).'</span>';
2592
2593 $return .= '<div class="valignmiddle inline-block info-box-more">';
2594 //if ($versiontrans) print img_warning($langs->trans("Version").' '.$this->getVersion(1)).' ';
2595 $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>';
2596 $return .= '</div><br>';
2597
2598 $return .= '<div class="valignmiddle inline-block info-box-actions">';
2599 $return .= '<div class="valignmiddle inline-block info-box-setup">';
2600 $return .= $codetoconfig;
2601 $return .= '</div>';
2602 $return .= '<div class="valignmiddle inline-block marginleftonly marginrightonly">';
2603 $return .= $codeenabledisable;
2604 $return .= '</div>';
2605 $return .= '</div>';
2606
2607 $return .= '
2608 </div><!-- /.info-box-content -->
2609 </div><!-- /.info-box -->
2610 </div>';
2611
2612 return $return;
2613 }
2614
2622 public function checkForUpdate()
2623 {
2624 require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
2625 if (!empty($this->url_last_version)) {
2626 $lastVersion = getURLContent($this->url_last_version, 'GET', '', 1, array(), array('http', 'https'), 0); // Accept http or https links on external remote server only
2627 if (isset($lastVersion['content']) && strlen($lastVersion['content']) < 30) {
2628 // 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 _ . -
2629 $this->lastVersion = preg_replace("/[^a-zA-Z0-9_\.\-]+/", "", $lastVersion['content']);
2630 if (version_compare($this->lastVersion, $this->version) > 0) {
2631 $this->needUpdate = true;
2632 return 1;
2633 } else {
2634 $this->needUpdate = false;
2635 return 0;
2636 }
2637 } else {
2638 return -1;
2639 }
2640 }
2641 return 0;
2642 }
2643
2650 public function checkForCompliance()
2651 {
2652 global $conf, $langs;
2653
2654 // Get list of illegal modules name or ID
2655 if (empty($conf->cache['noncompliantmodules'])) {
2656 require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
2657
2658 $urlforblacklistmodules = 'https://ping.dolibarr.org/modules-blacklist.txt';
2659
2660 $result = getURLContent($urlforblacklistmodules, 'GET', '', 1, array(), array('http', 'https'), 0); // Accept http or https links on external remote server only
2661 if (isset($result['content']) && $result['http_code'] == 200) {
2662 $langs->load("errors");
2663
2664 // 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 _ . -
2665 $arrayoflines = preg_split("/[\n,]/", $result['content']);
2666 foreach ($arrayoflines as $line) {
2667 $tmpfieldsofline = explode(';', $line);
2668 $modulekey = strtolower($tmpfieldsofline[0]);
2669 $conf->cache['noncompliantmodules'][$modulekey]['name'] = $tmpfieldsofline[0];
2670 $conf->cache['noncompliantmodules'][$modulekey]['id'] = $tmpfieldsofline[1];
2671 $conf->cache['noncompliantmodules'][$modulekey]['signature'] = $tmpfieldsofline[2];
2672 $conf->cache['noncompliantmodules'][$modulekey]['message'] = $langs->trans(empty($tmpfieldsofline[3]) ? 'WarningModuleAffiliatedToAReportedCompany' : $tmpfieldsofline[3]);
2673 if (!empty($tmpfieldsofline[4])) {
2674 $message2 = $langs->trans("WarningModuleAffiliatedToAPiratPlatform", '{s}');
2675 $listofillegalurl = '';
2676 foreach (explode(" ", $tmpfieldsofline[4]) as $illegalurl) {
2677 $listofillegalurl .= ($listofillegalurl ? ' '.$langs->trans("or").' ' : '').'<b>'.preg_replace('/[^a-z0-9\.\-]/', '', $illegalurl).'</b>';
2678 }
2679 $message2 = str_replace('{s}', $listofillegalurl, $message2);
2680 $conf->cache['noncompliantmodules'][$modulekey]['message2'] = $message2;
2681 }
2682 }
2683 }
2684 }
2685
2686 if (!empty($conf->cache['noncompliantmodules'])) {
2687 $modulekey = strtolower($this->name);
2688 if (in_array($modulekey, array_keys($conf->cache['noncompliantmodules']))) {
2689 $answer = trim($conf->cache['noncompliantmodules'][$modulekey]['message']);
2690 if (!empty($conf->cache['noncompliantmodules'][$modulekey]['message2'])) {
2691 $answer .= '<br>'.$conf->cache['noncompliantmodules'][$modulekey]['message2'];
2692 }
2693 return $answer;
2694 }
2695 }
2696
2697 return 0;
2698 }
2699
2718 protected function declareNewDictionary($dictionaryArray, $langs = '')
2719 {
2720 $fields = array('name', 'lib', 'sql', 'sqlsort', 'field', 'fieldvalue', 'fieldinsert', 'rowid', 'cond', 'help', 'fieldcheck');
2721
2722 foreach ($fields as $field) {
2723 if (isset($dictionaryArray[$field])) {
2724 $this->dictionaries['tab'.$field][] = $dictionaryArray[$field];
2725 }
2726 }
2727 if ($langs && !in_array($langs, $this->dictionaries[$langs])) {
2728 $this->dictionaries['langs'][] = $langs;
2729 }
2730 }
2731}
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:140