Passed
Push — main ( b35285...f1540e )
by Rafael
58:14
created
Dolibarr/Modules/Install/Controller/InstallController.php 2 patches
Indentation   +31 added lines, -31 removed lines patch added patch discarded remove patch
@@ -113,7 +113,7 @@  discard block
 block discarded – undo
113 113
             pFooter(0);
114 114
         }
115 115
 
116
-		return true;
116
+        return true;
117 117
     }
118 118
 
119 119
     public function check($testget = false)
@@ -1209,14 +1209,14 @@  discard block
 block discarded – undo
1209 1209
                                id="db_pass" autocomplete="off"
1210 1210
                                name="db_pass"
1211 1211
                                value="<?php
1212
-                               // If $force_install_databasepass is on, we don't want to set password, we just show '***'. Real value will be extracted from the forced install file at step1.
1213
-                               // @phan-suppress-next-line PhanParamSuspiciousOrder
1214
-                               $autofill = ((!empty($_SESSION['dol_save_pass'])) ? $_SESSION['dol_save_pass'] : str_pad('', strlen($force_install_databasepass), '*'));
1215
-                               if (!empty($dolibarr_main_prod) && empty($_SESSION['dol_save_pass'])) {    // So value can't be found if install page still accessible
1212
+                                // If $force_install_databasepass is on, we don't want to set password, we just show '***'. Real value will be extracted from the forced install file at step1.
1213
+                                // @phan-suppress-next-line PhanParamSuspiciousOrder
1214
+                                $autofill = ((!empty($_SESSION['dol_save_pass'])) ? $_SESSION['dol_save_pass'] : str_pad('', strlen($force_install_databasepass), '*'));
1215
+                                if (!empty($dolibarr_main_prod) && empty($_SESSION['dol_save_pass'])) {    // So value can't be found if install page still accessible
1216 1216
                                     $autofill = '';
1217
-                               }
1218
-                               print dol_escape_htmltag($autofill);
1219
-                               ?>"
1217
+                                }
1218
+                                print dol_escape_htmltag($autofill);
1219
+                                ?>"
1220 1220
                             <?php if ($force_install_noedit == 2 && $force_install_databasepass !== null) {
1221 1221
                                 print ' disabled';
1222 1222
                             } ?>
@@ -1298,23 +1298,23 @@  discard block
 block discarded – undo
1298 1298
                                name="db_pass_root"
1299 1299
                                class="needroot text-security"
1300 1300
                                value="<?php
1301
-                               // If $force_install_databaserootpass is on, we don't want to set password here, we just show '***'. Real value will be extracted from the forced install file at step1.
1302
-                               // @phan-suppress-next-line PhanParamSuspiciousOrder
1303
-                               $autofill = ((!empty($force_install_databaserootpass)) ? str_pad('', strlen($force_install_databaserootpass), '*') : (isset($db_pass_root) ? $db_pass_root : ''));
1304
-                               if (!empty($dolibarr_main_prod)) {
1301
+                                // If $force_install_databaserootpass is on, we don't want to set password here, we just show '***'. Real value will be extracted from the forced install file at step1.
1302
+                                // @phan-suppress-next-line PhanParamSuspiciousOrder
1303
+                                $autofill = ((!empty($force_install_databaserootpass)) ? str_pad('', strlen($force_install_databaserootpass), '*') : (isset($db_pass_root) ? $db_pass_root : ''));
1304
+                                if (!empty($dolibarr_main_prod)) {
1305 1305
                                     $autofill = '';
1306
-                               }
1307
-                               // Do not autofill password if instance is a production instance
1308
-                               if (
1309
-                                   !empty($_SERVER["SERVER_NAME"]) && !in_array(
1310
-                                       $_SERVER["SERVER_NAME"],
1311
-                                       ['127.0.0.1', 'localhost', 'localhostgit']
1312
-                                   )
1313
-                               ) {
1306
+                                }
1307
+                                // Do not autofill password if instance is a production instance
1308
+                                if (
1309
+                                    !empty($_SERVER["SERVER_NAME"]) && !in_array(
1310
+                                        $_SERVER["SERVER_NAME"],
1311
+                                        ['127.0.0.1', 'localhost', 'localhostgit']
1312
+                                    )
1313
+                                ) {
1314 1314
                                     $autofill = '';
1315
-                               }    // Do not autofill password for remote access
1316
-                               print dol_escape_htmltag($autofill);
1317
-                               ?>"
1315
+                                }    // Do not autofill password for remote access
1316
+                                print dol_escape_htmltag($autofill);
1317
+                                ?>"
1318 1318
                             <?php if ($force_install_noedit > 0 && !empty($force_install_databaserootpass)) {
1319 1319
                                 print ' disabled'; /* May be removed by javascript*/
1320 1320
                             } ?>
@@ -3006,7 +3006,7 @@  discard block
 block discarded – undo
3006 3006
         $hash_unique_id = dol_hash('dolibarr' . $conf->file->instance_unique_id, 'sha256');   // Note: if the global salt changes, this hash changes too so ping may be counted twice. We don't mind. It is for statistics purpose only.
3007 3007
 
3008 3008
         //$out = '<input type="checkbox" name="dolibarrpingno" id="dolibarrpingno"' . ((getDolGlobalString('MAIN_FIRST_PING_OK_ID') == 'disabled') ? '' : ' value="checked" checked="true"') . '> ';
3009
-		$out = '<input type="checkbox" name="dolibarrpingno" id="dolibarrpingno"' . ((getDolGlobalString('MAIN_FIRST_PING_OK_ID') == 'disabled') ? '' : ' value="checked"') . '> ';
3009
+        $out = '<input type="checkbox" name="dolibarrpingno" id="dolibarrpingno"' . ((getDolGlobalString('MAIN_FIRST_PING_OK_ID') == 'disabled') ? '' : ' value="checked"') . '> ';
3010 3010
         $out .= '<label for="dolibarrpingno">' . $langs->trans("MakeAnonymousPing") . '</label>';
3011 3011
 
3012 3012
         $out .= '<!-- Add js script to manage the uncheck of option to not send the ping -->';
@@ -3151,8 +3151,8 @@  discard block
 block discarded – undo
3151 3151
     public function step5()
3152 3152
     {
3153 3153
         global $langs, $db, $hookmanager;
3154
-		global $config;
3155
-		$dbPrefix = $config->db->prefix;
3154
+        global $config;
3155
+        $dbPrefix = $config->db->prefix;
3156 3156
 
3157 3157
         $conffile = Config::getDolibarrConfigFilename();
3158 3158
         $conffiletoshow = $conffile;
@@ -3238,7 +3238,7 @@  discard block
 block discarded – undo
3238 3238
             }
3239 3239
         }
3240 3240
 
3241
-		/*
3241
+        /*
3242 3242
 		 *  View
3243 3243
 		 */
3244 3244
 
@@ -3276,14 +3276,14 @@  discard block
 block discarded – undo
3276 3276
 
3277 3277
             $db = getDoliDBInstance($conf->db->type, $conf->db->host, $conf->db->user, $conf->db->pass, $conf->db->name, (int) $conf->db->port);
3278 3278
 
3279
-			$conf = Config::getConf();
3280
-			$config=Config::getConfig();
3279
+            $conf = Config::getConf();
3280
+            $config=Config::getConfig();
3281 3281
 
3282
-			// Create the global $hookmanager object
3282
+            // Create the global $hookmanager object
3283 3283
             // include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
3284 3284
             // $hookmanager = new HookManager($db);
3285 3285
 
3286
-			$ok = 0;
3286
+            $ok = 0;
3287 3287
 
3288 3288
             // If first install
3289 3289
             if ($action == "set") {
Please login to merge, or discard this patch.
Spacing   +21 added lines, -21 removed lines patch added patch discarded remove patch
@@ -440,7 +440,7 @@  discard block
 block discarded – undo
440 440
                             print '<span class="error">A ' . $conffiletoshow . ' file exists with a dolibarr_main_document_root to ' . $dolibarr_main_document_root . ' that seems wrong. Try to fix or remove the ' . $conffiletoshow . ' file.</span><br>' . "\n";
441 441
                             dol_syslog("A '" . $conffiletoshow . "' file exists with a dolibarr_main_document_root to " . $dolibarr_main_document_root . " that seems wrong. Try to fix or remove the '" . $conffiletoshow . "' file.", LOG_WARNING);
442 442
                         } else {
443
-                            require_once $dolibarr_main_document_root . '/../Dolibarr/Lib/Admin.php';;
443
+                            require_once $dolibarr_main_document_root . '/../Dolibarr/Lib/Admin.php'; ;
444 444
 
445 445
                             // If password is encoded, we decode it
446 446
                             if (preg_match('/crypted:/i', $dolibarr_main_db_pass) || !empty($dolibarr_main_db_encrypted_pass)) {
@@ -557,7 +557,7 @@  discard block
 block discarded – undo
557 557
                     $allowupgrade = true;
558 558
                 }
559 559
 
560
-                $dir = DOL_DOCUMENT_ROOT . "/install/mysql/migration/";   // We use mysql migration scripts whatever is database driver
560
+                $dir = DOL_DOCUMENT_ROOT . "/install/mysql/migration/"; // We use mysql migration scripts whatever is database driver
561 561
                 dolibarr_install_syslog("Scan sql files for migration files in " . $dir);
562 562
 
563 563
                 // Get files list of migration file x.y.z-a.b.c.sql into /install/mysql/migration
@@ -2052,67 +2052,67 @@  discard block
 block discarded – undo
2052 2052
                     fwrite($fp, "\n");
2053 2053
                     if (empty($force_dolibarr_lib_FPDF_PATH)) {
2054 2054
                         fwrite($fp, '//');
2055
-                        $force_dolibarr_lib_FPDF_PATH = '';  // @phan-suppress-current-line PhanPluginRedundantAssignment
2055
+                        $force_dolibarr_lib_FPDF_PATH = ''; // @phan-suppress-current-line PhanPluginRedundantAssignment
2056 2056
                     }
2057 2057
                     fwrite($fp, '$dolibarr_lib_FPDF_PATH="' . dol_escape_php(dol_sanitizePathName($force_dolibarr_lib_FPDF_PATH)) . '";');
2058 2058
                     fwrite($fp, "\n");
2059 2059
                     if (empty($force_dolibarr_lib_TCPDF_PATH)) {
2060 2060
                         fwrite($fp, '//');
2061
-                        $force_dolibarr_lib_TCPDF_PATH = '';  // @phan-suppress-current-line PhanPluginRedundantAssignment
2061
+                        $force_dolibarr_lib_TCPDF_PATH = ''; // @phan-suppress-current-line PhanPluginRedundantAssignment
2062 2062
                     }
2063 2063
                     fwrite($fp, '$dolibarr_lib_TCPDF_PATH="' . dol_escape_php(dol_sanitizePathName($force_dolibarr_lib_TCPDF_PATH)) . '";');
2064 2064
                     fwrite($fp, "\n");
2065 2065
                     if (empty($force_dolibarr_lib_FPDI_PATH)) {
2066 2066
                         fwrite($fp, '//');
2067
-                        $force_dolibarr_lib_FPDI_PATH = '';  // @phan-suppress-current-line PhanPluginRedundantAssignment
2067
+                        $force_dolibarr_lib_FPDI_PATH = ''; // @phan-suppress-current-line PhanPluginRedundantAssignment
2068 2068
                     }
2069 2069
                     fwrite($fp, '$dolibarr_lib_FPDI_PATH="' . dol_escape_php(dol_sanitizePathName($force_dolibarr_lib_FPDI_PATH)) . '";');
2070 2070
                     fwrite($fp, "\n");
2071 2071
                     if (empty($force_dolibarr_lib_TCPDI_PATH)) {
2072 2072
                         fwrite($fp, '//');
2073
-                        $force_dolibarr_lib_TCPDI_PATH = '';  // @phan-suppress-current-line PhanPluginRedundantAssignment
2073
+                        $force_dolibarr_lib_TCPDI_PATH = ''; // @phan-suppress-current-line PhanPluginRedundantAssignment
2074 2074
                     }
2075 2075
                     fwrite($fp, '$dolibarr_lib_TCPDI_PATH="' . dol_escape_php(dol_sanitizePathName($force_dolibarr_lib_TCPDI_PATH)) . '";');
2076 2076
                     fwrite($fp, "\n");
2077 2077
                     if (empty($force_dolibarr_lib_GEOIP_PATH)) {
2078 2078
                         fwrite($fp, '//');
2079
-                        $force_dolibarr_lib_GEOIP_PATH = '';  // @phan-suppress-current-line PhanPluginRedundantAssignment
2079
+                        $force_dolibarr_lib_GEOIP_PATH = ''; // @phan-suppress-current-line PhanPluginRedundantAssignment
2080 2080
                     }
2081 2081
                     fwrite($fp, '$dolibarr_lib_GEOIP_PATH="' . dol_escape_php(dol_sanitizePathName($force_dolibarr_lib_GEOIP_PATH)) . '";');
2082 2082
                     fwrite($fp, "\n");
2083 2083
                     if (empty($force_dolibarr_lib_NUSOAP_PATH)) {
2084 2084
                         fwrite($fp, '//');
2085
-                        $force_dolibarr_lib_NUSOAP_PATH = '';  // @phan-suppress-current-line PhanPluginRedundantAssignment
2085
+                        $force_dolibarr_lib_NUSOAP_PATH = ''; // @phan-suppress-current-line PhanPluginRedundantAssignment
2086 2086
                     }
2087 2087
                     fwrite($fp, '$dolibarr_lib_NUSOAP_PATH="' . dol_escape_php(dol_sanitizePathName($force_dolibarr_lib_NUSOAP_PATH)) . '";');
2088 2088
                     fwrite($fp, "\n");
2089 2089
                     if (empty($force_dolibarr_lib_ODTPHP_PATH)) {
2090 2090
                         fwrite($fp, '//');
2091
-                        $force_dolibarr_lib_ODTPHP_PATH = '';  // @phan-suppress-current-line PhanPluginRedundantAssignment
2091
+                        $force_dolibarr_lib_ODTPHP_PATH = ''; // @phan-suppress-current-line PhanPluginRedundantAssignment
2092 2092
                     }
2093 2093
                     fwrite($fp, '$dolibarr_lib_ODTPHP_PATH="' . dol_escape_php(dol_sanitizePathName($force_dolibarr_lib_ODTPHP_PATH)) . '";');
2094 2094
                     fwrite($fp, "\n");
2095 2095
                     if (empty($force_dolibarr_lib_ODTPHP_PATHTOPCLZIP)) {
2096 2096
                         fwrite($fp, '//');
2097
-                        $force_dolibarr_lib_ODTPHP_PATHTOPCLZIP = '';  // @phan-suppress-current-line PhanPluginRedundantAssignment
2097
+                        $force_dolibarr_lib_ODTPHP_PATHTOPCLZIP = ''; // @phan-suppress-current-line PhanPluginRedundantAssignment
2098 2098
                     }
2099 2099
                     fwrite($fp, '$dolibarr_lib_ODTPHP_PATHTOPCLZIP="' . dol_escape_php(dol_sanitizePathName($force_dolibarr_lib_ODTPHP_PATHTOPCLZIP)) . '";');
2100 2100
                     fwrite($fp, "\n");
2101 2101
                     if (empty($force_dolibarr_js_CKEDITOR)) {
2102 2102
                         fwrite($fp, '//');
2103
-                        $force_dolibarr_js_CKEDITOR = '';  // @phan-suppress-current-line PhanPluginRedundantAssignment
2103
+                        $force_dolibarr_js_CKEDITOR = ''; // @phan-suppress-current-line PhanPluginRedundantAssignment
2104 2104
                     }
2105 2105
                     fwrite($fp, '$dolibarr_js_CKEDITOR=\'' . dol_escape_php($force_dolibarr_js_CKEDITOR, 1) . '\';');
2106 2106
                     fwrite($fp, "\n");
2107 2107
                     if (empty($force_dolibarr_js_JQUERY)) {
2108 2108
                         fwrite($fp, '//');
2109
-                        $force_dolibarr_js_JQUERY = '';  // @phan-suppress-current-line PhanPluginRedundantAssignment
2109
+                        $force_dolibarr_js_JQUERY = ''; // @phan-suppress-current-line PhanPluginRedundantAssignment
2110 2110
                     }
2111 2111
                     fwrite($fp, '$dolibarr_js_JQUERY=\'' . dol_escape_php($force_dolibarr_js_JQUERY, 1) . '\';');
2112 2112
                     fwrite($fp, "\n");
2113 2113
                     if (empty($force_dolibarr_js_JQUERY_UI)) {
2114 2114
                         fwrite($fp, '//');
2115
-                        $force_dolibarr_js_JQUERY_UI = '';  // @phan-suppress-current-line PhanPluginRedundantAssignment
2115
+                        $force_dolibarr_js_JQUERY_UI = ''; // @phan-suppress-current-line PhanPluginRedundantAssignment
2116 2116
                     }
2117 2117
                     fwrite($fp, '$dolibarr_js_JQUERY_UI=\'' . dol_escape_php($force_dolibarr_js_JQUERY_UI, 1) . '\';');
2118 2118
                     fwrite($fp, "\n");
@@ -2121,13 +2121,13 @@  discard block
 block discarded – undo
2121 2121
                     fwrite($fp, "\n");
2122 2122
                     if (empty($force_dolibarr_font_DOL_DEFAULT_TTF)) {
2123 2123
                         fwrite($fp, '//');
2124
-                        $force_dolibarr_font_DOL_DEFAULT_TTF = '';  // @phan-suppress-current-line PhanPluginRedundantAssignment
2124
+                        $force_dolibarr_font_DOL_DEFAULT_TTF = ''; // @phan-suppress-current-line PhanPluginRedundantAssignment
2125 2125
                     }
2126 2126
                     fwrite($fp, '$dolibarr_font_DOL_DEFAULT_TTF=\'' . dol_escape_php($force_dolibarr_font_DOL_DEFAULT_TTF, 1) . '\';');
2127 2127
                     fwrite($fp, "\n");
2128 2128
                     if (empty($force_dolibarr_font_DOL_DEFAULT_TTF_BOLD)) {
2129 2129
                         fwrite($fp, '//');
2130
-                        $force_dolibarr_font_DOL_DEFAULT_TTF_BOLD = '';  // @phan-suppress-current-line PhanPluginRedundantAssignment
2130
+                        $force_dolibarr_font_DOL_DEFAULT_TTF_BOLD = ''; // @phan-suppress-current-line PhanPluginRedundantAssignment
2131 2131
                     }
2132 2132
                     fwrite($fp, '$dolibarr_font_DOL_DEFAULT_TTF_BOLD=\'' . dol_escape_php($force_dolibarr_font_DOL_DEFAULT_TTF_BOLD, 1) . '\';');
2133 2133
                     fwrite($fp, "\n");
@@ -2447,9 +2447,9 @@  discard block
 block discarded – undo
2447 2447
 // Only works if you are not in safe_mode. / Ne fonctionne que si on est pas en safe_mode.
2448 2448
 
2449 2449
         $err = error_reporting();
2450
-        error_reporting(0);      // Disable all errors
2450
+        error_reporting(0); // Disable all errors
2451 2451
 //error_reporting(E_ALL);
2452
-        @set_time_limit(1800);   // Need 1800 on some very slow OS like Windows 7/64
2452
+        @set_time_limit(1800); // Need 1800 on some very slow OS like Windows 7/64
2453 2453
         error_reporting($err);
2454 2454
 
2455 2455
         $action = GETPOST('action', 'aZ09') ? GETPOST('action', 'aZ09') : (empty($argv[1]) ? '' : $argv[1]);
@@ -3003,7 +3003,7 @@  discard block
 block discarded – undo
3003 3003
 
3004 3004
         $conf->file->instance_unique_id = (empty($dolibarr_main_instance_unique_id) ? (empty($dolibarr_main_cookie_cryptkey) ? '' : $dolibarr_main_cookie_cryptkey) : $dolibarr_main_instance_unique_id); // Unique id of instance
3005 3005
 
3006
-        $hash_unique_id = dol_hash('dolibarr' . $conf->file->instance_unique_id, 'sha256');   // Note: if the global salt changes, this hash changes too so ping may be counted twice. We don't mind. It is for statistics purpose only.
3006
+        $hash_unique_id = dol_hash('dolibarr' . $conf->file->instance_unique_id, 'sha256'); // Note: if the global salt changes, this hash changes too so ping may be counted twice. We don't mind. It is for statistics purpose only.
3007 3007
 
3008 3008
         //$out = '<input type="checkbox" name="dolibarrpingno" id="dolibarrpingno"' . ((getDolGlobalString('MAIN_FIRST_PING_OK_ID') == 'disabled') ? '' : ' value="checked" checked="true"') . '> ';
3009 3009
 		$out = '<input type="checkbox" name="dolibarrpingno" id="dolibarrpingno"' . ((getDolGlobalString('MAIN_FIRST_PING_OK_ID') == 'disabled') ? '' : ' value="checked"') . '> ';
@@ -3277,7 +3277,7 @@  discard block
 block discarded – undo
3277 3277
             $db = getDoliDBInstance($conf->db->type, $conf->db->host, $conf->db->user, $conf->db->pass, $conf->db->name, (int) $conf->db->port);
3278 3278
 
3279 3279
 			$conf = Config::getConf();
3280
-			$config=Config::getConfig();
3280
+			$config = Config::getConfig();
3281 3281
 
3282 3282
 			// Create the global $hookmanager object
3283 3283
             // include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
@@ -3342,8 +3342,8 @@  discard block
 block discarded – undo
3342 3342
                     $newuser->admin = 1;
3343 3343
                     $newuser->entity = 0;
3344 3344
 
3345
-                    $conf->global->USER_MAIL_REQUIRED = 0;          // Force global option to be sure to create a new user with no email
3346
-                    $conf->global->USER_PASSWORD_GENERATED = '';    // To not use any rule for password validation
3345
+                    $conf->global->USER_MAIL_REQUIRED = 0; // Force global option to be sure to create a new user with no email
3346
+                    $conf->global->USER_PASSWORD_GENERATED = ''; // To not use any rule for password validation
3347 3347
 
3348 3348
                     $result = $newuser->create($createuser, 1);
3349 3349
 
Please login to merge, or discard this patch.
Dolibarr/Modules/User/Model/User.php 2 patches
Indentation   +4141 added lines, -4141 removed lines patch added patch discarded remove patch
@@ -55,4145 +55,4145 @@
 block discarded – undo
55 55
  */
56 56
 class User extends GenericDocument
57 57
 {
58
-	use CommonPeople;
59
-
60
-	const STATUS_DISABLED = 0;
61
-	const STATUS_ENABLED = 1;
62
-	/**
63
-	 * @var string ID to identify managed object
64
-	 */
65
-	public $element = 'user';
66
-	/**
67
-	 * @var string Name of table without prefix where object is stored
68
-	 */
69
-	public $table_element = 'user';
70
-	/**
71
-	 * @var string Field with ID of parent key if this field has a parent
72
-	 */
73
-	public $fk_element = 'fk_user';
74
-	/**
75
-	 * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
76
-	 * @var int
77
-	 */
78
-	public $ismultientitymanaged = 1;
79
-	/**
80
-	 * @var int  Does object support extrafields ? 0=No, 1=Yes
81
-	 */
82
-	public $isextrafieldmanaged = 1;
83
-	/**
84
-	 * @var string picto
85
-	 */
86
-	public $picto = 'user';
87
-	public $id = 0;
88
-	/**
89
-	 * @var User old copy of User
90
-	 */
91
-	public $oldcopy;
92
-	/**
93
-	 * @var int
94
-	 * @deprecated
95
-	 * @see $status
96
-	 */
97
-	public $statut;
98
-	public $status;
99
-	/**
100
-	 * @var string      Open ID
101
-	 */
102
-	public $openid;
103
-	public $ldap_sid;
104
-	public $search_sid;
105
-	public $employee;
106
-	public $civility_code;
107
-	/**
108
-	 * @var string fullname
109
-	 */
110
-	public $fullname;
111
-	/**
112
-	 * @var string gender
113
-	 */
114
-	public $gender;
115
-	public $birth;
116
-	/**
117
-	 * @var string email
118
-	 */
119
-	public $email;
120
-	/**
121
-	 * @var string email
122
-	 */
123
-	public $email_oauth2;
124
-	/**
125
-	 * @var string personal email
126
-	 */
127
-	public $personal_email;
128
-	/**
129
-	 * @var array array of socialnetwo18dprks
130
-	 */
131
-	public $socialnetworks;
132
-	/**
133
-	 * @var string job position
134
-	 */
135
-	public $job;
136
-	/**
137
-	 * @var string user signature
138
-	 */
139
-	public $signature;
140
-	/**
141
-	 * @var string office phone
142
-	 */
143
-	public $office_phone;
144
-	/**
145
-	 * @var string office fax
146
-	 */
147
-	public $office_fax;
148
-	/**
149
-	 * @var string phone mobile
150
-	 */
151
-	public $user_mobile;
152
-	/**
153
-	 * @var string personal phone mobile
154
-	 */
155
-	public $personal_mobile;
156
-	/**
157
-	 * @var int 1 if admin 0 if standard user
158
-	 */
159
-	public $admin;
160
-	/**
161
-	 * @var string user login
162
-	 */
163
-	public $login;
164
-	/**
165
-	 * @var string user apikey
166
-	 */
167
-	public $api_key;
168
-	/**
169
-	 * @var int Entity
170
-	 */
171
-	public $entity;
172
-	/**
173
-	 * @var string Clear password in memory
174
-	 */
175
-	public $pass;
176
-	/**
177
-	 * @var string Encrypted password in memory
178
-	 */
179
-	public $pass_crypted;
180
-	/**
181
-	 * @var string Clear password in database (defined if DATABASE_PWD_ENCRYPTED=0)
182
-	 */
183
-	public $pass_indatabase;
184
-	/**
185
-	 * @var string Encrypted password in database (always defined)
186
-	 */
187
-	public $pass_indatabase_crypted;
188
-	/**
189
-	 * @var string Temporary password
190
-	 */
191
-	public $pass_temp;
192
-	/**
193
-	 * Date creation record (datec)
194
-	 *
195
-	 * @var integer
196
-	 */
197
-	public $datec;
198
-	/**
199
-	 * Date modification record (tms)
200
-	 *
201
-	 * @var integer
202
-	 */
203
-	public $datem;
204
-	/**
205
-	 * @var int If this is defined, it is an external user
206
-	 */
207
-	public $socid;
208
-	/**
209
-	 * @var int If this is defined, it is a user created from a contact
210
-	 */
211
-	public $contact_id;
212
-	/**
213
-	 * @var int ID
214
-	 */
215
-	public $fk_member;
216
-	/**
217
-	 * @var int User ID of supervisor
218
-	 */
219
-	public $fk_user;
220
-	/**
221
-	 * @var int User ID of expense validator
222
-	 */
223
-	public $fk_user_expense_validator;
224
-	/**
225
-	 * @var int User ID of holidays validator
226
-	 */
227
-	public $fk_user_holiday_validator;
228
-	/**
229
-	 * @string clicktodial url
230
-	 */
231
-	public $clicktodial_url;
232
-	/**
233
-	 * @var string clicktodial login
234
-	 */
235
-	public $clicktodial_login;
236
-	/**
237
-	 * @var string clicktodial password
238
-	 */
239
-	public $clicktodial_password;
240
-	/**
241
-	 * @var string clicktodial poste
242
-	 */
243
-	public $clicktodial_poste;
244
-	/**
245
-	 * @var int     0 by default, 1 if click to dial data were already loaded for this user
246
-	 */
247
-	public $clicktodial_loaded;
248
-	public $datelastlogin;
249
-	public $datepreviouslogin;
250
-	public $flagdelsessionsbefore;
251
-	public $iplastlogin;
252
-	public $ippreviouslogin;
253
-	public $datestartvalidity;
254
-	public $dateendvalidity;
255
-	/**
256
-	 * @var string photo filename
257
-	 */
258
-	public $photo;
259
-	/**
260
-	 * @var string default language
261
-	 */
262
-	public $lang;
263
-	/**
264
-	 * @var stdClass Class of permissions user->rights->permx
265
-	 */
266
-	public $rights;
267
-	/**
268
-	 * @var int  All permissions are loaded
269
-	 */
270
-	public $all_permissions_are_loaded;
271
-	/**
272
-	 * @var int Number of rights granted to the user. Value loaded after a getrights().
273
-	 */
274
-	public $nb_rights;
275
-	/**
276
-	 * @var array   To store list of groups of user (used by API /info for example)
277
-	 */
278
-	public $user_group_list;
279
-	/**
280
-	 * @var stdClass To store personal config
281
-	 */
282
-	public $conf; // To store default values for user. Loaded by loadDefaultValues().
283
-	public $default_values; // To store current search criteria for user
284
-	public $lastsearch_values_tmp; // To store last saved search criteria for user
285
-	public $lastsearch_values;
286
-	/**
287
-	 * @var array<int,array{rowid:int,id:int,fk_user:int,fk_soc:int,firstname:string,lastname:string,login:string,statut:int,entity:string,email:string,gender:int,admin:string,photo:string,fullpath:string,fullname:string,level:int}>
288
-	 *       Store the entire hierarchy tree of users
289
-	 */
290
-	public $users = []; // To store an array of all parents for all ids.
291
-	public $parentof; // Cache array of already loaded children
292
-	public $accountancy_code; // Accountancy code in prevision of the complete accountancy module
293
-
294
-	public $thm; // Average cost of employee - Used for valuation of time spent
295
-	public $tjm; // Average cost of employee
296
-
297
-	public $salary; // Monthly salary       - Denormalized value from llx_user_employment
298
-	public $salaryextra; // Monthly salary extra - Denormalized value from llx_user_employment
299
-	public $weeklyhours; // Weekly hours         - Denormalized value from llx_user_employment
300
-
301
-	/**
302
-	 * @var string Define background color for user in agenda
303
-	 */
304
-	public $color;
305
-
306
-	public $dateemployment; // Define date of employment by company
307
-	public $dateemploymentend; // Define date of employment end by company
308
-
309
-	public $default_c_exp_tax_cat;
310
-
311
-	/**
312
-	 * @var string ref for employee
313
-	 */
314
-	public $ref_employee;
315
-
316
-	/**
317
-	 * @var string national registration number
318
-	 */
319
-	public $national_registration_number;
320
-
321
-	public $default_range;
322
-
323
-	/**
324
-	 * @var int id of warehouse
325
-	 */
326
-	public $fk_warehouse;
327
-
328
-	/**
329
-	 * @var int egroupware id
330
-	 */
331
-	public $egroupware_id;
332
-
333
-	public $fields = [
334
-		'rowid' => ['type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'index' => 1, 'position' => 1, 'comment' => 'Id'],
335
-		'lastname' => ['type' => 'varchar(50)', 'label' => 'Lastname', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'showoncombobox' => 1, 'index' => 1, 'position' => 20, 'searchall' => 1],
336
-		'firstname' => ['type' => 'varchar(50)', 'label' => 'Firstname', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'showoncombobox' => 1, 'index' => 1, 'position' => 10, 'searchall' => 1],
337
-		'ref_employee' => ['type' => 'varchar(50)', 'label' => 'RefEmployee', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'showoncombobox' => 1, 'index' => 1, 'position' => 30, 'searchall' => 1],
338
-		'national_registration_number' => ['type' => 'varchar(50)', 'label' => 'NationalRegistrationNumber', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'showoncombobox' => 1, 'index' => 1, 'position' => 40, 'searchall' => 1],
339
-	];
340
-	/**
341
-	 * @var array Cache array of already loaded permissions
342
-	 */
343
-	private $_tab_loaded = [];
344
-	private $cache_childids;
345
-	/**
346
-	 * Cache the SQL results of the function "findUserIdByEmail($email)"
347
-	 *
348
-	 * NOTE: findUserIdByEmailCache[...] === -1 means not found in database
349
-	 *
350
-	 * @var array
351
-	 */
352
-	private $findUserIdByEmailCache;
353
-
354
-	/**
355
-	 *    Constructor of the class
356
-	 *
357
-	 * @param DoliDB $db Database handler
358
-	 */
359
-	public function __construct($db)
360
-	{
361
-		$this->db = $db;
362
-
363
-		// User preference
364
-		$this->clicktodial_loaded = 0;
365
-
366
-		// For cache usage
367
-		$this->all_permissions_are_loaded = 0;
368
-		$this->nb_rights = 0;
369
-
370
-		// Force some default values
371
-		$this->admin = 0;
372
-		$this->employee = 1;
373
-
374
-		$this->conf = new stdClass();
375
-		$this->rights = new stdClass();
376
-		$this->rights->user = new stdClass();
377
-		$this->rights->user->user = new stdClass();
378
-		$this->rights->user->self = new stdClass();
379
-		$this->rights->user->user_advance = new stdClass();
380
-		$this->rights->user->self_advance = new stdClass();
381
-		$this->rights->user->group_advance = new stdClass();
382
-	}
383
-
384
-	/**
385
-	 * Function used to replace a thirdparty id with another one.
386
-	 *
387
-	 * @param DoliDB $dbs Database handler, because function is static we name it $dbs not $db to avoid breaking
388
-	 *                          coding test
389
-	 * @param int $origin_id Old thirdparty id
390
-	 * @param int $dest_id New thirdparty id
391
-	 *
392
-	 * @return  bool
393
-	 */
394
-	public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
395
-	{
396
-		$tables = [
397
-			'user',
398
-		];
399
-
400
-		return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
401
-	}
402
-
403
-	/**
404
-	 *  Add a right to the user
405
-	 *
406
-	 * @param int $rid Id of permission to add or 0 to add several permissions
407
-	 * @param string $allmodule Add all permissions of module $allmodule or 'allmodules' to include all modules.
408
-	 * @param string $allperms Add all permissions of module $allmodule, subperms $allperms only or '' to include all
409
-	 *                          permissions.
410
-	 * @param int $entity Entity to use
411
-	 * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers
412
-	 *
413
-	 * @return int                     > 0 if OK, < 0 if KO
414
-	 * @see    clearrights(), delrights(), getrights(), hasRight()
415
-	 */
416
-	public function addrights($rid, $allmodule = '', $allperms = '', $entity = 0, $notrigger = 0)
417
-	{
418
-		global $conf, $user, $langs;
419
-		global $config;
420
-		$dbPrefix = $config->db->prefix;
421
-
422
-		$entity = (empty($entity) ? $conf->entity : $entity);
423
-
424
-		dol_syslog(get_class($this) . "::addrights $rid, $allmodule, $allperms, $entity, $notrigger for user id=" . $this->id);
425
-
426
-		if (empty($this->id)) {
427
-			$this->error = 'Try to call addrights on an object user with an empty id';
428
-			return -1;
429
-		}
430
-
431
-		$error = 0;
432
-		$whereforadd = '';
433
-
434
-		$this->db->begin();
435
-
436
-		if (!empty($rid)) {
437
-			$module = $perms = $subperms = '';
438
-
439
-			// If we ask to add a given permission, we first load properties of this permission (module, perms and subperms).
440
-			$sql = "SELECT module, perms, subperms";
441
-			$sql .= " FROM " . $dbPrefix . "rights_def";
442
-			$sql .= " WHERE id = " . ((int)$rid);
443
-			$sql .= " AND entity = " . ((int)$entity);
444
-
445
-			$result = $this->db->query($sql);
446
-			if ($result) {
447
-				$obj = $this->db->fetch_object($result);
448
-
449
-				if ($obj) {
450
-					$module = $obj->module;
451
-					$perms = $obj->perms;
452
-					$subperms = $obj->subperms;
453
-				}
454
-			} else {
455
-				$error++;
456
-				dol_print_error($this->db);
457
-			}
458
-
459
-			// Define the where for the permission to add
460
-			$whereforadd = "id=" . ((int)$rid);
461
-			// Add also inherited permissions
462
-			if (!empty($subperms)) {
463
-				$whereforadd .= " OR (module='" . $this->db->escape($module) . "' AND perms='" . $this->db->escape($perms) . "' AND (subperms='lire' OR subperms='read'))";
464
-			} elseif (!empty($perms)) {
465
-				$whereforadd .= " OR (module='" . $this->db->escape($module) . "' AND (perms='lire' OR perms='read') AND subperms IS NULL)";
466
-			}
467
-		} else {
468
-			// A list of permission was requested (not a single specific permission)
469
-			// based on the name of a module of permissions
470
-			// Used in the where clause to determine the list of permissions to add.
471
-			if (!empty($allmodule)) {
472
-				if ($allmodule == 'allmodules') {
473
-					$whereforadd = 'allmodules';
474
-				} else {
475
-					$whereforadd = "module='" . $this->db->escape($allmodule) . "'";
476
-					if (!empty($allperms)) {
477
-						$whereforadd .= " AND perms='" . $this->db->escape($allperms) . "'";
478
-					}
479
-				}
480
-			}
481
-		}
482
-
483
-		// Add automatically other permission using the criteria whereforadd
484
-		// $whereforadd can be a SQL filter or the string 'allmodules'
485
-		if (!empty($whereforadd)) {
486
-			//print "$module-$perms-$subperms";
487
-			$sql = "SELECT id";
488
-			$sql .= " FROM " . $dbPrefix . "rights_def";
489
-			$sql .= " WHERE entity = " . ((int)$entity);
490
-			if (!empty($whereforadd) && $whereforadd != 'allmodules') {
491
-				$sql .= " AND (" . $whereforadd . ")";  // Note: parenthesis are important because whereforadd can contains OR. Also note that $whereforadd is already sanitized
492
-			}
493
-
494
-			$sqldelete = "DELETE FROM " . $dbPrefix . "user_rights";
495
-			$sqldelete .= " WHERE fk_user = " . ((int)$this->id) . " AND fk_id IN (";
496
-			$sqldelete .= $sql;
497
-			$sqldelete .= ") AND entity = " . ((int)$entity);
498
-			if (!$this->db->query($sqldelete)) {
499
-				$error++;
500
-			}
501
-
502
-			if (!$error) {
503
-				$resql = $this->db->query($sql);
504
-				if ($resql) {
505
-					$num = $this->db->num_rows($resql);
506
-					$i = 0;
507
-					while ($i < $num) {
508
-						$obj = $this->db->fetch_object($resql);
509
-
510
-						if ($obj) {
511
-							$nid = $obj->id;
512
-
513
-							$sql = "INSERT INTO " . $dbPrefix . "user_rights (entity, fk_user, fk_id) VALUES (" . ((int)$entity) . ", " . ((int)$this->id) . ", " . ((int)$nid) . ")";
514
-							if (!$this->db->query($sql)) {
515
-								$error++;
516
-							}
517
-						}
518
-
519
-						$i++;
520
-					}
521
-				} else {
522
-					$error++;
523
-					dol_print_error($this->db);
524
-				}
525
-			}
526
-		}
527
-
528
-		if (!$error && !$notrigger) {
529
-			$langs->load("other");
530
-			$this->context = ['audit' => $langs->trans("PermissionsAdd") . ($rid ? ' (id=' . $rid . ')' : '')];
531
-
532
-			// Call trigger
533
-			$result = $this->call_trigger('USER_MODIFY', $user);
534
-			if ($result < 0) {
535
-				$error++;
536
-			}
537
-			// End call triggers
538
-		}
539
-
540
-		if ($error) {
541
-			$this->db->rollback();
542
-			return -$error;
543
-		} else {
544
-			$this->db->commit();
545
-			return 1;
546
-		}
547
-	}
548
-
549
-	/**
550
-	 *  Remove a right to the user
551
-	 *
552
-	 * @param int $rid Id du droit a retirer
553
-	 * @param string $allmodule Retirer tous les droits du module allmodule
554
-	 * @param string $allperms Retirer tous les droits du module allmodule, perms allperms
555
-	 * @param int $entity Entity to use
556
-	 * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers
557
-	 *
558
-	 * @return int                 > 0 if OK, < 0 if OK
559
-	 * @see    clearrights(), addrights(), getrights(), hasRight()
560
-	 */
561
-	public function delrights($rid, $allmodule = '', $allperms = '', $entity = 0, $notrigger = 0)
562
-	{
563
-		global $conf, $user, $langs;
564
-
565
-		$error = 0;
566
-		$wherefordel = '';
567
-		$entity = (!empty($entity) ? $entity : $conf->entity);
568
-
569
-		$this->db->begin();
570
-
571
-		if (!empty($rid)) {
572
-			$module = $perms = $subperms = '';
573
-
574
-			// When the request is to delete a specific permissions, this gets the
575
-			// les charactis for the module, permissions and sub-permission of this permission.
576
-			$sql = "SELECT module, perms, subperms";
577
-			$sql .= " FROM " . $dbPrefix . "rights_def";
578
-			$sql .= " WHERE id = '" . $this->db->escape($rid) . "'";
579
-			$sql .= " AND entity = " . ((int)$entity);
580
-
581
-			$result = $this->db->query($sql);
582
-			if ($result) {
583
-				$obj = $this->db->fetch_object($result);
584
-
585
-				if ($obj) {
586
-					$module = $obj->module;
587
-					$perms = $obj->perms;
588
-					$subperms = $obj->subperms;
589
-				}
590
-			} else {
591
-				$error++;
592
-				dol_print_error($this->db);
593
-			}
594
-
595
-			// Where clause for the list of permissions to delete
596
-			$wherefordel = "id=" . ((int)$rid);
597
-			// Suppression des droits induits
598
-			if ($subperms == 'lire' || $subperms == 'read') {
599
-				$wherefordel .= " OR (module='" . $this->db->escape($module) . "' AND perms='" . $this->db->escape($perms) . "' AND subperms IS NOT NULL)";
600
-			}
601
-			if ($perms == 'lire' || $perms == 'read') {
602
-				$wherefordel .= " OR (module='" . $this->db->escape($module) . "')";
603
-			}
604
-		} else {
605
-			// The deletion of the permissions concerns the name of a module or
606
-			// list of permissions.
607
-			// Used in the Where clause to determine the list of permission to delete
608
-			if (!empty($allmodule)) {
609
-				if ($allmodule == 'allmodules') {
610
-					$wherefordel = 'allmodules';
611
-				} else {
612
-					$wherefordel = "module='" . $this->db->escape($allmodule) . "'";
613
-					if (!empty($allperms)) {
614
-						$wherefordel .= " AND perms='" . $this->db->escape($allperms) . "'";
615
-					}
616
-				}
617
-			}
618
-		}
619
-
620
-		// Suppression des droits selon critere defini dans wherefordel
621
-		if (!empty($wherefordel)) {
622
-			//print "$module-$perms-$subperms";
623
-			$sql = "SELECT id";
624
-			$sql .= " FROM " . $dbPrefix . "rights_def";
625
-			$sql .= " WHERE entity = " . ((int)$entity);
626
-			if (!empty($wherefordel) && $wherefordel != 'allmodules') {
627
-				$sql .= " AND (" . $wherefordel . ")";  // Note: parenthesis are important because wherefordel can contains OR. Also note that $wherefordel is already sanitized
628
-			}
629
-
630
-			// avoid admin can remove his own important rights
631
-			if ($this->admin == 1) {
632
-				$sql .= " AND id NOT IN (251, 252, 253, 254, 255, 256)"; // other users rights
633
-				$sql .= " AND id NOT IN (341, 342, 343, 344)"; // own rights
634
-				$sql .= " AND id NOT IN (351, 352, 353, 354)"; // groups rights
635
-				$sql .= " AND id NOT IN (358)"; // user export
636
-			}
637
-
638
-			$sqldelete = "DELETE FROM " . $dbPrefix . "user_rights";
639
-			$sqldelete .= " WHERE fk_user = " . ((int)$this->id) . " AND fk_id IN (";
640
-			$sqldelete .= $sql;
641
-			$sqldelete .= ")";
642
-			$sqldelete .= " AND entity = " . ((int)$entity);
643
-
644
-			$resql = $this->db->query($sqldelete);
645
-			if (!$resql) {
646
-				$error++;
647
-				dol_print_error($this->db);
648
-			}
649
-		}
650
-
651
-		if (!$error && !$notrigger) {
652
-			$langs->load("other");
653
-			$this->context = ['audit' => $langs->trans("PermissionsDelete") . ($rid ? ' (id=' . $rid . ')' : '')];
654
-
655
-			// Call trigger
656
-			$result = $this->call_trigger('USER_MODIFY', $user);
657
-			if ($result < 0) {
658
-				$error++;
659
-			}
660
-			// End call triggers
661
-		}
662
-
663
-		if ($error) {
664
-			$this->db->rollback();
665
-			return -$error;
666
-		} else {
667
-			$this->db->commit();
668
-			return 1;
669
-		}
670
-	}
671
-
672
-	/**
673
-	 *  Clear all permissions array of user
674
-	 *
675
-	 * @return void
676
-	 * @see    getrights(), hasRight()
677
-	 */
678
-	public function clearrights()
679
-	{
680
-		dol_syslog(get_class($this) . "::clearrights reset user->rights");
681
-		$this->rights = new stdClass();
682
-		$this->nb_rights = 0;
683
-		$this->all_permissions_are_loaded = 0;
684
-		$this->_tab_loaded = [];
685
-	}
686
-
687
-	/**
688
-	 *  Load permissions granted to a user->id into object user->rights
689
-	 *
690
-	 * @param string $moduletag Limit permission for a particular module ('' by default means load all permissions)
691
-	 * @param int $forcereload Force reload of permissions even if they were already loaded (ignore cache)
692
-	 *
693
-	 * @return void
694
-	 * @see    clearrights(), delrights(), addrights(), hasRight()
695
-	 */
696
-	public function getrights($moduletag = '', $forcereload = 0)
697
-	{
698
-		global $conf;
699
-
700
-		$alreadyloaded = false;
701
-
702
-		if (empty($forcereload)) {
703
-			if ($moduletag && isset($this->_tab_loaded[$moduletag]) && $this->_tab_loaded[$moduletag]) {
704
-				// Rights for this module are already loaded, so we leave
705
-				$alreadyloaded = true;
706
-			}
707
-
708
-			if (!empty($this->all_permissions_are_loaded)) {
709
-				// We already loaded all rights for this user, so we leave
710
-				$alreadyloaded = true;
711
-			}
712
-		}
713
-
714
-		// For avoid error
715
-		if (!isset($this->rights) || !is_object($this->rights)) {
716
-			$this->rights = new stdClass(); // For avoid error
717
-		}
718
-		if (!isset($this->rights->user) || !is_object($this->rights->user)) {
719
-			$this->rights->user = new stdClass(); // For avoid error
720
-		}
721
-
722
-		// Get permission of users + Get permissions of groups
723
-
724
-		if (!$alreadyloaded) {
725
-			// First user permissions
726
-			$sql = "SELECT DISTINCT r.module, r.perms, r.subperms";
727
-			$sql .= " FROM " . $dbPrefix . "user_rights as ur,";
728
-			$sql .= " " . $dbPrefix . "rights_def as r";
729
-			$sql .= " WHERE r.id = ur.fk_id";
730
-			if (getDolGlobalString('MULTICOMPANY_BACKWARD_COMPATIBILITY')) {
731
-				// on old version, we use entity defined into table r only
732
-				$sql .= " AND r.entity IN (0," . (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE') ? "1," : "") . $conf->entity . ")";
733
-			} else {
734
-				// On table r=rights_def, the unique key is (id, entity) because id is hard coded into module descriptor and insert during module activation.
735
-				// So we must include the filter on entity on both table r. and ur.
736
-				$sql .= " AND r.entity = " . ((int)$conf->entity) . " AND ur.entity = " . ((int)$conf->entity);
737
-			}
738
-			$sql .= " AND ur.fk_user= " . ((int)$this->id);
739
-			$sql .= " AND r.perms IS NOT NULL";
740
-			if (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) {
741
-				$sql .= " AND r.perms NOT LIKE '%_advance'"; // Hide advanced perms if option is not enabled
742
-			}
743
-			if ($moduletag) {
744
-				$sql .= " AND r.module = '" . $this->db->escape($moduletag) . "'";
745
-			}
746
-
747
-			$resql = $this->db->query($sql);
748
-			if ($resql) {
749
-				$num = $this->db->num_rows($resql);
750
-				$i = 0;
751
-				while ($i < $num) {
752
-					$obj = $this->db->fetch_object($resql);
753
-
754
-					if ($obj) {
755
-						$module = $obj->module;
756
-						$perms = $obj->perms;
757
-						$subperms = $obj->subperms;
758
-
759
-						if (!empty($perms)) {
760
-							if (!empty($module)) {
761
-								if (!isset($this->rights->$module) || !is_object($this->rights->$module)) {
762
-									$this->rights->$module = new stdClass();
763
-								}
764
-								if (!empty($subperms)) {
765
-									if (!isset($this->rights->$module->$perms) || !is_object($this->rights->$module->$perms)) {
766
-										$this->rights->$module->$perms = new stdClass();
767
-									}
768
-									if (empty($this->rights->$module->$perms->$subperms)) {
769
-										$this->nb_rights++;
770
-									}
771
-									$this->rights->$module->$perms->$subperms = 1;
772
-								} else {
773
-									if (empty($this->rights->$module->$perms)) {
774
-										$this->nb_rights++;
775
-									}
776
-									$this->rights->$module->$perms = 1;
777
-								}
778
-							}
779
-						}
780
-					}
781
-					$i++;
782
-				}
783
-				$this->db->free($resql);
784
-			}
785
-
786
-			// Now permissions of groups
787
-			$sql = "SELECT DISTINCT r.module, r.perms, r.subperms";
788
-			$sql .= " FROM " . $dbPrefix . "usergroup_rights as gr,";
789
-			$sql .= " " . $dbPrefix . "usergroup_user as gu,";
790
-			$sql .= " " . $dbPrefix . "rights_def as r";
791
-			$sql .= " WHERE r.id = gr.fk_id";
792
-			// A very strange business rules. Must be same than into user->getrights() user/perms.php and user/group/perms.php
793
-			if (getDolGlobalString('MULTICOMPANY_BACKWARD_COMPATIBILITY')) {
794
-				if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
795
-					$sql .= " AND gu.entity IN (0," . $conf->entity . ")";
796
-				} else {
797
-					$sql .= " AND r.entity = " . ((int)$conf->entity);
798
-				}
799
-			} else {
800
-				$sql .= " AND gr.entity = " . ((int)$conf->entity);  // Only groups created in current entity
801
-				// The entity on the table usergroup_user should be useless and should never be used because it is already into gr and r.
802
-				// but when using MULTICOMPANY_TRANSVERSE_MODE, we may insert record that make rubbish result due to duplicate record of
803
-				// other entities, so we are forced to add a filter here
804
-				$sql .= " AND gu.entity IN (0," . $conf->entity . ")";
805
-				$sql .= " AND r.entity = " . ((int)$conf->entity);   // Only permission of modules enabled in current entity
806
-			}
807
-			// End of strange business rule
808
-			$sql .= " AND gr.fk_usergroup = gu.fk_usergroup";
809
-			$sql .= " AND gu.fk_user = " . ((int)$this->id);
810
-			$sql .= " AND r.perms IS NOT NULL";
811
-			if ($moduletag) {
812
-				$sql .= " AND r.module = '" . $this->db->escape($moduletag) . "'";
813
-			}
814
-
815
-			$resql = $this->db->query($sql);
816
-			if ($resql) {
817
-				$num = $this->db->num_rows($resql);
818
-				$i = 0;
819
-				while ($i < $num) {
820
-					$obj = $this->db->fetch_object($resql);
821
-
822
-					if ($obj) {
823
-						$module = $obj->module;
824
-						$perms = $obj->perms;
825
-						$subperms = $obj->subperms;
826
-
827
-						if (!empty($perms)) {
828
-							if (!empty($module)) {
829
-								if (!isset($this->rights->$module) || !is_object($this->rights->$module)) {
830
-									$this->rights->$module = new stdClass();
831
-								}
832
-								if (!empty($subperms)) {
833
-									if (!isset($this->rights->$module->$perms) || !is_object($this->rights->$module->$perms)) {
834
-										$this->rights->$module->$perms = new stdClass();
835
-									}
836
-									if (empty($this->rights->$module->$perms->$subperms)) {
837
-										$this->nb_rights++;
838
-									}
839
-									$this->rights->$module->$perms->$subperms = 1;
840
-								} else {
841
-									if (empty($this->rights->$module->$perms)) {
842
-										$this->nb_rights++;
843
-									}
844
-									// if we have already define a subperm like this $this->rights->$module->level1->level2 with llx_user_rights, we don't want override level1 because the level2 can be not define on user group
845
-									if (!isset($this->rights->$module->$perms) || !is_object($this->rights->$module->$perms)) {
846
-										$this->rights->$module->$perms = 1;
847
-									}
848
-								}
849
-							}
850
-						}
851
-					}
852
-					$i++;
853
-				}
854
-				$this->db->free($resql);
855
-			}
856
-
857
-			// Force permission on user for admin
858
-			if (!empty($this->admin)) {
859
-				if (empty($this->rights->user->user)) {
860
-					$this->rights->user->user = new stdClass();
861
-				}
862
-				$listofpermtotest = ['lire', 'creer', 'password', 'supprimer', 'export'];
863
-				foreach ($listofpermtotest as $permtotest) {
864
-					if (empty($this->rights->user->user->$permtotest)) {
865
-						$this->rights->user->user->$permtotest = 1;
866
-						$this->nb_rights++;
867
-					}
868
-				}
869
-				if (empty($this->rights->user->self)) {
870
-					$this->rights->user->self = new stdClass();
871
-				}
872
-				$listofpermtotest = ['creer', 'password'];
873
-				foreach ($listofpermtotest as $permtotest) {
874
-					if (empty($this->rights->user->self->$permtotest)) {
875
-						$this->rights->user->self->$permtotest = 1;
876
-						$this->nb_rights++;
877
-					}
878
-				}
879
-				// Add test on advanced permissions
880
-				if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) {
881
-					if (empty($this->rights->user->user_advance)) {
882
-						$this->rights->user->user_advance = new stdClass();
883
-					}
884
-					$listofpermtotest = ['readperms', 'write'];
885
-					foreach ($listofpermtotest as $permtotest) {
886
-						if (empty($this->rights->user->user_advance->$permtotest)) {
887
-							$this->rights->user->user_advance->$permtotest = 1;
888
-							$this->nb_rights++;
889
-						}
890
-					}
891
-					if (empty($this->rights->user->self_advance)) {
892
-						$this->rights->user->self_advance = new stdClass();
893
-					}
894
-					$listofpermtotest = ['readperms', 'writeperms'];
895
-					foreach ($listofpermtotest as $permtotest) {
896
-						if (empty($this->rights->user->self_advance->$permtotest)) {
897
-							$this->rights->user->self_advance->$permtotest = 1;
898
-							$this->nb_rights++;
899
-						}
900
-					}
901
-					if (empty($this->rights->user->group_advance)) {
902
-						$this->rights->user->group_advance = new stdClass();
903
-					}
904
-					$listofpermtotest = ['read', 'readperms', 'write', 'delete'];
905
-					foreach ($listofpermtotest as $permtotest) {
906
-						if (empty($this->rights->user) || empty($this->rights->user->group_advance->$permtotest)) {
907
-							$this->rights->user->group_advance->$permtotest = 1;
908
-							$this->nb_rights++;
909
-						}
910
-					}
911
-				}
912
-			}
913
-
914
-			// For backward compatibility
915
-			if (isset($this->rights->propale) && !isset($this->rights->propal)) {
916
-				$this->rights->propal = $this->rights->propale;
917
-			}
918
-			if (isset($this->rights->propal) && !isset($this->rights->propale)) {
919
-				$this->rights->propale = $this->rights->propal;
920
-			}
921
-
922
-			if (!$moduletag) {
923
-				// If the module was not define, then everything is loaded.
924
-				// Therefore, we can consider that the permissions are cached
925
-				// because they were all loaded for this user instance.
926
-				$this->all_permissions_are_loaded = 1;
927
-			} else {
928
-				// If the module is defined, we flag it as loaded into cache
929
-				$this->_tab_loaded[$moduletag] = 1;
930
-			}
931
-		}
932
-	}
933
-
934
-	/**
935
-	 *  Change status of a user
936
-	 *
937
-	 * @param int $status Status to set
938
-	 *
939
-	 * @return int                 Return integer <0 if KO, 0 if nothing is done, >0 if OK
940
-	 */
941
-	public function setstatus($status)
942
-	{
943
-		global $conf, $langs, $user;
944
-
945
-		$error = 0;
946
-
947
-		// Check parameters
948
-		if (isset($this->statut)) {
949
-			if ($this->statut == $status) {
950
-				return 0;
951
-			}
952
-		} elseif (isset($this->status) && $this->status == $status) {
953
-			return 0;
954
-		}
955
-
956
-		$this->db->begin();
957
-
958
-		// Save in database
959
-		$sql = "UPDATE " . $dbPrefix . "user";
960
-		$sql .= " SET statut = " . ((int)$status);
961
-		$sql .= " WHERE rowid = " . ((int)$this->id);
962
-		$result = $this->db->query($sql);
963
-
964
-		dol_syslog(get_class($this) . "::setstatus", LOG_DEBUG);
965
-		if ($result) {
966
-			if ($status == 0) {
967
-				$this->context['actionmsg'] = 'User ' . $this->login . ' disabled';
968
-			} else {
969
-				$this->context['actionmsg'] = 'User ' . $this->login . ' enabled';
970
-			}
971
-			// Call trigger
972
-			$result = $this->call_trigger('USER_ENABLEDISABLE', $user);
973
-			if ($result < 0) {
974
-				$error++;
975
-			}
976
-			// End call triggers
977
-		}
978
-
979
-		if ($error) {
980
-			$this->db->rollback();
981
-			return -$error;
982
-		} else {
983
-			$this->status = $status;
984
-			$this->statut = $status;
985
-			$this->db->commit();
986
-			return 1;
987
-		}
988
-	}
989
-
990
-	/**
991
-	 * Sets object to supplied categories.
992
-	 *
993
-	 * Deletes object from existing categories not supplied.
994
-	 * Adds it to non existing supplied categories.
995
-	 * Existing categories are left untouch.
996
-	 *
997
-	 * @param int[]|int $categories Category or categories IDs
998
-	 *
999
-	 * @return  int                         Return integer <0 if KO, >0 if OK
1000
-	 */
1001
-	public function setCategories($categories)
1002
-	{
1003
-		return parent::setCategoriesCommon($categories, Categorie::TYPE_USER);
1004
-	}
1005
-
1006
-	/**
1007
-	 *  Delete the user
1008
-	 *
1009
-	 * @param User $user User than delete
1010
-	 *
1011
-	 * @return     int             Return integer <0 if KO, >0 if OK
1012
-	 */
1013
-	public function delete(User $user)
1014
-	{
1015
-		global $conf, $langs;
1016
-
1017
-		$error = 0;
1018
-
1019
-		$this->db->begin();
1020
-
1021
-		$this->fetch($this->id);
1022
-
1023
-		dol_syslog(get_class($this) . "::delete", LOG_DEBUG);
1024
-
1025
-		// Remove rights
1026
-		$sql = "DELETE FROM " . $dbPrefix . "user_rights WHERE fk_user = " . ((int)$this->id);
1027
-
1028
-		if (!$error && !$this->db->query($sql)) {
1029
-			$error++;
1030
-			$this->error = $this->db->lasterror();
1031
-		}
1032
-
1033
-		// Remove group
1034
-		$sql = "DELETE FROM " . $dbPrefix . "usergroup_user WHERE fk_user  = " . ((int)$this->id);
1035
-		if (!$error && !$this->db->query($sql)) {
1036
-			$error++;
1037
-			$this->error = $this->db->lasterror();
1038
-		}
1039
-
1040
-		// Remove params
1041
-		$sql = "DELETE FROM " . $dbPrefix . "user_param WHERE fk_user  = " . ((int)$this->id);
1042
-		if (!$error && !$this->db->query($sql)) {
1043
-			$error++;
1044
-			$this->error = $this->db->lasterror();
1045
-		}
1046
-
1047
-		// If contact, remove link
1048
-		if ($this->contact_id > 0) {
1049
-			$sql = "UPDATE " . $dbPrefix . "socpeople SET fk_user_creat = null WHERE rowid = " . ((int)$this->contact_id);
1050
-			if (!$error && !$this->db->query($sql)) {
1051
-				$error++;
1052
-				$this->error = $this->db->lasterror();
1053
-			}
1054
-		}
1055
-
1056
-		// Remove extrafields
1057
-		if (!$error) {
1058
-			$result = $this->deleteExtraFields();
1059
-			if ($result < 0) {
1060
-				$error++;
1061
-				dol_syslog(get_class($this) . "::delete error -4 " . $this->error, LOG_ERR);
1062
-			}
1063
-		}
1064
-
1065
-		// Remove user
1066
-		if (!$error) {
1067
-			$sql = "DELETE FROM " . $dbPrefix . "user WHERE rowid = " . ((int)$this->id);
1068
-			dol_syslog(get_class($this) . "::delete", LOG_DEBUG);
1069
-			if (!$this->db->query($sql)) {
1070
-				$error++;
1071
-				$this->error = $this->db->lasterror();
1072
-			}
1073
-		}
1074
-
1075
-		if (!$error) {
1076
-			// Call trigger
1077
-			$result = $this->call_trigger('USER_DELETE', $user);
1078
-			if ($result < 0) {
1079
-				$error++;
1080
-				$this->db->rollback();
1081
-				return -1;
1082
-			}
1083
-			// End call triggers
1084
-
1085
-			$this->db->commit();
1086
-			return 1;
1087
-		} else {
1088
-			$this->db->rollback();
1089
-			return -1;
1090
-		}
1091
-	}
1092
-
1093
-	/**
1094
-	 *  Load a user from database with its id or ref (login).
1095
-	 *  This function does not load permissions, only user properties. Use getrights() for this just after the fetch.
1096
-	 *
1097
-	 * @param int $id If defined, id to used for search
1098
-	 * @param string $login If defined, login to used for search
1099
-	 * @param string $sid If defined, sid to used for search
1100
-	 * @param int $loadpersonalconf 1=also load personal conf of user (in $user->conf->xxx), 0=do not load personal
1101
-	 *                                 conf.
1102
-	 * @param int $entity If a value is >= 0, we force the search on a specific entity. If -1, means
1103
-	 *                                 search depens on default setup.
1104
-	 * @param string $email If defined, email to used for search
1105
-	 * @param int $fk_socpeople If defined, id of contact for search
1106
-	 * @param int $use_email_oauth2 1=Use also email_oauth2 to fetch on email
1107
-	 *
1108
-	 * @return int                         Return integer <0 if KO, 0 not found, >0 if OK
1109
-	 */
1110
-	public function fetch($id = 0, $login = '', $sid = '', $loadpersonalconf = 0, $entity = -1, $email = '', $fk_socpeople = 0, $use_email_oauth2 = 0)
1111
-	{
1112
-		global $conf, $user;
1113
-		global $config;
1114
-		$dbPrefix = $config->db->prefix;
1115
-
1116
-		// Clean parameters
1117
-		$login = trim($login);
1118
-
1119
-		// Get user
1120
-		$sql = "SELECT u.rowid, u.lastname, u.firstname, u.employee, u.gender, u.civility as civility_code, u.birth, u.job,";
1121
-		$sql .= " u.email, u.email_oauth2, u.personal_email,";
1122
-		$sql .= " u.socialnetworks,";
1123
-		$sql .= " u.signature, u.office_phone, u.office_fax, u.user_mobile, u.personal_mobile,";
1124
-		$sql .= " u.address, u.zip, u.town, u.fk_state as state_id, u.fk_country as country_id,";
1125
-		$sql .= " u.admin, u.login, u.note_private, u.note_public,";
1126
-		$sql .= " u.pass, u.pass_crypted, u.pass_temp, u.api_key,";
1127
-		$sql .= " u.fk_soc, u.fk_socpeople, u.fk_member, u.fk_user, u.ldap_sid, u.fk_user_expense_validator, u.fk_user_holiday_validator,";
1128
-		$sql .= " u.statut as status, u.lang, u.entity,";
1129
-		$sql .= " u.datec as datec,";
1130
-		$sql .= " u.tms as datem,";
1131
-		$sql .= " u.datelastlogin as datel,";
1132
-		$sql .= " u.datepreviouslogin as datep,";
1133
-		$sql .= " u.flagdelsessionsbefore,";
1134
-		$sql .= " u.iplastlogin,";
1135
-		$sql .= " u.ippreviouslogin,";
1136
-		$sql .= " u.datelastpassvalidation,";
1137
-		$sql .= " u.datestartvalidity,";
1138
-		$sql .= " u.dateendvalidity,";
1139
-		$sql .= " u.photo as photo,";
1140
-		$sql .= " u.openid as openid,";
1141
-		$sql .= " u.accountancy_code,";
1142
-		$sql .= " u.thm,";
1143
-		$sql .= " u.tjm,";
1144
-		$sql .= " u.salary,";
1145
-		$sql .= " u.salaryextra,";
1146
-		$sql .= " u.weeklyhours,";
1147
-		$sql .= " u.color,";
1148
-		$sql .= " u.dateemployment, u.dateemploymentend,";
1149
-		$sql .= " u.fk_warehouse,";
1150
-		$sql .= " u.ref_ext,";
1151
-		$sql .= " u.default_range, u.default_c_exp_tax_cat,"; // Expense report default mode
1152
-		$sql .= " u.national_registration_number,";
1153
-		$sql .= " u.ref_employee,";
1154
-		$sql .= " c.code as country_code, c.label as country,";
1155
-		$sql .= " d.code_departement as state_code, d.nom as state";
1156
-		$sql .= " FROM " . $dbPrefix . "user as u";
1157
-		$sql .= " LEFT JOIN " . $dbPrefix . "c_country as c ON u.fk_country = c.rowid";
1158
-		$sql .= " LEFT JOIN " . $dbPrefix . "c_departements as d ON u.fk_state = d.rowid";
1159
-
1160
-		if ($entity < 0) {
1161
-			if ((!isModEnabled('multicompany') || !getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) && (!empty($user->entity))) {
1162
-				$sql .= " WHERE u.entity IN (0, " . ((int)$conf->entity) . ")";
1163
-			} else {
1164
-				$sql .= " WHERE u.entity IS NOT NULL"; // multicompany is on in transverse mode or user making fetch is on entity 0, so user is allowed to fetch anywhere into database
1165
-			}
1166
-		} else {
1167
-			// The fetch was forced on an entity
1168
-			if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
1169
-				$sql .= " WHERE u.entity IS NOT NULL"; // multicompany is on in transverse mode or user making fetch is on entity 0, so user is allowed to fetch anywhere into database
1170
-			} else {
1171
-				$sql .= " WHERE u.entity IN (0, " . ((int)(($entity != '' && $entity >= 0) ? $entity : $conf->entity)) . ")"; // search in entity provided in parameter
1172
-			}
1173
-		}
1174
-
1175
-		if ($sid) {
1176
-			// permet une recherche du user par son SID ActiveDirectory ou Samba
1177
-			$sql .= " AND (u.ldap_sid = '" . $this->db->escape($sid) . "' OR u.login = '" . $this->db->escape($login) . "')";
1178
-		} elseif ($login) {
1179
-			$sql .= " AND u.login = '" . $this->db->escape($login) . "'";
1180
-		} elseif ($email) {
1181
-			$sql .= " AND (u.email = '" . $this->db->escape($email) . "'";
1182
-			if ($use_email_oauth2) {
1183
-				$sql .= " OR u.email_oauth2 = '" . $this->db->escape($email) . "'";
1184
-			}
1185
-			$sql .= ")";
1186
-		} elseif ($fk_socpeople > 0) {
1187
-			$sql .= " AND u.fk_socpeople = " . ((int)$fk_socpeople);
1188
-		} else {
1189
-			$sql .= " AND u.rowid = " . ((int)$id);
1190
-		}
1191
-		$sql .= " ORDER BY u.entity ASC"; // Avoid random result when there is 2 login in 2 different entities
1192
-
1193
-		if ($sid) {
1194
-			// permet une recherche du user par son SID ActiveDirectory ou Samba
1195
-			$sql .= ' ' . $this->db->plimit(1);
1196
-		}
1197
-
1198
-		$resql = $this->db->query($sql);
1199
-		if ($resql) {
1200
-			$num = $this->db->num_rows($resql);
1201
-			if ($num > 1) {
1202
-				$this->error = "USERDUPLICATEFOUND";
1203
-				dol_syslog(get_class($this) . "::fetch more than 1 user found", LOG_WARNING);
1204
-
1205
-				$this->db->free($resql);
1206
-				return 0;
1207
-			}
1208
-
1209
-			$obj = $this->db->fetch_object($resql);
1210
-			if ($obj) {
1211
-				$this->id = $obj->rowid;
1212
-				$this->ref = $obj->rowid;
1213
-
1214
-				$this->ref_ext = $obj->ref_ext;
1215
-
1216
-				$this->ldap_sid = $obj->ldap_sid;
1217
-				$this->civility_code = $obj->civility_code;
1218
-				$this->lastname = $obj->lastname;
1219
-				$this->firstname = $obj->firstname;
1220
-				$this->ref_employee = $obj->ref_employee;
1221
-				$this->national_registration_number = $obj->national_registration_number;
1222
-
1223
-				$this->employee = $obj->employee;
1224
-
1225
-				$this->login = $obj->login;
1226
-				$this->gender = $obj->gender;
1227
-				$this->birth = $this->db->jdate($obj->birth);
1228
-				$this->pass_indatabase = $obj->pass;
1229
-				$this->pass_indatabase_crypted = $obj->pass_crypted;
1230
-				$this->pass = $obj->pass;
1231
-				$this->pass_temp = $obj->pass_temp;
1232
-				$this->api_key = dolDecrypt($obj->api_key);
1233
-
1234
-				$this->address = $obj->address;
1235
-				$this->zip = $obj->zip;
1236
-				$this->town = $obj->town;
1237
-
1238
-				$this->country_id = $obj->country_id;
1239
-				$this->country_code = $obj->country_id ? $obj->country_code : '';
1240
-				//$this->country = $obj->country_id?($langs->trans('Country'.$obj->country_code)!='Country'.$obj->country_code?$langs->transnoentities('Country'.$obj->country_code):$obj->country):'';
1241
-
1242
-				$this->state_id = $obj->state_id;
1243
-				$this->state_code = $obj->state_code;
1244
-				$this->state = ($obj->state != '-' ? $obj->state : '');
1245
-
1246
-				$this->office_phone = $obj->office_phone;
1247
-				$this->office_fax = $obj->office_fax;
1248
-				$this->user_mobile = $obj->user_mobile;
1249
-				$this->personal_mobile = $obj->personal_mobile;
1250
-				$this->email = $obj->email;
1251
-				$this->email_oauth2 = $obj->email_oauth2;
1252
-				$this->personal_email = $obj->personal_email;
1253
-				$this->socialnetworks = ($obj->socialnetworks ? (array)json_decode($obj->socialnetworks, true) : []);
1254
-				$this->job = $obj->job;
1255
-				$this->signature = $obj->signature;
1256
-				$this->admin = $obj->admin;
1257
-				$this->note_public = $obj->note_public;
1258
-				$this->note_private = $obj->note_private;
1259
-				$this->note = $obj->note_private;   // deprecated
1260
-
1261
-				$this->statut = $obj->status;         // deprecated
1262
-				$this->status = $obj->status;
1263
-
1264
-				$this->photo = $obj->photo;
1265
-				$this->openid = $obj->openid;
1266
-				$this->lang = $obj->lang;
1267
-				$this->entity = $obj->entity;
1268
-				$this->accountancy_code = $obj->accountancy_code;
1269
-				$this->thm = $obj->thm;
1270
-				$this->tjm = $obj->tjm;
1271
-				$this->salary = $obj->salary;
1272
-				$this->salaryextra = $obj->salaryextra;
1273
-				$this->weeklyhours = $obj->weeklyhours;
1274
-				$this->color = $obj->color;
1275
-				$this->dateemployment = $this->db->jdate($obj->dateemployment);
1276
-				$this->dateemploymentend = $this->db->jdate($obj->dateemploymentend);
1277
-
1278
-				$this->datec = $this->db->jdate($obj->datec);
1279
-				$this->datem = $this->db->jdate($obj->datem);
1280
-				$this->datelastlogin = $this->db->jdate($obj->datel);
1281
-				$this->datepreviouslogin = $this->db->jdate($obj->datep);
1282
-				$this->flagdelsessionsbefore = $this->db->jdate($obj->flagdelsessionsbefore, 'gmt');
1283
-				$this->iplastlogin = $obj->iplastlogin;
1284
-				$this->ippreviouslogin = $obj->ippreviouslogin;
1285
-				$this->datestartvalidity = $this->db->jdate($obj->datestartvalidity);
1286
-				$this->dateendvalidity = $this->db->jdate($obj->dateendvalidity);
1287
-
1288
-				$this->socid = $obj->fk_soc;
1289
-				$this->contact_id = $obj->fk_socpeople;
1290
-				$this->fk_member = $obj->fk_member;
1291
-				$this->fk_user = $obj->fk_user;
1292
-				$this->fk_user_expense_validator = $obj->fk_user_expense_validator;
1293
-				$this->fk_user_holiday_validator = $obj->fk_user_holiday_validator;
1294
-
1295
-				$this->default_range = $obj->default_range;
1296
-				$this->default_c_exp_tax_cat = $obj->default_c_exp_tax_cat;
1297
-				$this->fk_warehouse = $obj->fk_warehouse;
1298
-
1299
-				// Protection when module multicompany was set, admin was set to first entity and then, the module was disabled,
1300
-				// in such case, this admin user must be admin for ALL entities.
1301
-				if (!isModEnabled('multicompany') && $this->admin && $this->entity == 1) {
1302
-					$this->entity = 0;
1303
-				}
1304
-
1305
-				// Retrieve all extrafield
1306
-				// fetch optionals attributes and labels
1307
-				$this->fetch_optionals();
1308
-
1309
-				$this->db->free($resql);
1310
-			} else {
1311
-				$this->error = "USERNOTFOUND";
1312
-				dol_syslog(get_class($this) . "::fetch user not found", LOG_DEBUG);
1313
-
1314
-				$this->db->free($resql);
1315
-				return 0;
1316
-			}
1317
-		} else {
1318
-			$this->error = $this->db->lasterror();
1319
-			return -1;
1320
-		}
1321
-
1322
-		// To get back the global configuration unique to the user
1323
-		if ($loadpersonalconf) {
1324
-			$result = $this->loadPersonalConf();
1325
-
1326
-			$result = $this->loadDefaultValues();
1327
-
1328
-			if ($result < 0) {
1329
-				$this->error = $this->db->lasterror();
1330
-				return -3;
1331
-			}
1332
-		}
1333
-
1334
-		return 1;
1335
-	}
1336
-
1337
-	/**
1338
-	 *  Load const values from database table user_param and set it into user->conf->XXX
1339
-	 *
1340
-	 * @return int                     >= 0 if OK, < 0 if KO
1341
-	 */
1342
-	public function loadPersonalConf()
1343
-	{
1344
-		global $conf;
1345
-
1346
-		// Load user->conf for user
1347
-		$sql = "SELECT param, value FROM " . $dbPrefix . "user_param";
1348
-		$sql .= " WHERE fk_user = " . ((int)$this->id);
1349
-		$sql .= " AND entity = " . ((int)$conf->entity);
1350
-		//dol_syslog(get_class($this).'::fetch load personalized conf', LOG_DEBUG);
1351
-		$resql = $this->db->query($sql);
1352
-		if ($resql) {
1353
-			$num = $this->db->num_rows($resql);
1354
-			$i = 0;
1355
-			while ($i < $num) {
1356
-				$obj = $this->db->fetch_object($resql);
1357
-				$p = (!empty($obj->param) ? $obj->param : '');
1358
-				if (!empty($p)) {
1359
-					$this->conf->$p = $obj->value;
1360
-				}
1361
-				$i++;
1362
-			}
1363
-			$this->db->free($resql);
1364
-
1365
-			return $num;
1366
-		} else {
1367
-			$this->error = $this->db->lasterror();
1368
-
1369
-			return -2;
1370
-		}
1371
-	}
1372
-
1373
-	/**
1374
-	 *  Load default values from database table into property ->default_values
1375
-	 *
1376
-	 * @return int                     > 0 if OK, < 0 if KO
1377
-	 */
1378
-	public function loadDefaultValues()
1379
-	{
1380
-		global $conf;
1381
-
1382
-		if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
1383
-			// Load user->default_values for user. TODO Save this in memcached ?
1384
-
1385
-			$defaultValues = new DefaultValues($this->db);
1386
-			$result = $defaultValues->fetchAll('', '', 0, 0, '(t.user_id:in:0,' . $this->id . ') AND (entity:in:' . (isset($this->entity) ? $this->entity : $conf->entity) . ',' . $conf->entity . ')');    // User 0 (all) + me (if defined)
1387
-			//$result = $defaultValues->fetchAll('', '', 0, 0, array('t.user_id'=>array(0, $this->id), 'entity'=>array((isset($this->entity) ? $this->entity : $conf->entity), $conf->entity)));    // User 0 (all) + me (if defined)
1388
-
1389
-			if (!is_array($result) && $result < 0) {
1390
-				setEventMessages($defaultValues->error, $defaultValues->errors, 'errors');
1391
-				dol_print_error($this->db);
1392
-				return -1;
1393
-			} elseif (count($result) > 0) {
1394
-				foreach ($result as $defval) {
1395
-					if (!empty($defval->page) && !empty($defval->type) && !empty($defval->param)) {
1396
-						$pagewithoutquerystring = $defval->page;
1397
-						$pagequeries = '';
1398
-						$reg = [];
1399
-						if (preg_match('/^([^\?]+)\?(.*)$/', $pagewithoutquerystring, $reg)) {    // There is query param
1400
-							$pagewithoutquerystring = $reg[1];
1401
-							$pagequeries = $reg[2];
1402
-						}
1403
-						$this->default_values[$pagewithoutquerystring][$defval->type][$pagequeries ? $pagequeries : '_noquery_'][$defval->param] = $defval->value;
1404
-					}
1405
-				}
1406
-			}
1407
-			if (!empty($this->default_values)) {
1408
-				foreach ($this->default_values as $a => $b) {
1409
-					foreach ($b as $c => $d) {
1410
-						krsort($this->default_values[$a][$c]);
1411
-					}
1412
-				}
1413
-			}
1414
-		}
1415
-		return 1;
1416
-	}
1417
-
1418
-
1419
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1420
-
1421
-	/**
1422
-	 *  Load all objects into $this->users
1423
-	 *
1424
-	 * @param string $sortorder sort order
1425
-	 * @param string $sortfield sort field
1426
-	 * @param int $limit limit page
1427
-	 * @param int $offset page
1428
-	 * @param string $filter Filter as an Universal Search string.
1429
-	 *                                      Example: '((client:=:1) OR ((client:>=:2) AND (client:<=:3))) AND
1430
-	 *                                      (client:!=:8) AND (nom:like:'a%')'
1431
-	 * @param string $filtermode No more used
1432
-	 * @param bool $entityfilter Activate entity filter
1433
-	 *
1434
-	 * @return int                         Return integer <0 if KO, >0 if OK
1435
-	 */
1436
-	public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND', $entityfilter = false)
1437
-	{
1438
-		global $conf, $user;
1439
-
1440
-		$sql = "SELECT t.rowid";
1441
-		$sql .= ' FROM ' . $dbPrefix . $this->table_element . ' as t ';
1442
-
1443
-		if ($entityfilter) {
1444
-			if (getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
1445
-				if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1446
-					$sql .= " WHERE t.entity IS NOT NULL"; // Show all users
1447
-				} else {
1448
-					$sql .= "," . $dbPrefix . "usergroup_user as ug";
1449
-					$sql .= " WHERE ((ug.fk_user = t.rowid";
1450
-					$sql .= " AND ug.entity IN (" . getEntity('usergroup') . "))";
1451
-					$sql .= " OR t.entity = 0)"; // Show always superadmin
1452
-				}
1453
-			} else {
1454
-				$sql .= " WHERE t.entity IN (" . getEntity('user') . ")";
1455
-			}
1456
-		} else {
1457
-			$sql .= " WHERE 1 = 1";
1458
-		}
1459
-
1460
-		// Manage filter
1461
-		$errormessage = '';
1462
-		$sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
1463
-		if ($errormessage) {
1464
-			$this->errors[] = $errormessage;
1465
-			dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
1466
-			return -1;
1467
-		}
1468
-
1469
-		$sql .= $this->db->order($sortfield, $sortorder);
1470
-		if ($limit) {
1471
-			$sql .= $this->db->plimit($limit + 1, $offset);
1472
-		}
1473
-
1474
-		dol_syslog(__METHOD__, LOG_DEBUG);
1475
-
1476
-		$resql = $this->db->query($sql);
1477
-		if ($resql) {
1478
-			$this->users = [];
1479
-			$num = $this->db->num_rows($resql);
1480
-			if ($num) {
1481
-				while ($obj = $this->db->fetch_object($resql)) {
1482
-					$line = new self($this->db);
1483
-					$result = $line->fetch($obj->rowid);
1484
-					if ($result > 0 && !empty($line->id)) {
1485
-						$this->users[$obj->rowid] = clone $line;
1486
-					}
1487
-				}
1488
-				$this->db->free($resql);
1489
-			}
1490
-			return $num;
1491
-		} else {
1492
-			$this->errors[] = $this->db->lasterror();
1493
-			return -1;
1494
-		}
1495
-	}
1496
-
1497
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1498
-
1499
-	/**
1500
-	 *  Create a user from a contact object. User will be internal but if contact is linked to a third party, user will
1501
-	 *  be external
1502
-	 *
1503
-	 * @param Contact $contact Object for source contact
1504
-	 * @param string $login Login to force
1505
-	 * @param string $password Password to force
1506
-	 *
1507
-	 * @return int                 Return integer <0 if error, if OK returns id of created user
1508
-	 */
1509
-	public function create_from_contact($contact, $login = '', $password = '')
1510
-	{
1511
-		// phpcs:enable
1512
-		global $conf, $user, $langs;
1513
-
1514
-		$error = 0;
1515
-
1516
-		// Define parameters
1517
-		$this->admin = 0;
1518
-		$this->civility_code = $contact->civility_code;
1519
-		$this->lastname = $contact->lastname;
1520
-		$this->firstname = $contact->firstname;
1521
-		//$this->gender = $contact->gender;     // contact ha sno gender
1522
-		$this->email = $contact->email;
1523
-		$this->socialnetworks = $contact->socialnetworks;
1524
-		$this->office_phone = $contact->phone_pro;
1525
-		$this->office_fax = $contact->fax;
1526
-		$this->user_mobile = $contact->phone_mobile;
1527
-		$this->address = $contact->address;
1528
-		$this->zip = $contact->zip;
1529
-		$this->town = $contact->town;
1530
-		$this->setUpperOrLowerCase();
1531
-		$this->state_id = $contact->state_id;
1532
-		$this->country_id = $contact->country_id;
1533
-		$this->employee = 0;
1534
-
1535
-		if (empty($login)) {
1536
-			include_once BASE_PATH . '/../Dolibarr/Lib/Functions2.php';
1537
-			$login = dol_buildlogin($contact->lastname, $contact->firstname);
1538
-		}
1539
-		$this->login = $login;
1540
-
1541
-		$this->db->begin();
1542
-
1543
-		// Create user and set $this->id. Trigger is disabled because executed later.
1544
-		$result = $this->create($user, 1);
1545
-		if ($result > 0) {
1546
-			$sql = "UPDATE " . $dbPrefix . "user";
1547
-			$sql .= " SET fk_socpeople=" . ((int)$contact->id);
1548
-			$sql .= ", civility='" . $this->db->escape($contact->civility_code) . "'";
1549
-			if ($contact->socid > 0) {
1550
-				$sql .= ", fk_soc=" . ((int)$contact->socid);
1551
-			}
1552
-			$sql .= " WHERE rowid=" . ((int)$this->id);
1553
-
1554
-			$resql = $this->db->query($sql);
1555
-
1556
-			dol_syslog(get_class($this) . "::create_from_contact", LOG_DEBUG);
1557
-			if ($resql) {
1558
-				$this->context['createfromcontact'] = 'createfromcontact';
1559
-
1560
-				// Call trigger
1561
-				$result = $this->call_trigger('USER_CREATE', $user);
1562
-				if ($result < 0) {
1563
-					$error++;
1564
-					$this->db->rollback();
1565
-					return -1;
1566
-				}
1567
-				// End call triggers
1568
-
1569
-				$this->db->commit();
1570
-				return $this->id;
1571
-			} else {
1572
-				$this->error = $this->db->error();
1573
-
1574
-				$this->db->rollback();
1575
-				return -1;
1576
-			}
1577
-		} else {
1578
-			// $this->error deja positionne
1579
-			dol_syslog(get_class($this) . "::create_from_contact - 0");
1580
-
1581
-			$this->db->rollback();
1582
-			return $result;
1583
-		}
1584
-	}
1585
-
1586
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1587
-
1588
-	/**
1589
-	 *  Create a user into database
1590
-	 *
1591
-	 * @param User $user Object user doing creation
1592
-	 * @param int $notrigger 1=do not execute triggers, 0 otherwise
1593
-	 *
1594
-	 * @return int                     Return integer <0 if KO, id of created user if OK
1595
-	 */
1596
-	public function create($user, $notrigger = 0)
1597
-	{
1598
-		global $conf, $langs;
1599
-		global $mysoc;
1600
-
1601
-		global $config;
1602
-		$dbPrefix = $config->db->prefix;
1603
-
1604
-		// Clean parameters
1605
-		$this->setUpperOrLowerCase();
1606
-
1607
-		$this->civility_code = trim((string)$this->civility_code);
1608
-		$this->login = trim((string)$this->login);
1609
-		if (!isset($this->entity)) {
1610
-			$this->entity = $conf->entity; // If not defined, we use default value
1611
-		}
1612
-
1613
-		dol_syslog(get_class($this) . "::create login=" . $this->login . ", user=" . (is_object($user) ? $user->id : ''), LOG_DEBUG);
1614
-
1615
-		$badCharUnauthorizedIntoLoginName = getDolGlobalString('MAIN_LOGIN_BADCHARUNAUTHORIZED', ',@<>"\'');
1616
-
1617
-		// Check parameters
1618
-		if (getDolGlobalString('USER_MAIL_REQUIRED') && !isValidEmail($this->email)) {
1619
-			$langs->load("errors");
1620
-			$this->error = $langs->trans("ErrorBadEMail", $this->email);
1621
-			return -1;
1622
-		}
1623
-		if (empty($this->login)) {
1624
-			$langs->load("errors");
1625
-			$this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Login"));
1626
-			return -1;
1627
-		} elseif (preg_match('/[' . preg_quote($badCharUnauthorizedIntoLoginName, '/') . ']/', $this->login)) {
1628
-			$langs->load("errors");
1629
-			$this->error = $langs->trans("ErrorBadCharIntoLoginName", $langs->transnoentitiesnoconv("Login"));
1630
-			return -1;
1631
-		}
1632
-
1633
-		$this->datec = dol_now();
1634
-
1635
-		$error = 0;
1636
-		$this->db->begin();
1637
-
1638
-		// Check if login already exists in same entity or into entity 0.
1639
-		if ($this->login) {
1640
-			$sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND login = '" . $this->db->escape($this->login) . "'";
1641
-			$resqltochecklogin = $this->db->query($sqltochecklogin);
1642
-			if ($resqltochecklogin) {
1643
-				$objtochecklogin = $this->db->fetch_object($resqltochecklogin);
1644
-				if ($objtochecklogin && $objtochecklogin->nb > 0) {
1645
-					$langs->load("errors");
1646
-					$this->error = $langs->trans("ErrorLoginAlreadyExists", $this->login);
1647
-					dol_syslog(get_class($this) . "::create " . $this->error, LOG_DEBUG);
1648
-					$this->db->rollback();
1649
-					return -6;
1650
-				}
1651
-				$this->db->free($resqltochecklogin);
1652
-			}
1653
-		}
1654
-		if (!empty($this->email)) {
1655
-			$sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND email = '" . $this->db->escape($this->email) . "'";
1656
-			$resqltochecklogin = $this->db->query($sqltochecklogin);
1657
-			if ($resqltochecklogin) {
1658
-				$objtochecklogin = $this->db->fetch_object($resqltochecklogin);
1659
-				if ($objtochecklogin && $objtochecklogin->nb > 0) {
1660
-					$langs->load("errors");
1661
-					$this->error = $langs->trans("ErrorEmailAlreadyExists", $this->email);
1662
-					dol_syslog(get_class($this) . "::create " . $this->error, LOG_DEBUG);
1663
-					$this->db->rollback();
1664
-					return -6;
1665
-				}
1666
-				$this->db->free($resqltochecklogin);
1667
-			}
1668
-		}
1669
-
1670
-		// Insert into database
1671
-		$sql = "INSERT INTO " . $dbPrefix . "user (datec, login, ldap_sid, entity)";
1672
-		$sql .= " VALUES('" . $this->db->idate($this->datec) . "', '" . $this->db->escape($this->login) . "', '" . $this->db->escape($this->ldap_sid) . "', " . ((int)$this->entity) . ")";
1673
-		$result = $this->db->query($sql);
1674
-
1675
-		dol_syslog(get_class($this) . "::create", LOG_DEBUG);
1676
-		if ($result) {
1677
-			$this->id = $this->db->last_insert_id($dbPrefix . "user");
1678
-
1679
-			// Set default rights
1680
-			if ($this->set_default_rights() < 0) {
1681
-				$this->error = 'ErrorFailedToSetDefaultRightOfUser';
1682
-				$this->db->rollback();
1683
-				return -5;
1684
-			}
1685
-
1686
-			if (getDolGlobalString('MAIN_DEFAULT_WAREHOUSE_USER') && getDolGlobalString('STOCK_USERSTOCK_AUTOCREATE')) {
1687
-				require_once DOL_DOCUMENT_ROOT . '/product/stock/class/entrepot.class.php';
1688
-				$langs->load("stocks");
1689
-
1690
-				$entrepot = new Entrepot($this->db);
1691
-				$entrepot->label = $langs->trans("PersonalStock", $this->getFullName($langs));
1692
-				$entrepot->libelle = $entrepot->label; // For backward compatibility
1693
-				$entrepot->description = $langs->trans("ThisWarehouseIsPersonalStock", $this->getFullName($langs));
1694
-				$entrepot->statut = 1;
1695
-				$entrepot->country_id = $mysoc->country_id;
1696
-
1697
-				$warehouseid = $entrepot->create($user);
1698
-
1699
-				$this->fk_warehouse = $warehouseid;
1700
-			}
1701
-
1702
-			// Update minor fields
1703
-			$result = $this->update($user, 1, 1);
1704
-			if ($result < 0) {
1705
-				$this->db->rollback();
1706
-				return -4;
1707
-			}
1708
-
1709
-			if (!$notrigger) {
1710
-				// Call trigger
1711
-				$result = $this->call_trigger('USER_CREATE', $user);
1712
-				if ($result < 0) {
1713
-					$error++;
1714
-				}
1715
-				// End call triggers
1716
-			}
1717
-
1718
-			if (!$error) {
1719
-				$this->db->commit();
1720
-				return $this->id;
1721
-			} else {
1722
-				//$this->error=$interface->error;
1723
-				dol_syslog(get_class($this) . "::create " . $this->error, LOG_ERR);
1724
-				$this->db->rollback();
1725
-				return -3;
1726
-			}
1727
-		} else {
1728
-			$this->error = $this->db->lasterror();
1729
-			$this->db->rollback();
1730
-			return -2;
1731
-		}
1732
-	}
1733
-
1734
-	/**
1735
-	 *    Assign rights by default
1736
-	 *
1737
-	 * @return     integer erreur <0, si ok renvoi le nbre de droits par default positions
1738
-	 */
1739
-	public function set_default_rights()
1740
-	{
1741
-		// phpcs:enable
1742
-		global $conf;
1743
-
1744
-		$rd = [];
1745
-		$num = 0;
1746
-		$sql = "SELECT id FROM " . $dbPrefix . "rights_def";
1747
-		$sql .= " WHERE bydefault = 1";
1748
-		$sql .= " AND entity = " . ((int)$conf->entity);
1749
-
1750
-		$resql = $this->db->query($sql);
1751
-		if ($resql) {
1752
-			$num = $this->db->num_rows($resql);
1753
-			$i = 0;
1754
-			while ($i < $num) {
1755
-				$row = $this->db->fetch_row($resql);
1756
-				$rd[$i] = $row[0];
1757
-				$i++;
1758
-			}
1759
-			$this->db->free($resql);
1760
-		}
1761
-		$i = 0;
1762
-		while ($i < $num) {
1763
-			$sql = "DELETE FROM " . $dbPrefix . "user_rights WHERE fk_user = $this->id AND fk_id=$rd[$i]";
1764
-			$result = $this->db->query($sql);
1765
-
1766
-			$sql = "INSERT INTO " . $dbPrefix . "user_rights (fk_user, fk_id) VALUES ($this->id, $rd[$i])";
1767
-			$result = $this->db->query($sql);
1768
-			if (!$result) {
1769
-				return -1;
1770
-			}
1771
-			$i++;
1772
-		}
1773
-
1774
-		return $i;
1775
-	}
1776
-
1777
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1778
-
1779
-	/**
1780
-	 *      Update a user into database (and also password if this->pass is defined)
1781
-	 *
1782
-	 * @param User $user User making update
1783
-	 * @param int $notrigger 1=do not execute triggers, 0 by default
1784
-	 * @param int $nosyncmember 0=Synchronize linked member (standard info), 1=Do not synchronize linked member
1785
-	 * @param int $nosyncmemberpass 0=Synchronize linked member (password), 1=Do not synchronize linked member
1786
-	 * @param int $nosynccontact 0=Synchronize linked contact, 1=Do not synchronize linked contact
1787
-	 *
1788
-	 * @return int                         Return integer <0 if KO, >=0 if OK
1789
-	 */
1790
-	public function update($user, $notrigger = 0, $nosyncmember = 0, $nosyncmemberpass = 0, $nosynccontact = 0)
1791
-	{
1792
-		global $conf, $langs;
1793
-
1794
-		global $config;
1795
-		$dbPrefix = $config->db->prefix;
1796
-
1797
-		$nbrowsaffected = 0;
1798
-		$error = 0;
1799
-
1800
-		dol_syslog(get_class($this) . "::update notrigger=" . $notrigger . ", nosyncmember=" . $nosyncmember . ", nosyncmemberpass=" . $nosyncmemberpass);
1801
-
1802
-		// Clean parameters
1803
-		$this->civility_code = trim((string)$this->civility_code);
1804
-		$this->lastname = trim((string)$this->lastname);
1805
-		$this->firstname = trim((string)$this->firstname);
1806
-		$this->ref_employee = trim((string)$this->ref_employee);
1807
-		$this->national_registration_number = trim((string)$this->national_registration_number);
1808
-		$this->employee = ($this->employee > 0 ? $this->employee : 0);
1809
-		$this->login = trim((string)$this->login);
1810
-		$this->gender = trim((string)$this->gender);
1811
-
1812
-		$this->pass = trim((string)$this->pass);
1813
-		$this->api_key = trim((string)$this->api_key);
1814
-		$this->datestartvalidity = empty($this->datestartvalidity) ? '' : $this->datestartvalidity;
1815
-		$this->dateendvalidity = empty($this->dateendvalidity) ? '' : $this->dateendvalidity;
1816
-
1817
-		$this->address = trim((string)$this->address);
1818
-		$this->zip = trim((string)$this->zip);
1819
-		$this->town = trim((string)$this->town);
1820
-
1821
-		$this->state_id = ($this->state_id > 0 ? $this->state_id : 0);
1822
-		$this->country_id = ($this->country_id > 0 ? $this->country_id : 0);
1823
-		$this->office_phone = trim((string)$this->office_phone);
1824
-		$this->office_fax = trim((string)$this->office_fax);
1825
-		$this->user_mobile = trim((string)$this->user_mobile);
1826
-		$this->personal_mobile = trim((string)$this->personal_mobile);
1827
-		$this->email = trim((string)$this->email);
1828
-		$this->personal_email = trim((string)$this->personal_email);
1829
-
1830
-		$this->job = trim((string)$this->job);
1831
-		$this->signature = trim((string)$this->signature);
1832
-		$this->note_public = trim((string)$this->note_public);
1833
-		$this->note_private = trim((string)$this->note_private);
1834
-		$this->openid = trim((string)$this->openid);
1835
-		$this->admin = ($this->admin > 0 ? $this->admin : 0);
1836
-
1837
-		$this->accountancy_code = trim((string)$this->accountancy_code);
1838
-		$this->color = trim((string)$this->color);
1839
-		$this->dateemployment = empty($this->dateemployment) ? '' : $this->dateemployment;
1840
-		$this->dateemploymentend = empty($this->dateemploymentend) ? '' : $this->dateemploymentend;
1841
-
1842
-		$this->birth = empty($this->birth) ? '' : $this->birth;
1843
-		$this->fk_warehouse = (int)$this->fk_warehouse;
1844
-
1845
-		$this->setUpperOrLowerCase();
1846
-
1847
-		// Check parameters
1848
-		$badCharUnauthorizedIntoLoginName = getDolGlobalString('MAIN_LOGIN_BADCHARUNAUTHORIZED', ',@<>"\'');
1849
-
1850
-		if (getDolGlobalString('USER_MAIL_REQUIRED') && !isValidEmail($this->email)) {
1851
-			$langs->load("errors");
1852
-			$this->error = $langs->trans("ErrorBadEMail", $this->email);
1853
-			return -1;
1854
-		}
1855
-		if (empty($this->login)) {
1856
-			$langs->load("errors");
1857
-			$this->error = $langs->trans("ErrorFieldRequired", 'Login');
1858
-			return -1;
1859
-		} elseif (preg_match('/[' . preg_quote($badCharUnauthorizedIntoLoginName, '/') . ']/', $this->login)) {
1860
-			$langs->load("errors");
1861
-			$this->error = $langs->trans("ErrorBadCharIntoLoginName", $langs->transnoentitiesnoconv("Login"));
1862
-			return -1;
1863
-		}
1864
-
1865
-		$this->db->begin();
1866
-
1867
-		// Check if login already exists in same entity or into entity 0.
1868
-		if (!empty($this->oldcopy) && $this->oldcopy->login != $this->login) {
1869
-			$sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND login = '" . $this->db->escape($this->login) . "'";
1870
-			$resqltochecklogin = $this->db->query($sqltochecklogin);
1871
-			if ($resqltochecklogin) {
1872
-				$objtochecklogin = $this->db->fetch_object($resqltochecklogin);
1873
-				if ($objtochecklogin && $objtochecklogin->nb > 0) {
1874
-					$langs->load("errors");
1875
-					$this->error = $langs->trans("ErrorLoginAlreadyExists", $this->login);
1876
-					dol_syslog(get_class($this) . "::create " . $this->error, LOG_DEBUG);
1877
-					$this->db->rollback();
1878
-					return -1;
1879
-				}
1880
-			}
1881
-		}
1882
-		if (!empty($this->oldcopy) && !empty($this->email) && $this->oldcopy->email != $this->email) {
1883
-			$sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND email = '" . $this->db->escape($this->email) . "'";
1884
-			$resqltochecklogin = $this->db->query($sqltochecklogin);
1885
-			if ($resqltochecklogin) {
1886
-				$objtochecklogin = $this->db->fetch_object($resqltochecklogin);
1887
-				if ($objtochecklogin && $objtochecklogin->nb > 0) {
1888
-					$langs->load("errors");
1889
-					$this->error = $langs->trans("ErrorEmailAlreadyExists", $this->email);
1890
-					dol_syslog(get_class($this) . "::create " . $this->error, LOG_DEBUG);
1891
-					$this->db->rollback();
1892
-					return -1;
1893
-				}
1894
-			}
1895
-		}
1896
-
1897
-		// Update data
1898
-		$sql = "UPDATE " . $dbPrefix . "user SET";
1899
-		$sql .= " civility = '" . $this->db->escape($this->civility_code) . "'";
1900
-		$sql .= ", lastname = '" . $this->db->escape($this->lastname) . "'";
1901
-		$sql .= ", firstname = '" . $this->db->escape($this->firstname) . "'";
1902
-		$sql .= ", ref_employee = '" . $this->db->escape($this->ref_employee) . "'";
1903
-		$sql .= ", national_registration_number = '" . $this->db->escape($this->national_registration_number) . "'";
1904
-		$sql .= ", employee = " . (int)$this->employee;
1905
-		$sql .= ", login = '" . $this->db->escape($this->login) . "'";
1906
-		$sql .= ", api_key = " . ($this->api_key ? "'" . $this->db->escape(dolEncrypt($this->api_key, '', '', 'dolibarr')) . "'" : "null");
1907
-		$sql .= ", gender = " . ($this->gender != -1 ? "'" . $this->db->escape($this->gender) . "'" : "null"); // 'man' or 'woman'
1908
-		$sql .= ", birth=" . (strval($this->birth) != '' ? "'" . $this->db->idate($this->birth, 'tzserver') . "'" : 'null');
1909
-		if (!empty($user->admin)) {
1910
-			$sql .= ", admin = " . (int)$this->admin; // admin flag can be set/unset only by an admin user
1911
-		}
1912
-		$sql .= ", address = '" . $this->db->escape($this->address) . "'";
1913
-		$sql .= ", zip = '" . $this->db->escape($this->zip) . "'";
1914
-		$sql .= ", town = '" . $this->db->escape($this->town) . "'";
1915
-		$sql .= ", fk_state = " . ((!empty($this->state_id) && $this->state_id > 0) ? "'" . $this->db->escape($this->state_id) . "'" : "null");
1916
-		$sql .= ", fk_country = " . ((!empty($this->country_id) && $this->country_id > 0) ? "'" . $this->db->escape($this->country_id) . "'" : "null");
1917
-		$sql .= ", office_phone = '" . $this->db->escape($this->office_phone) . "'";
1918
-		$sql .= ", office_fax = '" . $this->db->escape($this->office_fax) . "'";
1919
-		$sql .= ", user_mobile = '" . $this->db->escape($this->user_mobile) . "'";
1920
-		$sql .= ", personal_mobile = '" . $this->db->escape($this->personal_mobile) . "'";
1921
-		$sql .= ", email = '" . $this->db->escape($this->email) . "'";
1922
-		$sql .= ", personal_email = '" . $this->db->escape($this->personal_email) . "'";
1923
-		$sql .= ", socialnetworks = '" . $this->db->escape(json_encode($this->socialnetworks)) . "'";
1924
-		$sql .= ", job = '" . $this->db->escape($this->job) . "'";
1925
-		$sql .= ", signature = '" . $this->db->escape($this->signature) . "'";
1926
-		$sql .= ", accountancy_code = '" . $this->db->escape($this->accountancy_code) . "'";
1927
-		$sql .= ", color = '" . $this->db->escape($this->color) . "'";
1928
-		$sql .= ", dateemployment=" . (strval($this->dateemployment) != '' ? "'" . $this->db->idate($this->dateemployment) . "'" : 'null');
1929
-		$sql .= ", dateemploymentend=" . (strval($this->dateemploymentend) != '' ? "'" . $this->db->idate($this->dateemploymentend) . "'" : 'null');
1930
-		$sql .= ", datestartvalidity=" . (strval($this->datestartvalidity) != '' ? "'" . $this->db->idate($this->datestartvalidity) . "'" : 'null');
1931
-		$sql .= ", dateendvalidity=" . (strval($this->dateendvalidity) != '' ? "'" . $this->db->idate($this->dateendvalidity) . "'" : 'null');
1932
-		$sql .= ", note_private = '" . $this->db->escape($this->note_private) . "'";
1933
-		$sql .= ", note_public = '" . $this->db->escape($this->note_public) . "'";
1934
-		$sql .= ", photo = " . ($this->photo ? "'" . $this->db->escape($this->photo) . "'" : "null");
1935
-		$sql .= ", openid = " . ($this->openid ? "'" . $this->db->escape($this->openid) . "'" : "null");
1936
-		$sql .= ", fk_user = " . ($this->fk_user > 0 ? "'" . $this->db->escape($this->fk_user) . "'" : "null");
1937
-		$sql .= ", fk_user_expense_validator = " . ($this->fk_user_expense_validator > 0 ? "'" . $this->db->escape($this->fk_user_expense_validator) . "'" : "null");
1938
-		$sql .= ", fk_user_holiday_validator = " . ($this->fk_user_holiday_validator > 0 ? "'" . $this->db->escape($this->fk_user_holiday_validator) . "'" : "null");
1939
-		if (isset($this->thm) || $this->thm != '') {
1940
-			$sql .= ", thm= " . ($this->thm != '' ? "'" . $this->db->escape($this->thm) . "'" : "null");
1941
-		}
1942
-		if (isset($this->tjm) || $this->tjm != '') {
1943
-			$sql .= ", tjm= " . ($this->tjm != '' ? "'" . $this->db->escape($this->tjm) . "'" : "null");
1944
-		}
1945
-		if (isset($this->salary) || $this->salary != '') {
1946
-			$sql .= ", salary= " . ($this->salary != '' ? "'" . $this->db->escape($this->salary) . "'" : "null");
1947
-		}
1948
-		if (isset($this->salaryextra) || $this->salaryextra != '') {
1949
-			$sql .= ", salaryextra= " . ($this->salaryextra != '' ? "'" . $this->db->escape($this->salaryextra) . "'" : "null");
1950
-		}
1951
-		$sql .= ", weeklyhours= " . ($this->weeklyhours != '' ? "'" . $this->db->escape($this->weeklyhours) . "'" : "null");
1952
-		if (!empty($user->admin) && empty($user->entity) && $user->id != $this->id) {
1953
-			$sql .= ", entity = " . ((int)$this->entity); // entity flag can be set/unset only by an another superadmin user
1954
-		}
1955
-		$sql .= ", default_range = " . ($this->default_range > 0 ? $this->default_range : 'null');
1956
-		$sql .= ", default_c_exp_tax_cat = " . ($this->default_c_exp_tax_cat > 0 ? $this->default_c_exp_tax_cat : 'null');
1957
-		$sql .= ", fk_warehouse = " . ($this->fk_warehouse > 0 ? $this->fk_warehouse : "null");
1958
-		$sql .= ", lang = " . ($this->lang ? "'" . $this->db->escape($this->lang) . "'" : "null");
1959
-		$sql .= " WHERE rowid = " . ((int)$this->id);
1960
-
1961
-		dol_syslog(get_class($this) . "::update", LOG_DEBUG);
1962
-		$resql = $this->db->query($sql);
1963
-		if ($resql) {
1964
-			$nbrowsaffected += $this->db->affected_rows($resql);
1965
-
1966
-			// Update password
1967
-			if (!empty($this->pass)) {
1968
-				if ($this->pass != $this->pass_indatabase && !dol_verifyHash($this->pass, $this->pass_indatabase_crypted)) {
1969
-					// If a new value for password is set and different than the one encrypted into database
1970
-					$result = $this->setPassword($user, $this->pass, 0, $notrigger, $nosyncmemberpass, 0, 1);
1971
-					if (is_int($result) && $result < 0) {
1972
-						return -5;
1973
-					}
1974
-				}
1975
-			}
1976
-
1977
-			// If user is linked to a member, remove old link to this member
1978
-			if ($this->fk_member > 0) {
1979
-				dol_syslog(get_class($this) . "::update remove link with member. We will recreate it later", LOG_DEBUG);
1980
-				$sql = "UPDATE " . $dbPrefix . "user SET fk_member = NULL where fk_member = " . ((int)$this->fk_member);
1981
-				$resql = $this->db->query($sql);
1982
-				if (!$resql) {
1983
-					$this->error = $this->db->error();
1984
-					$this->db->rollback();
1985
-					return -5;
1986
-				}
1987
-			}
1988
-			// Set link to user
1989
-			dol_syslog(get_class($this) . "::update set link with member", LOG_DEBUG);
1990
-			$sql = "UPDATE " . $dbPrefix . "user SET fk_member =" . ($this->fk_member > 0 ? ((int)$this->fk_member) : 'null') . " where rowid = " . ((int)$this->id);
1991
-			$resql = $this->db->query($sql);
1992
-			if (!$resql) {
1993
-				$this->error = $this->db->error();
1994
-				$this->db->rollback();
1995
-				return -5;
1996
-			}
1997
-
1998
-			if ($nbrowsaffected) {  // If something has changed in data
1999
-				if ($this->fk_member > 0 && !$nosyncmember) {
2000
-					dol_syslog(get_class($this) . "::update user is linked with a member. We try to update member too.", LOG_DEBUG);
2001
-
2002
-
2003
-					// This user is linked with a member, so we also update member information
2004
-					// if this is an update.
2005
-					$adh = new Adherent($this->db);
2006
-					$result = $adh->fetch($this->fk_member);
2007
-
2008
-					if ($result > 0) {
2009
-						$adh->civility_code = $this->civility_code;
2010
-						$adh->firstname = $this->firstname;
2011
-						$adh->lastname = $this->lastname;
2012
-						$adh->login = $this->login;
2013
-						$adh->gender = $this->gender;
2014
-						$adh->birth = $this->birth;
2015
-
2016
-						$adh->pass = $this->pass;
2017
-
2018
-						$adh->address = $this->address;
2019
-						$adh->town = $this->town;
2020
-						$adh->zip = $this->zip;
2021
-						$adh->state_id = $this->state_id;
2022
-						$adh->country_id = $this->country_id;
2023
-
2024
-						$adh->email = $this->email;
2025
-
2026
-						$adh->socialnetworks = $this->socialnetworks;
2027
-
2028
-						$adh->phone = $this->office_phone;
2029
-						$adh->phone_mobile = $this->user_mobile;
2030
-
2031
-						$adh->default_lang = $this->lang;
2032
-
2033
-						$adh->user_id = $this->id;
2034
-						$adh->user_login = $this->login;
2035
-
2036
-						$result = $adh->update($user, 0, 1, 0);
2037
-						if ($result < 0) {
2038
-							$this->error = $adh->error;
2039
-							$this->errors = $adh->errors;
2040
-							dol_syslog(get_class($this) . "::update error after calling adh->update to sync it with user: " . $this->error, LOG_ERR);
2041
-							$error++;
2042
-						}
2043
-					} elseif ($result < 0) {
2044
-						$this->error = $adh->error;
2045
-						$this->errors = $adh->errors;
2046
-						$error++;
2047
-					}
2048
-				}
2049
-
2050
-				if ($this->contact_id > 0 && !$nosynccontact) {
2051
-					dol_syslog(get_class($this) . "::update user is linked with a contact. We try to update contact too.", LOG_DEBUG);
2052
-
2053
-
2054
-					// This user is linked with a contact, so we also update contact information if this is an update.
2055
-					$tmpobj = new Contact($this->db);
2056
-					$result = $tmpobj->fetch($this->contact_id);
2057
-
2058
-					if ($result >= 0) {
2059
-						$tmpobj->civility_code = $this->civility_code;
2060
-						$tmpobj->firstname = $this->firstname;
2061
-						$tmpobj->lastname = $this->lastname;
2062
-						$tmpobj->login = $this->login;
2063
-						$tmpobj->gender = $this->gender;
2064
-						$tmpobj->birth = $this->birth;
2065
-
2066
-						//$tmpobj->pass=$this->pass;
2067
-
2068
-						$tmpobj->email = $this->email;
2069
-
2070
-						$tmpobj->socialnetworks = $this->socialnetworks;
2071
-
2072
-						$tmpobj->phone_pro = $this->office_phone;
2073
-						$tmpobj->phone_mobile = $this->user_mobile;
2074
-						$tmpobj->fax = $this->office_fax;
2075
-
2076
-						$tmpobj->default_lang = $this->lang;
2077
-
2078
-						$tmpobj->address = $this->address;
2079
-						$tmpobj->town = $this->town;
2080
-						$tmpobj->zip = $this->zip;
2081
-						$tmpobj->state_id = $this->state_id;
2082
-						$tmpobj->country_id = $this->country_id;
2083
-
2084
-						$tmpobj->user_id = $this->id;
2085
-						$tmpobj->user_login = $this->login;
2086
-
2087
-						$result = $tmpobj->update($tmpobj->id, $user, 0, 'update', 1);
2088
-						if ($result < 0) {
2089
-							$this->error = $tmpobj->error;
2090
-							$this->errors = $tmpobj->errors;
2091
-							dol_syslog(get_class($this) . "::update error after calling adh->update to sync it with user: " . $this->error, LOG_ERR);
2092
-							$error++;
2093
-						}
2094
-					} else {
2095
-						$this->error = $tmpobj->error;
2096
-						$this->errors = $tmpobj->errors;
2097
-						$error++;
2098
-					}
2099
-				}
2100
-			}
2101
-
2102
-			$action = 'update';
2103
-
2104
-			// Actions on extra fields
2105
-			if (!$error) {
2106
-				$result = $this->insertExtraFields();
2107
-				if ($result < 0) {
2108
-					$error++;
2109
-				}
2110
-			}
2111
-
2112
-			if (!$error && !$notrigger) {
2113
-				// Call trigger
2114
-				$result = $this->call_trigger('USER_MODIFY', $user);
2115
-				if ($result < 0) {
2116
-					$error++;
2117
-				}
2118
-				// End call triggers
2119
-			}
2120
-
2121
-			if (!$error) {
2122
-				$this->db->commit();
2123
-				return $nbrowsaffected;
2124
-			} else {
2125
-				dol_syslog(get_class($this) . "::update error=" . $this->error, LOG_ERR);
2126
-				$this->db->rollback();
2127
-				return -1;
2128
-			}
2129
-		} else {
2130
-			$this->error = $this->db->lasterror();
2131
-			$this->db->rollback();
2132
-			return -2;
2133
-		}
2134
-	}
2135
-
2136
-	/**
2137
-	 *  Change password of a user
2138
-	 *
2139
-	 * @param User $user Object user of user requesting the change (not the user for who we change
2140
-	 *                                       the password). May be unknown.
2141
-	 * @param string $password New password, in clear text or already encrypted (to generate if not
2142
-	 *                                       provided)
2143
-	 * @param int $changelater 0=Default, 1=Save password into pass_temp to change password only after
2144
-	 *                                       clicking on confirm email
2145
-	 * @param int $notrigger 1=Does not launch triggers
2146
-	 * @param int $nosyncmember Do not synchronize linked member
2147
-	 * @param int $passwordalreadycrypted 0=Value is cleartext password, 1=Value is encrypted value.
2148
-	 * @param int $flagdelsessionsbefore 1=Save also the current date to ask to invalidate all other session before
2149
-	 *                                       this date.
2150
-	 *
2151
-	 * @return int|string                      If OK return clear password, 0 if no change (warning, you may retrieve 1
2152
-	 *                                         instead of 0 even if password was same), < 0 if error
2153
-	 */
2154
-	public function setPassword($user, $password = '', $changelater = 0, $notrigger = 0, $nosyncmember = 0, $passwordalreadycrypted = 0, $flagdelsessionsbefore = 1)
2155
-	{
2156
-		global $conf, $langs;
2157
-		global $config;
2158
-		$dbPrefix = $config->db->prefix;
2159
-
2160
-		require_once BASE_PATH . '/../Dolibarr/Lib/Security2.php';
2161
-
2162
-		$error = 0;
2163
-
2164
-		dol_syslog(get_class($this) . "::setPassword user=" . $user->id . " password=" . preg_replace('/./i', '*', $password) . " changelater=" . $changelater . " notrigger=" . $notrigger . " nosyncmember=" . $nosyncmember, LOG_DEBUG);
2165
-
2166
-		// If new password not provided, we generate one
2167
-		if (!$password) {
2168
-			$password = getRandomPassword(false);
2169
-		}
2170
-
2171
-		// Check and encrypt the password
2172
-		if (empty($passwordalreadycrypted)) {
2173
-			if (getDolGlobalString('USER_PASSWORD_GENERATED')) {
2174
-				// Add a check on rules for password syntax using the setup of the password generator
2175
-				$modGeneratePassClass = 'modGeneratePass' . ucfirst($conf->global->USER_PASSWORD_GENERATED);
2176
-
2177
-				include_once DOL_DOCUMENT_ROOT . '/core/modules/security/generate/' . $modGeneratePassClass . '.class.php';
2178
-				if (class_exists($modGeneratePassClass)) {
2179
-					$modGeneratePass = new $modGeneratePassClass($this->db, $conf, $langs, $user);
2180
-
2181
-					// To check an input user password, we disable the cleaning on ambiguous characters (this is used only for auto-generated password)
2182
-					$modGeneratePass->WithoutAmbi = 0;
2183
-
2184
-					// Call to validatePassword($password) to check pass match rules
2185
-					$testpassword = $modGeneratePass->validatePassword($password);
2186
-					if (!$testpassword) {
2187
-						$this->error = $modGeneratePass->error;
2188
-						return -1;
2189
-					}
2190
-				}
2191
-			}
2192
-
2193
-
2194
-			// Now, we encrypt the new password
2195
-			$password_crypted = dol_hash($password);
2196
-		}
2197
-
2198
-		// Update password
2199
-		if (!$changelater) {
2200
-			if (!is_object($this->oldcopy)) {
2201
-				$this->oldcopy = clone $this;
2202
-			}
2203
-
2204
-			$this->db->begin();
2205
-
2206
-			$sql = "UPDATE " . $dbPrefix . "user";
2207
-			$sql .= " SET pass_crypted = '" . $this->db->escape($password_crypted) . "',";
2208
-			$sql .= " pass_temp = null";
2209
-			if (!empty($flagdelsessionsbefore)) {
2210
-				$sql .= ", flagdelsessionsbefore = '" . $this->db->idate(dol_now() - 5, 'gmt') . "'";
2211
-			}
2212
-			if (getDolGlobalString('DATABASE_PWD_ENCRYPTED')) {
2213
-				$sql .= ", pass = null";
2214
-			} else {
2215
-				$sql .= ", pass = '" . $this->db->escape($password) . "'";
2216
-			}
2217
-			$sql .= " WHERE rowid = " . ((int)$this->id);
2218
-
2219
-			dol_syslog(get_class($this) . "::setPassword", LOG_DEBUG);
2220
-			$result = $this->db->query($sql);
2221
-			if ($result) {
2222
-				if ($this->db->affected_rows($result)) {
2223
-					$this->pass = $password;
2224
-					$this->pass_indatabase = $password;
2225
-					$this->pass_indatabase_crypted = $password_crypted;
2226
-
2227
-					if ($this->fk_member && !$nosyncmember) {
2228
-						// This user is linked with a member, so we also update members information
2229
-						// if this is an update.
2230
-						$adh = new Adherent($this->db);
2231
-						$result = $adh->fetch($this->fk_member);
2232
-
2233
-						if ($result >= 0) {
2234
-							$result = $adh->setPassword($user, $this->pass, (!getDolGlobalString('DATABASE_PWD_ENCRYPTED') ? 0 : 1), 1); // The encryption is not managed in the 'adherent' module
2235
-							if (is_int($result) && $result < 0) {
2236
-								$this->error = $adh->error;
2237
-								dol_syslog(get_class($this) . "::setPassword " . $this->error, LOG_ERR);
2238
-								$error++;
2239
-							}
2240
-						} else {
2241
-							$this->error = $adh->error;
2242
-							$error++;
2243
-						}
2244
-					}
2245
-
2246
-					dol_syslog(get_class($this) . "::setPassword notrigger=" . $notrigger . " error=" . $error, LOG_DEBUG);
2247
-
2248
-					if (!$error && !$notrigger) {
2249
-						// Call trigger
2250
-						$result = $this->call_trigger('USER_NEW_PASSWORD', $user);
2251
-						if ($result < 0) {
2252
-							$error++;
2253
-							$this->db->rollback();
2254
-							return -1;
2255
-						}
2256
-						// End call triggers
2257
-					}
2258
-
2259
-					$this->db->commit();
2260
-					return $this->pass;
2261
-				} else {
2262
-					$this->db->rollback();
2263
-					return 0;
2264
-				}
2265
-			} else {
2266
-				$this->db->rollback();
2267
-				dol_print_error($this->db);
2268
-				return -1;
2269
-			}
2270
-		} else {
2271
-			// We store password in password temporary field.
2272
-			// After receiving confirmation link, we will erase and store it in pass_crypted
2273
-			$sql = "UPDATE " . $dbPrefix . "user";
2274
-			$sql .= " SET pass_temp = '" . $this->db->escape($password) . "'";
2275
-			$sql .= " WHERE rowid = " . ((int)$this->id);
2276
-
2277
-			dol_syslog(get_class($this) . "::setPassword", LOG_DEBUG); // No log
2278
-			$result = $this->db->query($sql);
2279
-			if ($result) {
2280
-				return $password;
2281
-			} else {
2282
-				dol_print_error($this->db);
2283
-				return -3;
2284
-			}
2285
-		}
2286
-	}
2287
-
2288
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2289
-
2290
-	/**
2291
-	 *      Renvoie la derniere erreur fonctionnelle de manipulation de l'objet
2292
-	 *
2293
-	 * @return    string      chaine erreur
2294
-	 */
2295
-	public function error()
2296
-	{
2297
-		return $this->error;
2298
-	}
2299
-
2300
-	/**
2301
-	 *  Create a user into database from a member object.
2302
-	 *  If $member->fk_soc is set, it will be an external user.
2303
-	 *
2304
-	 * @param Adherent $member Object member source
2305
-	 * @param string $login Login to force
2306
-	 *
2307
-	 * @return int                         Return integer <0 if KO, if OK, return id of created account
2308
-	 */
2309
-	public function create_from_member($member, $login = '')
2310
-	{
2311
-		// phpcs:enable
2312
-		global $conf, $user, $langs;
2313
-
2314
-		// Set properties on new user
2315
-		$this->admin = 0;
2316
-		$this->civility_code = $member->civility_id;
2317
-		$this->lastname = $member->lastname;
2318
-		$this->firstname = $member->firstname;
2319
-		$this->gender = $member->gender;
2320
-		$this->email = $member->email;
2321
-		$this->fk_member = $member->id;
2322
-		$this->address = $member->address;
2323
-		$this->zip = $member->zip;
2324
-		$this->town = $member->town;
2325
-		$this->setUpperOrLowerCase();
2326
-		$this->state_id = $member->state_id;
2327
-		$this->country_id = $member->country_id;
2328
-		$this->socialnetworks = $member->socialnetworks;
2329
-
2330
-		$this->pass = $member->pass;
2331
-		$this->pass_crypted = $member->pass_indatabase_crypted;
2332
-
2333
-		if (empty($login)) {
2334
-			include_once BASE_PATH . '/../Dolibarr/Lib/Functions2.php';
2335
-			$login = dol_buildlogin($member->lastname, $member->firstname);
2336
-		}
2337
-		$this->login = $login;
2338
-
2339
-		$this->db->begin();
2340
-
2341
-		// Create and set $this->id
2342
-		$result = $this->create($user);
2343
-		if ($result > 0) {
2344
-			if (!empty($this->pass)) {  // If a clear password was received (this situation should not happen anymore now), we use it to save it into database
2345
-				$newpass = $this->setPassword($user, $this->pass);
2346
-				if (is_int($newpass) && $newpass < 0) {
2347
-					$result = -2;
2348
-				}
2349
-			} elseif (!empty($this->pass_crypted)) {    // If an encrypted password is already known, we save it directly into database because the previous create did not save it.
2350
-				$sql = "UPDATE " . $dbPrefix . "user";
2351
-				$sql .= " SET pass_crypted = '" . $this->db->escape($this->pass_crypted) . "'";
2352
-				$sql .= " WHERE rowid=" . ((int)$this->id);
2353
-
2354
-				$resql = $this->db->query($sql);
2355
-				if (!$resql) {
2356
-					$result = -1;
2357
-				}
2358
-			}
2359
-
2360
-			if ($result > 0 && $member->socid) {    // If member is linked to a thirdparty
2361
-				$sql = "UPDATE " . $dbPrefix . "user";
2362
-				$sql .= " SET fk_soc=" . ((int)$member->socid);
2363
-				$sql .= " WHERE rowid=" . ((int)$this->id);
2364
-
2365
-				dol_syslog(get_class($this) . "::create_from_member", LOG_DEBUG);
2366
-				$resql = $this->db->query($sql);
2367
-				if ($resql) {
2368
-					$this->db->commit();
2369
-					return $this->id;
2370
-				} else {
2371
-					$this->error = $this->db->lasterror();
2372
-
2373
-					$this->db->rollback();
2374
-					return -1;
2375
-				}
2376
-			}
2377
-		}
2378
-
2379
-		if ($result > 0) {
2380
-			$this->db->commit();
2381
-			return $this->id;
2382
-		} else {
2383
-			// $this->error deja positionne
2384
-			$this->db->rollback();
2385
-			return -2;
2386
-		}
2387
-	}
2388
-
2389
-
2390
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2391
-
2392
-	/**
2393
-	 *  Update the user's last login date in the database.
2394
-	 *  Function called when a new connection is made by the user
2395
-	 *
2396
-	 * @return int     Return integer <0 si echec, >=0 si ok
2397
-	 */
2398
-	public function update_last_login_date()
2399
-	{
2400
-		global $config;
2401
-		$dbPrefix = $config->db->prefix;
2402
-
2403
-		// phpcs:enable
2404
-		$now = dol_now();
2405
-
2406
-		$userremoteip = getUserRemoteIP();
2407
-
2408
-		$sql = "UPDATE " . $dbPrefix . "user SET";
2409
-		$sql .= " datepreviouslogin = datelastlogin,";
2410
-		$sql .= " ippreviouslogin = iplastlogin,";
2411
-		$sql .= " datelastlogin = '" . $this->db->idate($now) . "',";
2412
-		$sql .= " iplastlogin = '" . $this->db->escape($userremoteip) . "',";
2413
-		$sql .= " tms = tms"; // The last update date must change because the last login date is updated
2414
-		$sql .= " WHERE rowid = " . ((int)$this->id);
2415
-
2416
-		dol_syslog(get_class($this) . "::update_last_login_date user->id=" . $this->id . " " . $sql, LOG_DEBUG);
2417
-		$resql = $this->db->query($sql);
2418
-		if ($resql) {
2419
-			$this->datepreviouslogin = $this->datelastlogin;
2420
-			$this->datelastlogin = $now;
2421
-			$this->ippreviouslogin = $this->iplastlogin;
2422
-			$this->iplastlogin = $userremoteip;
2423
-			return 1;
2424
-		} else {
2425
-			$this->error = $this->db->lasterror() . ' sql=' . $sql;
2426
-			return -1;
2427
-		}
2428
-	}
2429
-
2430
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2431
-
2432
-	/**
2433
-	 *  Send a new password (or instructions to reset it) by email
2434
-	 *
2435
-	 * @param User $user Object user that send the email (not the user we send to) @todo object $user is not
2436
-	 *                            used !
2437
-	 * @param string $password New password
2438
-	 * @param int $changelater 0=Send clear passwod into email, 1=Change password only after clicking on confirm
2439
-	 *                            email. @return int                     Return integer < 0 si erreur, > 0 si ok
2440
-	 * @todo Add method 2 = Send link to reset password
2441
-	 *
2442
-	 */
2443
-	public function send_password($user, $password = '', $changelater = 0)
2444
-	{
2445
-		// phpcs:enable
2446
-		global $conf, $langs, $mysoc;
2447
-		global $dolibarr_main_url_root;
2448
-
2449
-		require_once DOL_DOCUMENT_ROOT . '/core/class/CMailFile.class.php';
2450
-
2451
-		$msgishtml = 0;
2452
-
2453
-		// Define $msg
2454
-		$mesg = '';
2455
-
2456
-		$outputlangs = new Translate("", $conf);
2457
-
2458
-		if (
2459
-			isset($this->conf->MAIN_LANG_DEFAULT)
2460
-			&& $this->conf->MAIN_LANG_DEFAULT != 'auto'
2461
-		) {  // If user has defined its own language (rare because in most cases, auto is used)
2462
-			$outputlangs->getDefaultLang($this->conf->MAIN_LANG_DEFAULT);
2463
-		}
2464
-
2465
-		if ($this->conf->MAIN_LANG_DEFAULT) {
2466
-			$outputlangs->setDefaultLang($this->conf->MAIN_LANG_DEFAULT);
2467
-		} else {    // If user has not defined its own language, we used current language
2468
-			$outputlangs = $langs;
2469
-		}
2470
-
2471
-		// Load translation files required by the page
2472
-		$outputlangs->loadLangs(["main", "errors", "users", "other"]);
2473
-
2474
-		$appli = getDolGlobalString('MAIN_APPLICATION_TITLE', constant('DOL_APPLICATION_TITLE'));
2475
-
2476
-		$subject = '[' . $appli . '] ' . $outputlangs->transnoentitiesnoconv("SubjectNewPassword", $appli);
2477
-
2478
-		// Define $urlwithroot
2479
-		$urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($dolibarr_main_url_root));
2480
-		$urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
2481
-
2482
-		if (!$changelater) {
2483
-			$url = $urlwithroot . '/';
2484
-			if (getDolGlobalString('URL_REDIRECTION_AFTER_CHANGEPASSWORD')) {
2485
-				$url = getDolGlobalString('URL_REDIRECTION_AFTER_CHANGEPASSWORD');
2486
-			}
2487
-
2488
-			dol_syslog(get_class($this) . "::send_password changelater is off, url=" . $url);
2489
-
2490
-			$mesg .= $outputlangs->transnoentitiesnoconv("RequestToResetPasswordReceived") . ".\n";
2491
-			$mesg .= $outputlangs->transnoentitiesnoconv("NewKeyIs") . " :\n\n";
2492
-			$mesg .= $outputlangs->transnoentitiesnoconv("Login") . " = " . $this->login . "\n";
2493
-			$mesg .= $outputlangs->transnoentitiesnoconv("Password") . " = " . $password . "\n\n";
2494
-			$mesg .= "\n";
2495
-
2496
-			$mesg .= $outputlangs->transnoentitiesnoconv("ClickHereToGoTo", $appli) . ': ' . $url . "\n\n";
2497
-			$mesg .= "--\n";
2498
-			$mesg .= $user->getFullName($outputlangs); // Username that send the email (not the user for who we want to reset password)
2499
-		} else {
2500
-			//print $password.'-'.$this->id.'-'.$conf->file->instance_unique_id;
2501
-			$url = $urlwithroot . '/user/passwordforgotten.php?action=validatenewpassword';
2502
-			$url .= '&username=' . urlencode($this->login) . "&passworduidhash=" . urlencode(dol_hash($password . '-' . $this->id . '-' . $conf->file->instance_unique_id));
2503
-			if (isModEnabled('multicompany')) {
2504
-				$url .= '&entity=' . (!empty($this->entity) ? $this->entity : 1);
2505
-			}
2506
-
2507
-			dol_syslog(get_class($this) . "::send_password changelater is on, url=" . $url);
2508
-
2509
-			$msgishtml = 1;
2510
-
2511
-			$mesg .= $outputlangs->transnoentitiesnoconv("RequestToResetPasswordReceived") . "<br>\n";
2512
-			$mesg .= $outputlangs->transnoentitiesnoconv("NewKeyWillBe") . " :<br>\n<br>\n";
2513
-			$mesg .= '<strong>' . $outputlangs->transnoentitiesnoconv("Login") . "</strong> = " . $this->login . "<br>\n";
2514
-			$mesg .= '<strong>' . $outputlangs->transnoentitiesnoconv("Password") . "</strong> = " . $password . "<br>\n<br>\n";
2515
-			$mesg .= "<br>\n";
2516
-			$mesg .= $outputlangs->transnoentitiesnoconv("YouMustClickToChange") . " :<br>\n";
2517
-			$mesg .= '<a href="' . $url . '" rel="noopener">' . $outputlangs->transnoentitiesnoconv("ConfirmPasswordChange") . '</a>' . "<br>\n<br>\n";
2518
-			$mesg .= $outputlangs->transnoentitiesnoconv("ForgetIfNothing") . "<br>\n<br>\n";
2519
-		}
2520
-
2521
-		$trackid = 'use' . $this->id;
2522
-		$sendcontext = 'password';
2523
-
2524
-		$mailfile = new CMailFile(
2525
-			$subject,
2526
-			$this->email,
2527
-			$conf->global->MAIN_MAIL_EMAIL_FROM,
2528
-			$mesg,
2529
-			[],
2530
-			[],
2531
-			[],
2532
-			'',
2533
-			'',
2534
-			0,
2535
-			$msgishtml,
2536
-			'',
2537
-			'',
2538
-			$trackid,
2539
-			'',
2540
-			$sendcontext
2541
-		);
2542
-
2543
-		if ($mailfile->sendfile()) {
2544
-			return 1;
2545
-		} else {
2546
-			$langs->trans("errors");
2547
-			$this->error = $langs->trans("ErrorFailedToSendPassword") . ' ' . $mailfile->error;
2548
-			return -1;
2549
-		}
2550
-	}
2551
-
2552
-
2553
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2554
-
2555
-	/**
2556
-	 *  Read clicktodial information for user
2557
-	 *
2558
-	 * @return int Return integer <0 if KO, >0 if OK
2559
-	 */
2560
-	public function fetch_clicktodial()
2561
-	{
2562
-		// phpcs:enable
2563
-		$sql = "SELECT url, login, pass, poste ";
2564
-		$sql .= " FROM " . $dbPrefix . "user_clicktodial as u";
2565
-		$sql .= " WHERE u.fk_user = " . ((int)$this->id);
2566
-
2567
-		$resql = $this->db->query($sql);
2568
-		if ($resql) {
2569
-			if ($this->db->num_rows($resql)) {
2570
-				$obj = $this->db->fetch_object($resql);
2571
-
2572
-				$this->clicktodial_url = $obj->url;
2573
-				$this->clicktodial_login = $obj->login;
2574
-				$this->clicktodial_password = $obj->pass;
2575
-				$this->clicktodial_poste = $obj->poste;
2576
-			}
2577
-
2578
-			$this->clicktodial_loaded = 1; // Data loaded (found or not)
2579
-
2580
-			$this->db->free($resql);
2581
-			return 1;
2582
-		} else {
2583
-			$this->error = $this->db->error();
2584
-			return -1;
2585
-		}
2586
-	}
2587
-
2588
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2589
-
2590
-	/**
2591
-	 *  Update clicktodial info
2592
-	 *
2593
-	 * @return int  Return integer <0 if KO, >0 if OK
2594
-	 */
2595
-	public function update_clicktodial()
2596
-	{
2597
-		global $config;
2598
-		$dbPrefix = $config->db->prefix;
2599
-
2600
-		// phpcs:enable
2601
-		$this->db->begin();
2602
-
2603
-		$sql = "DELETE FROM " . $dbPrefix . "user_clicktodial";
2604
-		$sql .= " WHERE fk_user = " . ((int)$this->id);
2605
-
2606
-		dol_syslog(get_class($this) . '::update_clicktodial', LOG_DEBUG);
2607
-		$result = $this->db->query($sql);
2608
-
2609
-		$sql = "INSERT INTO " . $dbPrefix . "user_clicktodial";
2610
-		$sql .= " (fk_user,url,login,pass,poste)";
2611
-		$sql .= " VALUES (" . $this->id;
2612
-		$sql .= ", '" . $this->db->escape($this->clicktodial_url) . "'";
2613
-		$sql .= ", '" . $this->db->escape($this->clicktodial_login) . "'";
2614
-		$sql .= ", '" . $this->db->escape($this->clicktodial_password) . "'";
2615
-		$sql .= ", '" . $this->db->escape($this->clicktodial_poste) . "')";
2616
-
2617
-		dol_syslog(get_class($this) . '::update_clicktodial', LOG_DEBUG);
2618
-		$result = $this->db->query($sql);
2619
-		if ($result) {
2620
-			$this->db->commit();
2621
-			return 1;
2622
-		} else {
2623
-			$this->db->rollback();
2624
-			$this->error = $this->db->lasterror();
2625
-			return -1;
2626
-		}
2627
-	}
2628
-
2629
-	/**
2630
-	 *  Add user into a group
2631
-	 *
2632
-	 * @param int $group Id of group
2633
-	 * @param int $entity Entity
2634
-	 * @param int $notrigger Disable triggers
2635
-	 *
2636
-	 * @return int                 Return integer <0 if KO, >0 if OK
2637
-	 */
2638
-	public function SetInGroup($group, $entity, $notrigger = 0)
2639
-	{
2640
-		// phpcs:enable
2641
-		global $conf, $langs, $user;
2642
-		global $config;
2643
-		$dbPrefix = $config->db->prefix;
2644
-
2645
-		$error = 0;
2646
-
2647
-		$this->db->begin();
2648
-
2649
-		$sql = "DELETE FROM " . $dbPrefix . "usergroup_user";
2650
-		$sql .= " WHERE fk_user  = " . ((int)$this->id);
2651
-		$sql .= " AND fk_usergroup = " . ((int)$group);
2652
-		$sql .= " AND entity = " . ((int)$entity);
2653
-
2654
-		$result = $this->db->query($sql);
2655
-
2656
-		$sql = "INSERT INTO " . $dbPrefix . "usergroup_user (entity, fk_user, fk_usergroup)";
2657
-		$sql .= " VALUES (" . ((int)$entity) . "," . ((int)$this->id) . "," . ((int)$group) . ")";
2658
-
2659
-		$result = $this->db->query($sql);
2660
-		if ($result) {
2661
-			if (!$error && !$notrigger) {
2662
-				$this->context = ['audit' => $langs->trans("UserSetInGroup"), 'newgroupid' => $group];
2663
-
2664
-				// Call trigger
2665
-				$result = $this->call_trigger('USER_MODIFY', $user);
2666
-				if ($result < 0) {
2667
-					$error++;
2668
-				}
2669
-				// End call triggers
2670
-			}
2671
-
2672
-			if (!$error) {
2673
-				$this->db->commit();
2674
-				return 1;
2675
-			} else {
2676
-				dol_syslog(get_class($this) . "::SetInGroup " . $this->error, LOG_ERR);
2677
-				$this->db->rollback();
2678
-				return -2;
2679
-			}
2680
-		} else {
2681
-			$this->error = $this->db->lasterror();
2682
-			$this->db->rollback();
2683
-			return -1;
2684
-		}
2685
-	}
2686
-
2687
-	/**
2688
-	 *  Remove a user from a group
2689
-	 *
2690
-	 * @param int $group Id of group
2691
-	 * @param int $entity Entity
2692
-	 * @param int $notrigger Disable triggers
2693
-	 *
2694
-	 * @return int                  Return integer <0 if KO, >0 if OK
2695
-	 */
2696
-	public function RemoveFromGroup($group, $entity, $notrigger = 0)
2697
-	{
2698
-		// phpcs:enable
2699
-		global $conf, $langs, $user;
2700
-
2701
-		$error = 0;
2702
-
2703
-		$this->db->begin();
2704
-
2705
-		$sql = "DELETE FROM " . $dbPrefix . "usergroup_user";
2706
-		$sql .= " WHERE fk_user  = " . ((int)$this->id);
2707
-		$sql .= " AND fk_usergroup = " . ((int)$group);
2708
-		if (empty($entity)) {
2709
-			$sql .= " AND entity IN (0, 1)";    // group may be in entity 0 (so $entity=0) and link with user into entity 1.
2710
-		} else {
2711
-			$sql .= " AND entity = " . ((int)$entity);
2712
-		}
2713
-
2714
-		$result = $this->db->query($sql);
2715
-		if ($result) {
2716
-			if (!$error && !$notrigger) {
2717
-				$this->context = ['audit' => $langs->trans("UserRemovedFromGroup"), 'oldgroupid' => $group];
2718
-
2719
-				// Call trigger
2720
-				$result = $this->call_trigger('USER_MODIFY', $user);
2721
-				if ($result < 0) {
2722
-					$error++;
2723
-				}
2724
-				// End call triggers
2725
-			}
2726
-
2727
-			if (!$error) {
2728
-				$this->db->commit();
2729
-				return 1;
2730
-			} else {
2731
-				dol_syslog(get_class($this) . "::RemoveFromGroup " . $this->error, LOG_ERR);
2732
-				$this->db->rollback();
2733
-				return -2;
2734
-			}
2735
-		} else {
2736
-			$this->error = $this->db->lasterror();
2737
-			$this->db->rollback();
2738
-			return -1;
2739
-		}
2740
-	}
2741
-
2742
-	/**
2743
-	 *  Return a link with photo
2744
-	 *  Use this->id,this->photo
2745
-	 *
2746
-	 * @return int     0=Valid, >0 if not valid
2747
-	 */
2748
-	public function isNotIntoValidityDateRange()
2749
-	{
2750
-		include_once BASE_PATH . '/../Dolibarr/Lib/Date.php';
2751
-
2752
-		$now = dol_now();
2753
-
2754
-		//dol_syslog("isNotIntoValidityDateRange ".$this->datestartvalidity);
2755
-
2756
-		// Check date start validity
2757
-		if ($this->datestartvalidity && $this->datestartvalidity > dol_get_last_hour($now)) {
2758
-			return 1;
2759
-		}
2760
-		// Check date end validity
2761
-		if ($this->dateendvalidity && $this->dateendvalidity < dol_get_first_hour($now)) {
2762
-			return 1;
2763
-		}
2764
-
2765
-		return 0;
2766
-	}
2767
-
2768
-	/**
2769
-	 *  Return a link with photo
2770
-	 *  Use this->id,this->photo
2771
-	 *
2772
-	 * @param int $width Width of image
2773
-	 * @param int $height Height of image
2774
-	 * @param string $cssclass Force a css class
2775
-	 * @param string $imagesize 'mini', 'small' or '' (original)
2776
-	 *
2777
-	 * @return string                  String with URL link
2778
-	 * @see getImagePublicURLOfObject()
2779
-	 */
2780
-	public function getPhotoUrl($width, $height, $cssclass = '', $imagesize = '')
2781
-	{
2782
-		$result = '<a href="' . DOL_URL_ROOT . '/user/card.php?id=' . $this->id . '">';
2783
-		$result .= Form::showphoto('userphoto', $this, $width, $height, 0, $cssclass, $imagesize);
2784
-		$result .= '</a>';
2785
-
2786
-		return $result;
2787
-	}
2788
-
2789
-	/**
2790
-	 * Return array of data to show into tooltips
2791
-	 *
2792
-	 * @param array $params Array with options, infologin
2793
-	 *
2794
-	 * @return array
2795
-	 * @since v18
2796
-	 */
2797
-	public function getTooltipContentArray($params)
2798
-	{
2799
-		global $conf, $langs, $menumanager;
2800
-		global $dolibarr_main_demo;
2801
-
2802
-		$infologin = $params['infologin'] ?? 0;
2803
-		$option = $params['option'] ?? '';
2804
-
2805
-		$data = [];
2806
-		if (!empty($this->photo)) {
2807
-			$photo = '<div class="photointooltip floatright">';
2808
-			$photo .= Form::showphoto('userphoto', $this, 0, 60, 0, 'photoref photowithmargin photologintooltip', 'small', 0, 1); // Force height to 60 so we total height of tooltip can be calculated and collision can be managed
2809
-			$photo .= '</div>';
2810
-			$data['photo'] = $photo;
2811
-			//$label .= '<div style="clear: both;"></div>';
2812
-		}
2813
-
2814
-		// Info Login
2815
-		$data['opendiv'] = '<div class="centpercent divtooltip">';
2816
-		$data['picto'] = img_picto('', $this->picto) . ' <u class="paddingrightonly">' . $langs->trans("User") . '</u> ' . $this->getLibStatut(4);
2817
-		$data['name'] = '<br><b>' . $langs->trans('Name') . ':</b> ' . dol_string_nohtmltag($this->getFullName($langs, ''));
2818
-		if (!empty($this->login)) {
2819
-			$data['login'] = '<br><b>' . $langs->trans('Login') . ':</b> ' . dol_string_nohtmltag($this->login);
2820
-		}
2821
-		if (!empty($this->job)) {
2822
-			$data['job'] = '<br><b>' . $langs->trans("Job") . ':</b> ' . dol_string_nohtmltag($this->job);
2823
-		}
2824
-		$data['email'] = '<br><b>' . $langs->trans("Email") . ':</b> ' . dol_string_nohtmltag($this->email);
2825
-		if (!empty($this->office_phone) || !empty($this->office_fax) || !empty($this->fax)) {
2826
-			$phonelist = [];
2827
-			if ($this->office_phone) {
2828
-				$phonelist[] = dol_print_phone($this->office_phone, $this->country_code, $this->id, 0, '', '&nbsp', 'phone');
2829
-			}
2830
-			if ($this->office_fax) {
2831
-				$phonelist[] = dol_print_phone($this->office_fax, $this->country_code, $this->id, 0, '', '&nbsp', 'fax');
2832
-			}
2833
-			if ($this->user_mobile) {
2834
-				$phonelist[] = dol_print_phone($this->user_mobile, $this->country_code, $this->id, 0, '', '&nbsp', 'mobile');
2835
-			}
2836
-			$data['phones'] = '<br><b>' . $langs->trans('Phone') . ':</b> ' . implode('&nbsp;', $phonelist);
2837
-		}
2838
-		if (!empty($this->admin)) {
2839
-			$data['administrator'] = '<br><b>' . $langs->trans("Administrator") . '</b>: ' . yn($this->admin);
2840
-		}
2841
-		if (!empty($this->accountancy_code) || $option == 'accountancy') {
2842
-			$langs->load("companies");
2843
-			$data['accountancycode'] = '<br><b>' . $langs->trans("AccountancyCode") . '</b>: ' . $this->accountancy_code;
2844
-		}
2845
-		$company = '';
2846
-		if (!empty($this->socid)) { // Add thirdparty for external users
2847
-			$thirdpartystatic = new Societe($this->db);
2848
-			$thirdpartystatic->fetch($this->socid);
2849
-			$companyimg = '';
2850
-			if (empty($params['hidethirdpartylogo'])) {
2851
-				$companyimg = ' ' . $thirdpartystatic->getNomUrl(2, (($option == 'nolink') ? 'nolink' : '')); // picto only of company
2852
-			}
2853
-			$company = ' (' . $langs->trans("Company") . ': ' . ($companyimg ? $companyimg : img_picto('', 'company')) . ' ' . dol_string_nohtmltag($thirdpartystatic->name) . ')';
2854
-		}
2855
-		$type = ($this->socid ? $langs->trans("ExternalUser") . $company : $langs->trans("InternalUser"));
2856
-		$data['type'] = '<br><b>' . $langs->trans("Type") . ':</b> ' . $type;
2857
-		$data['closediv'] = '</div>';
2858
-
2859
-		if ($infologin > 0) {
2860
-			$data['newlinelogin'] = '<br>';
2861
-			$data['session'] = '<br><u>' . $langs->trans("Session") . '</u>';
2862
-			$data['ip'] = '<br><b>' . $langs->trans("IPAddress") . '</b>: ' . dol_string_nohtmltag(getUserRemoteIP());
2863
-			if (getDolGlobalString('MAIN_MODULE_MULTICOMPANY')) {
2864
-				$data['multicompany'] = '<br><b>' . $langs->trans("ConnectedOnMultiCompany") . ':</b> ' . $conf->entity . ' (User entity ' . $this->entity . ')';
2865
-			}
2866
-			$data['authentication'] = '<br><b>' . $langs->trans("AuthenticationMode") . ':</b> ' . dol_string_nohtmltag($_SESSION["dol_authmode"] . (empty($dolibarr_main_demo) ? '' : ' (demo)'));
2867
-			$data['connectedsince'] = '<br><b>' . $langs->trans("ConnectedSince") . ':</b> ' . dol_print_date($this->datelastlogin, "dayhour", 'tzuser');
2868
-			$data['previousconnexion'] = '<br><b>' . $langs->trans("PreviousConnexion") . ':</b> ' . dol_print_date($this->datepreviouslogin, "dayhour", 'tzuser');
2869
-			$data['currenttheme'] = '<br><b>' . $langs->trans("CurrentTheme") . ':</b> ' . dol_string_nohtmltag($conf->theme);
2870
-			$data['currentmenumanager'] = '<br><b>' . $langs->trans("CurrentMenuManager") . ':</b> ' . dol_string_nohtmltag($menumanager->name);
2871
-			$s = picto_from_langcode($langs->getDefaultLang());
2872
-			$data['currentuserlang'] = '<br><b>' . $langs->trans("CurrentUserLanguage") . ':</b> ' . dol_string_nohtmltag(($s ? $s . ' ' : '') . $langs->getDefaultLang());
2873
-			$data['browser'] = '<br><b>' . $langs->trans("Browser") . ':</b> ' . dol_string_nohtmltag($conf->browser->name . ($conf->browser->version ? ' ' . $conf->browser->version : '') . ' (' . $_SERVER['HTTP_USER_AGENT'] . ')');
2874
-			$data['layout'] = '<br><b>' . $langs->trans("Layout") . ':</b> ' . dol_string_nohtmltag($conf->browser->layout);
2875
-			$data['screen'] = '<br><b>' . $langs->trans("Screen") . ':</b> ' . dol_string_nohtmltag($_SESSION['dol_screenwidth'] . ' x ' . $_SESSION['dol_screenheight']);
2876
-			if ($conf->browser->layout == 'phone') {
2877
-				$data['phone'] = '<br><b>' . $langs->trans("Phone") . ':</b> ' . $langs->trans("Yes");
2878
-			}
2879
-			if (!empty($_SESSION["disablemodules"])) {
2880
-				$data['disabledmodules'] = '<br><b>' . $langs->trans("DisabledModules") . ':</b> <br>' . dol_string_nohtmltag(implode(', ', explode(',', $_SESSION["disablemodules"])));
2881
-			}
2882
-		}
2883
-
2884
-		return $data;
2885
-	}
2886
-
2887
-	/**
2888
-	 *  Return the label of the status of user (active, inactive)
2889
-	 *
2890
-	 * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short
2891
-	 *                  label + Picto, 6=Long label + Picto
2892
-	 *
2893
-	 * @return string                 Label of status
2894
-	 */
2895
-	public function getLibStatut($mode = 0)
2896
-	{
2897
-		return $this->LibStatut(isset($this->statut) ? (int)$this->statut : (int)$this->status, $mode);
2898
-	}
2899
-
2900
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2901
-
2902
-	/**
2903
-	 *  Return the label of a status of user (active, inactive)
2904
-	 *
2905
-	 * @param int $status Id status
2906
-	 * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short
2907
-	 *                    label + Picto, 6=Long label + Picto
2908
-	 *
2909
-	 * @return string                  Label of status
2910
-	 */
2911
-	public function LibStatut($status, $mode = 0)
2912
-	{
2913
-		// phpcs:enable
2914
-		global $langs;
2915
-
2916
-		if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2917
-			global $langs;
2918
-			//$langs->load("mymodule");
2919
-			$this->labelStatus[self::STATUS_ENABLED] = $langs->transnoentitiesnoconv('Enabled');
2920
-			$this->labelStatus[self::STATUS_DISABLED] = $langs->transnoentitiesnoconv('Disabled');
2921
-			$this->labelStatusShort[self::STATUS_ENABLED] = $langs->transnoentitiesnoconv('Enabled');
2922
-			$this->labelStatusShort[self::STATUS_DISABLED] = $langs->transnoentitiesnoconv('Disabled');
2923
-		}
2924
-
2925
-		$statusType = 'status5';
2926
-		if ($status == self::STATUS_ENABLED) {
2927
-			$statusType = 'status4';
2928
-		}
2929
-
2930
-		$label = $this->labelStatus[$status];
2931
-		$labelshort = $this->labelStatusShort[$status];
2932
-
2933
-		$now = dol_now();
2934
-		if (!empty($this->datestartvalidity) && $now < $this->datestartvalidity) {
2935
-			$statusType = 'status3';
2936
-			$label .= ' (' . $langs->trans("UserNotYetValid") . ')';
2937
-		}
2938
-		if (!empty($this->dateendvalidity) && $now > ($this->dateendvalidity + 24 * 3600 - 1)) {
2939
-			$statusType = 'status2';
2940
-			$label .= ' (' . $langs->trans("UserExpired") . ')';
2941
-		}
2942
-
2943
-		return dolGetStatus($label, $labelshort, '', $statusType, $mode);
2944
-	}
2945
-
2946
-	/**
2947
-	 *  Return a HTML link to the user card (with optionally the picto)
2948
-	 *  Use this->id,this->lastname, this->firstname
2949
-	 *
2950
-	 * @param int $withpictoimg Include picto in link (0=No picto, 1=Include picto into link, 2=Only
2951
-	 *                                       picto, -1=Include photo into link, -2=Only picto photo, -3=Only photo very
2952
-	 *                                       small)
2953
-	 * @param string $option On what the link point to ('leave', 'accountancy', 'nolink', )
2954
-	 * @param integer $infologin 0=Add default info tooltip, 1=Add complete info tooltip, -1=No info
2955
-	 *                                       tooltip
2956
-	 * @param integer $notooltip 1=Disable tooltip on picto and name
2957
-	 * @param int $maxlen Max length of visible user name
2958
-	 * @param int $hidethirdpartylogo Hide logo of thirdparty if user is external user
2959
-	 * @param string $mode ''=Show firstname and lastname, 'firstname'=Show only firstname,
2960
-	 *                                       'firstelselast'=Show firstname or lastname if not defined, 'login'=Show
2961
-	 *                                       login
2962
-	 * @param string $morecss Add more css on link
2963
-	 * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save
2964
-	 *                                       lastsearch_values whenclicking
2965
-	 *
2966
-	 * @return string                              String with URL
2967
-	 */
2968
-	public function getNomUrl($withpictoimg = 0, $option = '', $infologin = 0, $notooltip = 0, $maxlen = 24, $hidethirdpartylogo = 0, $mode = '', $morecss = '', $save_lastsearch_value = -1)
2969
-	{
2970
-		global $langs, $conf, $db, $hookmanager, $user;
2971
-		global $dolibarr_main_authentication, $dolibarr_main_demo;
2972
-
2973
-		if (!$user->hasRight('user', 'user', 'read') && $user->id != $this->id) {
2974
-			$option = 'nolink';
2975
-		}
2976
-
2977
-		if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpictoimg) {
2978
-			$withpictoimg = 0;
2979
-		}
2980
-
2981
-		$result = '';
2982
-		$params = [
2983
-			'id' => $this->id,
2984
-			'objecttype' => $this->element,
2985
-			'infologin' => $infologin,
2986
-			'option' => $option,
2987
-			'hidethirdpartylogo' => $hidethirdpartylogo,
2988
-		];
2989
-		$classfortooltip = 'classfortooltip';
2990
-		$dataparams = '';
2991
-		if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2992
-			$classfortooltip = 'classforajaxtooltip';
2993
-			$dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
2994
-			$label = '';
2995
-		} else {
2996
-			$label = implode($this->getTooltipContentArray($params));
2997
-		}
2998
-
2999
-		$companylink = '';
3000
-		if (!empty($this->socid)) { // Add thirdparty for external users
3001
-			$thirdpartystatic = new Societe($this->db);
3002
-			$thirdpartystatic->fetch($this->socid);
3003
-			if (empty($hidethirdpartylogo)) {
3004
-				$companylink = ' ' . $thirdpartystatic->getNomUrl(2, (($option == 'nolink') ? 'nolink' : '')); // picto only of company
3005
-			}
3006
-		}
3007
-
3008
-		if ($infologin < 0) {
3009
-			$label = '';
3010
-		}
3011
-
3012
-		$url = DOL_URL_ROOT . '/user/card.php?id=' . $this->id;
3013
-		if ($option == 'leave') {
3014
-			$url = DOL_URL_ROOT . '/holiday/list.php?id=' . $this->id;
3015
-		}
3016
-
3017
-		if ($option != 'nolink') {
3018
-			// Add param to save lastsearch_values or not
3019
-			$add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3020
-			if ($save_lastsearch_value == -1 && isset($_SERVER['PHP_SELF']) && preg_match('/list\.php/', $_SERVER['PHP_SELF'])) {
3021
-				$add_save_lastsearch_values = 1;
3022
-			}
3023
-			if ($add_save_lastsearch_values) {
3024
-				$url .= '&save_lastsearch_values=1';
3025
-			}
3026
-		}
3027
-
3028
-		$linkstart = '<a href="' . $url . '"';
3029
-		$linkclose = "";
3030
-		if (empty($notooltip)) {
3031
-			if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3032
-				$langs->load("users");
3033
-				$label = $langs->trans("ShowUser");
3034
-				$linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
3035
-			}
3036
-			$linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
3037
-			$linkclose .= $dataparams . ' class="' . $classfortooltip . ($morecss ? ' ' . $morecss : '') . '"';
3038
-		} else {
3039
-			$linkclose = ($morecss ? ' class="' . $morecss . '"' : '');
3040
-		}
3041
-
3042
-		$linkstart .= $linkclose . '>';
3043
-		$linkend = '</a>';
3044
-
3045
-		//if ($withpictoimg == -1) $result.='<div class="nowrap">';
3046
-		$result .= (($option == 'nolink') ? '' : $linkstart);
3047
-		if ($withpictoimg) {
3048
-			$paddafterimage = '';
3049
-			if (abs((int)$withpictoimg) == 1) {
3050
-				$paddafterimage = 'style="margin-' . ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right') . ': 3px;"';
3051
-			}
3052
-			// Only picto
3053
-			if ($withpictoimg > 0) {
3054
-				$picto = '<!-- picto user --><span class="nopadding userimg' . ($morecss ? ' ' . $morecss : '') . '"><div class="valignmiddle userphoto inline-block center marginrightonlyshort"' . ($paddafterimage ? ' ' . $paddafterimage : '') . '>' . img_object('', 'user', 'class=""', 0, 0, $notooltip ? 0 : 1) . '</div></span>';
3055
-			} else {
3056
-				// Picto must be a photo
3057
-				$picto = '<!-- picto photo user --><span class="nopadding userimg' . ($morecss ? ' ' . $morecss : '') . '"' . ($paddafterimage ? ' ' . $paddafterimage : '') . '>' . Form::showphoto('userphoto', $this, 0, 0, 0, 'userphoto' . ($withpictoimg == -3 ? 'small' : ''), 'mini', 0, 1) . '</span>';
3058
-			}
3059
-			$result .= $picto;
3060
-		}
3061
-		if ($withpictoimg > -2 && $withpictoimg != 2) {
3062
-			if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3063
-				$result .= '<span class="nopadding usertext' . ((!isset($this->status) || $this->status) ? '' : ' strikefordisabled') . ($morecss ? ' ' . $morecss : '') . '">';
3064
-			}
3065
-			if ($mode == 'login') {
3066
-				$result .= dol_string_nohtmltag(dol_trunc($this->login, $maxlen));
3067
-			} else {
3068
-				$result .= dol_string_nohtmltag($this->getFullName($langs, '', ($mode == 'firstelselast' ? 3 : ($mode == 'firstname' ? 2 : -1)), $maxlen));
3069
-			}
3070
-			if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3071
-				$result .= '</span>';
3072
-			}
3073
-		}
3074
-		$result .= (($option == 'nolink') ? '' : $linkend);
3075
-		//if ($withpictoimg == -1) $result.='</div>';
3076
-
3077
-		$result .= $companylink;
3078
-
3079
-		global $action;
3080
-		$hookmanager->initHooks(['userdao']);
3081
-		$parameters = ['id' => $this->id, 'getnomurl' => &$result];
3082
-		$reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3083
-		if ($reshook > 0) {
3084
-			$result = $hookmanager->resPrint;
3085
-		} else {
3086
-			$result .= $hookmanager->resPrint;
3087
-		}
3088
-
3089
-		return $result;
3090
-	}
3091
-
3092
-
3093
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
3094
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3095
-
3096
-	/**
3097
-	 *  Return if a user has a permission.
3098
-	 *  You can use it like this: if ($user->hasRight('module', 'level11')).
3099
-	 *  It replaces old syntax: if ($user->rights->module->level1)
3100
-	 *
3101
-	 * @param string $module Module of permission to check
3102
-	 * @param string $permlevel1 Permission level1 (Example: 'read', 'write', 'delete')
3103
-	 * @param string $permlevel2 Permission level2
3104
-	 *
3105
-	 * @return int                     1 if user has permission, 0 if not.
3106
-	 * @see    clearrights(), delrights(), getrights(), hasRight()
3107
-	 */
3108
-	public function hasRight($module, $permlevel1, $permlevel2 = '')
3109
-	{
3110
-		// For compatibility with bad naming permissions on module
3111
-		$moduletomoduletouse = [
3112
-			'compta' => 'comptabilite',
3113
-			'contract' => 'contrat',
3114
-			'member' => 'adherent',
3115
-			'mo' => 'mrp',
3116
-			'order' => 'commande',
3117
-			'produit' => 'product',
3118
-			'project' => 'projet',
3119
-			'propale' => 'propal',
3120
-			'shipping' => 'expedition',
3121
-			'task' => 'task@projet',
3122
-			'fichinter' => 'ficheinter',
3123
-			'inventory' => 'stock',
3124
-			'invoice' => 'facture',
3125
-			'invoice_supplier' => 'fournisseur',
3126
-			'order_supplier' => 'fournisseur',
3127
-			'knowledgerecord' => 'knowledgerecord@knowledgemanagement',
3128
-			'skill@hrm' => 'all@hrm', // skill / job / position objects rights are for the moment grouped into right level "all"
3129
-			'job@hrm' => 'all@hrm', // skill / job / position objects rights are for the moment grouped into right level "all"
3130
-			'position@hrm' => 'all@hrm', // skill / job / position objects rights are for the moment grouped into right level "all"
3131
-			'facturerec' => 'facture',
3132
-			'margins' => 'margin',
3133
-		];
3134
-
3135
-		if (!empty($moduletomoduletouse[$module])) {
3136
-			$module = $moduletomoduletouse[$module];
3137
-		}
3138
-
3139
-		$moduleRightsMapping = [
3140
-			'product' => 'produit',
3141
-			'margin' => 'margins',
3142
-			'comptabilite' => 'compta',
3143
-		];
3144
-
3145
-		$rightsPath = $module;
3146
-		if (!empty($moduleRightsMapping[$rightsPath])) {
3147
-			$rightsPath = $moduleRightsMapping[$rightsPath];
3148
-		}
3149
-
3150
-		// If module is abc@module, we check permission user->hasRight(module, abc, permlevel1)
3151
-		$tmp = explode('@', $rightsPath, 2);
3152
-		if (!empty($tmp[1])) {
3153
-			if (strpos($module, '@') !== false) {
3154
-				$module = $tmp[1];
3155
-			}
3156
-			if ($tmp[0] != $tmp[1]) {
3157
-				// If $module = 'myobject@mymodule'
3158
-				$rightsPath = $tmp[1];
3159
-				$permlevel2 = $permlevel1;
3160
-				$permlevel1 = $tmp[0];
3161
-			} else {
3162
-				// If $module = 'abc@abc'
3163
-				$rightsPath = $tmp[1];
3164
-			}
3165
-		}
3166
-
3167
-		// In $conf->modules, we have 'accounting', 'product', 'facture', ...
3168
-		// In $user->rights, we have 'accounting', 'produit', 'facture', ...
3169
-		//var_dump($this->rights->$rightsPath);
3170
-		//var_dump($conf->modules);
3171
-		//var_dump($module.' '.isModEnabled($module).' '.$rightsPath.' '.$permlevel1.' '.$permlevel2);
3172
-		if (!isModEnabled($module)) {
3173
-			return 0;
3174
-		}
3175
-
3176
-		// Special case for external user
3177
-		if (!empty($this->socid)) {
3178
-			if ($module = 'societe' && $permlevel1 = 'client' && $permlevel2 == 'voir') {
3179
-				return 0;   // An external user never has the permission ->societe->client->voir to see all thirdparties (always restricted to himself)
3180
-			}
3181
-		}
3182
-
3183
-		// For compatibility with bad naming permissions on permlevel1
3184
-		if ($permlevel1 == 'propale') {
3185
-			$permlevel1 = 'propal';
3186
-		}
3187
-		if ($permlevel1 == 'member') {
3188
-			$permlevel1 = 'adherent';
3189
-		}
3190
-		if ($permlevel1 == 'recruitmentcandidature') {
3191
-			$permlevel1 = 'recruitmentjobposition';
3192
-		}
3193
-
3194
-		//var_dump($this->rights);
3195
-		//var_dump($rightsPath.' '.$permlevel1.' '.$permlevel2);
3196
-		if (empty($rightsPath) || empty($this->rights) || empty($this->rights->$rightsPath) || empty($permlevel1)) {
3197
-			return 0;
3198
-		}
3199
-
3200
-		if ($permlevel2) {
3201
-			if (!empty($this->rights->$rightsPath->$permlevel1)) {
3202
-				if (!empty($this->rights->$rightsPath->$permlevel1->$permlevel2)) {
3203
-					return $this->rights->$rightsPath->$permlevel1->$permlevel2;
3204
-				}
3205
-				// For backward compatibility with old permissions called "lire", "creer", "create", "supprimer"
3206
-				// instead of "read", "write", "delete"
3207
-				if ($permlevel2 == 'read' && !empty($this->rights->$rightsPath->$permlevel1->lire)) {
3208
-					return $this->rights->$rightsPath->$permlevel1->lire;
3209
-				}
3210
-				if ($permlevel2 == 'write' && !empty($this->rights->$rightsPath->$permlevel1->creer)) {
3211
-					return $this->rights->$rightsPath->$permlevel1->creer;
3212
-				}
3213
-				if ($permlevel2 == 'write' && !empty($this->rights->$rightsPath->$permlevel1->create)) {
3214
-					return $this->rights->$rightsPath->$permlevel1->create;
3215
-				}
3216
-				if ($permlevel2 == 'delete' && !empty($this->rights->$rightsPath->$permlevel1->supprimer)) {
3217
-					return $this->rights->$rightsPath->$permlevel1->supprimer;
3218
-				}
3219
-			}
3220
-		} else {
3221
-			if (!empty($this->rights->$rightsPath->$permlevel1)) {
3222
-				return $this->rights->$rightsPath->$permlevel1;
3223
-			}
3224
-			// For backward compatibility with old permissions called "lire", "creer", "create", "supprimer"
3225
-			// instead of "read", "write", "delete"
3226
-			if ($permlevel1 == 'read' && !empty($this->rights->$rightsPath->lire)) {
3227
-				return $this->rights->$rightsPath->lire;
3228
-			}
3229
-			if ($permlevel1 == 'write' && !empty($this->rights->$rightsPath->creer)) {
3230
-				return $this->rights->$rightsPath->creer;
3231
-			}
3232
-			if ($permlevel1 == 'write' && !empty($this->rights->$rightsPath->create)) {
3233
-				return $this->rights->$rightsPath->create;
3234
-			}
3235
-			if ($permlevel1 == 'delete' && !empty($this->rights->$rightsPath->supprimer)) {
3236
-				return $this->rights->$rightsPath->supprimer;
3237
-			}
3238
-		}
3239
-
3240
-		return 0;
3241
-	}
3242
-
3243
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
3244
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3245
-
3246
-	/**
3247
-	 *  Return clickable link of login (optionally with picto)
3248
-	 *
3249
-	 * @param int $withpictoimg Include picto into link (1=picto, -1=photo)
3250
-	 * @param string $option On what the link point to ('leave', 'accountancy', 'nolink', )
3251
-	 * @param integer $notooltip 1=Disable tooltip on picto and name
3252
-	 * @param string $morecss Add more css on link
3253
-	 *
3254
-	 * @return string                      String with URL
3255
-	 */
3256
-	public function getLoginUrl($withpictoimg = 0, $option = '', $notooltip = 0, $morecss = '')
3257
-	{
3258
-		global $langs, $user;
3259
-
3260
-		$result = '';
3261
-
3262
-		$linkstart = '<a href="' . DOL_URL_ROOT . '/user/card.php?id=' . $this->id . '">';
3263
-		$linkend = '</a>';
3264
-
3265
-		//Check user's rights to see an other user
3266
-		if ((!$user->hasRight('user', 'user', 'lire') && $this->id != $user->id)) {
3267
-			$option = 'nolink';
3268
-		}
3269
-
3270
-		if ($option == 'xxx') {
3271
-			$linkstart = '<a href="' . DOL_URL_ROOT . '/user/card.php?id=' . $this->id . '">';
3272
-			$linkend = '</a>';  // @phan-suppress-current-line PhanPluginRedundantAssignment
3273
-		}
3274
-
3275
-		if ($option == 'nolink') {
3276
-			$linkstart = '';
3277
-			$linkend = '';
3278
-		}
3279
-
3280
-		$result .= $linkstart;
3281
-		if ($withpictoimg) {
3282
-			$paddafterimage = '';
3283
-			if (abs($withpictoimg) == 1) {
3284
-				$paddafterimage = 'style="margin-' . ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right') . ': 3px;"';
3285
-			}
3286
-			// Only picto
3287
-			if ($withpictoimg > 0) {
3288
-				$picto = '<!-- picto user --><span class="nopadding userimg' . ($morecss ? ' ' . $morecss : '') . '">' . img_object('', 'user', $paddafterimage . ' ' . ($notooltip ? '' : 'class="paddingright classfortooltip"'), 0, 0, $notooltip ? 0 : 1) . '</span>';
3289
-			} else {
3290
-				// Picto must be a photo
3291
-				$picto = '<!-- picto photo user --><span class="nopadding userimg' . ($morecss ? ' ' . $morecss : '') . '"' . ($paddafterimage ? ' ' . $paddafterimage : '') . '>' . Form::showphoto('userphoto', $this, 0, 0, 0, 'userphoto' . ($withpictoimg == -3 ? 'small' : ''), 'mini', 0, 1) . '</span>';
3292
-			}
3293
-			$result .= $picto;
3294
-		}
3295
-		$result .= $this->login;
3296
-		$result .= $linkend;
3297
-
3298
-		return $result;
3299
-	}
3300
-
3301
-	/**
3302
-	 *  Return clicable link of object (optionally with picto)
3303
-	 *
3304
-	 * @param string $option Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
3305
-	 * @param array $arraydata Array of data
3306
-	 *
3307
-	 * @return     string                              HTML Code for Kanban thumb.
3308
-	 */
3309
-	public function getKanbanView($option = '', $arraydata = null)
3310
-	{
3311
-		global $langs;
3312
-
3313
-		$selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3314
-
3315
-		$return = '<div class="box-flex-item box-flex-grow-zero">';
3316
-		$return .= '<div class="info-box info-box-sm">';
3317
-		$return .= '<span class="info-box-icon bg-infobox-action">';
3318
-
3319
-		$label = '';
3320
-		if (!empty($this->photo)) {
3321
-			//$label .= '<div class="photointooltip floatright">';
3322
-			$label .= Form::showphoto('userphoto', $this, 0, 60, 0, 'photokanban photoref photowithmargin photologintooltip', 'small', 0, 1); // Force height to 60 so we total height of tooltip can be calculated and collision can be managed
3323
-			//$label .= '</div>';
3324
-			//$label .= '<div style="clear: both;"></div>';
3325
-			$return .= $label;
3326
-		} else {
3327
-			$return .= img_picto('', $this->picto);
3328
-		}
3329
-
3330
-		//$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
3331
-		$return .= '</span>';
3332
-		$return .= '<div class="info-box-content">';
3333
-		$return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl(0, '', 0, 0, 24, 0, '', 'valignmiddle') : $this->ref);
3334
-		if (isModEnabled('multicompany') && $this->admin && !$this->entity) {
3335
-			$return .= img_picto($langs->trans("SuperAdministratorDesc"), 'redstar', 'class="valignmiddle paddingright paddingleft"');
3336
-		} elseif ($this->admin) {
3337
-			$return .= img_picto($langs->trans("AdministratorDesc"), 'star', 'class="valignmiddle paddingright paddingleft"');
3338
-		}
3339
-		$return .= '</span>';
3340
-		if ($selected >= 0) {
3341
-			$return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
3342
-		}
3343
-		if (property_exists($this, 'label')) {
3344
-			$return .= '<br><span class="info-box-label opacitymedium">' . $this->label . '</span>';
3345
-		}
3346
-		if ($this->email) {
3347
-			$return .= '<br><span class="info-box-label opacitymedium small">' . img_picto('', 'email') . ' ' . $this->email . '</span>';
3348
-		}
3349
-		if (method_exists($this, 'getLibStatut')) {
3350
-			$return .= '<br><div class="info-box-status">' . $this->getLibStatut(3) . '</div>';
3351
-		}
3352
-		$return .= '</div>';
3353
-		$return .= '</div>';
3354
-		$return .= '</div>';
3355
-
3356
-		return $return;
3357
-	}
3358
-
3359
-	/**
3360
-	 *  Retourne chaine DN complete dans l'annuaire LDAP pour l'objet
3361
-	 *
3362
-	 * @param array $info Info array loaded by _load_ldap_info
3363
-	 * @param int $mode 0=Return full DN (uid=qqq,ou=xxx,dc=aaa,dc=bbb)
3364
-	 *                              1=Return parent (ou=xxx,dc=aaa,dc=bbb)
3365
-	 *                              2=Return key only (RDN) (uid=qqq)
3366
-	 *
3367
-	 * @return string              DN
3368
-	 */
3369
-	public function _load_ldap_dn($info, $mode = 0)
3370
-	{
3371
-		// phpcs:enable
3372
-		global $conf;
3373
-		$dn = '';
3374
-		if ($mode == 0) {
3375
-			$dn = getDolGlobalString('LDAP_KEY_USERS') . "=" . $info[getDolGlobalString('LDAP_KEY_USERS')] . "," . getDolGlobalString('LDAP_USER_DN');
3376
-		} elseif ($mode == 1) {
3377
-			$dn = getDolGlobalString('LDAP_USER_DN');
3378
-		} elseif ($mode == 2) {
3379
-			$dn = getDolGlobalString('LDAP_KEY_USERS') . "=" . $info[getDolGlobalString('LDAP_KEY_USERS')];
3380
-		}
3381
-		return $dn;
3382
-	}
3383
-
3384
-	/**
3385
-	 *  Initialize the info array (array of LDAP values) that will be used to call LDAP functions
3386
-	 *
3387
-	 * @return     array       Table with attribute information
3388
-	 */
3389
-	public function _load_ldap_info()
3390
-	{
3391
-		// phpcs:enable
3392
-		global $conf, $langs;
3393
-
3394
-		$info = [];
3395
-
3396
-		$socialnetworks = getArrayOfSocialNetworks();
3397
-
3398
-		$keymodified = false;
3399
-
3400
-		// Object classes
3401
-		$info["objectclass"] = explode(',', getDolGlobalString('LDAP_USER_OBJECT_CLASS'));
3402
-
3403
-		$this->fullname = $this->getFullName($langs);
3404
-
3405
-		// Possible LDAP KEY (constname => varname)
3406
-		$ldapkey = [
3407
-			'LDAP_FIELD_FULLNAME' => 'fullname',
3408
-			'LDAP_FIELD_NAME' => 'lastname',
3409
-			'LDAP_FIELD_FIRSTNAME' => 'firstname',
3410
-			'LDAP_FIELD_LOGIN' => 'login',
3411
-			'LDAP_FIELD_LOGIN_SAMBA' => 'login',
3412
-			'LDAP_FIELD_PHONE' => 'office_phone',
3413
-			'LDAP_FIELD_MOBILE' => 'user_mobile',
3414
-			'LDAP_FIELD_FAX' => 'office_fax',
3415
-			'LDAP_FIELD_MAIL' => 'email',
3416
-			'LDAP_FIELD_SID' => 'ldap_sid',
3417
-		];
3418
-
3419
-		// Champs
3420
-		foreach ($ldapkey as $constname => $varname) {
3421
-			if (!empty($this->$varname) && getDolGlobalString($constname)) {
3422
-				$info[getDolGlobalString($constname)] = $this->$varname;
3423
-
3424
-				// Check if it is the LDAP key and if its value has been changed
3425
-				if (getDolGlobalString('LDAP_KEY_USERS') && $conf->global->LDAP_KEY_USERS == getDolGlobalString($constname)) {
3426
-					if (!empty($this->oldcopy) && $this->$varname != $this->oldcopy->$varname) {
3427
-						$keymodified = true; // For check if LDAP key has been modified
3428
-					}
3429
-				}
3430
-			}
3431
-		}
3432
-		foreach ($socialnetworks as $key => $value) {
3433
-			if (!empty($this->socialnetworks[$value['label']]) && getDolGlobalString('LDAP_FIELD_' . strtoupper($value['label']))) {
3434
-				$info[getDolGlobalString('LDAP_FIELD_' . strtoupper($value['label']))] = $this->socialnetworks[$value['label']];
3435
-			}
3436
-		}
3437
-		if ($this->address && getDolGlobalString('LDAP_FIELD_ADDRESS')) {
3438
-			$info[getDolGlobalString('LDAP_FIELD_ADDRESS')] = $this->address;
3439
-		}
3440
-		if ($this->zip && getDolGlobalString('LDAP_FIELD_ZIP')) {
3441
-			$info[getDolGlobalString('LDAP_FIELD_ZIP')] = $this->zip;
3442
-		}
3443
-		if ($this->town && getDolGlobalString('LDAP_FIELD_TOWN')) {
3444
-			$info[getDolGlobalString('LDAP_FIELD_TOWN')] = $this->town;
3445
-		}
3446
-		if ($this->note_public && getDolGlobalString('LDAP_FIELD_DESCRIPTION')) {
3447
-			$info[getDolGlobalString('LDAP_FIELD_DESCRIPTION')] = dol_string_nohtmltag($this->note_public, 2);
3448
-		}
3449
-		if ($this->socid > 0) {
3450
-			$soc = new Societe($this->db);
3451
-			$soc->fetch($this->socid);
3452
-
3453
-			$info[getDolGlobalString('LDAP_FIELD_COMPANY')] = $soc->name;
3454
-			if ($soc->client == 1) {
3455
-				$info["businessCategory"] = "Customers";
3456
-			}
3457
-			if ($soc->client == 2) {
3458
-				$info["businessCategory"] = "Prospects";
3459
-			}
3460
-			if ($soc->fournisseur == 1) {
3461
-				$info["businessCategory"] = "Suppliers";
3462
-			}
3463
-		}
3464
-
3465
-		// When password is modified
3466
-		if (!empty($this->pass)) {
3467
-			if (getDolGlobalString('LDAP_FIELD_PASSWORD')) {
3468
-				$info[getDolGlobalString('LDAP_FIELD_PASSWORD')] = $this->pass; // this->pass = unencrypted password
3469
-			}
3470
-			if (getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')) {
3471
-				$info[getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')] = dol_hash($this->pass, 'openldap'); // Create OpenLDAP password (see LDAP_PASSWORD_HASH_TYPE)
3472
-			}
3473
-		} elseif (getDolGlobalString('LDAP_SERVER_PROTOCOLVERSION') !== '3') {
3474
-			// Set LDAP password if possible
3475
-			// If ldap key is modified and LDAPv3 we use ldap_rename function for avoid lose encrypt password
3476
-			if (getDolGlobalString('DATABASE_PWD_ENCRYPTED')) {
3477
-				// Just for the default MD5 !
3478
-				if (!getDolGlobalString('MAIN_SECURITY_HASH_ALGO')) {
3479
-					if ($this->pass_indatabase_crypted && getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')) {
3480
-						$info[getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')] = dolGetLdapPasswordHash($this->pass_indatabase_crypted, 'md5frommd5'); // Create OpenLDAP MD5 password from Dolibarr MD5 password
3481
-					}
3482
-				}
3483
-			} elseif (!empty($this->pass_indatabase)) {
3484
-				// Use $this->pass_indatabase value if exists
3485
-				if (getDolGlobalString('LDAP_FIELD_PASSWORD')) {
3486
-					$info[getDolGlobalString('LDAP_FIELD_PASSWORD')] = $this->pass_indatabase; // $this->pass_indatabase = unencrypted password
3487
-				}
3488
-				if (getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')) {
3489
-					$info[getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')] = dol_hash($this->pass_indatabase, 'openldap'); // Create OpenLDAP password (see LDAP_PASSWORD_HASH_TYPE)
3490
-				}
3491
-			}
3492
-		}
3493
-
3494
-		if (getDolGlobalString('LDAP_SERVER_TYPE') == 'egroupware') {
3495
-			$info["objectclass"][4] = "phpgwContact"; // compatibilite egroupware
3496
-
3497
-			$info['uidnumber'] = $this->id;
3498
-
3499
-			$info['phpgwTz'] = 0;
3500
-			$info['phpgwMailType'] = 'INTERNET';
3501
-			$info['phpgwMailHomeType'] = 'INTERNET';
3502
-
3503
-			$info["phpgwContactTypeId"] = 'n';
3504
-			$info["phpgwContactCatId"] = 0;
3505
-			$info["phpgwContactAccess"] = "public";
3506
-
3507
-			if (dol_strlen($this->egroupware_id) == 0) {
3508
-				$this->egroupware_id = 1;
3509
-			}
3510
-
3511
-			$info["phpgwContactOwner"] = $this->egroupware_id;
3512
-
3513
-			if ($this->email) {
3514
-				$info["rfc822Mailbox"] = $this->email;
3515
-			}
3516
-			if ($this->user_mobile) {
3517
-				$info["phpgwCellTelephoneNumber"] = $this->user_mobile;
3518
-			}
3519
-		}
3520
-
3521
-		if (getDolGlobalString('LDAP_FIELD_USERID')) {
3522
-			$info[getDolGlobalString('LDAP_FIELD_USERID')] = $this->id;
3523
-		}
3524
-		if (getDolGlobalString('LDAP_FIELD_GROUPID')) {
3525
-			$usergroup = new UserGroup($this->db);
3526
-			$groupslist = $usergroup->listGroupsForUser($this->id);
3527
-			$info[getDolGlobalString('LDAP_FIELD_GROUPID')] = '65534';
3528
-			if (!empty($groupslist)) {
3529
-				foreach ($groupslist as $groupforuser) {
3530
-					$info[getDolGlobalString('LDAP_FIELD_GROUPID')] = $groupforuser->id; //Select first group in list
3531
-					break;
3532
-				}
3533
-			}
3534
-		}
3535
-		if (getDolGlobalString('LDAP_FIELD_HOMEDIRECTORY') && getDolGlobalString('LDAP_FIELD_HOMEDIRECTORYPREFIX')) {
3536
-			$info[getDolGlobalString('LDAP_FIELD_HOMEDIRECTORY')] = "{$conf->global->LDAP_FIELD_HOMEDIRECTORYPREFIX}/$this->login";
3537
-		}
3538
-
3539
-		return $info;
3540
-	}
3541
-
3542
-	/**
3543
-	 *  Initialise an instance with random values.
3544
-	 *  Used to build previews or test instances.
3545
-	 *  id must be 0 if object instance is a specimen.
3546
-	 *
3547
-	 * @return int
3548
-	 */
3549
-	public function initAsSpecimen()
3550
-	{
3551
-		global $user, $langs;
3552
-
3553
-		$now = dol_now();
3554
-
3555
-		// Initialise parameters
3556
-		$this->id = 0;
3557
-		$this->ref = 'SPECIMEN';
3558
-		$this->specimen = 1;
3559
-
3560
-		$this->lastname = 'DOLIBARR';
3561
-		$this->firstname = 'SPECIMEN';
3562
-		$this->gender = 'man';
3563
-		$this->note_public = 'This is a note public';
3564
-		$this->note_private = 'This is a note private';
3565
-		$this->email = '[email protected]';
3566
-		$this->personal_email = '[email protected]';
3567
-		$this->socialnetworks = [
3568
-			'skype' => 'skypepseudo',
3569
-			'twitter' => 'twitterpseudo',
3570
-			'facebook' => 'facebookpseudo',
3571
-			'linkedin' => 'linkedinpseudo',
3572
-		];
3573
-		$this->office_phone = '0999999999';
3574
-		$this->office_fax = '0999999998';
3575
-		$this->user_mobile = '0999999997';
3576
-		$this->personal_mobile = '0999999996';
3577
-		$this->admin = 0;
3578
-		$this->login = 'dolibspec';
3579
-		$this->pass = 'dolibSpec+@123';
3580
-		//$this->pass_indatabase='dolibspec';                                   Set after a fetch
3581
-		//$this->pass_indatabase_crypted='e80ca5a88c892b0aaaf7e154853bccab';    Set after a fetch
3582
-		$this->datec = $now;
3583
-		$this->datem = $now;
3584
-
3585
-		$this->datelastlogin = $now;
3586
-		$this->iplastlogin = '127.0.0.1';
3587
-		$this->datepreviouslogin = $now;
3588
-		$this->ippreviouslogin = '127.0.0.1';
3589
-		$this->statut = 1;      // deprecated
3590
-		$this->status = 1;
3591
-
3592
-		$this->entity = 1;
3593
-
3594
-		return 1;
3595
-	}
3596
-
3597
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3598
-
3599
-	/**
3600
-	 *  Load info of user object
3601
-	 *
3602
-	 * @param int $id Id of user to load
3603
-	 *
3604
-	 * @return void
3605
-	 */
3606
-	public function info($id)
3607
-	{
3608
-		$sql = "SELECT u.rowid, u.login as ref, u.datec,";
3609
-		$sql .= " u.tms as date_modification, u.entity";
3610
-		$sql .= " FROM " . $dbPrefix . "user as u";
3611
-		$sql .= " WHERE u.rowid = " . ((int)$id);
3612
-
3613
-		$result = $this->db->query($sql);
3614
-		if ($result) {
3615
-			if ($this->db->num_rows($result)) {
3616
-				$obj = $this->db->fetch_object($result);
3617
-
3618
-				$this->id = $obj->rowid;
3619
-
3620
-				$this->ref = (!$obj->ref) ? $obj->rowid : $obj->ref;
3621
-				$this->date_creation = $this->db->jdate($obj->datec);
3622
-				$this->date_modification = $this->db->jdate($obj->date_modification);
3623
-				$this->entity = $obj->entity;
3624
-			}
3625
-
3626
-			$this->db->free($result);
3627
-		} else {
3628
-			dol_print_error($this->db);
3629
-		}
3630
-	}
3631
-
3632
-
3633
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3634
-
3635
-	/**
3636
-	 *    Return number of mass Emailing received by this contacts with its email
3637
-	 *
3638
-	 * @return       int     Number of EMailings
3639
-	 */
3640
-	public function getNbOfEMailings()
3641
-	{
3642
-		$sql = "SELECT count(mc.email) as nb";
3643
-		$sql .= " FROM " . $dbPrefix . "mailing_cibles as mc";
3644
-		$sql .= " WHERE mc.email = '" . $this->db->escape($this->email) . "'";
3645
-		$sql .= " AND mc.statut NOT IN (-1,0)"; // -1 error, 0 not sent, 1 sent with success
3646
-
3647
-		$resql = $this->db->query($sql);
3648
-		if ($resql) {
3649
-			$obj = $this->db->fetch_object($resql);
3650
-			$nb = $obj->nb;
3651
-
3652
-			$this->db->free($resql);
3653
-			return $nb;
3654
-		} else {
3655
-			$this->error = $this->db->error();
3656
-			return -1;
3657
-		}
3658
-	}
3659
-
3660
-	/**
3661
-	 *  Return number of existing users
3662
-	 *
3663
-	 * @param string $limitTo Limit to '' or 'active'
3664
-	 * @param string $option 'superadmin' = return for entity 0 only
3665
-	 * @param int $admin Filter on admin tag
3666
-	 *
3667
-	 * @return int                 Number of users
3668
-	 */
3669
-	public function getNbOfUsers($limitTo, $option = '', $admin = -1)
3670
-	{
3671
-		global $conf;
3672
-
3673
-		$sql = "SELECT count(rowid) as nb";
3674
-		$sql .= " FROM " . $dbPrefix . "user";
3675
-		if ($option == 'superadmin') {
3676
-			$sql .= " WHERE entity = 0";
3677
-		} else {
3678
-			$sql .= " WHERE entity IN (" . getEntity('user', 0) . ")";
3679
-			if ($limitTo == 'active') {
3680
-				$sql .= " AND statut = 1";
3681
-			}
3682
-		}
3683
-		if ($admin >= 0) {
3684
-			$sql .= " AND admin = " . (int)$admin;
3685
-		}
3686
-
3687
-		$resql = $this->db->query($sql);
3688
-		if ($resql) {
3689
-			$obj = $this->db->fetch_object($resql);
3690
-			$nb = (int)$obj->nb;
3691
-
3692
-			$this->db->free($resql);
3693
-			return $nb;
3694
-		} else {
3695
-			$this->error = $this->db->lasterror();
3696
-			return -1;
3697
-		}
3698
-	}
3699
-
3700
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3701
-
3702
-	/**
3703
-	 *  Update user using data from the LDAP
3704
-	 *
3705
-	 * @param Object $ldapuser Ladp User
3706
-	 *
3707
-	 * @return int                 Return integer <0 if KO, >0 if OK
3708
-	 */
3709
-	public function update_ldap2dolibarr(&$ldapuser)
3710
-	{
3711
-		// phpcs:enable
3712
-		// TODO: Voir pourquoi le update met à jour avec toutes les valeurs vide (global $user écrase ?)
3713
-		global $user, $conf;
3714
-
3715
-		$socialnetworks = getArrayOfSocialNetworks();
3716
-
3717
-		$tmpvar = getDolGlobalString('LDAP_FIELD_FIRSTNAME');
3718
-		$this->firstname = $ldapuser->$tmpvar;
3719
-		$tmpvar = getDolGlobalString('LDAP_FIELD_NAME');
3720
-		$this->lastname = $ldapuser->$tmpvar;
3721
-		$tmpvar = getDolGlobalString('LDAP_FIELD_LOGIN');
3722
-		$this->login = $ldapuser->$tmpvar;
3723
-		$tmpvar = getDolGlobalString('LDAP_FIELD_PASSWORD');
3724
-		$this->pass = $ldapuser->$tmpvar;
3725
-		$tmpvar = getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED');
3726
-		$this->pass_indatabase_crypted = $ldapuser->$tmpvar;
3727
-
3728
-		$tmpvar = getDolGlobalString('LDAP_FIELD_PHONE');
3729
-		$this->office_phone = $ldapuser->$tmpvar;
3730
-		$tmpvar = getDolGlobalString('LDAP_FIELD_MOBILE');
3731
-		$this->user_mobile = $ldapuser->$tmpvar;
3732
-		$tmpvar = getDolGlobalString('LDAP_FIELD_FAX');
3733
-		$this->office_fax = $ldapuser->$tmpvar;
3734
-		$tmpvar = getDolGlobalString('LDAP_FIELD_MAIL');
3735
-		$this->email = $ldapuser->$tmpvar;
3736
-		foreach ($socialnetworks as $key => $value) {
3737
-			$tmpvar = getDolGlobalString('LDAP_FIELD_' . strtoupper($value['label']));
3738
-			$this->socialnetworks[$value['label']] = $ldapuser->$tmpvar;
3739
-		}
3740
-		$tmpvar = getDolGlobalString('LDAP_FIELD_SID');
3741
-		$this->ldap_sid = $ldapuser->$tmpvar;
3742
-
3743
-		$tmpvar = getDolGlobalString('LDAP_FIELD_TITLE');
3744
-		$this->job = $ldapuser->$tmpvar;
3745
-		$tmpvar = getDolGlobalString('LDAP_FIELD_DESCRIPTION');
3746
-		$this->note_public = $ldapuser->$tmpvar;
3747
-
3748
-		$result = $this->update($user);
3749
-
3750
-		dol_syslog(get_class($this) . "::update_ldap2dolibarr result=" . $result, LOG_DEBUG);
3751
-
3752
-		return $result;
3753
-	}
3754
-
3755
-	/**
3756
-	 * Return and array with all instantiated first level children users of current user
3757
-	 *
3758
-	 * @return  User[]|int<-1,-1>
3759
-	 * @see getAllChildIds()
3760
-	 */
3761
-	public function get_children()
3762
-	{
3763
-		// phpcs:enable
3764
-		$sql = "SELECT rowid FROM " . $dbPrefix . "user";
3765
-		$sql .= " WHERE fk_user = " . ((int)$this->id);
3766
-
3767
-		dol_syslog(get_class($this) . "::get_children", LOG_DEBUG);
3768
-		$res = $this->db->query($sql);
3769
-		if ($res) {
3770
-			$users = [];
3771
-			while ($rec = $this->db->fetch_array($res)) {
3772
-				$user = new User($this->db);
3773
-				$user->fetch($rec['rowid']);
3774
-				$users[] = $user;
3775
-			}
3776
-			return $users;
3777
-		} else {
3778
-			dol_print_error($this->db);
3779
-			return -1;
3780
-		}
3781
-	}
3782
-
3783
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3784
-
3785
-	/**
3786
-	 *  Return list of all child user ids in hierarchy (all sublevels).
3787
-	 *  Note: Calling this function also reset full list of users into $this->users.
3788
-	 *
3789
-	 * @param int $addcurrentuser 1=Add also current user id to the list.
3790
-	 *
3791
-	 * @return     array                       Array of user id lower than user (all levels under user). This overwrite
3792
-	 *                                         this->users.
3793
-	 * @see get_children()
3794
-	 */
3795
-	public function getAllChildIds($addcurrentuser = 0)
3796
-	{
3797
-		$childids = [];
3798
-
3799
-		if (isset($this->cache_childids[$this->id])) {
3800
-			$childids = $this->cache_childids[$this->id];
3801
-		} else {
3802
-			// Init this->users
3803
-			$this->get_full_tree();
3804
-
3805
-			$idtoscan = $this->id;
3806
-
3807
-			dol_syslog("Build childid for id = " . $idtoscan);
3808
-			foreach ($this->users as $id => $val) {
3809
-				//var_dump($val['fullpath']);
3810
-				if (preg_match('/_' . $idtoscan . '_/', $val['fullpath'])) {
3811
-					$childids[$val['id']] = $val['id'];
3812
-				}
3813
-			}
3814
-		}
3815
-		$this->cache_childids[$this->id] = $childids;
3816
-
3817
-		if ($addcurrentuser) {
3818
-			$childids[$this->id] = $this->id;
3819
-		}
3820
-
3821
-		return $childids;
3822
-	}
3823
-
3824
-	/**
3825
-	 *  Build the hierarchy/tree of users into an array.
3826
-	 *  Set and return this->users that is an array sorted according to tree with arrays of:
3827
-	 *              id = id user
3828
-	 *              lastname
3829
-	 *              firstname
3830
-	 *              fullname = Name with full path to user
3831
-	 *              fullpath = Full path composed of the ids: "_grandparentid_parentid_id"
3832
-	 *
3833
-	 * @param int $deleteafterid Removed all users including the leaf $deleteafterid (and all its child) in user
3834
-	 *                              tree.
3835
-	 * @param string $filter SQL filter on users. This parameter must not come from user input.
3836
-	 *
3837
-	 * @return     array|int                   Array of users $this->users. Note: $this->parentof is also set.
3838
-	 */
3839
-	public function get_full_tree($deleteafterid = 0, $filter = '')
3840
-	{
3841
-		// phpcs:enable
3842
-		global $conf, $user;
3843
-		global $hookmanager;
3844
-
3845
-		global $config;
3846
-		$dbPrefix = $config->db->prefix;
3847
-
3848
-		// Actions hooked (by external module)
3849
-		$hookmanager->initHooks(['userdao']);
3850
-
3851
-		$this->users = [];
3852
-
3853
-		// Init this->parentof that is array(id_son=>id_parent, ...)
3854
-		$this->loadParentOf();
3855
-
3856
-		// Init $this->users array
3857
-		$sql = "SELECT DISTINCT u.rowid, u.firstname, u.lastname, u.fk_user, u.fk_soc, u.login, u.email, u.gender, u.admin, u.statut, u.photo, u.entity"; // Distinct reduce pb with old tables with duplicates
3858
-		$sql .= " FROM " . $dbPrefix . "user as u";
3859
-		// Add fields from hooks
3860
-		$parameters = [];
3861
-		$reshook = $hookmanager->executeHooks('printUserListWhere', $parameters); // Note that $action and $object may have been modified by hook
3862
-		if ($reshook > 0) {
3863
-			$sql .= $hookmanager->resPrint;
3864
-		} else {
3865
-			$sql .= " WHERE u.entity IN (" . getEntity('user') . ")";
3866
-		}
3867
-		if ($filter) {
3868
-			$sql .= " AND " . $filter;
3869
-		}
3870
-
3871
-		dol_syslog(get_class($this) . "::get_full_tree get user list", LOG_DEBUG);
3872
-		$resql = $this->db->query($sql);
3873
-		if ($resql) {
3874
-			$i = 0;
3875
-			while ($obj = $this->db->fetch_object($resql)) {
3876
-				$this->users[$obj->rowid]['rowid'] = $obj->rowid;
3877
-				$this->users[$obj->rowid]['id'] = $obj->rowid;
3878
-				$this->users[$obj->rowid]['fk_user'] = $obj->fk_user;
3879
-				$this->users[$obj->rowid]['fk_soc'] = $obj->fk_soc;
3880
-				$this->users[$obj->rowid]['firstname'] = $obj->firstname;
3881
-				$this->users[$obj->rowid]['lastname'] = $obj->lastname;
3882
-				$this->users[$obj->rowid]['login'] = $obj->login;
3883
-				$this->users[$obj->rowid]['statut'] = $obj->statut;
3884
-				$this->users[$obj->rowid]['entity'] = $obj->entity;
3885
-				$this->users[$obj->rowid]['email'] = $obj->email;
3886
-				$this->users[$obj->rowid]['gender'] = $obj->gender;
3887
-				$this->users[$obj->rowid]['admin'] = $obj->admin;
3888
-				$this->users[$obj->rowid]['photo'] = $obj->photo;
3889
-				$i++;
3890
-			}
3891
-		} else {
3892
-			dol_print_error($this->db);
3893
-			return -1;
3894
-		}
3895
-
3896
-		// We add the fullpath property to each element of the first level (no parent exists)
3897
-		dol_syslog(get_class($this) . "::get_full_tree call to build_path_from_id_user", LOG_DEBUG);
3898
-		foreach ($this->users as $key => $val) {
3899
-			$result = $this->build_path_from_id_user($key, 0); // Process a branch from the root user key (this user has no parent)
3900
-			if ($result < 0) {
3901
-				$this->error = 'ErrorLoopInHierarchy';
3902
-				return -1;
3903
-			}
3904
-		}
3905
-
3906
-		// Exclude leaf including $deleteafterid from tree
3907
-		if ($deleteafterid) {
3908
-			//print "Look to discard user ".$deleteafterid."\n";
3909
-			$keyfilter1 = '^' . $deleteafterid . '$';
3910
-			$keyfilter2 = '_' . $deleteafterid . '$';
3911
-			$keyfilter3 = '^' . $deleteafterid . '_';
3912
-			$keyfilter4 = '_' . $deleteafterid . '_';
3913
-			foreach (array_keys($this->users) as $key) {
3914
-				$fullpath = $this->users[$key]['fullpath'];
3915
-				if (
3916
-					preg_match('/' . $keyfilter1 . '/', $fullpath) || preg_match('/' . $keyfilter2 . '/', $fullpath)
3917
-					|| preg_match('/' . $keyfilter3 . '/', $fullpath) || preg_match('/' . $keyfilter4 . '/', $fullpath)
3918
-				) {
3919
-					unset($this->users[$key]);
3920
-				}
3921
-			}
3922
-		}
3923
-
3924
-		dol_syslog(get_class($this) . "::get_full_tree dol_sort_array", LOG_DEBUG);
3925
-		$this->users = dol_sort_array($this->users, 'fullname', 'asc', true, false);
3926
-
3927
-		//var_dump($this->users);
3928
-
3929
-		return $this->users;
3930
-	}
3931
-
3932
-	/**
3933
-	 *  Load this->parentof that is array(id_son=>id_parent, ...)
3934
-	 *
3935
-	 * @return     int<-1,1>     Return integer <0 if KO, >0 if OK
3936
-	 */
3937
-	private function loadParentOf()
3938
-	{
3939
-		global $conf;
3940
-
3941
-		global $config;
3942
-		$dbPrefix = $config->db->prefix;
3943
-
3944
-		$this->parentof = [];
3945
-
3946
-		// Load array[child]=parent
3947
-		$sql = "SELECT fk_user as id_parent, rowid as id_son";
3948
-		$sql .= " FROM " . $dbPrefix . "user";
3949
-		$sql .= " WHERE fk_user <> 0";
3950
-		$sql .= " AND entity IN (" . getEntity('user') . ")";
3951
-
3952
-		dol_syslog(get_class($this) . "::loadParentOf", LOG_DEBUG);
3953
-		$resql = $this->db->query($sql);
3954
-		if ($resql) {
3955
-			while ($obj = $this->db->fetch_object($resql)) {
3956
-				$this->parentof[$obj->id_son] = $obj->id_parent;
3957
-			}
3958
-			return 1;
3959
-		} else {
3960
-			dol_print_error($this->db);
3961
-			return -1;
3962
-		}
3963
-	}
3964
-
3965
-	/**
3966
-	 *  For user id_user and its children available in this->users, define property fullpath and fullname.
3967
-	 *  Function called by get_full_tree().
3968
-	 *
3969
-	 * @param int $id_user id_user entry to update
3970
-	 * @param int $protection Deep counter to avoid infinite loop (no more required, a protection is added with array
3971
-	 *                        useridfound)
3972
-	 *
3973
-	 * @return     int<-1,1>               Return integer < 0 if KO (infinite loop), >= 0 if OK
3974
-	 */
3975
-	public function build_path_from_id_user($id_user, $protection = 0)
3976
-	{
3977
-		// phpcs:enable
3978
-		//dol_syslog(get_class($this)."::build_path_from_id_user id_user=".$id_user." protection=".$protection, LOG_DEBUG);
3979
-
3980
-		if (!empty($this->users[$id_user]['fullpath'])) {
3981
-			// Already defined
3982
-			dol_syslog(get_class($this) . "::build_path_from_id_user fullpath and fullname already defined", LOG_WARNING);
3983
-			return 0;
3984
-		}
3985
-
3986
-		// Define fullpath and fullname
3987
-		$this->users[$id_user]['fullpath'] = '_' . $id_user;
3988
-		$this->users[$id_user]['fullname'] = $this->users[$id_user]['lastname'];
3989
-		$i = 0;
3990
-		$cursor_user = $id_user;
3991
-
3992
-		$useridfound = [$id_user];
3993
-		while (!empty($this->parentof[$cursor_user]) && !empty($this->users[$this->parentof[$cursor_user]])) {
3994
-			if (in_array($this->parentof[$cursor_user], $useridfound)) {
3995
-				dol_syslog("The hierarchy of user has a recursive loop", LOG_WARNING);
3996
-				return -1; // Should not happen. Protection against looping hierarchy
3997
-			}
3998
-			$useridfound[] = $this->parentof[$cursor_user];
3999
-			$this->users[$id_user]['fullpath'] = '_' . $this->parentof[$cursor_user] . $this->users[$id_user]['fullpath'];
4000
-			$this->users[$id_user]['fullname'] = $this->users[$this->parentof[$cursor_user]]['lastname'] . ' >> ' . $this->users[$id_user]['fullname'];
4001
-			$i++;
4002
-			$cursor_user = $this->parentof[$cursor_user];
4003
-		}
4004
-
4005
-		// We count number of _ to have level
4006
-		$this->users[$id_user]['level'] = dol_strlen(preg_replace('/[^_]/i', '', $this->users[$id_user]['fullpath']));
4007
-
4008
-		return 1;
4009
-	}
4010
-
4011
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4012
-
4013
-	/**
4014
-	 *      Load metrics this->nb for dashboard
4015
-	 *
4016
-	 * @return     int         Return integer <0 if KO, >0 if OK
4017
-	 */
4018
-	public function loadStateBoard()
4019
-	{
4020
-		global $conf;
4021
-
4022
-		$this->nb = [];
4023
-
4024
-		$sql = "SELECT COUNT(DISTINCT u.rowid) as nb";
4025
-		$sql .= " FROM " . $dbPrefix . "user as u";
4026
-		if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
4027
-			$sql .= ", " . $dbPrefix . "usergroup_user as ug";
4028
-			$sql .= " WHERE ug.entity IN (" . getEntity('usergroup') . ")";
4029
-			$sql .= " AND ug.fk_user = u.rowid";
4030
-		} else {
4031
-			$sql .= " WHERE u.entity IN (" . getEntity('user') . ")";
4032
-		}
4033
-		$sql .= " AND u.statut > 0";
4034
-		//$sql.= " AND employee != 0";
4035
-
4036
-		$resql = $this->db->query($sql);
4037
-		if ($resql) {
4038
-			while ($obj = $this->db->fetch_object($resql)) {
4039
-				$this->nb["users"] = $obj->nb;
4040
-			}
4041
-			$this->db->free($resql);
4042
-			return 1;
4043
-		} else {
4044
-			dol_print_error($this->db);
4045
-			$this->error = $this->db->error();
4046
-			return -1;
4047
-		}
4048
-	}
4049
-
4050
-	/**
4051
-	 *  Create a document onto disk according to template module.
4052
-	 *
4053
-	 * @param string $modele Force model to use ('' to not force)
4054
-	 * @param Translate $outputlangs Object langs to use for output
4055
-	 * @param int $hidedetails Hide details of lines
4056
-	 * @param int $hidedesc Hide description
4057
-	 * @param int $hideref Hide ref
4058
-	 * @param null|array $moreparams Array to provide more information
4059
-	 *
4060
-	 * @return     int                         0 if KO, 1 if OK
4061
-	 */
4062
-	public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
4063
-	{
4064
-		global $conf, $user, $langs;
4065
-
4066
-		$langs->load("user");
4067
-
4068
-		// Set the '$modele' to the name of the document template (model) to use
4069
-		if (!dol_strlen($modele)) {
4070
-			if (getDolGlobalString('USER_ADDON_PDF')) {
4071
-				$modele = getDolGlobalString('USER_ADDON_PDF');
4072
-			} else {
4073
-				$modele = 'bluesky';
4074
-			}
4075
-		}
4076
-
4077
-		$modelpath = "core/modules/user/doc/";
4078
-
4079
-		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
4080
-	}
4081
-
4082
-	/**
4083
-	 *  Return property of user from its id
4084
-	 *
4085
-	 * @param int $rowid id of contact
4086
-	 * @param string $mode 'email', 'mobile', or 'name'
4087
-	 *
4088
-	 * @return string              Email of user with format: "Full name <email>"
4089
-	 */
4090
-	public function user_get_property($rowid, $mode)
4091
-	{
4092
-		// phpcs:enable
4093
-		$user_property = '';
4094
-
4095
-		if (empty($rowid)) {
4096
-			return '';
4097
-		}
4098
-
4099
-		$sql = "SELECT rowid, email, user_mobile, civility, lastname, firstname";
4100
-		$sql .= " FROM " . $dbPrefix . "user";
4101
-		$sql .= " WHERE rowid = " . ((int)$rowid);
4102
-
4103
-		$resql = $this->db->query($sql);
4104
-		if ($resql) {
4105
-			$nump = $this->db->num_rows($resql);
4106
-
4107
-			if ($nump) {
4108
-				$obj = $this->db->fetch_object($resql);
4109
-
4110
-				if ($mode == 'email') {
4111
-					$user_property = dolGetFirstLastname($obj->firstname, $obj->lastname) . " <" . $obj->email . ">";
4112
-				} elseif ($mode == 'mobile') {
4113
-					$user_property = $obj->user_mobile;
4114
-				} elseif ($mode == 'name') {
4115
-					$user_property = dolGetFirstLastname($obj->firstname, $obj->lastname);
4116
-				}
4117
-			}
4118
-			return $user_property;
4119
-		} else {
4120
-			dol_print_error($this->db);
4121
-		}
4122
-
4123
-		return '';
4124
-	}
4125
-
4126
-	/**
4127
-	 * Return string with full Url to virtual card
4128
-	 *
4129
-	 * @param string $mode Mode for link
4130
-	 * @param string $typeofurl 'external' or 'internal'
4131
-	 *
4132
-	 * @return  string                  Url string link
4133
-	 */
4134
-	public function getOnlineVirtualCardUrl($mode = '', $typeofurl = 'external')
4135
-	{
4136
-		global $dolibarr_main_url_root;
4137
-		global $conf;
4138
-
4139
-		$encodedsecurekey = dol_hash($conf->file->instance_unique_id . 'uservirtualcard' . $this->id . '-' . $this->login, 'md5');
4140
-		if (isModEnabled('multicompany')) {
4141
-			$entity_qr = '&entity=' . ((int)$conf->entity);
4142
-		} else {
4143
-			$entity_qr = '';
4144
-		}
4145
-		// Define $urlwithroot
4146
-		$urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($dolibarr_main_url_root));
4147
-		$urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
4148
-		//$urlwithroot=DOL_MAIN_URL_ROOT;                   // This is to use same domain name than current
4149
-
4150
-		if ($typeofurl == 'internal') {
4151
-			$urlwithroot = DOL_URL_ROOT;
4152
-		}
4153
-
4154
-		return $urlwithroot . '/public/users/view.php?id=' . $this->id . '&securekey=' . $encodedsecurekey . $entity_qr . ($mode ? '&mode=' . urlencode($mode) : '');
4155
-	}
4156
-
4157
-	/**
4158
-	 * Find a user by the given e-mail and return it's user id when found
4159
-	 *
4160
-	 * NOTE:
4161
-	 * Use AGENDA_DISABLE_EXACT_USER_EMAIL_COMPARE_FOR_EXTERNAL_CALENDAR
4162
-	 * to disable exact e-mail search
4163
-	 *
4164
-	 * @param string $email The full e-mail (or a part of a e-mail)
4165
-	 *
4166
-	 * @return int              Return integer <0 = user was not found, >0 = The id of the user
4167
-	 */
4168
-	public function findUserIdByEmail($email)
4169
-	{
4170
-		if (isset($this->findUserIdByEmailCache[$email])) {
4171
-			return $this->findUserIdByEmailCache[$email];
4172
-		}
4173
-
4174
-		$this->findUserIdByEmailCache[$email] = -1;
4175
-
4176
-		$sql = 'SELECT rowid';
4177
-		$sql .= ' FROM ' . $dbPrefix . 'user';
4178
-		if (getDolGlobalString('AGENDA_DISABLE_EXACT_USER_EMAIL_COMPARE_FOR_EXTERNAL_CALENDAR')) {
4179
-			$sql .= " WHERE email LIKE '%" . $this->db->escape($this->db->escapeforlike($email)) . "%'";
4180
-		} else {
4181
-			$sql .= " WHERE email = '" . $this->db->escape($email) . "'";
4182
-		}
4183
-		$sql .= ' LIMIT 1';
4184
-
4185
-		$resql = $this->db->query($sql);
4186
-		if (!$resql) {
4187
-			return -1;
4188
-		}
4189
-
4190
-		$obj = $this->db->fetch_object($resql);
4191
-		if (!$obj) {
4192
-			return -1;
4193
-		}
4194
-
4195
-		$this->findUserIdByEmailCache[$email] = (int)$obj->rowid;
4196
-
4197
-		return $this->findUserIdByEmailCache[$email];
4198
-	}
58
+    use CommonPeople;
59
+
60
+    const STATUS_DISABLED = 0;
61
+    const STATUS_ENABLED = 1;
62
+    /**
63
+     * @var string ID to identify managed object
64
+     */
65
+    public $element = 'user';
66
+    /**
67
+     * @var string Name of table without prefix where object is stored
68
+     */
69
+    public $table_element = 'user';
70
+    /**
71
+     * @var string Field with ID of parent key if this field has a parent
72
+     */
73
+    public $fk_element = 'fk_user';
74
+    /**
75
+     * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
76
+     * @var int
77
+     */
78
+    public $ismultientitymanaged = 1;
79
+    /**
80
+     * @var int  Does object support extrafields ? 0=No, 1=Yes
81
+     */
82
+    public $isextrafieldmanaged = 1;
83
+    /**
84
+     * @var string picto
85
+     */
86
+    public $picto = 'user';
87
+    public $id = 0;
88
+    /**
89
+     * @var User old copy of User
90
+     */
91
+    public $oldcopy;
92
+    /**
93
+     * @var int
94
+     * @deprecated
95
+     * @see $status
96
+     */
97
+    public $statut;
98
+    public $status;
99
+    /**
100
+     * @var string      Open ID
101
+     */
102
+    public $openid;
103
+    public $ldap_sid;
104
+    public $search_sid;
105
+    public $employee;
106
+    public $civility_code;
107
+    /**
108
+     * @var string fullname
109
+     */
110
+    public $fullname;
111
+    /**
112
+     * @var string gender
113
+     */
114
+    public $gender;
115
+    public $birth;
116
+    /**
117
+     * @var string email
118
+     */
119
+    public $email;
120
+    /**
121
+     * @var string email
122
+     */
123
+    public $email_oauth2;
124
+    /**
125
+     * @var string personal email
126
+     */
127
+    public $personal_email;
128
+    /**
129
+     * @var array array of socialnetwo18dprks
130
+     */
131
+    public $socialnetworks;
132
+    /**
133
+     * @var string job position
134
+     */
135
+    public $job;
136
+    /**
137
+     * @var string user signature
138
+     */
139
+    public $signature;
140
+    /**
141
+     * @var string office phone
142
+     */
143
+    public $office_phone;
144
+    /**
145
+     * @var string office fax
146
+     */
147
+    public $office_fax;
148
+    /**
149
+     * @var string phone mobile
150
+     */
151
+    public $user_mobile;
152
+    /**
153
+     * @var string personal phone mobile
154
+     */
155
+    public $personal_mobile;
156
+    /**
157
+     * @var int 1 if admin 0 if standard user
158
+     */
159
+    public $admin;
160
+    /**
161
+     * @var string user login
162
+     */
163
+    public $login;
164
+    /**
165
+     * @var string user apikey
166
+     */
167
+    public $api_key;
168
+    /**
169
+     * @var int Entity
170
+     */
171
+    public $entity;
172
+    /**
173
+     * @var string Clear password in memory
174
+     */
175
+    public $pass;
176
+    /**
177
+     * @var string Encrypted password in memory
178
+     */
179
+    public $pass_crypted;
180
+    /**
181
+     * @var string Clear password in database (defined if DATABASE_PWD_ENCRYPTED=0)
182
+     */
183
+    public $pass_indatabase;
184
+    /**
185
+     * @var string Encrypted password in database (always defined)
186
+     */
187
+    public $pass_indatabase_crypted;
188
+    /**
189
+     * @var string Temporary password
190
+     */
191
+    public $pass_temp;
192
+    /**
193
+     * Date creation record (datec)
194
+     *
195
+     * @var integer
196
+     */
197
+    public $datec;
198
+    /**
199
+     * Date modification record (tms)
200
+     *
201
+     * @var integer
202
+     */
203
+    public $datem;
204
+    /**
205
+     * @var int If this is defined, it is an external user
206
+     */
207
+    public $socid;
208
+    /**
209
+     * @var int If this is defined, it is a user created from a contact
210
+     */
211
+    public $contact_id;
212
+    /**
213
+     * @var int ID
214
+     */
215
+    public $fk_member;
216
+    /**
217
+     * @var int User ID of supervisor
218
+     */
219
+    public $fk_user;
220
+    /**
221
+     * @var int User ID of expense validator
222
+     */
223
+    public $fk_user_expense_validator;
224
+    /**
225
+     * @var int User ID of holidays validator
226
+     */
227
+    public $fk_user_holiday_validator;
228
+    /**
229
+     * @string clicktodial url
230
+     */
231
+    public $clicktodial_url;
232
+    /**
233
+     * @var string clicktodial login
234
+     */
235
+    public $clicktodial_login;
236
+    /**
237
+     * @var string clicktodial password
238
+     */
239
+    public $clicktodial_password;
240
+    /**
241
+     * @var string clicktodial poste
242
+     */
243
+    public $clicktodial_poste;
244
+    /**
245
+     * @var int     0 by default, 1 if click to dial data were already loaded for this user
246
+     */
247
+    public $clicktodial_loaded;
248
+    public $datelastlogin;
249
+    public $datepreviouslogin;
250
+    public $flagdelsessionsbefore;
251
+    public $iplastlogin;
252
+    public $ippreviouslogin;
253
+    public $datestartvalidity;
254
+    public $dateendvalidity;
255
+    /**
256
+     * @var string photo filename
257
+     */
258
+    public $photo;
259
+    /**
260
+     * @var string default language
261
+     */
262
+    public $lang;
263
+    /**
264
+     * @var stdClass Class of permissions user->rights->permx
265
+     */
266
+    public $rights;
267
+    /**
268
+     * @var int  All permissions are loaded
269
+     */
270
+    public $all_permissions_are_loaded;
271
+    /**
272
+     * @var int Number of rights granted to the user. Value loaded after a getrights().
273
+     */
274
+    public $nb_rights;
275
+    /**
276
+     * @var array   To store list of groups of user (used by API /info for example)
277
+     */
278
+    public $user_group_list;
279
+    /**
280
+     * @var stdClass To store personal config
281
+     */
282
+    public $conf; // To store default values for user. Loaded by loadDefaultValues().
283
+    public $default_values; // To store current search criteria for user
284
+    public $lastsearch_values_tmp; // To store last saved search criteria for user
285
+    public $lastsearch_values;
286
+    /**
287
+     * @var array<int,array{rowid:int,id:int,fk_user:int,fk_soc:int,firstname:string,lastname:string,login:string,statut:int,entity:string,email:string,gender:int,admin:string,photo:string,fullpath:string,fullname:string,level:int}>
288
+     *       Store the entire hierarchy tree of users
289
+     */
290
+    public $users = []; // To store an array of all parents for all ids.
291
+    public $parentof; // Cache array of already loaded children
292
+    public $accountancy_code; // Accountancy code in prevision of the complete accountancy module
293
+
294
+    public $thm; // Average cost of employee - Used for valuation of time spent
295
+    public $tjm; // Average cost of employee
296
+
297
+    public $salary; // Monthly salary       - Denormalized value from llx_user_employment
298
+    public $salaryextra; // Monthly salary extra - Denormalized value from llx_user_employment
299
+    public $weeklyhours; // Weekly hours         - Denormalized value from llx_user_employment
300
+
301
+    /**
302
+     * @var string Define background color for user in agenda
303
+     */
304
+    public $color;
305
+
306
+    public $dateemployment; // Define date of employment by company
307
+    public $dateemploymentend; // Define date of employment end by company
308
+
309
+    public $default_c_exp_tax_cat;
310
+
311
+    /**
312
+     * @var string ref for employee
313
+     */
314
+    public $ref_employee;
315
+
316
+    /**
317
+     * @var string national registration number
318
+     */
319
+    public $national_registration_number;
320
+
321
+    public $default_range;
322
+
323
+    /**
324
+     * @var int id of warehouse
325
+     */
326
+    public $fk_warehouse;
327
+
328
+    /**
329
+     * @var int egroupware id
330
+     */
331
+    public $egroupware_id;
332
+
333
+    public $fields = [
334
+        'rowid' => ['type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'index' => 1, 'position' => 1, 'comment' => 'Id'],
335
+        'lastname' => ['type' => 'varchar(50)', 'label' => 'Lastname', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'showoncombobox' => 1, 'index' => 1, 'position' => 20, 'searchall' => 1],
336
+        'firstname' => ['type' => 'varchar(50)', 'label' => 'Firstname', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'showoncombobox' => 1, 'index' => 1, 'position' => 10, 'searchall' => 1],
337
+        'ref_employee' => ['type' => 'varchar(50)', 'label' => 'RefEmployee', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'showoncombobox' => 1, 'index' => 1, 'position' => 30, 'searchall' => 1],
338
+        'national_registration_number' => ['type' => 'varchar(50)', 'label' => 'NationalRegistrationNumber', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'showoncombobox' => 1, 'index' => 1, 'position' => 40, 'searchall' => 1],
339
+    ];
340
+    /**
341
+     * @var array Cache array of already loaded permissions
342
+     */
343
+    private $_tab_loaded = [];
344
+    private $cache_childids;
345
+    /**
346
+     * Cache the SQL results of the function "findUserIdByEmail($email)"
347
+     *
348
+     * NOTE: findUserIdByEmailCache[...] === -1 means not found in database
349
+     *
350
+     * @var array
351
+     */
352
+    private $findUserIdByEmailCache;
353
+
354
+    /**
355
+     *    Constructor of the class
356
+     *
357
+     * @param DoliDB $db Database handler
358
+     */
359
+    public function __construct($db)
360
+    {
361
+        $this->db = $db;
362
+
363
+        // User preference
364
+        $this->clicktodial_loaded = 0;
365
+
366
+        // For cache usage
367
+        $this->all_permissions_are_loaded = 0;
368
+        $this->nb_rights = 0;
369
+
370
+        // Force some default values
371
+        $this->admin = 0;
372
+        $this->employee = 1;
373
+
374
+        $this->conf = new stdClass();
375
+        $this->rights = new stdClass();
376
+        $this->rights->user = new stdClass();
377
+        $this->rights->user->user = new stdClass();
378
+        $this->rights->user->self = new stdClass();
379
+        $this->rights->user->user_advance = new stdClass();
380
+        $this->rights->user->self_advance = new stdClass();
381
+        $this->rights->user->group_advance = new stdClass();
382
+    }
383
+
384
+    /**
385
+     * Function used to replace a thirdparty id with another one.
386
+     *
387
+     * @param DoliDB $dbs Database handler, because function is static we name it $dbs not $db to avoid breaking
388
+     *                          coding test
389
+     * @param int $origin_id Old thirdparty id
390
+     * @param int $dest_id New thirdparty id
391
+     *
392
+     * @return  bool
393
+     */
394
+    public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
395
+    {
396
+        $tables = [
397
+            'user',
398
+        ];
399
+
400
+        return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
401
+    }
402
+
403
+    /**
404
+     *  Add a right to the user
405
+     *
406
+     * @param int $rid Id of permission to add or 0 to add several permissions
407
+     * @param string $allmodule Add all permissions of module $allmodule or 'allmodules' to include all modules.
408
+     * @param string $allperms Add all permissions of module $allmodule, subperms $allperms only or '' to include all
409
+     *                          permissions.
410
+     * @param int $entity Entity to use
411
+     * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers
412
+     *
413
+     * @return int                     > 0 if OK, < 0 if KO
414
+     * @see    clearrights(), delrights(), getrights(), hasRight()
415
+     */
416
+    public function addrights($rid, $allmodule = '', $allperms = '', $entity = 0, $notrigger = 0)
417
+    {
418
+        global $conf, $user, $langs;
419
+        global $config;
420
+        $dbPrefix = $config->db->prefix;
421
+
422
+        $entity = (empty($entity) ? $conf->entity : $entity);
423
+
424
+        dol_syslog(get_class($this) . "::addrights $rid, $allmodule, $allperms, $entity, $notrigger for user id=" . $this->id);
425
+
426
+        if (empty($this->id)) {
427
+            $this->error = 'Try to call addrights on an object user with an empty id';
428
+            return -1;
429
+        }
430
+
431
+        $error = 0;
432
+        $whereforadd = '';
433
+
434
+        $this->db->begin();
435
+
436
+        if (!empty($rid)) {
437
+            $module = $perms = $subperms = '';
438
+
439
+            // If we ask to add a given permission, we first load properties of this permission (module, perms and subperms).
440
+            $sql = "SELECT module, perms, subperms";
441
+            $sql .= " FROM " . $dbPrefix . "rights_def";
442
+            $sql .= " WHERE id = " . ((int)$rid);
443
+            $sql .= " AND entity = " . ((int)$entity);
444
+
445
+            $result = $this->db->query($sql);
446
+            if ($result) {
447
+                $obj = $this->db->fetch_object($result);
448
+
449
+                if ($obj) {
450
+                    $module = $obj->module;
451
+                    $perms = $obj->perms;
452
+                    $subperms = $obj->subperms;
453
+                }
454
+            } else {
455
+                $error++;
456
+                dol_print_error($this->db);
457
+            }
458
+
459
+            // Define the where for the permission to add
460
+            $whereforadd = "id=" . ((int)$rid);
461
+            // Add also inherited permissions
462
+            if (!empty($subperms)) {
463
+                $whereforadd .= " OR (module='" . $this->db->escape($module) . "' AND perms='" . $this->db->escape($perms) . "' AND (subperms='lire' OR subperms='read'))";
464
+            } elseif (!empty($perms)) {
465
+                $whereforadd .= " OR (module='" . $this->db->escape($module) . "' AND (perms='lire' OR perms='read') AND subperms IS NULL)";
466
+            }
467
+        } else {
468
+            // A list of permission was requested (not a single specific permission)
469
+            // based on the name of a module of permissions
470
+            // Used in the where clause to determine the list of permissions to add.
471
+            if (!empty($allmodule)) {
472
+                if ($allmodule == 'allmodules') {
473
+                    $whereforadd = 'allmodules';
474
+                } else {
475
+                    $whereforadd = "module='" . $this->db->escape($allmodule) . "'";
476
+                    if (!empty($allperms)) {
477
+                        $whereforadd .= " AND perms='" . $this->db->escape($allperms) . "'";
478
+                    }
479
+                }
480
+            }
481
+        }
482
+
483
+        // Add automatically other permission using the criteria whereforadd
484
+        // $whereforadd can be a SQL filter or the string 'allmodules'
485
+        if (!empty($whereforadd)) {
486
+            //print "$module-$perms-$subperms";
487
+            $sql = "SELECT id";
488
+            $sql .= " FROM " . $dbPrefix . "rights_def";
489
+            $sql .= " WHERE entity = " . ((int)$entity);
490
+            if (!empty($whereforadd) && $whereforadd != 'allmodules') {
491
+                $sql .= " AND (" . $whereforadd . ")";  // Note: parenthesis are important because whereforadd can contains OR. Also note that $whereforadd is already sanitized
492
+            }
493
+
494
+            $sqldelete = "DELETE FROM " . $dbPrefix . "user_rights";
495
+            $sqldelete .= " WHERE fk_user = " . ((int)$this->id) . " AND fk_id IN (";
496
+            $sqldelete .= $sql;
497
+            $sqldelete .= ") AND entity = " . ((int)$entity);
498
+            if (!$this->db->query($sqldelete)) {
499
+                $error++;
500
+            }
501
+
502
+            if (!$error) {
503
+                $resql = $this->db->query($sql);
504
+                if ($resql) {
505
+                    $num = $this->db->num_rows($resql);
506
+                    $i = 0;
507
+                    while ($i < $num) {
508
+                        $obj = $this->db->fetch_object($resql);
509
+
510
+                        if ($obj) {
511
+                            $nid = $obj->id;
512
+
513
+                            $sql = "INSERT INTO " . $dbPrefix . "user_rights (entity, fk_user, fk_id) VALUES (" . ((int)$entity) . ", " . ((int)$this->id) . ", " . ((int)$nid) . ")";
514
+                            if (!$this->db->query($sql)) {
515
+                                $error++;
516
+                            }
517
+                        }
518
+
519
+                        $i++;
520
+                    }
521
+                } else {
522
+                    $error++;
523
+                    dol_print_error($this->db);
524
+                }
525
+            }
526
+        }
527
+
528
+        if (!$error && !$notrigger) {
529
+            $langs->load("other");
530
+            $this->context = ['audit' => $langs->trans("PermissionsAdd") . ($rid ? ' (id=' . $rid . ')' : '')];
531
+
532
+            // Call trigger
533
+            $result = $this->call_trigger('USER_MODIFY', $user);
534
+            if ($result < 0) {
535
+                $error++;
536
+            }
537
+            // End call triggers
538
+        }
539
+
540
+        if ($error) {
541
+            $this->db->rollback();
542
+            return -$error;
543
+        } else {
544
+            $this->db->commit();
545
+            return 1;
546
+        }
547
+    }
548
+
549
+    /**
550
+     *  Remove a right to the user
551
+     *
552
+     * @param int $rid Id du droit a retirer
553
+     * @param string $allmodule Retirer tous les droits du module allmodule
554
+     * @param string $allperms Retirer tous les droits du module allmodule, perms allperms
555
+     * @param int $entity Entity to use
556
+     * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers
557
+     *
558
+     * @return int                 > 0 if OK, < 0 if OK
559
+     * @see    clearrights(), addrights(), getrights(), hasRight()
560
+     */
561
+    public function delrights($rid, $allmodule = '', $allperms = '', $entity = 0, $notrigger = 0)
562
+    {
563
+        global $conf, $user, $langs;
564
+
565
+        $error = 0;
566
+        $wherefordel = '';
567
+        $entity = (!empty($entity) ? $entity : $conf->entity);
568
+
569
+        $this->db->begin();
570
+
571
+        if (!empty($rid)) {
572
+            $module = $perms = $subperms = '';
573
+
574
+            // When the request is to delete a specific permissions, this gets the
575
+            // les charactis for the module, permissions and sub-permission of this permission.
576
+            $sql = "SELECT module, perms, subperms";
577
+            $sql .= " FROM " . $dbPrefix . "rights_def";
578
+            $sql .= " WHERE id = '" . $this->db->escape($rid) . "'";
579
+            $sql .= " AND entity = " . ((int)$entity);
580
+
581
+            $result = $this->db->query($sql);
582
+            if ($result) {
583
+                $obj = $this->db->fetch_object($result);
584
+
585
+                if ($obj) {
586
+                    $module = $obj->module;
587
+                    $perms = $obj->perms;
588
+                    $subperms = $obj->subperms;
589
+                }
590
+            } else {
591
+                $error++;
592
+                dol_print_error($this->db);
593
+            }
594
+
595
+            // Where clause for the list of permissions to delete
596
+            $wherefordel = "id=" . ((int)$rid);
597
+            // Suppression des droits induits
598
+            if ($subperms == 'lire' || $subperms == 'read') {
599
+                $wherefordel .= " OR (module='" . $this->db->escape($module) . "' AND perms='" . $this->db->escape($perms) . "' AND subperms IS NOT NULL)";
600
+            }
601
+            if ($perms == 'lire' || $perms == 'read') {
602
+                $wherefordel .= " OR (module='" . $this->db->escape($module) . "')";
603
+            }
604
+        } else {
605
+            // The deletion of the permissions concerns the name of a module or
606
+            // list of permissions.
607
+            // Used in the Where clause to determine the list of permission to delete
608
+            if (!empty($allmodule)) {
609
+                if ($allmodule == 'allmodules') {
610
+                    $wherefordel = 'allmodules';
611
+                } else {
612
+                    $wherefordel = "module='" . $this->db->escape($allmodule) . "'";
613
+                    if (!empty($allperms)) {
614
+                        $wherefordel .= " AND perms='" . $this->db->escape($allperms) . "'";
615
+                    }
616
+                }
617
+            }
618
+        }
619
+
620
+        // Suppression des droits selon critere defini dans wherefordel
621
+        if (!empty($wherefordel)) {
622
+            //print "$module-$perms-$subperms";
623
+            $sql = "SELECT id";
624
+            $sql .= " FROM " . $dbPrefix . "rights_def";
625
+            $sql .= " WHERE entity = " . ((int)$entity);
626
+            if (!empty($wherefordel) && $wherefordel != 'allmodules') {
627
+                $sql .= " AND (" . $wherefordel . ")";  // Note: parenthesis are important because wherefordel can contains OR. Also note that $wherefordel is already sanitized
628
+            }
629
+
630
+            // avoid admin can remove his own important rights
631
+            if ($this->admin == 1) {
632
+                $sql .= " AND id NOT IN (251, 252, 253, 254, 255, 256)"; // other users rights
633
+                $sql .= " AND id NOT IN (341, 342, 343, 344)"; // own rights
634
+                $sql .= " AND id NOT IN (351, 352, 353, 354)"; // groups rights
635
+                $sql .= " AND id NOT IN (358)"; // user export
636
+            }
637
+
638
+            $sqldelete = "DELETE FROM " . $dbPrefix . "user_rights";
639
+            $sqldelete .= " WHERE fk_user = " . ((int)$this->id) . " AND fk_id IN (";
640
+            $sqldelete .= $sql;
641
+            $sqldelete .= ")";
642
+            $sqldelete .= " AND entity = " . ((int)$entity);
643
+
644
+            $resql = $this->db->query($sqldelete);
645
+            if (!$resql) {
646
+                $error++;
647
+                dol_print_error($this->db);
648
+            }
649
+        }
650
+
651
+        if (!$error && !$notrigger) {
652
+            $langs->load("other");
653
+            $this->context = ['audit' => $langs->trans("PermissionsDelete") . ($rid ? ' (id=' . $rid . ')' : '')];
654
+
655
+            // Call trigger
656
+            $result = $this->call_trigger('USER_MODIFY', $user);
657
+            if ($result < 0) {
658
+                $error++;
659
+            }
660
+            // End call triggers
661
+        }
662
+
663
+        if ($error) {
664
+            $this->db->rollback();
665
+            return -$error;
666
+        } else {
667
+            $this->db->commit();
668
+            return 1;
669
+        }
670
+    }
671
+
672
+    /**
673
+     *  Clear all permissions array of user
674
+     *
675
+     * @return void
676
+     * @see    getrights(), hasRight()
677
+     */
678
+    public function clearrights()
679
+    {
680
+        dol_syslog(get_class($this) . "::clearrights reset user->rights");
681
+        $this->rights = new stdClass();
682
+        $this->nb_rights = 0;
683
+        $this->all_permissions_are_loaded = 0;
684
+        $this->_tab_loaded = [];
685
+    }
686
+
687
+    /**
688
+     *  Load permissions granted to a user->id into object user->rights
689
+     *
690
+     * @param string $moduletag Limit permission for a particular module ('' by default means load all permissions)
691
+     * @param int $forcereload Force reload of permissions even if they were already loaded (ignore cache)
692
+     *
693
+     * @return void
694
+     * @see    clearrights(), delrights(), addrights(), hasRight()
695
+     */
696
+    public function getrights($moduletag = '', $forcereload = 0)
697
+    {
698
+        global $conf;
699
+
700
+        $alreadyloaded = false;
701
+
702
+        if (empty($forcereload)) {
703
+            if ($moduletag && isset($this->_tab_loaded[$moduletag]) && $this->_tab_loaded[$moduletag]) {
704
+                // Rights for this module are already loaded, so we leave
705
+                $alreadyloaded = true;
706
+            }
707
+
708
+            if (!empty($this->all_permissions_are_loaded)) {
709
+                // We already loaded all rights for this user, so we leave
710
+                $alreadyloaded = true;
711
+            }
712
+        }
713
+
714
+        // For avoid error
715
+        if (!isset($this->rights) || !is_object($this->rights)) {
716
+            $this->rights = new stdClass(); // For avoid error
717
+        }
718
+        if (!isset($this->rights->user) || !is_object($this->rights->user)) {
719
+            $this->rights->user = new stdClass(); // For avoid error
720
+        }
721
+
722
+        // Get permission of users + Get permissions of groups
723
+
724
+        if (!$alreadyloaded) {
725
+            // First user permissions
726
+            $sql = "SELECT DISTINCT r.module, r.perms, r.subperms";
727
+            $sql .= " FROM " . $dbPrefix . "user_rights as ur,";
728
+            $sql .= " " . $dbPrefix . "rights_def as r";
729
+            $sql .= " WHERE r.id = ur.fk_id";
730
+            if (getDolGlobalString('MULTICOMPANY_BACKWARD_COMPATIBILITY')) {
731
+                // on old version, we use entity defined into table r only
732
+                $sql .= " AND r.entity IN (0," . (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE') ? "1," : "") . $conf->entity . ")";
733
+            } else {
734
+                // On table r=rights_def, the unique key is (id, entity) because id is hard coded into module descriptor and insert during module activation.
735
+                // So we must include the filter on entity on both table r. and ur.
736
+                $sql .= " AND r.entity = " . ((int)$conf->entity) . " AND ur.entity = " . ((int)$conf->entity);
737
+            }
738
+            $sql .= " AND ur.fk_user= " . ((int)$this->id);
739
+            $sql .= " AND r.perms IS NOT NULL";
740
+            if (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) {
741
+                $sql .= " AND r.perms NOT LIKE '%_advance'"; // Hide advanced perms if option is not enabled
742
+            }
743
+            if ($moduletag) {
744
+                $sql .= " AND r.module = '" . $this->db->escape($moduletag) . "'";
745
+            }
746
+
747
+            $resql = $this->db->query($sql);
748
+            if ($resql) {
749
+                $num = $this->db->num_rows($resql);
750
+                $i = 0;
751
+                while ($i < $num) {
752
+                    $obj = $this->db->fetch_object($resql);
753
+
754
+                    if ($obj) {
755
+                        $module = $obj->module;
756
+                        $perms = $obj->perms;
757
+                        $subperms = $obj->subperms;
758
+
759
+                        if (!empty($perms)) {
760
+                            if (!empty($module)) {
761
+                                if (!isset($this->rights->$module) || !is_object($this->rights->$module)) {
762
+                                    $this->rights->$module = new stdClass();
763
+                                }
764
+                                if (!empty($subperms)) {
765
+                                    if (!isset($this->rights->$module->$perms) || !is_object($this->rights->$module->$perms)) {
766
+                                        $this->rights->$module->$perms = new stdClass();
767
+                                    }
768
+                                    if (empty($this->rights->$module->$perms->$subperms)) {
769
+                                        $this->nb_rights++;
770
+                                    }
771
+                                    $this->rights->$module->$perms->$subperms = 1;
772
+                                } else {
773
+                                    if (empty($this->rights->$module->$perms)) {
774
+                                        $this->nb_rights++;
775
+                                    }
776
+                                    $this->rights->$module->$perms = 1;
777
+                                }
778
+                            }
779
+                        }
780
+                    }
781
+                    $i++;
782
+                }
783
+                $this->db->free($resql);
784
+            }
785
+
786
+            // Now permissions of groups
787
+            $sql = "SELECT DISTINCT r.module, r.perms, r.subperms";
788
+            $sql .= " FROM " . $dbPrefix . "usergroup_rights as gr,";
789
+            $sql .= " " . $dbPrefix . "usergroup_user as gu,";
790
+            $sql .= " " . $dbPrefix . "rights_def as r";
791
+            $sql .= " WHERE r.id = gr.fk_id";
792
+            // A very strange business rules. Must be same than into user->getrights() user/perms.php and user/group/perms.php
793
+            if (getDolGlobalString('MULTICOMPANY_BACKWARD_COMPATIBILITY')) {
794
+                if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
795
+                    $sql .= " AND gu.entity IN (0," . $conf->entity . ")";
796
+                } else {
797
+                    $sql .= " AND r.entity = " . ((int)$conf->entity);
798
+                }
799
+            } else {
800
+                $sql .= " AND gr.entity = " . ((int)$conf->entity);  // Only groups created in current entity
801
+                // The entity on the table usergroup_user should be useless and should never be used because it is already into gr and r.
802
+                // but when using MULTICOMPANY_TRANSVERSE_MODE, we may insert record that make rubbish result due to duplicate record of
803
+                // other entities, so we are forced to add a filter here
804
+                $sql .= " AND gu.entity IN (0," . $conf->entity . ")";
805
+                $sql .= " AND r.entity = " . ((int)$conf->entity);   // Only permission of modules enabled in current entity
806
+            }
807
+            // End of strange business rule
808
+            $sql .= " AND gr.fk_usergroup = gu.fk_usergroup";
809
+            $sql .= " AND gu.fk_user = " . ((int)$this->id);
810
+            $sql .= " AND r.perms IS NOT NULL";
811
+            if ($moduletag) {
812
+                $sql .= " AND r.module = '" . $this->db->escape($moduletag) . "'";
813
+            }
814
+
815
+            $resql = $this->db->query($sql);
816
+            if ($resql) {
817
+                $num = $this->db->num_rows($resql);
818
+                $i = 0;
819
+                while ($i < $num) {
820
+                    $obj = $this->db->fetch_object($resql);
821
+
822
+                    if ($obj) {
823
+                        $module = $obj->module;
824
+                        $perms = $obj->perms;
825
+                        $subperms = $obj->subperms;
826
+
827
+                        if (!empty($perms)) {
828
+                            if (!empty($module)) {
829
+                                if (!isset($this->rights->$module) || !is_object($this->rights->$module)) {
830
+                                    $this->rights->$module = new stdClass();
831
+                                }
832
+                                if (!empty($subperms)) {
833
+                                    if (!isset($this->rights->$module->$perms) || !is_object($this->rights->$module->$perms)) {
834
+                                        $this->rights->$module->$perms = new stdClass();
835
+                                    }
836
+                                    if (empty($this->rights->$module->$perms->$subperms)) {
837
+                                        $this->nb_rights++;
838
+                                    }
839
+                                    $this->rights->$module->$perms->$subperms = 1;
840
+                                } else {
841
+                                    if (empty($this->rights->$module->$perms)) {
842
+                                        $this->nb_rights++;
843
+                                    }
844
+                                    // if we have already define a subperm like this $this->rights->$module->level1->level2 with llx_user_rights, we don't want override level1 because the level2 can be not define on user group
845
+                                    if (!isset($this->rights->$module->$perms) || !is_object($this->rights->$module->$perms)) {
846
+                                        $this->rights->$module->$perms = 1;
847
+                                    }
848
+                                }
849
+                            }
850
+                        }
851
+                    }
852
+                    $i++;
853
+                }
854
+                $this->db->free($resql);
855
+            }
856
+
857
+            // Force permission on user for admin
858
+            if (!empty($this->admin)) {
859
+                if (empty($this->rights->user->user)) {
860
+                    $this->rights->user->user = new stdClass();
861
+                }
862
+                $listofpermtotest = ['lire', 'creer', 'password', 'supprimer', 'export'];
863
+                foreach ($listofpermtotest as $permtotest) {
864
+                    if (empty($this->rights->user->user->$permtotest)) {
865
+                        $this->rights->user->user->$permtotest = 1;
866
+                        $this->nb_rights++;
867
+                    }
868
+                }
869
+                if (empty($this->rights->user->self)) {
870
+                    $this->rights->user->self = new stdClass();
871
+                }
872
+                $listofpermtotest = ['creer', 'password'];
873
+                foreach ($listofpermtotest as $permtotest) {
874
+                    if (empty($this->rights->user->self->$permtotest)) {
875
+                        $this->rights->user->self->$permtotest = 1;
876
+                        $this->nb_rights++;
877
+                    }
878
+                }
879
+                // Add test on advanced permissions
880
+                if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) {
881
+                    if (empty($this->rights->user->user_advance)) {
882
+                        $this->rights->user->user_advance = new stdClass();
883
+                    }
884
+                    $listofpermtotest = ['readperms', 'write'];
885
+                    foreach ($listofpermtotest as $permtotest) {
886
+                        if (empty($this->rights->user->user_advance->$permtotest)) {
887
+                            $this->rights->user->user_advance->$permtotest = 1;
888
+                            $this->nb_rights++;
889
+                        }
890
+                    }
891
+                    if (empty($this->rights->user->self_advance)) {
892
+                        $this->rights->user->self_advance = new stdClass();
893
+                    }
894
+                    $listofpermtotest = ['readperms', 'writeperms'];
895
+                    foreach ($listofpermtotest as $permtotest) {
896
+                        if (empty($this->rights->user->self_advance->$permtotest)) {
897
+                            $this->rights->user->self_advance->$permtotest = 1;
898
+                            $this->nb_rights++;
899
+                        }
900
+                    }
901
+                    if (empty($this->rights->user->group_advance)) {
902
+                        $this->rights->user->group_advance = new stdClass();
903
+                    }
904
+                    $listofpermtotest = ['read', 'readperms', 'write', 'delete'];
905
+                    foreach ($listofpermtotest as $permtotest) {
906
+                        if (empty($this->rights->user) || empty($this->rights->user->group_advance->$permtotest)) {
907
+                            $this->rights->user->group_advance->$permtotest = 1;
908
+                            $this->nb_rights++;
909
+                        }
910
+                    }
911
+                }
912
+            }
913
+
914
+            // For backward compatibility
915
+            if (isset($this->rights->propale) && !isset($this->rights->propal)) {
916
+                $this->rights->propal = $this->rights->propale;
917
+            }
918
+            if (isset($this->rights->propal) && !isset($this->rights->propale)) {
919
+                $this->rights->propale = $this->rights->propal;
920
+            }
921
+
922
+            if (!$moduletag) {
923
+                // If the module was not define, then everything is loaded.
924
+                // Therefore, we can consider that the permissions are cached
925
+                // because they were all loaded for this user instance.
926
+                $this->all_permissions_are_loaded = 1;
927
+            } else {
928
+                // If the module is defined, we flag it as loaded into cache
929
+                $this->_tab_loaded[$moduletag] = 1;
930
+            }
931
+        }
932
+    }
933
+
934
+    /**
935
+     *  Change status of a user
936
+     *
937
+     * @param int $status Status to set
938
+     *
939
+     * @return int                 Return integer <0 if KO, 0 if nothing is done, >0 if OK
940
+     */
941
+    public function setstatus($status)
942
+    {
943
+        global $conf, $langs, $user;
944
+
945
+        $error = 0;
946
+
947
+        // Check parameters
948
+        if (isset($this->statut)) {
949
+            if ($this->statut == $status) {
950
+                return 0;
951
+            }
952
+        } elseif (isset($this->status) && $this->status == $status) {
953
+            return 0;
954
+        }
955
+
956
+        $this->db->begin();
957
+
958
+        // Save in database
959
+        $sql = "UPDATE " . $dbPrefix . "user";
960
+        $sql .= " SET statut = " . ((int)$status);
961
+        $sql .= " WHERE rowid = " . ((int)$this->id);
962
+        $result = $this->db->query($sql);
963
+
964
+        dol_syslog(get_class($this) . "::setstatus", LOG_DEBUG);
965
+        if ($result) {
966
+            if ($status == 0) {
967
+                $this->context['actionmsg'] = 'User ' . $this->login . ' disabled';
968
+            } else {
969
+                $this->context['actionmsg'] = 'User ' . $this->login . ' enabled';
970
+            }
971
+            // Call trigger
972
+            $result = $this->call_trigger('USER_ENABLEDISABLE', $user);
973
+            if ($result < 0) {
974
+                $error++;
975
+            }
976
+            // End call triggers
977
+        }
978
+
979
+        if ($error) {
980
+            $this->db->rollback();
981
+            return -$error;
982
+        } else {
983
+            $this->status = $status;
984
+            $this->statut = $status;
985
+            $this->db->commit();
986
+            return 1;
987
+        }
988
+    }
989
+
990
+    /**
991
+     * Sets object to supplied categories.
992
+     *
993
+     * Deletes object from existing categories not supplied.
994
+     * Adds it to non existing supplied categories.
995
+     * Existing categories are left untouch.
996
+     *
997
+     * @param int[]|int $categories Category or categories IDs
998
+     *
999
+     * @return  int                         Return integer <0 if KO, >0 if OK
1000
+     */
1001
+    public function setCategories($categories)
1002
+    {
1003
+        return parent::setCategoriesCommon($categories, Categorie::TYPE_USER);
1004
+    }
1005
+
1006
+    /**
1007
+     *  Delete the user
1008
+     *
1009
+     * @param User $user User than delete
1010
+     *
1011
+     * @return     int             Return integer <0 if KO, >0 if OK
1012
+     */
1013
+    public function delete(User $user)
1014
+    {
1015
+        global $conf, $langs;
1016
+
1017
+        $error = 0;
1018
+
1019
+        $this->db->begin();
1020
+
1021
+        $this->fetch($this->id);
1022
+
1023
+        dol_syslog(get_class($this) . "::delete", LOG_DEBUG);
1024
+
1025
+        // Remove rights
1026
+        $sql = "DELETE FROM " . $dbPrefix . "user_rights WHERE fk_user = " . ((int)$this->id);
1027
+
1028
+        if (!$error && !$this->db->query($sql)) {
1029
+            $error++;
1030
+            $this->error = $this->db->lasterror();
1031
+        }
1032
+
1033
+        // Remove group
1034
+        $sql = "DELETE FROM " . $dbPrefix . "usergroup_user WHERE fk_user  = " . ((int)$this->id);
1035
+        if (!$error && !$this->db->query($sql)) {
1036
+            $error++;
1037
+            $this->error = $this->db->lasterror();
1038
+        }
1039
+
1040
+        // Remove params
1041
+        $sql = "DELETE FROM " . $dbPrefix . "user_param WHERE fk_user  = " . ((int)$this->id);
1042
+        if (!$error && !$this->db->query($sql)) {
1043
+            $error++;
1044
+            $this->error = $this->db->lasterror();
1045
+        }
1046
+
1047
+        // If contact, remove link
1048
+        if ($this->contact_id > 0) {
1049
+            $sql = "UPDATE " . $dbPrefix . "socpeople SET fk_user_creat = null WHERE rowid = " . ((int)$this->contact_id);
1050
+            if (!$error && !$this->db->query($sql)) {
1051
+                $error++;
1052
+                $this->error = $this->db->lasterror();
1053
+            }
1054
+        }
1055
+
1056
+        // Remove extrafields
1057
+        if (!$error) {
1058
+            $result = $this->deleteExtraFields();
1059
+            if ($result < 0) {
1060
+                $error++;
1061
+                dol_syslog(get_class($this) . "::delete error -4 " . $this->error, LOG_ERR);
1062
+            }
1063
+        }
1064
+
1065
+        // Remove user
1066
+        if (!$error) {
1067
+            $sql = "DELETE FROM " . $dbPrefix . "user WHERE rowid = " . ((int)$this->id);
1068
+            dol_syslog(get_class($this) . "::delete", LOG_DEBUG);
1069
+            if (!$this->db->query($sql)) {
1070
+                $error++;
1071
+                $this->error = $this->db->lasterror();
1072
+            }
1073
+        }
1074
+
1075
+        if (!$error) {
1076
+            // Call trigger
1077
+            $result = $this->call_trigger('USER_DELETE', $user);
1078
+            if ($result < 0) {
1079
+                $error++;
1080
+                $this->db->rollback();
1081
+                return -1;
1082
+            }
1083
+            // End call triggers
1084
+
1085
+            $this->db->commit();
1086
+            return 1;
1087
+        } else {
1088
+            $this->db->rollback();
1089
+            return -1;
1090
+        }
1091
+    }
1092
+
1093
+    /**
1094
+     *  Load a user from database with its id or ref (login).
1095
+     *  This function does not load permissions, only user properties. Use getrights() for this just after the fetch.
1096
+     *
1097
+     * @param int $id If defined, id to used for search
1098
+     * @param string $login If defined, login to used for search
1099
+     * @param string $sid If defined, sid to used for search
1100
+     * @param int $loadpersonalconf 1=also load personal conf of user (in $user->conf->xxx), 0=do not load personal
1101
+     *                                 conf.
1102
+     * @param int $entity If a value is >= 0, we force the search on a specific entity. If -1, means
1103
+     *                                 search depens on default setup.
1104
+     * @param string $email If defined, email to used for search
1105
+     * @param int $fk_socpeople If defined, id of contact for search
1106
+     * @param int $use_email_oauth2 1=Use also email_oauth2 to fetch on email
1107
+     *
1108
+     * @return int                         Return integer <0 if KO, 0 not found, >0 if OK
1109
+     */
1110
+    public function fetch($id = 0, $login = '', $sid = '', $loadpersonalconf = 0, $entity = -1, $email = '', $fk_socpeople = 0, $use_email_oauth2 = 0)
1111
+    {
1112
+        global $conf, $user;
1113
+        global $config;
1114
+        $dbPrefix = $config->db->prefix;
1115
+
1116
+        // Clean parameters
1117
+        $login = trim($login);
1118
+
1119
+        // Get user
1120
+        $sql = "SELECT u.rowid, u.lastname, u.firstname, u.employee, u.gender, u.civility as civility_code, u.birth, u.job,";
1121
+        $sql .= " u.email, u.email_oauth2, u.personal_email,";
1122
+        $sql .= " u.socialnetworks,";
1123
+        $sql .= " u.signature, u.office_phone, u.office_fax, u.user_mobile, u.personal_mobile,";
1124
+        $sql .= " u.address, u.zip, u.town, u.fk_state as state_id, u.fk_country as country_id,";
1125
+        $sql .= " u.admin, u.login, u.note_private, u.note_public,";
1126
+        $sql .= " u.pass, u.pass_crypted, u.pass_temp, u.api_key,";
1127
+        $sql .= " u.fk_soc, u.fk_socpeople, u.fk_member, u.fk_user, u.ldap_sid, u.fk_user_expense_validator, u.fk_user_holiday_validator,";
1128
+        $sql .= " u.statut as status, u.lang, u.entity,";
1129
+        $sql .= " u.datec as datec,";
1130
+        $sql .= " u.tms as datem,";
1131
+        $sql .= " u.datelastlogin as datel,";
1132
+        $sql .= " u.datepreviouslogin as datep,";
1133
+        $sql .= " u.flagdelsessionsbefore,";
1134
+        $sql .= " u.iplastlogin,";
1135
+        $sql .= " u.ippreviouslogin,";
1136
+        $sql .= " u.datelastpassvalidation,";
1137
+        $sql .= " u.datestartvalidity,";
1138
+        $sql .= " u.dateendvalidity,";
1139
+        $sql .= " u.photo as photo,";
1140
+        $sql .= " u.openid as openid,";
1141
+        $sql .= " u.accountancy_code,";
1142
+        $sql .= " u.thm,";
1143
+        $sql .= " u.tjm,";
1144
+        $sql .= " u.salary,";
1145
+        $sql .= " u.salaryextra,";
1146
+        $sql .= " u.weeklyhours,";
1147
+        $sql .= " u.color,";
1148
+        $sql .= " u.dateemployment, u.dateemploymentend,";
1149
+        $sql .= " u.fk_warehouse,";
1150
+        $sql .= " u.ref_ext,";
1151
+        $sql .= " u.default_range, u.default_c_exp_tax_cat,"; // Expense report default mode
1152
+        $sql .= " u.national_registration_number,";
1153
+        $sql .= " u.ref_employee,";
1154
+        $sql .= " c.code as country_code, c.label as country,";
1155
+        $sql .= " d.code_departement as state_code, d.nom as state";
1156
+        $sql .= " FROM " . $dbPrefix . "user as u";
1157
+        $sql .= " LEFT JOIN " . $dbPrefix . "c_country as c ON u.fk_country = c.rowid";
1158
+        $sql .= " LEFT JOIN " . $dbPrefix . "c_departements as d ON u.fk_state = d.rowid";
1159
+
1160
+        if ($entity < 0) {
1161
+            if ((!isModEnabled('multicompany') || !getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) && (!empty($user->entity))) {
1162
+                $sql .= " WHERE u.entity IN (0, " . ((int)$conf->entity) . ")";
1163
+            } else {
1164
+                $sql .= " WHERE u.entity IS NOT NULL"; // multicompany is on in transverse mode or user making fetch is on entity 0, so user is allowed to fetch anywhere into database
1165
+            }
1166
+        } else {
1167
+            // The fetch was forced on an entity
1168
+            if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
1169
+                $sql .= " WHERE u.entity IS NOT NULL"; // multicompany is on in transverse mode or user making fetch is on entity 0, so user is allowed to fetch anywhere into database
1170
+            } else {
1171
+                $sql .= " WHERE u.entity IN (0, " . ((int)(($entity != '' && $entity >= 0) ? $entity : $conf->entity)) . ")"; // search in entity provided in parameter
1172
+            }
1173
+        }
1174
+
1175
+        if ($sid) {
1176
+            // permet une recherche du user par son SID ActiveDirectory ou Samba
1177
+            $sql .= " AND (u.ldap_sid = '" . $this->db->escape($sid) . "' OR u.login = '" . $this->db->escape($login) . "')";
1178
+        } elseif ($login) {
1179
+            $sql .= " AND u.login = '" . $this->db->escape($login) . "'";
1180
+        } elseif ($email) {
1181
+            $sql .= " AND (u.email = '" . $this->db->escape($email) . "'";
1182
+            if ($use_email_oauth2) {
1183
+                $sql .= " OR u.email_oauth2 = '" . $this->db->escape($email) . "'";
1184
+            }
1185
+            $sql .= ")";
1186
+        } elseif ($fk_socpeople > 0) {
1187
+            $sql .= " AND u.fk_socpeople = " . ((int)$fk_socpeople);
1188
+        } else {
1189
+            $sql .= " AND u.rowid = " . ((int)$id);
1190
+        }
1191
+        $sql .= " ORDER BY u.entity ASC"; // Avoid random result when there is 2 login in 2 different entities
1192
+
1193
+        if ($sid) {
1194
+            // permet une recherche du user par son SID ActiveDirectory ou Samba
1195
+            $sql .= ' ' . $this->db->plimit(1);
1196
+        }
1197
+
1198
+        $resql = $this->db->query($sql);
1199
+        if ($resql) {
1200
+            $num = $this->db->num_rows($resql);
1201
+            if ($num > 1) {
1202
+                $this->error = "USERDUPLICATEFOUND";
1203
+                dol_syslog(get_class($this) . "::fetch more than 1 user found", LOG_WARNING);
1204
+
1205
+                $this->db->free($resql);
1206
+                return 0;
1207
+            }
1208
+
1209
+            $obj = $this->db->fetch_object($resql);
1210
+            if ($obj) {
1211
+                $this->id = $obj->rowid;
1212
+                $this->ref = $obj->rowid;
1213
+
1214
+                $this->ref_ext = $obj->ref_ext;
1215
+
1216
+                $this->ldap_sid = $obj->ldap_sid;
1217
+                $this->civility_code = $obj->civility_code;
1218
+                $this->lastname = $obj->lastname;
1219
+                $this->firstname = $obj->firstname;
1220
+                $this->ref_employee = $obj->ref_employee;
1221
+                $this->national_registration_number = $obj->national_registration_number;
1222
+
1223
+                $this->employee = $obj->employee;
1224
+
1225
+                $this->login = $obj->login;
1226
+                $this->gender = $obj->gender;
1227
+                $this->birth = $this->db->jdate($obj->birth);
1228
+                $this->pass_indatabase = $obj->pass;
1229
+                $this->pass_indatabase_crypted = $obj->pass_crypted;
1230
+                $this->pass = $obj->pass;
1231
+                $this->pass_temp = $obj->pass_temp;
1232
+                $this->api_key = dolDecrypt($obj->api_key);
1233
+
1234
+                $this->address = $obj->address;
1235
+                $this->zip = $obj->zip;
1236
+                $this->town = $obj->town;
1237
+
1238
+                $this->country_id = $obj->country_id;
1239
+                $this->country_code = $obj->country_id ? $obj->country_code : '';
1240
+                //$this->country = $obj->country_id?($langs->trans('Country'.$obj->country_code)!='Country'.$obj->country_code?$langs->transnoentities('Country'.$obj->country_code):$obj->country):'';
1241
+
1242
+                $this->state_id = $obj->state_id;
1243
+                $this->state_code = $obj->state_code;
1244
+                $this->state = ($obj->state != '-' ? $obj->state : '');
1245
+
1246
+                $this->office_phone = $obj->office_phone;
1247
+                $this->office_fax = $obj->office_fax;
1248
+                $this->user_mobile = $obj->user_mobile;
1249
+                $this->personal_mobile = $obj->personal_mobile;
1250
+                $this->email = $obj->email;
1251
+                $this->email_oauth2 = $obj->email_oauth2;
1252
+                $this->personal_email = $obj->personal_email;
1253
+                $this->socialnetworks = ($obj->socialnetworks ? (array)json_decode($obj->socialnetworks, true) : []);
1254
+                $this->job = $obj->job;
1255
+                $this->signature = $obj->signature;
1256
+                $this->admin = $obj->admin;
1257
+                $this->note_public = $obj->note_public;
1258
+                $this->note_private = $obj->note_private;
1259
+                $this->note = $obj->note_private;   // deprecated
1260
+
1261
+                $this->statut = $obj->status;         // deprecated
1262
+                $this->status = $obj->status;
1263
+
1264
+                $this->photo = $obj->photo;
1265
+                $this->openid = $obj->openid;
1266
+                $this->lang = $obj->lang;
1267
+                $this->entity = $obj->entity;
1268
+                $this->accountancy_code = $obj->accountancy_code;
1269
+                $this->thm = $obj->thm;
1270
+                $this->tjm = $obj->tjm;
1271
+                $this->salary = $obj->salary;
1272
+                $this->salaryextra = $obj->salaryextra;
1273
+                $this->weeklyhours = $obj->weeklyhours;
1274
+                $this->color = $obj->color;
1275
+                $this->dateemployment = $this->db->jdate($obj->dateemployment);
1276
+                $this->dateemploymentend = $this->db->jdate($obj->dateemploymentend);
1277
+
1278
+                $this->datec = $this->db->jdate($obj->datec);
1279
+                $this->datem = $this->db->jdate($obj->datem);
1280
+                $this->datelastlogin = $this->db->jdate($obj->datel);
1281
+                $this->datepreviouslogin = $this->db->jdate($obj->datep);
1282
+                $this->flagdelsessionsbefore = $this->db->jdate($obj->flagdelsessionsbefore, 'gmt');
1283
+                $this->iplastlogin = $obj->iplastlogin;
1284
+                $this->ippreviouslogin = $obj->ippreviouslogin;
1285
+                $this->datestartvalidity = $this->db->jdate($obj->datestartvalidity);
1286
+                $this->dateendvalidity = $this->db->jdate($obj->dateendvalidity);
1287
+
1288
+                $this->socid = $obj->fk_soc;
1289
+                $this->contact_id = $obj->fk_socpeople;
1290
+                $this->fk_member = $obj->fk_member;
1291
+                $this->fk_user = $obj->fk_user;
1292
+                $this->fk_user_expense_validator = $obj->fk_user_expense_validator;
1293
+                $this->fk_user_holiday_validator = $obj->fk_user_holiday_validator;
1294
+
1295
+                $this->default_range = $obj->default_range;
1296
+                $this->default_c_exp_tax_cat = $obj->default_c_exp_tax_cat;
1297
+                $this->fk_warehouse = $obj->fk_warehouse;
1298
+
1299
+                // Protection when module multicompany was set, admin was set to first entity and then, the module was disabled,
1300
+                // in such case, this admin user must be admin for ALL entities.
1301
+                if (!isModEnabled('multicompany') && $this->admin && $this->entity == 1) {
1302
+                    $this->entity = 0;
1303
+                }
1304
+
1305
+                // Retrieve all extrafield
1306
+                // fetch optionals attributes and labels
1307
+                $this->fetch_optionals();
1308
+
1309
+                $this->db->free($resql);
1310
+            } else {
1311
+                $this->error = "USERNOTFOUND";
1312
+                dol_syslog(get_class($this) . "::fetch user not found", LOG_DEBUG);
1313
+
1314
+                $this->db->free($resql);
1315
+                return 0;
1316
+            }
1317
+        } else {
1318
+            $this->error = $this->db->lasterror();
1319
+            return -1;
1320
+        }
1321
+
1322
+        // To get back the global configuration unique to the user
1323
+        if ($loadpersonalconf) {
1324
+            $result = $this->loadPersonalConf();
1325
+
1326
+            $result = $this->loadDefaultValues();
1327
+
1328
+            if ($result < 0) {
1329
+                $this->error = $this->db->lasterror();
1330
+                return -3;
1331
+            }
1332
+        }
1333
+
1334
+        return 1;
1335
+    }
1336
+
1337
+    /**
1338
+     *  Load const values from database table user_param and set it into user->conf->XXX
1339
+     *
1340
+     * @return int                     >= 0 if OK, < 0 if KO
1341
+     */
1342
+    public function loadPersonalConf()
1343
+    {
1344
+        global $conf;
1345
+
1346
+        // Load user->conf for user
1347
+        $sql = "SELECT param, value FROM " . $dbPrefix . "user_param";
1348
+        $sql .= " WHERE fk_user = " . ((int)$this->id);
1349
+        $sql .= " AND entity = " . ((int)$conf->entity);
1350
+        //dol_syslog(get_class($this).'::fetch load personalized conf', LOG_DEBUG);
1351
+        $resql = $this->db->query($sql);
1352
+        if ($resql) {
1353
+            $num = $this->db->num_rows($resql);
1354
+            $i = 0;
1355
+            while ($i < $num) {
1356
+                $obj = $this->db->fetch_object($resql);
1357
+                $p = (!empty($obj->param) ? $obj->param : '');
1358
+                if (!empty($p)) {
1359
+                    $this->conf->$p = $obj->value;
1360
+                }
1361
+                $i++;
1362
+            }
1363
+            $this->db->free($resql);
1364
+
1365
+            return $num;
1366
+        } else {
1367
+            $this->error = $this->db->lasterror();
1368
+
1369
+            return -2;
1370
+        }
1371
+    }
1372
+
1373
+    /**
1374
+     *  Load default values from database table into property ->default_values
1375
+     *
1376
+     * @return int                     > 0 if OK, < 0 if KO
1377
+     */
1378
+    public function loadDefaultValues()
1379
+    {
1380
+        global $conf;
1381
+
1382
+        if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
1383
+            // Load user->default_values for user. TODO Save this in memcached ?
1384
+
1385
+            $defaultValues = new DefaultValues($this->db);
1386
+            $result = $defaultValues->fetchAll('', '', 0, 0, '(t.user_id:in:0,' . $this->id . ') AND (entity:in:' . (isset($this->entity) ? $this->entity : $conf->entity) . ',' . $conf->entity . ')');    // User 0 (all) + me (if defined)
1387
+            //$result = $defaultValues->fetchAll('', '', 0, 0, array('t.user_id'=>array(0, $this->id), 'entity'=>array((isset($this->entity) ? $this->entity : $conf->entity), $conf->entity)));    // User 0 (all) + me (if defined)
1388
+
1389
+            if (!is_array($result) && $result < 0) {
1390
+                setEventMessages($defaultValues->error, $defaultValues->errors, 'errors');
1391
+                dol_print_error($this->db);
1392
+                return -1;
1393
+            } elseif (count($result) > 0) {
1394
+                foreach ($result as $defval) {
1395
+                    if (!empty($defval->page) && !empty($defval->type) && !empty($defval->param)) {
1396
+                        $pagewithoutquerystring = $defval->page;
1397
+                        $pagequeries = '';
1398
+                        $reg = [];
1399
+                        if (preg_match('/^([^\?]+)\?(.*)$/', $pagewithoutquerystring, $reg)) {    // There is query param
1400
+                            $pagewithoutquerystring = $reg[1];
1401
+                            $pagequeries = $reg[2];
1402
+                        }
1403
+                        $this->default_values[$pagewithoutquerystring][$defval->type][$pagequeries ? $pagequeries : '_noquery_'][$defval->param] = $defval->value;
1404
+                    }
1405
+                }
1406
+            }
1407
+            if (!empty($this->default_values)) {
1408
+                foreach ($this->default_values as $a => $b) {
1409
+                    foreach ($b as $c => $d) {
1410
+                        krsort($this->default_values[$a][$c]);
1411
+                    }
1412
+                }
1413
+            }
1414
+        }
1415
+        return 1;
1416
+    }
1417
+
1418
+
1419
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1420
+
1421
+    /**
1422
+     *  Load all objects into $this->users
1423
+     *
1424
+     * @param string $sortorder sort order
1425
+     * @param string $sortfield sort field
1426
+     * @param int $limit limit page
1427
+     * @param int $offset page
1428
+     * @param string $filter Filter as an Universal Search string.
1429
+     *                                      Example: '((client:=:1) OR ((client:>=:2) AND (client:<=:3))) AND
1430
+     *                                      (client:!=:8) AND (nom:like:'a%')'
1431
+     * @param string $filtermode No more used
1432
+     * @param bool $entityfilter Activate entity filter
1433
+     *
1434
+     * @return int                         Return integer <0 if KO, >0 if OK
1435
+     */
1436
+    public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND', $entityfilter = false)
1437
+    {
1438
+        global $conf, $user;
1439
+
1440
+        $sql = "SELECT t.rowid";
1441
+        $sql .= ' FROM ' . $dbPrefix . $this->table_element . ' as t ';
1442
+
1443
+        if ($entityfilter) {
1444
+            if (getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
1445
+                if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1446
+                    $sql .= " WHERE t.entity IS NOT NULL"; // Show all users
1447
+                } else {
1448
+                    $sql .= "," . $dbPrefix . "usergroup_user as ug";
1449
+                    $sql .= " WHERE ((ug.fk_user = t.rowid";
1450
+                    $sql .= " AND ug.entity IN (" . getEntity('usergroup') . "))";
1451
+                    $sql .= " OR t.entity = 0)"; // Show always superadmin
1452
+                }
1453
+            } else {
1454
+                $sql .= " WHERE t.entity IN (" . getEntity('user') . ")";
1455
+            }
1456
+        } else {
1457
+            $sql .= " WHERE 1 = 1";
1458
+        }
1459
+
1460
+        // Manage filter
1461
+        $errormessage = '';
1462
+        $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
1463
+        if ($errormessage) {
1464
+            $this->errors[] = $errormessage;
1465
+            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
1466
+            return -1;
1467
+        }
1468
+
1469
+        $sql .= $this->db->order($sortfield, $sortorder);
1470
+        if ($limit) {
1471
+            $sql .= $this->db->plimit($limit + 1, $offset);
1472
+        }
1473
+
1474
+        dol_syslog(__METHOD__, LOG_DEBUG);
1475
+
1476
+        $resql = $this->db->query($sql);
1477
+        if ($resql) {
1478
+            $this->users = [];
1479
+            $num = $this->db->num_rows($resql);
1480
+            if ($num) {
1481
+                while ($obj = $this->db->fetch_object($resql)) {
1482
+                    $line = new self($this->db);
1483
+                    $result = $line->fetch($obj->rowid);
1484
+                    if ($result > 0 && !empty($line->id)) {
1485
+                        $this->users[$obj->rowid] = clone $line;
1486
+                    }
1487
+                }
1488
+                $this->db->free($resql);
1489
+            }
1490
+            return $num;
1491
+        } else {
1492
+            $this->errors[] = $this->db->lasterror();
1493
+            return -1;
1494
+        }
1495
+    }
1496
+
1497
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1498
+
1499
+    /**
1500
+     *  Create a user from a contact object. User will be internal but if contact is linked to a third party, user will
1501
+     *  be external
1502
+     *
1503
+     * @param Contact $contact Object for source contact
1504
+     * @param string $login Login to force
1505
+     * @param string $password Password to force
1506
+     *
1507
+     * @return int                 Return integer <0 if error, if OK returns id of created user
1508
+     */
1509
+    public function create_from_contact($contact, $login = '', $password = '')
1510
+    {
1511
+        // phpcs:enable
1512
+        global $conf, $user, $langs;
1513
+
1514
+        $error = 0;
1515
+
1516
+        // Define parameters
1517
+        $this->admin = 0;
1518
+        $this->civility_code = $contact->civility_code;
1519
+        $this->lastname = $contact->lastname;
1520
+        $this->firstname = $contact->firstname;
1521
+        //$this->gender = $contact->gender;     // contact ha sno gender
1522
+        $this->email = $contact->email;
1523
+        $this->socialnetworks = $contact->socialnetworks;
1524
+        $this->office_phone = $contact->phone_pro;
1525
+        $this->office_fax = $contact->fax;
1526
+        $this->user_mobile = $contact->phone_mobile;
1527
+        $this->address = $contact->address;
1528
+        $this->zip = $contact->zip;
1529
+        $this->town = $contact->town;
1530
+        $this->setUpperOrLowerCase();
1531
+        $this->state_id = $contact->state_id;
1532
+        $this->country_id = $contact->country_id;
1533
+        $this->employee = 0;
1534
+
1535
+        if (empty($login)) {
1536
+            include_once BASE_PATH . '/../Dolibarr/Lib/Functions2.php';
1537
+            $login = dol_buildlogin($contact->lastname, $contact->firstname);
1538
+        }
1539
+        $this->login = $login;
1540
+
1541
+        $this->db->begin();
1542
+
1543
+        // Create user and set $this->id. Trigger is disabled because executed later.
1544
+        $result = $this->create($user, 1);
1545
+        if ($result > 0) {
1546
+            $sql = "UPDATE " . $dbPrefix . "user";
1547
+            $sql .= " SET fk_socpeople=" . ((int)$contact->id);
1548
+            $sql .= ", civility='" . $this->db->escape($contact->civility_code) . "'";
1549
+            if ($contact->socid > 0) {
1550
+                $sql .= ", fk_soc=" . ((int)$contact->socid);
1551
+            }
1552
+            $sql .= " WHERE rowid=" . ((int)$this->id);
1553
+
1554
+            $resql = $this->db->query($sql);
1555
+
1556
+            dol_syslog(get_class($this) . "::create_from_contact", LOG_DEBUG);
1557
+            if ($resql) {
1558
+                $this->context['createfromcontact'] = 'createfromcontact';
1559
+
1560
+                // Call trigger
1561
+                $result = $this->call_trigger('USER_CREATE', $user);
1562
+                if ($result < 0) {
1563
+                    $error++;
1564
+                    $this->db->rollback();
1565
+                    return -1;
1566
+                }
1567
+                // End call triggers
1568
+
1569
+                $this->db->commit();
1570
+                return $this->id;
1571
+            } else {
1572
+                $this->error = $this->db->error();
1573
+
1574
+                $this->db->rollback();
1575
+                return -1;
1576
+            }
1577
+        } else {
1578
+            // $this->error deja positionne
1579
+            dol_syslog(get_class($this) . "::create_from_contact - 0");
1580
+
1581
+            $this->db->rollback();
1582
+            return $result;
1583
+        }
1584
+    }
1585
+
1586
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1587
+
1588
+    /**
1589
+     *  Create a user into database
1590
+     *
1591
+     * @param User $user Object user doing creation
1592
+     * @param int $notrigger 1=do not execute triggers, 0 otherwise
1593
+     *
1594
+     * @return int                     Return integer <0 if KO, id of created user if OK
1595
+     */
1596
+    public function create($user, $notrigger = 0)
1597
+    {
1598
+        global $conf, $langs;
1599
+        global $mysoc;
1600
+
1601
+        global $config;
1602
+        $dbPrefix = $config->db->prefix;
1603
+
1604
+        // Clean parameters
1605
+        $this->setUpperOrLowerCase();
1606
+
1607
+        $this->civility_code = trim((string)$this->civility_code);
1608
+        $this->login = trim((string)$this->login);
1609
+        if (!isset($this->entity)) {
1610
+            $this->entity = $conf->entity; // If not defined, we use default value
1611
+        }
1612
+
1613
+        dol_syslog(get_class($this) . "::create login=" . $this->login . ", user=" . (is_object($user) ? $user->id : ''), LOG_DEBUG);
1614
+
1615
+        $badCharUnauthorizedIntoLoginName = getDolGlobalString('MAIN_LOGIN_BADCHARUNAUTHORIZED', ',@<>"\'');
1616
+
1617
+        // Check parameters
1618
+        if (getDolGlobalString('USER_MAIL_REQUIRED') && !isValidEmail($this->email)) {
1619
+            $langs->load("errors");
1620
+            $this->error = $langs->trans("ErrorBadEMail", $this->email);
1621
+            return -1;
1622
+        }
1623
+        if (empty($this->login)) {
1624
+            $langs->load("errors");
1625
+            $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Login"));
1626
+            return -1;
1627
+        } elseif (preg_match('/[' . preg_quote($badCharUnauthorizedIntoLoginName, '/') . ']/', $this->login)) {
1628
+            $langs->load("errors");
1629
+            $this->error = $langs->trans("ErrorBadCharIntoLoginName", $langs->transnoentitiesnoconv("Login"));
1630
+            return -1;
1631
+        }
1632
+
1633
+        $this->datec = dol_now();
1634
+
1635
+        $error = 0;
1636
+        $this->db->begin();
1637
+
1638
+        // Check if login already exists in same entity or into entity 0.
1639
+        if ($this->login) {
1640
+            $sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND login = '" . $this->db->escape($this->login) . "'";
1641
+            $resqltochecklogin = $this->db->query($sqltochecklogin);
1642
+            if ($resqltochecklogin) {
1643
+                $objtochecklogin = $this->db->fetch_object($resqltochecklogin);
1644
+                if ($objtochecklogin && $objtochecklogin->nb > 0) {
1645
+                    $langs->load("errors");
1646
+                    $this->error = $langs->trans("ErrorLoginAlreadyExists", $this->login);
1647
+                    dol_syslog(get_class($this) . "::create " . $this->error, LOG_DEBUG);
1648
+                    $this->db->rollback();
1649
+                    return -6;
1650
+                }
1651
+                $this->db->free($resqltochecklogin);
1652
+            }
1653
+        }
1654
+        if (!empty($this->email)) {
1655
+            $sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND email = '" . $this->db->escape($this->email) . "'";
1656
+            $resqltochecklogin = $this->db->query($sqltochecklogin);
1657
+            if ($resqltochecklogin) {
1658
+                $objtochecklogin = $this->db->fetch_object($resqltochecklogin);
1659
+                if ($objtochecklogin && $objtochecklogin->nb > 0) {
1660
+                    $langs->load("errors");
1661
+                    $this->error = $langs->trans("ErrorEmailAlreadyExists", $this->email);
1662
+                    dol_syslog(get_class($this) . "::create " . $this->error, LOG_DEBUG);
1663
+                    $this->db->rollback();
1664
+                    return -6;
1665
+                }
1666
+                $this->db->free($resqltochecklogin);
1667
+            }
1668
+        }
1669
+
1670
+        // Insert into database
1671
+        $sql = "INSERT INTO " . $dbPrefix . "user (datec, login, ldap_sid, entity)";
1672
+        $sql .= " VALUES('" . $this->db->idate($this->datec) . "', '" . $this->db->escape($this->login) . "', '" . $this->db->escape($this->ldap_sid) . "', " . ((int)$this->entity) . ")";
1673
+        $result = $this->db->query($sql);
1674
+
1675
+        dol_syslog(get_class($this) . "::create", LOG_DEBUG);
1676
+        if ($result) {
1677
+            $this->id = $this->db->last_insert_id($dbPrefix . "user");
1678
+
1679
+            // Set default rights
1680
+            if ($this->set_default_rights() < 0) {
1681
+                $this->error = 'ErrorFailedToSetDefaultRightOfUser';
1682
+                $this->db->rollback();
1683
+                return -5;
1684
+            }
1685
+
1686
+            if (getDolGlobalString('MAIN_DEFAULT_WAREHOUSE_USER') && getDolGlobalString('STOCK_USERSTOCK_AUTOCREATE')) {
1687
+                require_once DOL_DOCUMENT_ROOT . '/product/stock/class/entrepot.class.php';
1688
+                $langs->load("stocks");
1689
+
1690
+                $entrepot = new Entrepot($this->db);
1691
+                $entrepot->label = $langs->trans("PersonalStock", $this->getFullName($langs));
1692
+                $entrepot->libelle = $entrepot->label; // For backward compatibility
1693
+                $entrepot->description = $langs->trans("ThisWarehouseIsPersonalStock", $this->getFullName($langs));
1694
+                $entrepot->statut = 1;
1695
+                $entrepot->country_id = $mysoc->country_id;
1696
+
1697
+                $warehouseid = $entrepot->create($user);
1698
+
1699
+                $this->fk_warehouse = $warehouseid;
1700
+            }
1701
+
1702
+            // Update minor fields
1703
+            $result = $this->update($user, 1, 1);
1704
+            if ($result < 0) {
1705
+                $this->db->rollback();
1706
+                return -4;
1707
+            }
1708
+
1709
+            if (!$notrigger) {
1710
+                // Call trigger
1711
+                $result = $this->call_trigger('USER_CREATE', $user);
1712
+                if ($result < 0) {
1713
+                    $error++;
1714
+                }
1715
+                // End call triggers
1716
+            }
1717
+
1718
+            if (!$error) {
1719
+                $this->db->commit();
1720
+                return $this->id;
1721
+            } else {
1722
+                //$this->error=$interface->error;
1723
+                dol_syslog(get_class($this) . "::create " . $this->error, LOG_ERR);
1724
+                $this->db->rollback();
1725
+                return -3;
1726
+            }
1727
+        } else {
1728
+            $this->error = $this->db->lasterror();
1729
+            $this->db->rollback();
1730
+            return -2;
1731
+        }
1732
+    }
1733
+
1734
+    /**
1735
+     *    Assign rights by default
1736
+     *
1737
+     * @return     integer erreur <0, si ok renvoi le nbre de droits par default positions
1738
+     */
1739
+    public function set_default_rights()
1740
+    {
1741
+        // phpcs:enable
1742
+        global $conf;
1743
+
1744
+        $rd = [];
1745
+        $num = 0;
1746
+        $sql = "SELECT id FROM " . $dbPrefix . "rights_def";
1747
+        $sql .= " WHERE bydefault = 1";
1748
+        $sql .= " AND entity = " . ((int)$conf->entity);
1749
+
1750
+        $resql = $this->db->query($sql);
1751
+        if ($resql) {
1752
+            $num = $this->db->num_rows($resql);
1753
+            $i = 0;
1754
+            while ($i < $num) {
1755
+                $row = $this->db->fetch_row($resql);
1756
+                $rd[$i] = $row[0];
1757
+                $i++;
1758
+            }
1759
+            $this->db->free($resql);
1760
+        }
1761
+        $i = 0;
1762
+        while ($i < $num) {
1763
+            $sql = "DELETE FROM " . $dbPrefix . "user_rights WHERE fk_user = $this->id AND fk_id=$rd[$i]";
1764
+            $result = $this->db->query($sql);
1765
+
1766
+            $sql = "INSERT INTO " . $dbPrefix . "user_rights (fk_user, fk_id) VALUES ($this->id, $rd[$i])";
1767
+            $result = $this->db->query($sql);
1768
+            if (!$result) {
1769
+                return -1;
1770
+            }
1771
+            $i++;
1772
+        }
1773
+
1774
+        return $i;
1775
+    }
1776
+
1777
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1778
+
1779
+    /**
1780
+     *      Update a user into database (and also password if this->pass is defined)
1781
+     *
1782
+     * @param User $user User making update
1783
+     * @param int $notrigger 1=do not execute triggers, 0 by default
1784
+     * @param int $nosyncmember 0=Synchronize linked member (standard info), 1=Do not synchronize linked member
1785
+     * @param int $nosyncmemberpass 0=Synchronize linked member (password), 1=Do not synchronize linked member
1786
+     * @param int $nosynccontact 0=Synchronize linked contact, 1=Do not synchronize linked contact
1787
+     *
1788
+     * @return int                         Return integer <0 if KO, >=0 if OK
1789
+     */
1790
+    public function update($user, $notrigger = 0, $nosyncmember = 0, $nosyncmemberpass = 0, $nosynccontact = 0)
1791
+    {
1792
+        global $conf, $langs;
1793
+
1794
+        global $config;
1795
+        $dbPrefix = $config->db->prefix;
1796
+
1797
+        $nbrowsaffected = 0;
1798
+        $error = 0;
1799
+
1800
+        dol_syslog(get_class($this) . "::update notrigger=" . $notrigger . ", nosyncmember=" . $nosyncmember . ", nosyncmemberpass=" . $nosyncmemberpass);
1801
+
1802
+        // Clean parameters
1803
+        $this->civility_code = trim((string)$this->civility_code);
1804
+        $this->lastname = trim((string)$this->lastname);
1805
+        $this->firstname = trim((string)$this->firstname);
1806
+        $this->ref_employee = trim((string)$this->ref_employee);
1807
+        $this->national_registration_number = trim((string)$this->national_registration_number);
1808
+        $this->employee = ($this->employee > 0 ? $this->employee : 0);
1809
+        $this->login = trim((string)$this->login);
1810
+        $this->gender = trim((string)$this->gender);
1811
+
1812
+        $this->pass = trim((string)$this->pass);
1813
+        $this->api_key = trim((string)$this->api_key);
1814
+        $this->datestartvalidity = empty($this->datestartvalidity) ? '' : $this->datestartvalidity;
1815
+        $this->dateendvalidity = empty($this->dateendvalidity) ? '' : $this->dateendvalidity;
1816
+
1817
+        $this->address = trim((string)$this->address);
1818
+        $this->zip = trim((string)$this->zip);
1819
+        $this->town = trim((string)$this->town);
1820
+
1821
+        $this->state_id = ($this->state_id > 0 ? $this->state_id : 0);
1822
+        $this->country_id = ($this->country_id > 0 ? $this->country_id : 0);
1823
+        $this->office_phone = trim((string)$this->office_phone);
1824
+        $this->office_fax = trim((string)$this->office_fax);
1825
+        $this->user_mobile = trim((string)$this->user_mobile);
1826
+        $this->personal_mobile = trim((string)$this->personal_mobile);
1827
+        $this->email = trim((string)$this->email);
1828
+        $this->personal_email = trim((string)$this->personal_email);
1829
+
1830
+        $this->job = trim((string)$this->job);
1831
+        $this->signature = trim((string)$this->signature);
1832
+        $this->note_public = trim((string)$this->note_public);
1833
+        $this->note_private = trim((string)$this->note_private);
1834
+        $this->openid = trim((string)$this->openid);
1835
+        $this->admin = ($this->admin > 0 ? $this->admin : 0);
1836
+
1837
+        $this->accountancy_code = trim((string)$this->accountancy_code);
1838
+        $this->color = trim((string)$this->color);
1839
+        $this->dateemployment = empty($this->dateemployment) ? '' : $this->dateemployment;
1840
+        $this->dateemploymentend = empty($this->dateemploymentend) ? '' : $this->dateemploymentend;
1841
+
1842
+        $this->birth = empty($this->birth) ? '' : $this->birth;
1843
+        $this->fk_warehouse = (int)$this->fk_warehouse;
1844
+
1845
+        $this->setUpperOrLowerCase();
1846
+
1847
+        // Check parameters
1848
+        $badCharUnauthorizedIntoLoginName = getDolGlobalString('MAIN_LOGIN_BADCHARUNAUTHORIZED', ',@<>"\'');
1849
+
1850
+        if (getDolGlobalString('USER_MAIL_REQUIRED') && !isValidEmail($this->email)) {
1851
+            $langs->load("errors");
1852
+            $this->error = $langs->trans("ErrorBadEMail", $this->email);
1853
+            return -1;
1854
+        }
1855
+        if (empty($this->login)) {
1856
+            $langs->load("errors");
1857
+            $this->error = $langs->trans("ErrorFieldRequired", 'Login');
1858
+            return -1;
1859
+        } elseif (preg_match('/[' . preg_quote($badCharUnauthorizedIntoLoginName, '/') . ']/', $this->login)) {
1860
+            $langs->load("errors");
1861
+            $this->error = $langs->trans("ErrorBadCharIntoLoginName", $langs->transnoentitiesnoconv("Login"));
1862
+            return -1;
1863
+        }
1864
+
1865
+        $this->db->begin();
1866
+
1867
+        // Check if login already exists in same entity or into entity 0.
1868
+        if (!empty($this->oldcopy) && $this->oldcopy->login != $this->login) {
1869
+            $sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND login = '" . $this->db->escape($this->login) . "'";
1870
+            $resqltochecklogin = $this->db->query($sqltochecklogin);
1871
+            if ($resqltochecklogin) {
1872
+                $objtochecklogin = $this->db->fetch_object($resqltochecklogin);
1873
+                if ($objtochecklogin && $objtochecklogin->nb > 0) {
1874
+                    $langs->load("errors");
1875
+                    $this->error = $langs->trans("ErrorLoginAlreadyExists", $this->login);
1876
+                    dol_syslog(get_class($this) . "::create " . $this->error, LOG_DEBUG);
1877
+                    $this->db->rollback();
1878
+                    return -1;
1879
+                }
1880
+            }
1881
+        }
1882
+        if (!empty($this->oldcopy) && !empty($this->email) && $this->oldcopy->email != $this->email) {
1883
+            $sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND email = '" . $this->db->escape($this->email) . "'";
1884
+            $resqltochecklogin = $this->db->query($sqltochecklogin);
1885
+            if ($resqltochecklogin) {
1886
+                $objtochecklogin = $this->db->fetch_object($resqltochecklogin);
1887
+                if ($objtochecklogin && $objtochecklogin->nb > 0) {
1888
+                    $langs->load("errors");
1889
+                    $this->error = $langs->trans("ErrorEmailAlreadyExists", $this->email);
1890
+                    dol_syslog(get_class($this) . "::create " . $this->error, LOG_DEBUG);
1891
+                    $this->db->rollback();
1892
+                    return -1;
1893
+                }
1894
+            }
1895
+        }
1896
+
1897
+        // Update data
1898
+        $sql = "UPDATE " . $dbPrefix . "user SET";
1899
+        $sql .= " civility = '" . $this->db->escape($this->civility_code) . "'";
1900
+        $sql .= ", lastname = '" . $this->db->escape($this->lastname) . "'";
1901
+        $sql .= ", firstname = '" . $this->db->escape($this->firstname) . "'";
1902
+        $sql .= ", ref_employee = '" . $this->db->escape($this->ref_employee) . "'";
1903
+        $sql .= ", national_registration_number = '" . $this->db->escape($this->national_registration_number) . "'";
1904
+        $sql .= ", employee = " . (int)$this->employee;
1905
+        $sql .= ", login = '" . $this->db->escape($this->login) . "'";
1906
+        $sql .= ", api_key = " . ($this->api_key ? "'" . $this->db->escape(dolEncrypt($this->api_key, '', '', 'dolibarr')) . "'" : "null");
1907
+        $sql .= ", gender = " . ($this->gender != -1 ? "'" . $this->db->escape($this->gender) . "'" : "null"); // 'man' or 'woman'
1908
+        $sql .= ", birth=" . (strval($this->birth) != '' ? "'" . $this->db->idate($this->birth, 'tzserver') . "'" : 'null');
1909
+        if (!empty($user->admin)) {
1910
+            $sql .= ", admin = " . (int)$this->admin; // admin flag can be set/unset only by an admin user
1911
+        }
1912
+        $sql .= ", address = '" . $this->db->escape($this->address) . "'";
1913
+        $sql .= ", zip = '" . $this->db->escape($this->zip) . "'";
1914
+        $sql .= ", town = '" . $this->db->escape($this->town) . "'";
1915
+        $sql .= ", fk_state = " . ((!empty($this->state_id) && $this->state_id > 0) ? "'" . $this->db->escape($this->state_id) . "'" : "null");
1916
+        $sql .= ", fk_country = " . ((!empty($this->country_id) && $this->country_id > 0) ? "'" . $this->db->escape($this->country_id) . "'" : "null");
1917
+        $sql .= ", office_phone = '" . $this->db->escape($this->office_phone) . "'";
1918
+        $sql .= ", office_fax = '" . $this->db->escape($this->office_fax) . "'";
1919
+        $sql .= ", user_mobile = '" . $this->db->escape($this->user_mobile) . "'";
1920
+        $sql .= ", personal_mobile = '" . $this->db->escape($this->personal_mobile) . "'";
1921
+        $sql .= ", email = '" . $this->db->escape($this->email) . "'";
1922
+        $sql .= ", personal_email = '" . $this->db->escape($this->personal_email) . "'";
1923
+        $sql .= ", socialnetworks = '" . $this->db->escape(json_encode($this->socialnetworks)) . "'";
1924
+        $sql .= ", job = '" . $this->db->escape($this->job) . "'";
1925
+        $sql .= ", signature = '" . $this->db->escape($this->signature) . "'";
1926
+        $sql .= ", accountancy_code = '" . $this->db->escape($this->accountancy_code) . "'";
1927
+        $sql .= ", color = '" . $this->db->escape($this->color) . "'";
1928
+        $sql .= ", dateemployment=" . (strval($this->dateemployment) != '' ? "'" . $this->db->idate($this->dateemployment) . "'" : 'null');
1929
+        $sql .= ", dateemploymentend=" . (strval($this->dateemploymentend) != '' ? "'" . $this->db->idate($this->dateemploymentend) . "'" : 'null');
1930
+        $sql .= ", datestartvalidity=" . (strval($this->datestartvalidity) != '' ? "'" . $this->db->idate($this->datestartvalidity) . "'" : 'null');
1931
+        $sql .= ", dateendvalidity=" . (strval($this->dateendvalidity) != '' ? "'" . $this->db->idate($this->dateendvalidity) . "'" : 'null');
1932
+        $sql .= ", note_private = '" . $this->db->escape($this->note_private) . "'";
1933
+        $sql .= ", note_public = '" . $this->db->escape($this->note_public) . "'";
1934
+        $sql .= ", photo = " . ($this->photo ? "'" . $this->db->escape($this->photo) . "'" : "null");
1935
+        $sql .= ", openid = " . ($this->openid ? "'" . $this->db->escape($this->openid) . "'" : "null");
1936
+        $sql .= ", fk_user = " . ($this->fk_user > 0 ? "'" . $this->db->escape($this->fk_user) . "'" : "null");
1937
+        $sql .= ", fk_user_expense_validator = " . ($this->fk_user_expense_validator > 0 ? "'" . $this->db->escape($this->fk_user_expense_validator) . "'" : "null");
1938
+        $sql .= ", fk_user_holiday_validator = " . ($this->fk_user_holiday_validator > 0 ? "'" . $this->db->escape($this->fk_user_holiday_validator) . "'" : "null");
1939
+        if (isset($this->thm) || $this->thm != '') {
1940
+            $sql .= ", thm= " . ($this->thm != '' ? "'" . $this->db->escape($this->thm) . "'" : "null");
1941
+        }
1942
+        if (isset($this->tjm) || $this->tjm != '') {
1943
+            $sql .= ", tjm= " . ($this->tjm != '' ? "'" . $this->db->escape($this->tjm) . "'" : "null");
1944
+        }
1945
+        if (isset($this->salary) || $this->salary != '') {
1946
+            $sql .= ", salary= " . ($this->salary != '' ? "'" . $this->db->escape($this->salary) . "'" : "null");
1947
+        }
1948
+        if (isset($this->salaryextra) || $this->salaryextra != '') {
1949
+            $sql .= ", salaryextra= " . ($this->salaryextra != '' ? "'" . $this->db->escape($this->salaryextra) . "'" : "null");
1950
+        }
1951
+        $sql .= ", weeklyhours= " . ($this->weeklyhours != '' ? "'" . $this->db->escape($this->weeklyhours) . "'" : "null");
1952
+        if (!empty($user->admin) && empty($user->entity) && $user->id != $this->id) {
1953
+            $sql .= ", entity = " . ((int)$this->entity); // entity flag can be set/unset only by an another superadmin user
1954
+        }
1955
+        $sql .= ", default_range = " . ($this->default_range > 0 ? $this->default_range : 'null');
1956
+        $sql .= ", default_c_exp_tax_cat = " . ($this->default_c_exp_tax_cat > 0 ? $this->default_c_exp_tax_cat : 'null');
1957
+        $sql .= ", fk_warehouse = " . ($this->fk_warehouse > 0 ? $this->fk_warehouse : "null");
1958
+        $sql .= ", lang = " . ($this->lang ? "'" . $this->db->escape($this->lang) . "'" : "null");
1959
+        $sql .= " WHERE rowid = " . ((int)$this->id);
1960
+
1961
+        dol_syslog(get_class($this) . "::update", LOG_DEBUG);
1962
+        $resql = $this->db->query($sql);
1963
+        if ($resql) {
1964
+            $nbrowsaffected += $this->db->affected_rows($resql);
1965
+
1966
+            // Update password
1967
+            if (!empty($this->pass)) {
1968
+                if ($this->pass != $this->pass_indatabase && !dol_verifyHash($this->pass, $this->pass_indatabase_crypted)) {
1969
+                    // If a new value for password is set and different than the one encrypted into database
1970
+                    $result = $this->setPassword($user, $this->pass, 0, $notrigger, $nosyncmemberpass, 0, 1);
1971
+                    if (is_int($result) && $result < 0) {
1972
+                        return -5;
1973
+                    }
1974
+                }
1975
+            }
1976
+
1977
+            // If user is linked to a member, remove old link to this member
1978
+            if ($this->fk_member > 0) {
1979
+                dol_syslog(get_class($this) . "::update remove link with member. We will recreate it later", LOG_DEBUG);
1980
+                $sql = "UPDATE " . $dbPrefix . "user SET fk_member = NULL where fk_member = " . ((int)$this->fk_member);
1981
+                $resql = $this->db->query($sql);
1982
+                if (!$resql) {
1983
+                    $this->error = $this->db->error();
1984
+                    $this->db->rollback();
1985
+                    return -5;
1986
+                }
1987
+            }
1988
+            // Set link to user
1989
+            dol_syslog(get_class($this) . "::update set link with member", LOG_DEBUG);
1990
+            $sql = "UPDATE " . $dbPrefix . "user SET fk_member =" . ($this->fk_member > 0 ? ((int)$this->fk_member) : 'null') . " where rowid = " . ((int)$this->id);
1991
+            $resql = $this->db->query($sql);
1992
+            if (!$resql) {
1993
+                $this->error = $this->db->error();
1994
+                $this->db->rollback();
1995
+                return -5;
1996
+            }
1997
+
1998
+            if ($nbrowsaffected) {  // If something has changed in data
1999
+                if ($this->fk_member > 0 && !$nosyncmember) {
2000
+                    dol_syslog(get_class($this) . "::update user is linked with a member. We try to update member too.", LOG_DEBUG);
2001
+
2002
+
2003
+                    // This user is linked with a member, so we also update member information
2004
+                    // if this is an update.
2005
+                    $adh = new Adherent($this->db);
2006
+                    $result = $adh->fetch($this->fk_member);
2007
+
2008
+                    if ($result > 0) {
2009
+                        $adh->civility_code = $this->civility_code;
2010
+                        $adh->firstname = $this->firstname;
2011
+                        $adh->lastname = $this->lastname;
2012
+                        $adh->login = $this->login;
2013
+                        $adh->gender = $this->gender;
2014
+                        $adh->birth = $this->birth;
2015
+
2016
+                        $adh->pass = $this->pass;
2017
+
2018
+                        $adh->address = $this->address;
2019
+                        $adh->town = $this->town;
2020
+                        $adh->zip = $this->zip;
2021
+                        $adh->state_id = $this->state_id;
2022
+                        $adh->country_id = $this->country_id;
2023
+
2024
+                        $adh->email = $this->email;
2025
+
2026
+                        $adh->socialnetworks = $this->socialnetworks;
2027
+
2028
+                        $adh->phone = $this->office_phone;
2029
+                        $adh->phone_mobile = $this->user_mobile;
2030
+
2031
+                        $adh->default_lang = $this->lang;
2032
+
2033
+                        $adh->user_id = $this->id;
2034
+                        $adh->user_login = $this->login;
2035
+
2036
+                        $result = $adh->update($user, 0, 1, 0);
2037
+                        if ($result < 0) {
2038
+                            $this->error = $adh->error;
2039
+                            $this->errors = $adh->errors;
2040
+                            dol_syslog(get_class($this) . "::update error after calling adh->update to sync it with user: " . $this->error, LOG_ERR);
2041
+                            $error++;
2042
+                        }
2043
+                    } elseif ($result < 0) {
2044
+                        $this->error = $adh->error;
2045
+                        $this->errors = $adh->errors;
2046
+                        $error++;
2047
+                    }
2048
+                }
2049
+
2050
+                if ($this->contact_id > 0 && !$nosynccontact) {
2051
+                    dol_syslog(get_class($this) . "::update user is linked with a contact. We try to update contact too.", LOG_DEBUG);
2052
+
2053
+
2054
+                    // This user is linked with a contact, so we also update contact information if this is an update.
2055
+                    $tmpobj = new Contact($this->db);
2056
+                    $result = $tmpobj->fetch($this->contact_id);
2057
+
2058
+                    if ($result >= 0) {
2059
+                        $tmpobj->civility_code = $this->civility_code;
2060
+                        $tmpobj->firstname = $this->firstname;
2061
+                        $tmpobj->lastname = $this->lastname;
2062
+                        $tmpobj->login = $this->login;
2063
+                        $tmpobj->gender = $this->gender;
2064
+                        $tmpobj->birth = $this->birth;
2065
+
2066
+                        //$tmpobj->pass=$this->pass;
2067
+
2068
+                        $tmpobj->email = $this->email;
2069
+
2070
+                        $tmpobj->socialnetworks = $this->socialnetworks;
2071
+
2072
+                        $tmpobj->phone_pro = $this->office_phone;
2073
+                        $tmpobj->phone_mobile = $this->user_mobile;
2074
+                        $tmpobj->fax = $this->office_fax;
2075
+
2076
+                        $tmpobj->default_lang = $this->lang;
2077
+
2078
+                        $tmpobj->address = $this->address;
2079
+                        $tmpobj->town = $this->town;
2080
+                        $tmpobj->zip = $this->zip;
2081
+                        $tmpobj->state_id = $this->state_id;
2082
+                        $tmpobj->country_id = $this->country_id;
2083
+
2084
+                        $tmpobj->user_id = $this->id;
2085
+                        $tmpobj->user_login = $this->login;
2086
+
2087
+                        $result = $tmpobj->update($tmpobj->id, $user, 0, 'update', 1);
2088
+                        if ($result < 0) {
2089
+                            $this->error = $tmpobj->error;
2090
+                            $this->errors = $tmpobj->errors;
2091
+                            dol_syslog(get_class($this) . "::update error after calling adh->update to sync it with user: " . $this->error, LOG_ERR);
2092
+                            $error++;
2093
+                        }
2094
+                    } else {
2095
+                        $this->error = $tmpobj->error;
2096
+                        $this->errors = $tmpobj->errors;
2097
+                        $error++;
2098
+                    }
2099
+                }
2100
+            }
2101
+
2102
+            $action = 'update';
2103
+
2104
+            // Actions on extra fields
2105
+            if (!$error) {
2106
+                $result = $this->insertExtraFields();
2107
+                if ($result < 0) {
2108
+                    $error++;
2109
+                }
2110
+            }
2111
+
2112
+            if (!$error && !$notrigger) {
2113
+                // Call trigger
2114
+                $result = $this->call_trigger('USER_MODIFY', $user);
2115
+                if ($result < 0) {
2116
+                    $error++;
2117
+                }
2118
+                // End call triggers
2119
+            }
2120
+
2121
+            if (!$error) {
2122
+                $this->db->commit();
2123
+                return $nbrowsaffected;
2124
+            } else {
2125
+                dol_syslog(get_class($this) . "::update error=" . $this->error, LOG_ERR);
2126
+                $this->db->rollback();
2127
+                return -1;
2128
+            }
2129
+        } else {
2130
+            $this->error = $this->db->lasterror();
2131
+            $this->db->rollback();
2132
+            return -2;
2133
+        }
2134
+    }
2135
+
2136
+    /**
2137
+     *  Change password of a user
2138
+     *
2139
+     * @param User $user Object user of user requesting the change (not the user for who we change
2140
+     *                                       the password). May be unknown.
2141
+     * @param string $password New password, in clear text or already encrypted (to generate if not
2142
+     *                                       provided)
2143
+     * @param int $changelater 0=Default, 1=Save password into pass_temp to change password only after
2144
+     *                                       clicking on confirm email
2145
+     * @param int $notrigger 1=Does not launch triggers
2146
+     * @param int $nosyncmember Do not synchronize linked member
2147
+     * @param int $passwordalreadycrypted 0=Value is cleartext password, 1=Value is encrypted value.
2148
+     * @param int $flagdelsessionsbefore 1=Save also the current date to ask to invalidate all other session before
2149
+     *                                       this date.
2150
+     *
2151
+     * @return int|string                      If OK return clear password, 0 if no change (warning, you may retrieve 1
2152
+     *                                         instead of 0 even if password was same), < 0 if error
2153
+     */
2154
+    public function setPassword($user, $password = '', $changelater = 0, $notrigger = 0, $nosyncmember = 0, $passwordalreadycrypted = 0, $flagdelsessionsbefore = 1)
2155
+    {
2156
+        global $conf, $langs;
2157
+        global $config;
2158
+        $dbPrefix = $config->db->prefix;
2159
+
2160
+        require_once BASE_PATH . '/../Dolibarr/Lib/Security2.php';
2161
+
2162
+        $error = 0;
2163
+
2164
+        dol_syslog(get_class($this) . "::setPassword user=" . $user->id . " password=" . preg_replace('/./i', '*', $password) . " changelater=" . $changelater . " notrigger=" . $notrigger . " nosyncmember=" . $nosyncmember, LOG_DEBUG);
2165
+
2166
+        // If new password not provided, we generate one
2167
+        if (!$password) {
2168
+            $password = getRandomPassword(false);
2169
+        }
2170
+
2171
+        // Check and encrypt the password
2172
+        if (empty($passwordalreadycrypted)) {
2173
+            if (getDolGlobalString('USER_PASSWORD_GENERATED')) {
2174
+                // Add a check on rules for password syntax using the setup of the password generator
2175
+                $modGeneratePassClass = 'modGeneratePass' . ucfirst($conf->global->USER_PASSWORD_GENERATED);
2176
+
2177
+                include_once DOL_DOCUMENT_ROOT . '/core/modules/security/generate/' . $modGeneratePassClass . '.class.php';
2178
+                if (class_exists($modGeneratePassClass)) {
2179
+                    $modGeneratePass = new $modGeneratePassClass($this->db, $conf, $langs, $user);
2180
+
2181
+                    // To check an input user password, we disable the cleaning on ambiguous characters (this is used only for auto-generated password)
2182
+                    $modGeneratePass->WithoutAmbi = 0;
2183
+
2184
+                    // Call to validatePassword($password) to check pass match rules
2185
+                    $testpassword = $modGeneratePass->validatePassword($password);
2186
+                    if (!$testpassword) {
2187
+                        $this->error = $modGeneratePass->error;
2188
+                        return -1;
2189
+                    }
2190
+                }
2191
+            }
2192
+
2193
+
2194
+            // Now, we encrypt the new password
2195
+            $password_crypted = dol_hash($password);
2196
+        }
2197
+
2198
+        // Update password
2199
+        if (!$changelater) {
2200
+            if (!is_object($this->oldcopy)) {
2201
+                $this->oldcopy = clone $this;
2202
+            }
2203
+
2204
+            $this->db->begin();
2205
+
2206
+            $sql = "UPDATE " . $dbPrefix . "user";
2207
+            $sql .= " SET pass_crypted = '" . $this->db->escape($password_crypted) . "',";
2208
+            $sql .= " pass_temp = null";
2209
+            if (!empty($flagdelsessionsbefore)) {
2210
+                $sql .= ", flagdelsessionsbefore = '" . $this->db->idate(dol_now() - 5, 'gmt') . "'";
2211
+            }
2212
+            if (getDolGlobalString('DATABASE_PWD_ENCRYPTED')) {
2213
+                $sql .= ", pass = null";
2214
+            } else {
2215
+                $sql .= ", pass = '" . $this->db->escape($password) . "'";
2216
+            }
2217
+            $sql .= " WHERE rowid = " . ((int)$this->id);
2218
+
2219
+            dol_syslog(get_class($this) . "::setPassword", LOG_DEBUG);
2220
+            $result = $this->db->query($sql);
2221
+            if ($result) {
2222
+                if ($this->db->affected_rows($result)) {
2223
+                    $this->pass = $password;
2224
+                    $this->pass_indatabase = $password;
2225
+                    $this->pass_indatabase_crypted = $password_crypted;
2226
+
2227
+                    if ($this->fk_member && !$nosyncmember) {
2228
+                        // This user is linked with a member, so we also update members information
2229
+                        // if this is an update.
2230
+                        $adh = new Adherent($this->db);
2231
+                        $result = $adh->fetch($this->fk_member);
2232
+
2233
+                        if ($result >= 0) {
2234
+                            $result = $adh->setPassword($user, $this->pass, (!getDolGlobalString('DATABASE_PWD_ENCRYPTED') ? 0 : 1), 1); // The encryption is not managed in the 'adherent' module
2235
+                            if (is_int($result) && $result < 0) {
2236
+                                $this->error = $adh->error;
2237
+                                dol_syslog(get_class($this) . "::setPassword " . $this->error, LOG_ERR);
2238
+                                $error++;
2239
+                            }
2240
+                        } else {
2241
+                            $this->error = $adh->error;
2242
+                            $error++;
2243
+                        }
2244
+                    }
2245
+
2246
+                    dol_syslog(get_class($this) . "::setPassword notrigger=" . $notrigger . " error=" . $error, LOG_DEBUG);
2247
+
2248
+                    if (!$error && !$notrigger) {
2249
+                        // Call trigger
2250
+                        $result = $this->call_trigger('USER_NEW_PASSWORD', $user);
2251
+                        if ($result < 0) {
2252
+                            $error++;
2253
+                            $this->db->rollback();
2254
+                            return -1;
2255
+                        }
2256
+                        // End call triggers
2257
+                    }
2258
+
2259
+                    $this->db->commit();
2260
+                    return $this->pass;
2261
+                } else {
2262
+                    $this->db->rollback();
2263
+                    return 0;
2264
+                }
2265
+            } else {
2266
+                $this->db->rollback();
2267
+                dol_print_error($this->db);
2268
+                return -1;
2269
+            }
2270
+        } else {
2271
+            // We store password in password temporary field.
2272
+            // After receiving confirmation link, we will erase and store it in pass_crypted
2273
+            $sql = "UPDATE " . $dbPrefix . "user";
2274
+            $sql .= " SET pass_temp = '" . $this->db->escape($password) . "'";
2275
+            $sql .= " WHERE rowid = " . ((int)$this->id);
2276
+
2277
+            dol_syslog(get_class($this) . "::setPassword", LOG_DEBUG); // No log
2278
+            $result = $this->db->query($sql);
2279
+            if ($result) {
2280
+                return $password;
2281
+            } else {
2282
+                dol_print_error($this->db);
2283
+                return -3;
2284
+            }
2285
+        }
2286
+    }
2287
+
2288
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2289
+
2290
+    /**
2291
+     *      Renvoie la derniere erreur fonctionnelle de manipulation de l'objet
2292
+     *
2293
+     * @return    string      chaine erreur
2294
+     */
2295
+    public function error()
2296
+    {
2297
+        return $this->error;
2298
+    }
2299
+
2300
+    /**
2301
+     *  Create a user into database from a member object.
2302
+     *  If $member->fk_soc is set, it will be an external user.
2303
+     *
2304
+     * @param Adherent $member Object member source
2305
+     * @param string $login Login to force
2306
+     *
2307
+     * @return int                         Return integer <0 if KO, if OK, return id of created account
2308
+     */
2309
+    public function create_from_member($member, $login = '')
2310
+    {
2311
+        // phpcs:enable
2312
+        global $conf, $user, $langs;
2313
+
2314
+        // Set properties on new user
2315
+        $this->admin = 0;
2316
+        $this->civility_code = $member->civility_id;
2317
+        $this->lastname = $member->lastname;
2318
+        $this->firstname = $member->firstname;
2319
+        $this->gender = $member->gender;
2320
+        $this->email = $member->email;
2321
+        $this->fk_member = $member->id;
2322
+        $this->address = $member->address;
2323
+        $this->zip = $member->zip;
2324
+        $this->town = $member->town;
2325
+        $this->setUpperOrLowerCase();
2326
+        $this->state_id = $member->state_id;
2327
+        $this->country_id = $member->country_id;
2328
+        $this->socialnetworks = $member->socialnetworks;
2329
+
2330
+        $this->pass = $member->pass;
2331
+        $this->pass_crypted = $member->pass_indatabase_crypted;
2332
+
2333
+        if (empty($login)) {
2334
+            include_once BASE_PATH . '/../Dolibarr/Lib/Functions2.php';
2335
+            $login = dol_buildlogin($member->lastname, $member->firstname);
2336
+        }
2337
+        $this->login = $login;
2338
+
2339
+        $this->db->begin();
2340
+
2341
+        // Create and set $this->id
2342
+        $result = $this->create($user);
2343
+        if ($result > 0) {
2344
+            if (!empty($this->pass)) {  // If a clear password was received (this situation should not happen anymore now), we use it to save it into database
2345
+                $newpass = $this->setPassword($user, $this->pass);
2346
+                if (is_int($newpass) && $newpass < 0) {
2347
+                    $result = -2;
2348
+                }
2349
+            } elseif (!empty($this->pass_crypted)) {    // If an encrypted password is already known, we save it directly into database because the previous create did not save it.
2350
+                $sql = "UPDATE " . $dbPrefix . "user";
2351
+                $sql .= " SET pass_crypted = '" . $this->db->escape($this->pass_crypted) . "'";
2352
+                $sql .= " WHERE rowid=" . ((int)$this->id);
2353
+
2354
+                $resql = $this->db->query($sql);
2355
+                if (!$resql) {
2356
+                    $result = -1;
2357
+                }
2358
+            }
2359
+
2360
+            if ($result > 0 && $member->socid) {    // If member is linked to a thirdparty
2361
+                $sql = "UPDATE " . $dbPrefix . "user";
2362
+                $sql .= " SET fk_soc=" . ((int)$member->socid);
2363
+                $sql .= " WHERE rowid=" . ((int)$this->id);
2364
+
2365
+                dol_syslog(get_class($this) . "::create_from_member", LOG_DEBUG);
2366
+                $resql = $this->db->query($sql);
2367
+                if ($resql) {
2368
+                    $this->db->commit();
2369
+                    return $this->id;
2370
+                } else {
2371
+                    $this->error = $this->db->lasterror();
2372
+
2373
+                    $this->db->rollback();
2374
+                    return -1;
2375
+                }
2376
+            }
2377
+        }
2378
+
2379
+        if ($result > 0) {
2380
+            $this->db->commit();
2381
+            return $this->id;
2382
+        } else {
2383
+            // $this->error deja positionne
2384
+            $this->db->rollback();
2385
+            return -2;
2386
+        }
2387
+    }
2388
+
2389
+
2390
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2391
+
2392
+    /**
2393
+     *  Update the user's last login date in the database.
2394
+     *  Function called when a new connection is made by the user
2395
+     *
2396
+     * @return int     Return integer <0 si echec, >=0 si ok
2397
+     */
2398
+    public function update_last_login_date()
2399
+    {
2400
+        global $config;
2401
+        $dbPrefix = $config->db->prefix;
2402
+
2403
+        // phpcs:enable
2404
+        $now = dol_now();
2405
+
2406
+        $userremoteip = getUserRemoteIP();
2407
+
2408
+        $sql = "UPDATE " . $dbPrefix . "user SET";
2409
+        $sql .= " datepreviouslogin = datelastlogin,";
2410
+        $sql .= " ippreviouslogin = iplastlogin,";
2411
+        $sql .= " datelastlogin = '" . $this->db->idate($now) . "',";
2412
+        $sql .= " iplastlogin = '" . $this->db->escape($userremoteip) . "',";
2413
+        $sql .= " tms = tms"; // The last update date must change because the last login date is updated
2414
+        $sql .= " WHERE rowid = " . ((int)$this->id);
2415
+
2416
+        dol_syslog(get_class($this) . "::update_last_login_date user->id=" . $this->id . " " . $sql, LOG_DEBUG);
2417
+        $resql = $this->db->query($sql);
2418
+        if ($resql) {
2419
+            $this->datepreviouslogin = $this->datelastlogin;
2420
+            $this->datelastlogin = $now;
2421
+            $this->ippreviouslogin = $this->iplastlogin;
2422
+            $this->iplastlogin = $userremoteip;
2423
+            return 1;
2424
+        } else {
2425
+            $this->error = $this->db->lasterror() . ' sql=' . $sql;
2426
+            return -1;
2427
+        }
2428
+    }
2429
+
2430
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2431
+
2432
+    /**
2433
+     *  Send a new password (or instructions to reset it) by email
2434
+     *
2435
+     * @param User $user Object user that send the email (not the user we send to) @todo object $user is not
2436
+     *                            used !
2437
+     * @param string $password New password
2438
+     * @param int $changelater 0=Send clear passwod into email, 1=Change password only after clicking on confirm
2439
+     *                            email. @return int                     Return integer < 0 si erreur, > 0 si ok
2440
+     * @todo Add method 2 = Send link to reset password
2441
+     *
2442
+     */
2443
+    public function send_password($user, $password = '', $changelater = 0)
2444
+    {
2445
+        // phpcs:enable
2446
+        global $conf, $langs, $mysoc;
2447
+        global $dolibarr_main_url_root;
2448
+
2449
+        require_once DOL_DOCUMENT_ROOT . '/core/class/CMailFile.class.php';
2450
+
2451
+        $msgishtml = 0;
2452
+
2453
+        // Define $msg
2454
+        $mesg = '';
2455
+
2456
+        $outputlangs = new Translate("", $conf);
2457
+
2458
+        if (
2459
+            isset($this->conf->MAIN_LANG_DEFAULT)
2460
+            && $this->conf->MAIN_LANG_DEFAULT != 'auto'
2461
+        ) {  // If user has defined its own language (rare because in most cases, auto is used)
2462
+            $outputlangs->getDefaultLang($this->conf->MAIN_LANG_DEFAULT);
2463
+        }
2464
+
2465
+        if ($this->conf->MAIN_LANG_DEFAULT) {
2466
+            $outputlangs->setDefaultLang($this->conf->MAIN_LANG_DEFAULT);
2467
+        } else {    // If user has not defined its own language, we used current language
2468
+            $outputlangs = $langs;
2469
+        }
2470
+
2471
+        // Load translation files required by the page
2472
+        $outputlangs->loadLangs(["main", "errors", "users", "other"]);
2473
+
2474
+        $appli = getDolGlobalString('MAIN_APPLICATION_TITLE', constant('DOL_APPLICATION_TITLE'));
2475
+
2476
+        $subject = '[' . $appli . '] ' . $outputlangs->transnoentitiesnoconv("SubjectNewPassword", $appli);
2477
+
2478
+        // Define $urlwithroot
2479
+        $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($dolibarr_main_url_root));
2480
+        $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
2481
+
2482
+        if (!$changelater) {
2483
+            $url = $urlwithroot . '/';
2484
+            if (getDolGlobalString('URL_REDIRECTION_AFTER_CHANGEPASSWORD')) {
2485
+                $url = getDolGlobalString('URL_REDIRECTION_AFTER_CHANGEPASSWORD');
2486
+            }
2487
+
2488
+            dol_syslog(get_class($this) . "::send_password changelater is off, url=" . $url);
2489
+
2490
+            $mesg .= $outputlangs->transnoentitiesnoconv("RequestToResetPasswordReceived") . ".\n";
2491
+            $mesg .= $outputlangs->transnoentitiesnoconv("NewKeyIs") . " :\n\n";
2492
+            $mesg .= $outputlangs->transnoentitiesnoconv("Login") . " = " . $this->login . "\n";
2493
+            $mesg .= $outputlangs->transnoentitiesnoconv("Password") . " = " . $password . "\n\n";
2494
+            $mesg .= "\n";
2495
+
2496
+            $mesg .= $outputlangs->transnoentitiesnoconv("ClickHereToGoTo", $appli) . ': ' . $url . "\n\n";
2497
+            $mesg .= "--\n";
2498
+            $mesg .= $user->getFullName($outputlangs); // Username that send the email (not the user for who we want to reset password)
2499
+        } else {
2500
+            //print $password.'-'.$this->id.'-'.$conf->file->instance_unique_id;
2501
+            $url = $urlwithroot . '/user/passwordforgotten.php?action=validatenewpassword';
2502
+            $url .= '&username=' . urlencode($this->login) . "&passworduidhash=" . urlencode(dol_hash($password . '-' . $this->id . '-' . $conf->file->instance_unique_id));
2503
+            if (isModEnabled('multicompany')) {
2504
+                $url .= '&entity=' . (!empty($this->entity) ? $this->entity : 1);
2505
+            }
2506
+
2507
+            dol_syslog(get_class($this) . "::send_password changelater is on, url=" . $url);
2508
+
2509
+            $msgishtml = 1;
2510
+
2511
+            $mesg .= $outputlangs->transnoentitiesnoconv("RequestToResetPasswordReceived") . "<br>\n";
2512
+            $mesg .= $outputlangs->transnoentitiesnoconv("NewKeyWillBe") . " :<br>\n<br>\n";
2513
+            $mesg .= '<strong>' . $outputlangs->transnoentitiesnoconv("Login") . "</strong> = " . $this->login . "<br>\n";
2514
+            $mesg .= '<strong>' . $outputlangs->transnoentitiesnoconv("Password") . "</strong> = " . $password . "<br>\n<br>\n";
2515
+            $mesg .= "<br>\n";
2516
+            $mesg .= $outputlangs->transnoentitiesnoconv("YouMustClickToChange") . " :<br>\n";
2517
+            $mesg .= '<a href="' . $url . '" rel="noopener">' . $outputlangs->transnoentitiesnoconv("ConfirmPasswordChange") . '</a>' . "<br>\n<br>\n";
2518
+            $mesg .= $outputlangs->transnoentitiesnoconv("ForgetIfNothing") . "<br>\n<br>\n";
2519
+        }
2520
+
2521
+        $trackid = 'use' . $this->id;
2522
+        $sendcontext = 'password';
2523
+
2524
+        $mailfile = new CMailFile(
2525
+            $subject,
2526
+            $this->email,
2527
+            $conf->global->MAIN_MAIL_EMAIL_FROM,
2528
+            $mesg,
2529
+            [],
2530
+            [],
2531
+            [],
2532
+            '',
2533
+            '',
2534
+            0,
2535
+            $msgishtml,
2536
+            '',
2537
+            '',
2538
+            $trackid,
2539
+            '',
2540
+            $sendcontext
2541
+        );
2542
+
2543
+        if ($mailfile->sendfile()) {
2544
+            return 1;
2545
+        } else {
2546
+            $langs->trans("errors");
2547
+            $this->error = $langs->trans("ErrorFailedToSendPassword") . ' ' . $mailfile->error;
2548
+            return -1;
2549
+        }
2550
+    }
2551
+
2552
+
2553
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2554
+
2555
+    /**
2556
+     *  Read clicktodial information for user
2557
+     *
2558
+     * @return int Return integer <0 if KO, >0 if OK
2559
+     */
2560
+    public function fetch_clicktodial()
2561
+    {
2562
+        // phpcs:enable
2563
+        $sql = "SELECT url, login, pass, poste ";
2564
+        $sql .= " FROM " . $dbPrefix . "user_clicktodial as u";
2565
+        $sql .= " WHERE u.fk_user = " . ((int)$this->id);
2566
+
2567
+        $resql = $this->db->query($sql);
2568
+        if ($resql) {
2569
+            if ($this->db->num_rows($resql)) {
2570
+                $obj = $this->db->fetch_object($resql);
2571
+
2572
+                $this->clicktodial_url = $obj->url;
2573
+                $this->clicktodial_login = $obj->login;
2574
+                $this->clicktodial_password = $obj->pass;
2575
+                $this->clicktodial_poste = $obj->poste;
2576
+            }
2577
+
2578
+            $this->clicktodial_loaded = 1; // Data loaded (found or not)
2579
+
2580
+            $this->db->free($resql);
2581
+            return 1;
2582
+        } else {
2583
+            $this->error = $this->db->error();
2584
+            return -1;
2585
+        }
2586
+    }
2587
+
2588
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2589
+
2590
+    /**
2591
+     *  Update clicktodial info
2592
+     *
2593
+     * @return int  Return integer <0 if KO, >0 if OK
2594
+     */
2595
+    public function update_clicktodial()
2596
+    {
2597
+        global $config;
2598
+        $dbPrefix = $config->db->prefix;
2599
+
2600
+        // phpcs:enable
2601
+        $this->db->begin();
2602
+
2603
+        $sql = "DELETE FROM " . $dbPrefix . "user_clicktodial";
2604
+        $sql .= " WHERE fk_user = " . ((int)$this->id);
2605
+
2606
+        dol_syslog(get_class($this) . '::update_clicktodial', LOG_DEBUG);
2607
+        $result = $this->db->query($sql);
2608
+
2609
+        $sql = "INSERT INTO " . $dbPrefix . "user_clicktodial";
2610
+        $sql .= " (fk_user,url,login,pass,poste)";
2611
+        $sql .= " VALUES (" . $this->id;
2612
+        $sql .= ", '" . $this->db->escape($this->clicktodial_url) . "'";
2613
+        $sql .= ", '" . $this->db->escape($this->clicktodial_login) . "'";
2614
+        $sql .= ", '" . $this->db->escape($this->clicktodial_password) . "'";
2615
+        $sql .= ", '" . $this->db->escape($this->clicktodial_poste) . "')";
2616
+
2617
+        dol_syslog(get_class($this) . '::update_clicktodial', LOG_DEBUG);
2618
+        $result = $this->db->query($sql);
2619
+        if ($result) {
2620
+            $this->db->commit();
2621
+            return 1;
2622
+        } else {
2623
+            $this->db->rollback();
2624
+            $this->error = $this->db->lasterror();
2625
+            return -1;
2626
+        }
2627
+    }
2628
+
2629
+    /**
2630
+     *  Add user into a group
2631
+     *
2632
+     * @param int $group Id of group
2633
+     * @param int $entity Entity
2634
+     * @param int $notrigger Disable triggers
2635
+     *
2636
+     * @return int                 Return integer <0 if KO, >0 if OK
2637
+     */
2638
+    public function SetInGroup($group, $entity, $notrigger = 0)
2639
+    {
2640
+        // phpcs:enable
2641
+        global $conf, $langs, $user;
2642
+        global $config;
2643
+        $dbPrefix = $config->db->prefix;
2644
+
2645
+        $error = 0;
2646
+
2647
+        $this->db->begin();
2648
+
2649
+        $sql = "DELETE FROM " . $dbPrefix . "usergroup_user";
2650
+        $sql .= " WHERE fk_user  = " . ((int)$this->id);
2651
+        $sql .= " AND fk_usergroup = " . ((int)$group);
2652
+        $sql .= " AND entity = " . ((int)$entity);
2653
+
2654
+        $result = $this->db->query($sql);
2655
+
2656
+        $sql = "INSERT INTO " . $dbPrefix . "usergroup_user (entity, fk_user, fk_usergroup)";
2657
+        $sql .= " VALUES (" . ((int)$entity) . "," . ((int)$this->id) . "," . ((int)$group) . ")";
2658
+
2659
+        $result = $this->db->query($sql);
2660
+        if ($result) {
2661
+            if (!$error && !$notrigger) {
2662
+                $this->context = ['audit' => $langs->trans("UserSetInGroup"), 'newgroupid' => $group];
2663
+
2664
+                // Call trigger
2665
+                $result = $this->call_trigger('USER_MODIFY', $user);
2666
+                if ($result < 0) {
2667
+                    $error++;
2668
+                }
2669
+                // End call triggers
2670
+            }
2671
+
2672
+            if (!$error) {
2673
+                $this->db->commit();
2674
+                return 1;
2675
+            } else {
2676
+                dol_syslog(get_class($this) . "::SetInGroup " . $this->error, LOG_ERR);
2677
+                $this->db->rollback();
2678
+                return -2;
2679
+            }
2680
+        } else {
2681
+            $this->error = $this->db->lasterror();
2682
+            $this->db->rollback();
2683
+            return -1;
2684
+        }
2685
+    }
2686
+
2687
+    /**
2688
+     *  Remove a user from a group
2689
+     *
2690
+     * @param int $group Id of group
2691
+     * @param int $entity Entity
2692
+     * @param int $notrigger Disable triggers
2693
+     *
2694
+     * @return int                  Return integer <0 if KO, >0 if OK
2695
+     */
2696
+    public function RemoveFromGroup($group, $entity, $notrigger = 0)
2697
+    {
2698
+        // phpcs:enable
2699
+        global $conf, $langs, $user;
2700
+
2701
+        $error = 0;
2702
+
2703
+        $this->db->begin();
2704
+
2705
+        $sql = "DELETE FROM " . $dbPrefix . "usergroup_user";
2706
+        $sql .= " WHERE fk_user  = " . ((int)$this->id);
2707
+        $sql .= " AND fk_usergroup = " . ((int)$group);
2708
+        if (empty($entity)) {
2709
+            $sql .= " AND entity IN (0, 1)";    // group may be in entity 0 (so $entity=0) and link with user into entity 1.
2710
+        } else {
2711
+            $sql .= " AND entity = " . ((int)$entity);
2712
+        }
2713
+
2714
+        $result = $this->db->query($sql);
2715
+        if ($result) {
2716
+            if (!$error && !$notrigger) {
2717
+                $this->context = ['audit' => $langs->trans("UserRemovedFromGroup"), 'oldgroupid' => $group];
2718
+
2719
+                // Call trigger
2720
+                $result = $this->call_trigger('USER_MODIFY', $user);
2721
+                if ($result < 0) {
2722
+                    $error++;
2723
+                }
2724
+                // End call triggers
2725
+            }
2726
+
2727
+            if (!$error) {
2728
+                $this->db->commit();
2729
+                return 1;
2730
+            } else {
2731
+                dol_syslog(get_class($this) . "::RemoveFromGroup " . $this->error, LOG_ERR);
2732
+                $this->db->rollback();
2733
+                return -2;
2734
+            }
2735
+        } else {
2736
+            $this->error = $this->db->lasterror();
2737
+            $this->db->rollback();
2738
+            return -1;
2739
+        }
2740
+    }
2741
+
2742
+    /**
2743
+     *  Return a link with photo
2744
+     *  Use this->id,this->photo
2745
+     *
2746
+     * @return int     0=Valid, >0 if not valid
2747
+     */
2748
+    public function isNotIntoValidityDateRange()
2749
+    {
2750
+        include_once BASE_PATH . '/../Dolibarr/Lib/Date.php';
2751
+
2752
+        $now = dol_now();
2753
+
2754
+        //dol_syslog("isNotIntoValidityDateRange ".$this->datestartvalidity);
2755
+
2756
+        // Check date start validity
2757
+        if ($this->datestartvalidity && $this->datestartvalidity > dol_get_last_hour($now)) {
2758
+            return 1;
2759
+        }
2760
+        // Check date end validity
2761
+        if ($this->dateendvalidity && $this->dateendvalidity < dol_get_first_hour($now)) {
2762
+            return 1;
2763
+        }
2764
+
2765
+        return 0;
2766
+    }
2767
+
2768
+    /**
2769
+     *  Return a link with photo
2770
+     *  Use this->id,this->photo
2771
+     *
2772
+     * @param int $width Width of image
2773
+     * @param int $height Height of image
2774
+     * @param string $cssclass Force a css class
2775
+     * @param string $imagesize 'mini', 'small' or '' (original)
2776
+     *
2777
+     * @return string                  String with URL link
2778
+     * @see getImagePublicURLOfObject()
2779
+     */
2780
+    public function getPhotoUrl($width, $height, $cssclass = '', $imagesize = '')
2781
+    {
2782
+        $result = '<a href="' . DOL_URL_ROOT . '/user/card.php?id=' . $this->id . '">';
2783
+        $result .= Form::showphoto('userphoto', $this, $width, $height, 0, $cssclass, $imagesize);
2784
+        $result .= '</a>';
2785
+
2786
+        return $result;
2787
+    }
2788
+
2789
+    /**
2790
+     * Return array of data to show into tooltips
2791
+     *
2792
+     * @param array $params Array with options, infologin
2793
+     *
2794
+     * @return array
2795
+     * @since v18
2796
+     */
2797
+    public function getTooltipContentArray($params)
2798
+    {
2799
+        global $conf, $langs, $menumanager;
2800
+        global $dolibarr_main_demo;
2801
+
2802
+        $infologin = $params['infologin'] ?? 0;
2803
+        $option = $params['option'] ?? '';
2804
+
2805
+        $data = [];
2806
+        if (!empty($this->photo)) {
2807
+            $photo = '<div class="photointooltip floatright">';
2808
+            $photo .= Form::showphoto('userphoto', $this, 0, 60, 0, 'photoref photowithmargin photologintooltip', 'small', 0, 1); // Force height to 60 so we total height of tooltip can be calculated and collision can be managed
2809
+            $photo .= '</div>';
2810
+            $data['photo'] = $photo;
2811
+            //$label .= '<div style="clear: both;"></div>';
2812
+        }
2813
+
2814
+        // Info Login
2815
+        $data['opendiv'] = '<div class="centpercent divtooltip">';
2816
+        $data['picto'] = img_picto('', $this->picto) . ' <u class="paddingrightonly">' . $langs->trans("User") . '</u> ' . $this->getLibStatut(4);
2817
+        $data['name'] = '<br><b>' . $langs->trans('Name') . ':</b> ' . dol_string_nohtmltag($this->getFullName($langs, ''));
2818
+        if (!empty($this->login)) {
2819
+            $data['login'] = '<br><b>' . $langs->trans('Login') . ':</b> ' . dol_string_nohtmltag($this->login);
2820
+        }
2821
+        if (!empty($this->job)) {
2822
+            $data['job'] = '<br><b>' . $langs->trans("Job") . ':</b> ' . dol_string_nohtmltag($this->job);
2823
+        }
2824
+        $data['email'] = '<br><b>' . $langs->trans("Email") . ':</b> ' . dol_string_nohtmltag($this->email);
2825
+        if (!empty($this->office_phone) || !empty($this->office_fax) || !empty($this->fax)) {
2826
+            $phonelist = [];
2827
+            if ($this->office_phone) {
2828
+                $phonelist[] = dol_print_phone($this->office_phone, $this->country_code, $this->id, 0, '', '&nbsp', 'phone');
2829
+            }
2830
+            if ($this->office_fax) {
2831
+                $phonelist[] = dol_print_phone($this->office_fax, $this->country_code, $this->id, 0, '', '&nbsp', 'fax');
2832
+            }
2833
+            if ($this->user_mobile) {
2834
+                $phonelist[] = dol_print_phone($this->user_mobile, $this->country_code, $this->id, 0, '', '&nbsp', 'mobile');
2835
+            }
2836
+            $data['phones'] = '<br><b>' . $langs->trans('Phone') . ':</b> ' . implode('&nbsp;', $phonelist);
2837
+        }
2838
+        if (!empty($this->admin)) {
2839
+            $data['administrator'] = '<br><b>' . $langs->trans("Administrator") . '</b>: ' . yn($this->admin);
2840
+        }
2841
+        if (!empty($this->accountancy_code) || $option == 'accountancy') {
2842
+            $langs->load("companies");
2843
+            $data['accountancycode'] = '<br><b>' . $langs->trans("AccountancyCode") . '</b>: ' . $this->accountancy_code;
2844
+        }
2845
+        $company = '';
2846
+        if (!empty($this->socid)) { // Add thirdparty for external users
2847
+            $thirdpartystatic = new Societe($this->db);
2848
+            $thirdpartystatic->fetch($this->socid);
2849
+            $companyimg = '';
2850
+            if (empty($params['hidethirdpartylogo'])) {
2851
+                $companyimg = ' ' . $thirdpartystatic->getNomUrl(2, (($option == 'nolink') ? 'nolink' : '')); // picto only of company
2852
+            }
2853
+            $company = ' (' . $langs->trans("Company") . ': ' . ($companyimg ? $companyimg : img_picto('', 'company')) . ' ' . dol_string_nohtmltag($thirdpartystatic->name) . ')';
2854
+        }
2855
+        $type = ($this->socid ? $langs->trans("ExternalUser") . $company : $langs->trans("InternalUser"));
2856
+        $data['type'] = '<br><b>' . $langs->trans("Type") . ':</b> ' . $type;
2857
+        $data['closediv'] = '</div>';
2858
+
2859
+        if ($infologin > 0) {
2860
+            $data['newlinelogin'] = '<br>';
2861
+            $data['session'] = '<br><u>' . $langs->trans("Session") . '</u>';
2862
+            $data['ip'] = '<br><b>' . $langs->trans("IPAddress") . '</b>: ' . dol_string_nohtmltag(getUserRemoteIP());
2863
+            if (getDolGlobalString('MAIN_MODULE_MULTICOMPANY')) {
2864
+                $data['multicompany'] = '<br><b>' . $langs->trans("ConnectedOnMultiCompany") . ':</b> ' . $conf->entity . ' (User entity ' . $this->entity . ')';
2865
+            }
2866
+            $data['authentication'] = '<br><b>' . $langs->trans("AuthenticationMode") . ':</b> ' . dol_string_nohtmltag($_SESSION["dol_authmode"] . (empty($dolibarr_main_demo) ? '' : ' (demo)'));
2867
+            $data['connectedsince'] = '<br><b>' . $langs->trans("ConnectedSince") . ':</b> ' . dol_print_date($this->datelastlogin, "dayhour", 'tzuser');
2868
+            $data['previousconnexion'] = '<br><b>' . $langs->trans("PreviousConnexion") . ':</b> ' . dol_print_date($this->datepreviouslogin, "dayhour", 'tzuser');
2869
+            $data['currenttheme'] = '<br><b>' . $langs->trans("CurrentTheme") . ':</b> ' . dol_string_nohtmltag($conf->theme);
2870
+            $data['currentmenumanager'] = '<br><b>' . $langs->trans("CurrentMenuManager") . ':</b> ' . dol_string_nohtmltag($menumanager->name);
2871
+            $s = picto_from_langcode($langs->getDefaultLang());
2872
+            $data['currentuserlang'] = '<br><b>' . $langs->trans("CurrentUserLanguage") . ':</b> ' . dol_string_nohtmltag(($s ? $s . ' ' : '') . $langs->getDefaultLang());
2873
+            $data['browser'] = '<br><b>' . $langs->trans("Browser") . ':</b> ' . dol_string_nohtmltag($conf->browser->name . ($conf->browser->version ? ' ' . $conf->browser->version : '') . ' (' . $_SERVER['HTTP_USER_AGENT'] . ')');
2874
+            $data['layout'] = '<br><b>' . $langs->trans("Layout") . ':</b> ' . dol_string_nohtmltag($conf->browser->layout);
2875
+            $data['screen'] = '<br><b>' . $langs->trans("Screen") . ':</b> ' . dol_string_nohtmltag($_SESSION['dol_screenwidth'] . ' x ' . $_SESSION['dol_screenheight']);
2876
+            if ($conf->browser->layout == 'phone') {
2877
+                $data['phone'] = '<br><b>' . $langs->trans("Phone") . ':</b> ' . $langs->trans("Yes");
2878
+            }
2879
+            if (!empty($_SESSION["disablemodules"])) {
2880
+                $data['disabledmodules'] = '<br><b>' . $langs->trans("DisabledModules") . ':</b> <br>' . dol_string_nohtmltag(implode(', ', explode(',', $_SESSION["disablemodules"])));
2881
+            }
2882
+        }
2883
+
2884
+        return $data;
2885
+    }
2886
+
2887
+    /**
2888
+     *  Return the label of the status of user (active, inactive)
2889
+     *
2890
+     * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short
2891
+     *                  label + Picto, 6=Long label + Picto
2892
+     *
2893
+     * @return string                 Label of status
2894
+     */
2895
+    public function getLibStatut($mode = 0)
2896
+    {
2897
+        return $this->LibStatut(isset($this->statut) ? (int)$this->statut : (int)$this->status, $mode);
2898
+    }
2899
+
2900
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2901
+
2902
+    /**
2903
+     *  Return the label of a status of user (active, inactive)
2904
+     *
2905
+     * @param int $status Id status
2906
+     * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short
2907
+     *                    label + Picto, 6=Long label + Picto
2908
+     *
2909
+     * @return string                  Label of status
2910
+     */
2911
+    public function LibStatut($status, $mode = 0)
2912
+    {
2913
+        // phpcs:enable
2914
+        global $langs;
2915
+
2916
+        if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
2917
+            global $langs;
2918
+            //$langs->load("mymodule");
2919
+            $this->labelStatus[self::STATUS_ENABLED] = $langs->transnoentitiesnoconv('Enabled');
2920
+            $this->labelStatus[self::STATUS_DISABLED] = $langs->transnoentitiesnoconv('Disabled');
2921
+            $this->labelStatusShort[self::STATUS_ENABLED] = $langs->transnoentitiesnoconv('Enabled');
2922
+            $this->labelStatusShort[self::STATUS_DISABLED] = $langs->transnoentitiesnoconv('Disabled');
2923
+        }
2924
+
2925
+        $statusType = 'status5';
2926
+        if ($status == self::STATUS_ENABLED) {
2927
+            $statusType = 'status4';
2928
+        }
2929
+
2930
+        $label = $this->labelStatus[$status];
2931
+        $labelshort = $this->labelStatusShort[$status];
2932
+
2933
+        $now = dol_now();
2934
+        if (!empty($this->datestartvalidity) && $now < $this->datestartvalidity) {
2935
+            $statusType = 'status3';
2936
+            $label .= ' (' . $langs->trans("UserNotYetValid") . ')';
2937
+        }
2938
+        if (!empty($this->dateendvalidity) && $now > ($this->dateendvalidity + 24 * 3600 - 1)) {
2939
+            $statusType = 'status2';
2940
+            $label .= ' (' . $langs->trans("UserExpired") . ')';
2941
+        }
2942
+
2943
+        return dolGetStatus($label, $labelshort, '', $statusType, $mode);
2944
+    }
2945
+
2946
+    /**
2947
+     *  Return a HTML link to the user card (with optionally the picto)
2948
+     *  Use this->id,this->lastname, this->firstname
2949
+     *
2950
+     * @param int $withpictoimg Include picto in link (0=No picto, 1=Include picto into link, 2=Only
2951
+     *                                       picto, -1=Include photo into link, -2=Only picto photo, -3=Only photo very
2952
+     *                                       small)
2953
+     * @param string $option On what the link point to ('leave', 'accountancy', 'nolink', )
2954
+     * @param integer $infologin 0=Add default info tooltip, 1=Add complete info tooltip, -1=No info
2955
+     *                                       tooltip
2956
+     * @param integer $notooltip 1=Disable tooltip on picto and name
2957
+     * @param int $maxlen Max length of visible user name
2958
+     * @param int $hidethirdpartylogo Hide logo of thirdparty if user is external user
2959
+     * @param string $mode ''=Show firstname and lastname, 'firstname'=Show only firstname,
2960
+     *                                       'firstelselast'=Show firstname or lastname if not defined, 'login'=Show
2961
+     *                                       login
2962
+     * @param string $morecss Add more css on link
2963
+     * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save
2964
+     *                                       lastsearch_values whenclicking
2965
+     *
2966
+     * @return string                              String with URL
2967
+     */
2968
+    public function getNomUrl($withpictoimg = 0, $option = '', $infologin = 0, $notooltip = 0, $maxlen = 24, $hidethirdpartylogo = 0, $mode = '', $morecss = '', $save_lastsearch_value = -1)
2969
+    {
2970
+        global $langs, $conf, $db, $hookmanager, $user;
2971
+        global $dolibarr_main_authentication, $dolibarr_main_demo;
2972
+
2973
+        if (!$user->hasRight('user', 'user', 'read') && $user->id != $this->id) {
2974
+            $option = 'nolink';
2975
+        }
2976
+
2977
+        if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpictoimg) {
2978
+            $withpictoimg = 0;
2979
+        }
2980
+
2981
+        $result = '';
2982
+        $params = [
2983
+            'id' => $this->id,
2984
+            'objecttype' => $this->element,
2985
+            'infologin' => $infologin,
2986
+            'option' => $option,
2987
+            'hidethirdpartylogo' => $hidethirdpartylogo,
2988
+        ];
2989
+        $classfortooltip = 'classfortooltip';
2990
+        $dataparams = '';
2991
+        if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
2992
+            $classfortooltip = 'classforajaxtooltip';
2993
+            $dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
2994
+            $label = '';
2995
+        } else {
2996
+            $label = implode($this->getTooltipContentArray($params));
2997
+        }
2998
+
2999
+        $companylink = '';
3000
+        if (!empty($this->socid)) { // Add thirdparty for external users
3001
+            $thirdpartystatic = new Societe($this->db);
3002
+            $thirdpartystatic->fetch($this->socid);
3003
+            if (empty($hidethirdpartylogo)) {
3004
+                $companylink = ' ' . $thirdpartystatic->getNomUrl(2, (($option == 'nolink') ? 'nolink' : '')); // picto only of company
3005
+            }
3006
+        }
3007
+
3008
+        if ($infologin < 0) {
3009
+            $label = '';
3010
+        }
3011
+
3012
+        $url = DOL_URL_ROOT . '/user/card.php?id=' . $this->id;
3013
+        if ($option == 'leave') {
3014
+            $url = DOL_URL_ROOT . '/holiday/list.php?id=' . $this->id;
3015
+        }
3016
+
3017
+        if ($option != 'nolink') {
3018
+            // Add param to save lastsearch_values or not
3019
+            $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3020
+            if ($save_lastsearch_value == -1 && isset($_SERVER['PHP_SELF']) && preg_match('/list\.php/', $_SERVER['PHP_SELF'])) {
3021
+                $add_save_lastsearch_values = 1;
3022
+            }
3023
+            if ($add_save_lastsearch_values) {
3024
+                $url .= '&save_lastsearch_values=1';
3025
+            }
3026
+        }
3027
+
3028
+        $linkstart = '<a href="' . $url . '"';
3029
+        $linkclose = "";
3030
+        if (empty($notooltip)) {
3031
+            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3032
+                $langs->load("users");
3033
+                $label = $langs->trans("ShowUser");
3034
+                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
3035
+            }
3036
+            $linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
3037
+            $linkclose .= $dataparams . ' class="' . $classfortooltip . ($morecss ? ' ' . $morecss : '') . '"';
3038
+        } else {
3039
+            $linkclose = ($morecss ? ' class="' . $morecss . '"' : '');
3040
+        }
3041
+
3042
+        $linkstart .= $linkclose . '>';
3043
+        $linkend = '</a>';
3044
+
3045
+        //if ($withpictoimg == -1) $result.='<div class="nowrap">';
3046
+        $result .= (($option == 'nolink') ? '' : $linkstart);
3047
+        if ($withpictoimg) {
3048
+            $paddafterimage = '';
3049
+            if (abs((int)$withpictoimg) == 1) {
3050
+                $paddafterimage = 'style="margin-' . ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right') . ': 3px;"';
3051
+            }
3052
+            // Only picto
3053
+            if ($withpictoimg > 0) {
3054
+                $picto = '<!-- picto user --><span class="nopadding userimg' . ($morecss ? ' ' . $morecss : '') . '"><div class="valignmiddle userphoto inline-block center marginrightonlyshort"' . ($paddafterimage ? ' ' . $paddafterimage : '') . '>' . img_object('', 'user', 'class=""', 0, 0, $notooltip ? 0 : 1) . '</div></span>';
3055
+            } else {
3056
+                // Picto must be a photo
3057
+                $picto = '<!-- picto photo user --><span class="nopadding userimg' . ($morecss ? ' ' . $morecss : '') . '"' . ($paddafterimage ? ' ' . $paddafterimage : '') . '>' . Form::showphoto('userphoto', $this, 0, 0, 0, 'userphoto' . ($withpictoimg == -3 ? 'small' : ''), 'mini', 0, 1) . '</span>';
3058
+            }
3059
+            $result .= $picto;
3060
+        }
3061
+        if ($withpictoimg > -2 && $withpictoimg != 2) {
3062
+            if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3063
+                $result .= '<span class="nopadding usertext' . ((!isset($this->status) || $this->status) ? '' : ' strikefordisabled') . ($morecss ? ' ' . $morecss : '') . '">';
3064
+            }
3065
+            if ($mode == 'login') {
3066
+                $result .= dol_string_nohtmltag(dol_trunc($this->login, $maxlen));
3067
+            } else {
3068
+                $result .= dol_string_nohtmltag($this->getFullName($langs, '', ($mode == 'firstelselast' ? 3 : ($mode == 'firstname' ? 2 : -1)), $maxlen));
3069
+            }
3070
+            if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3071
+                $result .= '</span>';
3072
+            }
3073
+        }
3074
+        $result .= (($option == 'nolink') ? '' : $linkend);
3075
+        //if ($withpictoimg == -1) $result.='</div>';
3076
+
3077
+        $result .= $companylink;
3078
+
3079
+        global $action;
3080
+        $hookmanager->initHooks(['userdao']);
3081
+        $parameters = ['id' => $this->id, 'getnomurl' => &$result];
3082
+        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3083
+        if ($reshook > 0) {
3084
+            $result = $hookmanager->resPrint;
3085
+        } else {
3086
+            $result .= $hookmanager->resPrint;
3087
+        }
3088
+
3089
+        return $result;
3090
+    }
3091
+
3092
+
3093
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
3094
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3095
+
3096
+    /**
3097
+     *  Return if a user has a permission.
3098
+     *  You can use it like this: if ($user->hasRight('module', 'level11')).
3099
+     *  It replaces old syntax: if ($user->rights->module->level1)
3100
+     *
3101
+     * @param string $module Module of permission to check
3102
+     * @param string $permlevel1 Permission level1 (Example: 'read', 'write', 'delete')
3103
+     * @param string $permlevel2 Permission level2
3104
+     *
3105
+     * @return int                     1 if user has permission, 0 if not.
3106
+     * @see    clearrights(), delrights(), getrights(), hasRight()
3107
+     */
3108
+    public function hasRight($module, $permlevel1, $permlevel2 = '')
3109
+    {
3110
+        // For compatibility with bad naming permissions on module
3111
+        $moduletomoduletouse = [
3112
+            'compta' => 'comptabilite',
3113
+            'contract' => 'contrat',
3114
+            'member' => 'adherent',
3115
+            'mo' => 'mrp',
3116
+            'order' => 'commande',
3117
+            'produit' => 'product',
3118
+            'project' => 'projet',
3119
+            'propale' => 'propal',
3120
+            'shipping' => 'expedition',
3121
+            'task' => 'task@projet',
3122
+            'fichinter' => 'ficheinter',
3123
+            'inventory' => 'stock',
3124
+            'invoice' => 'facture',
3125
+            'invoice_supplier' => 'fournisseur',
3126
+            'order_supplier' => 'fournisseur',
3127
+            'knowledgerecord' => 'knowledgerecord@knowledgemanagement',
3128
+            'skill@hrm' => 'all@hrm', // skill / job / position objects rights are for the moment grouped into right level "all"
3129
+            'job@hrm' => 'all@hrm', // skill / job / position objects rights are for the moment grouped into right level "all"
3130
+            'position@hrm' => 'all@hrm', // skill / job / position objects rights are for the moment grouped into right level "all"
3131
+            'facturerec' => 'facture',
3132
+            'margins' => 'margin',
3133
+        ];
3134
+
3135
+        if (!empty($moduletomoduletouse[$module])) {
3136
+            $module = $moduletomoduletouse[$module];
3137
+        }
3138
+
3139
+        $moduleRightsMapping = [
3140
+            'product' => 'produit',
3141
+            'margin' => 'margins',
3142
+            'comptabilite' => 'compta',
3143
+        ];
3144
+
3145
+        $rightsPath = $module;
3146
+        if (!empty($moduleRightsMapping[$rightsPath])) {
3147
+            $rightsPath = $moduleRightsMapping[$rightsPath];
3148
+        }
3149
+
3150
+        // If module is abc@module, we check permission user->hasRight(module, abc, permlevel1)
3151
+        $tmp = explode('@', $rightsPath, 2);
3152
+        if (!empty($tmp[1])) {
3153
+            if (strpos($module, '@') !== false) {
3154
+                $module = $tmp[1];
3155
+            }
3156
+            if ($tmp[0] != $tmp[1]) {
3157
+                // If $module = 'myobject@mymodule'
3158
+                $rightsPath = $tmp[1];
3159
+                $permlevel2 = $permlevel1;
3160
+                $permlevel1 = $tmp[0];
3161
+            } else {
3162
+                // If $module = 'abc@abc'
3163
+                $rightsPath = $tmp[1];
3164
+            }
3165
+        }
3166
+
3167
+        // In $conf->modules, we have 'accounting', 'product', 'facture', ...
3168
+        // In $user->rights, we have 'accounting', 'produit', 'facture', ...
3169
+        //var_dump($this->rights->$rightsPath);
3170
+        //var_dump($conf->modules);
3171
+        //var_dump($module.' '.isModEnabled($module).' '.$rightsPath.' '.$permlevel1.' '.$permlevel2);
3172
+        if (!isModEnabled($module)) {
3173
+            return 0;
3174
+        }
3175
+
3176
+        // Special case for external user
3177
+        if (!empty($this->socid)) {
3178
+            if ($module = 'societe' && $permlevel1 = 'client' && $permlevel2 == 'voir') {
3179
+                return 0;   // An external user never has the permission ->societe->client->voir to see all thirdparties (always restricted to himself)
3180
+            }
3181
+        }
3182
+
3183
+        // For compatibility with bad naming permissions on permlevel1
3184
+        if ($permlevel1 == 'propale') {
3185
+            $permlevel1 = 'propal';
3186
+        }
3187
+        if ($permlevel1 == 'member') {
3188
+            $permlevel1 = 'adherent';
3189
+        }
3190
+        if ($permlevel1 == 'recruitmentcandidature') {
3191
+            $permlevel1 = 'recruitmentjobposition';
3192
+        }
3193
+
3194
+        //var_dump($this->rights);
3195
+        //var_dump($rightsPath.' '.$permlevel1.' '.$permlevel2);
3196
+        if (empty($rightsPath) || empty($this->rights) || empty($this->rights->$rightsPath) || empty($permlevel1)) {
3197
+            return 0;
3198
+        }
3199
+
3200
+        if ($permlevel2) {
3201
+            if (!empty($this->rights->$rightsPath->$permlevel1)) {
3202
+                if (!empty($this->rights->$rightsPath->$permlevel1->$permlevel2)) {
3203
+                    return $this->rights->$rightsPath->$permlevel1->$permlevel2;
3204
+                }
3205
+                // For backward compatibility with old permissions called "lire", "creer", "create", "supprimer"
3206
+                // instead of "read", "write", "delete"
3207
+                if ($permlevel2 == 'read' && !empty($this->rights->$rightsPath->$permlevel1->lire)) {
3208
+                    return $this->rights->$rightsPath->$permlevel1->lire;
3209
+                }
3210
+                if ($permlevel2 == 'write' && !empty($this->rights->$rightsPath->$permlevel1->creer)) {
3211
+                    return $this->rights->$rightsPath->$permlevel1->creer;
3212
+                }
3213
+                if ($permlevel2 == 'write' && !empty($this->rights->$rightsPath->$permlevel1->create)) {
3214
+                    return $this->rights->$rightsPath->$permlevel1->create;
3215
+                }
3216
+                if ($permlevel2 == 'delete' && !empty($this->rights->$rightsPath->$permlevel1->supprimer)) {
3217
+                    return $this->rights->$rightsPath->$permlevel1->supprimer;
3218
+                }
3219
+            }
3220
+        } else {
3221
+            if (!empty($this->rights->$rightsPath->$permlevel1)) {
3222
+                return $this->rights->$rightsPath->$permlevel1;
3223
+            }
3224
+            // For backward compatibility with old permissions called "lire", "creer", "create", "supprimer"
3225
+            // instead of "read", "write", "delete"
3226
+            if ($permlevel1 == 'read' && !empty($this->rights->$rightsPath->lire)) {
3227
+                return $this->rights->$rightsPath->lire;
3228
+            }
3229
+            if ($permlevel1 == 'write' && !empty($this->rights->$rightsPath->creer)) {
3230
+                return $this->rights->$rightsPath->creer;
3231
+            }
3232
+            if ($permlevel1 == 'write' && !empty($this->rights->$rightsPath->create)) {
3233
+                return $this->rights->$rightsPath->create;
3234
+            }
3235
+            if ($permlevel1 == 'delete' && !empty($this->rights->$rightsPath->supprimer)) {
3236
+                return $this->rights->$rightsPath->supprimer;
3237
+            }
3238
+        }
3239
+
3240
+        return 0;
3241
+    }
3242
+
3243
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
3244
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3245
+
3246
+    /**
3247
+     *  Return clickable link of login (optionally with picto)
3248
+     *
3249
+     * @param int $withpictoimg Include picto into link (1=picto, -1=photo)
3250
+     * @param string $option On what the link point to ('leave', 'accountancy', 'nolink', )
3251
+     * @param integer $notooltip 1=Disable tooltip on picto and name
3252
+     * @param string $morecss Add more css on link
3253
+     *
3254
+     * @return string                      String with URL
3255
+     */
3256
+    public function getLoginUrl($withpictoimg = 0, $option = '', $notooltip = 0, $morecss = '')
3257
+    {
3258
+        global $langs, $user;
3259
+
3260
+        $result = '';
3261
+
3262
+        $linkstart = '<a href="' . DOL_URL_ROOT . '/user/card.php?id=' . $this->id . '">';
3263
+        $linkend = '</a>';
3264
+
3265
+        //Check user's rights to see an other user
3266
+        if ((!$user->hasRight('user', 'user', 'lire') && $this->id != $user->id)) {
3267
+            $option = 'nolink';
3268
+        }
3269
+
3270
+        if ($option == 'xxx') {
3271
+            $linkstart = '<a href="' . DOL_URL_ROOT . '/user/card.php?id=' . $this->id . '">';
3272
+            $linkend = '</a>';  // @phan-suppress-current-line PhanPluginRedundantAssignment
3273
+        }
3274
+
3275
+        if ($option == 'nolink') {
3276
+            $linkstart = '';
3277
+            $linkend = '';
3278
+        }
3279
+
3280
+        $result .= $linkstart;
3281
+        if ($withpictoimg) {
3282
+            $paddafterimage = '';
3283
+            if (abs($withpictoimg) == 1) {
3284
+                $paddafterimage = 'style="margin-' . ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right') . ': 3px;"';
3285
+            }
3286
+            // Only picto
3287
+            if ($withpictoimg > 0) {
3288
+                $picto = '<!-- picto user --><span class="nopadding userimg' . ($morecss ? ' ' . $morecss : '') . '">' . img_object('', 'user', $paddafterimage . ' ' . ($notooltip ? '' : 'class="paddingright classfortooltip"'), 0, 0, $notooltip ? 0 : 1) . '</span>';
3289
+            } else {
3290
+                // Picto must be a photo
3291
+                $picto = '<!-- picto photo user --><span class="nopadding userimg' . ($morecss ? ' ' . $morecss : '') . '"' . ($paddafterimage ? ' ' . $paddafterimage : '') . '>' . Form::showphoto('userphoto', $this, 0, 0, 0, 'userphoto' . ($withpictoimg == -3 ? 'small' : ''), 'mini', 0, 1) . '</span>';
3292
+            }
3293
+            $result .= $picto;
3294
+        }
3295
+        $result .= $this->login;
3296
+        $result .= $linkend;
3297
+
3298
+        return $result;
3299
+    }
3300
+
3301
+    /**
3302
+     *  Return clicable link of object (optionally with picto)
3303
+     *
3304
+     * @param string $option Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
3305
+     * @param array $arraydata Array of data
3306
+     *
3307
+     * @return     string                              HTML Code for Kanban thumb.
3308
+     */
3309
+    public function getKanbanView($option = '', $arraydata = null)
3310
+    {
3311
+        global $langs;
3312
+
3313
+        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3314
+
3315
+        $return = '<div class="box-flex-item box-flex-grow-zero">';
3316
+        $return .= '<div class="info-box info-box-sm">';
3317
+        $return .= '<span class="info-box-icon bg-infobox-action">';
3318
+
3319
+        $label = '';
3320
+        if (!empty($this->photo)) {
3321
+            //$label .= '<div class="photointooltip floatright">';
3322
+            $label .= Form::showphoto('userphoto', $this, 0, 60, 0, 'photokanban photoref photowithmargin photologintooltip', 'small', 0, 1); // Force height to 60 so we total height of tooltip can be calculated and collision can be managed
3323
+            //$label .= '</div>';
3324
+            //$label .= '<div style="clear: both;"></div>';
3325
+            $return .= $label;
3326
+        } else {
3327
+            $return .= img_picto('', $this->picto);
3328
+        }
3329
+
3330
+        //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
3331
+        $return .= '</span>';
3332
+        $return .= '<div class="info-box-content">';
3333
+        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl(0, '', 0, 0, 24, 0, '', 'valignmiddle') : $this->ref);
3334
+        if (isModEnabled('multicompany') && $this->admin && !$this->entity) {
3335
+            $return .= img_picto($langs->trans("SuperAdministratorDesc"), 'redstar', 'class="valignmiddle paddingright paddingleft"');
3336
+        } elseif ($this->admin) {
3337
+            $return .= img_picto($langs->trans("AdministratorDesc"), 'star', 'class="valignmiddle paddingright paddingleft"');
3338
+        }
3339
+        $return .= '</span>';
3340
+        if ($selected >= 0) {
3341
+            $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
3342
+        }
3343
+        if (property_exists($this, 'label')) {
3344
+            $return .= '<br><span class="info-box-label opacitymedium">' . $this->label . '</span>';
3345
+        }
3346
+        if ($this->email) {
3347
+            $return .= '<br><span class="info-box-label opacitymedium small">' . img_picto('', 'email') . ' ' . $this->email . '</span>';
3348
+        }
3349
+        if (method_exists($this, 'getLibStatut')) {
3350
+            $return .= '<br><div class="info-box-status">' . $this->getLibStatut(3) . '</div>';
3351
+        }
3352
+        $return .= '</div>';
3353
+        $return .= '</div>';
3354
+        $return .= '</div>';
3355
+
3356
+        return $return;
3357
+    }
3358
+
3359
+    /**
3360
+     *  Retourne chaine DN complete dans l'annuaire LDAP pour l'objet
3361
+     *
3362
+     * @param array $info Info array loaded by _load_ldap_info
3363
+     * @param int $mode 0=Return full DN (uid=qqq,ou=xxx,dc=aaa,dc=bbb)
3364
+     *                              1=Return parent (ou=xxx,dc=aaa,dc=bbb)
3365
+     *                              2=Return key only (RDN) (uid=qqq)
3366
+     *
3367
+     * @return string              DN
3368
+     */
3369
+    public function _load_ldap_dn($info, $mode = 0)
3370
+    {
3371
+        // phpcs:enable
3372
+        global $conf;
3373
+        $dn = '';
3374
+        if ($mode == 0) {
3375
+            $dn = getDolGlobalString('LDAP_KEY_USERS') . "=" . $info[getDolGlobalString('LDAP_KEY_USERS')] . "," . getDolGlobalString('LDAP_USER_DN');
3376
+        } elseif ($mode == 1) {
3377
+            $dn = getDolGlobalString('LDAP_USER_DN');
3378
+        } elseif ($mode == 2) {
3379
+            $dn = getDolGlobalString('LDAP_KEY_USERS') . "=" . $info[getDolGlobalString('LDAP_KEY_USERS')];
3380
+        }
3381
+        return $dn;
3382
+    }
3383
+
3384
+    /**
3385
+     *  Initialize the info array (array of LDAP values) that will be used to call LDAP functions
3386
+     *
3387
+     * @return     array       Table with attribute information
3388
+     */
3389
+    public function _load_ldap_info()
3390
+    {
3391
+        // phpcs:enable
3392
+        global $conf, $langs;
3393
+
3394
+        $info = [];
3395
+
3396
+        $socialnetworks = getArrayOfSocialNetworks();
3397
+
3398
+        $keymodified = false;
3399
+
3400
+        // Object classes
3401
+        $info["objectclass"] = explode(',', getDolGlobalString('LDAP_USER_OBJECT_CLASS'));
3402
+
3403
+        $this->fullname = $this->getFullName($langs);
3404
+
3405
+        // Possible LDAP KEY (constname => varname)
3406
+        $ldapkey = [
3407
+            'LDAP_FIELD_FULLNAME' => 'fullname',
3408
+            'LDAP_FIELD_NAME' => 'lastname',
3409
+            'LDAP_FIELD_FIRSTNAME' => 'firstname',
3410
+            'LDAP_FIELD_LOGIN' => 'login',
3411
+            'LDAP_FIELD_LOGIN_SAMBA' => 'login',
3412
+            'LDAP_FIELD_PHONE' => 'office_phone',
3413
+            'LDAP_FIELD_MOBILE' => 'user_mobile',
3414
+            'LDAP_FIELD_FAX' => 'office_fax',
3415
+            'LDAP_FIELD_MAIL' => 'email',
3416
+            'LDAP_FIELD_SID' => 'ldap_sid',
3417
+        ];
3418
+
3419
+        // Champs
3420
+        foreach ($ldapkey as $constname => $varname) {
3421
+            if (!empty($this->$varname) && getDolGlobalString($constname)) {
3422
+                $info[getDolGlobalString($constname)] = $this->$varname;
3423
+
3424
+                // Check if it is the LDAP key and if its value has been changed
3425
+                if (getDolGlobalString('LDAP_KEY_USERS') && $conf->global->LDAP_KEY_USERS == getDolGlobalString($constname)) {
3426
+                    if (!empty($this->oldcopy) && $this->$varname != $this->oldcopy->$varname) {
3427
+                        $keymodified = true; // For check if LDAP key has been modified
3428
+                    }
3429
+                }
3430
+            }
3431
+        }
3432
+        foreach ($socialnetworks as $key => $value) {
3433
+            if (!empty($this->socialnetworks[$value['label']]) && getDolGlobalString('LDAP_FIELD_' . strtoupper($value['label']))) {
3434
+                $info[getDolGlobalString('LDAP_FIELD_' . strtoupper($value['label']))] = $this->socialnetworks[$value['label']];
3435
+            }
3436
+        }
3437
+        if ($this->address && getDolGlobalString('LDAP_FIELD_ADDRESS')) {
3438
+            $info[getDolGlobalString('LDAP_FIELD_ADDRESS')] = $this->address;
3439
+        }
3440
+        if ($this->zip && getDolGlobalString('LDAP_FIELD_ZIP')) {
3441
+            $info[getDolGlobalString('LDAP_FIELD_ZIP')] = $this->zip;
3442
+        }
3443
+        if ($this->town && getDolGlobalString('LDAP_FIELD_TOWN')) {
3444
+            $info[getDolGlobalString('LDAP_FIELD_TOWN')] = $this->town;
3445
+        }
3446
+        if ($this->note_public && getDolGlobalString('LDAP_FIELD_DESCRIPTION')) {
3447
+            $info[getDolGlobalString('LDAP_FIELD_DESCRIPTION')] = dol_string_nohtmltag($this->note_public, 2);
3448
+        }
3449
+        if ($this->socid > 0) {
3450
+            $soc = new Societe($this->db);
3451
+            $soc->fetch($this->socid);
3452
+
3453
+            $info[getDolGlobalString('LDAP_FIELD_COMPANY')] = $soc->name;
3454
+            if ($soc->client == 1) {
3455
+                $info["businessCategory"] = "Customers";
3456
+            }
3457
+            if ($soc->client == 2) {
3458
+                $info["businessCategory"] = "Prospects";
3459
+            }
3460
+            if ($soc->fournisseur == 1) {
3461
+                $info["businessCategory"] = "Suppliers";
3462
+            }
3463
+        }
3464
+
3465
+        // When password is modified
3466
+        if (!empty($this->pass)) {
3467
+            if (getDolGlobalString('LDAP_FIELD_PASSWORD')) {
3468
+                $info[getDolGlobalString('LDAP_FIELD_PASSWORD')] = $this->pass; // this->pass = unencrypted password
3469
+            }
3470
+            if (getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')) {
3471
+                $info[getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')] = dol_hash($this->pass, 'openldap'); // Create OpenLDAP password (see LDAP_PASSWORD_HASH_TYPE)
3472
+            }
3473
+        } elseif (getDolGlobalString('LDAP_SERVER_PROTOCOLVERSION') !== '3') {
3474
+            // Set LDAP password if possible
3475
+            // If ldap key is modified and LDAPv3 we use ldap_rename function for avoid lose encrypt password
3476
+            if (getDolGlobalString('DATABASE_PWD_ENCRYPTED')) {
3477
+                // Just for the default MD5 !
3478
+                if (!getDolGlobalString('MAIN_SECURITY_HASH_ALGO')) {
3479
+                    if ($this->pass_indatabase_crypted && getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')) {
3480
+                        $info[getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')] = dolGetLdapPasswordHash($this->pass_indatabase_crypted, 'md5frommd5'); // Create OpenLDAP MD5 password from Dolibarr MD5 password
3481
+                    }
3482
+                }
3483
+            } elseif (!empty($this->pass_indatabase)) {
3484
+                // Use $this->pass_indatabase value if exists
3485
+                if (getDolGlobalString('LDAP_FIELD_PASSWORD')) {
3486
+                    $info[getDolGlobalString('LDAP_FIELD_PASSWORD')] = $this->pass_indatabase; // $this->pass_indatabase = unencrypted password
3487
+                }
3488
+                if (getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')) {
3489
+                    $info[getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')] = dol_hash($this->pass_indatabase, 'openldap'); // Create OpenLDAP password (see LDAP_PASSWORD_HASH_TYPE)
3490
+                }
3491
+            }
3492
+        }
3493
+
3494
+        if (getDolGlobalString('LDAP_SERVER_TYPE') == 'egroupware') {
3495
+            $info["objectclass"][4] = "phpgwContact"; // compatibilite egroupware
3496
+
3497
+            $info['uidnumber'] = $this->id;
3498
+
3499
+            $info['phpgwTz'] = 0;
3500
+            $info['phpgwMailType'] = 'INTERNET';
3501
+            $info['phpgwMailHomeType'] = 'INTERNET';
3502
+
3503
+            $info["phpgwContactTypeId"] = 'n';
3504
+            $info["phpgwContactCatId"] = 0;
3505
+            $info["phpgwContactAccess"] = "public";
3506
+
3507
+            if (dol_strlen($this->egroupware_id) == 0) {
3508
+                $this->egroupware_id = 1;
3509
+            }
3510
+
3511
+            $info["phpgwContactOwner"] = $this->egroupware_id;
3512
+
3513
+            if ($this->email) {
3514
+                $info["rfc822Mailbox"] = $this->email;
3515
+            }
3516
+            if ($this->user_mobile) {
3517
+                $info["phpgwCellTelephoneNumber"] = $this->user_mobile;
3518
+            }
3519
+        }
3520
+
3521
+        if (getDolGlobalString('LDAP_FIELD_USERID')) {
3522
+            $info[getDolGlobalString('LDAP_FIELD_USERID')] = $this->id;
3523
+        }
3524
+        if (getDolGlobalString('LDAP_FIELD_GROUPID')) {
3525
+            $usergroup = new UserGroup($this->db);
3526
+            $groupslist = $usergroup->listGroupsForUser($this->id);
3527
+            $info[getDolGlobalString('LDAP_FIELD_GROUPID')] = '65534';
3528
+            if (!empty($groupslist)) {
3529
+                foreach ($groupslist as $groupforuser) {
3530
+                    $info[getDolGlobalString('LDAP_FIELD_GROUPID')] = $groupforuser->id; //Select first group in list
3531
+                    break;
3532
+                }
3533
+            }
3534
+        }
3535
+        if (getDolGlobalString('LDAP_FIELD_HOMEDIRECTORY') && getDolGlobalString('LDAP_FIELD_HOMEDIRECTORYPREFIX')) {
3536
+            $info[getDolGlobalString('LDAP_FIELD_HOMEDIRECTORY')] = "{$conf->global->LDAP_FIELD_HOMEDIRECTORYPREFIX}/$this->login";
3537
+        }
3538
+
3539
+        return $info;
3540
+    }
3541
+
3542
+    /**
3543
+     *  Initialise an instance with random values.
3544
+     *  Used to build previews or test instances.
3545
+     *  id must be 0 if object instance is a specimen.
3546
+     *
3547
+     * @return int
3548
+     */
3549
+    public function initAsSpecimen()
3550
+    {
3551
+        global $user, $langs;
3552
+
3553
+        $now = dol_now();
3554
+
3555
+        // Initialise parameters
3556
+        $this->id = 0;
3557
+        $this->ref = 'SPECIMEN';
3558
+        $this->specimen = 1;
3559
+
3560
+        $this->lastname = 'DOLIBARR';
3561
+        $this->firstname = 'SPECIMEN';
3562
+        $this->gender = 'man';
3563
+        $this->note_public = 'This is a note public';
3564
+        $this->note_private = 'This is a note private';
3565
+        $this->email = '[email protected]';
3566
+        $this->personal_email = '[email protected]';
3567
+        $this->socialnetworks = [
3568
+            'skype' => 'skypepseudo',
3569
+            'twitter' => 'twitterpseudo',
3570
+            'facebook' => 'facebookpseudo',
3571
+            'linkedin' => 'linkedinpseudo',
3572
+        ];
3573
+        $this->office_phone = '0999999999';
3574
+        $this->office_fax = '0999999998';
3575
+        $this->user_mobile = '0999999997';
3576
+        $this->personal_mobile = '0999999996';
3577
+        $this->admin = 0;
3578
+        $this->login = 'dolibspec';
3579
+        $this->pass = 'dolibSpec+@123';
3580
+        //$this->pass_indatabase='dolibspec';                                   Set after a fetch
3581
+        //$this->pass_indatabase_crypted='e80ca5a88c892b0aaaf7e154853bccab';    Set after a fetch
3582
+        $this->datec = $now;
3583
+        $this->datem = $now;
3584
+
3585
+        $this->datelastlogin = $now;
3586
+        $this->iplastlogin = '127.0.0.1';
3587
+        $this->datepreviouslogin = $now;
3588
+        $this->ippreviouslogin = '127.0.0.1';
3589
+        $this->statut = 1;      // deprecated
3590
+        $this->status = 1;
3591
+
3592
+        $this->entity = 1;
3593
+
3594
+        return 1;
3595
+    }
3596
+
3597
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3598
+
3599
+    /**
3600
+     *  Load info of user object
3601
+     *
3602
+     * @param int $id Id of user to load
3603
+     *
3604
+     * @return void
3605
+     */
3606
+    public function info($id)
3607
+    {
3608
+        $sql = "SELECT u.rowid, u.login as ref, u.datec,";
3609
+        $sql .= " u.tms as date_modification, u.entity";
3610
+        $sql .= " FROM " . $dbPrefix . "user as u";
3611
+        $sql .= " WHERE u.rowid = " . ((int)$id);
3612
+
3613
+        $result = $this->db->query($sql);
3614
+        if ($result) {
3615
+            if ($this->db->num_rows($result)) {
3616
+                $obj = $this->db->fetch_object($result);
3617
+
3618
+                $this->id = $obj->rowid;
3619
+
3620
+                $this->ref = (!$obj->ref) ? $obj->rowid : $obj->ref;
3621
+                $this->date_creation = $this->db->jdate($obj->datec);
3622
+                $this->date_modification = $this->db->jdate($obj->date_modification);
3623
+                $this->entity = $obj->entity;
3624
+            }
3625
+
3626
+            $this->db->free($result);
3627
+        } else {
3628
+            dol_print_error($this->db);
3629
+        }
3630
+    }
3631
+
3632
+
3633
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3634
+
3635
+    /**
3636
+     *    Return number of mass Emailing received by this contacts with its email
3637
+     *
3638
+     * @return       int     Number of EMailings
3639
+     */
3640
+    public function getNbOfEMailings()
3641
+    {
3642
+        $sql = "SELECT count(mc.email) as nb";
3643
+        $sql .= " FROM " . $dbPrefix . "mailing_cibles as mc";
3644
+        $sql .= " WHERE mc.email = '" . $this->db->escape($this->email) . "'";
3645
+        $sql .= " AND mc.statut NOT IN (-1,0)"; // -1 error, 0 not sent, 1 sent with success
3646
+
3647
+        $resql = $this->db->query($sql);
3648
+        if ($resql) {
3649
+            $obj = $this->db->fetch_object($resql);
3650
+            $nb = $obj->nb;
3651
+
3652
+            $this->db->free($resql);
3653
+            return $nb;
3654
+        } else {
3655
+            $this->error = $this->db->error();
3656
+            return -1;
3657
+        }
3658
+    }
3659
+
3660
+    /**
3661
+     *  Return number of existing users
3662
+     *
3663
+     * @param string $limitTo Limit to '' or 'active'
3664
+     * @param string $option 'superadmin' = return for entity 0 only
3665
+     * @param int $admin Filter on admin tag
3666
+     *
3667
+     * @return int                 Number of users
3668
+     */
3669
+    public function getNbOfUsers($limitTo, $option = '', $admin = -1)
3670
+    {
3671
+        global $conf;
3672
+
3673
+        $sql = "SELECT count(rowid) as nb";
3674
+        $sql .= " FROM " . $dbPrefix . "user";
3675
+        if ($option == 'superadmin') {
3676
+            $sql .= " WHERE entity = 0";
3677
+        } else {
3678
+            $sql .= " WHERE entity IN (" . getEntity('user', 0) . ")";
3679
+            if ($limitTo == 'active') {
3680
+                $sql .= " AND statut = 1";
3681
+            }
3682
+        }
3683
+        if ($admin >= 0) {
3684
+            $sql .= " AND admin = " . (int)$admin;
3685
+        }
3686
+
3687
+        $resql = $this->db->query($sql);
3688
+        if ($resql) {
3689
+            $obj = $this->db->fetch_object($resql);
3690
+            $nb = (int)$obj->nb;
3691
+
3692
+            $this->db->free($resql);
3693
+            return $nb;
3694
+        } else {
3695
+            $this->error = $this->db->lasterror();
3696
+            return -1;
3697
+        }
3698
+    }
3699
+
3700
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3701
+
3702
+    /**
3703
+     *  Update user using data from the LDAP
3704
+     *
3705
+     * @param Object $ldapuser Ladp User
3706
+     *
3707
+     * @return int                 Return integer <0 if KO, >0 if OK
3708
+     */
3709
+    public function update_ldap2dolibarr(&$ldapuser)
3710
+    {
3711
+        // phpcs:enable
3712
+        // TODO: Voir pourquoi le update met à jour avec toutes les valeurs vide (global $user écrase ?)
3713
+        global $user, $conf;
3714
+
3715
+        $socialnetworks = getArrayOfSocialNetworks();
3716
+
3717
+        $tmpvar = getDolGlobalString('LDAP_FIELD_FIRSTNAME');
3718
+        $this->firstname = $ldapuser->$tmpvar;
3719
+        $tmpvar = getDolGlobalString('LDAP_FIELD_NAME');
3720
+        $this->lastname = $ldapuser->$tmpvar;
3721
+        $tmpvar = getDolGlobalString('LDAP_FIELD_LOGIN');
3722
+        $this->login = $ldapuser->$tmpvar;
3723
+        $tmpvar = getDolGlobalString('LDAP_FIELD_PASSWORD');
3724
+        $this->pass = $ldapuser->$tmpvar;
3725
+        $tmpvar = getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED');
3726
+        $this->pass_indatabase_crypted = $ldapuser->$tmpvar;
3727
+
3728
+        $tmpvar = getDolGlobalString('LDAP_FIELD_PHONE');
3729
+        $this->office_phone = $ldapuser->$tmpvar;
3730
+        $tmpvar = getDolGlobalString('LDAP_FIELD_MOBILE');
3731
+        $this->user_mobile = $ldapuser->$tmpvar;
3732
+        $tmpvar = getDolGlobalString('LDAP_FIELD_FAX');
3733
+        $this->office_fax = $ldapuser->$tmpvar;
3734
+        $tmpvar = getDolGlobalString('LDAP_FIELD_MAIL');
3735
+        $this->email = $ldapuser->$tmpvar;
3736
+        foreach ($socialnetworks as $key => $value) {
3737
+            $tmpvar = getDolGlobalString('LDAP_FIELD_' . strtoupper($value['label']));
3738
+            $this->socialnetworks[$value['label']] = $ldapuser->$tmpvar;
3739
+        }
3740
+        $tmpvar = getDolGlobalString('LDAP_FIELD_SID');
3741
+        $this->ldap_sid = $ldapuser->$tmpvar;
3742
+
3743
+        $tmpvar = getDolGlobalString('LDAP_FIELD_TITLE');
3744
+        $this->job = $ldapuser->$tmpvar;
3745
+        $tmpvar = getDolGlobalString('LDAP_FIELD_DESCRIPTION');
3746
+        $this->note_public = $ldapuser->$tmpvar;
3747
+
3748
+        $result = $this->update($user);
3749
+
3750
+        dol_syslog(get_class($this) . "::update_ldap2dolibarr result=" . $result, LOG_DEBUG);
3751
+
3752
+        return $result;
3753
+    }
3754
+
3755
+    /**
3756
+     * Return and array with all instantiated first level children users of current user
3757
+     *
3758
+     * @return  User[]|int<-1,-1>
3759
+     * @see getAllChildIds()
3760
+     */
3761
+    public function get_children()
3762
+    {
3763
+        // phpcs:enable
3764
+        $sql = "SELECT rowid FROM " . $dbPrefix . "user";
3765
+        $sql .= " WHERE fk_user = " . ((int)$this->id);
3766
+
3767
+        dol_syslog(get_class($this) . "::get_children", LOG_DEBUG);
3768
+        $res = $this->db->query($sql);
3769
+        if ($res) {
3770
+            $users = [];
3771
+            while ($rec = $this->db->fetch_array($res)) {
3772
+                $user = new User($this->db);
3773
+                $user->fetch($rec['rowid']);
3774
+                $users[] = $user;
3775
+            }
3776
+            return $users;
3777
+        } else {
3778
+            dol_print_error($this->db);
3779
+            return -1;
3780
+        }
3781
+    }
3782
+
3783
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3784
+
3785
+    /**
3786
+     *  Return list of all child user ids in hierarchy (all sublevels).
3787
+     *  Note: Calling this function also reset full list of users into $this->users.
3788
+     *
3789
+     * @param int $addcurrentuser 1=Add also current user id to the list.
3790
+     *
3791
+     * @return     array                       Array of user id lower than user (all levels under user). This overwrite
3792
+     *                                         this->users.
3793
+     * @see get_children()
3794
+     */
3795
+    public function getAllChildIds($addcurrentuser = 0)
3796
+    {
3797
+        $childids = [];
3798
+
3799
+        if (isset($this->cache_childids[$this->id])) {
3800
+            $childids = $this->cache_childids[$this->id];
3801
+        } else {
3802
+            // Init this->users
3803
+            $this->get_full_tree();
3804
+
3805
+            $idtoscan = $this->id;
3806
+
3807
+            dol_syslog("Build childid for id = " . $idtoscan);
3808
+            foreach ($this->users as $id => $val) {
3809
+                //var_dump($val['fullpath']);
3810
+                if (preg_match('/_' . $idtoscan . '_/', $val['fullpath'])) {
3811
+                    $childids[$val['id']] = $val['id'];
3812
+                }
3813
+            }
3814
+        }
3815
+        $this->cache_childids[$this->id] = $childids;
3816
+
3817
+        if ($addcurrentuser) {
3818
+            $childids[$this->id] = $this->id;
3819
+        }
3820
+
3821
+        return $childids;
3822
+    }
3823
+
3824
+    /**
3825
+     *  Build the hierarchy/tree of users into an array.
3826
+     *  Set and return this->users that is an array sorted according to tree with arrays of:
3827
+     *              id = id user
3828
+     *              lastname
3829
+     *              firstname
3830
+     *              fullname = Name with full path to user
3831
+     *              fullpath = Full path composed of the ids: "_grandparentid_parentid_id"
3832
+     *
3833
+     * @param int $deleteafterid Removed all users including the leaf $deleteafterid (and all its child) in user
3834
+     *                              tree.
3835
+     * @param string $filter SQL filter on users. This parameter must not come from user input.
3836
+     *
3837
+     * @return     array|int                   Array of users $this->users. Note: $this->parentof is also set.
3838
+     */
3839
+    public function get_full_tree($deleteafterid = 0, $filter = '')
3840
+    {
3841
+        // phpcs:enable
3842
+        global $conf, $user;
3843
+        global $hookmanager;
3844
+
3845
+        global $config;
3846
+        $dbPrefix = $config->db->prefix;
3847
+
3848
+        // Actions hooked (by external module)
3849
+        $hookmanager->initHooks(['userdao']);
3850
+
3851
+        $this->users = [];
3852
+
3853
+        // Init this->parentof that is array(id_son=>id_parent, ...)
3854
+        $this->loadParentOf();
3855
+
3856
+        // Init $this->users array
3857
+        $sql = "SELECT DISTINCT u.rowid, u.firstname, u.lastname, u.fk_user, u.fk_soc, u.login, u.email, u.gender, u.admin, u.statut, u.photo, u.entity"; // Distinct reduce pb with old tables with duplicates
3858
+        $sql .= " FROM " . $dbPrefix . "user as u";
3859
+        // Add fields from hooks
3860
+        $parameters = [];
3861
+        $reshook = $hookmanager->executeHooks('printUserListWhere', $parameters); // Note that $action and $object may have been modified by hook
3862
+        if ($reshook > 0) {
3863
+            $sql .= $hookmanager->resPrint;
3864
+        } else {
3865
+            $sql .= " WHERE u.entity IN (" . getEntity('user') . ")";
3866
+        }
3867
+        if ($filter) {
3868
+            $sql .= " AND " . $filter;
3869
+        }
3870
+
3871
+        dol_syslog(get_class($this) . "::get_full_tree get user list", LOG_DEBUG);
3872
+        $resql = $this->db->query($sql);
3873
+        if ($resql) {
3874
+            $i = 0;
3875
+            while ($obj = $this->db->fetch_object($resql)) {
3876
+                $this->users[$obj->rowid]['rowid'] = $obj->rowid;
3877
+                $this->users[$obj->rowid]['id'] = $obj->rowid;
3878
+                $this->users[$obj->rowid]['fk_user'] = $obj->fk_user;
3879
+                $this->users[$obj->rowid]['fk_soc'] = $obj->fk_soc;
3880
+                $this->users[$obj->rowid]['firstname'] = $obj->firstname;
3881
+                $this->users[$obj->rowid]['lastname'] = $obj->lastname;
3882
+                $this->users[$obj->rowid]['login'] = $obj->login;
3883
+                $this->users[$obj->rowid]['statut'] = $obj->statut;
3884
+                $this->users[$obj->rowid]['entity'] = $obj->entity;
3885
+                $this->users[$obj->rowid]['email'] = $obj->email;
3886
+                $this->users[$obj->rowid]['gender'] = $obj->gender;
3887
+                $this->users[$obj->rowid]['admin'] = $obj->admin;
3888
+                $this->users[$obj->rowid]['photo'] = $obj->photo;
3889
+                $i++;
3890
+            }
3891
+        } else {
3892
+            dol_print_error($this->db);
3893
+            return -1;
3894
+        }
3895
+
3896
+        // We add the fullpath property to each element of the first level (no parent exists)
3897
+        dol_syslog(get_class($this) . "::get_full_tree call to build_path_from_id_user", LOG_DEBUG);
3898
+        foreach ($this->users as $key => $val) {
3899
+            $result = $this->build_path_from_id_user($key, 0); // Process a branch from the root user key (this user has no parent)
3900
+            if ($result < 0) {
3901
+                $this->error = 'ErrorLoopInHierarchy';
3902
+                return -1;
3903
+            }
3904
+        }
3905
+
3906
+        // Exclude leaf including $deleteafterid from tree
3907
+        if ($deleteafterid) {
3908
+            //print "Look to discard user ".$deleteafterid."\n";
3909
+            $keyfilter1 = '^' . $deleteafterid . '$';
3910
+            $keyfilter2 = '_' . $deleteafterid . '$';
3911
+            $keyfilter3 = '^' . $deleteafterid . '_';
3912
+            $keyfilter4 = '_' . $deleteafterid . '_';
3913
+            foreach (array_keys($this->users) as $key) {
3914
+                $fullpath = $this->users[$key]['fullpath'];
3915
+                if (
3916
+                    preg_match('/' . $keyfilter1 . '/', $fullpath) || preg_match('/' . $keyfilter2 . '/', $fullpath)
3917
+                    || preg_match('/' . $keyfilter3 . '/', $fullpath) || preg_match('/' . $keyfilter4 . '/', $fullpath)
3918
+                ) {
3919
+                    unset($this->users[$key]);
3920
+                }
3921
+            }
3922
+        }
3923
+
3924
+        dol_syslog(get_class($this) . "::get_full_tree dol_sort_array", LOG_DEBUG);
3925
+        $this->users = dol_sort_array($this->users, 'fullname', 'asc', true, false);
3926
+
3927
+        //var_dump($this->users);
3928
+
3929
+        return $this->users;
3930
+    }
3931
+
3932
+    /**
3933
+     *  Load this->parentof that is array(id_son=>id_parent, ...)
3934
+     *
3935
+     * @return     int<-1,1>     Return integer <0 if KO, >0 if OK
3936
+     */
3937
+    private function loadParentOf()
3938
+    {
3939
+        global $conf;
3940
+
3941
+        global $config;
3942
+        $dbPrefix = $config->db->prefix;
3943
+
3944
+        $this->parentof = [];
3945
+
3946
+        // Load array[child]=parent
3947
+        $sql = "SELECT fk_user as id_parent, rowid as id_son";
3948
+        $sql .= " FROM " . $dbPrefix . "user";
3949
+        $sql .= " WHERE fk_user <> 0";
3950
+        $sql .= " AND entity IN (" . getEntity('user') . ")";
3951
+
3952
+        dol_syslog(get_class($this) . "::loadParentOf", LOG_DEBUG);
3953
+        $resql = $this->db->query($sql);
3954
+        if ($resql) {
3955
+            while ($obj = $this->db->fetch_object($resql)) {
3956
+                $this->parentof[$obj->id_son] = $obj->id_parent;
3957
+            }
3958
+            return 1;
3959
+        } else {
3960
+            dol_print_error($this->db);
3961
+            return -1;
3962
+        }
3963
+    }
3964
+
3965
+    /**
3966
+     *  For user id_user and its children available in this->users, define property fullpath and fullname.
3967
+     *  Function called by get_full_tree().
3968
+     *
3969
+     * @param int $id_user id_user entry to update
3970
+     * @param int $protection Deep counter to avoid infinite loop (no more required, a protection is added with array
3971
+     *                        useridfound)
3972
+     *
3973
+     * @return     int<-1,1>               Return integer < 0 if KO (infinite loop), >= 0 if OK
3974
+     */
3975
+    public function build_path_from_id_user($id_user, $protection = 0)
3976
+    {
3977
+        // phpcs:enable
3978
+        //dol_syslog(get_class($this)."::build_path_from_id_user id_user=".$id_user." protection=".$protection, LOG_DEBUG);
3979
+
3980
+        if (!empty($this->users[$id_user]['fullpath'])) {
3981
+            // Already defined
3982
+            dol_syslog(get_class($this) . "::build_path_from_id_user fullpath and fullname already defined", LOG_WARNING);
3983
+            return 0;
3984
+        }
3985
+
3986
+        // Define fullpath and fullname
3987
+        $this->users[$id_user]['fullpath'] = '_' . $id_user;
3988
+        $this->users[$id_user]['fullname'] = $this->users[$id_user]['lastname'];
3989
+        $i = 0;
3990
+        $cursor_user = $id_user;
3991
+
3992
+        $useridfound = [$id_user];
3993
+        while (!empty($this->parentof[$cursor_user]) && !empty($this->users[$this->parentof[$cursor_user]])) {
3994
+            if (in_array($this->parentof[$cursor_user], $useridfound)) {
3995
+                dol_syslog("The hierarchy of user has a recursive loop", LOG_WARNING);
3996
+                return -1; // Should not happen. Protection against looping hierarchy
3997
+            }
3998
+            $useridfound[] = $this->parentof[$cursor_user];
3999
+            $this->users[$id_user]['fullpath'] = '_' . $this->parentof[$cursor_user] . $this->users[$id_user]['fullpath'];
4000
+            $this->users[$id_user]['fullname'] = $this->users[$this->parentof[$cursor_user]]['lastname'] . ' >> ' . $this->users[$id_user]['fullname'];
4001
+            $i++;
4002
+            $cursor_user = $this->parentof[$cursor_user];
4003
+        }
4004
+
4005
+        // We count number of _ to have level
4006
+        $this->users[$id_user]['level'] = dol_strlen(preg_replace('/[^_]/i', '', $this->users[$id_user]['fullpath']));
4007
+
4008
+        return 1;
4009
+    }
4010
+
4011
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4012
+
4013
+    /**
4014
+     *      Load metrics this->nb for dashboard
4015
+     *
4016
+     * @return     int         Return integer <0 if KO, >0 if OK
4017
+     */
4018
+    public function loadStateBoard()
4019
+    {
4020
+        global $conf;
4021
+
4022
+        $this->nb = [];
4023
+
4024
+        $sql = "SELECT COUNT(DISTINCT u.rowid) as nb";
4025
+        $sql .= " FROM " . $dbPrefix . "user as u";
4026
+        if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
4027
+            $sql .= ", " . $dbPrefix . "usergroup_user as ug";
4028
+            $sql .= " WHERE ug.entity IN (" . getEntity('usergroup') . ")";
4029
+            $sql .= " AND ug.fk_user = u.rowid";
4030
+        } else {
4031
+            $sql .= " WHERE u.entity IN (" . getEntity('user') . ")";
4032
+        }
4033
+        $sql .= " AND u.statut > 0";
4034
+        //$sql.= " AND employee != 0";
4035
+
4036
+        $resql = $this->db->query($sql);
4037
+        if ($resql) {
4038
+            while ($obj = $this->db->fetch_object($resql)) {
4039
+                $this->nb["users"] = $obj->nb;
4040
+            }
4041
+            $this->db->free($resql);
4042
+            return 1;
4043
+        } else {
4044
+            dol_print_error($this->db);
4045
+            $this->error = $this->db->error();
4046
+            return -1;
4047
+        }
4048
+    }
4049
+
4050
+    /**
4051
+     *  Create a document onto disk according to template module.
4052
+     *
4053
+     * @param string $modele Force model to use ('' to not force)
4054
+     * @param Translate $outputlangs Object langs to use for output
4055
+     * @param int $hidedetails Hide details of lines
4056
+     * @param int $hidedesc Hide description
4057
+     * @param int $hideref Hide ref
4058
+     * @param null|array $moreparams Array to provide more information
4059
+     *
4060
+     * @return     int                         0 if KO, 1 if OK
4061
+     */
4062
+    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
4063
+    {
4064
+        global $conf, $user, $langs;
4065
+
4066
+        $langs->load("user");
4067
+
4068
+        // Set the '$modele' to the name of the document template (model) to use
4069
+        if (!dol_strlen($modele)) {
4070
+            if (getDolGlobalString('USER_ADDON_PDF')) {
4071
+                $modele = getDolGlobalString('USER_ADDON_PDF');
4072
+            } else {
4073
+                $modele = 'bluesky';
4074
+            }
4075
+        }
4076
+
4077
+        $modelpath = "core/modules/user/doc/";
4078
+
4079
+        return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
4080
+    }
4081
+
4082
+    /**
4083
+     *  Return property of user from its id
4084
+     *
4085
+     * @param int $rowid id of contact
4086
+     * @param string $mode 'email', 'mobile', or 'name'
4087
+     *
4088
+     * @return string              Email of user with format: "Full name <email>"
4089
+     */
4090
+    public function user_get_property($rowid, $mode)
4091
+    {
4092
+        // phpcs:enable
4093
+        $user_property = '';
4094
+
4095
+        if (empty($rowid)) {
4096
+            return '';
4097
+        }
4098
+
4099
+        $sql = "SELECT rowid, email, user_mobile, civility, lastname, firstname";
4100
+        $sql .= " FROM " . $dbPrefix . "user";
4101
+        $sql .= " WHERE rowid = " . ((int)$rowid);
4102
+
4103
+        $resql = $this->db->query($sql);
4104
+        if ($resql) {
4105
+            $nump = $this->db->num_rows($resql);
4106
+
4107
+            if ($nump) {
4108
+                $obj = $this->db->fetch_object($resql);
4109
+
4110
+                if ($mode == 'email') {
4111
+                    $user_property = dolGetFirstLastname($obj->firstname, $obj->lastname) . " <" . $obj->email . ">";
4112
+                } elseif ($mode == 'mobile') {
4113
+                    $user_property = $obj->user_mobile;
4114
+                } elseif ($mode == 'name') {
4115
+                    $user_property = dolGetFirstLastname($obj->firstname, $obj->lastname);
4116
+                }
4117
+            }
4118
+            return $user_property;
4119
+        } else {
4120
+            dol_print_error($this->db);
4121
+        }
4122
+
4123
+        return '';
4124
+    }
4125
+
4126
+    /**
4127
+     * Return string with full Url to virtual card
4128
+     *
4129
+     * @param string $mode Mode for link
4130
+     * @param string $typeofurl 'external' or 'internal'
4131
+     *
4132
+     * @return  string                  Url string link
4133
+     */
4134
+    public function getOnlineVirtualCardUrl($mode = '', $typeofurl = 'external')
4135
+    {
4136
+        global $dolibarr_main_url_root;
4137
+        global $conf;
4138
+
4139
+        $encodedsecurekey = dol_hash($conf->file->instance_unique_id . 'uservirtualcard' . $this->id . '-' . $this->login, 'md5');
4140
+        if (isModEnabled('multicompany')) {
4141
+            $entity_qr = '&entity=' . ((int)$conf->entity);
4142
+        } else {
4143
+            $entity_qr = '';
4144
+        }
4145
+        // Define $urlwithroot
4146
+        $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($dolibarr_main_url_root));
4147
+        $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
4148
+        //$urlwithroot=DOL_MAIN_URL_ROOT;                   // This is to use same domain name than current
4149
+
4150
+        if ($typeofurl == 'internal') {
4151
+            $urlwithroot = DOL_URL_ROOT;
4152
+        }
4153
+
4154
+        return $urlwithroot . '/public/users/view.php?id=' . $this->id . '&securekey=' . $encodedsecurekey . $entity_qr . ($mode ? '&mode=' . urlencode($mode) : '');
4155
+    }
4156
+
4157
+    /**
4158
+     * Find a user by the given e-mail and return it's user id when found
4159
+     *
4160
+     * NOTE:
4161
+     * Use AGENDA_DISABLE_EXACT_USER_EMAIL_COMPARE_FOR_EXTERNAL_CALENDAR
4162
+     * to disable exact e-mail search
4163
+     *
4164
+     * @param string $email The full e-mail (or a part of a e-mail)
4165
+     *
4166
+     * @return int              Return integer <0 = user was not found, >0 = The id of the user
4167
+     */
4168
+    public function findUserIdByEmail($email)
4169
+    {
4170
+        if (isset($this->findUserIdByEmailCache[$email])) {
4171
+            return $this->findUserIdByEmailCache[$email];
4172
+        }
4173
+
4174
+        $this->findUserIdByEmailCache[$email] = -1;
4175
+
4176
+        $sql = 'SELECT rowid';
4177
+        $sql .= ' FROM ' . $dbPrefix . 'user';
4178
+        if (getDolGlobalString('AGENDA_DISABLE_EXACT_USER_EMAIL_COMPARE_FOR_EXTERNAL_CALENDAR')) {
4179
+            $sql .= " WHERE email LIKE '%" . $this->db->escape($this->db->escapeforlike($email)) . "%'";
4180
+        } else {
4181
+            $sql .= " WHERE email = '" . $this->db->escape($email) . "'";
4182
+        }
4183
+        $sql .= ' LIMIT 1';
4184
+
4185
+        $resql = $this->db->query($sql);
4186
+        if (!$resql) {
4187
+            return -1;
4188
+        }
4189
+
4190
+        $obj = $this->db->fetch_object($resql);
4191
+        if (!$obj) {
4192
+            return -1;
4193
+        }
4194
+
4195
+        $this->findUserIdByEmailCache[$email] = (int)$obj->rowid;
4196
+
4197
+        return $this->findUserIdByEmailCache[$email];
4198
+    }
4199 4199
 }
Please login to merge, or discard this patch.
Spacing   +109 added lines, -109 removed lines patch added patch discarded remove patch
@@ -439,8 +439,8 @@  discard block
 block discarded – undo
439 439
 			// If we ask to add a given permission, we first load properties of this permission (module, perms and subperms).
440 440
 			$sql = "SELECT module, perms, subperms";
441 441
 			$sql .= " FROM " . $dbPrefix . "rights_def";
442
-			$sql .= " WHERE id = " . ((int)$rid);
443
-			$sql .= " AND entity = " . ((int)$entity);
442
+			$sql .= " WHERE id = " . ((int) $rid);
443
+			$sql .= " AND entity = " . ((int) $entity);
444 444
 
445 445
 			$result = $this->db->query($sql);
446 446
 			if ($result) {
@@ -457,7 +457,7 @@  discard block
 block discarded – undo
457 457
 			}
458 458
 
459 459
 			// Define the where for the permission to add
460
-			$whereforadd = "id=" . ((int)$rid);
460
+			$whereforadd = "id=" . ((int) $rid);
461 461
 			// Add also inherited permissions
462 462
 			if (!empty($subperms)) {
463 463
 				$whereforadd .= " OR (module='" . $this->db->escape($module) . "' AND perms='" . $this->db->escape($perms) . "' AND (subperms='lire' OR subperms='read'))";
@@ -486,15 +486,15 @@  discard block
 block discarded – undo
486 486
 			//print "$module-$perms-$subperms";
487 487
 			$sql = "SELECT id";
488 488
 			$sql .= " FROM " . $dbPrefix . "rights_def";
489
-			$sql .= " WHERE entity = " . ((int)$entity);
489
+			$sql .= " WHERE entity = " . ((int) $entity);
490 490
 			if (!empty($whereforadd) && $whereforadd != 'allmodules') {
491
-				$sql .= " AND (" . $whereforadd . ")";  // Note: parenthesis are important because whereforadd can contains OR. Also note that $whereforadd is already sanitized
491
+				$sql .= " AND (" . $whereforadd . ")"; // Note: parenthesis are important because whereforadd can contains OR. Also note that $whereforadd is already sanitized
492 492
 			}
493 493
 
494 494
 			$sqldelete = "DELETE FROM " . $dbPrefix . "user_rights";
495
-			$sqldelete .= " WHERE fk_user = " . ((int)$this->id) . " AND fk_id IN (";
495
+			$sqldelete .= " WHERE fk_user = " . ((int) $this->id) . " AND fk_id IN (";
496 496
 			$sqldelete .= $sql;
497
-			$sqldelete .= ") AND entity = " . ((int)$entity);
497
+			$sqldelete .= ") AND entity = " . ((int) $entity);
498 498
 			if (!$this->db->query($sqldelete)) {
499 499
 				$error++;
500 500
 			}
@@ -510,7 +510,7 @@  discard block
 block discarded – undo
510 510
 						if ($obj) {
511 511
 							$nid = $obj->id;
512 512
 
513
-							$sql = "INSERT INTO " . $dbPrefix . "user_rights (entity, fk_user, fk_id) VALUES (" . ((int)$entity) . ", " . ((int)$this->id) . ", " . ((int)$nid) . ")";
513
+							$sql = "INSERT INTO " . $dbPrefix . "user_rights (entity, fk_user, fk_id) VALUES (" . ((int) $entity) . ", " . ((int) $this->id) . ", " . ((int) $nid) . ")";
514 514
 							if (!$this->db->query($sql)) {
515 515
 								$error++;
516 516
 							}
@@ -576,7 +576,7 @@  discard block
 block discarded – undo
576 576
 			$sql = "SELECT module, perms, subperms";
577 577
 			$sql .= " FROM " . $dbPrefix . "rights_def";
578 578
 			$sql .= " WHERE id = '" . $this->db->escape($rid) . "'";
579
-			$sql .= " AND entity = " . ((int)$entity);
579
+			$sql .= " AND entity = " . ((int) $entity);
580 580
 
581 581
 			$result = $this->db->query($sql);
582 582
 			if ($result) {
@@ -593,7 +593,7 @@  discard block
 block discarded – undo
593 593
 			}
594 594
 
595 595
 			// Where clause for the list of permissions to delete
596
-			$wherefordel = "id=" . ((int)$rid);
596
+			$wherefordel = "id=" . ((int) $rid);
597 597
 			// Suppression des droits induits
598 598
 			if ($subperms == 'lire' || $subperms == 'read') {
599 599
 				$wherefordel .= " OR (module='" . $this->db->escape($module) . "' AND perms='" . $this->db->escape($perms) . "' AND subperms IS NOT NULL)";
@@ -622,9 +622,9 @@  discard block
 block discarded – undo
622 622
 			//print "$module-$perms-$subperms";
623 623
 			$sql = "SELECT id";
624 624
 			$sql .= " FROM " . $dbPrefix . "rights_def";
625
-			$sql .= " WHERE entity = " . ((int)$entity);
625
+			$sql .= " WHERE entity = " . ((int) $entity);
626 626
 			if (!empty($wherefordel) && $wherefordel != 'allmodules') {
627
-				$sql .= " AND (" . $wherefordel . ")";  // Note: parenthesis are important because wherefordel can contains OR. Also note that $wherefordel is already sanitized
627
+				$sql .= " AND (" . $wherefordel . ")"; // Note: parenthesis are important because wherefordel can contains OR. Also note that $wherefordel is already sanitized
628 628
 			}
629 629
 
630 630
 			// avoid admin can remove his own important rights
@@ -636,10 +636,10 @@  discard block
 block discarded – undo
636 636
 			}
637 637
 
638 638
 			$sqldelete = "DELETE FROM " . $dbPrefix . "user_rights";
639
-			$sqldelete .= " WHERE fk_user = " . ((int)$this->id) . " AND fk_id IN (";
639
+			$sqldelete .= " WHERE fk_user = " . ((int) $this->id) . " AND fk_id IN (";
640 640
 			$sqldelete .= $sql;
641 641
 			$sqldelete .= ")";
642
-			$sqldelete .= " AND entity = " . ((int)$entity);
642
+			$sqldelete .= " AND entity = " . ((int) $entity);
643 643
 
644 644
 			$resql = $this->db->query($sqldelete);
645 645
 			if (!$resql) {
@@ -733,9 +733,9 @@  discard block
 block discarded – undo
733 733
 			} else {
734 734
 				// On table r=rights_def, the unique key is (id, entity) because id is hard coded into module descriptor and insert during module activation.
735 735
 				// So we must include the filter on entity on both table r. and ur.
736
-				$sql .= " AND r.entity = " . ((int)$conf->entity) . " AND ur.entity = " . ((int)$conf->entity);
736
+				$sql .= " AND r.entity = " . ((int) $conf->entity) . " AND ur.entity = " . ((int) $conf->entity);
737 737
 			}
738
-			$sql .= " AND ur.fk_user= " . ((int)$this->id);
738
+			$sql .= " AND ur.fk_user= " . ((int) $this->id);
739 739
 			$sql .= " AND r.perms IS NOT NULL";
740 740
 			if (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) {
741 741
 				$sql .= " AND r.perms NOT LIKE '%_advance'"; // Hide advanced perms if option is not enabled
@@ -794,19 +794,19 @@  discard block
 block discarded – undo
794 794
 				if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
795 795
 					$sql .= " AND gu.entity IN (0," . $conf->entity . ")";
796 796
 				} else {
797
-					$sql .= " AND r.entity = " . ((int)$conf->entity);
797
+					$sql .= " AND r.entity = " . ((int) $conf->entity);
798 798
 				}
799 799
 			} else {
800
-				$sql .= " AND gr.entity = " . ((int)$conf->entity);  // Only groups created in current entity
800
+				$sql .= " AND gr.entity = " . ((int) $conf->entity); // Only groups created in current entity
801 801
 				// The entity on the table usergroup_user should be useless and should never be used because it is already into gr and r.
802 802
 				// but when using MULTICOMPANY_TRANSVERSE_MODE, we may insert record that make rubbish result due to duplicate record of
803 803
 				// other entities, so we are forced to add a filter here
804 804
 				$sql .= " AND gu.entity IN (0," . $conf->entity . ")";
805
-				$sql .= " AND r.entity = " . ((int)$conf->entity);   // Only permission of modules enabled in current entity
805
+				$sql .= " AND r.entity = " . ((int) $conf->entity); // Only permission of modules enabled in current entity
806 806
 			}
807 807
 			// End of strange business rule
808 808
 			$sql .= " AND gr.fk_usergroup = gu.fk_usergroup";
809
-			$sql .= " AND gu.fk_user = " . ((int)$this->id);
809
+			$sql .= " AND gu.fk_user = " . ((int) $this->id);
810 810
 			$sql .= " AND r.perms IS NOT NULL";
811 811
 			if ($moduletag) {
812 812
 				$sql .= " AND r.module = '" . $this->db->escape($moduletag) . "'";
@@ -957,8 +957,8 @@  discard block
 block discarded – undo
957 957
 
958 958
 		// Save in database
959 959
 		$sql = "UPDATE " . $dbPrefix . "user";
960
-		$sql .= " SET statut = " . ((int)$status);
961
-		$sql .= " WHERE rowid = " . ((int)$this->id);
960
+		$sql .= " SET statut = " . ((int) $status);
961
+		$sql .= " WHERE rowid = " . ((int) $this->id);
962 962
 		$result = $this->db->query($sql);
963 963
 
964 964
 		dol_syslog(get_class($this) . "::setstatus", LOG_DEBUG);
@@ -1023,7 +1023,7 @@  discard block
 block discarded – undo
1023 1023
 		dol_syslog(get_class($this) . "::delete", LOG_DEBUG);
1024 1024
 
1025 1025
 		// Remove rights
1026
-		$sql = "DELETE FROM " . $dbPrefix . "user_rights WHERE fk_user = " . ((int)$this->id);
1026
+		$sql = "DELETE FROM " . $dbPrefix . "user_rights WHERE fk_user = " . ((int) $this->id);
1027 1027
 
1028 1028
 		if (!$error && !$this->db->query($sql)) {
1029 1029
 			$error++;
@@ -1031,14 +1031,14 @@  discard block
 block discarded – undo
1031 1031
 		}
1032 1032
 
1033 1033
 		// Remove group
1034
-		$sql = "DELETE FROM " . $dbPrefix . "usergroup_user WHERE fk_user  = " . ((int)$this->id);
1034
+		$sql = "DELETE FROM " . $dbPrefix . "usergroup_user WHERE fk_user  = " . ((int) $this->id);
1035 1035
 		if (!$error && !$this->db->query($sql)) {
1036 1036
 			$error++;
1037 1037
 			$this->error = $this->db->lasterror();
1038 1038
 		}
1039 1039
 
1040 1040
 		// Remove params
1041
-		$sql = "DELETE FROM " . $dbPrefix . "user_param WHERE fk_user  = " . ((int)$this->id);
1041
+		$sql = "DELETE FROM " . $dbPrefix . "user_param WHERE fk_user  = " . ((int) $this->id);
1042 1042
 		if (!$error && !$this->db->query($sql)) {
1043 1043
 			$error++;
1044 1044
 			$this->error = $this->db->lasterror();
@@ -1046,7 +1046,7 @@  discard block
 block discarded – undo
1046 1046
 
1047 1047
 		// If contact, remove link
1048 1048
 		if ($this->contact_id > 0) {
1049
-			$sql = "UPDATE " . $dbPrefix . "socpeople SET fk_user_creat = null WHERE rowid = " . ((int)$this->contact_id);
1049
+			$sql = "UPDATE " . $dbPrefix . "socpeople SET fk_user_creat = null WHERE rowid = " . ((int) $this->contact_id);
1050 1050
 			if (!$error && !$this->db->query($sql)) {
1051 1051
 				$error++;
1052 1052
 				$this->error = $this->db->lasterror();
@@ -1064,7 +1064,7 @@  discard block
 block discarded – undo
1064 1064
 
1065 1065
 		// Remove user
1066 1066
 		if (!$error) {
1067
-			$sql = "DELETE FROM " . $dbPrefix . "user WHERE rowid = " . ((int)$this->id);
1067
+			$sql = "DELETE FROM " . $dbPrefix . "user WHERE rowid = " . ((int) $this->id);
1068 1068
 			dol_syslog(get_class($this) . "::delete", LOG_DEBUG);
1069 1069
 			if (!$this->db->query($sql)) {
1070 1070
 				$error++;
@@ -1159,7 +1159,7 @@  discard block
 block discarded – undo
1159 1159
 
1160 1160
 		if ($entity < 0) {
1161 1161
 			if ((!isModEnabled('multicompany') || !getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) && (!empty($user->entity))) {
1162
-				$sql .= " WHERE u.entity IN (0, " . ((int)$conf->entity) . ")";
1162
+				$sql .= " WHERE u.entity IN (0, " . ((int) $conf->entity) . ")";
1163 1163
 			} else {
1164 1164
 				$sql .= " WHERE u.entity IS NOT NULL"; // multicompany is on in transverse mode or user making fetch is on entity 0, so user is allowed to fetch anywhere into database
1165 1165
 			}
@@ -1168,7 +1168,7 @@  discard block
 block discarded – undo
1168 1168
 			if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
1169 1169
 				$sql .= " WHERE u.entity IS NOT NULL"; // multicompany is on in transverse mode or user making fetch is on entity 0, so user is allowed to fetch anywhere into database
1170 1170
 			} else {
1171
-				$sql .= " WHERE u.entity IN (0, " . ((int)(($entity != '' && $entity >= 0) ? $entity : $conf->entity)) . ")"; // search in entity provided in parameter
1171
+				$sql .= " WHERE u.entity IN (0, " . ((int) (($entity != '' && $entity >= 0) ? $entity : $conf->entity)) . ")"; // search in entity provided in parameter
1172 1172
 			}
1173 1173
 		}
1174 1174
 
@@ -1184,9 +1184,9 @@  discard block
 block discarded – undo
1184 1184
 			}
1185 1185
 			$sql .= ")";
1186 1186
 		} elseif ($fk_socpeople > 0) {
1187
-			$sql .= " AND u.fk_socpeople = " . ((int)$fk_socpeople);
1187
+			$sql .= " AND u.fk_socpeople = " . ((int) $fk_socpeople);
1188 1188
 		} else {
1189
-			$sql .= " AND u.rowid = " . ((int)$id);
1189
+			$sql .= " AND u.rowid = " . ((int) $id);
1190 1190
 		}
1191 1191
 		$sql .= " ORDER BY u.entity ASC"; // Avoid random result when there is 2 login in 2 different entities
1192 1192
 
@@ -1250,15 +1250,15 @@  discard block
 block discarded – undo
1250 1250
 				$this->email = $obj->email;
1251 1251
 				$this->email_oauth2 = $obj->email_oauth2;
1252 1252
 				$this->personal_email = $obj->personal_email;
1253
-				$this->socialnetworks = ($obj->socialnetworks ? (array)json_decode($obj->socialnetworks, true) : []);
1253
+				$this->socialnetworks = ($obj->socialnetworks ? (array) json_decode($obj->socialnetworks, true) : []);
1254 1254
 				$this->job = $obj->job;
1255 1255
 				$this->signature = $obj->signature;
1256 1256
 				$this->admin = $obj->admin;
1257 1257
 				$this->note_public = $obj->note_public;
1258 1258
 				$this->note_private = $obj->note_private;
1259
-				$this->note = $obj->note_private;   // deprecated
1259
+				$this->note = $obj->note_private; // deprecated
1260 1260
 
1261
-				$this->statut = $obj->status;         // deprecated
1261
+				$this->statut = $obj->status; // deprecated
1262 1262
 				$this->status = $obj->status;
1263 1263
 
1264 1264
 				$this->photo = $obj->photo;
@@ -1345,8 +1345,8 @@  discard block
 block discarded – undo
1345 1345
 
1346 1346
 		// Load user->conf for user
1347 1347
 		$sql = "SELECT param, value FROM " . $dbPrefix . "user_param";
1348
-		$sql .= " WHERE fk_user = " . ((int)$this->id);
1349
-		$sql .= " AND entity = " . ((int)$conf->entity);
1348
+		$sql .= " WHERE fk_user = " . ((int) $this->id);
1349
+		$sql .= " AND entity = " . ((int) $conf->entity);
1350 1350
 		//dol_syslog(get_class($this).'::fetch load personalized conf', LOG_DEBUG);
1351 1351
 		$resql = $this->db->query($sql);
1352 1352
 		if ($resql) {
@@ -1383,7 +1383,7 @@  discard block
 block discarded – undo
1383 1383
 			// Load user->default_values for user. TODO Save this in memcached ?
1384 1384
 
1385 1385
 			$defaultValues = new DefaultValues($this->db);
1386
-			$result = $defaultValues->fetchAll('', '', 0, 0, '(t.user_id:in:0,' . $this->id . ') AND (entity:in:' . (isset($this->entity) ? $this->entity : $conf->entity) . ',' . $conf->entity . ')');    // User 0 (all) + me (if defined)
1386
+			$result = $defaultValues->fetchAll('', '', 0, 0, '(t.user_id:in:0,' . $this->id . ') AND (entity:in:' . (isset($this->entity) ? $this->entity : $conf->entity) . ',' . $conf->entity . ')'); // User 0 (all) + me (if defined)
1387 1387
 			//$result = $defaultValues->fetchAll('', '', 0, 0, array('t.user_id'=>array(0, $this->id), 'entity'=>array((isset($this->entity) ? $this->entity : $conf->entity), $conf->entity)));    // User 0 (all) + me (if defined)
1388 1388
 
1389 1389
 			if (!is_array($result) && $result < 0) {
@@ -1544,12 +1544,12 @@  discard block
 block discarded – undo
1544 1544
 		$result = $this->create($user, 1);
1545 1545
 		if ($result > 0) {
1546 1546
 			$sql = "UPDATE " . $dbPrefix . "user";
1547
-			$sql .= " SET fk_socpeople=" . ((int)$contact->id);
1547
+			$sql .= " SET fk_socpeople=" . ((int) $contact->id);
1548 1548
 			$sql .= ", civility='" . $this->db->escape($contact->civility_code) . "'";
1549 1549
 			if ($contact->socid > 0) {
1550
-				$sql .= ", fk_soc=" . ((int)$contact->socid);
1550
+				$sql .= ", fk_soc=" . ((int) $contact->socid);
1551 1551
 			}
1552
-			$sql .= " WHERE rowid=" . ((int)$this->id);
1552
+			$sql .= " WHERE rowid=" . ((int) $this->id);
1553 1553
 
1554 1554
 			$resql = $this->db->query($sql);
1555 1555
 
@@ -1604,8 +1604,8 @@  discard block
 block discarded – undo
1604 1604
 		// Clean parameters
1605 1605
 		$this->setUpperOrLowerCase();
1606 1606
 
1607
-		$this->civility_code = trim((string)$this->civility_code);
1608
-		$this->login = trim((string)$this->login);
1607
+		$this->civility_code = trim((string) $this->civility_code);
1608
+		$this->login = trim((string) $this->login);
1609 1609
 		if (!isset($this->entity)) {
1610 1610
 			$this->entity = $conf->entity; // If not defined, we use default value
1611 1611
 		}
@@ -1637,7 +1637,7 @@  discard block
 block discarded – undo
1637 1637
 
1638 1638
 		// Check if login already exists in same entity or into entity 0.
1639 1639
 		if ($this->login) {
1640
-			$sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND login = '" . $this->db->escape($this->login) . "'";
1640
+			$sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int) $this->entity) . ", 0) AND login = '" . $this->db->escape($this->login) . "'";
1641 1641
 			$resqltochecklogin = $this->db->query($sqltochecklogin);
1642 1642
 			if ($resqltochecklogin) {
1643 1643
 				$objtochecklogin = $this->db->fetch_object($resqltochecklogin);
@@ -1652,7 +1652,7 @@  discard block
 block discarded – undo
1652 1652
 			}
1653 1653
 		}
1654 1654
 		if (!empty($this->email)) {
1655
-			$sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND email = '" . $this->db->escape($this->email) . "'";
1655
+			$sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int) $this->entity) . ", 0) AND email = '" . $this->db->escape($this->email) . "'";
1656 1656
 			$resqltochecklogin = $this->db->query($sqltochecklogin);
1657 1657
 			if ($resqltochecklogin) {
1658 1658
 				$objtochecklogin = $this->db->fetch_object($resqltochecklogin);
@@ -1669,7 +1669,7 @@  discard block
 block discarded – undo
1669 1669
 
1670 1670
 		// Insert into database
1671 1671
 		$sql = "INSERT INTO " . $dbPrefix . "user (datec, login, ldap_sid, entity)";
1672
-		$sql .= " VALUES('" . $this->db->idate($this->datec) . "', '" . $this->db->escape($this->login) . "', '" . $this->db->escape($this->ldap_sid) . "', " . ((int)$this->entity) . ")";
1672
+		$sql .= " VALUES('" . $this->db->idate($this->datec) . "', '" . $this->db->escape($this->login) . "', '" . $this->db->escape($this->ldap_sid) . "', " . ((int) $this->entity) . ")";
1673 1673
 		$result = $this->db->query($sql);
1674 1674
 
1675 1675
 		dol_syslog(get_class($this) . "::create", LOG_DEBUG);
@@ -1745,7 +1745,7 @@  discard block
 block discarded – undo
1745 1745
 		$num = 0;
1746 1746
 		$sql = "SELECT id FROM " . $dbPrefix . "rights_def";
1747 1747
 		$sql .= " WHERE bydefault = 1";
1748
-		$sql .= " AND entity = " . ((int)$conf->entity);
1748
+		$sql .= " AND entity = " . ((int) $conf->entity);
1749 1749
 
1750 1750
 		$resql = $this->db->query($sql);
1751 1751
 		if ($resql) {
@@ -1800,47 +1800,47 @@  discard block
 block discarded – undo
1800 1800
 		dol_syslog(get_class($this) . "::update notrigger=" . $notrigger . ", nosyncmember=" . $nosyncmember . ", nosyncmemberpass=" . $nosyncmemberpass);
1801 1801
 
1802 1802
 		// Clean parameters
1803
-		$this->civility_code = trim((string)$this->civility_code);
1804
-		$this->lastname = trim((string)$this->lastname);
1805
-		$this->firstname = trim((string)$this->firstname);
1806
-		$this->ref_employee = trim((string)$this->ref_employee);
1807
-		$this->national_registration_number = trim((string)$this->national_registration_number);
1803
+		$this->civility_code = trim((string) $this->civility_code);
1804
+		$this->lastname = trim((string) $this->lastname);
1805
+		$this->firstname = trim((string) $this->firstname);
1806
+		$this->ref_employee = trim((string) $this->ref_employee);
1807
+		$this->national_registration_number = trim((string) $this->national_registration_number);
1808 1808
 		$this->employee = ($this->employee > 0 ? $this->employee : 0);
1809
-		$this->login = trim((string)$this->login);
1810
-		$this->gender = trim((string)$this->gender);
1809
+		$this->login = trim((string) $this->login);
1810
+		$this->gender = trim((string) $this->gender);
1811 1811
 
1812
-		$this->pass = trim((string)$this->pass);
1813
-		$this->api_key = trim((string)$this->api_key);
1812
+		$this->pass = trim((string) $this->pass);
1813
+		$this->api_key = trim((string) $this->api_key);
1814 1814
 		$this->datestartvalidity = empty($this->datestartvalidity) ? '' : $this->datestartvalidity;
1815 1815
 		$this->dateendvalidity = empty($this->dateendvalidity) ? '' : $this->dateendvalidity;
1816 1816
 
1817
-		$this->address = trim((string)$this->address);
1818
-		$this->zip = trim((string)$this->zip);
1819
-		$this->town = trim((string)$this->town);
1817
+		$this->address = trim((string) $this->address);
1818
+		$this->zip = trim((string) $this->zip);
1819
+		$this->town = trim((string) $this->town);
1820 1820
 
1821 1821
 		$this->state_id = ($this->state_id > 0 ? $this->state_id : 0);
1822 1822
 		$this->country_id = ($this->country_id > 0 ? $this->country_id : 0);
1823
-		$this->office_phone = trim((string)$this->office_phone);
1824
-		$this->office_fax = trim((string)$this->office_fax);
1825
-		$this->user_mobile = trim((string)$this->user_mobile);
1826
-		$this->personal_mobile = trim((string)$this->personal_mobile);
1827
-		$this->email = trim((string)$this->email);
1828
-		$this->personal_email = trim((string)$this->personal_email);
1829
-
1830
-		$this->job = trim((string)$this->job);
1831
-		$this->signature = trim((string)$this->signature);
1832
-		$this->note_public = trim((string)$this->note_public);
1833
-		$this->note_private = trim((string)$this->note_private);
1834
-		$this->openid = trim((string)$this->openid);
1823
+		$this->office_phone = trim((string) $this->office_phone);
1824
+		$this->office_fax = trim((string) $this->office_fax);
1825
+		$this->user_mobile = trim((string) $this->user_mobile);
1826
+		$this->personal_mobile = trim((string) $this->personal_mobile);
1827
+		$this->email = trim((string) $this->email);
1828
+		$this->personal_email = trim((string) $this->personal_email);
1829
+
1830
+		$this->job = trim((string) $this->job);
1831
+		$this->signature = trim((string) $this->signature);
1832
+		$this->note_public = trim((string) $this->note_public);
1833
+		$this->note_private = trim((string) $this->note_private);
1834
+		$this->openid = trim((string) $this->openid);
1835 1835
 		$this->admin = ($this->admin > 0 ? $this->admin : 0);
1836 1836
 
1837
-		$this->accountancy_code = trim((string)$this->accountancy_code);
1838
-		$this->color = trim((string)$this->color);
1837
+		$this->accountancy_code = trim((string) $this->accountancy_code);
1838
+		$this->color = trim((string) $this->color);
1839 1839
 		$this->dateemployment = empty($this->dateemployment) ? '' : $this->dateemployment;
1840 1840
 		$this->dateemploymentend = empty($this->dateemploymentend) ? '' : $this->dateemploymentend;
1841 1841
 
1842 1842
 		$this->birth = empty($this->birth) ? '' : $this->birth;
1843
-		$this->fk_warehouse = (int)$this->fk_warehouse;
1843
+		$this->fk_warehouse = (int) $this->fk_warehouse;
1844 1844
 
1845 1845
 		$this->setUpperOrLowerCase();
1846 1846
 
@@ -1866,7 +1866,7 @@  discard block
 block discarded – undo
1866 1866
 
1867 1867
 		// Check if login already exists in same entity or into entity 0.
1868 1868
 		if (!empty($this->oldcopy) && $this->oldcopy->login != $this->login) {
1869
-			$sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND login = '" . $this->db->escape($this->login) . "'";
1869
+			$sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int) $this->entity) . ", 0) AND login = '" . $this->db->escape($this->login) . "'";
1870 1870
 			$resqltochecklogin = $this->db->query($sqltochecklogin);
1871 1871
 			if ($resqltochecklogin) {
1872 1872
 				$objtochecklogin = $this->db->fetch_object($resqltochecklogin);
@@ -1880,7 +1880,7 @@  discard block
 block discarded – undo
1880 1880
 			}
1881 1881
 		}
1882 1882
 		if (!empty($this->oldcopy) && !empty($this->email) && $this->oldcopy->email != $this->email) {
1883
-			$sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND email = '" . $this->db->escape($this->email) . "'";
1883
+			$sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int) $this->entity) . ", 0) AND email = '" . $this->db->escape($this->email) . "'";
1884 1884
 			$resqltochecklogin = $this->db->query($sqltochecklogin);
1885 1885
 			if ($resqltochecklogin) {
1886 1886
 				$objtochecklogin = $this->db->fetch_object($resqltochecklogin);
@@ -1901,13 +1901,13 @@  discard block
 block discarded – undo
1901 1901
 		$sql .= ", firstname = '" . $this->db->escape($this->firstname) . "'";
1902 1902
 		$sql .= ", ref_employee = '" . $this->db->escape($this->ref_employee) . "'";
1903 1903
 		$sql .= ", national_registration_number = '" . $this->db->escape($this->national_registration_number) . "'";
1904
-		$sql .= ", employee = " . (int)$this->employee;
1904
+		$sql .= ", employee = " . (int) $this->employee;
1905 1905
 		$sql .= ", login = '" . $this->db->escape($this->login) . "'";
1906 1906
 		$sql .= ", api_key = " . ($this->api_key ? "'" . $this->db->escape(dolEncrypt($this->api_key, '', '', 'dolibarr')) . "'" : "null");
1907 1907
 		$sql .= ", gender = " . ($this->gender != -1 ? "'" . $this->db->escape($this->gender) . "'" : "null"); // 'man' or 'woman'
1908 1908
 		$sql .= ", birth=" . (strval($this->birth) != '' ? "'" . $this->db->idate($this->birth, 'tzserver') . "'" : 'null');
1909 1909
 		if (!empty($user->admin)) {
1910
-			$sql .= ", admin = " . (int)$this->admin; // admin flag can be set/unset only by an admin user
1910
+			$sql .= ", admin = " . (int) $this->admin; // admin flag can be set/unset only by an admin user
1911 1911
 		}
1912 1912
 		$sql .= ", address = '" . $this->db->escape($this->address) . "'";
1913 1913
 		$sql .= ", zip = '" . $this->db->escape($this->zip) . "'";
@@ -1950,13 +1950,13 @@  discard block
 block discarded – undo
1950 1950
 		}
1951 1951
 		$sql .= ", weeklyhours= " . ($this->weeklyhours != '' ? "'" . $this->db->escape($this->weeklyhours) . "'" : "null");
1952 1952
 		if (!empty($user->admin) && empty($user->entity) && $user->id != $this->id) {
1953
-			$sql .= ", entity = " . ((int)$this->entity); // entity flag can be set/unset only by an another superadmin user
1953
+			$sql .= ", entity = " . ((int) $this->entity); // entity flag can be set/unset only by an another superadmin user
1954 1954
 		}
1955 1955
 		$sql .= ", default_range = " . ($this->default_range > 0 ? $this->default_range : 'null');
1956 1956
 		$sql .= ", default_c_exp_tax_cat = " . ($this->default_c_exp_tax_cat > 0 ? $this->default_c_exp_tax_cat : 'null');
1957 1957
 		$sql .= ", fk_warehouse = " . ($this->fk_warehouse > 0 ? $this->fk_warehouse : "null");
1958 1958
 		$sql .= ", lang = " . ($this->lang ? "'" . $this->db->escape($this->lang) . "'" : "null");
1959
-		$sql .= " WHERE rowid = " . ((int)$this->id);
1959
+		$sql .= " WHERE rowid = " . ((int) $this->id);
1960 1960
 
1961 1961
 		dol_syslog(get_class($this) . "::update", LOG_DEBUG);
1962 1962
 		$resql = $this->db->query($sql);
@@ -1977,7 +1977,7 @@  discard block
 block discarded – undo
1977 1977
 			// If user is linked to a member, remove old link to this member
1978 1978
 			if ($this->fk_member > 0) {
1979 1979
 				dol_syslog(get_class($this) . "::update remove link with member. We will recreate it later", LOG_DEBUG);
1980
-				$sql = "UPDATE " . $dbPrefix . "user SET fk_member = NULL where fk_member = " . ((int)$this->fk_member);
1980
+				$sql = "UPDATE " . $dbPrefix . "user SET fk_member = NULL where fk_member = " . ((int) $this->fk_member);
1981 1981
 				$resql = $this->db->query($sql);
1982 1982
 				if (!$resql) {
1983 1983
 					$this->error = $this->db->error();
@@ -1987,7 +1987,7 @@  discard block
 block discarded – undo
1987 1987
 			}
1988 1988
 			// Set link to user
1989 1989
 			dol_syslog(get_class($this) . "::update set link with member", LOG_DEBUG);
1990
-			$sql = "UPDATE " . $dbPrefix . "user SET fk_member =" . ($this->fk_member > 0 ? ((int)$this->fk_member) : 'null') . " where rowid = " . ((int)$this->id);
1990
+			$sql = "UPDATE " . $dbPrefix . "user SET fk_member =" . ($this->fk_member > 0 ? ((int) $this->fk_member) : 'null') . " where rowid = " . ((int) $this->id);
1991 1991
 			$resql = $this->db->query($sql);
1992 1992
 			if (!$resql) {
1993 1993
 				$this->error = $this->db->error();
@@ -2214,7 +2214,7 @@  discard block
 block discarded – undo
2214 2214
 			} else {
2215 2215
 				$sql .= ", pass = '" . $this->db->escape($password) . "'";
2216 2216
 			}
2217
-			$sql .= " WHERE rowid = " . ((int)$this->id);
2217
+			$sql .= " WHERE rowid = " . ((int) $this->id);
2218 2218
 
2219 2219
 			dol_syslog(get_class($this) . "::setPassword", LOG_DEBUG);
2220 2220
 			$result = $this->db->query($sql);
@@ -2272,7 +2272,7 @@  discard block
 block discarded – undo
2272 2272
 			// After receiving confirmation link, we will erase and store it in pass_crypted
2273 2273
 			$sql = "UPDATE " . $dbPrefix . "user";
2274 2274
 			$sql .= " SET pass_temp = '" . $this->db->escape($password) . "'";
2275
-			$sql .= " WHERE rowid = " . ((int)$this->id);
2275
+			$sql .= " WHERE rowid = " . ((int) $this->id);
2276 2276
 
2277 2277
 			dol_syslog(get_class($this) . "::setPassword", LOG_DEBUG); // No log
2278 2278
 			$result = $this->db->query($sql);
@@ -2349,7 +2349,7 @@  discard block
 block discarded – undo
2349 2349
 			} elseif (!empty($this->pass_crypted)) {    // If an encrypted password is already known, we save it directly into database because the previous create did not save it.
2350 2350
 				$sql = "UPDATE " . $dbPrefix . "user";
2351 2351
 				$sql .= " SET pass_crypted = '" . $this->db->escape($this->pass_crypted) . "'";
2352
-				$sql .= " WHERE rowid=" . ((int)$this->id);
2352
+				$sql .= " WHERE rowid=" . ((int) $this->id);
2353 2353
 
2354 2354
 				$resql = $this->db->query($sql);
2355 2355
 				if (!$resql) {
@@ -2359,8 +2359,8 @@  discard block
 block discarded – undo
2359 2359
 
2360 2360
 			if ($result > 0 && $member->socid) {    // If member is linked to a thirdparty
2361 2361
 				$sql = "UPDATE " . $dbPrefix . "user";
2362
-				$sql .= " SET fk_soc=" . ((int)$member->socid);
2363
-				$sql .= " WHERE rowid=" . ((int)$this->id);
2362
+				$sql .= " SET fk_soc=" . ((int) $member->socid);
2363
+				$sql .= " WHERE rowid=" . ((int) $this->id);
2364 2364
 
2365 2365
 				dol_syslog(get_class($this) . "::create_from_member", LOG_DEBUG);
2366 2366
 				$resql = $this->db->query($sql);
@@ -2411,7 +2411,7 @@  discard block
 block discarded – undo
2411 2411
 		$sql .= " datelastlogin = '" . $this->db->idate($now) . "',";
2412 2412
 		$sql .= " iplastlogin = '" . $this->db->escape($userremoteip) . "',";
2413 2413
 		$sql .= " tms = tms"; // The last update date must change because the last login date is updated
2414
-		$sql .= " WHERE rowid = " . ((int)$this->id);
2414
+		$sql .= " WHERE rowid = " . ((int) $this->id);
2415 2415
 
2416 2416
 		dol_syslog(get_class($this) . "::update_last_login_date user->id=" . $this->id . " " . $sql, LOG_DEBUG);
2417 2417
 		$resql = $this->db->query($sql);
@@ -2562,7 +2562,7 @@  discard block
 block discarded – undo
2562 2562
 		// phpcs:enable
2563 2563
 		$sql = "SELECT url, login, pass, poste ";
2564 2564
 		$sql .= " FROM " . $dbPrefix . "user_clicktodial as u";
2565
-		$sql .= " WHERE u.fk_user = " . ((int)$this->id);
2565
+		$sql .= " WHERE u.fk_user = " . ((int) $this->id);
2566 2566
 
2567 2567
 		$resql = $this->db->query($sql);
2568 2568
 		if ($resql) {
@@ -2601,7 +2601,7 @@  discard block
 block discarded – undo
2601 2601
 		$this->db->begin();
2602 2602
 
2603 2603
 		$sql = "DELETE FROM " . $dbPrefix . "user_clicktodial";
2604
-		$sql .= " WHERE fk_user = " . ((int)$this->id);
2604
+		$sql .= " WHERE fk_user = " . ((int) $this->id);
2605 2605
 
2606 2606
 		dol_syslog(get_class($this) . '::update_clicktodial', LOG_DEBUG);
2607 2607
 		$result = $this->db->query($sql);
@@ -2647,14 +2647,14 @@  discard block
 block discarded – undo
2647 2647
 		$this->db->begin();
2648 2648
 
2649 2649
 		$sql = "DELETE FROM " . $dbPrefix . "usergroup_user";
2650
-		$sql .= " WHERE fk_user  = " . ((int)$this->id);
2651
-		$sql .= " AND fk_usergroup = " . ((int)$group);
2652
-		$sql .= " AND entity = " . ((int)$entity);
2650
+		$sql .= " WHERE fk_user  = " . ((int) $this->id);
2651
+		$sql .= " AND fk_usergroup = " . ((int) $group);
2652
+		$sql .= " AND entity = " . ((int) $entity);
2653 2653
 
2654 2654
 		$result = $this->db->query($sql);
2655 2655
 
2656 2656
 		$sql = "INSERT INTO " . $dbPrefix . "usergroup_user (entity, fk_user, fk_usergroup)";
2657
-		$sql .= " VALUES (" . ((int)$entity) . "," . ((int)$this->id) . "," . ((int)$group) . ")";
2657
+		$sql .= " VALUES (" . ((int) $entity) . "," . ((int) $this->id) . "," . ((int) $group) . ")";
2658 2658
 
2659 2659
 		$result = $this->db->query($sql);
2660 2660
 		if ($result) {
@@ -2703,12 +2703,12 @@  discard block
 block discarded – undo
2703 2703
 		$this->db->begin();
2704 2704
 
2705 2705
 		$sql = "DELETE FROM " . $dbPrefix . "usergroup_user";
2706
-		$sql .= " WHERE fk_user  = " . ((int)$this->id);
2707
-		$sql .= " AND fk_usergroup = " . ((int)$group);
2706
+		$sql .= " WHERE fk_user  = " . ((int) $this->id);
2707
+		$sql .= " AND fk_usergroup = " . ((int) $group);
2708 2708
 		if (empty($entity)) {
2709
-			$sql .= " AND entity IN (0, 1)";    // group may be in entity 0 (so $entity=0) and link with user into entity 1.
2709
+			$sql .= " AND entity IN (0, 1)"; // group may be in entity 0 (so $entity=0) and link with user into entity 1.
2710 2710
 		} else {
2711
-			$sql .= " AND entity = " . ((int)$entity);
2711
+			$sql .= " AND entity = " . ((int) $entity);
2712 2712
 		}
2713 2713
 
2714 2714
 		$result = $this->db->query($sql);
@@ -2894,7 +2894,7 @@  discard block
 block discarded – undo
2894 2894
 	 */
2895 2895
 	public function getLibStatut($mode = 0)
2896 2896
 	{
2897
-		return $this->LibStatut(isset($this->statut) ? (int)$this->statut : (int)$this->status, $mode);
2897
+		return $this->LibStatut(isset($this->statut) ? (int) $this->statut : (int) $this->status, $mode);
2898 2898
 	}
2899 2899
 
2900 2900
 	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
@@ -3046,7 +3046,7 @@  discard block
 block discarded – undo
3046 3046
 		$result .= (($option == 'nolink') ? '' : $linkstart);
3047 3047
 		if ($withpictoimg) {
3048 3048
 			$paddafterimage = '';
3049
-			if (abs((int)$withpictoimg) == 1) {
3049
+			if (abs((int) $withpictoimg) == 1) {
3050 3050
 				$paddafterimage = 'style="margin-' . ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right') . ': 3px;"';
3051 3051
 			}
3052 3052
 			// Only picto
@@ -3176,7 +3176,7 @@  discard block
 block discarded – undo
3176 3176
 		// Special case for external user
3177 3177
 		if (!empty($this->socid)) {
3178 3178
 			if ($module = 'societe' && $permlevel1 = 'client' && $permlevel2 == 'voir') {
3179
-				return 0;   // An external user never has the permission ->societe->client->voir to see all thirdparties (always restricted to himself)
3179
+				return 0; // An external user never has the permission ->societe->client->voir to see all thirdparties (always restricted to himself)
3180 3180
 			}
3181 3181
 		}
3182 3182
 
@@ -3269,7 +3269,7 @@  discard block
 block discarded – undo
3269 3269
 
3270 3270
 		if ($option == 'xxx') {
3271 3271
 			$linkstart = '<a href="' . DOL_URL_ROOT . '/user/card.php?id=' . $this->id . '">';
3272
-			$linkend = '</a>';  // @phan-suppress-current-line PhanPluginRedundantAssignment
3272
+			$linkend = '</a>'; // @phan-suppress-current-line PhanPluginRedundantAssignment
3273 3273
 		}
3274 3274
 
3275 3275
 		if ($option == 'nolink') {
@@ -3586,7 +3586,7 @@  discard block
 block discarded – undo
3586 3586
 		$this->iplastlogin = '127.0.0.1';
3587 3587
 		$this->datepreviouslogin = $now;
3588 3588
 		$this->ippreviouslogin = '127.0.0.1';
3589
-		$this->statut = 1;      // deprecated
3589
+		$this->statut = 1; // deprecated
3590 3590
 		$this->status = 1;
3591 3591
 
3592 3592
 		$this->entity = 1;
@@ -3608,7 +3608,7 @@  discard block
 block discarded – undo
3608 3608
 		$sql = "SELECT u.rowid, u.login as ref, u.datec,";
3609 3609
 		$sql .= " u.tms as date_modification, u.entity";
3610 3610
 		$sql .= " FROM " . $dbPrefix . "user as u";
3611
-		$sql .= " WHERE u.rowid = " . ((int)$id);
3611
+		$sql .= " WHERE u.rowid = " . ((int) $id);
3612 3612
 
3613 3613
 		$result = $this->db->query($sql);
3614 3614
 		if ($result) {
@@ -3681,13 +3681,13 @@  discard block
 block discarded – undo
3681 3681
 			}
3682 3682
 		}
3683 3683
 		if ($admin >= 0) {
3684
-			$sql .= " AND admin = " . (int)$admin;
3684
+			$sql .= " AND admin = " . (int) $admin;
3685 3685
 		}
3686 3686
 
3687 3687
 		$resql = $this->db->query($sql);
3688 3688
 		if ($resql) {
3689 3689
 			$obj = $this->db->fetch_object($resql);
3690
-			$nb = (int)$obj->nb;
3690
+			$nb = (int) $obj->nb;
3691 3691
 
3692 3692
 			$this->db->free($resql);
3693 3693
 			return $nb;
@@ -3762,7 +3762,7 @@  discard block
 block discarded – undo
3762 3762
 	{
3763 3763
 		// phpcs:enable
3764 3764
 		$sql = "SELECT rowid FROM " . $dbPrefix . "user";
3765
-		$sql .= " WHERE fk_user = " . ((int)$this->id);
3765
+		$sql .= " WHERE fk_user = " . ((int) $this->id);
3766 3766
 
3767 3767
 		dol_syslog(get_class($this) . "::get_children", LOG_DEBUG);
3768 3768
 		$res = $this->db->query($sql);
@@ -4098,7 +4098,7 @@  discard block
 block discarded – undo
4098 4098
 
4099 4099
 		$sql = "SELECT rowid, email, user_mobile, civility, lastname, firstname";
4100 4100
 		$sql .= " FROM " . $dbPrefix . "user";
4101
-		$sql .= " WHERE rowid = " . ((int)$rowid);
4101
+		$sql .= " WHERE rowid = " . ((int) $rowid);
4102 4102
 
4103 4103
 		$resql = $this->db->query($sql);
4104 4104
 		if ($resql) {
@@ -4138,7 +4138,7 @@  discard block
 block discarded – undo
4138 4138
 
4139 4139
 		$encodedsecurekey = dol_hash($conf->file->instance_unique_id . 'uservirtualcard' . $this->id . '-' . $this->login, 'md5');
4140 4140
 		if (isModEnabled('multicompany')) {
4141
-			$entity_qr = '&entity=' . ((int)$conf->entity);
4141
+			$entity_qr = '&entity=' . ((int) $conf->entity);
4142 4142
 		} else {
4143 4143
 			$entity_qr = '';
4144 4144
 		}
@@ -4192,7 +4192,7 @@  discard block
 block discarded – undo
4192 4192
 			return -1;
4193 4193
 		}
4194 4194
 
4195
-		$this->findUserIdByEmailCache[$email] = (int)$obj->rowid;
4195
+		$this->findUserIdByEmailCache[$email] = (int) $obj->rowid;
4196 4196
 
4197 4197
 		return $this->findUserIdByEmailCache[$email];
4198 4198
 	}
Please login to merge, or discard this patch.
Dolibarr/Core/Base/Config.php 1 patch
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -165,8 +165,8 @@  discard block
 block discarded – undo
165 165
         $conf->file->main_force_https = empty($dolibarr_main_force_https) ? '' : $dolibarr_main_force_https; // Force https
166 166
         $conf->file->strict_mode = empty($dolibarr_strict_mode) ? '' : $dolibarr_strict_mode; // Force php strict mode (for debug)
167 167
         $conf->file->instance_unique_id = empty($dolibarr_main_instance_unique_id) ? (empty($dolibarr_main_cookie_cryptkey) ? '' : $dolibarr_main_cookie_cryptkey) : $dolibarr_main_instance_unique_id; // Unique id of instance
168
-        $conf->file->main_path = $dolibarr_main_document_root ?? BASE_PATH;  // Define htdocs path inside the config file
169
-        $conf->file->main_url = $dolibarr_main_url_root ?? BASE_URL;    // Define url inside the config file
168
+        $conf->file->main_path = $dolibarr_main_document_root ?? BASE_PATH; // Define htdocs path inside the config file
169
+        $conf->file->main_url = $dolibarr_main_url_root ?? BASE_URL; // Define url inside the config file
170 170
         $conf->file->main_doc = $dolibarr_main_data_root ?? static::getDataDir($conf->file->main_path);
171 171
         $conf->file->path = ['main' => $conf->file->main_path];
172 172
         $conf->file->url = ['main' => '/'];
@@ -181,7 +181,7 @@  discard block
 block discarded – undo
181 181
 
182 182
             $i = 0;
183 183
             foreach ($path as $value) {
184
-                $conf->file->path['alt' . ($i++)] = (string)$value;
184
+                $conf->file->path['alt' . ($i++)] = (string) $value;
185 185
             }
186 186
             $values = preg_split('/[;,]/', $dolibarr_main_url_root_alt);
187 187
             $i = 0;
@@ -201,7 +201,7 @@  discard block
 block discarded – undo
201 201
                     print "\"/custom\"<br>\n";
202 202
                     exit;
203 203
                 }
204
-                $conf->file->url['alt' . ($i++)] = (string)$value;
204
+                $conf->file->url['alt' . ($i++)] = (string) $value;
205 205
             }
206 206
         }
207 207
 
@@ -443,7 +443,7 @@  discard block
 block discarded – undo
443 443
             }
444 444
             if (!class_exists('MenuManager')) {
445 445
                 $menufound = 0;
446
-                $dirmenus = array_merge(["/core/menus/"], (array)$conf->modules_parts['menus']);
446
+                $dirmenus = array_merge(["/core/menus/"], (array) $conf->modules_parts['menus']);
447 447
                 foreach ($dirmenus as $dirmenu) {
448 448
                     $menufound = dol_include_once($dirmenu . "standard/" . $file_menu);
449 449
                     if (class_exists('MenuManager')) {
@@ -510,7 +510,7 @@  discard block
 block discarded – undo
510 510
     private static function loadDb()
511 511
     {
512 512
         $conf = static::$dolibarrConfig;
513
-        static::$db = getDoliDBInstance($conf->db->type, $conf->db->host, $conf->db->user, $conf->db->pass, $conf->db->name, (int)$conf->db->port);
513
+        static::$db = getDoliDBInstance($conf->db->type, $conf->db->host, $conf->db->user, $conf->db->pass, $conf->db->name, (int) $conf->db->port);
514 514
         static::$dolibarrConfig->setValues(static::$db);
515 515
 
516 516
         return static::$db;
Please login to merge, or discard this patch.
htdocs/master.inc.php 1 patch
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -89,15 +89,15 @@  discard block
 block discarded – undo
89 89
 $conf->file->main_force_https = empty($dolibarr_main_force_https) ? '' : $dolibarr_main_force_https; // Force https
90 90
 $conf->file->strict_mode = empty($dolibarr_strict_mode) ? '' : $dolibarr_strict_mode; // Force php strict mode (for debug)
91 91
 $conf->file->instance_unique_id = empty($dolibarr_main_instance_unique_id) ? (empty($dolibarr_main_cookie_cryptkey) ? '' : $dolibarr_main_cookie_cryptkey) : $dolibarr_main_instance_unique_id; // Unique id of instance
92
-$conf->file->dol_main_url_root = $dolibarr_main_url_root;   // Define url inside the config file
93
-$conf->file->dol_document_root = array('main' => (string)DOL_DOCUMENT_ROOT); // Define array of document root directories ('/home/htdocs')
94
-$conf->file->dol_url_root = array('main' => (string)DOL_URL_ROOT); // Define array of url root path ('' or '/dolibarr')
92
+$conf->file->dol_main_url_root = $dolibarr_main_url_root; // Define url inside the config file
93
+$conf->file->dol_document_root = array('main' => (string) DOL_DOCUMENT_ROOT); // Define array of document root directories ('/home/htdocs')
94
+$conf->file->dol_url_root = array('main' => (string) DOL_URL_ROOT); // Define array of url root path ('' or '/dolibarr')
95 95
 if (!empty($dolibarr_main_document_root_alt)) {
96 96
     // dolibarr_main_document_root_alt can contains several directories
97 97
     $values = preg_split('/[;,]/', $dolibarr_main_document_root_alt);
98 98
     $i = 0;
99 99
     foreach ($values as $value) {
100
-        $conf->file->dol_document_root['alt' . ($i++)] = (string)$value;
100
+        $conf->file->dol_document_root['alt' . ($i++)] = (string) $value;
101 101
     }
102 102
     $values = preg_split('/[;,]/', $dolibarr_main_url_root_alt);
103 103
     $i = 0;
@@ -117,7 +117,7 @@  discard block
 block discarded – undo
117 117
             print "\"/custom\"<br>\n";
118 118
             exit;
119 119
         }
120
-        $conf->file->dol_url_root['alt' . ($i++)] = (string)$value;
120
+        $conf->file->dol_url_root['alt' . ($i++)] = (string) $value;
121 121
     }
122 122
 }
123 123
 
@@ -138,7 +138,7 @@  discard block
 block discarded – undo
138 138
  */
139 139
 //$db = null;
140 140
 if (!isset($db) && !defined('NOREQUIREDB')) {
141
-    $db = getDoliDBInstance($conf->db->type, $conf->db->host, $conf->db->user, $conf->db->pass, $conf->db->name, (int)$conf->db->port);
141
+    $db = getDoliDBInstance($conf->db->type, $conf->db->host, $conf->db->user, $conf->db->pass, $conf->db->name, (int) $conf->db->port);
142 142
 
143 143
     if ($db->error) {
144 144
         // If we were into a website context
Please login to merge, or discard this patch.
htdocs/index_dol.php 1 patch
Indentation   +668 added lines, -668 removed lines patch added patch discarded remove patch
@@ -68,24 +68,24 @@  discard block
 block discarded – undo
68 68
 
69 69
 // Check if company name is defined (first install)
70 70
 if (!isset($conf->global->MAIN_INFO_SOCIETE_NOM) || !getDolGlobalString('MAIN_INFO_SOCIETE_NOM')) {
71
-	header("Location: " . DOL_URL_ROOT . "/admin/index.php?mainmenu=home&leftmenu=setup&mesg=setupnotcomplete");
72
-	exit;
71
+    header("Location: " . DOL_URL_ROOT . "/admin/index.php?mainmenu=home&leftmenu=setup&mesg=setupnotcomplete");
72
+    exit;
73 73
 }
74 74
 if ($nbmodulesnotautoenabled <= getDolGlobalString('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) {  // If only user module enabled
75
-	header("Location: " . DOL_URL_ROOT . "/admin/index.php?mainmenu=home&leftmenu=setup&mesg=setupnotcomplete");
76
-	exit;
75
+    header("Location: " . DOL_URL_ROOT . "/admin/index.php?mainmenu=home&leftmenu=setup&mesg=setupnotcomplete");
76
+    exit;
77 77
 }
78 78
 if (GETPOST('addbox')) {    // Add box (when submit is done from a form when ajax disabled)
79
-	require_once DOL_DOCUMENT_ROOT . '/core/class/infobox.class.php';
80
-	$zone = GETPOSTINT('areacode');
81
-	$userid = GETPOSTINT('userid');
82
-	$boxorder = GETPOST('boxorder', 'aZ09');
83
-	$boxorder .= GETPOST('boxcombo', 'aZ09');
84
-
85
-	$result = InfoBox::saveboxorder($db, $zone, $boxorder, $userid);
86
-	if ($result > 0) {
87
-		setEventMessages($langs->trans("BoxAdded"), null);
88
-	}
79
+    require_once DOL_DOCUMENT_ROOT . '/core/class/infobox.class.php';
80
+    $zone = GETPOSTINT('areacode');
81
+    $userid = GETPOSTINT('userid');
82
+    $boxorder = GETPOST('boxorder', 'aZ09');
83
+    $boxorder .= GETPOST('boxcombo', 'aZ09');
84
+
85
+    $result = InfoBox::saveboxorder($db, $zone, $boxorder, $userid);
86
+    if ($result > 0) {
87
+        setEventMessages($langs->trans("BoxAdded"), null);
88
+    }
89 89
 }
90 90
 
91 91
 /*
@@ -93,13 +93,13 @@  discard block
 block discarded – undo
93 93
  */
94 94
 
95 95
 if (!isset($form) || !is_object($form)) {
96
-	$form = new Form($db);
96
+    $form = new Form($db);
97 97
 }
98 98
 
99 99
 // Title
100 100
 $title = $langs->trans("HomeArea") . ' - Dolibarr ' . DOL_VERSION;
101 101
 if (getDolGlobalString('MAIN_APPLICATION_TITLE')) {
102
-	$title = $langs->trans("HomeArea") . ' - ' . getDolGlobalString('MAIN_APPLICATION_TITLE');
102
+    $title = $langs->trans("HomeArea") . ' - ' . getDolGlobalString('MAIN_APPLICATION_TITLE');
103 103
 }
104 104
 
105 105
 llxHeader('', $title);
@@ -109,18 +109,18 @@  discard block
 block discarded – undo
109 109
 print load_fiche_titre('&nbsp;', $resultboxes['selectboxlist'], '', 0, '', 'titleforhome');
110 110
 
111 111
 if (getDolGlobalString('MAIN_MOTD')) {
112
-	$conf->global->MAIN_MOTD = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', '<br>', getDolGlobalString('MAIN_MOTD'));
113
-	if (getDolGlobalString('MAIN_MOTD')) {
114
-		$substitutionarray = getCommonSubstitutionArray($langs);
115
-		complete_substitutions_array($substitutionarray, $langs);
116
-		$texttoshow = make_substitutions(getDolGlobalString('MAIN_MOTD'), $substitutionarray, $langs);
117
-
118
-		print "\n<!-- Start of welcome text -->\n";
119
-		print '<table class="centpercent notopnoleftnoright"><tr><td>';
120
-		print dol_htmlentitiesbr($texttoshow);
121
-		print '</td></tr></table><br>';
122
-		print "\n<!-- End of welcome text -->\n";
123
-	}
112
+    $conf->global->MAIN_MOTD = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', '<br>', getDolGlobalString('MAIN_MOTD'));
113
+    if (getDolGlobalString('MAIN_MOTD')) {
114
+        $substitutionarray = getCommonSubstitutionArray($langs);
115
+        complete_substitutions_array($substitutionarray, $langs);
116
+        $texttoshow = make_substitutions(getDolGlobalString('MAIN_MOTD'), $substitutionarray, $langs);
117
+
118
+        print "\n<!-- Start of welcome text -->\n";
119
+        print '<table class="centpercent notopnoleftnoright"><tr><td>';
120
+        print dol_htmlentitiesbr($texttoshow);
121
+        print '</td></tr></table><br>';
122
+        print "\n<!-- End of welcome text -->\n";
123
+    }
124 124
 }
125 125
 
126 126
 /*
@@ -129,35 +129,35 @@  discard block
 block discarded – undo
129 129
 
130 130
 // Security warning if install.lock file is missing or if conf file is writable
131 131
 if (!getDolGlobalString('MAIN_REMOVE_INSTALL_WARNING')) {
132
-	$message = '';
133
-
134
-	// Check if install lock file is present
135
-	$lockfile = DOL_DATA_ROOT . '/install.lock';
136
-	if (!empty($lockfile) && !file_exists($lockfile) && is_dir(DOL_DOCUMENT_ROOT . "/install")) {
137
-		$langs->load("errors");
138
-		//if (!empty($message)) $message.='<br>';
139
-		$message .= info_admin($langs->transnoentities("WarningLockFileDoesNotExists", DOL_DATA_ROOT) . ' ' . $langs->transnoentities("WarningUntilDirRemoved", DOL_DOCUMENT_ROOT . "/install"), 0, 0, '1', 'clearboth');
140
-	}
141
-
142
-	// Conf files must be in read only mode
143
-	if (is_writable($conffile)) {   // $conffile is defined into filefunc.inc.php
144
-		$langs->load("errors");
145
-		//$langs->load("other");
146
-		//if (!empty($message)) $message.='<br>';
147
-		$message .= info_admin($langs->transnoentities("WarningConfFileMustBeReadOnly") . ' ' . $langs->transnoentities("WarningUntilDirRemoved", DOL_DOCUMENT_ROOT . "/install"), 0, 0, '1', 'clearboth');
148
-	}
149
-
150
-	$object = new stdClass();
151
-	$parameters = array();
152
-	$reshook = $hookmanager->executeHooks('infoadmin', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
153
-	if ($reshook == 0) {
154
-		$message .= $hookmanager->resPrint;
155
-	}
156
-	if ($message) { // $message is an HTML string.
157
-		print dol_string_onlythesehtmltags($message, 1, 0, 0, 0, array('div', 'span', 'b'));
158
-		print '<br>';
159
-		//print info_admin($langs->trans("WarningUntilDirRemoved",DOL_DOCUMENT_ROOT."/install"));
160
-	}
132
+    $message = '';
133
+
134
+    // Check if install lock file is present
135
+    $lockfile = DOL_DATA_ROOT . '/install.lock';
136
+    if (!empty($lockfile) && !file_exists($lockfile) && is_dir(DOL_DOCUMENT_ROOT . "/install")) {
137
+        $langs->load("errors");
138
+        //if (!empty($message)) $message.='<br>';
139
+        $message .= info_admin($langs->transnoentities("WarningLockFileDoesNotExists", DOL_DATA_ROOT) . ' ' . $langs->transnoentities("WarningUntilDirRemoved", DOL_DOCUMENT_ROOT . "/install"), 0, 0, '1', 'clearboth');
140
+    }
141
+
142
+    // Conf files must be in read only mode
143
+    if (is_writable($conffile)) {   // $conffile is defined into filefunc.inc.php
144
+        $langs->load("errors");
145
+        //$langs->load("other");
146
+        //if (!empty($message)) $message.='<br>';
147
+        $message .= info_admin($langs->transnoentities("WarningConfFileMustBeReadOnly") . ' ' . $langs->transnoentities("WarningUntilDirRemoved", DOL_DOCUMENT_ROOT . "/install"), 0, 0, '1', 'clearboth');
148
+    }
149
+
150
+    $object = new stdClass();
151
+    $parameters = array();
152
+    $reshook = $hookmanager->executeHooks('infoadmin', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
153
+    if ($reshook == 0) {
154
+        $message .= $hookmanager->resPrint;
155
+    }
156
+    if ($message) { // $message is an HTML string.
157
+        print dol_string_onlythesehtmltags($message, 1, 0, 0, 0, array('div', 'span', 'b'));
158
+        print '<br>';
159
+        //print info_admin($langs->trans("WarningUntilDirRemoved",DOL_DOCUMENT_ROOT."/install"));
160
+    }
161 161
 }
162 162
 
163 163
 /*
@@ -173,577 +173,577 @@  discard block
 block discarded – undo
173 173
 
174 174
 // Dolibarr Working Board with weather
175 175
 if (!getDolGlobalString('MAIN_DISABLE_GLOBAL_WORKBOARD') && getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
176
-	$showweather = (!getDolGlobalString('MAIN_DISABLE_METEO') || getDolGlobalInt('MAIN_DISABLE_METEO') == 2) ? 1 : 0;
177
-
178
-	//Array that contains all WorkboardResponse classes to process them
179
-	$dashboardlines = array();
180
-
181
-	// Number of actions to do (late)
182
-	if (isModEnabled('agenda') && !getDolGlobalString('MAIN_DISABLE_BLOCK_AGENDA') && $user->hasRight('agenda', 'myactions', 'read')) {
183
-		$board = new ActionComm($db);
184
-		$dashboardlines[$board->element] = $board->load_board($user);
185
-	}
186
-
187
-	// Number of project opened
188
-	if (isModEnabled('project') && !getDolGlobalString('MAIN_DISABLE_BLOCK_PROJECT') && $user->hasRight('projet', 'lire')) {
189
-		$board = new Project($db);
190
-		$dashboardlines[$board->element] = $board->load_board($user);
191
-	}
192
-
193
-	// Number of tasks to do (late)
194
-	if (isModEnabled('project') && !getDolGlobalString('MAIN_DISABLE_BLOCK_PROJECT') && !getDolGlobalString('PROJECT_HIDE_TASKS') && $user->hasRight('projet', 'lire')) {
195
-		$board = new Task($db);
196
-		$dashboardlines[$board->element] = $board->load_board($user);
197
-	}
198
-
199
-	// Number of commercial customer proposals open (expired)
200
-	if (isModEnabled('propal') && !getDolGlobalString('MAIN_DISABLE_BLOCK_CUSTOMER') && $user->hasRight('propal', 'read')) {
201
-		$board = new Propal($db);
202
-		$dashboardlines[$board->element . '_opened'] = $board->load_board($user, "opened");
203
-		// Number of commercial proposals CLOSED signed (billed)
204
-		$dashboardlines[$board->element . '_signed'] = $board->load_board($user, "signed");
205
-	}
206
-
207
-	// Number of supplier proposals open (expired)
208
-	if (isModEnabled('supplier_proposal') && !getDolGlobalString('MAIN_DISABLE_BLOCK_SUPPLIER') && $user->hasRight('supplier_proposal', 'lire')) {
209
-		$langs->load("supplier_proposal");
210
-		$board = new SupplierProposal($db);
211
-		$dashboardlines[$board->element . '_opened'] = $board->load_board($user, "opened");
212
-		// Number of commercial proposals CLOSED signed (billed)
213
-		$dashboardlines[$board->element . '_signed'] = $board->load_board($user, "signed");
214
-	}
215
-
216
-	// Number of sales orders
217
-	if (isModEnabled('order') && !getDolGlobalString('MAIN_DISABLE_BLOCK_CUSTOMER') && $user->hasRight('commande', 'lire')) {
218
-		$board = new Commande($db);
219
-		// Number of customer orders to be shipped (validated and in progress)
220
-		$dashboardlines[$board->element . '_toship'] = $board->load_board($user, 'toship');
221
-		// Number of customer orders to be billed (not visible by default, does not match a lot of organization).
222
-		if (getDolGlobalInt('ORDER_BILL_AFTER_VALIDATION')) {
223
-			$dashboardlines[$board->element . '_tobill'] = $board->load_board($user, 'tobill');
224
-		}
225
-		// Number of customer orders to be billed (delivered but not billed)
226
-		$dashboardlines[$board->element . '_shippedtobill'] = $board->load_board($user, 'shippedtobill');
227
-	}
228
-
229
-	// Number of suppliers orders
230
-	if (isModEnabled('supplier_order') && !getDolGlobalString('MAIN_DISABLE_BLOCK_SUPPLIER') && $user->hasRight('fournisseur', 'commande', 'lire')) {
231
-		$board = new CommandeFournisseur($db);
232
-		$dashboardlines[$board->element . '_opened'] = $board->load_board($user, "opened");
233
-		$dashboardlines[$board->element . '_awaiting'] = $board->load_board($user, 'awaiting');
234
-	}
235
-
236
-	// Number of contract / services enabled (delayed)
237
-	if (isModEnabled('contract') && !getDolGlobalString('MAIN_DISABLE_BLOCK_CONTRACT') && $user->hasRight('contrat', 'lire')) {
238
-		$board = new Contrat($db);
239
-		$dashboardlines[$board->element . '_inactive'] = $board->load_board($user, "inactive");
240
-		// Number of active services (expired)
241
-		$dashboardlines[$board->element . '_active'] = $board->load_board($user, "active");
242
-	}
243
-
244
-	// Number of tickets open
245
-	if (isModEnabled('ticket') && !getDolGlobalString('MAIN_DISABLE_BLOCK_TICKET') && $user->hasRight('ticket', 'read')) {
246
-		$board = new Ticket($db);
247
-		$dashboardlines[$board->element . '_opened'] = $board->load_board($user, "opened");
248
-		// Number of active services (expired)
249
-		//$dashboardlines[$board->element.'_active'] = $board->load_board($user, "active");
250
-	}
251
-
252
-	// Number of invoices customers (paid)
253
-	if (isModEnabled('invoice') && !getDolGlobalString('MAIN_DISABLE_BLOCK_CUSTOMER') && $user->hasRight('facture', 'lire')) {
254
-		$board = new Facture($db);
255
-		$dashboardlines[$board->element] = $board->load_board($user);
256
-	}
257
-
258
-	// Number of supplier invoices (paid)
259
-	if (isModEnabled('supplier_invoice') && !getDolGlobalString('MAIN_DISABLE_BLOCK_SUPPLIER') && $user->hasRight('fournisseur', 'facture', 'lire')) {
260
-		$board = new FactureFournisseur($db);
261
-		$dashboardlines[$board->element] = $board->load_board($user);
262
-	}
263
-
264
-	// Number of transactions to conciliate
265
-	if (isModEnabled('bank') && !getDolGlobalString('MAIN_DISABLE_BLOCK_BANK') && $user->hasRight('banque', 'lire') && !$user->socid) {
266
-		$board = new Account($db);
267
-		$nb = $board->countAccountToReconcile(); // Get nb of account to reconciliate
268
-		if ($nb > 0) {
269
-			$dashboardlines[$board->element] = $board->load_board($user);
270
-		}
271
-	}
272
-
273
-
274
-	// Number of cheque to send
275
-	if (isModEnabled('bank') && !getDolGlobalString('MAIN_DISABLE_BLOCK_BANK') && $user->hasRight('banque', 'lire') && !$user->socid) {
276
-		if (!getDolGlobalString('BANK_DISABLE_CHECK_DEPOSIT')) {
277
-			include_once DOL_DOCUMENT_ROOT . '/compta/paiement/cheque/class/remisecheque.class.php';
278
-			$board = new RemiseCheque($db);
279
-			$dashboardlines[$board->element] = $board->load_board($user);
280
-		}
281
-		if (isModEnabled('prelevement')) {
282
-			include_once DOL_DOCUMENT_ROOT . '/compta/prelevement/class/bonprelevement.class.php';
283
-			$board = new BonPrelevement($db);
284
-			$dashboardlines[$board->element . '_direct_debit'] = $board->load_board($user, 'direct_debit');
285
-		}
286
-		if (isModEnabled('paymentbybanktransfer')) {
287
-			include_once DOL_DOCUMENT_ROOT . '/compta/prelevement/class/bonprelevement.class.php';
288
-			$board = new BonPrelevement($db);
289
-			$dashboardlines[$board->element . '_credit_transfer'] = $board->load_board($user, 'credit_transfer');
290
-		}
291
-	}
292
-
293
-	// Number of foundation members
294
-	if (isModEnabled('member') && !getDolGlobalString('MAIN_DISABLE_BLOCK_ADHERENT') && $user->hasRight('adherent', 'lire') && !$user->socid) {
295
-		$board = new Adherent($db);
296
-		$dashboardlines[$board->element . '_shift'] = $board->load_board($user, 'shift');
297
-		$dashboardlines[$board->element . '_expired'] = $board->load_board($user, 'expired');
298
-	}
299
-
300
-	// Number of expense reports to approve
301
-	if (isModEnabled('expensereport') && !getDolGlobalString('MAIN_DISABLE_BLOCK_EXPENSEREPORT') && $user->hasRight('expensereport', 'approve')) {
302
-		$board = new ExpenseReport($db);
303
-		$dashboardlines[$board->element . '_toapprove'] = $board->load_board($user, 'toapprove');
304
-	}
305
-
306
-	// Number of expense reports to pay
307
-	if (isModEnabled('expensereport') && !getDolGlobalString('MAIN_DISABLE_BLOCK_EXPENSEREPORT') && $user->hasRight('expensereport', 'to_paid')) {
308
-		$board = new ExpenseReport($db);
309
-		$dashboardlines[$board->element . '_topay'] = $board->load_board($user, 'topay');
310
-	}
311
-
312
-	// Number of holidays to approve
313
-	if (isModEnabled('holiday') && !getDolGlobalString('MAIN_DISABLE_BLOCK_HOLIDAY') && $user->hasRight('holiday', 'approve')) {
314
-		$board = new Holiday($db);
315
-		$dashboardlines[$board->element] = $board->load_board($user);
316
-	}
317
-
318
-	$object = new stdClass();
319
-	$parameters = array();
320
-	$action = '';
321
-	$reshook = $hookmanager->executeHooks(
322
-		'addOpenElementsDashboardLine',
323
-		$parameters,
324
-		$object,
325
-		$action
326
-	); // Note that $action and $object may have been modified by some hooks
327
-	if ($reshook == 0) {
328
-		$dashboardlines = array_merge($dashboardlines, $hookmanager->resArray);
329
-	}
330
-
331
-	/* Open object dashboard */
332
-	$dashboardgroup = array(
333
-		'action' =>
334
-			array(
335
-				'groupName' => 'Agenda',
336
-				'stats' => array('action'),
337
-			),
338
-		'project' =>
339
-			array(
340
-				'groupName' => 'Projects',
341
-				'globalStatsKey' => 'projects',
342
-				'stats' => array('project', 'project_task'),
343
-			),
344
-		'propal' =>
345
-			array(
346
-				'groupName' => 'Proposals',
347
-				'globalStatsKey' => 'proposals',
348
-				'stats' =>
349
-					array('propal_opened', 'propal_signed'),
350
-			),
351
-		'commande' =>
352
-			array(
353
-				'groupName' => 'Orders',
354
-				'globalStatsKey' => 'orders',
355
-				'stats' =>
356
-					array('commande_toship', 'commande_tobill', 'commande_shippedtobill'),
357
-			),
358
-		'facture' =>
359
-			array(
360
-				'groupName' => 'Invoices',
361
-				'globalStatsKey' => 'invoices',
362
-				'stats' =>
363
-					array('facture'),
364
-			),
365
-		'supplier_proposal' =>
366
-			array(
367
-				'lang' => 'supplier_proposal',
368
-				'groupName' => 'SupplierProposals',
369
-				'globalStatsKey' => 'askprice',
370
-				'stats' =>
371
-					array('supplier_proposal_opened', 'supplier_proposal_signed'),
372
-			),
373
-		'order_supplier' =>
374
-			array(
375
-				'groupName' => 'SuppliersOrders',
376
-				'globalStatsKey' => 'supplier_orders',
377
-				'stats' =>
378
-					array('order_supplier_opened', 'order_supplier_awaiting'),
379
-			),
380
-		'invoice_supplier' =>
381
-			array(
382
-				'groupName' => 'BillsSuppliers',
383
-				'globalStatsKey' => 'supplier_invoices',
384
-				'stats' =>
385
-					array('invoice_supplier'),
386
-			),
387
-		'contrat' =>
388
-			array(
389
-				'groupName' => 'Contracts',
390
-				'globalStatsKey' => 'Contracts',
391
-				'stats' =>
392
-					array('contrat_inactive', 'contrat_active'),
393
-			),
394
-		'ticket' =>
395
-			array(
396
-				'groupName' => 'Tickets',
397
-				'globalStatsKey' => 'ticket',
398
-				'stats' =>
399
-					array('ticket_opened'),
400
-			),
401
-		'bank_account' =>
402
-			array(
403
-				'groupName' => 'BankAccount',
404
-				'stats' =>
405
-					array('bank_account', 'chequereceipt', 'widthdraw_direct_debit', 'widthdraw_credit_transfer'),
406
-			),
407
-		'member' =>
408
-			array(
409
-				'groupName' => 'Members',
410
-				'globalStatsKey' => 'members',
411
-				'stats' =>
412
-					array('member_shift', 'member_expired'),
413
-			),
414
-		'expensereport' =>
415
-			array(
416
-				'groupName' => 'ExpenseReport',
417
-				'globalStatsKey' => 'expensereports',
418
-				'stats' =>
419
-					array('expensereport_toapprove', 'expensereport_topay'),
420
-			),
421
-		'holiday' =>
422
-			array(
423
-				'groupName' => 'Holidays',
424
-				'globalStatsKey' => 'holidays',
425
-				'stats' =>
426
-					array('holiday'),
427
-			),
428
-	);
429
-
430
-	$object = new stdClass();
431
-	$parameters = array(
432
-		'dashboardgroup' => $dashboardgroup
433
-	);
434
-	$reshook = $hookmanager->executeHooks('addOpenElementsDashboardGroup', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
435
-	if ($reshook == 0) {
436
-		$dashboardgroup = array_merge($dashboardgroup, $hookmanager->resArray);
437
-	}
438
-
439
-
440
-	// Calculate total nb of late
441
-	$totallate = $totaltodo = 0;
442
-
443
-	//Remove any invalid response
444
-	//load_board can return an integer if failed, or WorkboardResponse if OK
445
-	$valid_dashboardlines = array();
446
-	foreach ($dashboardlines as $workboardid => $tmp) {
447
-		if ($tmp instanceof WorkboardResponse) {
448
-			$tmp->id = $workboardid; // Complete the object to add its id into its name
449
-			$valid_dashboardlines[$workboardid] = $tmp;
450
-		}
451
-	}
452
-
453
-	// We calculate $totallate. Must be defined before start of next loop because it is show in first fetch on next loop
454
-	foreach ($valid_dashboardlines as $board) {
455
-		if (is_numeric($board->nbtodo) && is_numeric($board->nbtodolate) && $board->nbtodolate > 0) {
456
-			$totaltodo += $board->nbtodo;
457
-			$totallate += $board->nbtodolate;
458
-		}
459
-	}
460
-
461
-	$openedDashBoardSize = 'info-box-sm'; // use sm by default
462
-	foreach ($dashboardgroup as $dashbordelement) {
463
-		if (is_array($dashbordelement['stats']) && count($dashbordelement['stats']) > 2) {
464
-			$openedDashBoardSize = ''; // use default info box size : big
465
-			break;
466
-		}
467
-	}
468
-
469
-	$totalLateNumber = $totallate;
470
-	$totallatePercentage = ((!empty($totaltodo)) ? round($totallate / $totaltodo * 100, 2) : 0);
471
-	if (getDolGlobalString('MAIN_USE_METEO_WITH_PERCENTAGE')) {
472
-		$totallate = $totallatePercentage;
473
-	}
474
-
475
-	$boxwork = '';
476
-	$boxwork .= '<div class="box">';
477
-	$boxwork .= '<table summary="' . dol_escape_htmltag($langs->trans("WorkingBoard")) . '" class="noborder boxtable boxtablenobottom boxworkingboard centpercent">' . "\n";
478
-	$boxwork .= '<tr class="liste_titre">';
479
-	$boxwork .= '<th class="liste_titre"><div class="inline-block valignmiddle">' . $langs->trans("DolibarrWorkBoard") . '</div>';
480
-	if ($showweather) {
481
-		if ($totallate > 0) {
482
-			$text = $langs->transnoentitiesnoconv("WarningYouHaveAtLeastOneTaskLate") . ' (' . $langs->transnoentitiesnoconv(
483
-					"NActionsLate",
484
-					$totallate . (getDolGlobalString('MAIN_USE_METEO_WITH_PERCENTAGE') ? '%' : '')
485
-				) . ')';
486
-		} else {
487
-			$text = $langs->transnoentitiesnoconv("NoItemLate");
488
-		}
489
-		$text .= '. ' . $langs->transnoentitiesnoconv("LateDesc");
490
-		//$text.=$form->textwithpicto('',$langs->trans("LateDesc"));
491
-		$options = 'height="24px" style="float: right"';
492
-		$boxwork .= showWeather($totallate, $text, $options, 'inline-block valignmiddle');
493
-	}
494
-	$boxwork .= '</th>';
495
-	$boxwork .= '</tr>' . "\n";
496
-
497
-	// Show dashboard
498
-	$nbworkboardempty = 0;
499
-	$isIntopOpenedDashBoard = $globalStatInTopOpenedDashBoard = array();
500
-	if (!empty($valid_dashboardlines)) {
501
-		$openedDashBoard = '';
502
-
503
-		$boxwork .= '<tr class="nobottom nohover"><td class="tdboxstats nohover flexcontainer centpercent"><div style="display: flex: flex-wrap: wrap">';
504
-
505
-		foreach ($dashboardgroup as $groupKey => $groupElement) {
506
-			$boards = array();
507
-
508
-			// Scan $groupElement and save the one with 'stats' that must be used for the open objects dashboard
509
-			if (!getDolGlobalString('MAIN_DISABLE_NEW_OPENED_DASH_BOARD')) {
510
-				foreach ($groupElement['stats'] as $infoKey) {
511
-					if (!empty($valid_dashboardlines[$infoKey])) {
512
-						$boards[] = $valid_dashboardlines[$infoKey];
513
-						$isIntopOpenedDashBoard[] = $infoKey;
514
-					}
515
-				}
516
-			}
517
-
518
-			if (!empty($boards)) {
519
-				if (!empty($groupElement['lang'])) {
520
-					$langs->load($groupElement['lang']);
521
-				}
522
-				$groupName = $langs->trans($groupElement['groupName']);
523
-				$groupKeyLowerCase = strtolower($groupKey);
524
-
525
-				// global stats
526
-				$globalStatsKey = false;
527
-				if (!empty($groupElement['globalStatsKey']) && empty($groupElement['globalStats'])) { // can be filled by hook
528
-					$globalStatsKey = $groupElement['globalStatsKey'];
529
-					$groupElement['globalStats'] = array();
530
-				}
531
-
532
-				$openedDashBoard .= '<div class="box-flex-item"><div class="box-flex-item-with-margin">' . "\n";
533
-				$openedDashBoard .= '	<div class="info-box ' . $openedDashBoardSize . '">' . "\n";
534
-				$openedDashBoard .= '		<span class="info-box-icon bg-infobox-' . $groupKeyLowerCase . '">' . "\n";
535
-				$openedDashBoard .= '		<i class="fa fa-dol-' . $groupKeyLowerCase . '"></i>' . "\n";
536
-
537
-				// Show the span for the total of record. TODO This seems not used.
538
-				if (!empty($groupElement['globalStats'])) {
539
-					$globalStatInTopOpenedDashBoard[] = $globalStatsKey;
540
-					$openedDashBoard .= '<span class="info-box-icon-text" title="' . $groupElement['globalStats']['text'] . '">' . $groupElement['globalStats']['nbTotal'] . '</span>';
541
-				}
542
-
543
-				$openedDashBoard .= '</span>' . "\n";
544
-				$openedDashBoard .= '<div class="info-box-content">' . "\n";
545
-
546
-				$openedDashBoard .= '<div class="info-box-title" title="' . strip_tags($groupName) . '">' . $groupName . '</div>' . "\n";
547
-				$openedDashBoard .= '<div class="info-box-lines">' . "\n";
548
-
549
-				foreach ($boards as $board) {
550
-					$openedDashBoard .= '<div class="info-box-line spanoverflow nowrap">';
551
-
552
-					if (!empty($board->labelShort)) {
553
-						$infoName = '<div class="marginrightonly inline-block valignmiddle info-box-line-text" title="' . $board->label . '">' . $board->labelShort . '</div>';
554
-					} else {
555
-						$infoName = '<div class="marginrightonly inline-block valignmiddle info-box-line-text">' . $board->label . '</div>';
556
-					}
557
-
558
-					$textLateTitle = $langs->trans("NActionsLate", $board->nbtodolate);
559
-					$textLateTitle .= ' (' . $langs->trans("Late") . ' = ' . $langs->trans("DateReference") . ' > ' . $langs->trans("DateToday") . ' ' . (ceil(empty($board->warning_delay) ? 0 : $board->warning_delay) >= 0 ? '+' : '') . ceil(empty($board->warning_delay) ? 0 : $board->warning_delay) . ' ' . $langs->trans("days") . ')';
560
-
561
-					if ($board->id == 'bank_account') {
562
-						$textLateTitle .= '<br><span class="opacitymedium">' . $langs->trans("IfYouDontReconcileDisableProperty", $langs->transnoentitiesnoconv("Conciliable")) . '</span>';
563
-					}
564
-
565
-					$textLate = '';
566
-					if ($board->nbtodolate > 0) {
567
-						$textLate .= '<span title="' . dol_escape_htmltag($textLateTitle) . '" class="classfortooltip badge badge-warning">';
568
-						$textLate .= '<i class="fa fa-exclamation-triangle"></i> ' . $board->nbtodolate;
569
-						$textLate .= '</span>';
570
-					}
571
-
572
-					$nbtodClass = '';
573
-					if ($board->nbtodo > 0) {
574
-						$nbtodClass = 'badge badge-info';
575
-					} else {
576
-						$nbtodClass = 'opacitymedium';
577
-					}
578
-
579
-					// Forge the line to show into the open object box
580
-					$labeltoshow = $board->label . ' (' . $board->nbtodo . ')';
581
-					if ($board->total > 0) {
582
-						$labeltoshow .= ' - ' . price($board->total, 0, $langs, 1, -1, -1, $conf->currency);
583
-					}
584
-					$openedDashBoard .= '<a href="' . $board->url . '" class="info-box-text info-box-text-a">';
585
-					$openedDashBoard .= $infoName;
586
-					$openedDashBoard .= '<div class="inline-block nowraponall">';
587
-					$openedDashBoard .= '<span class="classfortooltip' . ($nbtodClass ? ' ' . $nbtodClass : '') . '" title="' . $labeltoshow . '">';
588
-					$openedDashBoard .= $board->nbtodo;
589
-					if ($board->total > 0 && getDolGlobalString('MAIN_WORKBOARD_SHOW_TOTAL_WO_TAX')) {
590
-						$openedDashBoard .= ' : ' . price($board->total, 0, $langs, 1, -1, -1, $conf->currency);
591
-					}
592
-					$openedDashBoard .= '</span>';
593
-
594
-					if ($textLate) {
595
-						if ($board->url_late) {
596
-							$openedDashBoard .= '</div></a>';
597
-							$openedDashBoard .= ' <div class="inline-block"><a href="' . $board->url_late . '" class="info-box-text info-box-text-a paddingleft">';
598
-						} else {
599
-							$openedDashBoard .= ' ';
600
-						}
601
-						$openedDashBoard .= $textLate;
602
-					}
603
-					$openedDashBoard .= '</a>' . "\n";
604
-					$openedDashBoard .= '</div>';
605
-					$openedDashBoard .= '</div>' . "\n";
606
-				}
607
-
608
-				// TODO Add hook here to add more "info-box-line"
609
-
610
-				$openedDashBoard .= '		</div><!-- /.info-box-lines --></div><!-- /.info-box-content -->' . "\n";
611
-				$openedDashBoard .= '	</div><!-- /.info-box -->' . "\n";
612
-				$openedDashBoard .= '</div><!-- /.box-flex-item-with-margin -->' . "\n";
613
-				$openedDashBoard .= '</div><!-- /.box-flex-item -->' . "\n";
614
-				$openedDashBoard .= "\n";
615
-			}
616
-		}
617
-
618
-		if ($showweather && !empty($isIntopOpenedDashBoard)) {
619
-			$appendClass = (getDolGlobalInt('MAIN_DISABLE_METEO') == 2 ? ' hideonsmartphone' : '');
620
-			$weather = getWeatherStatus($totallate);
621
-
622
-			$text = '';
623
-			if ($totallate > 0) {
624
-				$text = $langs->transnoentitiesnoconv("WarningYouHaveAtLeastOneTaskLate") . ' (' . $langs->transnoentitiesnoconv(
625
-						"NActionsLate",
626
-						$totallate . (getDolGlobalString('MAIN_USE_METEO_WITH_PERCENTAGE') ? '%' : '')
627
-					) . ')';
628
-			} else {
629
-				$text = $langs->transnoentitiesnoconv("NoItemLate");
630
-			}
631
-			$text .= '. ' . $langs->transnoentitiesnoconv("LateDesc");
632
-
633
-			$weatherDashBoard = '<div class="box-flex-item ' . $appendClass . '"><div class="box-flex-item-with-margin">' . "\n";
634
-			$weatherDashBoard .= '	<div class="info-box ' . $openedDashBoardSize . ' info-box-weather info-box-weather-level' . $weather->level . '">' . "\n";
635
-			$weatherDashBoard .= '		<span class="info-box-icon">';
636
-			$weatherDashBoard .= img_weather('', $weather->level, '', 0, 'valignmiddle width50');
637
-			$weatherDashBoard .= '       </span>' . "\n";
638
-			$weatherDashBoard .= '		<div class="info-box-content">' . "\n";
639
-			$weatherDashBoard .= '			<div class="info-box-title">' . $langs->trans('GlobalOpenedElemView') . '</div>' . "\n";
640
-
641
-			if ($totallatePercentage > 0 && getDolGlobalString('MAIN_USE_METEO_WITH_PERCENTAGE')) {
642
-				$weatherDashBoard .= '			<span class="info-box-number">' . $langs->transnoentitiesnoconv(
643
-						"NActionsLate",
644
-						price($totallatePercentage) . '%'
645
-					) . '</span>' . "\n";
646
-				$weatherDashBoard .= '			<span class="progress-description">' . $langs->trans(
647
-						'NActionsLate',
648
-						$totalLateNumber
649
-					) . '</span>' . "\n";
650
-			} else {
651
-				$weatherDashBoard .= '			<span class="info-box-number">' . $langs->transnoentitiesnoconv(
652
-						"NActionsLate",
653
-						$totalLateNumber
654
-					) . '</span>' . "\n";
655
-				if ($totallatePercentage > 0) {
656
-					$weatherDashBoard .= '			<span class="progress-description">' . $langs->trans(
657
-							'NActionsLate',
658
-							price($totallatePercentage) . '%'
659
-						) . '</span>' . "\n";
660
-				}
661
-			}
662
-
663
-			$weatherDashBoard .= '		</div><!-- /.info-box-content -->' . "\n";
664
-			$weatherDashBoard .= '	</div><!-- /.info-box -->' . "\n";
665
-			$weatherDashBoard .= '</div><!-- /.box-flex-item-with-margin -->' . "\n";
666
-			$weatherDashBoard .= '</div><!-- /.box-flex-item -->' . "\n";
667
-			$weatherDashBoard .= "\n";
668
-
669
-			$openedDashBoard = $weatherDashBoard . $openedDashBoard;
670
-		}
671
-
672
-		if (!empty($isIntopOpenedDashBoard)) {
673
-			for ($i = 1; $i <= 10; $i++) {
674
-				$openedDashBoard .= '<div class="box-flex-item filler"></div>';
675
-			}
676
-		}
677
-
678
-		$nbworkboardcount = 0;
679
-		foreach ($valid_dashboardlines as $infoKey => $board) {
680
-			if (in_array($infoKey, $isIntopOpenedDashBoard)) {
681
-				// skip if info is present on top
682
-				continue;
683
-			}
684
-
685
-			if (empty($board->nbtodo)) {
686
-				$nbworkboardempty++;
687
-			}
688
-			$nbworkboardcount++;
689
-
690
-
691
-			$textlate = $langs->trans("NActionsLate", $board->nbtodolate);
692
-			$textlate .= ' (' . $langs->trans("Late") . ' = ' . $langs->trans("DateReference") . ' > ' . $langs->trans("DateToday") . ' ' . (ceil($board->warning_delay) >= 0 ? '+' : '') . ceil($board->warning_delay) . ' ' . $langs->trans("days") . ')';
693
-
694
-
695
-			$boxwork .= '<div class="boxstatsindicator thumbstat150 nobold nounderline"><div class="boxstats130 boxstatsborder">';
696
-			$boxwork .= '<div class="boxstatscontent">';
697
-			$boxwork .= '<span class="boxstatstext" title="' . dol_escape_htmltag($board->label) . '">' . $board->img . ' <span>' . $board->label . '</span></span><br>';
698
-			$boxwork .= '<a class="valignmiddle dashboardlineindicator" href="' . $board->url . '"><span class="dashboardlineindicator' . (($board->nbtodo == 0) ? ' dashboardlineok' : '') . '">' . $board->nbtodo . '</span></a>';
699
-			if ($board->total > 0 && getDolGlobalString('MAIN_WORKBOARD_SHOW_TOTAL_WO_TAX')) {
700
-				$boxwork .= '&nbsp;/&nbsp;<a class="valignmiddle dashboardlineindicator" href="' . $board->url . '"><span class="dashboardlineindicator' . (($board->nbtodo == 0) ? ' dashboardlineok' : '') . '">' . price($board->total) . '</span></a>';
701
-			}
702
-			$boxwork .= '</div>';
703
-			if ($board->nbtodolate > 0) {
704
-				$boxwork .= '<div class="dashboardlinelatecoin nowrap">';
705
-				$boxwork .= '<a title="' . dol_escape_htmltag($textlate) . '" class="valignmiddle dashboardlineindicatorlate' . ($board->nbtodolate > 0 ? ' dashboardlineko' : ' dashboardlineok') . '" href="' . ((!$board->url_late) ? $board->url : $board->url_late) . '">';
706
-				//$boxwork .= img_picto($textlate, "warning_white", 'class="valigntextbottom"');
707
-				$boxwork .= img_picto(
708
-					$textlate,
709
-					"warning_white",
710
-					'class="inline-block hideonsmartphone valigntextbottom"'
711
-				);
712
-				$boxwork .= '<span class="dashboardlineindicatorlate' . ($board->nbtodolate > 0 ? ' dashboardlineko' : ' dashboardlineok') . '">';
713
-				$boxwork .= $board->nbtodolate;
714
-				$boxwork .= '</span>';
715
-				$boxwork .= '</a>';
716
-				$boxwork .= '</div>';
717
-			}
718
-			$boxwork .= '</div></div>';
719
-			$boxwork .= "\n";
720
-		}
721
-
722
-		$boxwork .= '<div class="boxstatsindicator thumbstat150 nobold nounderline"><div class="boxstats150empty"></div></div>';
723
-		$boxwork .= '<div class="boxstatsindicator thumbstat150 nobold nounderline"><div class="boxstats150empty"></div></div>';
724
-		$boxwork .= '<div class="boxstatsindicator thumbstat150 nobold nounderline"><div class="boxstats150empty"></div></div>';
725
-		$boxwork .= '<div class="boxstatsindicator thumbstat150 nobold nounderline"><div class="boxstats150empty"></div></div>';
726
-
727
-		$boxwork .= '</div>';
728
-		$boxwork .= '</td></tr>';
729
-	} else {
730
-		$boxwork .= '<tr class="nohover">';
731
-		$boxwork .= '<td class="nohover valignmiddle opacitymedium">';
732
-		$boxwork .= $langs->trans("NoOpenedElementToProcess");
733
-		$boxwork .= '</td>';
734
-		$boxwork .= '</tr>';
735
-	}
736
-
737
-	$boxwork .= '</td></tr>';
738
-
739
-	$boxwork .= '</table>'; // End table array of working board
740
-	$boxwork .= '</div>';
741
-
742
-	if (!empty($isIntopOpenedDashBoard)) {
743
-		print '<div class="fichecenter">';
744
-		print '<div class="opened-dash-board-wrap"><div class="box-flex-container">' . $openedDashBoard . '</div></div>';
745
-		print '</div>';
746
-	}
176
+    $showweather = (!getDolGlobalString('MAIN_DISABLE_METEO') || getDolGlobalInt('MAIN_DISABLE_METEO') == 2) ? 1 : 0;
177
+
178
+    //Array that contains all WorkboardResponse classes to process them
179
+    $dashboardlines = array();
180
+
181
+    // Number of actions to do (late)
182
+    if (isModEnabled('agenda') && !getDolGlobalString('MAIN_DISABLE_BLOCK_AGENDA') && $user->hasRight('agenda', 'myactions', 'read')) {
183
+        $board = new ActionComm($db);
184
+        $dashboardlines[$board->element] = $board->load_board($user);
185
+    }
186
+
187
+    // Number of project opened
188
+    if (isModEnabled('project') && !getDolGlobalString('MAIN_DISABLE_BLOCK_PROJECT') && $user->hasRight('projet', 'lire')) {
189
+        $board = new Project($db);
190
+        $dashboardlines[$board->element] = $board->load_board($user);
191
+    }
192
+
193
+    // Number of tasks to do (late)
194
+    if (isModEnabled('project') && !getDolGlobalString('MAIN_DISABLE_BLOCK_PROJECT') && !getDolGlobalString('PROJECT_HIDE_TASKS') && $user->hasRight('projet', 'lire')) {
195
+        $board = new Task($db);
196
+        $dashboardlines[$board->element] = $board->load_board($user);
197
+    }
198
+
199
+    // Number of commercial customer proposals open (expired)
200
+    if (isModEnabled('propal') && !getDolGlobalString('MAIN_DISABLE_BLOCK_CUSTOMER') && $user->hasRight('propal', 'read')) {
201
+        $board = new Propal($db);
202
+        $dashboardlines[$board->element . '_opened'] = $board->load_board($user, "opened");
203
+        // Number of commercial proposals CLOSED signed (billed)
204
+        $dashboardlines[$board->element . '_signed'] = $board->load_board($user, "signed");
205
+    }
206
+
207
+    // Number of supplier proposals open (expired)
208
+    if (isModEnabled('supplier_proposal') && !getDolGlobalString('MAIN_DISABLE_BLOCK_SUPPLIER') && $user->hasRight('supplier_proposal', 'lire')) {
209
+        $langs->load("supplier_proposal");
210
+        $board = new SupplierProposal($db);
211
+        $dashboardlines[$board->element . '_opened'] = $board->load_board($user, "opened");
212
+        // Number of commercial proposals CLOSED signed (billed)
213
+        $dashboardlines[$board->element . '_signed'] = $board->load_board($user, "signed");
214
+    }
215
+
216
+    // Number of sales orders
217
+    if (isModEnabled('order') && !getDolGlobalString('MAIN_DISABLE_BLOCK_CUSTOMER') && $user->hasRight('commande', 'lire')) {
218
+        $board = new Commande($db);
219
+        // Number of customer orders to be shipped (validated and in progress)
220
+        $dashboardlines[$board->element . '_toship'] = $board->load_board($user, 'toship');
221
+        // Number of customer orders to be billed (not visible by default, does not match a lot of organization).
222
+        if (getDolGlobalInt('ORDER_BILL_AFTER_VALIDATION')) {
223
+            $dashboardlines[$board->element . '_tobill'] = $board->load_board($user, 'tobill');
224
+        }
225
+        // Number of customer orders to be billed (delivered but not billed)
226
+        $dashboardlines[$board->element . '_shippedtobill'] = $board->load_board($user, 'shippedtobill');
227
+    }
228
+
229
+    // Number of suppliers orders
230
+    if (isModEnabled('supplier_order') && !getDolGlobalString('MAIN_DISABLE_BLOCK_SUPPLIER') && $user->hasRight('fournisseur', 'commande', 'lire')) {
231
+        $board = new CommandeFournisseur($db);
232
+        $dashboardlines[$board->element . '_opened'] = $board->load_board($user, "opened");
233
+        $dashboardlines[$board->element . '_awaiting'] = $board->load_board($user, 'awaiting');
234
+    }
235
+
236
+    // Number of contract / services enabled (delayed)
237
+    if (isModEnabled('contract') && !getDolGlobalString('MAIN_DISABLE_BLOCK_CONTRACT') && $user->hasRight('contrat', 'lire')) {
238
+        $board = new Contrat($db);
239
+        $dashboardlines[$board->element . '_inactive'] = $board->load_board($user, "inactive");
240
+        // Number of active services (expired)
241
+        $dashboardlines[$board->element . '_active'] = $board->load_board($user, "active");
242
+    }
243
+
244
+    // Number of tickets open
245
+    if (isModEnabled('ticket') && !getDolGlobalString('MAIN_DISABLE_BLOCK_TICKET') && $user->hasRight('ticket', 'read')) {
246
+        $board = new Ticket($db);
247
+        $dashboardlines[$board->element . '_opened'] = $board->load_board($user, "opened");
248
+        // Number of active services (expired)
249
+        //$dashboardlines[$board->element.'_active'] = $board->load_board($user, "active");
250
+    }
251
+
252
+    // Number of invoices customers (paid)
253
+    if (isModEnabled('invoice') && !getDolGlobalString('MAIN_DISABLE_BLOCK_CUSTOMER') && $user->hasRight('facture', 'lire')) {
254
+        $board = new Facture($db);
255
+        $dashboardlines[$board->element] = $board->load_board($user);
256
+    }
257
+
258
+    // Number of supplier invoices (paid)
259
+    if (isModEnabled('supplier_invoice') && !getDolGlobalString('MAIN_DISABLE_BLOCK_SUPPLIER') && $user->hasRight('fournisseur', 'facture', 'lire')) {
260
+        $board = new FactureFournisseur($db);
261
+        $dashboardlines[$board->element] = $board->load_board($user);
262
+    }
263
+
264
+    // Number of transactions to conciliate
265
+    if (isModEnabled('bank') && !getDolGlobalString('MAIN_DISABLE_BLOCK_BANK') && $user->hasRight('banque', 'lire') && !$user->socid) {
266
+        $board = new Account($db);
267
+        $nb = $board->countAccountToReconcile(); // Get nb of account to reconciliate
268
+        if ($nb > 0) {
269
+            $dashboardlines[$board->element] = $board->load_board($user);
270
+        }
271
+    }
272
+
273
+
274
+    // Number of cheque to send
275
+    if (isModEnabled('bank') && !getDolGlobalString('MAIN_DISABLE_BLOCK_BANK') && $user->hasRight('banque', 'lire') && !$user->socid) {
276
+        if (!getDolGlobalString('BANK_DISABLE_CHECK_DEPOSIT')) {
277
+            include_once DOL_DOCUMENT_ROOT . '/compta/paiement/cheque/class/remisecheque.class.php';
278
+            $board = new RemiseCheque($db);
279
+            $dashboardlines[$board->element] = $board->load_board($user);
280
+        }
281
+        if (isModEnabled('prelevement')) {
282
+            include_once DOL_DOCUMENT_ROOT . '/compta/prelevement/class/bonprelevement.class.php';
283
+            $board = new BonPrelevement($db);
284
+            $dashboardlines[$board->element . '_direct_debit'] = $board->load_board($user, 'direct_debit');
285
+        }
286
+        if (isModEnabled('paymentbybanktransfer')) {
287
+            include_once DOL_DOCUMENT_ROOT . '/compta/prelevement/class/bonprelevement.class.php';
288
+            $board = new BonPrelevement($db);
289
+            $dashboardlines[$board->element . '_credit_transfer'] = $board->load_board($user, 'credit_transfer');
290
+        }
291
+    }
292
+
293
+    // Number of foundation members
294
+    if (isModEnabled('member') && !getDolGlobalString('MAIN_DISABLE_BLOCK_ADHERENT') && $user->hasRight('adherent', 'lire') && !$user->socid) {
295
+        $board = new Adherent($db);
296
+        $dashboardlines[$board->element . '_shift'] = $board->load_board($user, 'shift');
297
+        $dashboardlines[$board->element . '_expired'] = $board->load_board($user, 'expired');
298
+    }
299
+
300
+    // Number of expense reports to approve
301
+    if (isModEnabled('expensereport') && !getDolGlobalString('MAIN_DISABLE_BLOCK_EXPENSEREPORT') && $user->hasRight('expensereport', 'approve')) {
302
+        $board = new ExpenseReport($db);
303
+        $dashboardlines[$board->element . '_toapprove'] = $board->load_board($user, 'toapprove');
304
+    }
305
+
306
+    // Number of expense reports to pay
307
+    if (isModEnabled('expensereport') && !getDolGlobalString('MAIN_DISABLE_BLOCK_EXPENSEREPORT') && $user->hasRight('expensereport', 'to_paid')) {
308
+        $board = new ExpenseReport($db);
309
+        $dashboardlines[$board->element . '_topay'] = $board->load_board($user, 'topay');
310
+    }
311
+
312
+    // Number of holidays to approve
313
+    if (isModEnabled('holiday') && !getDolGlobalString('MAIN_DISABLE_BLOCK_HOLIDAY') && $user->hasRight('holiday', 'approve')) {
314
+        $board = new Holiday($db);
315
+        $dashboardlines[$board->element] = $board->load_board($user);
316
+    }
317
+
318
+    $object = new stdClass();
319
+    $parameters = array();
320
+    $action = '';
321
+    $reshook = $hookmanager->executeHooks(
322
+        'addOpenElementsDashboardLine',
323
+        $parameters,
324
+        $object,
325
+        $action
326
+    ); // Note that $action and $object may have been modified by some hooks
327
+    if ($reshook == 0) {
328
+        $dashboardlines = array_merge($dashboardlines, $hookmanager->resArray);
329
+    }
330
+
331
+    /* Open object dashboard */
332
+    $dashboardgroup = array(
333
+        'action' =>
334
+            array(
335
+                'groupName' => 'Agenda',
336
+                'stats' => array('action'),
337
+            ),
338
+        'project' =>
339
+            array(
340
+                'groupName' => 'Projects',
341
+                'globalStatsKey' => 'projects',
342
+                'stats' => array('project', 'project_task'),
343
+            ),
344
+        'propal' =>
345
+            array(
346
+                'groupName' => 'Proposals',
347
+                'globalStatsKey' => 'proposals',
348
+                'stats' =>
349
+                    array('propal_opened', 'propal_signed'),
350
+            ),
351
+        'commande' =>
352
+            array(
353
+                'groupName' => 'Orders',
354
+                'globalStatsKey' => 'orders',
355
+                'stats' =>
356
+                    array('commande_toship', 'commande_tobill', 'commande_shippedtobill'),
357
+            ),
358
+        'facture' =>
359
+            array(
360
+                'groupName' => 'Invoices',
361
+                'globalStatsKey' => 'invoices',
362
+                'stats' =>
363
+                    array('facture'),
364
+            ),
365
+        'supplier_proposal' =>
366
+            array(
367
+                'lang' => 'supplier_proposal',
368
+                'groupName' => 'SupplierProposals',
369
+                'globalStatsKey' => 'askprice',
370
+                'stats' =>
371
+                    array('supplier_proposal_opened', 'supplier_proposal_signed'),
372
+            ),
373
+        'order_supplier' =>
374
+            array(
375
+                'groupName' => 'SuppliersOrders',
376
+                'globalStatsKey' => 'supplier_orders',
377
+                'stats' =>
378
+                    array('order_supplier_opened', 'order_supplier_awaiting'),
379
+            ),
380
+        'invoice_supplier' =>
381
+            array(
382
+                'groupName' => 'BillsSuppliers',
383
+                'globalStatsKey' => 'supplier_invoices',
384
+                'stats' =>
385
+                    array('invoice_supplier'),
386
+            ),
387
+        'contrat' =>
388
+            array(
389
+                'groupName' => 'Contracts',
390
+                'globalStatsKey' => 'Contracts',
391
+                'stats' =>
392
+                    array('contrat_inactive', 'contrat_active'),
393
+            ),
394
+        'ticket' =>
395
+            array(
396
+                'groupName' => 'Tickets',
397
+                'globalStatsKey' => 'ticket',
398
+                'stats' =>
399
+                    array('ticket_opened'),
400
+            ),
401
+        'bank_account' =>
402
+            array(
403
+                'groupName' => 'BankAccount',
404
+                'stats' =>
405
+                    array('bank_account', 'chequereceipt', 'widthdraw_direct_debit', 'widthdraw_credit_transfer'),
406
+            ),
407
+        'member' =>
408
+            array(
409
+                'groupName' => 'Members',
410
+                'globalStatsKey' => 'members',
411
+                'stats' =>
412
+                    array('member_shift', 'member_expired'),
413
+            ),
414
+        'expensereport' =>
415
+            array(
416
+                'groupName' => 'ExpenseReport',
417
+                'globalStatsKey' => 'expensereports',
418
+                'stats' =>
419
+                    array('expensereport_toapprove', 'expensereport_topay'),
420
+            ),
421
+        'holiday' =>
422
+            array(
423
+                'groupName' => 'Holidays',
424
+                'globalStatsKey' => 'holidays',
425
+                'stats' =>
426
+                    array('holiday'),
427
+            ),
428
+    );
429
+
430
+    $object = new stdClass();
431
+    $parameters = array(
432
+        'dashboardgroup' => $dashboardgroup
433
+    );
434
+    $reshook = $hookmanager->executeHooks('addOpenElementsDashboardGroup', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
435
+    if ($reshook == 0) {
436
+        $dashboardgroup = array_merge($dashboardgroup, $hookmanager->resArray);
437
+    }
438
+
439
+
440
+    // Calculate total nb of late
441
+    $totallate = $totaltodo = 0;
442
+
443
+    //Remove any invalid response
444
+    //load_board can return an integer if failed, or WorkboardResponse if OK
445
+    $valid_dashboardlines = array();
446
+    foreach ($dashboardlines as $workboardid => $tmp) {
447
+        if ($tmp instanceof WorkboardResponse) {
448
+            $tmp->id = $workboardid; // Complete the object to add its id into its name
449
+            $valid_dashboardlines[$workboardid] = $tmp;
450
+        }
451
+    }
452
+
453
+    // We calculate $totallate. Must be defined before start of next loop because it is show in first fetch on next loop
454
+    foreach ($valid_dashboardlines as $board) {
455
+        if (is_numeric($board->nbtodo) && is_numeric($board->nbtodolate) && $board->nbtodolate > 0) {
456
+            $totaltodo += $board->nbtodo;
457
+            $totallate += $board->nbtodolate;
458
+        }
459
+    }
460
+
461
+    $openedDashBoardSize = 'info-box-sm'; // use sm by default
462
+    foreach ($dashboardgroup as $dashbordelement) {
463
+        if (is_array($dashbordelement['stats']) && count($dashbordelement['stats']) > 2) {
464
+            $openedDashBoardSize = ''; // use default info box size : big
465
+            break;
466
+        }
467
+    }
468
+
469
+    $totalLateNumber = $totallate;
470
+    $totallatePercentage = ((!empty($totaltodo)) ? round($totallate / $totaltodo * 100, 2) : 0);
471
+    if (getDolGlobalString('MAIN_USE_METEO_WITH_PERCENTAGE')) {
472
+        $totallate = $totallatePercentage;
473
+    }
474
+
475
+    $boxwork = '';
476
+    $boxwork .= '<div class="box">';
477
+    $boxwork .= '<table summary="' . dol_escape_htmltag($langs->trans("WorkingBoard")) . '" class="noborder boxtable boxtablenobottom boxworkingboard centpercent">' . "\n";
478
+    $boxwork .= '<tr class="liste_titre">';
479
+    $boxwork .= '<th class="liste_titre"><div class="inline-block valignmiddle">' . $langs->trans("DolibarrWorkBoard") . '</div>';
480
+    if ($showweather) {
481
+        if ($totallate > 0) {
482
+            $text = $langs->transnoentitiesnoconv("WarningYouHaveAtLeastOneTaskLate") . ' (' . $langs->transnoentitiesnoconv(
483
+                    "NActionsLate",
484
+                    $totallate . (getDolGlobalString('MAIN_USE_METEO_WITH_PERCENTAGE') ? '%' : '')
485
+                ) . ')';
486
+        } else {
487
+            $text = $langs->transnoentitiesnoconv("NoItemLate");
488
+        }
489
+        $text .= '. ' . $langs->transnoentitiesnoconv("LateDesc");
490
+        //$text.=$form->textwithpicto('',$langs->trans("LateDesc"));
491
+        $options = 'height="24px" style="float: right"';
492
+        $boxwork .= showWeather($totallate, $text, $options, 'inline-block valignmiddle');
493
+    }
494
+    $boxwork .= '</th>';
495
+    $boxwork .= '</tr>' . "\n";
496
+
497
+    // Show dashboard
498
+    $nbworkboardempty = 0;
499
+    $isIntopOpenedDashBoard = $globalStatInTopOpenedDashBoard = array();
500
+    if (!empty($valid_dashboardlines)) {
501
+        $openedDashBoard = '';
502
+
503
+        $boxwork .= '<tr class="nobottom nohover"><td class="tdboxstats nohover flexcontainer centpercent"><div style="display: flex: flex-wrap: wrap">';
504
+
505
+        foreach ($dashboardgroup as $groupKey => $groupElement) {
506
+            $boards = array();
507
+
508
+            // Scan $groupElement and save the one with 'stats' that must be used for the open objects dashboard
509
+            if (!getDolGlobalString('MAIN_DISABLE_NEW_OPENED_DASH_BOARD')) {
510
+                foreach ($groupElement['stats'] as $infoKey) {
511
+                    if (!empty($valid_dashboardlines[$infoKey])) {
512
+                        $boards[] = $valid_dashboardlines[$infoKey];
513
+                        $isIntopOpenedDashBoard[] = $infoKey;
514
+                    }
515
+                }
516
+            }
517
+
518
+            if (!empty($boards)) {
519
+                if (!empty($groupElement['lang'])) {
520
+                    $langs->load($groupElement['lang']);
521
+                }
522
+                $groupName = $langs->trans($groupElement['groupName']);
523
+                $groupKeyLowerCase = strtolower($groupKey);
524
+
525
+                // global stats
526
+                $globalStatsKey = false;
527
+                if (!empty($groupElement['globalStatsKey']) && empty($groupElement['globalStats'])) { // can be filled by hook
528
+                    $globalStatsKey = $groupElement['globalStatsKey'];
529
+                    $groupElement['globalStats'] = array();
530
+                }
531
+
532
+                $openedDashBoard .= '<div class="box-flex-item"><div class="box-flex-item-with-margin">' . "\n";
533
+                $openedDashBoard .= '	<div class="info-box ' . $openedDashBoardSize . '">' . "\n";
534
+                $openedDashBoard .= '		<span class="info-box-icon bg-infobox-' . $groupKeyLowerCase . '">' . "\n";
535
+                $openedDashBoard .= '		<i class="fa fa-dol-' . $groupKeyLowerCase . '"></i>' . "\n";
536
+
537
+                // Show the span for the total of record. TODO This seems not used.
538
+                if (!empty($groupElement['globalStats'])) {
539
+                    $globalStatInTopOpenedDashBoard[] = $globalStatsKey;
540
+                    $openedDashBoard .= '<span class="info-box-icon-text" title="' . $groupElement['globalStats']['text'] . '">' . $groupElement['globalStats']['nbTotal'] . '</span>';
541
+                }
542
+
543
+                $openedDashBoard .= '</span>' . "\n";
544
+                $openedDashBoard .= '<div class="info-box-content">' . "\n";
545
+
546
+                $openedDashBoard .= '<div class="info-box-title" title="' . strip_tags($groupName) . '">' . $groupName . '</div>' . "\n";
547
+                $openedDashBoard .= '<div class="info-box-lines">' . "\n";
548
+
549
+                foreach ($boards as $board) {
550
+                    $openedDashBoard .= '<div class="info-box-line spanoverflow nowrap">';
551
+
552
+                    if (!empty($board->labelShort)) {
553
+                        $infoName = '<div class="marginrightonly inline-block valignmiddle info-box-line-text" title="' . $board->label . '">' . $board->labelShort . '</div>';
554
+                    } else {
555
+                        $infoName = '<div class="marginrightonly inline-block valignmiddle info-box-line-text">' . $board->label . '</div>';
556
+                    }
557
+
558
+                    $textLateTitle = $langs->trans("NActionsLate", $board->nbtodolate);
559
+                    $textLateTitle .= ' (' . $langs->trans("Late") . ' = ' . $langs->trans("DateReference") . ' > ' . $langs->trans("DateToday") . ' ' . (ceil(empty($board->warning_delay) ? 0 : $board->warning_delay) >= 0 ? '+' : '') . ceil(empty($board->warning_delay) ? 0 : $board->warning_delay) . ' ' . $langs->trans("days") . ')';
560
+
561
+                    if ($board->id == 'bank_account') {
562
+                        $textLateTitle .= '<br><span class="opacitymedium">' . $langs->trans("IfYouDontReconcileDisableProperty", $langs->transnoentitiesnoconv("Conciliable")) . '</span>';
563
+                    }
564
+
565
+                    $textLate = '';
566
+                    if ($board->nbtodolate > 0) {
567
+                        $textLate .= '<span title="' . dol_escape_htmltag($textLateTitle) . '" class="classfortooltip badge badge-warning">';
568
+                        $textLate .= '<i class="fa fa-exclamation-triangle"></i> ' . $board->nbtodolate;
569
+                        $textLate .= '</span>';
570
+                    }
571
+
572
+                    $nbtodClass = '';
573
+                    if ($board->nbtodo > 0) {
574
+                        $nbtodClass = 'badge badge-info';
575
+                    } else {
576
+                        $nbtodClass = 'opacitymedium';
577
+                    }
578
+
579
+                    // Forge the line to show into the open object box
580
+                    $labeltoshow = $board->label . ' (' . $board->nbtodo . ')';
581
+                    if ($board->total > 0) {
582
+                        $labeltoshow .= ' - ' . price($board->total, 0, $langs, 1, -1, -1, $conf->currency);
583
+                    }
584
+                    $openedDashBoard .= '<a href="' . $board->url . '" class="info-box-text info-box-text-a">';
585
+                    $openedDashBoard .= $infoName;
586
+                    $openedDashBoard .= '<div class="inline-block nowraponall">';
587
+                    $openedDashBoard .= '<span class="classfortooltip' . ($nbtodClass ? ' ' . $nbtodClass : '') . '" title="' . $labeltoshow . '">';
588
+                    $openedDashBoard .= $board->nbtodo;
589
+                    if ($board->total > 0 && getDolGlobalString('MAIN_WORKBOARD_SHOW_TOTAL_WO_TAX')) {
590
+                        $openedDashBoard .= ' : ' . price($board->total, 0, $langs, 1, -1, -1, $conf->currency);
591
+                    }
592
+                    $openedDashBoard .= '</span>';
593
+
594
+                    if ($textLate) {
595
+                        if ($board->url_late) {
596
+                            $openedDashBoard .= '</div></a>';
597
+                            $openedDashBoard .= ' <div class="inline-block"><a href="' . $board->url_late . '" class="info-box-text info-box-text-a paddingleft">';
598
+                        } else {
599
+                            $openedDashBoard .= ' ';
600
+                        }
601
+                        $openedDashBoard .= $textLate;
602
+                    }
603
+                    $openedDashBoard .= '</a>' . "\n";
604
+                    $openedDashBoard .= '</div>';
605
+                    $openedDashBoard .= '</div>' . "\n";
606
+                }
607
+
608
+                // TODO Add hook here to add more "info-box-line"
609
+
610
+                $openedDashBoard .= '		</div><!-- /.info-box-lines --></div><!-- /.info-box-content -->' . "\n";
611
+                $openedDashBoard .= '	</div><!-- /.info-box -->' . "\n";
612
+                $openedDashBoard .= '</div><!-- /.box-flex-item-with-margin -->' . "\n";
613
+                $openedDashBoard .= '</div><!-- /.box-flex-item -->' . "\n";
614
+                $openedDashBoard .= "\n";
615
+            }
616
+        }
617
+
618
+        if ($showweather && !empty($isIntopOpenedDashBoard)) {
619
+            $appendClass = (getDolGlobalInt('MAIN_DISABLE_METEO') == 2 ? ' hideonsmartphone' : '');
620
+            $weather = getWeatherStatus($totallate);
621
+
622
+            $text = '';
623
+            if ($totallate > 0) {
624
+                $text = $langs->transnoentitiesnoconv("WarningYouHaveAtLeastOneTaskLate") . ' (' . $langs->transnoentitiesnoconv(
625
+                        "NActionsLate",
626
+                        $totallate . (getDolGlobalString('MAIN_USE_METEO_WITH_PERCENTAGE') ? '%' : '')
627
+                    ) . ')';
628
+            } else {
629
+                $text = $langs->transnoentitiesnoconv("NoItemLate");
630
+            }
631
+            $text .= '. ' . $langs->transnoentitiesnoconv("LateDesc");
632
+
633
+            $weatherDashBoard = '<div class="box-flex-item ' . $appendClass . '"><div class="box-flex-item-with-margin">' . "\n";
634
+            $weatherDashBoard .= '	<div class="info-box ' . $openedDashBoardSize . ' info-box-weather info-box-weather-level' . $weather->level . '">' . "\n";
635
+            $weatherDashBoard .= '		<span class="info-box-icon">';
636
+            $weatherDashBoard .= img_weather('', $weather->level, '', 0, 'valignmiddle width50');
637
+            $weatherDashBoard .= '       </span>' . "\n";
638
+            $weatherDashBoard .= '		<div class="info-box-content">' . "\n";
639
+            $weatherDashBoard .= '			<div class="info-box-title">' . $langs->trans('GlobalOpenedElemView') . '</div>' . "\n";
640
+
641
+            if ($totallatePercentage > 0 && getDolGlobalString('MAIN_USE_METEO_WITH_PERCENTAGE')) {
642
+                $weatherDashBoard .= '			<span class="info-box-number">' . $langs->transnoentitiesnoconv(
643
+                        "NActionsLate",
644
+                        price($totallatePercentage) . '%'
645
+                    ) . '</span>' . "\n";
646
+                $weatherDashBoard .= '			<span class="progress-description">' . $langs->trans(
647
+                        'NActionsLate',
648
+                        $totalLateNumber
649
+                    ) . '</span>' . "\n";
650
+            } else {
651
+                $weatherDashBoard .= '			<span class="info-box-number">' . $langs->transnoentitiesnoconv(
652
+                        "NActionsLate",
653
+                        $totalLateNumber
654
+                    ) . '</span>' . "\n";
655
+                if ($totallatePercentage > 0) {
656
+                    $weatherDashBoard .= '			<span class="progress-description">' . $langs->trans(
657
+                            'NActionsLate',
658
+                            price($totallatePercentage) . '%'
659
+                        ) . '</span>' . "\n";
660
+                }
661
+            }
662
+
663
+            $weatherDashBoard .= '		</div><!-- /.info-box-content -->' . "\n";
664
+            $weatherDashBoard .= '	</div><!-- /.info-box -->' . "\n";
665
+            $weatherDashBoard .= '</div><!-- /.box-flex-item-with-margin -->' . "\n";
666
+            $weatherDashBoard .= '</div><!-- /.box-flex-item -->' . "\n";
667
+            $weatherDashBoard .= "\n";
668
+
669
+            $openedDashBoard = $weatherDashBoard . $openedDashBoard;
670
+        }
671
+
672
+        if (!empty($isIntopOpenedDashBoard)) {
673
+            for ($i = 1; $i <= 10; $i++) {
674
+                $openedDashBoard .= '<div class="box-flex-item filler"></div>';
675
+            }
676
+        }
677
+
678
+        $nbworkboardcount = 0;
679
+        foreach ($valid_dashboardlines as $infoKey => $board) {
680
+            if (in_array($infoKey, $isIntopOpenedDashBoard)) {
681
+                // skip if info is present on top
682
+                continue;
683
+            }
684
+
685
+            if (empty($board->nbtodo)) {
686
+                $nbworkboardempty++;
687
+            }
688
+            $nbworkboardcount++;
689
+
690
+
691
+            $textlate = $langs->trans("NActionsLate", $board->nbtodolate);
692
+            $textlate .= ' (' . $langs->trans("Late") . ' = ' . $langs->trans("DateReference") . ' > ' . $langs->trans("DateToday") . ' ' . (ceil($board->warning_delay) >= 0 ? '+' : '') . ceil($board->warning_delay) . ' ' . $langs->trans("days") . ')';
693
+
694
+
695
+            $boxwork .= '<div class="boxstatsindicator thumbstat150 nobold nounderline"><div class="boxstats130 boxstatsborder">';
696
+            $boxwork .= '<div class="boxstatscontent">';
697
+            $boxwork .= '<span class="boxstatstext" title="' . dol_escape_htmltag($board->label) . '">' . $board->img . ' <span>' . $board->label . '</span></span><br>';
698
+            $boxwork .= '<a class="valignmiddle dashboardlineindicator" href="' . $board->url . '"><span class="dashboardlineindicator' . (($board->nbtodo == 0) ? ' dashboardlineok' : '') . '">' . $board->nbtodo . '</span></a>';
699
+            if ($board->total > 0 && getDolGlobalString('MAIN_WORKBOARD_SHOW_TOTAL_WO_TAX')) {
700
+                $boxwork .= '&nbsp;/&nbsp;<a class="valignmiddle dashboardlineindicator" href="' . $board->url . '"><span class="dashboardlineindicator' . (($board->nbtodo == 0) ? ' dashboardlineok' : '') . '">' . price($board->total) . '</span></a>';
701
+            }
702
+            $boxwork .= '</div>';
703
+            if ($board->nbtodolate > 0) {
704
+                $boxwork .= '<div class="dashboardlinelatecoin nowrap">';
705
+                $boxwork .= '<a title="' . dol_escape_htmltag($textlate) . '" class="valignmiddle dashboardlineindicatorlate' . ($board->nbtodolate > 0 ? ' dashboardlineko' : ' dashboardlineok') . '" href="' . ((!$board->url_late) ? $board->url : $board->url_late) . '">';
706
+                //$boxwork .= img_picto($textlate, "warning_white", 'class="valigntextbottom"');
707
+                $boxwork .= img_picto(
708
+                    $textlate,
709
+                    "warning_white",
710
+                    'class="inline-block hideonsmartphone valigntextbottom"'
711
+                );
712
+                $boxwork .= '<span class="dashboardlineindicatorlate' . ($board->nbtodolate > 0 ? ' dashboardlineko' : ' dashboardlineok') . '">';
713
+                $boxwork .= $board->nbtodolate;
714
+                $boxwork .= '</span>';
715
+                $boxwork .= '</a>';
716
+                $boxwork .= '</div>';
717
+            }
718
+            $boxwork .= '</div></div>';
719
+            $boxwork .= "\n";
720
+        }
721
+
722
+        $boxwork .= '<div class="boxstatsindicator thumbstat150 nobold nounderline"><div class="boxstats150empty"></div></div>';
723
+        $boxwork .= '<div class="boxstatsindicator thumbstat150 nobold nounderline"><div class="boxstats150empty"></div></div>';
724
+        $boxwork .= '<div class="boxstatsindicator thumbstat150 nobold nounderline"><div class="boxstats150empty"></div></div>';
725
+        $boxwork .= '<div class="boxstatsindicator thumbstat150 nobold nounderline"><div class="boxstats150empty"></div></div>';
726
+
727
+        $boxwork .= '</div>';
728
+        $boxwork .= '</td></tr>';
729
+    } else {
730
+        $boxwork .= '<tr class="nohover">';
731
+        $boxwork .= '<td class="nohover valignmiddle opacitymedium">';
732
+        $boxwork .= $langs->trans("NoOpenedElementToProcess");
733
+        $boxwork .= '</td>';
734
+        $boxwork .= '</tr>';
735
+    }
736
+
737
+    $boxwork .= '</td></tr>';
738
+
739
+    $boxwork .= '</table>'; // End table array of working board
740
+    $boxwork .= '</div>';
741
+
742
+    if (!empty($isIntopOpenedDashBoard)) {
743
+        print '<div class="fichecenter">';
744
+        print '<div class="opened-dash-board-wrap"><div class="box-flex-container">' . $openedDashBoard . '</div></div>';
745
+        print '</div>';
746
+    }
747 747
 }
748 748
 
749 749
 
@@ -760,7 +760,7 @@  discard block
 block discarded – undo
760 760
 
761 761
 $boxlist .= '<div class="firstcolumn fichehalfleft boxhalfleft" id="boxhalfleft">';
762 762
 if (!empty($nbworkboardcount)) {
763
-	$boxlist .= $boxwork;
763
+    $boxlist .= $boxwork;
764 764
 }
765 765
 
766 766
 $boxlist .= $resultboxes['boxlista'];
@@ -800,10 +800,10 @@  discard block
 block discarded – undo
800 800
  */
801 801
 function showWeather($totallate, $text, $options, $morecss = '')
802 802
 {
803
-	global $conf;
803
+    global $conf;
804 804
 
805
-	$weather = getWeatherStatus($totallate);
806
-	return img_weather($text, $weather->picto, $options, 0, $morecss);
805
+    $weather = getWeatherStatus($totallate);
806
+    return img_weather($text, $weather->picto, $options, 0, $morecss);
807 807
 }
808 808
 
809 809
 
@@ -815,40 +815,40 @@  discard block
 block discarded – undo
815 815
  */
816 816
 function getWeatherStatus($totallate)
817 817
 {
818
-	$weather = new stdClass();
819
-	$weather->picto = '';
820
-
821
-	$offset = 0;
822
-	$factor = 10; // By default
823
-
824
-	$used_conf = (getDolGlobalString('MAIN_USE_METEO_WITH_PERCENTAGE') ? 'MAIN_METEO_PERCENTAGE_LEVEL' : 'MAIN_METEO_LEVEL');
825
-
826
-	$weather->level = 0;
827
-	$level0 = $offset;
828
-	$level0 = getDolGlobalString($used_conf . '0', $level0);
829
-	$level1 = $offset + $factor;
830
-	$level1 = getDolGlobalString($used_conf . '1', $level1);
831
-	$level2 = $offset + 2 * $factor;
832
-	$level2 = getDolGlobalString($used_conf . '2', $level2);
833
-	$level3 = $offset + 3 * $factor;
834
-	$level3 = getDolGlobalString($used_conf . '3', $level3);
835
-
836
-	if ($totallate <= $level0) {
837
-		$weather->picto = 'weather-clear.png';
838
-		$weather->level = 0;
839
-	} elseif ($totallate <= $level1) {
840
-		$weather->picto = 'weather-few-clouds.png';
841
-		$weather->level = 1;
842
-	} elseif ($totallate <= $level2) {
843
-		$weather->picto = 'weather-clouds.png';
844
-		$weather->level = 2;
845
-	} elseif ($totallate <= $level3) {
846
-		$weather->picto = 'weather-many-clouds.png';
847
-		$weather->level = 3;
848
-	} else {
849
-		$weather->picto = 'weather-storm.png';
850
-		$weather->level = 4;
851
-	}
852
-
853
-	return $weather;
818
+    $weather = new stdClass();
819
+    $weather->picto = '';
820
+
821
+    $offset = 0;
822
+    $factor = 10; // By default
823
+
824
+    $used_conf = (getDolGlobalString('MAIN_USE_METEO_WITH_PERCENTAGE') ? 'MAIN_METEO_PERCENTAGE_LEVEL' : 'MAIN_METEO_LEVEL');
825
+
826
+    $weather->level = 0;
827
+    $level0 = $offset;
828
+    $level0 = getDolGlobalString($used_conf . '0', $level0);
829
+    $level1 = $offset + $factor;
830
+    $level1 = getDolGlobalString($used_conf . '1', $level1);
831
+    $level2 = $offset + 2 * $factor;
832
+    $level2 = getDolGlobalString($used_conf . '2', $level2);
833
+    $level3 = $offset + 3 * $factor;
834
+    $level3 = getDolGlobalString($used_conf . '3', $level3);
835
+
836
+    if ($totallate <= $level0) {
837
+        $weather->picto = 'weather-clear.png';
838
+        $weather->level = 0;
839
+    } elseif ($totallate <= $level1) {
840
+        $weather->picto = 'weather-few-clouds.png';
841
+        $weather->level = 1;
842
+    } elseif ($totallate <= $level2) {
843
+        $weather->picto = 'weather-clouds.png';
844
+        $weather->level = 2;
845
+    } elseif ($totallate <= $level3) {
846
+        $weather->picto = 'weather-many-clouds.png';
847
+        $weather->level = 3;
848
+    } else {
849
+        $weather->picto = 'weather-storm.png';
850
+        $weather->level = 4;
851
+    }
852
+
853
+    return $weather;
854 854
 }
Please login to merge, or discard this patch.
htdocs/core/modules/DolibarrModules.class.php 2 patches
Indentation   +2537 added lines, -2537 removed lines patch added patch discarded remove patch
@@ -41,1255 +41,1255 @@  discard block
 block discarded – undo
41 41
  */
42 42
 class DolibarrModules // Can not be abstract, because we need to instantiate it into unActivateModule to be able to disable a module whose files were removed.
43 43
 {
44
-	/**
45
-	 * @var DoliDB Database handler
46
-	 */
47
-	public $db;
48
-
49
-	/**
50
-	 * @var int Module unique ID
51
-	 * @see https://wiki.dolibarr.org/index.php/List_of_modules_id
52
-	 */
53
-	public $numero;
54
-
55
-	/**
56
-	 * @var   string Publisher name
57
-	 * @since 4.0.0
58
-	 */
59
-	public $editor_name;
60
-
61
-	/**
62
-	 * @var   string URL of module at publisher site
63
-	 * @since 4.0.0
64
-	 */
65
-	public $editor_url;
66
-
67
-	/**
68
-	 * @var string  Family
69
-	 * @see $familyinfo
70
-	 *
71
-	 * Native values: 'crm', 'financial', 'hr', 'projects', 'products', 'ecm', 'technic', 'other'.
72
-	 * Use familyinfo to declare a custom value.
73
-	 */
74
-	public $family;
75
-
76
-	/**
77
-	 * @var array<string,array{position:string,label:string}> Custom family information
78
-	 * @see $family
79
-	 *
80
-	 * e.g.:
81
-	 * array(
82
-	 *     'myownfamily' => array(
83
-	 *         'position' => '001',
84
-	 *         'label' => $langs->trans("MyOwnFamily")
85
-	 *     )
86
-	 * );
87
-	 */
88
-	public $familyinfo;
89
-
90
-	/**
91
-	 * @var string    Module position on 2 digits
92
-	 */
93
-	public $module_position = '50';
94
-
95
-	/**
96
-	 * @var string Module name
97
-	 *
98
-	 * Only used if Module[ID]Name translation string is not found.
99
-	 *
100
-	 * You can use the following code to automatically derive it from your module's class name:
101
-	 * preg_replace('/^mod/i', '', get_class($this))
102
-	 */
103
-	public $name;
104
-
105
-	/**
106
-	 * @var string[] Paths to create when module is activated
107
-	 *
108
-	 * e.g.: array('/mymodule/temp')
109
-	 */
110
-	public $dirs = [];
111
-
112
-	/**
113
-	 * @var array Module boxes
114
-	 */
115
-	public $boxes = [];
116
-
117
-	/**
118
-	 * @var array Module constants
119
-	 */
120
-	public $const = [];
121
-
122
-	/**
123
-	 * @var array Module cron jobs entries
124
-	 */
125
-	public $cronjobs = [];
126
-
127
-	/**
128
-	 * @var array   Module access rights
129
-	 */
130
-	public $rights;
131
-
132
-	/**
133
-	 * @var int     1=Admin is always granted of permission of modules (even when module is disabled)
134
-	 */
135
-	public $rights_admin_allowed;
136
-
137
-	/**
138
-	 * @var string  Module access rights family
139
-	 */
140
-	public $rights_class;
141
-
142
-	/**
143
-	 * @var array|int   Module menu entries (1 means the menu entries are not declared into module descriptor but are
144
-	 *      hardcoded into menu manager)
145
-	 */
146
-	public $menu = [];
147
-
148
-	/**
149
-	 * @var array{triggers?:int<0,1>,login?:int<0,1>,substitutions?:int<0,1>,menus?:int<0,1>,theme?:int<0,1>,tpl?:int<0,1>,barcode?:int<0,1>,models?:int<0,1>,printing?:int<0,1>,css?:string[],js?:string[],hooks?:array{data?:string[],entity?:string},moduleforexternal?:int<0,1>,websitetemplates?:int<0,1>,contactelement?:int<0,1>}
150
-	 *      Module parts array(
151
-	 *      // Set this to 1 if module has its own trigger directory (/mymodule/core/triggers)
152
-	 *      'triggers' => 0,
153
-	 *      // Set this to 1 if module has its own login method directory (/mymodule/core/login)
154
-	 *      'login' => 0,
155
-	 *      // Set this to 1 if module has its own substitution function file (/mymodule/core/substitutions)
156
-	 *      'substitutions' => 0,
157
-	 *      // Set this to 1 if module has its own menus handler directory (/mymodule/core/menus)
158
-	 *      'menus' => 0,
159
-	 *      // Set this to 1 if module has its own theme directory (/mymodule/theme)
160
-	 *      'theme' => 0,
161
-	 *      // Set this to 1 if module overwrite template dir (/mymodule/core/tpl)
162
-	 *      'tpl' => 0,
163
-	 *      // Set this to 1 if module has its own barcode directory (/mymodule/core/modules/barcode)
164
-	 *      'barcode' => 0,
165
-	 *      // Set this to 1 if module has its own models directory (/mymodule/core/modules/xxx)
166
-	 *      'models' => 0,
167
-	 *      // Set this to relative path of css file if module has its own css file
168
-	 *      'css' => '/mymodule/css/mymodule.css.php',
169
-	 *      // Set this to relative path of js file if module must load a js on all pages
170
-	 *      'js' => '/mymodule/js/mymodule.js',
171
-	 *      // Set here all hooks context managed by module
172
-	 *      'hooks' => array('hookcontext1','hookcontext2')
173
-	 *      )
174
-	 */
175
-	public $module_parts = [];
176
-
177
-	/**
178
-	 * @var        string Module documents ?
179
-	 * @deprecated Seems unused anywhere
180
-	 */
181
-	public $docs;
182
-
183
-	/**
184
-	 * @var        string ?
185
-	 * @deprecated Seems unused anywhere
186
-	 */
187
-	public $dbversion = "-";
188
-
189
-	/**
190
-	 * @var string Error message
191
-	 */
192
-	public $error;
193
-
194
-	/**
195
-	 * @var string[] Array of Errors messages
196
-	 */
197
-	public $errors;
198
-
199
-	/**
200
-	 * @var string Module version
201
-	 * @see http://semver.org
202
-	 *
203
-	 * The following keywords can also be used:
204
-	 * 'development'
205
-	 * 'experimental'
206
-	 * 'dolibarr': only for core modules that share its version
207
-	 * 'dolibarr_deprecated': only for deprecated core modules
208
-	 */
209
-	public $version;
210
-
211
-	/**
212
-	 * Module last version
213
-	 * @var string $lastVersion
214
-	 */
215
-	public $lastVersion = '';
216
-
217
-	/**
218
-	 * true indicate this module need update
219
-	 * @var bool $needUpdate
220
-	 */
221
-	public $needUpdate = false;
222
-
223
-	/**
224
-	 * @var string Module description (short text)
225
-	 *
226
-	 * Only used if Module[ID]Desc translation string is not found.
227
-	 */
228
-	public $description;
229
-
230
-	/**
231
-	 * @var   string Module description (long text)
232
-	 * @since 4.0.0
233
-	 *
234
-	 * HTML content supported.
235
-	 */
236
-	public $descriptionlong;
237
-
238
-	/**
239
-	 * @var array dictionaries description
240
-	 */
241
-	public $dictionaries;
242
-
243
-	/**
244
-	 * @var array tabs description
245
-	 */
246
-	public $tabs;
247
-
248
-	// For exports
249
-
250
-	/**
251
-	 * @var string Module export code
252
-	 */
253
-	public $export_code;
254
-
255
-	/**
256
-	 * @var string[] Module export label
257
-	 */
258
-	public $export_label;
259
-
260
-	public $export_icon;
261
-
262
-	/**
263
-	 * @var array export enabled
264
-	 */
265
-	public $export_enabled;
266
-	public $export_permission;
267
-	public $export_fields_array;
268
-	public $export_TypeFields_array; // Array of key=>type where type can be 'Numeric', 'Date', 'Text', 'Boolean', 'Status', 'List:xxx:login:rowid'
269
-	public $export_entities_array;
270
-	public $export_aggregate_array;
271
-	public $export_examplevalues_array;
272
-	public $export_help_array;
273
-	public $export_special_array; // special or computed field
274
-	public $export_dependencies_array;
275
-	public $export_sql_start;
276
-	public $export_sql_end;
277
-	public $export_sql_order;
278
-
279
-
280
-	// For import
281
-
282
-	/**
283
-	 * @var string Module import code
284
-	 */
285
-	public $import_code;
286
-
287
-	/**
288
-	 * @var string[] Module import label
289
-	 */
290
-	public $import_label;
291
-
292
-	public $import_icon;
293
-	public $import_entities_array;
294
-	public $import_tables_array;
295
-	public $import_tables_creator_array;
296
-	public $import_fields_array;
297
-	public $import_fieldshidden_array;
298
-	public $import_convertvalue_array;
299
-	public $import_regex_array;
300
-	public $import_examplevalues_array;
301
-	public $import_updatekeys_array;
302
-	public $import_run_sql_after_array;
303
-	public $import_TypeFields_array;
304
-	public $import_help_array;
305
-
306
-	/**
307
-	 * @var string Module constant name
308
-	 */
309
-	public $const_name;
310
-
311
-	/**
312
-	 * @var bool Module can't be disabled
313
-	 */
314
-	public $always_enabled;
315
-
316
-	/**
317
-	 * @var bool Module is disabled
318
-	 */
319
-	public $disabled;
320
-
321
-	/**
322
-	 * @var int Module is enabled globally (Multicompany support)
323
-	 */
324
-	public $core_enabled;
325
-
326
-	/**
327
-	 * @var string Name of image file used for this module
328
-	 *
329
-	 * If file is in theme/yourtheme/img directory under name object_pictoname.png use 'pictoname'
330
-	 * If file is in module/img directory under name object_pictoname.png use 'pictoname@module'
331
-	 */
332
-	public $picto;
333
-
334
-	/**
335
-	 * @var string[]|string     List of config pages (Old modules uses a string. New one must use an array)
336
-	 *
337
-	 * Name of php pages stored into module/admin directory, used to setup module.
338
-	 * e.g.: array("setup.php@mymodule")
339
-	 */
340
-	public $config_page_url;
341
-
342
-
343
-	/**
344
-	 * @var array   List of module class names that must be enabled if this module is enabled. e.g.:
345
-	 *      array('modAnotherModule', 'FR'=>'modYetAnotherModule') Another example : array('always'=>array("modBanque",
346
-	 *      "modFacture", "modProduct", "modCategorie"), 'FR'=>array('modBlockedLog'));
347
-	 * @see $requiredby
348
-	 */
349
-	public $depends;
350
-
351
-	/**
352
-	 * @var string[] List of module class names to disable if the module is disabled.
353
-	 * @see $depends
354
-	 */
355
-	public $requiredby;
356
-
357
-	/**
358
-	 * @var string[] List of module class names as string this module is in conflict with.
359
-	 * @see $depends
360
-	 */
361
-	public $conflictwith;
362
-
363
-	/**
364
-	 * @var string[] Module language files
365
-	 */
366
-	public $langfiles;
367
-
368
-	/**
369
-	 * @var array<string,string> Array of warnings to show when we activate the module
370
-	 *
371
-	 * array('always'='text') or array('FR'='text')
372
-	 */
373
-	public $warnings_activation;
374
-
375
-	/**
376
-	 * @var array<string,string> Array of warnings to show when we activate an external module
377
-	 *
378
-	 * array('always'='text') or array('FR'='text')
379
-	 */
380
-	public $warnings_activation_ext;
381
-
382
-	/**
383
-	 * @var array<string,string> Array of warnings to show when we disable the module
384
-	 *
385
-	 * array('always'='text') or array('FR'='text')
386
-	 */
387
-	public $warnings_unactivation;
388
-
389
-	/**
390
-	 * @var array Minimum version of PHP required by module.
391
-	 * e.g.: PHP ≥ 7.0 = array(7, 0)
392
-	 */
393
-	public $phpmin;
394
-
395
-	public $phpmax;
396
-
397
-	/**
398
-	 * @var array Minimum version of Dolibarr required by module.
399
-	 * e.g.: Dolibarr ≥ 3.6 = array(3, 6)
400
-	 */
401
-	public $need_dolibarr_version;
402
-
403
-	public $need_javascript_ajax;
404
-
405
-	public $enabled_bydefault;
406
-
407
-	/**
408
-	 * @var bool|int Whether to hide the module.
409
-	 */
410
-	public $hidden = false;
411
-
412
-	/**
413
-	 * @var string url to check for module update
414
-	 */
415
-	public $url_last_version;
416
-
417
-
418
-	/**
419
-	 * Constructor. Define names, constants, directories, boxes, permissions
420
-	 *
421
-	 * @param DoliDB $db Database handler
422
-	 */
423
-	public function __construct($db)
424
-	{
425
-		$this->db = $db;
426
-	}
427
-	// We should but can't set this as abstract because this will make dolibarr hang
428
-	// after migration due to old module not implementing. We must wait PHP is able to make
429
-	// a try catch on Fatal error to manage this correctly.
430
-	// We need constructor into function unActivateModule into admin.lib.php
431
-
432
-
433
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
434
-
435
-	/**
436
-	 * Gives the long description of a module. First check README-la_LA.md then README.md
437
-	 * If no markdown files found, it returns translated value of the key ->descriptionlong.
438
-	 *
439
-	 * @return string     Long description of a module from README.md of from property.
440
-	 */
441
-	public function getDescLong()
442
-	{
443
-		global $langs;
444
-		$langs->load("admin");
445
-
446
-		include_once BASE_PATH . '/../Dolibarr/Lib/Files.php';
447
-		include_once BASE_PATH . '/../Dolibarr/Lib/GetUrl.php';
448
-
449
-		$content = '';
450
-		$pathoffile = $this->getDescLongReadmeFound();
451
-
452
-		if ($pathoffile) {     // Mostly for external modules
453
-			$content = file_get_contents($pathoffile);
454
-
455
-			if ((float)DOL_VERSION >= 6.0) {
456
-				@include_once BASE_PATH . '/../Dolibarr/Lib/ParseMd.php';
457
-
458
-				$content = dolMd2Html(
459
-					$content,
460
-					'parsedown',
461
-					[
462
-						'doc/' => dol_buildpath(strtolower($this->name) . '/doc/', 1),
463
-						'img/' => dol_buildpath(strtolower($this->name) . '/img/', 1),
464
-						'images/' => dol_buildpath(strtolower($this->name) . '/images/', 1),
465
-					]
466
-				);
467
-
468
-				$content = preg_replace('/<a href="/', '<a target="_blank" rel="noopener noreferrer" href="', $content);
469
-			} else {
470
-				$content = nl2br($content);
471
-			}
472
-		} else {
473
-			// Mostly for internal modules
474
-			if (!empty($this->descriptionlong)) {
475
-				if (is_array($this->langfiles)) {
476
-					foreach ($this->langfiles as $val) {
477
-						if ($val) {
478
-							$langs->load($val);
479
-						}
480
-					}
481
-				}
482
-
483
-				$content = $langs->transnoentitiesnoconv($this->descriptionlong);
484
-			}
485
-		}
486
-
487
-		return $content;
488
-	}
489
-
490
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
491
-
492
-	/**
493
-	 * Return path of file if a README file was found.
494
-	 *
495
-	 * @return string      Path of file if a README file was found.
496
-	 */
497
-	public function getDescLongReadmeFound()
498
-	{
499
-		global $langs;
500
-
501
-		$filefound = false;
502
-
503
-		// Define path to file README.md.
504
-		// First check README-la_LA.md then README-la.md then README.md
505
-		$pathoffile = dol_buildpath(strtolower($this->name) . '/README-' . $langs->defaultlang . '.md', 0);
506
-		if (dol_is_file($pathoffile)) {
507
-			$filefound = true;
508
-		}
509
-		if (!$filefound) {
510
-			$tmp = explode('_', $langs->defaultlang);
511
-			$pathoffile = dol_buildpath(strtolower($this->name) . '/README-' . $tmp[0] . '.md', 0);
512
-			if (dol_is_file($pathoffile)) {
513
-				$filefound = true;
514
-			}
515
-		}
516
-		if (!$filefound) {
517
-			$pathoffile = dol_buildpath(strtolower($this->name) . '/README.md', 0);
518
-			if (dol_is_file($pathoffile)) {
519
-				$filefound = true;
520
-			}
521
-		}
522
-
523
-		return ($filefound ? $pathoffile : '');
524
-	}
525
-
526
-	/**
527
-	 * Gives the changelog. First check ChangeLog-la_LA.md then ChangeLog.md
528
-	 *
529
-	 * @return string  Content of ChangeLog
530
-	 */
531
-	public function getChangeLog()
532
-	{
533
-		global $langs;
534
-		$langs->load("admin");
535
-
536
-		include_once BASE_PATH . '/../Dolibarr/Lib/Files.php';
537
-		include_once BASE_PATH . '/../Dolibarr/Lib/GetUrl.php';
538
-
539
-		$filefound = false;
540
-
541
-		// Define path to file README.md.
542
-		// First check ChangeLog-la_LA.md then ChangeLog.md
543
-		$pathoffile = dol_buildpath(strtolower($this->name) . '/ChangeLog-' . $langs->defaultlang . '.md', 0);
544
-		if (dol_is_file($pathoffile)) {
545
-			$filefound = true;
546
-		}
547
-		if (!$filefound) {
548
-			$pathoffile = dol_buildpath(strtolower($this->name) . '/ChangeLog.md', 0);
549
-			if (dol_is_file($pathoffile)) {
550
-				$filefound = true;
551
-			}
552
-		}
553
-
554
-		if ($filefound) {     // Mostly for external modules
555
-			$content = file_get_contents($pathoffile);
556
-
557
-			if ((float)DOL_VERSION >= 6.0) {
558
-				@include_once BASE_PATH . '/../Dolibarr/Lib/ParseMd.php';
559
-
560
-				$content = dolMd2Html($content, 'parsedown', ['doc/' => dol_buildpath(strtolower($this->name) . '/doc/', 1)]);
561
-			} else {
562
-				$content = nl2br($content);
563
-			}
564
-		}
565
-
566
-		return $content;
567
-	}
568
-
569
-	/**
570
-	 * Gives the publisher name
571
-	 *
572
-	 * @return string  Publisher name
573
-	 */
574
-	public function getPublisher()
575
-	{
576
-		return $this->editor_name;
577
-	}
578
-
579
-	/**
580
-	 * Gives the publisher url
581
-	 *
582
-	 * @return string  Publisher url
583
-	 */
584
-	public function getPublisherUrl()
585
-	{
586
-		return $this->editor_url;
587
-	}
588
-
589
-	/**
590
-	 * Gives the module position
591
-	 *
592
-	 * @return string   Module position (an external module should never return a value lower than 100000. 1-100000 are
593
-	 *                  reserved for core)
594
-	 */
595
-	public function getModulePosition()
596
-	{
597
-		if (in_array($this->version, ['dolibarr', 'experimental', 'development'])) {   // core module
598
-			return $this->module_position;
599
-		} else {                                                                            // external module
600
-			if ($this->module_position >= 100000) {
601
-				return $this->module_position;
602
-			} else {
603
-				$position = intval($this->module_position) + 100000;
604
-				return strval($position);
605
-			}
606
-		}
607
-	}
608
-
609
-	/**
610
-	 * Gives module related language files list
611
-	 *
612
-	 * @return string[]    Language files list
613
-	 */
614
-	public function getLangFilesArray()
615
-	{
616
-		return $this->langfiles;
617
-	}
618
-
619
-	/**
620
-	 * Gives translated label of an export dataset
621
-	 *
622
-	 * @param int $r Dataset index
623
-	 *
624
-	 * @return string       Translated databaset label
625
-	 */
626
-	public function getExportDatasetLabel($r)
627
-	{
628
-		global $langs;
629
-
630
-		$langstring = "ExportDataset_" . $this->export_code[$r];
631
-		if ($langs->trans($langstring) == $langstring) {
632
-			// Translation not found
633
-			return $langs->trans($this->export_label[$r]);
634
-		} else {
635
-			// Translation found
636
-			return $langs->trans($langstring);
637
-		}
638
-	}
639
-
640
-	/**
641
-	 * Gives translated label of an import dataset
642
-	 *
643
-	 * @param int $r Dataset index
644
-	 *
645
-	 * @return string      Translated dataset label
646
-	 */
647
-	public function getImportDatasetLabel($r)
648
-	{
649
-		global $langs;
650
-
651
-		$langstring = "ImportDataset_" . $this->import_code[$r];
652
-		//print "x".$langstring;
653
-		if ($langs->trans($langstring) == $langstring) {
654
-			// Translation not found
655
-			return $langs->transnoentitiesnoconv($this->import_label[$r]);
656
-		} else {
657
-			// Translation found
658
-			return $langs->transnoentitiesnoconv($langstring);
659
-		}
660
-	}
661
-
662
-	/**
663
-	 * Gives the last date of activation
664
-	 *
665
-	 * @return  int|string          Date of last activation or '' if module was never activated
666
-	 */
667
-	public function getLastActivationDate()
668
-	{
669
-		global $conf;
670
-
671
-		$err = 0;
672
-
673
-		$sql = "SELECT tms FROM " . $dbPrefix . "const";
674
-		$sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
675
-		$sql .= " AND entity IN (0, " . ((int)$conf->entity) . ")";
676
-
677
-		dol_syslog(get_class($this) . "::getLastActiveDate", LOG_DEBUG);
678
-		$resql = $this->db->query($sql);
679
-		if (!$resql) {
680
-			$err++;
681
-		} else {
682
-			$obj = $this->db->fetch_object($resql);
683
-			if ($obj) {
684
-				return $this->db->jdate($obj->tms);
685
-			}
686
-		}
687
-
688
-		return '';
689
-	}
690
-
691
-	/**
692
-	 * Gives the last author of activation
693
-	 *
694
-	 * @return array       Array array('authorid'=>Id of last activation user, 'lastactivationdate'=>Date of last
695
-	 *                     activation)
696
-	 */
697
-	public function getLastActivationInfo()
698
-	{
699
-		global $conf;
700
-
701
-		$err = 0;
702
-
703
-		$sql = "SELECT tms, note FROM " . $dbPrefix . "const";
704
-		$sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
705
-		$sql .= " AND entity IN (0, " . $conf->entity . ")";
706
-
707
-		dol_syslog(get_class($this) . "::getLastActiveDate", LOG_DEBUG);
708
-		$resql = $this->db->query($sql);
709
-		if (!$resql) {
710
-			$err++;
711
-		} else {
712
-			$obj = $this->db->fetch_object($resql);
713
-			if ($obj) {
714
-				$tmp = [];
715
-				if ($obj->note) {
716
-					$tmp = json_decode($obj->note, true);
717
-				}
718
-				return [
719
-					'authorid' => empty($tmp['authorid']) ? '' : $tmp['authorid'],
720
-					'ip' => empty($tmp['ip']) ? '' : $tmp['ip'],
721
-					'lastactivationdate' => $this->db->jdate($obj->tms),
722
-					'lastactivationversion' => (!empty($tmp['lastactivationversion']) ? $tmp['lastactivationversion'] : 'unknown'),
723
-				];
724
-			}
725
-		}
726
-
727
-		return [];
728
-	}
729
-
730
-	/**
731
-	 * Function called when module is enabled.
732
-	 * The init function adds tabs, constants, boxes, permissions and menus (defined in constructor) into Dolibarr
733
-	 * database. It also creates data directories
734
-	 *
735
-	 * @param string $options Options when enabling module ('', 'newboxdefonly', 'noboxes', 'menuonly')
736
-	 *                         'noboxes' = Do not insert boxes 'newboxdefonly' = For boxes, insert def of boxes only
737
-	 *                         and not boxes activation
738
-	 *
739
-	 * @return int                1 if OK, 0 if KO
740
-	 */
741
-	public function init($options = '')
742
-	{
743
-		return $this->_init([], $options);
744
-	}
745
-
746
-	/**
747
-	 * Enables a module.
748
-	 * Inserts all information into database.
749
-	 *
750
-	 * @param array $array_sql SQL requests to be executed when enabling module
751
-	 * @param string $options String with options when disabling module:
752
-	 *                              - 'noboxes' = Do all actions but do not insert boxes
753
-	 *                              - 'newboxdefonly' = Do all actions but for boxes, insert def of boxes only and not
754
-	 *                              boxes activation
755
-	 *
756
-	 * @return int                  1 if OK, 0 if KO
757
-	 */
758
-	protected function _init($array_sql, $options = '')
759
-	{
760
-		// phpcs:enable
761
-		$conf = \DoliCore\Base\Config::getConf();
762
-
763
-		$err = 0;
764
-
765
-		$this->db->begin();
766
-
767
-		// Insert activation module constant
768
-		if (!$err) {
769
-			$err += $this->_active();
770
-		}
771
-
772
-		// Insert new pages for tabs (into llx_const)
773
-		if (!$err) {
774
-			$err += $this->insert_tabs();
775
-		}
776
-
777
-		// Insert activation of module's parts. Copy website templates into doctemplates.
778
-		if (!$err) {
779
-			$err += $this->insert_module_parts();
780
-		}
781
-
782
-		// Insert constant defined by modules (into llx_const)
783
-		if (!$err && !preg_match('/newboxdefonly/', $options)) {
784
-			$err += $this->insert_const(); // Test on newboxdefonly to avoid to erase value during upgrade
785
-		}
786
-
787
-		// Insert boxes def (into llx_boxes_def) and boxes setup (into llx_boxes)
788
-		if (!$err && !preg_match('/noboxes/', $options)) {
789
-			$err += $this->insert_boxes($options);
790
-		}
791
-
792
-		// Insert cron job entries (entry in llx_cronjobs)
793
-		if (!$err) {
794
-			$err += $this->insert_cronjobs();
795
-		}
796
-
797
-		// Insert permission definitions of module into llx_rights_def. If user is admin, grant this permission to user.
798
-		if (!$err) {
799
-			$err += $this->insert_permissions(1, null, 1);
800
-		}
801
-
802
-		// Insert specific menus entries into database
803
-		if (!$err) {
804
-			$err += $this->insert_menus();
805
-		}
806
-
807
-		// Create module's directories
808
-		if (!$err) {
809
-			$err += $this->create_dirs();
810
-		}
811
-
812
-		// Execute addons requests
813
-		$num = count($array_sql);
814
-		for ($i = 0; $i < $num; $i++) {
815
-			if (!$err) {
816
-				$val = $array_sql[$i];
817
-				$sql = $val;
818
-				$ignoreerror = 0;
819
-				if (is_array($val)) {
820
-					$sql = $val['sql'];
821
-					$ignoreerror = $val['ignoreerror'];
822
-				}
823
-				// Add current entity id
824
-				$sql = str_replace('__ENTITY__', $conf->entity, $sql);
825
-
826
-				dol_syslog(get_class($this) . "::_init ignoreerror=" . $ignoreerror, LOG_DEBUG);
827
-				$result = $this->db->query($sql, $ignoreerror);
828
-				if (!$result) {
829
-					if (!$ignoreerror) {
830
-						$this->error = $this->db->lasterror();
831
-						$err++;
832
-					} else {
833
-						dol_syslog(get_class($this) . "::_init Warning " . $this->db->lasterror(), LOG_WARNING);
834
-					}
835
-				}
836
-			}
837
-		}
838
-
839
-		// Return code
840
-		if (!$err) {
841
-			$this->db->commit();
842
-			return 1;
843
-		} else {
844
-			$this->db->rollback();
845
-			return 0;
846
-		}
847
-	}
848
-
849
-	/**
850
-	 * Insert constants for module activation
851
-	 *
852
-	 * @return int Error count (0 if OK)
853
-	 */
854
-	protected function _active()
855
-	{
856
-		// phpcs:enable
857
-		global $conf, $user;
858
-		global $config;
859
-		$dbPrefix = $config->db->prefix;
860
-
861
-		$err = 0;
862
-
863
-		// Common module
864
-		$entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
865
-
866
-		$sql = "DELETE FROM " . $dbPrefix . "const";
867
-		$sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
868
-		$sql .= " AND entity IN (0, " . $entity . ")";
869
-
870
-		dol_syslog(get_class($this) . "::_active delete activation constant", LOG_DEBUG);
871
-		$resql = $this->db->query($sql);
872
-		if (!$resql) {
873
-			$err++;
874
-		}
875
-
876
-		$note = json_encode(
877
-			[
878
-				'authorid' => (is_object($user) ? $user->id : 0),
879
-				'ip' => (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']),
880
-				'lastactivationversion' => $this->version,
881
-			]
882
-		);
883
-
884
-		$sql = "INSERT INTO " . $dbPrefix . "const (name, value, visible, entity, note) VALUES";
885
-		$sql .= " (" . $this->db->encrypt($this->const_name);
886
-		$sql .= ", " . $this->db->encrypt('1');
887
-		$sql .= ", 0, " . ((int)$entity);
888
-		$sql .= ", '" . $this->db->escape($note) . "')";
889
-
890
-		dol_syslog(get_class($this) . "::_active insert activation constant", LOG_DEBUG);
891
-
892
-		$resql = $this->db->query($sql);
893
-
894
-		if (!$resql) {
895
-			$err++;
896
-		}
897
-
898
-		return $err;
899
-	}
900
-
901
-	/**
902
-	 * Adds tabs
903
-	 *
904
-	 * @return int  Error count (0 if ok)
905
-	 */
906
-	public function insert_tabs()
907
-	{
908
-		// phpcs:enable
909
-		global $conf;
910
-
911
-		$err = 0;
912
-
913
-		if (!empty($this->tabs)) {
914
-			dol_syslog(get_class($this) . "::insert_tabs", LOG_DEBUG);
915
-
916
-			$i = 0;
917
-			foreach ($this->tabs as $key => $value) {
918
-				if (is_array($value) && count($value) == 0) {
919
-					continue; // Discard empty arrays
920
-				}
921
-
922
-				$entity = $conf->entity;
923
-				$newvalue = $value;
924
-
925
-				if (is_array($value)) {
926
-					$newvalue = $value['data'];
927
-					if (isset($value['entity'])) {
928
-						$entity = $value['entity'];
929
-					}
930
-				}
931
-
932
-				if ($newvalue) {
933
-					$sql = "INSERT INTO " . $dbPrefix . "const (";
934
-					$sql .= "name";
935
-					$sql .= ", type";
936
-					$sql .= ", value";
937
-					$sql .= ", note";
938
-					$sql .= ", visible";
939
-					$sql .= ", entity";
940
-					$sql .= ")";
941
-					$sql .= " VALUES (";
942
-					$sql .= $this->db->encrypt($this->const_name . "_TABS_" . $i);
943
-					$sql .= ", 'chaine'";
944
-					$sql .= ", " . $this->db->encrypt($newvalue);
945
-					$sql .= ", null";
946
-					$sql .= ", '0'";
947
-					$sql .= ", " . ((int)$entity);
948
-					$sql .= ")";
949
-
950
-					$resql = $this->db->query($sql);
951
-					if (!$resql) {
952
-						dol_syslog($this->db->lasterror(), LOG_ERR);
953
-						if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
954
-							$this->error = $this->db->lasterror();
955
-							$this->errors[] = $this->db->lasterror();
956
-							$err++;
957
-							break;
958
-						}
959
-					}
960
-				}
961
-				$i++;
962
-			}
963
-		}
964
-		return $err;
965
-	}
966
-
967
-	/**
968
-	 * Save configuration for generic features.
969
-	 * This also generate website templates if the module provide some.
970
-	 *
971
-	 * @return int Error count (0 if OK)
972
-	 */
973
-	public function insert_module_parts()
974
-	{
975
-		// phpcs:enable
976
-		global $conf, $langs;
977
-
978
-		$error = 0;
979
-
980
-		if (is_array($this->module_parts)) {
981
-			if (empty($this->module_parts['icon']) && !empty($this->picto) && preg_match('/^fa\-/', $this->picto)) {
982
-				$this->module_parts['icon'] = $this->picto;
983
-			}
984
-
985
-			foreach ($this->module_parts as $key => $value) {
986
-				if (is_array($value) && count($value) == 0) {
987
-					continue; // Discard empty arrays
988
-				}
989
-
990
-				// If module brings website templates, we must generate the zip like we do whenenabling the website module
991
-				if ($key == 'websitetemplates' && $value == 1) {
992
-					$srcroot = dol_buildpath('/' . strtolower($this->name) . '/doctemplates/websites');
993
-
994
-					// Copy templates in dir format (recommended) into zip file
995
-					$docs = dol_dir_list($srcroot, 'directories', 0, 'website_.*$');
996
-					foreach ($docs as $cursorfile) {
997
-						$src = $srcroot . '/' . $cursorfile['name'];
998
-						$dest = DOL_DATA_ROOT . '/doctemplates/websites/' . $cursorfile['name'];
999
-
1000
-						dol_delete_file($dest . '.zip');
1001
-
1002
-						// Compress it
1003
-						global $errormsg;
1004
-						$errormsg = '';
1005
-						$result = dol_compress_dir($src, $dest . '.zip', 'zip');
1006
-						if ($result < 0) {
1007
-							$error++;
1008
-							$this->error = ($errormsg ? $errormsg : $langs->trans('ErrorFailToCreateZip', $dest));
1009
-							$this->errors[] = ($errormsg ? $errormsg : $langs->trans('ErrorFailToCreateZip', $dest));
1010
-						}
1011
-					}
1012
-
1013
-					// Copy also the preview website_xxx.jpg file
1014
-					$docs = dol_dir_list($srcroot, 'files', 0, 'website_.*\.jpg$');
1015
-					foreach ($docs as $cursorfile) {
1016
-						$src = $srcroot . '/' . $cursorfile['name'];
1017
-						$dest = DOL_DATA_ROOT . '/doctemplates/websites/' . $cursorfile['name'];
1018
-
1019
-						dol_copy($src, $dest);
1020
-					}
1021
-				}
1022
-
1023
-				$entity = $conf->entity; // Reset the current entity
1024
-				$newvalue = $value;
1025
-
1026
-				// Serialize array parameters
1027
-				if (is_array($value)) {
1028
-					// Can defined other parameters
1029
-					// Example when $key='hooks', then $value is an array('data'=>array('hookcontext1','hookcontext2'), 'entity'=>X)
1030
-					if (isset($value['data']) && is_array($value['data'])) {
1031
-						$newvalue = json_encode($value['data']);
1032
-						if (isset($value['entity'])) {
1033
-							$entity = $value['entity'];
1034
-						}
1035
-					} elseif (isset($value['data']) && !is_array($value['data'])) {
1036
-						$newvalue = $value['data'];
1037
-						if (isset($value['entity'])) {
1038
-							$entity = $value['entity'];
1039
-						}
1040
-					} else { // when hook is declared with syntax 'hook'=>array('hookcontext1','hookcontext2',...)
1041
-						$newvalue = json_encode($value);
1042
-					}
1043
-				}
1044
-
1045
-				if (!empty($newvalue)) {
1046
-					$sql = "INSERT INTO " . $dbPrefix . "const (";
1047
-					$sql .= "name";
1048
-					$sql .= ", type";
1049
-					$sql .= ", value";
1050
-					$sql .= ", note";
1051
-					$sql .= ", visible";
1052
-					$sql .= ", entity";
1053
-					$sql .= ")";
1054
-					$sql .= " VALUES (";
1055
-					$sql .= " " . $this->db->encrypt($this->const_name . "_" . strtoupper($key), 1);
1056
-					$sql .= ", 'chaine'";
1057
-					$sql .= ", " . $this->db->encrypt($newvalue, 1);
1058
-					$sql .= ", null";
1059
-					$sql .= ", '0'";
1060
-					$sql .= ", " . ((int)$entity);
1061
-					$sql .= ")";
1062
-
1063
-					dol_syslog(get_class($this) . "::insert_module_parts for key=" . $this->const_name . "_" . strtoupper($key), LOG_DEBUG);
1064
-
1065
-					$resql = $this->db->query($sql, 1);
1066
-					if (!$resql) {
1067
-						if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1068
-							$error++;
1069
-							$this->error = $this->db->lasterror();
1070
-						} else {
1071
-							dol_syslog(get_class($this) . "::insert_module_parts for " . $this->const_name . "_" . strtoupper($key) . " Record already exists.", LOG_WARNING);
1072
-						}
1073
-					}
1074
-				}
1075
-			}
1076
-		}
1077
-		return $error;
1078
-	}
1079
-
1080
-	/**
1081
-	 * Adds constants
1082
-	 *
1083
-	 * @return int Error count (0 if OK)
1084
-	 */
1085
-	public function insert_const()
1086
-	{
1087
-		// phpcs:enable
1088
-		global $conf;
1089
-
1090
-		$err = 0;
1091
-
1092
-		if (empty($this->const)) {
1093
-			return 0;
1094
-		}
1095
-
1096
-		dol_syslog(__METHOD__, LOG_DEBUG);
1097
-
1098
-		foreach ($this->const as $key => $value) {
1099
-			$name = $this->const[$key][0];
1100
-			$type = $this->const[$key][1];
1101
-			$val = $this->const[$key][2];
1102
-			$note = isset($this->const[$key][3]) ? $this->const[$key][3] : '';
1103
-			$visible = isset($this->const[$key][4]) ? $this->const[$key][4] : 0;
1104
-			$entity = (!empty($this->const[$key][5]) && $this->const[$key][5] != 'current') ? 0 : $conf->entity;
1105
-
1106
-			// Clean
1107
-			if (empty($visible)) {
1108
-				$visible = '0';
1109
-			}
1110
-			if (empty($val) && $val != '0') {
1111
-				$val = '';
1112
-			}
1113
-
1114
-			$sql = "SELECT count(*) as nb";
1115
-			$sql .= " FROM " . $dbPrefix . "const";
1116
-			$sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($name) . "'";
1117
-			$sql .= " AND entity = " . ((int)$entity);
1118
-
1119
-			$result = $this->db->query($sql);
1120
-			if ($result) {
1121
-				$row = $this->db->fetch_row($result);
1122
-
1123
-				if ($row[0] == 0) {   // If not found
1124
-					$sql = "INSERT INTO " . $dbPrefix . "const (name,type,value,note,visible,entity)";
1125
-					$sql .= " VALUES (";
1126
-					$sql .= $this->db->encrypt($name);
1127
-					$sql .= ",'" . $this->db->escape($type) . "'";
1128
-					$sql .= "," . (($val != '') ? $this->db->encrypt($val) : "''");
1129
-					$sql .= "," . ($note ? "'" . $this->db->escape($note) . "'" : "null");
1130
-					$sql .= ",'" . $this->db->escape($visible) . "'";
1131
-					$sql .= "," . $entity;
1132
-					$sql .= ")";
1133
-
1134
-					if (!$this->db->query($sql)) {
1135
-						$err++;
1136
-					}
1137
-				} else {
1138
-					dol_syslog(__METHOD__ . " constant '" . $name . "' already exists", LOG_DEBUG);
1139
-				}
1140
-			} else {
1141
-				$err++;
1142
-			}
1143
-		}
1144
-
1145
-		return $err;
1146
-	}
1147
-
1148
-
1149
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1150
-
1151
-	/**
1152
-	 * Adds boxes
1153
-	 *
1154
-	 * @param string $option Options when disabling module ('newboxdefonly'=insert only boxes definition)
1155
-	 *
1156
-	 * @return int             Error count (0 if OK)
1157
-	 */
1158
-	public function insert_boxes($option = '')
1159
-	{
1160
-		// phpcs:enable
1161
-		global $conf;
1162
-		global $config;
1163
-		$dbPrefix = $config->db->prefix;
1164
-
1165
-
1166
-		$err = 0;
1167
-
1168
-		if (is_array($this->boxes)) {
1169
-			dol_syslog(get_class($this) . "::insert_boxes", LOG_DEBUG);
1170
-
1171
-			$pos_name = InfoBox::getListOfPagesForBoxes();
1172
-
1173
-			foreach ($this->boxes as $key => $value) {
1174
-				$file = isset($this->boxes[$key]['file']) ? $this->boxes[$key]['file'] : '';
1175
-				$note = isset($this->boxes[$key]['note']) ? $this->boxes[$key]['note'] : '';
1176
-				$enabledbydefaulton = isset($this->boxes[$key]['enabledbydefaulton']) ? $this->boxes[$key]['enabledbydefaulton'] : 'Home';
1177
-
1178
-				if (empty($file)) {
1179
-					$file = isset($this->boxes[$key][1]) ? $this->boxes[$key][1] : ''; // For backward compatibility
1180
-				}
1181
-				if (empty($note)) {
1182
-					$note = isset($this->boxes[$key][2]) ? $this->boxes[$key][2] : ''; // For backward compatibility
1183
-				}
1184
-
1185
-				// Search if boxes def already present
1186
-				$sql = "SELECT count(*) as nb FROM " . $dbPrefix . "boxes_def";
1187
-				$sql .= " WHERE file = '" . $this->db->escape($file) . "'";
1188
-				$sql .= " AND entity = " . $conf->entity;
1189
-				if ($note) {
1190
-					$sql .= " AND note ='" . $this->db->escape($note) . "'";
1191
-				}
1192
-
1193
-				$result = $this->db->query($sql);
1194
-				if ($result) {
1195
-					$obj = $this->db->fetch_object($result);
1196
-					if ($obj->nb == 0) {
1197
-						$this->db->begin();
1198
-
1199
-						if (!$err) {
1200
-							$sql = "INSERT INTO " . $dbPrefix . "boxes_def (file, entity, note)";
1201
-							$sql .= " VALUES ('" . $this->db->escape($file) . "', ";
1202
-							$sql .= $conf->entity . ", ";
1203
-							$sql .= $note ? "'" . $this->db->escape($note) . "'" : "null";
1204
-							$sql .= ")";
1205
-
1206
-							dol_syslog(get_class($this) . "::insert_boxes", LOG_DEBUG);
1207
-							$resql = $this->db->query($sql);
1208
-							if (!$resql) {
1209
-								$err++;
1210
-							}
1211
-						}
1212
-						if (!$err && !preg_match('/newboxdefonly/', $option)) {
1213
-							$lastid = $this->db->last_insert_id($dbPrefix . "boxes_def", "rowid");
1214
-
1215
-							foreach ($pos_name as $key2 => $val2) {
1216
-								//print 'key2='.$key2.'-val2='.$val2."<br>\n";
1217
-								if ($enabledbydefaulton && $val2 != $enabledbydefaulton) {
1218
-									continue; // Not enabled by default onto this page.
1219
-								}
1220
-
1221
-								$sql = "INSERT INTO " . $dbPrefix . "boxes (box_id, position, box_order, fk_user, entity)";
1222
-								$sql .= " VALUES (" . ((int)$lastid) . ", " . ((int)$key2) . ", '0', 0, " . ((int)$conf->entity) . ")";
1223
-
1224
-								dol_syslog(get_class($this) . "::insert_boxes onto page " . $key2 . "=" . $val2, LOG_DEBUG);
1225
-								$resql = $this->db->query($sql);
1226
-								if (!$resql) {
1227
-									$err++;
1228
-								}
1229
-							}
1230
-						}
1231
-
1232
-						if (!$err) {
1233
-							$this->db->commit();
1234
-						} else {
1235
-							$this->error = $this->db->lasterror();
1236
-							$this->db->rollback();
1237
-						}
1238
-					}
1239
-					// else box already registered into database
1240
-				} else {
1241
-					$this->error = $this->db->lasterror();
1242
-					$err++;
1243
-				}
1244
-			}
1245
-		}
1246
-
1247
-		return $err;
1248
-	}
1249
-
1250
-
1251
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1252
-
1253
-	/**
1254
-	 * Adds cronjobs
1255
-	 *
1256
-	 * @return int             Error count (0 if OK)
1257
-	 */
1258
-	public function insert_cronjobs()
1259
-	{
1260
-		// phpcs:enable
1261
-		include_once DOL_DOCUMENT_ROOT . '/core/class/infobox.class.php';
1262
-
1263
-		global $conf;
1264
-
1265
-		$err = 0;
1266
-
1267
-		if (is_array($this->cronjobs)) {
1268
-			dol_syslog(get_class($this) . "::insert_cronjobs", LOG_DEBUG);
1269
-
1270
-			foreach ($this->cronjobs as $key => $value) {
1271
-				$entity = isset($this->cronjobs[$key]['entity']) ? $this->cronjobs[$key]['entity'] : $conf->entity;
1272
-				$label = isset($this->cronjobs[$key]['label']) ? $this->cronjobs[$key]['label'] : '';
1273
-				$jobtype = isset($this->cronjobs[$key]['jobtype']) ? $this->cronjobs[$key]['jobtype'] : '';
1274
-				$class = isset($this->cronjobs[$key]['class']) ? $this->cronjobs[$key]['class'] : '';
1275
-				$objectname = isset($this->cronjobs[$key]['objectname']) ? $this->cronjobs[$key]['objectname'] : '';
1276
-				$method = isset($this->cronjobs[$key]['method']) ? $this->cronjobs[$key]['method'] : '';
1277
-				$command = isset($this->cronjobs[$key]['command']) ? $this->cronjobs[$key]['command'] : '';
1278
-				$parameters = isset($this->cronjobs[$key]['parameters']) ? $this->cronjobs[$key]['parameters'] : '';
1279
-				$comment = isset($this->cronjobs[$key]['comment']) ? $this->cronjobs[$key]['comment'] : '';
1280
-				$frequency = isset($this->cronjobs[$key]['frequency']) ? $this->cronjobs[$key]['frequency'] : '';
1281
-				$unitfrequency = isset($this->cronjobs[$key]['unitfrequency']) ? $this->cronjobs[$key]['unitfrequency'] : '';
1282
-				$priority = isset($this->cronjobs[$key]['priority']) ? $this->cronjobs[$key]['priority'] : '';
1283
-				$datestart = isset($this->cronjobs[$key]['datestart']) ? $this->cronjobs[$key]['datestart'] : '';
1284
-				$dateend = isset($this->cronjobs[$key]['dateend']) ? $this->cronjobs[$key]['dateend'] : '';
1285
-				$status = isset($this->cronjobs[$key]['status']) ? $this->cronjobs[$key]['status'] : '';
1286
-				$test = isset($this->cronjobs[$key]['test']) ? $this->cronjobs[$key]['test'] : ''; // Line must be enabled or not (so visible or not)
1287
-
1288
-				// Search if cron entry already present
1289
-				$sql = "SELECT count(*) as nb FROM " . $dbPrefix . "cronjob";
1290
-				//$sql .= " WHERE module_name = '".$this->db->escape(empty($this->rights_class) ?strtolower($this->name) : $this->rights_class)."'";
1291
-				$sql .= " WHERE label = '" . $this->db->escape($label) . "'";
1292
-				/*if ($class) {
44
+    /**
45
+     * @var DoliDB Database handler
46
+     */
47
+    public $db;
48
+
49
+    /**
50
+     * @var int Module unique ID
51
+     * @see https://wiki.dolibarr.org/index.php/List_of_modules_id
52
+     */
53
+    public $numero;
54
+
55
+    /**
56
+     * @var   string Publisher name
57
+     * @since 4.0.0
58
+     */
59
+    public $editor_name;
60
+
61
+    /**
62
+     * @var   string URL of module at publisher site
63
+     * @since 4.0.0
64
+     */
65
+    public $editor_url;
66
+
67
+    /**
68
+     * @var string  Family
69
+     * @see $familyinfo
70
+     *
71
+     * Native values: 'crm', 'financial', 'hr', 'projects', 'products', 'ecm', 'technic', 'other'.
72
+     * Use familyinfo to declare a custom value.
73
+     */
74
+    public $family;
75
+
76
+    /**
77
+     * @var array<string,array{position:string,label:string}> Custom family information
78
+     * @see $family
79
+     *
80
+     * e.g.:
81
+     * array(
82
+     *     'myownfamily' => array(
83
+     *         'position' => '001',
84
+     *         'label' => $langs->trans("MyOwnFamily")
85
+     *     )
86
+     * );
87
+     */
88
+    public $familyinfo;
89
+
90
+    /**
91
+     * @var string    Module position on 2 digits
92
+     */
93
+    public $module_position = '50';
94
+
95
+    /**
96
+     * @var string Module name
97
+     *
98
+     * Only used if Module[ID]Name translation string is not found.
99
+     *
100
+     * You can use the following code to automatically derive it from your module's class name:
101
+     * preg_replace('/^mod/i', '', get_class($this))
102
+     */
103
+    public $name;
104
+
105
+    /**
106
+     * @var string[] Paths to create when module is activated
107
+     *
108
+     * e.g.: array('/mymodule/temp')
109
+     */
110
+    public $dirs = [];
111
+
112
+    /**
113
+     * @var array Module boxes
114
+     */
115
+    public $boxes = [];
116
+
117
+    /**
118
+     * @var array Module constants
119
+     */
120
+    public $const = [];
121
+
122
+    /**
123
+     * @var array Module cron jobs entries
124
+     */
125
+    public $cronjobs = [];
126
+
127
+    /**
128
+     * @var array   Module access rights
129
+     */
130
+    public $rights;
131
+
132
+    /**
133
+     * @var int     1=Admin is always granted of permission of modules (even when module is disabled)
134
+     */
135
+    public $rights_admin_allowed;
136
+
137
+    /**
138
+     * @var string  Module access rights family
139
+     */
140
+    public $rights_class;
141
+
142
+    /**
143
+     * @var array|int   Module menu entries (1 means the menu entries are not declared into module descriptor but are
144
+     *      hardcoded into menu manager)
145
+     */
146
+    public $menu = [];
147
+
148
+    /**
149
+     * @var array{triggers?:int<0,1>,login?:int<0,1>,substitutions?:int<0,1>,menus?:int<0,1>,theme?:int<0,1>,tpl?:int<0,1>,barcode?:int<0,1>,models?:int<0,1>,printing?:int<0,1>,css?:string[],js?:string[],hooks?:array{data?:string[],entity?:string},moduleforexternal?:int<0,1>,websitetemplates?:int<0,1>,contactelement?:int<0,1>}
150
+     *      Module parts array(
151
+     *      // Set this to 1 if module has its own trigger directory (/mymodule/core/triggers)
152
+     *      'triggers' => 0,
153
+     *      // Set this to 1 if module has its own login method directory (/mymodule/core/login)
154
+     *      'login' => 0,
155
+     *      // Set this to 1 if module has its own substitution function file (/mymodule/core/substitutions)
156
+     *      'substitutions' => 0,
157
+     *      // Set this to 1 if module has its own menus handler directory (/mymodule/core/menus)
158
+     *      'menus' => 0,
159
+     *      // Set this to 1 if module has its own theme directory (/mymodule/theme)
160
+     *      'theme' => 0,
161
+     *      // Set this to 1 if module overwrite template dir (/mymodule/core/tpl)
162
+     *      'tpl' => 0,
163
+     *      // Set this to 1 if module has its own barcode directory (/mymodule/core/modules/barcode)
164
+     *      'barcode' => 0,
165
+     *      // Set this to 1 if module has its own models directory (/mymodule/core/modules/xxx)
166
+     *      'models' => 0,
167
+     *      // Set this to relative path of css file if module has its own css file
168
+     *      'css' => '/mymodule/css/mymodule.css.php',
169
+     *      // Set this to relative path of js file if module must load a js on all pages
170
+     *      'js' => '/mymodule/js/mymodule.js',
171
+     *      // Set here all hooks context managed by module
172
+     *      'hooks' => array('hookcontext1','hookcontext2')
173
+     *      )
174
+     */
175
+    public $module_parts = [];
176
+
177
+    /**
178
+     * @var        string Module documents ?
179
+     * @deprecated Seems unused anywhere
180
+     */
181
+    public $docs;
182
+
183
+    /**
184
+     * @var        string ?
185
+     * @deprecated Seems unused anywhere
186
+     */
187
+    public $dbversion = "-";
188
+
189
+    /**
190
+     * @var string Error message
191
+     */
192
+    public $error;
193
+
194
+    /**
195
+     * @var string[] Array of Errors messages
196
+     */
197
+    public $errors;
198
+
199
+    /**
200
+     * @var string Module version
201
+     * @see http://semver.org
202
+     *
203
+     * The following keywords can also be used:
204
+     * 'development'
205
+     * 'experimental'
206
+     * 'dolibarr': only for core modules that share its version
207
+     * 'dolibarr_deprecated': only for deprecated core modules
208
+     */
209
+    public $version;
210
+
211
+    /**
212
+     * Module last version
213
+     * @var string $lastVersion
214
+     */
215
+    public $lastVersion = '';
216
+
217
+    /**
218
+     * true indicate this module need update
219
+     * @var bool $needUpdate
220
+     */
221
+    public $needUpdate = false;
222
+
223
+    /**
224
+     * @var string Module description (short text)
225
+     *
226
+     * Only used if Module[ID]Desc translation string is not found.
227
+     */
228
+    public $description;
229
+
230
+    /**
231
+     * @var   string Module description (long text)
232
+     * @since 4.0.0
233
+     *
234
+     * HTML content supported.
235
+     */
236
+    public $descriptionlong;
237
+
238
+    /**
239
+     * @var array dictionaries description
240
+     */
241
+    public $dictionaries;
242
+
243
+    /**
244
+     * @var array tabs description
245
+     */
246
+    public $tabs;
247
+
248
+    // For exports
249
+
250
+    /**
251
+     * @var string Module export code
252
+     */
253
+    public $export_code;
254
+
255
+    /**
256
+     * @var string[] Module export label
257
+     */
258
+    public $export_label;
259
+
260
+    public $export_icon;
261
+
262
+    /**
263
+     * @var array export enabled
264
+     */
265
+    public $export_enabled;
266
+    public $export_permission;
267
+    public $export_fields_array;
268
+    public $export_TypeFields_array; // Array of key=>type where type can be 'Numeric', 'Date', 'Text', 'Boolean', 'Status', 'List:xxx:login:rowid'
269
+    public $export_entities_array;
270
+    public $export_aggregate_array;
271
+    public $export_examplevalues_array;
272
+    public $export_help_array;
273
+    public $export_special_array; // special or computed field
274
+    public $export_dependencies_array;
275
+    public $export_sql_start;
276
+    public $export_sql_end;
277
+    public $export_sql_order;
278
+
279
+
280
+    // For import
281
+
282
+    /**
283
+     * @var string Module import code
284
+     */
285
+    public $import_code;
286
+
287
+    /**
288
+     * @var string[] Module import label
289
+     */
290
+    public $import_label;
291
+
292
+    public $import_icon;
293
+    public $import_entities_array;
294
+    public $import_tables_array;
295
+    public $import_tables_creator_array;
296
+    public $import_fields_array;
297
+    public $import_fieldshidden_array;
298
+    public $import_convertvalue_array;
299
+    public $import_regex_array;
300
+    public $import_examplevalues_array;
301
+    public $import_updatekeys_array;
302
+    public $import_run_sql_after_array;
303
+    public $import_TypeFields_array;
304
+    public $import_help_array;
305
+
306
+    /**
307
+     * @var string Module constant name
308
+     */
309
+    public $const_name;
310
+
311
+    /**
312
+     * @var bool Module can't be disabled
313
+     */
314
+    public $always_enabled;
315
+
316
+    /**
317
+     * @var bool Module is disabled
318
+     */
319
+    public $disabled;
320
+
321
+    /**
322
+     * @var int Module is enabled globally (Multicompany support)
323
+     */
324
+    public $core_enabled;
325
+
326
+    /**
327
+     * @var string Name of image file used for this module
328
+     *
329
+     * If file is in theme/yourtheme/img directory under name object_pictoname.png use 'pictoname'
330
+     * If file is in module/img directory under name object_pictoname.png use 'pictoname@module'
331
+     */
332
+    public $picto;
333
+
334
+    /**
335
+     * @var string[]|string     List of config pages (Old modules uses a string. New one must use an array)
336
+     *
337
+     * Name of php pages stored into module/admin directory, used to setup module.
338
+     * e.g.: array("setup.php@mymodule")
339
+     */
340
+    public $config_page_url;
341
+
342
+
343
+    /**
344
+     * @var array   List of module class names that must be enabled if this module is enabled. e.g.:
345
+     *      array('modAnotherModule', 'FR'=>'modYetAnotherModule') Another example : array('always'=>array("modBanque",
346
+     *      "modFacture", "modProduct", "modCategorie"), 'FR'=>array('modBlockedLog'));
347
+     * @see $requiredby
348
+     */
349
+    public $depends;
350
+
351
+    /**
352
+     * @var string[] List of module class names to disable if the module is disabled.
353
+     * @see $depends
354
+     */
355
+    public $requiredby;
356
+
357
+    /**
358
+     * @var string[] List of module class names as string this module is in conflict with.
359
+     * @see $depends
360
+     */
361
+    public $conflictwith;
362
+
363
+    /**
364
+     * @var string[] Module language files
365
+     */
366
+    public $langfiles;
367
+
368
+    /**
369
+     * @var array<string,string> Array of warnings to show when we activate the module
370
+     *
371
+     * array('always'='text') or array('FR'='text')
372
+     */
373
+    public $warnings_activation;
374
+
375
+    /**
376
+     * @var array<string,string> Array of warnings to show when we activate an external module
377
+     *
378
+     * array('always'='text') or array('FR'='text')
379
+     */
380
+    public $warnings_activation_ext;
381
+
382
+    /**
383
+     * @var array<string,string> Array of warnings to show when we disable the module
384
+     *
385
+     * array('always'='text') or array('FR'='text')
386
+     */
387
+    public $warnings_unactivation;
388
+
389
+    /**
390
+     * @var array Minimum version of PHP required by module.
391
+     * e.g.: PHP ≥ 7.0 = array(7, 0)
392
+     */
393
+    public $phpmin;
394
+
395
+    public $phpmax;
396
+
397
+    /**
398
+     * @var array Minimum version of Dolibarr required by module.
399
+     * e.g.: Dolibarr ≥ 3.6 = array(3, 6)
400
+     */
401
+    public $need_dolibarr_version;
402
+
403
+    public $need_javascript_ajax;
404
+
405
+    public $enabled_bydefault;
406
+
407
+    /**
408
+     * @var bool|int Whether to hide the module.
409
+     */
410
+    public $hidden = false;
411
+
412
+    /**
413
+     * @var string url to check for module update
414
+     */
415
+    public $url_last_version;
416
+
417
+
418
+    /**
419
+     * Constructor. Define names, constants, directories, boxes, permissions
420
+     *
421
+     * @param DoliDB $db Database handler
422
+     */
423
+    public function __construct($db)
424
+    {
425
+        $this->db = $db;
426
+    }
427
+    // We should but can't set this as abstract because this will make dolibarr hang
428
+    // after migration due to old module not implementing. We must wait PHP is able to make
429
+    // a try catch on Fatal error to manage this correctly.
430
+    // We need constructor into function unActivateModule into admin.lib.php
431
+
432
+
433
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
434
+
435
+    /**
436
+     * Gives the long description of a module. First check README-la_LA.md then README.md
437
+     * If no markdown files found, it returns translated value of the key ->descriptionlong.
438
+     *
439
+     * @return string     Long description of a module from README.md of from property.
440
+     */
441
+    public function getDescLong()
442
+    {
443
+        global $langs;
444
+        $langs->load("admin");
445
+
446
+        include_once BASE_PATH . '/../Dolibarr/Lib/Files.php';
447
+        include_once BASE_PATH . '/../Dolibarr/Lib/GetUrl.php';
448
+
449
+        $content = '';
450
+        $pathoffile = $this->getDescLongReadmeFound();
451
+
452
+        if ($pathoffile) {     // Mostly for external modules
453
+            $content = file_get_contents($pathoffile);
454
+
455
+            if ((float)DOL_VERSION >= 6.0) {
456
+                @include_once BASE_PATH . '/../Dolibarr/Lib/ParseMd.php';
457
+
458
+                $content = dolMd2Html(
459
+                    $content,
460
+                    'parsedown',
461
+                    [
462
+                        'doc/' => dol_buildpath(strtolower($this->name) . '/doc/', 1),
463
+                        'img/' => dol_buildpath(strtolower($this->name) . '/img/', 1),
464
+                        'images/' => dol_buildpath(strtolower($this->name) . '/images/', 1),
465
+                    ]
466
+                );
467
+
468
+                $content = preg_replace('/<a href="/', '<a target="_blank" rel="noopener noreferrer" href="', $content);
469
+            } else {
470
+                $content = nl2br($content);
471
+            }
472
+        } else {
473
+            // Mostly for internal modules
474
+            if (!empty($this->descriptionlong)) {
475
+                if (is_array($this->langfiles)) {
476
+                    foreach ($this->langfiles as $val) {
477
+                        if ($val) {
478
+                            $langs->load($val);
479
+                        }
480
+                    }
481
+                }
482
+
483
+                $content = $langs->transnoentitiesnoconv($this->descriptionlong);
484
+            }
485
+        }
486
+
487
+        return $content;
488
+    }
489
+
490
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
491
+
492
+    /**
493
+     * Return path of file if a README file was found.
494
+     *
495
+     * @return string      Path of file if a README file was found.
496
+     */
497
+    public function getDescLongReadmeFound()
498
+    {
499
+        global $langs;
500
+
501
+        $filefound = false;
502
+
503
+        // Define path to file README.md.
504
+        // First check README-la_LA.md then README-la.md then README.md
505
+        $pathoffile = dol_buildpath(strtolower($this->name) . '/README-' . $langs->defaultlang . '.md', 0);
506
+        if (dol_is_file($pathoffile)) {
507
+            $filefound = true;
508
+        }
509
+        if (!$filefound) {
510
+            $tmp = explode('_', $langs->defaultlang);
511
+            $pathoffile = dol_buildpath(strtolower($this->name) . '/README-' . $tmp[0] . '.md', 0);
512
+            if (dol_is_file($pathoffile)) {
513
+                $filefound = true;
514
+            }
515
+        }
516
+        if (!$filefound) {
517
+            $pathoffile = dol_buildpath(strtolower($this->name) . '/README.md', 0);
518
+            if (dol_is_file($pathoffile)) {
519
+                $filefound = true;
520
+            }
521
+        }
522
+
523
+        return ($filefound ? $pathoffile : '');
524
+    }
525
+
526
+    /**
527
+     * Gives the changelog. First check ChangeLog-la_LA.md then ChangeLog.md
528
+     *
529
+     * @return string  Content of ChangeLog
530
+     */
531
+    public function getChangeLog()
532
+    {
533
+        global $langs;
534
+        $langs->load("admin");
535
+
536
+        include_once BASE_PATH . '/../Dolibarr/Lib/Files.php';
537
+        include_once BASE_PATH . '/../Dolibarr/Lib/GetUrl.php';
538
+
539
+        $filefound = false;
540
+
541
+        // Define path to file README.md.
542
+        // First check ChangeLog-la_LA.md then ChangeLog.md
543
+        $pathoffile = dol_buildpath(strtolower($this->name) . '/ChangeLog-' . $langs->defaultlang . '.md', 0);
544
+        if (dol_is_file($pathoffile)) {
545
+            $filefound = true;
546
+        }
547
+        if (!$filefound) {
548
+            $pathoffile = dol_buildpath(strtolower($this->name) . '/ChangeLog.md', 0);
549
+            if (dol_is_file($pathoffile)) {
550
+                $filefound = true;
551
+            }
552
+        }
553
+
554
+        if ($filefound) {     // Mostly for external modules
555
+            $content = file_get_contents($pathoffile);
556
+
557
+            if ((float)DOL_VERSION >= 6.0) {
558
+                @include_once BASE_PATH . '/../Dolibarr/Lib/ParseMd.php';
559
+
560
+                $content = dolMd2Html($content, 'parsedown', ['doc/' => dol_buildpath(strtolower($this->name) . '/doc/', 1)]);
561
+            } else {
562
+                $content = nl2br($content);
563
+            }
564
+        }
565
+
566
+        return $content;
567
+    }
568
+
569
+    /**
570
+     * Gives the publisher name
571
+     *
572
+     * @return string  Publisher name
573
+     */
574
+    public function getPublisher()
575
+    {
576
+        return $this->editor_name;
577
+    }
578
+
579
+    /**
580
+     * Gives the publisher url
581
+     *
582
+     * @return string  Publisher url
583
+     */
584
+    public function getPublisherUrl()
585
+    {
586
+        return $this->editor_url;
587
+    }
588
+
589
+    /**
590
+     * Gives the module position
591
+     *
592
+     * @return string   Module position (an external module should never return a value lower than 100000. 1-100000 are
593
+     *                  reserved for core)
594
+     */
595
+    public function getModulePosition()
596
+    {
597
+        if (in_array($this->version, ['dolibarr', 'experimental', 'development'])) {   // core module
598
+            return $this->module_position;
599
+        } else {                                                                            // external module
600
+            if ($this->module_position >= 100000) {
601
+                return $this->module_position;
602
+            } else {
603
+                $position = intval($this->module_position) + 100000;
604
+                return strval($position);
605
+            }
606
+        }
607
+    }
608
+
609
+    /**
610
+     * Gives module related language files list
611
+     *
612
+     * @return string[]    Language files list
613
+     */
614
+    public function getLangFilesArray()
615
+    {
616
+        return $this->langfiles;
617
+    }
618
+
619
+    /**
620
+     * Gives translated label of an export dataset
621
+     *
622
+     * @param int $r Dataset index
623
+     *
624
+     * @return string       Translated databaset label
625
+     */
626
+    public function getExportDatasetLabel($r)
627
+    {
628
+        global $langs;
629
+
630
+        $langstring = "ExportDataset_" . $this->export_code[$r];
631
+        if ($langs->trans($langstring) == $langstring) {
632
+            // Translation not found
633
+            return $langs->trans($this->export_label[$r]);
634
+        } else {
635
+            // Translation found
636
+            return $langs->trans($langstring);
637
+        }
638
+    }
639
+
640
+    /**
641
+     * Gives translated label of an import dataset
642
+     *
643
+     * @param int $r Dataset index
644
+     *
645
+     * @return string      Translated dataset label
646
+     */
647
+    public function getImportDatasetLabel($r)
648
+    {
649
+        global $langs;
650
+
651
+        $langstring = "ImportDataset_" . $this->import_code[$r];
652
+        //print "x".$langstring;
653
+        if ($langs->trans($langstring) == $langstring) {
654
+            // Translation not found
655
+            return $langs->transnoentitiesnoconv($this->import_label[$r]);
656
+        } else {
657
+            // Translation found
658
+            return $langs->transnoentitiesnoconv($langstring);
659
+        }
660
+    }
661
+
662
+    /**
663
+     * Gives the last date of activation
664
+     *
665
+     * @return  int|string          Date of last activation or '' if module was never activated
666
+     */
667
+    public function getLastActivationDate()
668
+    {
669
+        global $conf;
670
+
671
+        $err = 0;
672
+
673
+        $sql = "SELECT tms FROM " . $dbPrefix . "const";
674
+        $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
675
+        $sql .= " AND entity IN (0, " . ((int)$conf->entity) . ")";
676
+
677
+        dol_syslog(get_class($this) . "::getLastActiveDate", LOG_DEBUG);
678
+        $resql = $this->db->query($sql);
679
+        if (!$resql) {
680
+            $err++;
681
+        } else {
682
+            $obj = $this->db->fetch_object($resql);
683
+            if ($obj) {
684
+                return $this->db->jdate($obj->tms);
685
+            }
686
+        }
687
+
688
+        return '';
689
+    }
690
+
691
+    /**
692
+     * Gives the last author of activation
693
+     *
694
+     * @return array       Array array('authorid'=>Id of last activation user, 'lastactivationdate'=>Date of last
695
+     *                     activation)
696
+     */
697
+    public function getLastActivationInfo()
698
+    {
699
+        global $conf;
700
+
701
+        $err = 0;
702
+
703
+        $sql = "SELECT tms, note FROM " . $dbPrefix . "const";
704
+        $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
705
+        $sql .= " AND entity IN (0, " . $conf->entity . ")";
706
+
707
+        dol_syslog(get_class($this) . "::getLastActiveDate", LOG_DEBUG);
708
+        $resql = $this->db->query($sql);
709
+        if (!$resql) {
710
+            $err++;
711
+        } else {
712
+            $obj = $this->db->fetch_object($resql);
713
+            if ($obj) {
714
+                $tmp = [];
715
+                if ($obj->note) {
716
+                    $tmp = json_decode($obj->note, true);
717
+                }
718
+                return [
719
+                    'authorid' => empty($tmp['authorid']) ? '' : $tmp['authorid'],
720
+                    'ip' => empty($tmp['ip']) ? '' : $tmp['ip'],
721
+                    'lastactivationdate' => $this->db->jdate($obj->tms),
722
+                    'lastactivationversion' => (!empty($tmp['lastactivationversion']) ? $tmp['lastactivationversion'] : 'unknown'),
723
+                ];
724
+            }
725
+        }
726
+
727
+        return [];
728
+    }
729
+
730
+    /**
731
+     * Function called when module is enabled.
732
+     * The init function adds tabs, constants, boxes, permissions and menus (defined in constructor) into Dolibarr
733
+     * database. It also creates data directories
734
+     *
735
+     * @param string $options Options when enabling module ('', 'newboxdefonly', 'noboxes', 'menuonly')
736
+     *                         'noboxes' = Do not insert boxes 'newboxdefonly' = For boxes, insert def of boxes only
737
+     *                         and not boxes activation
738
+     *
739
+     * @return int                1 if OK, 0 if KO
740
+     */
741
+    public function init($options = '')
742
+    {
743
+        return $this->_init([], $options);
744
+    }
745
+
746
+    /**
747
+     * Enables a module.
748
+     * Inserts all information into database.
749
+     *
750
+     * @param array $array_sql SQL requests to be executed when enabling module
751
+     * @param string $options String with options when disabling module:
752
+     *                              - 'noboxes' = Do all actions but do not insert boxes
753
+     *                              - 'newboxdefonly' = Do all actions but for boxes, insert def of boxes only and not
754
+     *                              boxes activation
755
+     *
756
+     * @return int                  1 if OK, 0 if KO
757
+     */
758
+    protected function _init($array_sql, $options = '')
759
+    {
760
+        // phpcs:enable
761
+        $conf = \DoliCore\Base\Config::getConf();
762
+
763
+        $err = 0;
764
+
765
+        $this->db->begin();
766
+
767
+        // Insert activation module constant
768
+        if (!$err) {
769
+            $err += $this->_active();
770
+        }
771
+
772
+        // Insert new pages for tabs (into llx_const)
773
+        if (!$err) {
774
+            $err += $this->insert_tabs();
775
+        }
776
+
777
+        // Insert activation of module's parts. Copy website templates into doctemplates.
778
+        if (!$err) {
779
+            $err += $this->insert_module_parts();
780
+        }
781
+
782
+        // Insert constant defined by modules (into llx_const)
783
+        if (!$err && !preg_match('/newboxdefonly/', $options)) {
784
+            $err += $this->insert_const(); // Test on newboxdefonly to avoid to erase value during upgrade
785
+        }
786
+
787
+        // Insert boxes def (into llx_boxes_def) and boxes setup (into llx_boxes)
788
+        if (!$err && !preg_match('/noboxes/', $options)) {
789
+            $err += $this->insert_boxes($options);
790
+        }
791
+
792
+        // Insert cron job entries (entry in llx_cronjobs)
793
+        if (!$err) {
794
+            $err += $this->insert_cronjobs();
795
+        }
796
+
797
+        // Insert permission definitions of module into llx_rights_def. If user is admin, grant this permission to user.
798
+        if (!$err) {
799
+            $err += $this->insert_permissions(1, null, 1);
800
+        }
801
+
802
+        // Insert specific menus entries into database
803
+        if (!$err) {
804
+            $err += $this->insert_menus();
805
+        }
806
+
807
+        // Create module's directories
808
+        if (!$err) {
809
+            $err += $this->create_dirs();
810
+        }
811
+
812
+        // Execute addons requests
813
+        $num = count($array_sql);
814
+        for ($i = 0; $i < $num; $i++) {
815
+            if (!$err) {
816
+                $val = $array_sql[$i];
817
+                $sql = $val;
818
+                $ignoreerror = 0;
819
+                if (is_array($val)) {
820
+                    $sql = $val['sql'];
821
+                    $ignoreerror = $val['ignoreerror'];
822
+                }
823
+                // Add current entity id
824
+                $sql = str_replace('__ENTITY__', $conf->entity, $sql);
825
+
826
+                dol_syslog(get_class($this) . "::_init ignoreerror=" . $ignoreerror, LOG_DEBUG);
827
+                $result = $this->db->query($sql, $ignoreerror);
828
+                if (!$result) {
829
+                    if (!$ignoreerror) {
830
+                        $this->error = $this->db->lasterror();
831
+                        $err++;
832
+                    } else {
833
+                        dol_syslog(get_class($this) . "::_init Warning " . $this->db->lasterror(), LOG_WARNING);
834
+                    }
835
+                }
836
+            }
837
+        }
838
+
839
+        // Return code
840
+        if (!$err) {
841
+            $this->db->commit();
842
+            return 1;
843
+        } else {
844
+            $this->db->rollback();
845
+            return 0;
846
+        }
847
+    }
848
+
849
+    /**
850
+     * Insert constants for module activation
851
+     *
852
+     * @return int Error count (0 if OK)
853
+     */
854
+    protected function _active()
855
+    {
856
+        // phpcs:enable
857
+        global $conf, $user;
858
+        global $config;
859
+        $dbPrefix = $config->db->prefix;
860
+
861
+        $err = 0;
862
+
863
+        // Common module
864
+        $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
865
+
866
+        $sql = "DELETE FROM " . $dbPrefix . "const";
867
+        $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
868
+        $sql .= " AND entity IN (0, " . $entity . ")";
869
+
870
+        dol_syslog(get_class($this) . "::_active delete activation constant", LOG_DEBUG);
871
+        $resql = $this->db->query($sql);
872
+        if (!$resql) {
873
+            $err++;
874
+        }
875
+
876
+        $note = json_encode(
877
+            [
878
+                'authorid' => (is_object($user) ? $user->id : 0),
879
+                'ip' => (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']),
880
+                'lastactivationversion' => $this->version,
881
+            ]
882
+        );
883
+
884
+        $sql = "INSERT INTO " . $dbPrefix . "const (name, value, visible, entity, note) VALUES";
885
+        $sql .= " (" . $this->db->encrypt($this->const_name);
886
+        $sql .= ", " . $this->db->encrypt('1');
887
+        $sql .= ", 0, " . ((int)$entity);
888
+        $sql .= ", '" . $this->db->escape($note) . "')";
889
+
890
+        dol_syslog(get_class($this) . "::_active insert activation constant", LOG_DEBUG);
891
+
892
+        $resql = $this->db->query($sql);
893
+
894
+        if (!$resql) {
895
+            $err++;
896
+        }
897
+
898
+        return $err;
899
+    }
900
+
901
+    /**
902
+     * Adds tabs
903
+     *
904
+     * @return int  Error count (0 if ok)
905
+     */
906
+    public function insert_tabs()
907
+    {
908
+        // phpcs:enable
909
+        global $conf;
910
+
911
+        $err = 0;
912
+
913
+        if (!empty($this->tabs)) {
914
+            dol_syslog(get_class($this) . "::insert_tabs", LOG_DEBUG);
915
+
916
+            $i = 0;
917
+            foreach ($this->tabs as $key => $value) {
918
+                if (is_array($value) && count($value) == 0) {
919
+                    continue; // Discard empty arrays
920
+                }
921
+
922
+                $entity = $conf->entity;
923
+                $newvalue = $value;
924
+
925
+                if (is_array($value)) {
926
+                    $newvalue = $value['data'];
927
+                    if (isset($value['entity'])) {
928
+                        $entity = $value['entity'];
929
+                    }
930
+                }
931
+
932
+                if ($newvalue) {
933
+                    $sql = "INSERT INTO " . $dbPrefix . "const (";
934
+                    $sql .= "name";
935
+                    $sql .= ", type";
936
+                    $sql .= ", value";
937
+                    $sql .= ", note";
938
+                    $sql .= ", visible";
939
+                    $sql .= ", entity";
940
+                    $sql .= ")";
941
+                    $sql .= " VALUES (";
942
+                    $sql .= $this->db->encrypt($this->const_name . "_TABS_" . $i);
943
+                    $sql .= ", 'chaine'";
944
+                    $sql .= ", " . $this->db->encrypt($newvalue);
945
+                    $sql .= ", null";
946
+                    $sql .= ", '0'";
947
+                    $sql .= ", " . ((int)$entity);
948
+                    $sql .= ")";
949
+
950
+                    $resql = $this->db->query($sql);
951
+                    if (!$resql) {
952
+                        dol_syslog($this->db->lasterror(), LOG_ERR);
953
+                        if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
954
+                            $this->error = $this->db->lasterror();
955
+                            $this->errors[] = $this->db->lasterror();
956
+                            $err++;
957
+                            break;
958
+                        }
959
+                    }
960
+                }
961
+                $i++;
962
+            }
963
+        }
964
+        return $err;
965
+    }
966
+
967
+    /**
968
+     * Save configuration for generic features.
969
+     * This also generate website templates if the module provide some.
970
+     *
971
+     * @return int Error count (0 if OK)
972
+     */
973
+    public function insert_module_parts()
974
+    {
975
+        // phpcs:enable
976
+        global $conf, $langs;
977
+
978
+        $error = 0;
979
+
980
+        if (is_array($this->module_parts)) {
981
+            if (empty($this->module_parts['icon']) && !empty($this->picto) && preg_match('/^fa\-/', $this->picto)) {
982
+                $this->module_parts['icon'] = $this->picto;
983
+            }
984
+
985
+            foreach ($this->module_parts as $key => $value) {
986
+                if (is_array($value) && count($value) == 0) {
987
+                    continue; // Discard empty arrays
988
+                }
989
+
990
+                // If module brings website templates, we must generate the zip like we do whenenabling the website module
991
+                if ($key == 'websitetemplates' && $value == 1) {
992
+                    $srcroot = dol_buildpath('/' . strtolower($this->name) . '/doctemplates/websites');
993
+
994
+                    // Copy templates in dir format (recommended) into zip file
995
+                    $docs = dol_dir_list($srcroot, 'directories', 0, 'website_.*$');
996
+                    foreach ($docs as $cursorfile) {
997
+                        $src = $srcroot . '/' . $cursorfile['name'];
998
+                        $dest = DOL_DATA_ROOT . '/doctemplates/websites/' . $cursorfile['name'];
999
+
1000
+                        dol_delete_file($dest . '.zip');
1001
+
1002
+                        // Compress it
1003
+                        global $errormsg;
1004
+                        $errormsg = '';
1005
+                        $result = dol_compress_dir($src, $dest . '.zip', 'zip');
1006
+                        if ($result < 0) {
1007
+                            $error++;
1008
+                            $this->error = ($errormsg ? $errormsg : $langs->trans('ErrorFailToCreateZip', $dest));
1009
+                            $this->errors[] = ($errormsg ? $errormsg : $langs->trans('ErrorFailToCreateZip', $dest));
1010
+                        }
1011
+                    }
1012
+
1013
+                    // Copy also the preview website_xxx.jpg file
1014
+                    $docs = dol_dir_list($srcroot, 'files', 0, 'website_.*\.jpg$');
1015
+                    foreach ($docs as $cursorfile) {
1016
+                        $src = $srcroot . '/' . $cursorfile['name'];
1017
+                        $dest = DOL_DATA_ROOT . '/doctemplates/websites/' . $cursorfile['name'];
1018
+
1019
+                        dol_copy($src, $dest);
1020
+                    }
1021
+                }
1022
+
1023
+                $entity = $conf->entity; // Reset the current entity
1024
+                $newvalue = $value;
1025
+
1026
+                // Serialize array parameters
1027
+                if (is_array($value)) {
1028
+                    // Can defined other parameters
1029
+                    // Example when $key='hooks', then $value is an array('data'=>array('hookcontext1','hookcontext2'), 'entity'=>X)
1030
+                    if (isset($value['data']) && is_array($value['data'])) {
1031
+                        $newvalue = json_encode($value['data']);
1032
+                        if (isset($value['entity'])) {
1033
+                            $entity = $value['entity'];
1034
+                        }
1035
+                    } elseif (isset($value['data']) && !is_array($value['data'])) {
1036
+                        $newvalue = $value['data'];
1037
+                        if (isset($value['entity'])) {
1038
+                            $entity = $value['entity'];
1039
+                        }
1040
+                    } else { // when hook is declared with syntax 'hook'=>array('hookcontext1','hookcontext2',...)
1041
+                        $newvalue = json_encode($value);
1042
+                    }
1043
+                }
1044
+
1045
+                if (!empty($newvalue)) {
1046
+                    $sql = "INSERT INTO " . $dbPrefix . "const (";
1047
+                    $sql .= "name";
1048
+                    $sql .= ", type";
1049
+                    $sql .= ", value";
1050
+                    $sql .= ", note";
1051
+                    $sql .= ", visible";
1052
+                    $sql .= ", entity";
1053
+                    $sql .= ")";
1054
+                    $sql .= " VALUES (";
1055
+                    $sql .= " " . $this->db->encrypt($this->const_name . "_" . strtoupper($key), 1);
1056
+                    $sql .= ", 'chaine'";
1057
+                    $sql .= ", " . $this->db->encrypt($newvalue, 1);
1058
+                    $sql .= ", null";
1059
+                    $sql .= ", '0'";
1060
+                    $sql .= ", " . ((int)$entity);
1061
+                    $sql .= ")";
1062
+
1063
+                    dol_syslog(get_class($this) . "::insert_module_parts for key=" . $this->const_name . "_" . strtoupper($key), LOG_DEBUG);
1064
+
1065
+                    $resql = $this->db->query($sql, 1);
1066
+                    if (!$resql) {
1067
+                        if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1068
+                            $error++;
1069
+                            $this->error = $this->db->lasterror();
1070
+                        } else {
1071
+                            dol_syslog(get_class($this) . "::insert_module_parts for " . $this->const_name . "_" . strtoupper($key) . " Record already exists.", LOG_WARNING);
1072
+                        }
1073
+                    }
1074
+                }
1075
+            }
1076
+        }
1077
+        return $error;
1078
+    }
1079
+
1080
+    /**
1081
+     * Adds constants
1082
+     *
1083
+     * @return int Error count (0 if OK)
1084
+     */
1085
+    public function insert_const()
1086
+    {
1087
+        // phpcs:enable
1088
+        global $conf;
1089
+
1090
+        $err = 0;
1091
+
1092
+        if (empty($this->const)) {
1093
+            return 0;
1094
+        }
1095
+
1096
+        dol_syslog(__METHOD__, LOG_DEBUG);
1097
+
1098
+        foreach ($this->const as $key => $value) {
1099
+            $name = $this->const[$key][0];
1100
+            $type = $this->const[$key][1];
1101
+            $val = $this->const[$key][2];
1102
+            $note = isset($this->const[$key][3]) ? $this->const[$key][3] : '';
1103
+            $visible = isset($this->const[$key][4]) ? $this->const[$key][4] : 0;
1104
+            $entity = (!empty($this->const[$key][5]) && $this->const[$key][5] != 'current') ? 0 : $conf->entity;
1105
+
1106
+            // Clean
1107
+            if (empty($visible)) {
1108
+                $visible = '0';
1109
+            }
1110
+            if (empty($val) && $val != '0') {
1111
+                $val = '';
1112
+            }
1113
+
1114
+            $sql = "SELECT count(*) as nb";
1115
+            $sql .= " FROM " . $dbPrefix . "const";
1116
+            $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($name) . "'";
1117
+            $sql .= " AND entity = " . ((int)$entity);
1118
+
1119
+            $result = $this->db->query($sql);
1120
+            if ($result) {
1121
+                $row = $this->db->fetch_row($result);
1122
+
1123
+                if ($row[0] == 0) {   // If not found
1124
+                    $sql = "INSERT INTO " . $dbPrefix . "const (name,type,value,note,visible,entity)";
1125
+                    $sql .= " VALUES (";
1126
+                    $sql .= $this->db->encrypt($name);
1127
+                    $sql .= ",'" . $this->db->escape($type) . "'";
1128
+                    $sql .= "," . (($val != '') ? $this->db->encrypt($val) : "''");
1129
+                    $sql .= "," . ($note ? "'" . $this->db->escape($note) . "'" : "null");
1130
+                    $sql .= ",'" . $this->db->escape($visible) . "'";
1131
+                    $sql .= "," . $entity;
1132
+                    $sql .= ")";
1133
+
1134
+                    if (!$this->db->query($sql)) {
1135
+                        $err++;
1136
+                    }
1137
+                } else {
1138
+                    dol_syslog(__METHOD__ . " constant '" . $name . "' already exists", LOG_DEBUG);
1139
+                }
1140
+            } else {
1141
+                $err++;
1142
+            }
1143
+        }
1144
+
1145
+        return $err;
1146
+    }
1147
+
1148
+
1149
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1150
+
1151
+    /**
1152
+     * Adds boxes
1153
+     *
1154
+     * @param string $option Options when disabling module ('newboxdefonly'=insert only boxes definition)
1155
+     *
1156
+     * @return int             Error count (0 if OK)
1157
+     */
1158
+    public function insert_boxes($option = '')
1159
+    {
1160
+        // phpcs:enable
1161
+        global $conf;
1162
+        global $config;
1163
+        $dbPrefix = $config->db->prefix;
1164
+
1165
+
1166
+        $err = 0;
1167
+
1168
+        if (is_array($this->boxes)) {
1169
+            dol_syslog(get_class($this) . "::insert_boxes", LOG_DEBUG);
1170
+
1171
+            $pos_name = InfoBox::getListOfPagesForBoxes();
1172
+
1173
+            foreach ($this->boxes as $key => $value) {
1174
+                $file = isset($this->boxes[$key]['file']) ? $this->boxes[$key]['file'] : '';
1175
+                $note = isset($this->boxes[$key]['note']) ? $this->boxes[$key]['note'] : '';
1176
+                $enabledbydefaulton = isset($this->boxes[$key]['enabledbydefaulton']) ? $this->boxes[$key]['enabledbydefaulton'] : 'Home';
1177
+
1178
+                if (empty($file)) {
1179
+                    $file = isset($this->boxes[$key][1]) ? $this->boxes[$key][1] : ''; // For backward compatibility
1180
+                }
1181
+                if (empty($note)) {
1182
+                    $note = isset($this->boxes[$key][2]) ? $this->boxes[$key][2] : ''; // For backward compatibility
1183
+                }
1184
+
1185
+                // Search if boxes def already present
1186
+                $sql = "SELECT count(*) as nb FROM " . $dbPrefix . "boxes_def";
1187
+                $sql .= " WHERE file = '" . $this->db->escape($file) . "'";
1188
+                $sql .= " AND entity = " . $conf->entity;
1189
+                if ($note) {
1190
+                    $sql .= " AND note ='" . $this->db->escape($note) . "'";
1191
+                }
1192
+
1193
+                $result = $this->db->query($sql);
1194
+                if ($result) {
1195
+                    $obj = $this->db->fetch_object($result);
1196
+                    if ($obj->nb == 0) {
1197
+                        $this->db->begin();
1198
+
1199
+                        if (!$err) {
1200
+                            $sql = "INSERT INTO " . $dbPrefix . "boxes_def (file, entity, note)";
1201
+                            $sql .= " VALUES ('" . $this->db->escape($file) . "', ";
1202
+                            $sql .= $conf->entity . ", ";
1203
+                            $sql .= $note ? "'" . $this->db->escape($note) . "'" : "null";
1204
+                            $sql .= ")";
1205
+
1206
+                            dol_syslog(get_class($this) . "::insert_boxes", LOG_DEBUG);
1207
+                            $resql = $this->db->query($sql);
1208
+                            if (!$resql) {
1209
+                                $err++;
1210
+                            }
1211
+                        }
1212
+                        if (!$err && !preg_match('/newboxdefonly/', $option)) {
1213
+                            $lastid = $this->db->last_insert_id($dbPrefix . "boxes_def", "rowid");
1214
+
1215
+                            foreach ($pos_name as $key2 => $val2) {
1216
+                                //print 'key2='.$key2.'-val2='.$val2."<br>\n";
1217
+                                if ($enabledbydefaulton && $val2 != $enabledbydefaulton) {
1218
+                                    continue; // Not enabled by default onto this page.
1219
+                                }
1220
+
1221
+                                $sql = "INSERT INTO " . $dbPrefix . "boxes (box_id, position, box_order, fk_user, entity)";
1222
+                                $sql .= " VALUES (" . ((int)$lastid) . ", " . ((int)$key2) . ", '0', 0, " . ((int)$conf->entity) . ")";
1223
+
1224
+                                dol_syslog(get_class($this) . "::insert_boxes onto page " . $key2 . "=" . $val2, LOG_DEBUG);
1225
+                                $resql = $this->db->query($sql);
1226
+                                if (!$resql) {
1227
+                                    $err++;
1228
+                                }
1229
+                            }
1230
+                        }
1231
+
1232
+                        if (!$err) {
1233
+                            $this->db->commit();
1234
+                        } else {
1235
+                            $this->error = $this->db->lasterror();
1236
+                            $this->db->rollback();
1237
+                        }
1238
+                    }
1239
+                    // else box already registered into database
1240
+                } else {
1241
+                    $this->error = $this->db->lasterror();
1242
+                    $err++;
1243
+                }
1244
+            }
1245
+        }
1246
+
1247
+        return $err;
1248
+    }
1249
+
1250
+
1251
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1252
+
1253
+    /**
1254
+     * Adds cronjobs
1255
+     *
1256
+     * @return int             Error count (0 if OK)
1257
+     */
1258
+    public function insert_cronjobs()
1259
+    {
1260
+        // phpcs:enable
1261
+        include_once DOL_DOCUMENT_ROOT . '/core/class/infobox.class.php';
1262
+
1263
+        global $conf;
1264
+
1265
+        $err = 0;
1266
+
1267
+        if (is_array($this->cronjobs)) {
1268
+            dol_syslog(get_class($this) . "::insert_cronjobs", LOG_DEBUG);
1269
+
1270
+            foreach ($this->cronjobs as $key => $value) {
1271
+                $entity = isset($this->cronjobs[$key]['entity']) ? $this->cronjobs[$key]['entity'] : $conf->entity;
1272
+                $label = isset($this->cronjobs[$key]['label']) ? $this->cronjobs[$key]['label'] : '';
1273
+                $jobtype = isset($this->cronjobs[$key]['jobtype']) ? $this->cronjobs[$key]['jobtype'] : '';
1274
+                $class = isset($this->cronjobs[$key]['class']) ? $this->cronjobs[$key]['class'] : '';
1275
+                $objectname = isset($this->cronjobs[$key]['objectname']) ? $this->cronjobs[$key]['objectname'] : '';
1276
+                $method = isset($this->cronjobs[$key]['method']) ? $this->cronjobs[$key]['method'] : '';
1277
+                $command = isset($this->cronjobs[$key]['command']) ? $this->cronjobs[$key]['command'] : '';
1278
+                $parameters = isset($this->cronjobs[$key]['parameters']) ? $this->cronjobs[$key]['parameters'] : '';
1279
+                $comment = isset($this->cronjobs[$key]['comment']) ? $this->cronjobs[$key]['comment'] : '';
1280
+                $frequency = isset($this->cronjobs[$key]['frequency']) ? $this->cronjobs[$key]['frequency'] : '';
1281
+                $unitfrequency = isset($this->cronjobs[$key]['unitfrequency']) ? $this->cronjobs[$key]['unitfrequency'] : '';
1282
+                $priority = isset($this->cronjobs[$key]['priority']) ? $this->cronjobs[$key]['priority'] : '';
1283
+                $datestart = isset($this->cronjobs[$key]['datestart']) ? $this->cronjobs[$key]['datestart'] : '';
1284
+                $dateend = isset($this->cronjobs[$key]['dateend']) ? $this->cronjobs[$key]['dateend'] : '';
1285
+                $status = isset($this->cronjobs[$key]['status']) ? $this->cronjobs[$key]['status'] : '';
1286
+                $test = isset($this->cronjobs[$key]['test']) ? $this->cronjobs[$key]['test'] : ''; // Line must be enabled or not (so visible or not)
1287
+
1288
+                // Search if cron entry already present
1289
+                $sql = "SELECT count(*) as nb FROM " . $dbPrefix . "cronjob";
1290
+                //$sql .= " WHERE module_name = '".$this->db->escape(empty($this->rights_class) ?strtolower($this->name) : $this->rights_class)."'";
1291
+                $sql .= " WHERE label = '" . $this->db->escape($label) . "'";
1292
+                /*if ($class) {
1293 1293
 					$sql .= " AND classesname = '".$this->db->escape($class)."'";
1294 1294
 				}
1295 1295
 				if ($objectname) {
@@ -1304,1304 +1304,1304 @@  discard block
 block discarded – undo
1304 1304
 				if ($parameters) {
1305 1305
 					$sql .= " AND params = '".$this->db->escape($parameters)."'";
1306 1306
 				}*/
1307
-				$sql .= " AND entity = " . ((int)$entity); // Must be exact entity
1308
-
1309
-				$now = dol_now();
1310
-
1311
-				$result = $this->db->query($sql);
1312
-				if ($result) {
1313
-					$obj = $this->db->fetch_object($result);
1314
-					if ($obj->nb == 0) {
1315
-						$this->db->begin();
1316
-
1317
-						if (!$err) {
1318
-							$sql = "INSERT INTO " . $dbPrefix . "cronjob (module_name, datec, datestart, dateend, label, jobtype, classesname, objectname, methodename, command, params, note,";
1319
-							if (is_int($frequency)) {
1320
-								$sql .= ' frequency,';
1321
-							}
1322
-							if (is_int($unitfrequency)) {
1323
-								$sql .= ' unitfrequency,';
1324
-							}
1325
-							if (is_int($priority)) {
1326
-								$sql .= ' priority,';
1327
-							}
1328
-							if (is_int($status)) {
1329
-								$sql .= ' status,';
1330
-							}
1331
-							$sql .= " entity, test)";
1332
-							$sql .= " VALUES (";
1333
-							$sql .= "'" . $this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class) . "', ";
1334
-							$sql .= "'" . $this->db->idate($now) . "', ";
1335
-							$sql .= ($datestart ? "'" . $this->db->idate($datestart) . "'" : "'" . $this->db->idate($now) . "'") . ", ";
1336
-							$sql .= ($dateend ? "'" . $this->db->idate($dateend) . "'" : "NULL") . ", ";
1337
-							$sql .= "'" . $this->db->escape($label) . "', ";
1338
-							$sql .= "'" . $this->db->escape($jobtype) . "', ";
1339
-							$sql .= ($class ? "'" . $this->db->escape($class) . "'" : "null") . ",";
1340
-							$sql .= ($objectname ? "'" . $this->db->escape($objectname) . "'" : "null") . ",";
1341
-							$sql .= ($method ? "'" . $this->db->escape($method) . "'" : "null") . ",";
1342
-							$sql .= ($command ? "'" . $this->db->escape($command) . "'" : "null") . ",";
1343
-							$sql .= ($parameters ? "'" . $this->db->escape($parameters) . "'" : "null") . ",";
1344
-							$sql .= ($comment ? "'" . $this->db->escape($comment) . "'" : "null") . ",";
1345
-							if (is_int($frequency)) {
1346
-								$sql .= "'" . $this->db->escape($frequency) . "', ";
1347
-							}
1348
-							if (is_int($unitfrequency)) {
1349
-								$sql .= "'" . $this->db->escape($unitfrequency) . "', ";
1350
-							}
1351
-							if (is_int($priority)) {
1352
-								$sql .= "'" . $this->db->escape($priority) . "', ";
1353
-							}
1354
-							if (is_int($status)) {
1355
-								$sql .= ((int)$status) . ", ";
1356
-							}
1357
-							$sql .= $entity . ",";
1358
-							$sql .= "'" . $this->db->escape($test) . "'";
1359
-							$sql .= ")";
1360
-
1361
-							$resql = $this->db->query($sql);
1362
-							if (!$resql) {
1363
-								$err++;
1364
-							}
1365
-						}
1366
-
1367
-						if (!$err) {
1368
-							$this->db->commit();
1369
-						} else {
1370
-							$this->error = $this->db->lasterror();
1371
-							$this->db->rollback();
1372
-						}
1373
-					}
1374
-					// else box already registered into database
1375
-				} else {
1376
-					$this->error = $this->db->lasterror();
1377
-					$err++;
1378
-				}
1379
-			}
1380
-		}
1381
-
1382
-		return $err;
1383
-	}
1384
-
1385
-
1386
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps,PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1387
-
1388
-	/**
1389
-	 * Adds access rights
1390
-	 *
1391
-	 * @param int $reinitadminperms If 1, we also grant them to all admin users
1392
-	 * @param int $force_entity Force current entity
1393
-	 * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
1394
-	 *
1395
-	 * @return int                      Error count (0 if OK)
1396
-	 */
1397
-	public function insert_permissions($reinitadminperms = 0, $force_entity = null, $notrigger = 0)
1398
-	{
1399
-		// phpcs:enable
1400
-		global $conf, $user;
1401
-
1402
-		global $config;
1403
-		$dbPrefix = $config->db->prefix;
1404
-
1405
-		$err = 0;
1406
-		$entity = (!empty($force_entity) ? $force_entity : $conf->entity);
1407
-
1408
-		dol_syslog(get_class($this) . "::insert_permissions", LOG_DEBUG);
1409
-
1410
-		// Test if module is activated
1411
-		$sql_del = "SELECT " . $this->db->decrypt('value') . " as value";
1412
-		$sql_del .= " FROM " . $dbPrefix . "const";
1413
-		$sql_del .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
1414
-		$sql_del .= " AND entity IN (0," . $entity . ")";
1415
-
1416
-		$resql = $this->db->query($sql_del);
1417
-		if ($resql) {
1418
-			$obj = $this->db->fetch_object($resql);
1419
-
1420
-			if ($obj !== null && !empty($obj->value) && !empty($this->rights)) {
1421
-				// If the module is active
1422
-				foreach ($this->rights as $key => $value) {
1423
-					$r_id = $this->rights[$key][0];   // permission id in llx_rights_def (not unique because primary key is couple id-entity)
1424
-					$r_desc = $this->rights[$key][1];
1425
-					$r_type = isset($this->rights[$key][2]) ? $this->rights[$key][2] : '';
1426
-					$r_def = empty($this->rights[$key][3]) ? 0 : $this->rights[$key][3];
1427
-					$r_perms = $this->rights[$key][4];
1428
-					$r_subperms = isset($this->rights[$key][5]) ? $this->rights[$key][5] : '';
1429
-					$r_modul = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
1430
-
1431
-					if (empty($r_type)) {
1432
-						$r_type = 'w';
1433
-					}
1434
-
1435
-					// Search if perm already present
1436
-					$sql = "SELECT count(*) as nb FROM " . $dbPrefix . "rights_def";
1437
-					$sql .= " WHERE id = " . ((int)$r_id) . " AND entity = " . ((int)$entity);
1438
-
1439
-					$resqlselect = $this->db->query($sql);
1440
-					if ($resqlselect) {
1441
-						$objcount = $this->db->fetch_object($resqlselect);
1442
-						if ($objcount && $objcount->nb == 0) {
1443
-							if (dol_strlen($r_perms)) {
1444
-								if (dol_strlen($r_subperms)) {
1445
-									$sql = "INSERT INTO " . $dbPrefix . "rights_def";
1446
-									$sql .= " (id, entity, libelle, module, type, bydefault, perms, subperms)";
1447
-									$sql .= " VALUES ";
1448
-									$sql .= "(" . $r_id . "," . $entity . ",'" . $this->db->escape($r_desc) . "','" . $this->db->escape($r_modul) . "','" . $this->db->escape($r_type) . "'," . $r_def . ",'" . $this->db->escape($r_perms) . "','" . $this->db->escape($r_subperms) . "')";
1449
-								} else {
1450
-									$sql = "INSERT INTO " . $dbPrefix . "rights_def";
1451
-									$sql .= " (id, entity, libelle, module, type, bydefault, perms)";
1452
-									$sql .= " VALUES ";
1453
-									$sql .= "(" . $r_id . "," . $entity . ",'" . $this->db->escape($r_desc) . "','" . $this->db->escape($r_modul) . "','" . $this->db->escape($r_type) . "'," . $r_def . ",'" . $this->db->escape($r_perms) . "')";
1454
-								}
1455
-							} else {
1456
-								$sql = "INSERT INTO " . $dbPrefix . "rights_def ";
1457
-								$sql .= " (id, entity, libelle, module, type, bydefault)";
1458
-								$sql .= " VALUES ";
1459
-								$sql .= "(" . $r_id . "," . $entity . ",'" . $this->db->escape($r_desc) . "','" . $this->db->escape($r_modul) . "','" . $this->db->escape($r_type) . "'," . $r_def . ")";
1460
-							}
1461
-
1462
-							$resqlinsert = $this->db->query($sql, 1);
1463
-
1464
-							if (!$resqlinsert) {
1465
-								if ($this->db->errno() != "DB_ERROR_RECORD_ALREADY_EXISTS") {
1466
-									$this->error = $this->db->lasterror();
1467
-									$err++;
1468
-									break;
1469
-								} else {
1470
-									dol_syslog(get_class($this) . "::insert_permissions record already exists", LOG_INFO);
1471
-								}
1472
-							}
1473
-
1474
-							$this->db->free($resqlinsert);
1475
-						}
1476
-
1477
-						$this->db->free($resqlselect);
1478
-					}
1479
-
1480
-					// If we want to init permissions on admin users
1481
-					if ($reinitadminperms) {
1482
-						$sql = "SELECT rowid FROM " . $dbPrefix . "user WHERE admin = 1";
1483
-						dol_syslog(get_class($this) . "::insert_permissions Search all admin users", LOG_DEBUG);
1484
-
1485
-						$resqlseladmin = $this->db->query($sql, 1);
1486
-
1487
-						if ($resqlseladmin) {
1488
-							$num = $this->db->num_rows($resqlseladmin);
1489
-							$i = 0;
1490
-							while ($i < $num) {
1491
-								$obj2 = $this->db->fetch_object($resqlseladmin);
1492
-								dol_syslog(get_class($this) . "::insert_permissions Add permission id " . $r_id . " to user id=" . $obj2->rowid);
1493
-
1494
-								$tmpuser = new User($this->db);
1495
-								$result = $tmpuser->fetch($obj2->rowid);
1496
-								if ($result > 0) {
1497
-									$tmpuser->addrights($r_id, '', '', 0, 1);
1498
-								} else {
1499
-									dol_syslog(get_class($this) . "::insert_permissions Failed to add the permission to user because fetch return an error", LOG_ERR);
1500
-								}
1501
-								$i++;
1502
-							}
1503
-						} else {
1504
-							dol_print_error($this->db);
1505
-						}
1506
-					}
1507
-				}
1508
-
1509
-				if ($reinitadminperms && !empty($user->admin)) {  // Reload permission for current user if defined
1510
-					// We reload permissions
1511
-					$user->clearrights();
1512
-					$user->getrights();
1513
-				}
1514
-			}
1515
-			$this->db->free($resql);
1516
-		} else {
1517
-			$this->error = $this->db->lasterror();
1518
-			$err++;
1519
-		}
1520
-
1521
-		return $err;
1522
-	}
1523
-
1524
-
1525
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1526
-
1527
-	/**
1528
-	 * Adds menu entries
1529
-	 *
1530
-	 * @return int     Error count (0 if OK)
1531
-	 */
1532
-	public function insert_menus()
1533
-	{
1534
-		// phpcs:enable
1535
-		global $conf, $user;
1536
-
1537
-		if (!is_array($this->menu) || empty($this->menu)) {
1538
-			return 0;
1539
-		}
1540
-
1541
-		include_once DOL_DOCUMENT_ROOT . '/core/class/menubase.class.php';
1542
-
1543
-		dol_syslog(get_class($this) . "::insert_menus", LOG_DEBUG);
1544
-
1545
-		$err = 0;
1546
-
1547
-		// Common module
1548
-		$entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
1549
-
1550
-		$this->db->begin();
1551
-
1552
-		foreach ($this->menu as $key => $value) {
1553
-			$menu = new Menubase($this->db);
1554
-			$menu->menu_handler = 'all';
1555
-
1556
-			//$menu->module=strtolower($this->name);    TODO When right_class will be same than module name
1557
-			$menu->module = (empty($this->rights_class) ? strtolower($this->name) : $this->rights_class);
1558
-
1559
-			if (!$this->menu[$key]['fk_menu']) {
1560
-				$menu->fk_menu = 0;
1561
-			} else {
1562
-				$foundparent = 0;
1563
-				$fk_parent = $this->menu[$key]['fk_menu'];
1564
-				$reg = [];
1565
-				if (preg_match('/^r=/', $fk_parent)) {    // old deprecated method
1566
-					$fk_parent = str_replace('r=', '', $fk_parent);
1567
-					if (isset($this->menu[$fk_parent]['rowid'])) {
1568
-						$menu->fk_menu = $this->menu[$fk_parent]['rowid'];
1569
-						$foundparent = 1;
1570
-					}
1571
-				} elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+),fk_leftmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
1572
-					$menu->fk_menu = -1;
1573
-					$menu->fk_mainmenu = $reg[1];
1574
-					$menu->fk_leftmenu = $reg[2];
1575
-					$foundparent = 1;
1576
-				} elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
1577
-					$menu->fk_menu = -1;
1578
-					$menu->fk_mainmenu = $reg[1];
1579
-					$menu->fk_leftmenu = '';
1580
-					$foundparent = 1;
1581
-				}
1582
-				if (!$foundparent) {
1583
-					$this->error = "ErrorBadDefinitionOfMenuArrayInModuleDescriptor";
1584
-					dol_syslog(get_class($this) . "::insert_menus " . $this->error . " " . $this->menu[$key]['fk_menu'], LOG_ERR);
1585
-					$err++;
1586
-				}
1587
-			}
1588
-			$menu->type = $this->menu[$key]['type'];
1589
-			$menu->mainmenu = isset($this->menu[$key]['mainmenu']) ? $this->menu[$key]['mainmenu'] : (isset($menu->fk_mainmenu) ? $menu->fk_mainmenu : '');
1590
-			$menu->leftmenu = isset($this->menu[$key]['leftmenu']) ? $this->menu[$key]['leftmenu'] : '';
1591
-			$menu->title = $this->menu[$key]['titre'];
1592
-			$menu->prefix = isset($this->menu[$key]['prefix']) ? $this->menu[$key]['prefix'] : '';
1593
-			$menu->url = $this->menu[$key]['url'];
1594
-			$menu->langs = isset($this->menu[$key]['langs']) ? $this->menu[$key]['langs'] : '';
1595
-			$menu->position = $this->menu[$key]['position'];
1596
-			$menu->perms = $this->menu[$key]['perms'];
1597
-			$menu->target = isset($this->menu[$key]['target']) ? $this->menu[$key]['target'] : '';
1598
-			$menu->user = $this->menu[$key]['user'];
1599
-			$menu->enabled = isset($this->menu[$key]['enabled']) ? $this->menu[$key]['enabled'] : 0;
1600
-			$menu->position = $this->menu[$key]['position'];
1601
-			$menu->entity = $entity;
1602
-
1603
-			if (!$err) {
1604
-				$result = $menu->create($user); // Save menu entry into table llx_menu
1605
-				if ($result > 0) {
1606
-					$this->menu[$key]['rowid'] = $result;
1607
-				} else {
1608
-					$this->error = $menu->error;
1609
-					dol_syslog(get_class($this) . '::insert_menus result=' . $result . " " . $this->error, LOG_ERR);
1610
-					$err++;
1611
-					break;
1612
-				}
1613
-			}
1614
-		}
1615
-
1616
-		if (!$err) {
1617
-			$this->db->commit();
1618
-		} else {
1619
-			dol_syslog(get_class($this) . "::insert_menus " . $this->error, LOG_ERR);
1620
-			$this->db->rollback();
1621
-		}
1622
-
1623
-		return $err;
1624
-	}
1625
-
1626
-
1627
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1628
-
1629
-	/**
1630
-	 * Creates directories
1631
-	 *
1632
-	 * @return int Error count (0 if OK)
1633
-	 */
1634
-	public function create_dirs()
1635
-	{
1636
-		// phpcs:enable
1637
-		global $langs, $conf;
1638
-
1639
-		$err = 0;
1640
-		$name = '';
1641
-
1642
-		if (isset($this->dirs) && is_array($this->dirs)) {
1643
-			foreach ($this->dirs as $key => $value) {
1644
-				$addtodatabase = 0;
1645
-
1646
-				if (!is_array($value)) {
1647
-					$dir = $value; // Default simple mode
1648
-				} else {
1649
-					$constname = $this->const_name . "_DIR_";
1650
-					$dir = $this->dirs[$key][1];
1651
-					$addtodatabase = empty($this->dirs[$key][2]) ? '' : $this->dirs[$key][2]; // Create constante in llx_const
1652
-					$subname = empty($this->dirs[$key][3]) ? '' : strtoupper($this->dirs[$key][3]); // Add submodule name (ex: $conf->module->submodule->dir_output)
1653
-					$forcename = empty($this->dirs[$key][4]) ? '' : strtoupper($this->dirs[$key][4]); // Change the module name if different
1654
-
1655
-					if (!empty($forcename)) {
1656
-						$constname = 'MAIN_MODULE_' . $forcename . "_DIR_";
1657
-					}
1658
-					if (!empty($subname)) {
1659
-						$constname = $constname . $subname . "_";
1660
-					}
1661
-
1662
-					$name = $constname . strtoupper($this->dirs[$key][0]);
1663
-				}
1664
-
1665
-				// Define directory full path ($dir must start with "/")
1666
-				if (!getDolGlobalString('MAIN_MODULE_MULTICOMPANY') || $conf->entity == 1) {
1667
-					$fulldir = DOL_DATA_ROOT . $dir;
1668
-				} else {
1669
-					$fulldir = DOL_DATA_ROOT . "/" . $conf->entity . $dir;
1670
-				}
1671
-				// Create dir if it does not exists
1672
-				if (!empty($fulldir) && !file_exists($fulldir)) {
1673
-					if (dol_mkdir($fulldir, DOL_DATA_ROOT) < 0) {
1674
-						$this->error = $langs->trans("ErrorCanNotCreateDir", $fulldir);
1675
-						dol_syslog(get_class($this) . "::_init " . $this->error, LOG_ERR);
1676
-						$err++;
1677
-					}
1678
-				}
1679
-
1680
-				// Define the constant in database if requested (not the default mode)
1681
-				if (!empty($addtodatabase) && !empty($name)) {
1682
-					$result = $this->insert_dirs($name, $dir);
1683
-					if ($result) {
1684
-						$err++;
1685
-					}
1686
-				}
1687
-			}
1688
-		}
1689
-
1690
-		return $err;
1691
-	}
1692
-
1693
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1694
-
1695
-	/**
1696
-	 * Adds directories definitions
1697
-	 *
1698
-	 * @param string $name Name
1699
-	 * @param string $dir Directory
1700
-	 *
1701
-	 * @return int             Error count (0 if OK)
1702
-	 */
1703
-	public function insert_dirs($name, $dir)
1704
-	{
1705
-		// phpcs:enable
1706
-		global $conf;
1707
-
1708
-		$err = 0;
1709
-
1710
-		$sql = "SELECT count(*)";
1711
-		$sql .= " FROM " . $dbPrefix . "const";
1712
-		$sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($name) . "'";
1713
-		$sql .= " AND entity = " . $conf->entity;
1714
-
1715
-		dol_syslog(get_class($this) . "::insert_dirs", LOG_DEBUG);
1716
-		$result = $this->db->query($sql);
1717
-		if ($result) {
1718
-			$row = $this->db->fetch_row($result);
1719
-
1720
-			if ($row[0] == 0) {
1721
-				$sql = "INSERT INTO " . $dbPrefix . "const (name, type, value, note, visible, entity)";
1722
-				$sql .= " VALUES (" . $this->db->encrypt($name) . ", 'chaine', " . $this->db->encrypt($dir) . ", '" . $this->db->escape("Directory for module " . $this->name) . "', '0', " . ((int)$conf->entity) . ")";
1723
-
1724
-				dol_syslog(get_class($this) . "::insert_dirs", LOG_DEBUG);
1725
-				$this->db->query($sql);
1726
-			}
1727
-		} else {
1728
-			$this->error = $this->db->lasterror();
1729
-			$err++;
1730
-		}
1731
-
1732
-		return $err;
1733
-	}
1734
-
1735
-
1736
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1737
-
1738
-	/**
1739
-	 * Function called when module is disabled.
1740
-	 * The remove function removes tabs, constants, boxes, permissions and menus from Dolibarr database.
1741
-	 * Data directories are not deleted
1742
-	 *
1743
-	 * @param string $options Options when enabling module ('', 'noboxes')
1744
-	 *
1745
-	 * @return int                     1 if OK, 0 if KO
1746
-	 */
1747
-	public function remove($options = '')
1748
-	{
1749
-		return $this->_remove([], $options);
1750
-	}
1751
-
1752
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1753
-
1754
-	/**
1755
-	 * Disable function. Deletes the module constants and boxes from the database.
1756
-	 *
1757
-	 * @param string[] $array_sql SQL requests to be executed when module is disabled
1758
-	 * @param string $options Options when disabling module:
1759
-	 *
1760
-	 * @return int                     1 if OK, 0 if KO
1761
-	 */
1762
-	protected function _remove($array_sql, $options = '')
1763
-	{
1764
-		// phpcs:enable
1765
-		$err = 0;
1766
-
1767
-		$this->db->begin();
1768
-
1769
-		// Remove activation module line (constant MAIN_MODULE_MYMODULE in llx_const)
1770
-		if (!$err) {
1771
-			$err += $this->_unactive();
1772
-		}
1773
-
1774
-		// Remove activation of module's new tabs (MAIN_MODULE_MYMODULE_TABS_XXX in llx_const)
1775
-		if (!$err) {
1776
-			$err += $this->delete_tabs();
1777
-		}
1778
-
1779
-		// Remove activation of module's parts (MAIN_MODULE_MYMODULE_XXX in llx_const)
1780
-		if (!$err) {
1781
-			$err += $this->delete_module_parts();
1782
-		}
1783
-
1784
-		// Remove constants defined by modules
1785
-		if (!$err) {
1786
-			$err += $this->delete_const();
1787
-		}
1788
-
1789
-		// Remove list of module's available boxes (entry in llx_boxes)
1790
-		if (!$err && !preg_match('/(newboxdefonly|noboxes)/', $options)) {
1791
-			$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
1792
-		}
1793
-
1794
-		// Remove list of module's cron job entries (entry in llx_cronjobs)
1795
-		if (!$err) {
1796
-			$err += $this->delete_cronjobs();
1797
-		}
1798
-
1799
-		// Remove module's permissions from list of available permissions (entries in llx_rights_def)
1800
-		if (!$err) {
1801
-			$err += $this->delete_permissions();
1802
-		}
1803
-
1804
-		// Remove module's menus (entries in llx_menu)
1805
-		if (!$err) {
1806
-			$err += $this->delete_menus();
1807
-		}
1808
-
1809
-		// Remove module's directories
1810
-		if (!$err) {
1811
-			$err += $this->delete_dirs();
1812
-		}
1813
-
1814
-		// Run complementary sql requests
1815
-		$num = count((array)$array_sql);
1816
-		for ($i = 0; $i < $num; $i++) {
1817
-			if (!$err) {
1818
-				dol_syslog(get_class($this) . "::_remove", LOG_DEBUG);
1819
-				$result = $this->db->query($array_sql[$i]);
1820
-				if (!$result) {
1821
-					$this->error = $this->db->error();
1822
-					$err++;
1823
-				}
1824
-			}
1825
-		}
1826
-
1827
-		// Return code
1828
-		if (!$err) {
1829
-			$this->db->commit();
1830
-			return 1;
1831
-		} else {
1832
-			$this->db->rollback();
1833
-			return 0;
1834
-		}
1835
-	}
1836
-
1837
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1838
-
1839
-	/**
1840
-	 * Module deactivation
1841
-	 *
1842
-	 * @return int Error count (0 if OK)
1843
-	 */
1844
-	protected function _unactive()
1845
-	{
1846
-		// phpcs:enable
1847
-		global $conf;
1848
-
1849
-		$err = 0;
1850
-
1851
-		// Common module
1852
-		$entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
1853
-
1854
-		$sql = "DELETE FROM " . $dbPrefix . "const";
1855
-		$sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
1856
-		$sql .= " AND entity IN (0, " . $entity . ")";
1857
-
1858
-		dol_syslog(get_class($this) . "::_unactive", LOG_DEBUG);
1859
-		$this->db->query($sql);
1860
-
1861
-		return $err;
1862
-	}
1863
-
1864
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1865
-
1866
-	/**
1867
-	 * Removes tabs
1868
-	 *
1869
-	 * @return int Error count (0 if OK)
1870
-	 */
1871
-	public function delete_tabs()
1872
-	{
1873
-		// phpcs:enable
1874
-		global $conf;
1875
-
1876
-		$err = 0;
1877
-
1878
-		$sql = "DELETE FROM " . $dbPrefix . "const";
1879
-		$sql .= " WHERE " . $this->db->decrypt('name') . " like '" . $this->db->escape($this->const_name) . "_TABS_%'";
1880
-		$sql .= " AND entity = " . $conf->entity;
1881
-
1882
-		dol_syslog(get_class($this) . "::delete_tabs", LOG_DEBUG);
1883
-		if (!$this->db->query($sql)) {
1884
-			$this->error = $this->db->lasterror();
1885
-			$err++;
1886
-		}
1887
-
1888
-		return $err;
1889
-	}
1890
-
1891
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1892
-
1893
-	/**
1894
-	 * Removes generic parts
1895
-	 *
1896
-	 * @return int Error count (0 if OK)
1897
-	 */
1898
-	public function delete_module_parts()
1899
-	{
1900
-		// phpcs:enable
1901
-		global $conf;
1902
-
1903
-		$err = 0;
1904
-
1905
-		if (is_array($this->module_parts)) {
1906
-			dol_syslog(get_class($this) . "::delete_module_parts", LOG_DEBUG);
1907
-
1908
-			if (empty($this->module_parts['icon']) && !empty($this->picto) && preg_match('/^fa\-/', $this->picto)) {
1909
-				$this->module_parts['icon'] = $this->picto;
1910
-			}
1911
-
1912
-			foreach ($this->module_parts as $key => $value) {
1913
-				// If entity is defined
1914
-				if (is_array($value) && isset($value['entity'])) {
1915
-					$entity = $value['entity'];
1916
-				} else {
1917
-					$entity = $conf->entity;
1918
-				}
1919
-
1920
-				$sql = "DELETE FROM " . $dbPrefix . "const";
1921
-				$sql .= " WHERE " . $this->db->decrypt('name') . " LIKE '" . $this->db->escape($this->const_name) . "_" . strtoupper($key) . "'";
1922
-				$sql .= " AND entity = " . ((int)$entity);
1923
-
1924
-				if (!$this->db->query($sql)) {
1925
-					$this->error = $this->db->lasterror();
1926
-					$err++;
1927
-				}
1928
-			}
1929
-		}
1930
-		return $err;
1931
-	}
1932
-
1933
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1934
-
1935
-	/**
1936
-	 * Removes constants tagged 'deleteonunactive'
1937
-	 *
1938
-	 * @return int Return integer <0 if KO, 0 if OK
1939
-	 */
1940
-	public function delete_const()
1941
-	{
1942
-		// phpcs:enable
1943
-		global $conf;
1944
-
1945
-		$err = 0;
1946
-
1947
-		if (empty($this->const)) {
1948
-			return 0;
1949
-		}
1950
-
1951
-		foreach ($this->const as $key => $value) {
1952
-			$name = $this->const[$key][0];
1953
-			$deleteonunactive = (!empty($this->const[$key][6])) ? 1 : 0;
1954
-
1955
-			if ($deleteonunactive) {
1956
-				$sql = "DELETE FROM " . $dbPrefix . "const";
1957
-				$sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($name) . "'";
1958
-				$sql .= " AND entity in (0, " . $conf->entity . ")";
1959
-				dol_syslog(get_class($this) . "::delete_const", LOG_DEBUG);
1960
-				if (!$this->db->query($sql)) {
1961
-					$this->error = $this->db->lasterror();
1962
-					$err++;
1963
-				}
1964
-			}
1965
-		}
1966
-
1967
-		return $err;
1968
-	}
1969
-
1970
-
1971
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1972
-
1973
-	/**
1974
-	 * Removes boxes
1975
-	 *
1976
-	 * @return int Error count (0 if OK)
1977
-	 */
1978
-	public function delete_boxes()
1979
-	{
1980
-		// phpcs:enable
1981
-		global $conf;
1982
-
1983
-		$err = 0;
1984
-
1985
-		if (is_array($this->boxes)) {
1986
-			foreach ($this->boxes as $key => $value) {
1987
-				//$titre = $this->boxes[$key][0];
1988
-				if (empty($this->boxes[$key]['file'])) {
1989
-					$file = isset($this->boxes[$key][1]) ? $this->boxes[$key][1] : ''; // For backward compatibility
1990
-				} else {
1991
-					$file = $this->boxes[$key]['file'];
1992
-				}
1993
-
1994
-				//$note  = $this->boxes[$key][2];
1995
-
1996
-				// TODO If the box is also included by another module and the other module is still on, we should not remove it.
1997
-				// For the moment, we manage this with hard coded exception
1998
-				//print "Remove box ".$file.'<br>';
1999
-				if ($file == 'box_graph_product_distribution.php') {
2000
-					if (isModEnabled("product") || isModEnabled("service")) {
2001
-						dol_syslog("We discard deleting module " . $file . " because another module still active requires it.");
2002
-						continue;
2003
-					}
2004
-				}
2005
-
2006
-				if ($this->db->type == 'sqlite3') {
2007
-					// sqlite doesn't support "USING" syntax.
2008
-					// TODO: remove this dependency.
2009
-					$sql = "DELETE FROM " . $dbPrefix . "boxes ";
2010
-					$sql .= "WHERE " . $dbPrefix . "boxes.box_id IN (";
2011
-					$sql .= "SELECT " . $dbPrefix . "boxes_def.rowid ";
2012
-					$sql .= "FROM " . $dbPrefix . "boxes_def ";
2013
-					$sql .= "WHERE " . $dbPrefix . "boxes_def.file = '" . $this->db->escape($file) . "') ";
2014
-					$sql .= "AND " . $dbPrefix . "boxes.entity = " . $conf->entity;
2015
-				} else {
2016
-					$sql = "DELETE FROM " . $dbPrefix . "boxes";
2017
-					$sql .= " USING " . $dbPrefix . "boxes, " . $dbPrefix . "boxes_def";
2018
-					$sql .= " WHERE " . $dbPrefix . "boxes.box_id = " . $dbPrefix . "boxes_def.rowid";
2019
-					$sql .= " AND " . $dbPrefix . "boxes_def.file = '" . $this->db->escape($file) . "'";
2020
-					$sql .= " AND " . $dbPrefix . "boxes.entity = " . $conf->entity;
2021
-				}
2022
-
2023
-				dol_syslog(get_class($this) . "::delete_boxes", LOG_DEBUG);
2024
-				$resql = $this->db->query($sql);
2025
-				if (!$resql) {
2026
-					$this->error = $this->db->lasterror();
2027
-					$err++;
2028
-				}
2029
-
2030
-				$sql = "DELETE FROM " . $dbPrefix . "boxes_def";
2031
-				$sql .= " WHERE file = '" . $this->db->escape($file) . "'";
2032
-				$sql .= " AND entity = " . $conf->entity;     // Do not use getEntity here, we want to delete only in current company
2033
-
2034
-				dol_syslog(get_class($this) . "::delete_boxes", LOG_DEBUG);
2035
-				$resql = $this->db->query($sql);
2036
-				if (!$resql) {
2037
-					$this->error = $this->db->lasterror();
2038
-					$err++;
2039
-				}
2040
-			}
2041
-		}
2042
-
2043
-		return $err;
2044
-	}
2045
-
2046
-
2047
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2048
-
2049
-	/**
2050
-	 * Removes boxes
2051
-	 *
2052
-	 * @return int Error count (0 if OK)
2053
-	 */
2054
-	public function delete_cronjobs()
2055
-	{
2056
-		// phpcs:enable
2057
-		global $conf;
2058
-
2059
-		$err = 0;
2060
-
2061
-		if (is_array($this->cronjobs)) {
2062
-			$sql = "DELETE FROM " . $dbPrefix . "cronjob";
2063
-			$sql .= " WHERE module_name = '" . $this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class) . "'";
2064
-			$sql .= " AND entity = " . $conf->entity;
2065
-			$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.
2066
-			// 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.
2067
-
2068
-			dol_syslog(get_class($this) . "::delete_cronjobs", LOG_DEBUG);
2069
-			$resql = $this->db->query($sql);
2070
-			if (!$resql) {
2071
-				$this->error = $this->db->lasterror();
2072
-				$err++;
2073
-			}
2074
-		}
2075
-
2076
-		return $err;
2077
-	}
2078
-
2079
-
2080
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2081
-
2082
-	/**
2083
-	 * Removes access rights
2084
-	 *
2085
-	 * @return int                     Error count (0 if OK)
2086
-	 */
2087
-	public function delete_permissions()
2088
-	{
2089
-		// phpcs:enable
2090
-		global $conf;
2091
-
2092
-		$err = 0;
2093
-
2094
-		$sql = "DELETE FROM " . $dbPrefix . "rights_def";
2095
-		$sql .= " WHERE module = '" . $this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class) . "'";
2096
-		$sql .= " AND entity = " . $conf->entity;
2097
-		dol_syslog(get_class($this) . "::delete_permissions", LOG_DEBUG);
2098
-		if (!$this->db->query($sql)) {
2099
-			$this->error = $this->db->lasterror();
2100
-			$err++;
2101
-		}
2102
-
2103
-		return $err;
2104
-	}
2105
-
2106
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2107
-
2108
-	/**
2109
-	 * Removes menu entries
2110
-	 *
2111
-	 * @return int Error count (0 if OK)
2112
-	 */
2113
-	public function delete_menus()
2114
-	{
2115
-		// phpcs:enable
2116
-		global $conf;
2117
-
2118
-		$err = 0;
2119
-
2120
-		//$module=strtolower($this->name);        TODO When right_class will be same than module name
2121
-		$module = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
2122
-
2123
-		$sql = "DELETE FROM " . $dbPrefix . "menu";
2124
-		$sql .= " WHERE module = '" . $this->db->escape($module) . "'";
2125
-		$sql .= " AND entity IN (0, " . $conf->entity . ")";
2126
-
2127
-		dol_syslog(get_class($this) . "::delete_menus", LOG_DEBUG);
2128
-		$resql = $this->db->query($sql);
2129
-		if (!$resql) {
2130
-			$this->error = $this->db->lasterror();
2131
-			$err++;
2132
-		}
2133
-
2134
-		return $err;
2135
-	}
2136
-
2137
-
2138
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2139
-
2140
-	/**
2141
-	 * Removes directories
2142
-	 *
2143
-	 * @return int Error count (0 if OK)
2144
-	 */
2145
-	public function delete_dirs()
2146
-	{
2147
-		// phpcs:enable
2148
-		global $conf;
2149
-
2150
-		$err = 0;
2151
-
2152
-		$sql = "DELETE FROM " . $dbPrefix . "const";
2153
-		$sql .= " WHERE " . $this->db->decrypt('name') . " LIKE '" . $this->db->escape($this->const_name) . "_DIR_%'";
2154
-		$sql .= " AND entity = " . $conf->entity;
2155
-
2156
-		dol_syslog(get_class($this) . "::delete_dirs", LOG_DEBUG);
2157
-		if (!$this->db->query($sql)) {
2158
-			$this->error = $this->db->lasterror();
2159
-			$err++;
2160
-		}
2161
-
2162
-		return $err;
2163
-	}
2164
-
2165
-
2166
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2167
-
2168
-	/**
2169
-	 * Return Kanban view of a module
2170
-	 *
2171
-	 * @param string $codeenabledisable HTML code for button to enable/disable module
2172
-	 * @param string $codetoconfig HTML code to go to config page
2173
-	 *
2174
-	 * @return  string                          HTML code of Kanban view
2175
-	 */
2176
-	public function getKanbanView($codeenabledisable = '', $codetoconfig = '')
2177
-	{
2178
-		global $conf, $langs;
2179
-
2180
-		// Define imginfo
2181
-		$imginfo = "info";
2182
-		if ($this->isCoreOrExternalModule() == 'external') {
2183
-			$imginfo = "info_black";
2184
-		}
2185
-
2186
-		$const_name = 'MAIN_MODULE_' . strtoupper(preg_replace('/^mod/i', '', get_class($this)));
2187
-
2188
-		$version = $this->getVersion(0);
2189
-		$versiontrans = '';
2190
-		if (preg_match('/development/i', $version)) {
2191
-			$versiontrans .= 'warning';
2192
-		}
2193
-		if (preg_match('/experimental/i', $version)) {
2194
-			$versiontrans .= 'warning';
2195
-		}
2196
-		if (preg_match('/deprecated/i', $version)) {
2197
-			$versiontrans .= 'warning';
2198
-		}
2199
-
2200
-		$return = '
1307
+                $sql .= " AND entity = " . ((int)$entity); // Must be exact entity
1308
+
1309
+                $now = dol_now();
1310
+
1311
+                $result = $this->db->query($sql);
1312
+                if ($result) {
1313
+                    $obj = $this->db->fetch_object($result);
1314
+                    if ($obj->nb == 0) {
1315
+                        $this->db->begin();
1316
+
1317
+                        if (!$err) {
1318
+                            $sql = "INSERT INTO " . $dbPrefix . "cronjob (module_name, datec, datestart, dateend, label, jobtype, classesname, objectname, methodename, command, params, note,";
1319
+                            if (is_int($frequency)) {
1320
+                                $sql .= ' frequency,';
1321
+                            }
1322
+                            if (is_int($unitfrequency)) {
1323
+                                $sql .= ' unitfrequency,';
1324
+                            }
1325
+                            if (is_int($priority)) {
1326
+                                $sql .= ' priority,';
1327
+                            }
1328
+                            if (is_int($status)) {
1329
+                                $sql .= ' status,';
1330
+                            }
1331
+                            $sql .= " entity, test)";
1332
+                            $sql .= " VALUES (";
1333
+                            $sql .= "'" . $this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class) . "', ";
1334
+                            $sql .= "'" . $this->db->idate($now) . "', ";
1335
+                            $sql .= ($datestart ? "'" . $this->db->idate($datestart) . "'" : "'" . $this->db->idate($now) . "'") . ", ";
1336
+                            $sql .= ($dateend ? "'" . $this->db->idate($dateend) . "'" : "NULL") . ", ";
1337
+                            $sql .= "'" . $this->db->escape($label) . "', ";
1338
+                            $sql .= "'" . $this->db->escape($jobtype) . "', ";
1339
+                            $sql .= ($class ? "'" . $this->db->escape($class) . "'" : "null") . ",";
1340
+                            $sql .= ($objectname ? "'" . $this->db->escape($objectname) . "'" : "null") . ",";
1341
+                            $sql .= ($method ? "'" . $this->db->escape($method) . "'" : "null") . ",";
1342
+                            $sql .= ($command ? "'" . $this->db->escape($command) . "'" : "null") . ",";
1343
+                            $sql .= ($parameters ? "'" . $this->db->escape($parameters) . "'" : "null") . ",";
1344
+                            $sql .= ($comment ? "'" . $this->db->escape($comment) . "'" : "null") . ",";
1345
+                            if (is_int($frequency)) {
1346
+                                $sql .= "'" . $this->db->escape($frequency) . "', ";
1347
+                            }
1348
+                            if (is_int($unitfrequency)) {
1349
+                                $sql .= "'" . $this->db->escape($unitfrequency) . "', ";
1350
+                            }
1351
+                            if (is_int($priority)) {
1352
+                                $sql .= "'" . $this->db->escape($priority) . "', ";
1353
+                            }
1354
+                            if (is_int($status)) {
1355
+                                $sql .= ((int)$status) . ", ";
1356
+                            }
1357
+                            $sql .= $entity . ",";
1358
+                            $sql .= "'" . $this->db->escape($test) . "'";
1359
+                            $sql .= ")";
1360
+
1361
+                            $resql = $this->db->query($sql);
1362
+                            if (!$resql) {
1363
+                                $err++;
1364
+                            }
1365
+                        }
1366
+
1367
+                        if (!$err) {
1368
+                            $this->db->commit();
1369
+                        } else {
1370
+                            $this->error = $this->db->lasterror();
1371
+                            $this->db->rollback();
1372
+                        }
1373
+                    }
1374
+                    // else box already registered into database
1375
+                } else {
1376
+                    $this->error = $this->db->lasterror();
1377
+                    $err++;
1378
+                }
1379
+            }
1380
+        }
1381
+
1382
+        return $err;
1383
+    }
1384
+
1385
+
1386
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps,PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1387
+
1388
+    /**
1389
+     * Adds access rights
1390
+     *
1391
+     * @param int $reinitadminperms If 1, we also grant them to all admin users
1392
+     * @param int $force_entity Force current entity
1393
+     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
1394
+     *
1395
+     * @return int                      Error count (0 if OK)
1396
+     */
1397
+    public function insert_permissions($reinitadminperms = 0, $force_entity = null, $notrigger = 0)
1398
+    {
1399
+        // phpcs:enable
1400
+        global $conf, $user;
1401
+
1402
+        global $config;
1403
+        $dbPrefix = $config->db->prefix;
1404
+
1405
+        $err = 0;
1406
+        $entity = (!empty($force_entity) ? $force_entity : $conf->entity);
1407
+
1408
+        dol_syslog(get_class($this) . "::insert_permissions", LOG_DEBUG);
1409
+
1410
+        // Test if module is activated
1411
+        $sql_del = "SELECT " . $this->db->decrypt('value') . " as value";
1412
+        $sql_del .= " FROM " . $dbPrefix . "const";
1413
+        $sql_del .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
1414
+        $sql_del .= " AND entity IN (0," . $entity . ")";
1415
+
1416
+        $resql = $this->db->query($sql_del);
1417
+        if ($resql) {
1418
+            $obj = $this->db->fetch_object($resql);
1419
+
1420
+            if ($obj !== null && !empty($obj->value) && !empty($this->rights)) {
1421
+                // If the module is active
1422
+                foreach ($this->rights as $key => $value) {
1423
+                    $r_id = $this->rights[$key][0];   // permission id in llx_rights_def (not unique because primary key is couple id-entity)
1424
+                    $r_desc = $this->rights[$key][1];
1425
+                    $r_type = isset($this->rights[$key][2]) ? $this->rights[$key][2] : '';
1426
+                    $r_def = empty($this->rights[$key][3]) ? 0 : $this->rights[$key][3];
1427
+                    $r_perms = $this->rights[$key][4];
1428
+                    $r_subperms = isset($this->rights[$key][5]) ? $this->rights[$key][5] : '';
1429
+                    $r_modul = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
1430
+
1431
+                    if (empty($r_type)) {
1432
+                        $r_type = 'w';
1433
+                    }
1434
+
1435
+                    // Search if perm already present
1436
+                    $sql = "SELECT count(*) as nb FROM " . $dbPrefix . "rights_def";
1437
+                    $sql .= " WHERE id = " . ((int)$r_id) . " AND entity = " . ((int)$entity);
1438
+
1439
+                    $resqlselect = $this->db->query($sql);
1440
+                    if ($resqlselect) {
1441
+                        $objcount = $this->db->fetch_object($resqlselect);
1442
+                        if ($objcount && $objcount->nb == 0) {
1443
+                            if (dol_strlen($r_perms)) {
1444
+                                if (dol_strlen($r_subperms)) {
1445
+                                    $sql = "INSERT INTO " . $dbPrefix . "rights_def";
1446
+                                    $sql .= " (id, entity, libelle, module, type, bydefault, perms, subperms)";
1447
+                                    $sql .= " VALUES ";
1448
+                                    $sql .= "(" . $r_id . "," . $entity . ",'" . $this->db->escape($r_desc) . "','" . $this->db->escape($r_modul) . "','" . $this->db->escape($r_type) . "'," . $r_def . ",'" . $this->db->escape($r_perms) . "','" . $this->db->escape($r_subperms) . "')";
1449
+                                } else {
1450
+                                    $sql = "INSERT INTO " . $dbPrefix . "rights_def";
1451
+                                    $sql .= " (id, entity, libelle, module, type, bydefault, perms)";
1452
+                                    $sql .= " VALUES ";
1453
+                                    $sql .= "(" . $r_id . "," . $entity . ",'" . $this->db->escape($r_desc) . "','" . $this->db->escape($r_modul) . "','" . $this->db->escape($r_type) . "'," . $r_def . ",'" . $this->db->escape($r_perms) . "')";
1454
+                                }
1455
+                            } else {
1456
+                                $sql = "INSERT INTO " . $dbPrefix . "rights_def ";
1457
+                                $sql .= " (id, entity, libelle, module, type, bydefault)";
1458
+                                $sql .= " VALUES ";
1459
+                                $sql .= "(" . $r_id . "," . $entity . ",'" . $this->db->escape($r_desc) . "','" . $this->db->escape($r_modul) . "','" . $this->db->escape($r_type) . "'," . $r_def . ")";
1460
+                            }
1461
+
1462
+                            $resqlinsert = $this->db->query($sql, 1);
1463
+
1464
+                            if (!$resqlinsert) {
1465
+                                if ($this->db->errno() != "DB_ERROR_RECORD_ALREADY_EXISTS") {
1466
+                                    $this->error = $this->db->lasterror();
1467
+                                    $err++;
1468
+                                    break;
1469
+                                } else {
1470
+                                    dol_syslog(get_class($this) . "::insert_permissions record already exists", LOG_INFO);
1471
+                                }
1472
+                            }
1473
+
1474
+                            $this->db->free($resqlinsert);
1475
+                        }
1476
+
1477
+                        $this->db->free($resqlselect);
1478
+                    }
1479
+
1480
+                    // If we want to init permissions on admin users
1481
+                    if ($reinitadminperms) {
1482
+                        $sql = "SELECT rowid FROM " . $dbPrefix . "user WHERE admin = 1";
1483
+                        dol_syslog(get_class($this) . "::insert_permissions Search all admin users", LOG_DEBUG);
1484
+
1485
+                        $resqlseladmin = $this->db->query($sql, 1);
1486
+
1487
+                        if ($resqlseladmin) {
1488
+                            $num = $this->db->num_rows($resqlseladmin);
1489
+                            $i = 0;
1490
+                            while ($i < $num) {
1491
+                                $obj2 = $this->db->fetch_object($resqlseladmin);
1492
+                                dol_syslog(get_class($this) . "::insert_permissions Add permission id " . $r_id . " to user id=" . $obj2->rowid);
1493
+
1494
+                                $tmpuser = new User($this->db);
1495
+                                $result = $tmpuser->fetch($obj2->rowid);
1496
+                                if ($result > 0) {
1497
+                                    $tmpuser->addrights($r_id, '', '', 0, 1);
1498
+                                } else {
1499
+                                    dol_syslog(get_class($this) . "::insert_permissions Failed to add the permission to user because fetch return an error", LOG_ERR);
1500
+                                }
1501
+                                $i++;
1502
+                            }
1503
+                        } else {
1504
+                            dol_print_error($this->db);
1505
+                        }
1506
+                    }
1507
+                }
1508
+
1509
+                if ($reinitadminperms && !empty($user->admin)) {  // Reload permission for current user if defined
1510
+                    // We reload permissions
1511
+                    $user->clearrights();
1512
+                    $user->getrights();
1513
+                }
1514
+            }
1515
+            $this->db->free($resql);
1516
+        } else {
1517
+            $this->error = $this->db->lasterror();
1518
+            $err++;
1519
+        }
1520
+
1521
+        return $err;
1522
+    }
1523
+
1524
+
1525
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1526
+
1527
+    /**
1528
+     * Adds menu entries
1529
+     *
1530
+     * @return int     Error count (0 if OK)
1531
+     */
1532
+    public function insert_menus()
1533
+    {
1534
+        // phpcs:enable
1535
+        global $conf, $user;
1536
+
1537
+        if (!is_array($this->menu) || empty($this->menu)) {
1538
+            return 0;
1539
+        }
1540
+
1541
+        include_once DOL_DOCUMENT_ROOT . '/core/class/menubase.class.php';
1542
+
1543
+        dol_syslog(get_class($this) . "::insert_menus", LOG_DEBUG);
1544
+
1545
+        $err = 0;
1546
+
1547
+        // Common module
1548
+        $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
1549
+
1550
+        $this->db->begin();
1551
+
1552
+        foreach ($this->menu as $key => $value) {
1553
+            $menu = new Menubase($this->db);
1554
+            $menu->menu_handler = 'all';
1555
+
1556
+            //$menu->module=strtolower($this->name);    TODO When right_class will be same than module name
1557
+            $menu->module = (empty($this->rights_class) ? strtolower($this->name) : $this->rights_class);
1558
+
1559
+            if (!$this->menu[$key]['fk_menu']) {
1560
+                $menu->fk_menu = 0;
1561
+            } else {
1562
+                $foundparent = 0;
1563
+                $fk_parent = $this->menu[$key]['fk_menu'];
1564
+                $reg = [];
1565
+                if (preg_match('/^r=/', $fk_parent)) {    // old deprecated method
1566
+                    $fk_parent = str_replace('r=', '', $fk_parent);
1567
+                    if (isset($this->menu[$fk_parent]['rowid'])) {
1568
+                        $menu->fk_menu = $this->menu[$fk_parent]['rowid'];
1569
+                        $foundparent = 1;
1570
+                    }
1571
+                } elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+),fk_leftmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
1572
+                    $menu->fk_menu = -1;
1573
+                    $menu->fk_mainmenu = $reg[1];
1574
+                    $menu->fk_leftmenu = $reg[2];
1575
+                    $foundparent = 1;
1576
+                } elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
1577
+                    $menu->fk_menu = -1;
1578
+                    $menu->fk_mainmenu = $reg[1];
1579
+                    $menu->fk_leftmenu = '';
1580
+                    $foundparent = 1;
1581
+                }
1582
+                if (!$foundparent) {
1583
+                    $this->error = "ErrorBadDefinitionOfMenuArrayInModuleDescriptor";
1584
+                    dol_syslog(get_class($this) . "::insert_menus " . $this->error . " " . $this->menu[$key]['fk_menu'], LOG_ERR);
1585
+                    $err++;
1586
+                }
1587
+            }
1588
+            $menu->type = $this->menu[$key]['type'];
1589
+            $menu->mainmenu = isset($this->menu[$key]['mainmenu']) ? $this->menu[$key]['mainmenu'] : (isset($menu->fk_mainmenu) ? $menu->fk_mainmenu : '');
1590
+            $menu->leftmenu = isset($this->menu[$key]['leftmenu']) ? $this->menu[$key]['leftmenu'] : '';
1591
+            $menu->title = $this->menu[$key]['titre'];
1592
+            $menu->prefix = isset($this->menu[$key]['prefix']) ? $this->menu[$key]['prefix'] : '';
1593
+            $menu->url = $this->menu[$key]['url'];
1594
+            $menu->langs = isset($this->menu[$key]['langs']) ? $this->menu[$key]['langs'] : '';
1595
+            $menu->position = $this->menu[$key]['position'];
1596
+            $menu->perms = $this->menu[$key]['perms'];
1597
+            $menu->target = isset($this->menu[$key]['target']) ? $this->menu[$key]['target'] : '';
1598
+            $menu->user = $this->menu[$key]['user'];
1599
+            $menu->enabled = isset($this->menu[$key]['enabled']) ? $this->menu[$key]['enabled'] : 0;
1600
+            $menu->position = $this->menu[$key]['position'];
1601
+            $menu->entity = $entity;
1602
+
1603
+            if (!$err) {
1604
+                $result = $menu->create($user); // Save menu entry into table llx_menu
1605
+                if ($result > 0) {
1606
+                    $this->menu[$key]['rowid'] = $result;
1607
+                } else {
1608
+                    $this->error = $menu->error;
1609
+                    dol_syslog(get_class($this) . '::insert_menus result=' . $result . " " . $this->error, LOG_ERR);
1610
+                    $err++;
1611
+                    break;
1612
+                }
1613
+            }
1614
+        }
1615
+
1616
+        if (!$err) {
1617
+            $this->db->commit();
1618
+        } else {
1619
+            dol_syslog(get_class($this) . "::insert_menus " . $this->error, LOG_ERR);
1620
+            $this->db->rollback();
1621
+        }
1622
+
1623
+        return $err;
1624
+    }
1625
+
1626
+
1627
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1628
+
1629
+    /**
1630
+     * Creates directories
1631
+     *
1632
+     * @return int Error count (0 if OK)
1633
+     */
1634
+    public function create_dirs()
1635
+    {
1636
+        // phpcs:enable
1637
+        global $langs, $conf;
1638
+
1639
+        $err = 0;
1640
+        $name = '';
1641
+
1642
+        if (isset($this->dirs) && is_array($this->dirs)) {
1643
+            foreach ($this->dirs as $key => $value) {
1644
+                $addtodatabase = 0;
1645
+
1646
+                if (!is_array($value)) {
1647
+                    $dir = $value; // Default simple mode
1648
+                } else {
1649
+                    $constname = $this->const_name . "_DIR_";
1650
+                    $dir = $this->dirs[$key][1];
1651
+                    $addtodatabase = empty($this->dirs[$key][2]) ? '' : $this->dirs[$key][2]; // Create constante in llx_const
1652
+                    $subname = empty($this->dirs[$key][3]) ? '' : strtoupper($this->dirs[$key][3]); // Add submodule name (ex: $conf->module->submodule->dir_output)
1653
+                    $forcename = empty($this->dirs[$key][4]) ? '' : strtoupper($this->dirs[$key][4]); // Change the module name if different
1654
+
1655
+                    if (!empty($forcename)) {
1656
+                        $constname = 'MAIN_MODULE_' . $forcename . "_DIR_";
1657
+                    }
1658
+                    if (!empty($subname)) {
1659
+                        $constname = $constname . $subname . "_";
1660
+                    }
1661
+
1662
+                    $name = $constname . strtoupper($this->dirs[$key][0]);
1663
+                }
1664
+
1665
+                // Define directory full path ($dir must start with "/")
1666
+                if (!getDolGlobalString('MAIN_MODULE_MULTICOMPANY') || $conf->entity == 1) {
1667
+                    $fulldir = DOL_DATA_ROOT . $dir;
1668
+                } else {
1669
+                    $fulldir = DOL_DATA_ROOT . "/" . $conf->entity . $dir;
1670
+                }
1671
+                // Create dir if it does not exists
1672
+                if (!empty($fulldir) && !file_exists($fulldir)) {
1673
+                    if (dol_mkdir($fulldir, DOL_DATA_ROOT) < 0) {
1674
+                        $this->error = $langs->trans("ErrorCanNotCreateDir", $fulldir);
1675
+                        dol_syslog(get_class($this) . "::_init " . $this->error, LOG_ERR);
1676
+                        $err++;
1677
+                    }
1678
+                }
1679
+
1680
+                // Define the constant in database if requested (not the default mode)
1681
+                if (!empty($addtodatabase) && !empty($name)) {
1682
+                    $result = $this->insert_dirs($name, $dir);
1683
+                    if ($result) {
1684
+                        $err++;
1685
+                    }
1686
+                }
1687
+            }
1688
+        }
1689
+
1690
+        return $err;
1691
+    }
1692
+
1693
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1694
+
1695
+    /**
1696
+     * Adds directories definitions
1697
+     *
1698
+     * @param string $name Name
1699
+     * @param string $dir Directory
1700
+     *
1701
+     * @return int             Error count (0 if OK)
1702
+     */
1703
+    public function insert_dirs($name, $dir)
1704
+    {
1705
+        // phpcs:enable
1706
+        global $conf;
1707
+
1708
+        $err = 0;
1709
+
1710
+        $sql = "SELECT count(*)";
1711
+        $sql .= " FROM " . $dbPrefix . "const";
1712
+        $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($name) . "'";
1713
+        $sql .= " AND entity = " . $conf->entity;
1714
+
1715
+        dol_syslog(get_class($this) . "::insert_dirs", LOG_DEBUG);
1716
+        $result = $this->db->query($sql);
1717
+        if ($result) {
1718
+            $row = $this->db->fetch_row($result);
1719
+
1720
+            if ($row[0] == 0) {
1721
+                $sql = "INSERT INTO " . $dbPrefix . "const (name, type, value, note, visible, entity)";
1722
+                $sql .= " VALUES (" . $this->db->encrypt($name) . ", 'chaine', " . $this->db->encrypt($dir) . ", '" . $this->db->escape("Directory for module " . $this->name) . "', '0', " . ((int)$conf->entity) . ")";
1723
+
1724
+                dol_syslog(get_class($this) . "::insert_dirs", LOG_DEBUG);
1725
+                $this->db->query($sql);
1726
+            }
1727
+        } else {
1728
+            $this->error = $this->db->lasterror();
1729
+            $err++;
1730
+        }
1731
+
1732
+        return $err;
1733
+    }
1734
+
1735
+
1736
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1737
+
1738
+    /**
1739
+     * Function called when module is disabled.
1740
+     * The remove function removes tabs, constants, boxes, permissions and menus from Dolibarr database.
1741
+     * Data directories are not deleted
1742
+     *
1743
+     * @param string $options Options when enabling module ('', 'noboxes')
1744
+     *
1745
+     * @return int                     1 if OK, 0 if KO
1746
+     */
1747
+    public function remove($options = '')
1748
+    {
1749
+        return $this->_remove([], $options);
1750
+    }
1751
+
1752
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1753
+
1754
+    /**
1755
+     * Disable function. Deletes the module constants and boxes from the database.
1756
+     *
1757
+     * @param string[] $array_sql SQL requests to be executed when module is disabled
1758
+     * @param string $options Options when disabling module:
1759
+     *
1760
+     * @return int                     1 if OK, 0 if KO
1761
+     */
1762
+    protected function _remove($array_sql, $options = '')
1763
+    {
1764
+        // phpcs:enable
1765
+        $err = 0;
1766
+
1767
+        $this->db->begin();
1768
+
1769
+        // Remove activation module line (constant MAIN_MODULE_MYMODULE in llx_const)
1770
+        if (!$err) {
1771
+            $err += $this->_unactive();
1772
+        }
1773
+
1774
+        // Remove activation of module's new tabs (MAIN_MODULE_MYMODULE_TABS_XXX in llx_const)
1775
+        if (!$err) {
1776
+            $err += $this->delete_tabs();
1777
+        }
1778
+
1779
+        // Remove activation of module's parts (MAIN_MODULE_MYMODULE_XXX in llx_const)
1780
+        if (!$err) {
1781
+            $err += $this->delete_module_parts();
1782
+        }
1783
+
1784
+        // Remove constants defined by modules
1785
+        if (!$err) {
1786
+            $err += $this->delete_const();
1787
+        }
1788
+
1789
+        // Remove list of module's available boxes (entry in llx_boxes)
1790
+        if (!$err && !preg_match('/(newboxdefonly|noboxes)/', $options)) {
1791
+            $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
1792
+        }
1793
+
1794
+        // Remove list of module's cron job entries (entry in llx_cronjobs)
1795
+        if (!$err) {
1796
+            $err += $this->delete_cronjobs();
1797
+        }
1798
+
1799
+        // Remove module's permissions from list of available permissions (entries in llx_rights_def)
1800
+        if (!$err) {
1801
+            $err += $this->delete_permissions();
1802
+        }
1803
+
1804
+        // Remove module's menus (entries in llx_menu)
1805
+        if (!$err) {
1806
+            $err += $this->delete_menus();
1807
+        }
1808
+
1809
+        // Remove module's directories
1810
+        if (!$err) {
1811
+            $err += $this->delete_dirs();
1812
+        }
1813
+
1814
+        // Run complementary sql requests
1815
+        $num = count((array)$array_sql);
1816
+        for ($i = 0; $i < $num; $i++) {
1817
+            if (!$err) {
1818
+                dol_syslog(get_class($this) . "::_remove", LOG_DEBUG);
1819
+                $result = $this->db->query($array_sql[$i]);
1820
+                if (!$result) {
1821
+                    $this->error = $this->db->error();
1822
+                    $err++;
1823
+                }
1824
+            }
1825
+        }
1826
+
1827
+        // Return code
1828
+        if (!$err) {
1829
+            $this->db->commit();
1830
+            return 1;
1831
+        } else {
1832
+            $this->db->rollback();
1833
+            return 0;
1834
+        }
1835
+    }
1836
+
1837
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1838
+
1839
+    /**
1840
+     * Module deactivation
1841
+     *
1842
+     * @return int Error count (0 if OK)
1843
+     */
1844
+    protected function _unactive()
1845
+    {
1846
+        // phpcs:enable
1847
+        global $conf;
1848
+
1849
+        $err = 0;
1850
+
1851
+        // Common module
1852
+        $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
1853
+
1854
+        $sql = "DELETE FROM " . $dbPrefix . "const";
1855
+        $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
1856
+        $sql .= " AND entity IN (0, " . $entity . ")";
1857
+
1858
+        dol_syslog(get_class($this) . "::_unactive", LOG_DEBUG);
1859
+        $this->db->query($sql);
1860
+
1861
+        return $err;
1862
+    }
1863
+
1864
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1865
+
1866
+    /**
1867
+     * Removes tabs
1868
+     *
1869
+     * @return int Error count (0 if OK)
1870
+     */
1871
+    public function delete_tabs()
1872
+    {
1873
+        // phpcs:enable
1874
+        global $conf;
1875
+
1876
+        $err = 0;
1877
+
1878
+        $sql = "DELETE FROM " . $dbPrefix . "const";
1879
+        $sql .= " WHERE " . $this->db->decrypt('name') . " like '" . $this->db->escape($this->const_name) . "_TABS_%'";
1880
+        $sql .= " AND entity = " . $conf->entity;
1881
+
1882
+        dol_syslog(get_class($this) . "::delete_tabs", LOG_DEBUG);
1883
+        if (!$this->db->query($sql)) {
1884
+            $this->error = $this->db->lasterror();
1885
+            $err++;
1886
+        }
1887
+
1888
+        return $err;
1889
+    }
1890
+
1891
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1892
+
1893
+    /**
1894
+     * Removes generic parts
1895
+     *
1896
+     * @return int Error count (0 if OK)
1897
+     */
1898
+    public function delete_module_parts()
1899
+    {
1900
+        // phpcs:enable
1901
+        global $conf;
1902
+
1903
+        $err = 0;
1904
+
1905
+        if (is_array($this->module_parts)) {
1906
+            dol_syslog(get_class($this) . "::delete_module_parts", LOG_DEBUG);
1907
+
1908
+            if (empty($this->module_parts['icon']) && !empty($this->picto) && preg_match('/^fa\-/', $this->picto)) {
1909
+                $this->module_parts['icon'] = $this->picto;
1910
+            }
1911
+
1912
+            foreach ($this->module_parts as $key => $value) {
1913
+                // If entity is defined
1914
+                if (is_array($value) && isset($value['entity'])) {
1915
+                    $entity = $value['entity'];
1916
+                } else {
1917
+                    $entity = $conf->entity;
1918
+                }
1919
+
1920
+                $sql = "DELETE FROM " . $dbPrefix . "const";
1921
+                $sql .= " WHERE " . $this->db->decrypt('name') . " LIKE '" . $this->db->escape($this->const_name) . "_" . strtoupper($key) . "'";
1922
+                $sql .= " AND entity = " . ((int)$entity);
1923
+
1924
+                if (!$this->db->query($sql)) {
1925
+                    $this->error = $this->db->lasterror();
1926
+                    $err++;
1927
+                }
1928
+            }
1929
+        }
1930
+        return $err;
1931
+    }
1932
+
1933
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1934
+
1935
+    /**
1936
+     * Removes constants tagged 'deleteonunactive'
1937
+     *
1938
+     * @return int Return integer <0 if KO, 0 if OK
1939
+     */
1940
+    public function delete_const()
1941
+    {
1942
+        // phpcs:enable
1943
+        global $conf;
1944
+
1945
+        $err = 0;
1946
+
1947
+        if (empty($this->const)) {
1948
+            return 0;
1949
+        }
1950
+
1951
+        foreach ($this->const as $key => $value) {
1952
+            $name = $this->const[$key][0];
1953
+            $deleteonunactive = (!empty($this->const[$key][6])) ? 1 : 0;
1954
+
1955
+            if ($deleteonunactive) {
1956
+                $sql = "DELETE FROM " . $dbPrefix . "const";
1957
+                $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($name) . "'";
1958
+                $sql .= " AND entity in (0, " . $conf->entity . ")";
1959
+                dol_syslog(get_class($this) . "::delete_const", LOG_DEBUG);
1960
+                if (!$this->db->query($sql)) {
1961
+                    $this->error = $this->db->lasterror();
1962
+                    $err++;
1963
+                }
1964
+            }
1965
+        }
1966
+
1967
+        return $err;
1968
+    }
1969
+
1970
+
1971
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1972
+
1973
+    /**
1974
+     * Removes boxes
1975
+     *
1976
+     * @return int Error count (0 if OK)
1977
+     */
1978
+    public function delete_boxes()
1979
+    {
1980
+        // phpcs:enable
1981
+        global $conf;
1982
+
1983
+        $err = 0;
1984
+
1985
+        if (is_array($this->boxes)) {
1986
+            foreach ($this->boxes as $key => $value) {
1987
+                //$titre = $this->boxes[$key][0];
1988
+                if (empty($this->boxes[$key]['file'])) {
1989
+                    $file = isset($this->boxes[$key][1]) ? $this->boxes[$key][1] : ''; // For backward compatibility
1990
+                } else {
1991
+                    $file = $this->boxes[$key]['file'];
1992
+                }
1993
+
1994
+                //$note  = $this->boxes[$key][2];
1995
+
1996
+                // TODO If the box is also included by another module and the other module is still on, we should not remove it.
1997
+                // For the moment, we manage this with hard coded exception
1998
+                //print "Remove box ".$file.'<br>';
1999
+                if ($file == 'box_graph_product_distribution.php') {
2000
+                    if (isModEnabled("product") || isModEnabled("service")) {
2001
+                        dol_syslog("We discard deleting module " . $file . " because another module still active requires it.");
2002
+                        continue;
2003
+                    }
2004
+                }
2005
+
2006
+                if ($this->db->type == 'sqlite3') {
2007
+                    // sqlite doesn't support "USING" syntax.
2008
+                    // TODO: remove this dependency.
2009
+                    $sql = "DELETE FROM " . $dbPrefix . "boxes ";
2010
+                    $sql .= "WHERE " . $dbPrefix . "boxes.box_id IN (";
2011
+                    $sql .= "SELECT " . $dbPrefix . "boxes_def.rowid ";
2012
+                    $sql .= "FROM " . $dbPrefix . "boxes_def ";
2013
+                    $sql .= "WHERE " . $dbPrefix . "boxes_def.file = '" . $this->db->escape($file) . "') ";
2014
+                    $sql .= "AND " . $dbPrefix . "boxes.entity = " . $conf->entity;
2015
+                } else {
2016
+                    $sql = "DELETE FROM " . $dbPrefix . "boxes";
2017
+                    $sql .= " USING " . $dbPrefix . "boxes, " . $dbPrefix . "boxes_def";
2018
+                    $sql .= " WHERE " . $dbPrefix . "boxes.box_id = " . $dbPrefix . "boxes_def.rowid";
2019
+                    $sql .= " AND " . $dbPrefix . "boxes_def.file = '" . $this->db->escape($file) . "'";
2020
+                    $sql .= " AND " . $dbPrefix . "boxes.entity = " . $conf->entity;
2021
+                }
2022
+
2023
+                dol_syslog(get_class($this) . "::delete_boxes", LOG_DEBUG);
2024
+                $resql = $this->db->query($sql);
2025
+                if (!$resql) {
2026
+                    $this->error = $this->db->lasterror();
2027
+                    $err++;
2028
+                }
2029
+
2030
+                $sql = "DELETE FROM " . $dbPrefix . "boxes_def";
2031
+                $sql .= " WHERE file = '" . $this->db->escape($file) . "'";
2032
+                $sql .= " AND entity = " . $conf->entity;     // Do not use getEntity here, we want to delete only in current company
2033
+
2034
+                dol_syslog(get_class($this) . "::delete_boxes", LOG_DEBUG);
2035
+                $resql = $this->db->query($sql);
2036
+                if (!$resql) {
2037
+                    $this->error = $this->db->lasterror();
2038
+                    $err++;
2039
+                }
2040
+            }
2041
+        }
2042
+
2043
+        return $err;
2044
+    }
2045
+
2046
+
2047
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2048
+
2049
+    /**
2050
+     * Removes boxes
2051
+     *
2052
+     * @return int Error count (0 if OK)
2053
+     */
2054
+    public function delete_cronjobs()
2055
+    {
2056
+        // phpcs:enable
2057
+        global $conf;
2058
+
2059
+        $err = 0;
2060
+
2061
+        if (is_array($this->cronjobs)) {
2062
+            $sql = "DELETE FROM " . $dbPrefix . "cronjob";
2063
+            $sql .= " WHERE module_name = '" . $this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class) . "'";
2064
+            $sql .= " AND entity = " . $conf->entity;
2065
+            $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.
2066
+            // 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.
2067
+
2068
+            dol_syslog(get_class($this) . "::delete_cronjobs", LOG_DEBUG);
2069
+            $resql = $this->db->query($sql);
2070
+            if (!$resql) {
2071
+                $this->error = $this->db->lasterror();
2072
+                $err++;
2073
+            }
2074
+        }
2075
+
2076
+        return $err;
2077
+    }
2078
+
2079
+
2080
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2081
+
2082
+    /**
2083
+     * Removes access rights
2084
+     *
2085
+     * @return int                     Error count (0 if OK)
2086
+     */
2087
+    public function delete_permissions()
2088
+    {
2089
+        // phpcs:enable
2090
+        global $conf;
2091
+
2092
+        $err = 0;
2093
+
2094
+        $sql = "DELETE FROM " . $dbPrefix . "rights_def";
2095
+        $sql .= " WHERE module = '" . $this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class) . "'";
2096
+        $sql .= " AND entity = " . $conf->entity;
2097
+        dol_syslog(get_class($this) . "::delete_permissions", LOG_DEBUG);
2098
+        if (!$this->db->query($sql)) {
2099
+            $this->error = $this->db->lasterror();
2100
+            $err++;
2101
+        }
2102
+
2103
+        return $err;
2104
+    }
2105
+
2106
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2107
+
2108
+    /**
2109
+     * Removes menu entries
2110
+     *
2111
+     * @return int Error count (0 if OK)
2112
+     */
2113
+    public function delete_menus()
2114
+    {
2115
+        // phpcs:enable
2116
+        global $conf;
2117
+
2118
+        $err = 0;
2119
+
2120
+        //$module=strtolower($this->name);        TODO When right_class will be same than module name
2121
+        $module = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
2122
+
2123
+        $sql = "DELETE FROM " . $dbPrefix . "menu";
2124
+        $sql .= " WHERE module = '" . $this->db->escape($module) . "'";
2125
+        $sql .= " AND entity IN (0, " . $conf->entity . ")";
2126
+
2127
+        dol_syslog(get_class($this) . "::delete_menus", LOG_DEBUG);
2128
+        $resql = $this->db->query($sql);
2129
+        if (!$resql) {
2130
+            $this->error = $this->db->lasterror();
2131
+            $err++;
2132
+        }
2133
+
2134
+        return $err;
2135
+    }
2136
+
2137
+
2138
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2139
+
2140
+    /**
2141
+     * Removes directories
2142
+     *
2143
+     * @return int Error count (0 if OK)
2144
+     */
2145
+    public function delete_dirs()
2146
+    {
2147
+        // phpcs:enable
2148
+        global $conf;
2149
+
2150
+        $err = 0;
2151
+
2152
+        $sql = "DELETE FROM " . $dbPrefix . "const";
2153
+        $sql .= " WHERE " . $this->db->decrypt('name') . " LIKE '" . $this->db->escape($this->const_name) . "_DIR_%'";
2154
+        $sql .= " AND entity = " . $conf->entity;
2155
+
2156
+        dol_syslog(get_class($this) . "::delete_dirs", LOG_DEBUG);
2157
+        if (!$this->db->query($sql)) {
2158
+            $this->error = $this->db->lasterror();
2159
+            $err++;
2160
+        }
2161
+
2162
+        return $err;
2163
+    }
2164
+
2165
+
2166
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2167
+
2168
+    /**
2169
+     * Return Kanban view of a module
2170
+     *
2171
+     * @param string $codeenabledisable HTML code for button to enable/disable module
2172
+     * @param string $codetoconfig HTML code to go to config page
2173
+     *
2174
+     * @return  string                          HTML code of Kanban view
2175
+     */
2176
+    public function getKanbanView($codeenabledisable = '', $codetoconfig = '')
2177
+    {
2178
+        global $conf, $langs;
2179
+
2180
+        // Define imginfo
2181
+        $imginfo = "info";
2182
+        if ($this->isCoreOrExternalModule() == 'external') {
2183
+            $imginfo = "info_black";
2184
+        }
2185
+
2186
+        $const_name = 'MAIN_MODULE_' . strtoupper(preg_replace('/^mod/i', '', get_class($this)));
2187
+
2188
+        $version = $this->getVersion(0);
2189
+        $versiontrans = '';
2190
+        if (preg_match('/development/i', $version)) {
2191
+            $versiontrans .= 'warning';
2192
+        }
2193
+        if (preg_match('/experimental/i', $version)) {
2194
+            $versiontrans .= 'warning';
2195
+        }
2196
+        if (preg_match('/deprecated/i', $version)) {
2197
+            $versiontrans .= 'warning';
2198
+        }
2199
+
2200
+        $return = '
2201 2201
     	<div class="box-flex-item info-box-module'
2202
-			. (getDolGlobalString($const_name) ? '' : ' --disabled')
2203
-			. ($this->isCoreOrExternalModule() == 'external' ? ' --external' : '')
2204
-			. ($this->needUpdate ? ' --need-update' : '')
2205
-			. '">
2202
+            . (getDolGlobalString($const_name) ? '' : ' --disabled')
2203
+            . ($this->isCoreOrExternalModule() == 'external' ? ' --external' : '')
2204
+            . ($this->needUpdate ? ' --need-update' : '')
2205
+            . '">
2206 2206
 	    <div class="info-box info-box-sm info-box-module">
2207 2207
 	    <div class="info-box-icon' . (!getDolGlobalString($const_name) ? '' : ' info-box-icon-module-enabled' . ($versiontrans ? ' info-box-icon-module-warning' : '')) . '">';
2208 2208
 
2209
-		$alttext = '';
2210
-		//if (is_array($objMod->need_dolibarr_version)) $alttext.=($alttext?' - ':'').'Dolibarr >= '.join('.',$objMod->need_dolibarr_version);
2211
-		//if (is_array($objMod->phpmin)) $alttext.=($alttext?' - ':'').'PHP >= '.join('.',$objMod->phpmin);
2212
-		if (!empty($this->picto)) {
2213
-			if (preg_match('/^\//i', $this->picto)) {
2214
-				$return .= img_picto($alttext, $this->picto, 'class="inline-block valignmiddle"', 1);
2215
-			} else {
2216
-				$return .= img_object($alttext, $this->picto, 'class="inline-block valignmiddle"');
2217
-			}
2218
-		} else {
2219
-			$return .= img_object($alttext, 'generic', 'class="inline-block valignmiddle"');
2220
-		}
2221
-
2222
-		if ($this->isCoreOrExternalModule() == 'external' || preg_match('/development|experimental|deprecated/i', $version)) {
2223
-			$versionTitle = $langs->trans("Version") . ' ' . $this->getVersion(1);
2224
-			if ($this->needUpdate) {
2225
-				$versionTitle .= '<br>' . $langs->trans('ModuleUpdateAvailable') . ' : ' . $this->lastVersion;
2226
-			}
2227
-
2228
-			$return .= '<span class="info-box-icon-version' . ($versiontrans ? ' ' . $versiontrans : '') . ' classfortooltip" title="' . dol_escape_js($versionTitle) . '" >';
2229
-			$return .= $this->getVersion(1);
2230
-			$return .= '</span>';
2231
-		}
2232
-
2233
-		$return .= '</div>
2209
+        $alttext = '';
2210
+        //if (is_array($objMod->need_dolibarr_version)) $alttext.=($alttext?' - ':'').'Dolibarr >= '.join('.',$objMod->need_dolibarr_version);
2211
+        //if (is_array($objMod->phpmin)) $alttext.=($alttext?' - ':'').'PHP >= '.join('.',$objMod->phpmin);
2212
+        if (!empty($this->picto)) {
2213
+            if (preg_match('/^\//i', $this->picto)) {
2214
+                $return .= img_picto($alttext, $this->picto, 'class="inline-block valignmiddle"', 1);
2215
+            } else {
2216
+                $return .= img_object($alttext, $this->picto, 'class="inline-block valignmiddle"');
2217
+            }
2218
+        } else {
2219
+            $return .= img_object($alttext, 'generic', 'class="inline-block valignmiddle"');
2220
+        }
2221
+
2222
+        if ($this->isCoreOrExternalModule() == 'external' || preg_match('/development|experimental|deprecated/i', $version)) {
2223
+            $versionTitle = $langs->trans("Version") . ' ' . $this->getVersion(1);
2224
+            if ($this->needUpdate) {
2225
+                $versionTitle .= '<br>' . $langs->trans('ModuleUpdateAvailable') . ' : ' . $this->lastVersion;
2226
+            }
2227
+
2228
+            $return .= '<span class="info-box-icon-version' . ($versiontrans ? ' ' . $versiontrans : '') . ' classfortooltip" title="' . dol_escape_js($versionTitle) . '" >';
2229
+            $return .= $this->getVersion(1);
2230
+            $return .= '</span>';
2231
+        }
2232
+
2233
+        $return .= '</div>
2234 2234
 	    <div class="info-box-content info-box-text-module' . (!getDolGlobalString($const_name) ? '' : ' info-box-module-enabled' . ($versiontrans ? ' info-box-content-warning' : '')) . '">
2235 2235
 	    <span class="info-box-title">' . $this->getName() . '</span>
2236 2236
 	    <span class="info-box-desc twolinesmax opacitymedium" title="' . dol_escape_htmltag($this->getDesc()) . '">' . nl2br($this->getDesc()) . '</span>';
2237 2237
 
2238
-		$return .= '<div class="valignmiddle inline-block info-box-more">';
2239
-		//if ($versiontrans) print img_warning($langs->trans("Version").' '.$this->getVersion(1)).' ';
2240
-		$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>';
2241
-		$return .= '</div><br>';
2242
-
2243
-		$return .= '<div class="valignmiddle inline-block info-box-actions">';
2244
-		$return .= '<div class="valignmiddle inline-block info-box-setup">';
2245
-		$return .= $codetoconfig;
2246
-		$return .= '</div>';
2247
-		$return .= '<div class="valignmiddle inline-block marginleftonly marginrightonly">';
2248
-		$return .= $codeenabledisable;
2249
-		$return .= '</div>';
2250
-		$return .= '</div>';
2251
-
2252
-		$return .= '
2238
+        $return .= '<div class="valignmiddle inline-block info-box-more">';
2239
+        //if ($versiontrans) print img_warning($langs->trans("Version").' '.$this->getVersion(1)).' ';
2240
+        $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>';
2241
+        $return .= '</div><br>';
2242
+
2243
+        $return .= '<div class="valignmiddle inline-block info-box-actions">';
2244
+        $return .= '<div class="valignmiddle inline-block info-box-setup">';
2245
+        $return .= $codetoconfig;
2246
+        $return .= '</div>';
2247
+        $return .= '<div class="valignmiddle inline-block marginleftonly marginrightonly">';
2248
+        $return .= $codeenabledisable;
2249
+        $return .= '</div>';
2250
+        $return .= '</div>';
2251
+
2252
+        $return .= '
2253 2253
 	    </div><!-- /.info-box-content -->
2254 2254
 	    </div><!-- /.info-box -->
2255 2255
 	    </div>';
2256 2256
 
2257
-		return $return;
2258
-	}
2259
-
2260
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2261
-
2262
-	/**
2263
-	 * Tells if module is core or external.
2264
-	 * 'dolibarr' and 'dolibarr_deprecated' is core
2265
-	 * 'experimental' and 'development' is core
2266
-	 *
2267
-	 * @return string  'core', 'external' or 'unknown'
2268
-	 */
2269
-	public function isCoreOrExternalModule()
2270
-	{
2271
-		if ($this->version == 'dolibarr' || $this->version == 'dolibarr_deprecated') {
2272
-			return 'core';
2273
-		}
2274
-		if (!empty($this->version) && !in_array($this->version, ['experimental', 'development'])) {
2275
-			return 'external';
2276
-		}
2277
-		if (!empty($this->editor_name) || !empty($this->editor_url)) {
2278
-			return 'external';
2279
-		}
2280
-		if ($this->numero >= 100000) {
2281
-			return 'external';
2282
-		}
2283
-		return 'unknown';
2284
-	}
2285
-
2286
-	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2287
-
2288
-	/**
2289
-	 * Gives module version (translated if param $translated is on)
2290
-	 * For 'experimental' modules, gives 'experimental' translation
2291
-	 * For 'dolibarr' modules, gives Dolibarr version
2292
-	 *
2293
-	 * @param int $translated 1=Special version keys are translated, 0=Special version keys are not translated
2294
-	 *
2295
-	 * @return string                       Module version
2296
-	 */
2297
-	public function getVersion($translated = 1)
2298
-	{
2299
-		global $langs;
2300
-		$langs->load("admin");
2301
-
2302
-		$ret = '';
2303
-
2304
-		$newversion = preg_replace('/_deprecated/', '', $this->version);
2305
-		if ($newversion == 'experimental') {
2306
-			$ret = ($translated ? $langs->transnoentitiesnoconv("VersionExperimental") : $newversion);
2307
-		} elseif ($newversion == 'development') {
2308
-			$ret = ($translated ? $langs->transnoentitiesnoconv("VersionDevelopment") : $newversion);
2309
-		} elseif ($newversion == 'dolibarr') {
2310
-			$ret = DOL_VERSION;
2311
-		} elseif ($newversion) {
2312
-			$ret = $newversion;
2313
-		} else {
2314
-			$ret = ($translated ? $langs->transnoentitiesnoconv("VersionUnknown") : 'unknown');
2315
-		}
2316
-
2317
-		if (preg_match('/_deprecated/', $this->version)) {
2318
-			$ret .= ($translated ? ' (' . $langs->transnoentitiesnoconv("Deprecated") . ')' : $this->version);
2319
-		}
2320
-		return $ret;
2321
-	}
2322
-
2323
-	/**
2324
-	 * Gives the translated module name if translation exists in admin.lang or into language files of module.
2325
-	 * Otherwise return the module key name.
2326
-	 *
2327
-	 * @return string  Translated module name
2328
-	 */
2329
-	public function getName()
2330
-	{
2331
-		global $langs;
2332
-		$langs->load("admin");
2333
-
2334
-		if ($langs->transnoentitiesnoconv("Module" . $this->numero . "Name") != "Module" . $this->numero . "Name") {
2335
-			// If module name translation exists
2336
-			return $langs->transnoentitiesnoconv("Module" . $this->numero . "Name");
2337
-		} else {
2338
-			// If module name translation using it's unique id does not exist, we try to use its name to find translation
2339
-			if (is_array($this->langfiles)) {
2340
-				foreach ($this->langfiles as $val) {
2341
-					if ($val) {
2342
-						$langs->load($val);
2343
-					}
2344
-				}
2345
-			}
2346
-
2347
-			if ($langs->trans("Module" . $this->name . "Name") != "Module" . $this->name . "Name") {
2348
-				// If module name translation exists
2349
-				return $langs->transnoentitiesnoconv("Module" . $this->name . "Name");
2350
-			}
2351
-
2352
-			// Last chance with simple label
2353
-			return $langs->transnoentitiesnoconv($this->name);
2354
-		}
2355
-	}
2356
-
2357
-	/**
2358
-	 * Gives the translated module description if translation exists in admin.lang or the default module description
2359
-	 *
2360
-	 * @return string  Translated module description
2361
-	 */
2362
-	public function getDesc()
2363
-	{
2364
-		global $langs;
2365
-		$langs->load("admin");
2366
-
2367
-		if ($langs->transnoentitiesnoconv("Module" . $this->numero . "Desc") != "Module" . $this->numero . "Desc") {
2368
-			// If module description translation exists
2369
-			return $langs->transnoentitiesnoconv("Module" . $this->numero . "Desc");
2370
-		} else {
2371
-			// If module description translation does not exist using its unique id, we can use its name to find translation
2372
-			if (is_array($this->langfiles)) {
2373
-				foreach ($this->langfiles as $val) {
2374
-					if ($val) {
2375
-						$langs->load($val);
2376
-					}
2377
-				}
2378
-			}
2379
-
2380
-			if ($langs->transnoentitiesnoconv("Module" . $this->name . "Desc") != "Module" . $this->name . "Desc") {
2381
-				// If module name translation exists
2382
-				return $langs->trans("Module" . $this->name . "Desc");
2383
-			}
2384
-
2385
-			// Last chance with simple label
2386
-			return $langs->trans($this->description);
2387
-		}
2388
-	}
2389
-
2390
-	/**
2391
-	 * Check for module update
2392
-	 * TODO : store results for $this->url_last_version and $this->needUpdate
2393
-	 * Add a cron task to monitor for updates
2394
-	 *
2395
-	 * @return int Return integer <0 if Error, 0 == no update needed,  >0 if need update
2396
-	 */
2397
-	public function checkForUpdate()
2398
-	{
2399
-		require_once BASE_PATH . '/../Dolibarr/Lib/GetUrl.php';
2400
-		if (!empty($this->url_last_version)) {
2401
-			$lastVersion = getURLContent($this->url_last_version, 'GET', '', 1, [], ['http', 'https'], 0);    // Accept http or https links on external remote server only
2402
-			if (isset($lastVersion['content']) && strlen($lastVersion['content']) < 30) {
2403
-				// 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 _ . -
2404
-				$this->lastVersion = preg_replace("/[^a-zA-Z0-9_\.\-]+/", "", $lastVersion['content']);
2405
-				if (version_compare($this->lastVersion, $this->version) > 0) {
2406
-					$this->needUpdate = true;
2407
-					return 1;
2408
-				} else {
2409
-					$this->needUpdate = false;
2410
-					return 0;
2411
-				}
2412
-			} else {
2413
-				return -1;
2414
-			}
2415
-		}
2416
-		return 0;
2417
-	}
2418
-
2419
-	/**
2420
-	 * Create tables and keys required by module:
2421
-	 * - Files table.sql or table-module.sql with create table instructions
2422
-	 * - Then table.key.sql or table-module.key.sql with create keys instructions
2423
-	 * - Then data_xxx.sql (usually provided by external modules only)
2424
-	 * - Then update_xxx.sql (usually provided by external modules only)
2425
-	 * Files must be stored in subdirectory 'tables' or 'data' into directory $reldir (Example: '/install/mysql/' or
2426
-	 * '/module/sql/') This function may also be called by :
2427
-	 * - _load_tables('/install/mysql/', 'modulename') into the this->init() of core module descriptors.
2428
-	 * - _load_tables('/mymodule/sql/') into the this->init() of external module descriptors.
2429
-	 *
2430
-	 * @param string $reldir Relative directory where to scan files. Example: '/install/mysql/' or
2431
-	 *                               '/module/sql/'
2432
-	 * @param string $onlywithsuffix Only with the defined suffix
2433
-	 *
2434
-	 * @return  int                         Return integer <=0 if KO, >0 if OK
2435
-	 */
2436
-	protected function _load_tables($reldir, $onlywithsuffix = '')
2437
-	{
2438
-		// phpcs:enable
2439
-		global $conf;
2440
-
2441
-		$error = 0;
2442
-		$dirfound = 0;
2443
-
2444
-		if (empty($reldir)) {
2445
-			return 1;
2446
-		}
2447
-
2448
-		include_once BASE_PATH . '/../Dolibarr/Lib/Admin.php';
2449
-
2450
-		$ok = 1;
2451
-		foreach ($conf->file->dol_document_root as $dirroot) {
2452
-			if ($ok) {
2453
-				$dirsql = $dirroot . $reldir;
2454
-				$ok = 0;
2455
-
2456
-				// We will loop on xxx/, xxx/tables/, xxx/data/
2457
-				$listofsubdir = ['', 'tables/', 'data/'];
2458
-				if ($this->db->type == 'pgsql') {
2459
-					$listofsubdir[] = '../pgsql/functions/';
2460
-				}
2461
-
2462
-				foreach ($listofsubdir as $subdir) {
2463
-					$dir = $dirsql . $subdir;
2464
-
2465
-					$handle = @opendir($dir); // Dir may not exists
2466
-					if (is_resource($handle)) {
2467
-						$dirfound++;
2468
-
2469
-						// Run llx_mytable.sql files, then llx_mytable_*.sql
2470
-						$files = [];
2471
-						while (($file = readdir($handle)) !== false) {
2472
-							$files[] = $file;
2473
-						}
2474
-						sort($files);
2475
-						foreach ($files as $file) {
2476
-							if ($onlywithsuffix) {
2477
-								if (!preg_match('/\-' . preg_quote($onlywithsuffix, '/') . '\./i', $file)) {
2478
-									//print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
2479
-									continue;
2480
-								} else {
2481
-									//print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
2482
-								}
2483
-							}
2484
-							if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'llx_') {
2485
-								$result = run_sql($dir . $file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
2486
-								if ($result <= 0) {
2487
-									$error++;
2488
-								}
2489
-							}
2490
-						}
2491
-
2492
-						rewinddir($handle);
2493
-
2494
-						// Run llx_mytable.key.sql files (Must be done after llx_mytable.sql) then then llx_mytable_*.key.sql
2495
-						$files = [];
2496
-						while (($file = readdir($handle)) !== false) {
2497
-							$files[] = $file;
2498
-						}
2499
-						sort($files);
2500
-						foreach ($files as $file) {
2501
-							if ($onlywithsuffix) {
2502
-								if (!preg_match('/\-' . preg_quote($onlywithsuffix, '/') . '\./i', $file)) {
2503
-									//print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
2504
-									continue;
2505
-								} else {
2506
-									//print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
2507
-								}
2508
-							}
2509
-							if (preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'llx_') {
2510
-								$result = run_sql($dir . $file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
2511
-								if ($result <= 0) {
2512
-									$error++;
2513
-								}
2514
-							}
2515
-						}
2516
-
2517
-						rewinddir($handle);
2518
-
2519
-						// Run functions-xxx.sql files (Must be done after llx_mytable.key.sql)
2520
-						$files = [];
2521
-						while (($file = readdir($handle)) !== false) {
2522
-							$files[] = $file;
2523
-						}
2524
-						sort($files);
2525
-						foreach ($files as $file) {
2526
-							if ($onlywithsuffix) {
2527
-								if (!preg_match('/\-' . preg_quote($onlywithsuffix, '/') . '\./i', $file)) {
2528
-									//print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
2529
-									continue;
2530
-								} else {
2531
-									//print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
2532
-								}
2533
-							}
2534
-							if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 9) == 'functions') {
2535
-								$result = run_sql($dir . $file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
2536
-								if ($result <= 0) {
2537
-									$error++;
2538
-								}
2539
-							}
2540
-						}
2541
-
2542
-						rewinddir($handle);
2543
-
2544
-						// Run data_xxx.sql files (Must be done after llx_mytable.key.sql)
2545
-						$files = [];
2546
-						while (($file = readdir($handle)) !== false) {
2547
-							$files[] = $file;
2548
-						}
2549
-						sort($files);
2550
-						foreach ($files as $file) {
2551
-							if ($onlywithsuffix) {
2552
-								if (!preg_match('/\-' . preg_quote($onlywithsuffix, '/') . '\./i', $file)) {
2553
-									//print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
2554
-									continue;
2555
-								} else {
2556
-									//print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
2557
-								}
2558
-							}
2559
-							if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'data') {
2560
-								$result = run_sql($dir . $file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
2561
-								if ($result <= 0) {
2562
-									$error++;
2563
-								}
2564
-							}
2565
-						}
2566
-
2567
-						rewinddir($handle);
2568
-
2569
-						// Run update_xxx.sql files
2570
-						$files = [];
2571
-						while (($file = readdir($handle)) !== false) {
2572
-							$files[] = $file;
2573
-						}
2574
-						sort($files);
2575
-						foreach ($files as $file) {
2576
-							if ($onlywithsuffix) {
2577
-								if (!preg_match('/\-' . preg_quote($onlywithsuffix, '/') . '\./i', $file)) {
2578
-									//print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
2579
-									continue;
2580
-								} else {
2581
-									//print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
2582
-								}
2583
-							}
2584
-							if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 6) == 'update') {
2585
-								$result = run_sql($dir . $file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
2586
-								if ($result <= 0) {
2587
-									$error++;
2588
-								}
2589
-							}
2590
-						}
2591
-
2592
-						closedir($handle);
2593
-					}
2594
-				}
2595
-
2596
-				if ($error == 0) {
2597
-					$ok = 1;
2598
-				}
2599
-			}
2600
-		}
2601
-
2602
-		if (!$dirfound) {
2603
-			dol_syslog("A module ask to load sql files into " . $reldir . " but this directory was not found.", LOG_WARNING);
2604
-		}
2605
-		return $ok;
2606
-	}
2257
+        return $return;
2258
+    }
2259
+
2260
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2261
+
2262
+    /**
2263
+     * Tells if module is core or external.
2264
+     * 'dolibarr' and 'dolibarr_deprecated' is core
2265
+     * 'experimental' and 'development' is core
2266
+     *
2267
+     * @return string  'core', 'external' or 'unknown'
2268
+     */
2269
+    public function isCoreOrExternalModule()
2270
+    {
2271
+        if ($this->version == 'dolibarr' || $this->version == 'dolibarr_deprecated') {
2272
+            return 'core';
2273
+        }
2274
+        if (!empty($this->version) && !in_array($this->version, ['experimental', 'development'])) {
2275
+            return 'external';
2276
+        }
2277
+        if (!empty($this->editor_name) || !empty($this->editor_url)) {
2278
+            return 'external';
2279
+        }
2280
+        if ($this->numero >= 100000) {
2281
+            return 'external';
2282
+        }
2283
+        return 'unknown';
2284
+    }
2285
+
2286
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2287
+
2288
+    /**
2289
+     * Gives module version (translated if param $translated is on)
2290
+     * For 'experimental' modules, gives 'experimental' translation
2291
+     * For 'dolibarr' modules, gives Dolibarr version
2292
+     *
2293
+     * @param int $translated 1=Special version keys are translated, 0=Special version keys are not translated
2294
+     *
2295
+     * @return string                       Module version
2296
+     */
2297
+    public function getVersion($translated = 1)
2298
+    {
2299
+        global $langs;
2300
+        $langs->load("admin");
2301
+
2302
+        $ret = '';
2303
+
2304
+        $newversion = preg_replace('/_deprecated/', '', $this->version);
2305
+        if ($newversion == 'experimental') {
2306
+            $ret = ($translated ? $langs->transnoentitiesnoconv("VersionExperimental") : $newversion);
2307
+        } elseif ($newversion == 'development') {
2308
+            $ret = ($translated ? $langs->transnoentitiesnoconv("VersionDevelopment") : $newversion);
2309
+        } elseif ($newversion == 'dolibarr') {
2310
+            $ret = DOL_VERSION;
2311
+        } elseif ($newversion) {
2312
+            $ret = $newversion;
2313
+        } else {
2314
+            $ret = ($translated ? $langs->transnoentitiesnoconv("VersionUnknown") : 'unknown');
2315
+        }
2316
+
2317
+        if (preg_match('/_deprecated/', $this->version)) {
2318
+            $ret .= ($translated ? ' (' . $langs->transnoentitiesnoconv("Deprecated") . ')' : $this->version);
2319
+        }
2320
+        return $ret;
2321
+    }
2322
+
2323
+    /**
2324
+     * Gives the translated module name if translation exists in admin.lang or into language files of module.
2325
+     * Otherwise return the module key name.
2326
+     *
2327
+     * @return string  Translated module name
2328
+     */
2329
+    public function getName()
2330
+    {
2331
+        global $langs;
2332
+        $langs->load("admin");
2333
+
2334
+        if ($langs->transnoentitiesnoconv("Module" . $this->numero . "Name") != "Module" . $this->numero . "Name") {
2335
+            // If module name translation exists
2336
+            return $langs->transnoentitiesnoconv("Module" . $this->numero . "Name");
2337
+        } else {
2338
+            // If module name translation using it's unique id does not exist, we try to use its name to find translation
2339
+            if (is_array($this->langfiles)) {
2340
+                foreach ($this->langfiles as $val) {
2341
+                    if ($val) {
2342
+                        $langs->load($val);
2343
+                    }
2344
+                }
2345
+            }
2346
+
2347
+            if ($langs->trans("Module" . $this->name . "Name") != "Module" . $this->name . "Name") {
2348
+                // If module name translation exists
2349
+                return $langs->transnoentitiesnoconv("Module" . $this->name . "Name");
2350
+            }
2351
+
2352
+            // Last chance with simple label
2353
+            return $langs->transnoentitiesnoconv($this->name);
2354
+        }
2355
+    }
2356
+
2357
+    /**
2358
+     * Gives the translated module description if translation exists in admin.lang or the default module description
2359
+     *
2360
+     * @return string  Translated module description
2361
+     */
2362
+    public function getDesc()
2363
+    {
2364
+        global $langs;
2365
+        $langs->load("admin");
2366
+
2367
+        if ($langs->transnoentitiesnoconv("Module" . $this->numero . "Desc") != "Module" . $this->numero . "Desc") {
2368
+            // If module description translation exists
2369
+            return $langs->transnoentitiesnoconv("Module" . $this->numero . "Desc");
2370
+        } else {
2371
+            // If module description translation does not exist using its unique id, we can use its name to find translation
2372
+            if (is_array($this->langfiles)) {
2373
+                foreach ($this->langfiles as $val) {
2374
+                    if ($val) {
2375
+                        $langs->load($val);
2376
+                    }
2377
+                }
2378
+            }
2379
+
2380
+            if ($langs->transnoentitiesnoconv("Module" . $this->name . "Desc") != "Module" . $this->name . "Desc") {
2381
+                // If module name translation exists
2382
+                return $langs->trans("Module" . $this->name . "Desc");
2383
+            }
2384
+
2385
+            // Last chance with simple label
2386
+            return $langs->trans($this->description);
2387
+        }
2388
+    }
2389
+
2390
+    /**
2391
+     * Check for module update
2392
+     * TODO : store results for $this->url_last_version and $this->needUpdate
2393
+     * Add a cron task to monitor for updates
2394
+     *
2395
+     * @return int Return integer <0 if Error, 0 == no update needed,  >0 if need update
2396
+     */
2397
+    public function checkForUpdate()
2398
+    {
2399
+        require_once BASE_PATH . '/../Dolibarr/Lib/GetUrl.php';
2400
+        if (!empty($this->url_last_version)) {
2401
+            $lastVersion = getURLContent($this->url_last_version, 'GET', '', 1, [], ['http', 'https'], 0);    // Accept http or https links on external remote server only
2402
+            if (isset($lastVersion['content']) && strlen($lastVersion['content']) < 30) {
2403
+                // 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 _ . -
2404
+                $this->lastVersion = preg_replace("/[^a-zA-Z0-9_\.\-]+/", "", $lastVersion['content']);
2405
+                if (version_compare($this->lastVersion, $this->version) > 0) {
2406
+                    $this->needUpdate = true;
2407
+                    return 1;
2408
+                } else {
2409
+                    $this->needUpdate = false;
2410
+                    return 0;
2411
+                }
2412
+            } else {
2413
+                return -1;
2414
+            }
2415
+        }
2416
+        return 0;
2417
+    }
2418
+
2419
+    /**
2420
+     * Create tables and keys required by module:
2421
+     * - Files table.sql or table-module.sql with create table instructions
2422
+     * - Then table.key.sql or table-module.key.sql with create keys instructions
2423
+     * - Then data_xxx.sql (usually provided by external modules only)
2424
+     * - Then update_xxx.sql (usually provided by external modules only)
2425
+     * Files must be stored in subdirectory 'tables' or 'data' into directory $reldir (Example: '/install/mysql/' or
2426
+     * '/module/sql/') This function may also be called by :
2427
+     * - _load_tables('/install/mysql/', 'modulename') into the this->init() of core module descriptors.
2428
+     * - _load_tables('/mymodule/sql/') into the this->init() of external module descriptors.
2429
+     *
2430
+     * @param string $reldir Relative directory where to scan files. Example: '/install/mysql/' or
2431
+     *                               '/module/sql/'
2432
+     * @param string $onlywithsuffix Only with the defined suffix
2433
+     *
2434
+     * @return  int                         Return integer <=0 if KO, >0 if OK
2435
+     */
2436
+    protected function _load_tables($reldir, $onlywithsuffix = '')
2437
+    {
2438
+        // phpcs:enable
2439
+        global $conf;
2440
+
2441
+        $error = 0;
2442
+        $dirfound = 0;
2443
+
2444
+        if (empty($reldir)) {
2445
+            return 1;
2446
+        }
2447
+
2448
+        include_once BASE_PATH . '/../Dolibarr/Lib/Admin.php';
2449
+
2450
+        $ok = 1;
2451
+        foreach ($conf->file->dol_document_root as $dirroot) {
2452
+            if ($ok) {
2453
+                $dirsql = $dirroot . $reldir;
2454
+                $ok = 0;
2455
+
2456
+                // We will loop on xxx/, xxx/tables/, xxx/data/
2457
+                $listofsubdir = ['', 'tables/', 'data/'];
2458
+                if ($this->db->type == 'pgsql') {
2459
+                    $listofsubdir[] = '../pgsql/functions/';
2460
+                }
2461
+
2462
+                foreach ($listofsubdir as $subdir) {
2463
+                    $dir = $dirsql . $subdir;
2464
+
2465
+                    $handle = @opendir($dir); // Dir may not exists
2466
+                    if (is_resource($handle)) {
2467
+                        $dirfound++;
2468
+
2469
+                        // Run llx_mytable.sql files, then llx_mytable_*.sql
2470
+                        $files = [];
2471
+                        while (($file = readdir($handle)) !== false) {
2472
+                            $files[] = $file;
2473
+                        }
2474
+                        sort($files);
2475
+                        foreach ($files as $file) {
2476
+                            if ($onlywithsuffix) {
2477
+                                if (!preg_match('/\-' . preg_quote($onlywithsuffix, '/') . '\./i', $file)) {
2478
+                                    //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
2479
+                                    continue;
2480
+                                } else {
2481
+                                    //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
2482
+                                }
2483
+                            }
2484
+                            if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'llx_') {
2485
+                                $result = run_sql($dir . $file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
2486
+                                if ($result <= 0) {
2487
+                                    $error++;
2488
+                                }
2489
+                            }
2490
+                        }
2491
+
2492
+                        rewinddir($handle);
2493
+
2494
+                        // Run llx_mytable.key.sql files (Must be done after llx_mytable.sql) then then llx_mytable_*.key.sql
2495
+                        $files = [];
2496
+                        while (($file = readdir($handle)) !== false) {
2497
+                            $files[] = $file;
2498
+                        }
2499
+                        sort($files);
2500
+                        foreach ($files as $file) {
2501
+                            if ($onlywithsuffix) {
2502
+                                if (!preg_match('/\-' . preg_quote($onlywithsuffix, '/') . '\./i', $file)) {
2503
+                                    //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
2504
+                                    continue;
2505
+                                } else {
2506
+                                    //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
2507
+                                }
2508
+                            }
2509
+                            if (preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'llx_') {
2510
+                                $result = run_sql($dir . $file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
2511
+                                if ($result <= 0) {
2512
+                                    $error++;
2513
+                                }
2514
+                            }
2515
+                        }
2516
+
2517
+                        rewinddir($handle);
2518
+
2519
+                        // Run functions-xxx.sql files (Must be done after llx_mytable.key.sql)
2520
+                        $files = [];
2521
+                        while (($file = readdir($handle)) !== false) {
2522
+                            $files[] = $file;
2523
+                        }
2524
+                        sort($files);
2525
+                        foreach ($files as $file) {
2526
+                            if ($onlywithsuffix) {
2527
+                                if (!preg_match('/\-' . preg_quote($onlywithsuffix, '/') . '\./i', $file)) {
2528
+                                    //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
2529
+                                    continue;
2530
+                                } else {
2531
+                                    //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
2532
+                                }
2533
+                            }
2534
+                            if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 9) == 'functions') {
2535
+                                $result = run_sql($dir . $file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
2536
+                                if ($result <= 0) {
2537
+                                    $error++;
2538
+                                }
2539
+                            }
2540
+                        }
2541
+
2542
+                        rewinddir($handle);
2543
+
2544
+                        // Run data_xxx.sql files (Must be done after llx_mytable.key.sql)
2545
+                        $files = [];
2546
+                        while (($file = readdir($handle)) !== false) {
2547
+                            $files[] = $file;
2548
+                        }
2549
+                        sort($files);
2550
+                        foreach ($files as $file) {
2551
+                            if ($onlywithsuffix) {
2552
+                                if (!preg_match('/\-' . preg_quote($onlywithsuffix, '/') . '\./i', $file)) {
2553
+                                    //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
2554
+                                    continue;
2555
+                                } else {
2556
+                                    //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
2557
+                                }
2558
+                            }
2559
+                            if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'data') {
2560
+                                $result = run_sql($dir . $file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
2561
+                                if ($result <= 0) {
2562
+                                    $error++;
2563
+                                }
2564
+                            }
2565
+                        }
2566
+
2567
+                        rewinddir($handle);
2568
+
2569
+                        // Run update_xxx.sql files
2570
+                        $files = [];
2571
+                        while (($file = readdir($handle)) !== false) {
2572
+                            $files[] = $file;
2573
+                        }
2574
+                        sort($files);
2575
+                        foreach ($files as $file) {
2576
+                            if ($onlywithsuffix) {
2577
+                                if (!preg_match('/\-' . preg_quote($onlywithsuffix, '/') . '\./i', $file)) {
2578
+                                    //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
2579
+                                    continue;
2580
+                                } else {
2581
+                                    //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
2582
+                                }
2583
+                            }
2584
+                            if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 6) == 'update') {
2585
+                                $result = run_sql($dir . $file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
2586
+                                if ($result <= 0) {
2587
+                                    $error++;
2588
+                                }
2589
+                            }
2590
+                        }
2591
+
2592
+                        closedir($handle);
2593
+                    }
2594
+                }
2595
+
2596
+                if ($error == 0) {
2597
+                    $ok = 1;
2598
+                }
2599
+            }
2600
+        }
2601
+
2602
+        if (!$dirfound) {
2603
+            dol_syslog("A module ask to load sql files into " . $reldir . " but this directory was not found.", LOG_WARNING);
2604
+        }
2605
+        return $ok;
2606
+    }
2607 2607
 }
Please login to merge, or discard this patch.
Spacing   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -452,7 +452,7 @@  discard block
 block discarded – undo
452 452
 		if ($pathoffile) {     // Mostly for external modules
453 453
 			$content = file_get_contents($pathoffile);
454 454
 
455
-			if ((float)DOL_VERSION >= 6.0) {
455
+			if ((float) DOL_VERSION >= 6.0) {
456 456
 				@include_once BASE_PATH . '/../Dolibarr/Lib/ParseMd.php';
457 457
 
458 458
 				$content = dolMd2Html(
@@ -554,7 +554,7 @@  discard block
 block discarded – undo
554 554
 		if ($filefound) {     // Mostly for external modules
555 555
 			$content = file_get_contents($pathoffile);
556 556
 
557
-			if ((float)DOL_VERSION >= 6.0) {
557
+			if ((float) DOL_VERSION >= 6.0) {
558 558
 				@include_once BASE_PATH . '/../Dolibarr/Lib/ParseMd.php';
559 559
 
560 560
 				$content = dolMd2Html($content, 'parsedown', ['doc/' => dol_buildpath(strtolower($this->name) . '/doc/', 1)]);
@@ -672,7 +672,7 @@  discard block
 block discarded – undo
672 672
 
673 673
 		$sql = "SELECT tms FROM " . $dbPrefix . "const";
674 674
 		$sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
675
-		$sql .= " AND entity IN (0, " . ((int)$conf->entity) . ")";
675
+		$sql .= " AND entity IN (0, " . ((int) $conf->entity) . ")";
676 676
 
677 677
 		dol_syslog(get_class($this) . "::getLastActiveDate", LOG_DEBUG);
678 678
 		$resql = $this->db->query($sql);
@@ -884,7 +884,7 @@  discard block
 block discarded – undo
884 884
 		$sql = "INSERT INTO " . $dbPrefix . "const (name, value, visible, entity, note) VALUES";
885 885
 		$sql .= " (" . $this->db->encrypt($this->const_name);
886 886
 		$sql .= ", " . $this->db->encrypt('1');
887
-		$sql .= ", 0, " . ((int)$entity);
887
+		$sql .= ", 0, " . ((int) $entity);
888 888
 		$sql .= ", '" . $this->db->escape($note) . "')";
889 889
 
890 890
 		dol_syslog(get_class($this) . "::_active insert activation constant", LOG_DEBUG);
@@ -944,7 +944,7 @@  discard block
 block discarded – undo
944 944
 					$sql .= ", " . $this->db->encrypt($newvalue);
945 945
 					$sql .= ", null";
946 946
 					$sql .= ", '0'";
947
-					$sql .= ", " . ((int)$entity);
947
+					$sql .= ", " . ((int) $entity);
948 948
 					$sql .= ")";
949 949
 
950 950
 					$resql = $this->db->query($sql);
@@ -1057,7 +1057,7 @@  discard block
 block discarded – undo
1057 1057
 					$sql .= ", " . $this->db->encrypt($newvalue, 1);
1058 1058
 					$sql .= ", null";
1059 1059
 					$sql .= ", '0'";
1060
-					$sql .= ", " . ((int)$entity);
1060
+					$sql .= ", " . ((int) $entity);
1061 1061
 					$sql .= ")";
1062 1062
 
1063 1063
 					dol_syslog(get_class($this) . "::insert_module_parts for key=" . $this->const_name . "_" . strtoupper($key), LOG_DEBUG);
@@ -1114,7 +1114,7 @@  discard block
 block discarded – undo
1114 1114
 			$sql = "SELECT count(*) as nb";
1115 1115
 			$sql .= " FROM " . $dbPrefix . "const";
1116 1116
 			$sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($name) . "'";
1117
-			$sql .= " AND entity = " . ((int)$entity);
1117
+			$sql .= " AND entity = " . ((int) $entity);
1118 1118
 
1119 1119
 			$result = $this->db->query($sql);
1120 1120
 			if ($result) {
@@ -1219,7 +1219,7 @@  discard block
 block discarded – undo
1219 1219
 								}
1220 1220
 
1221 1221
 								$sql = "INSERT INTO " . $dbPrefix . "boxes (box_id, position, box_order, fk_user, entity)";
1222
-								$sql .= " VALUES (" . ((int)$lastid) . ", " . ((int)$key2) . ", '0', 0, " . ((int)$conf->entity) . ")";
1222
+								$sql .= " VALUES (" . ((int) $lastid) . ", " . ((int) $key2) . ", '0', 0, " . ((int) $conf->entity) . ")";
1223 1223
 
1224 1224
 								dol_syslog(get_class($this) . "::insert_boxes onto page " . $key2 . "=" . $val2, LOG_DEBUG);
1225 1225
 								$resql = $this->db->query($sql);
@@ -1304,7 +1304,7 @@  discard block
 block discarded – undo
1304 1304
 				if ($parameters) {
1305 1305
 					$sql .= " AND params = '".$this->db->escape($parameters)."'";
1306 1306
 				}*/
1307
-				$sql .= " AND entity = " . ((int)$entity); // Must be exact entity
1307
+				$sql .= " AND entity = " . ((int) $entity); // Must be exact entity
1308 1308
 
1309 1309
 				$now = dol_now();
1310 1310
 
@@ -1352,7 +1352,7 @@  discard block
 block discarded – undo
1352 1352
 								$sql .= "'" . $this->db->escape($priority) . "', ";
1353 1353
 							}
1354 1354
 							if (is_int($status)) {
1355
-								$sql .= ((int)$status) . ", ";
1355
+								$sql .= ((int) $status) . ", ";
1356 1356
 							}
1357 1357
 							$sql .= $entity . ",";
1358 1358
 							$sql .= "'" . $this->db->escape($test) . "'";
@@ -1420,7 +1420,7 @@  discard block
 block discarded – undo
1420 1420
 			if ($obj !== null && !empty($obj->value) && !empty($this->rights)) {
1421 1421
 				// If the module is active
1422 1422
 				foreach ($this->rights as $key => $value) {
1423
-					$r_id = $this->rights[$key][0];   // permission id in llx_rights_def (not unique because primary key is couple id-entity)
1423
+					$r_id = $this->rights[$key][0]; // permission id in llx_rights_def (not unique because primary key is couple id-entity)
1424 1424
 					$r_desc = $this->rights[$key][1];
1425 1425
 					$r_type = isset($this->rights[$key][2]) ? $this->rights[$key][2] : '';
1426 1426
 					$r_def = empty($this->rights[$key][3]) ? 0 : $this->rights[$key][3];
@@ -1434,7 +1434,7 @@  discard block
 block discarded – undo
1434 1434
 
1435 1435
 					// Search if perm already present
1436 1436
 					$sql = "SELECT count(*) as nb FROM " . $dbPrefix . "rights_def";
1437
-					$sql .= " WHERE id = " . ((int)$r_id) . " AND entity = " . ((int)$entity);
1437
+					$sql .= " WHERE id = " . ((int) $r_id) . " AND entity = " . ((int) $entity);
1438 1438
 
1439 1439
 					$resqlselect = $this->db->query($sql);
1440 1440
 					if ($resqlselect) {
@@ -1719,7 +1719,7 @@  discard block
 block discarded – undo
1719 1719
 
1720 1720
 			if ($row[0] == 0) {
1721 1721
 				$sql = "INSERT INTO " . $dbPrefix . "const (name, type, value, note, visible, entity)";
1722
-				$sql .= " VALUES (" . $this->db->encrypt($name) . ", 'chaine', " . $this->db->encrypt($dir) . ", '" . $this->db->escape("Directory for module " . $this->name) . "', '0', " . ((int)$conf->entity) . ")";
1722
+				$sql .= " VALUES (" . $this->db->encrypt($name) . ", 'chaine', " . $this->db->encrypt($dir) . ", '" . $this->db->escape("Directory for module " . $this->name) . "', '0', " . ((int) $conf->entity) . ")";
1723 1723
 
1724 1724
 				dol_syslog(get_class($this) . "::insert_dirs", LOG_DEBUG);
1725 1725
 				$this->db->query($sql);
@@ -1812,7 +1812,7 @@  discard block
 block discarded – undo
1812 1812
 		}
1813 1813
 
1814 1814
 		// Run complementary sql requests
1815
-		$num = count((array)$array_sql);
1815
+		$num = count((array) $array_sql);
1816 1816
 		for ($i = 0; $i < $num; $i++) {
1817 1817
 			if (!$err) {
1818 1818
 				dol_syslog(get_class($this) . "::_remove", LOG_DEBUG);
@@ -1919,7 +1919,7 @@  discard block
 block discarded – undo
1919 1919
 
1920 1920
 				$sql = "DELETE FROM " . $dbPrefix . "const";
1921 1921
 				$sql .= " WHERE " . $this->db->decrypt('name') . " LIKE '" . $this->db->escape($this->const_name) . "_" . strtoupper($key) . "'";
1922
-				$sql .= " AND entity = " . ((int)$entity);
1922
+				$sql .= " AND entity = " . ((int) $entity);
1923 1923
 
1924 1924
 				if (!$this->db->query($sql)) {
1925 1925
 					$this->error = $this->db->lasterror();
@@ -2029,7 +2029,7 @@  discard block
 block discarded – undo
2029 2029
 
2030 2030
 				$sql = "DELETE FROM " . $dbPrefix . "boxes_def";
2031 2031
 				$sql .= " WHERE file = '" . $this->db->escape($file) . "'";
2032
-				$sql .= " AND entity = " . $conf->entity;     // Do not use getEntity here, we want to delete only in current company
2032
+				$sql .= " AND entity = " . $conf->entity; // Do not use getEntity here, we want to delete only in current company
2033 2033
 
2034 2034
 				dol_syslog(get_class($this) . "::delete_boxes", LOG_DEBUG);
2035 2035
 				$resql = $this->db->query($sql);
@@ -2237,7 +2237,7 @@  discard block
 block discarded – undo
2237 2237
 
2238 2238
 		$return .= '<div class="valignmiddle inline-block info-box-more">';
2239 2239
 		//if ($versiontrans) print img_warning($langs->trans("Version").' '.$this->getVersion(1)).' ';
2240
-		$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>';
2240
+		$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>';
2241 2241
 		$return .= '</div><br>';
2242 2242
 
2243 2243
 		$return .= '<div class="valignmiddle inline-block info-box-actions">';
@@ -2398,7 +2398,7 @@  discard block
 block discarded – undo
2398 2398
 	{
2399 2399
 		require_once BASE_PATH . '/../Dolibarr/Lib/GetUrl.php';
2400 2400
 		if (!empty($this->url_last_version)) {
2401
-			$lastVersion = getURLContent($this->url_last_version, 'GET', '', 1, [], ['http', 'https'], 0);    // Accept http or https links on external remote server only
2401
+			$lastVersion = getURLContent($this->url_last_version, 'GET', '', 1, [], ['http', 'https'], 0); // Accept http or https links on external remote server only
2402 2402
 			if (isset($lastVersion['content']) && strlen($lastVersion['content']) < 30) {
2403 2403
 				// 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 _ . -
2404 2404
 				$this->lastVersion = preg_replace("/[^a-zA-Z0-9_\.\-]+/", "", $lastVersion['content']);
Please login to merge, or discard this patch.
htdocs/core/modules/modApi.class.php 1 patch
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -143,11 +143,11 @@  discard block
 block discarded – undo
143 143
 
144 144
         // Add here list of permission defined by an id, a label, a boolean and two constant strings.
145 145
         // Example:
146
-        $this->rights[$r][0] = $this->numero + $r;  // Permission id (must not be already used)
146
+        $this->rights[$r][0] = $this->numero + $r; // Permission id (must not be already used)
147 147
         $this->rights[$r][1] = 'Generate/modify users API key'; // Permission label
148
-        $this->rights[$r][3] = 0;                   // Permission by default for new user (0/1)
149
-        $this->rights[$r][4] = 'apikey';                // In php code, permission will be checked by test if ($user->hasRight('permkey','level1','level2'))
150
-        $this->rights[$r][5] = 'generate';              // In php code, permission will be checked by test if ($user->hasRight('permkey','level1','level2'))
148
+        $this->rights[$r][3] = 0; // Permission by default for new user (0/1)
149
+        $this->rights[$r][4] = 'apikey'; // In php code, permission will be checked by test if ($user->hasRight('permkey','level1','level2'))
150
+        $this->rights[$r][5] = 'generate'; // In php code, permission will be checked by test if ($user->hasRight('permkey','level1','level2'))
151 151
         $r++;
152 152
 
153 153
 
@@ -233,7 +233,7 @@  discard block
 block discarded – undo
233 233
 
234 234
         // Remove old constants with entity fields different of 0
235 235
         $sql = array(
236
-            "DELETE FROM " . $dbPrefix . "const WHERE name = " . $this->db->encrypt('MAIN_MODULE_API'),      // API can't be enabled per environment. Why ?
236
+            "DELETE FROM " . $dbPrefix . "const WHERE name = " . $this->db->encrypt('MAIN_MODULE_API'), // API can't be enabled per environment. Why ?
237 237
             "DELETE FROM " . $dbPrefix . "const WHERE name = " . $this->db->encrypt('API_PRODUCTION_MODE')   // Not in production mode by default at activation
238 238
         );
239 239
 
Please login to merge, or discard this patch.
htdocs/index.php 1 patch
Indentation   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -76,7 +76,7 @@  discard block
 block discarded – undo
76 76
 $module = filter_input(INPUT_GET, MODULE_NAME_VAR);
77 77
 $controller = filter_input(INPUT_GET, CONTROLLER_VAR);
78 78
 if (isset($module) && isset($controller)) {
79
-	if (Dispatcher::run($module, $controller)) {
79
+    if (Dispatcher::run($module, $controller)) {
80 80
         die(); // The controller has been executed succesfully!
81 81
     }
82 82
 }
@@ -98,7 +98,7 @@  discard block
 block discarded – undo
98 98
 }
99 99
 
100 100
 if (empty($page) && empty($ctrl)) {
101
-	require BASE_PATH . DIRECTORY_SEPARATOR . 'index_dol.php';
101
+    require BASE_PATH . DIRECTORY_SEPARATOR . 'index_dol.php';
102 102
     die();
103 103
 }
104 104
 
Please login to merge, or discard this patch.