Passed
Push — dev ( 3becbd...634f81 )
by Rafael
53:25
created

versiontostring()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 8
nop 1
dl 0
loc 13
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* Copyright (C) 2008-2011  Laurent Destailleur         <[email protected]>
4
 * Copyright (C) 2005-2016  Regis Houssin               <[email protected]>
5
 * Copyright (C) 2012       J. Fernando Lagrange        <[email protected]>
6
 * Copyright (C) 2015       Raphaël Doursenaud          <[email protected]>
7
 * Copyright (C) 2023       Eric Seigne      		    <[email protected]>
8
 * Copyright (C) 2024		MDW							<[email protected]>
9
 * Copyright (C) 2024       Rafael San José             <[email protected]>
10
 *
11
 * This program is free software; you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation; either version 3 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
23
 * or see https://www.gnu.org/
24
 */
25
26
use Dolibarr\Code\Core\Classes\DolEditor;
27
use Dolibarr\Code\Core\Classes\Form;
28
use Dolibarr\Code\Core\Classes\FormMail;
29
use Dolibarr\Core\Base\DolibarrModules;
30
use Dolibarr\Lib\AveryLabels;
31
use Dolibarr\Lib\Version;
32
33
/**
34
 *  \file           htdocs/core/lib/admin.lib.php
35
 *  \brief          Library of admin functions
36
 */
37
38
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/functions2.lib.php';
39
40
/**
41
 *  Launch a sql file. Function is used by:
42
 *  - Migrate process (dolibarr-xyz-abc.sql)
43
 *  - Loading sql menus (auguria)
44
 *  - Running specific Sql by a module init
45
 *  - Loading sql file of website import package
46
 *  Install process however does not use it.
47
 *  Note that Sql files must have all comments at start of line. Also this function take ';' as the char to detect end of sql request
48
 *
49
 * @param string $sqlfile Full path to sql file
50
 * @param int $silent 1=Do not output anything, 0=Output line for update page
51
 * @param int $entity Entity targeted for multicompany module
52
 * @param int $usesavepoint 1=Run a savepoint before each request and a rollback to savepoint if error (this allow to have some request with errors inside global transactions).
53
 * @param string $handler Handler targeted for menu (replace __HANDLER__ with this value between quotes)
54
 * @param string $okerror Family of errors we accept ('default', 'none')
55
 * @param int $linelengthlimit Limit for length of each line (Use 0 if unknown, may be faster if defined)
56
 * @param int $nocommentremoval Do no try to remove comments (in such a case, we consider that each line is a request, so use also $linelengthlimit=0)
57
 * @param int $offsetforchartofaccount Offset to use to load chart of account table to update sql on the fly to add offset to rowid and account_parent value
58
 * @param int $colspan 2=Add a colspan=2 on td
59
 * @param int $onlysqltoimportwebsite Only sql requests used to import a website template are allowed
60
 * @param string $database Database (replace __DATABASE__ with this value)
61
 * @return     int                                     Return integer <=0 if KO, >0 if OK
62
 */
63
function run_sql($sqlfile, $silent = 1, $entity = 0, $usesavepoint = 1, $handler = '', $okerror = 'default', $linelengthlimit = 32768, $nocommentremoval = 0, $offsetforchartofaccount = 0, $colspan = 0, $onlysqltoimportwebsite = 0, $database = '')
64
{
65
    global $db, $conf, $langs, $user;
66
67
    dol_syslog("Admin.lib::run_sql run sql file " . $sqlfile . " silent=" . $silent . " entity=" . $entity . " usesavepoint=" . $usesavepoint . " handler=" . $handler . " okerror=" . $okerror, LOG_DEBUG);
68
69
    if (!is_numeric($linelengthlimit)) {
70
        dol_syslog("Admin.lib::run_sql param linelengthlimit is not a numeric", LOG_ERR);
71
        return -1;
72
    }
73
74
    $ok = 0;
75
    $error = 0;
76
    $i = 0;
77
    $buffer = '';
78
    $arraysql = array();
79
80
    // Get version of database
81
    $versionarray = $db->getVersionArray();
82
83
    $fp = fopen($sqlfile, "r");
84
    if ($fp) {
85
        while (!feof($fp)) {
86
            // Warning fgets with second parameter that is null or 0 hang.
87
            if ($linelengthlimit > 0) {
88
                $buf = fgets($fp, $linelengthlimit);
89
            } else {
90
                $buf = fgets($fp);
91
            }
92
93
            // Test if request must be ran only for particular database or version (if yes, we must remove the -- comment)
94
            $reg = array();
95
            if (preg_match('/^--\sV(MYSQL|PGSQL)([^\s]*)/i', $buf, $reg)) {
96
                $qualified = 1;
97
98
                // restrict on database type
99
                if (!empty($reg[1])) {
100
                    if (!preg_match('/' . preg_quote($reg[1]) . '/i', $db->type)) {
101
                        $qualified = 0;
102
                    }
103
                }
104
105
                // restrict on version
106
                if ($qualified) {
107
                    if (!empty($reg[2])) {
108
                        if (is_numeric($reg[2])) {  // This is a version
109
                            $versionrequest = explode('.', $reg[2]);
110
                            //var_dump($versionrequest);
111
                            //var_dump($versionarray);
112
                            if (!count($versionrequest) || !count($versionarray) || Version::compare($versionrequest, $versionarray) > 0) {
113
                                $qualified = 0;
114
                            }
115
                        } else { // This is a test on a constant. For example when we have -- VMYSQLUTF8UNICODE, we test constant $conf->global->UTF8UNICODE
116
                            $dbcollation = strtoupper(preg_replace('/_/', '', $conf->db->dolibarr_main_db_collation));
117
                            //var_dump($reg[2]);
118
                            //var_dump($dbcollation);
119
                            if (empty($conf->db->dolibarr_main_db_collation) || ($reg[2] != $dbcollation)) {
120
                                $qualified = 0;
121
                            }
122
                            //var_dump($qualified);
123
                        }
124
                    }
125
                }
126
127
                if ($qualified) {
128
                    // Version qualified, delete SQL comments
129
                    $buf = preg_replace('/^--\sV(MYSQL|PGSQL)([^\s]*)/i', '', $buf);
130
                    //print "Ligne $i qualifi?e par version: ".$buf.'<br>';
131
                }
132
            }
133
134
            // Add line buf to buffer if not a comment
135
            if ($nocommentremoval || !preg_match('/^\s*--/', $buf)) {
136
                if (empty($nocommentremoval)) {
137
                    $buf = preg_replace('/([,;ERLT\)])\s*--.*$/i', '\1', $buf); //remove comment from a line that not start with -- before add it to the buffer
138
                }
139
                if ($buffer) {
140
                    $buffer .= ' ';
141
                }
142
                $buffer .= trim($buf);
143
            }
144
145
            //print $buf.'<br>';exit;
146
147
            if (preg_match('/;/', $buffer)) {   // If string contains ';', it's end of a request string, we save it in arraysql.
148
                // Found new request
149
                if ($buffer) {
150
                    $arraysql[$i] = $buffer;
151
                }
152
                $i++;
153
                $buffer = '';
154
            }
155
        }
156
157
        if ($buffer) {
158
            $arraysql[$i] = $buffer;
159
        }
160
        fclose($fp);
161
    } else {
162
        dol_syslog("Admin.lib::run_sql failed to open file " . $sqlfile, LOG_ERR);
163
    }
164
165
    // Loop on each request to see if there is a __+MAX_table__ key
166
    $listofmaxrowid = array(); // This is a cache table
167
    foreach ($arraysql as $i => $sql) {
168
        $newsql = $sql;
169
170
        // Replace __+MAX_table__ with max of table
171
        while (preg_match('/__\+MAX_([A-Za-z0-9_]+)__/i', $newsql, $reg)) {
172
            $table = $reg[1];
173
            if (!isset($listofmaxrowid[$table])) {
174
                //var_dump($db);
175
                $sqlgetrowid = 'SELECT MAX(rowid) as max from ' . preg_replace('/^llx_/', MAIN_DB_PREFIX, $table);
176
                $resql = $db->query($sqlgetrowid);
177
                if ($resql) {
178
                    $obj = $db->fetch_object($resql);
179
                    $listofmaxrowid[$table] = $obj->max;
180
                    if (empty($listofmaxrowid[$table])) {
181
                        $listofmaxrowid[$table] = 0;
182
                    }
183
                } else {
184
                    if (!$silent) {
185
                        print '<tr><td class="tdtop"' . ($colspan ? ' colspan="' . $colspan . '"' : '') . '>';
186
                        print '<div class="error">' . $langs->trans("Failed to get max rowid for " . $table) . "</div>";
187
                        print '</td></tr>';
188
                    }
189
                    $error++;
190
                    break;
191
                }
192
            }
193
            // Replace __+MAX_llx_table__ with +999
194
            $from = '__+MAX_' . $table . '__';
195
            $to = '+' . $listofmaxrowid[$table];
196
            $newsql = str_replace($from, $to, $newsql);
197
            dol_syslog('Admin.lib::run_sql New Request ' . ($i + 1) . ' (replacing ' . $from . ' to ' . $to . ')', LOG_DEBUG);
198
199
            $arraysql[$i] = $newsql;
200
        }
201
202
        if ($offsetforchartofaccount > 0) {
203
            // Replace lines
204
            // 'INSERT INTO llx_accounting_account (entity, rowid, fk_pcg_version, pcg_type, account_number, account_parent, label, active) VALUES (__ENTITY__, 1401, 'PCG99-ABREGE', 'CAPIT', '1234', 1400,...'
205
            // with
206
            // 'INSERT INTO llx_accounting_account (entity, rowid, fk_pcg_version, pcg_type, account_number, account_parent, label, active) VALUES (__ENTITY__, 1401 + 200100000, 'PCG99-ABREGE','CAPIT', '1234', 1400 + 200100000,...'
207
            // Note: string with 'PCG99-ABREGE','CAPIT', 1234  instead of  'PCG99-ABREGE','CAPIT', '1234' is also supported
208
            $newsql = preg_replace('/VALUES\s*\(__ENTITY__, \s*(\d+)\s*,(\s*\'[^\',]*\'\s*,\s*\'[^\',]*\'\s*,\s*\'?[^\',]*\'?\s*),\s*\'?([^\',]*)\'?/ims', 'VALUES (__ENTITY__, \1 + ' . ((int)$offsetforchartofaccount) . ', \2, \3 + ' . ((int)$offsetforchartofaccount), $newsql);
209
            $newsql = preg_replace('/([,\s])0 \+ ' . ((int)$offsetforchartofaccount) . '/ims', '\1 0', $newsql);
210
            //var_dump($newsql);
211
            $arraysql[$i] = $newsql;
212
213
            // FIXME Because we force the rowid during insert, we must also update the sequence with postgresql by running
214
            // SELECT dol_util_rebuild_sequences();
215
        }
216
    }
217
218
    // Loop on each request to execute request
219
    $cursorinsert = 0;
220
    $listofinsertedrowid = array();
221
    $keyforsql = md5($sqlfile);
222
    foreach ($arraysql as $i => $sql) {
223
        if ($sql) {
224
            // Test if the SQL is allowed SQL
225
            if ($onlysqltoimportwebsite) {
226
                $newsql = str_replace(array("\'"), '__BACKSLASHQUOTE__', $sql); // Replace the \' char
227
228
                // Remove all strings contents including the ' so we can analyse SQL instruction only later
229
                $l = strlen($newsql);
230
                $is = 0;
231
                $quoteopen = 0;
232
                $newsqlclean = '';
233
                while ($is < $l) {
234
                    $char = $newsql[$is];
235
                    if ($char == "'") {
236
                        if ($quoteopen) {
237
                            $quoteopen--;
238
                        } else {
239
                            $quoteopen++;
240
                        }
241
                    } elseif (empty($quoteopen)) {
242
                        $newsqlclean .= $char;
243
                    }
244
                    $is++;
245
                }
246
                $newsqlclean = str_replace(array("null"), '__000__', $newsqlclean);
247
                //print $newsqlclean."<br>\n";
248
249
                $qualified = 0;
250
251
                // A very small control. This can still by bypassed by adding a second SQL request concatenated
252
                if (preg_match('/^--/', $newsqlclean)) {
253
                    $qualified = 1;
254
                } elseif (preg_match('/^UPDATE llx_website SET \w+ = \d+\+\d+ WHERE rowid = \d+;$/', $newsqlclean)) {
255
                    $qualified = 1;
256
                } elseif (preg_match('/^INSERT INTO llx_website_page\([a-z0-9_\s,]+\) VALUES\([0-9_\s,\+]+\);$/', $newsqlclean)) {
257
                    // Insert must match
258
                    // INSERT INTO llx_website_page(rowid, fk_page, fk_website, pageurl, aliasalt, title, description, lang, image, keywords, status, date_creation, tms, import_key, grabbed_from, type_container, htmlheader, content, author_alias) VALUES(1+123, null, 17, , , , , , , , , , , null, , , , , );
259
                    $qualified = 1;
260
                }
261
262
                // Another check to allow some legitimate original urls
263
                if (!$qualified) {
264
                    if (preg_match('/^UPDATE llx_website SET \w+ = \'[a-zA-Z,\s]*\' WHERE rowid = \d+;$/', $sql)) {
265
                        $qualified = 1;
266
                    }
267
                }
268
269
                // We also check content
270
                $extractphp = dolKeepOnlyPhpCode($sql);
271
                $extractphpold = '';
272
273
                // Security analysis
274
                $errorphpcheck = checkPHPCode($extractphpold, $extractphp); // Contains the setEventMessages
275
                if ($errorphpcheck) {
276
                    $error++;
277
                    //print 'Request '.($i + 1)." contains non allowed instructions.<br>\n";
278
                    //print "newsqlclean = ".$newsqlclean."<br>\n";
279
                    dol_syslog('Admin.lib::run_sql Request ' . ($i + 1) . " contains PHP code and checking this code returns errorphpcheck='.$errorphpcheck.'", LOG_WARNING);
280
                    dol_syslog("sql=" . $sql, LOG_DEBUG);
281
                    break;
282
                }
283
284
285
                if (!$qualified) {
286
                    $error++;
287
                    //print 'Request '.($i + 1)." contains non allowed instructions.<br>\n";
288
                    //print "newsqlclean = ".$newsqlclean."<br>\n";
289
                    dol_syslog('Admin.lib::run_sql Request ' . ($i + 1) . " contains non allowed instructions.", LOG_WARNING);
290
                    dol_syslog('$newsqlclean=' . $newsqlclean, LOG_DEBUG);
291
                    break;
292
                }
293
            }
294
295
            // Replace the prefix tables
296
            if (MAIN_DB_PREFIX != 'llx_') {
297
                $sql = preg_replace('/llx_/i', MAIN_DB_PREFIX, $sql);
298
            }
299
300
            if (!empty($handler)) {
301
                $sql = preg_replace('/__HANDLER__/i', "'" . $db->escape($handler) . "'", $sql);
302
            }
303
304
            if (!empty($database)) {
305
                $sql = preg_replace('/__DATABASE__/i', $db->escape($database), $sql);
306
            }
307
308
            $newsql = preg_replace('/__ENTITY__/i', (!empty($entity) ? $entity : (string)$conf->entity), $sql);
309
310
            // Add log of request
311
            if (!$silent) {
312
                print '<tr class="trforrunsql' . $keyforsql . '"><td class="tdtop opacitymedium"' . ($colspan ? ' colspan="' . $colspan . '"' : '') . '>' . $langs->trans("Request") . ' ' . ($i + 1) . " sql='" . dol_htmlentities($newsql, ENT_NOQUOTES) . "'</td></tr>\n";
313
            }
314
            dol_syslog('Admin.lib::run_sql Request ' . ($i + 1), LOG_DEBUG);
315
            $sqlmodified = 0;
316
317
            // Replace for encrypt data
318
            if (preg_match_all('/__ENCRYPT\(\'([^\']+)\'\)__/i', $newsql, $reg)) {
319
                $num = count($reg[0]);
320
321
                for ($j = 0; $j < $num; $j++) {
322
                    $from = $reg[0][$j];
323
                    $to = $db->encrypt($reg[1][$j]);
324
                    $newsql = str_replace($from, $to, $newsql);
325
                }
326
                $sqlmodified++;
327
            }
328
329
            // Replace for decrypt data
330
            if (preg_match_all('/__DECRYPT\(\'([A-Za-z0-9_]+)\'\)__/i', $newsql, $reg)) {
331
                $num = count($reg[0]);
332
333
                for ($j = 0; $j < $num; $j++) {
334
                    $from = $reg[0][$j];
335
                    $to = $db->decrypt($reg[1][$j]);
336
                    $newsql = str_replace($from, $to, $newsql);
337
                }
338
                $sqlmodified++;
339
            }
340
341
            // Replace __x__ with the rowid of the result of the insert number x
342
            while (preg_match('/__([0-9]+)__/', $newsql, $reg)) {
343
                $cursor = $reg[1];
344
                if (empty($listofinsertedrowid[$cursor])) {
345
                    if (!$silent) {
346
                        print '<tr><td class="tdtop"' . ($colspan ? ' colspan="' . $colspan . '"' : '') . '>';
347
                        print '<div class="error">' . $langs->trans("FileIsNotCorrect") . "</div>";
348
                        print '</td></tr>';
349
                    }
350
                    $error++;
351
                    break;
352
                }
353
354
                $from = '__' . $cursor . '__';
355
                $to = $listofinsertedrowid[$cursor];
356
                $newsql = str_replace($from, $to, $newsql);
357
                $sqlmodified++;
358
            }
359
360
            if ($sqlmodified) {
361
                dol_syslog('Admin.lib::run_sql New Request ' . ($i + 1), LOG_DEBUG);
362
            }
363
364
            $result = $db->query($newsql, $usesavepoint);
365
            if ($result) {
366
                if (!$silent) {
367
                    print '<!-- Result = OK -->' . "\n";
368
                }
369
370
                if (preg_replace('/insert into ([^\s]+)/i', $newsql, $reg)) {
371
                    $cursorinsert++;
372
373
                    // It's an insert
374
                    $table = preg_replace('/([^a-zA-Z_]+)/i', '', $reg[1]);
375
                    $insertedrowid = $db->last_insert_id($table);
376
                    $listofinsertedrowid[$cursorinsert] = $insertedrowid;
377
                    dol_syslog('Admin.lib::run_sql Insert nb ' . $cursorinsert . ', done in table ' . $table . ', rowid is ' . $listofinsertedrowid[$cursorinsert], LOG_DEBUG);
378
                }
379
                //            print '<td class="right">OK</td>';
380
            } else {
381
                $errno = $db->errno();
382
                if (!$silent) {
383
                    print '<!-- Result = ' . $errno . ' -->' . "\n";
384
                }
385
386
                // Define list of errors we accept (array $okerrors)
387
                $okerrors = array(  // By default
388
                    'DB_ERROR_TABLE_ALREADY_EXISTS',
389
                    'DB_ERROR_COLUMN_ALREADY_EXISTS',
390
                    'DB_ERROR_KEY_NAME_ALREADY_EXISTS',
391
                    'DB_ERROR_TABLE_OR_KEY_ALREADY_EXISTS', // PgSql use same code for table and key already exist
392
                    'DB_ERROR_RECORD_ALREADY_EXISTS',
393
                    'DB_ERROR_NOSUCHTABLE',
394
                    'DB_ERROR_NOSUCHFIELD',
395
                    'DB_ERROR_NO_FOREIGN_KEY_TO_DROP',
396
                    'DB_ERROR_NO_INDEX_TO_DROP',
397
                    'DB_ERROR_CANNOT_CREATE', // Qd contrainte deja existante
398
                    'DB_ERROR_CANT_DROP_PRIMARY_KEY',
399
                    'DB_ERROR_PRIMARY_KEY_ALREADY_EXISTS',
400
                    'DB_ERROR_22P02'
401
                );
402
                if ($okerror == 'none') {
403
                    $okerrors = array();
404
                }
405
406
                // Is it an error we accept
407
                if (!in_array($errno, $okerrors)) {
408
                    if (!$silent) {
409
                        print '<tr><td class="tdtop"' . ($colspan ? ' colspan="' . $colspan . '"' : '') . '>';
410
                        print '<div class="error">' . $langs->trans("Error") . " " . $db->errno() . " (Req " . ($i + 1) . "): " . $newsql . "<br>" . $db->error() . "</div>";
411
                        print '</td></tr>' . "\n";
412
                    }
413
                    dol_syslog('Admin.lib::run_sql Request ' . ($i + 1) . " Error " . $db->errno() . " " . $newsql . "<br>" . $db->error(), LOG_ERR);
414
                    $error++;
415
                }
416
            }
417
        }
418
    }
419
420
    if (!$silent) {
421
        print '<tr><td>' . $langs->trans("ProcessMigrateScript") . '</td>';
422
        print '<td class="right">';
423
        if ($error == 0) {
424
            print '<span class="ok">' . $langs->trans("OK") . '</span>';
425
        } else {
426
            print '<span class="error">' . $langs->trans("Error") . '</span>';
427
        }
428
429
        //if (!empty($conf->use_javascript_ajax)) {     // use_javascript_ajax is not defined
430
        print '<script type="text/javascript">
431
		jQuery(document).ready(function() {
432
			function init_trrunsql' . $keyforsql . '()
433
			{
434
				console.log("toggle .trforrunsql' . $keyforsql . '");
435
				jQuery(".trforrunsql' . $keyforsql . '").toggle();
436
			}
437
			init_trrunsql' . $keyforsql . '();
438
			jQuery(".trforrunsqlshowhide' . $keyforsql . '").click(function() {
439
				init_trrunsql' . $keyforsql . '();
440
			});
441
		});
442
		</script>';
443
        if (count($arraysql)) {
444
            print ' - <a class="trforrunsqlshowhide' . $keyforsql . '" href="#" title="' . ($langs->trans("ShowHideTheNRequests", count($arraysql))) . '">' . $langs->trans("ShowHideDetails") . '</a>';
445
        } else {
446
            print ' - <span class="opacitymedium">' . $langs->trans("ScriptIsEmpty") . '</span>';
447
        }
448
        //}
449
450
        print '</td></tr>' . "\n";
451
    }
452
453
    if ($error == 0) {
454
        $ok = 1;
455
    } else {
456
        $ok = 0;
457
    }
458
459
    return $ok;
460
}
461
462
/**
463
 *  Delete a constant
464
 *
465
 * @param DoliDB $db Database handler
466
 * @param string|int $name Name of constant or rowid of line
467
 * @param int $entity Multi company id, -1 for all entities
468
 * @return     int                     Return integer <0 if KO, >0 if OK
469
 *
470
 * @see        dolibarr_get_const(), dolibarr_set_const(), dol_set_user_param()
471
 */
472
function dolibarr_del_const($db, $name, $entity = 1)
473
{
474
    global $conf;
475
476
    if (empty($name)) {
477
        dol_print_error(null, 'Error call dolibar_del_const with parameter name empty');
478
        return -1;
479
    }
480
481
    $sql = "DELETE FROM " . MAIN_DB_PREFIX . "const";
482
    $sql .= " WHERE (" . $db->decrypt('name') . " = '" . $db->escape($name) . "'";
483
    if (is_numeric($name)) {
484
        $sql .= " OR rowid = " . ((int)$name);
485
    }
486
    $sql .= ")";
487
    if ($entity >= 0) {
488
        $sql .= " AND entity = " . ((int)$entity);
489
    }
490
491
    dol_syslog("admin.lib::dolibarr_del_const", LOG_DEBUG);
492
    $resql = $db->query($sql);
493
    if ($resql) {
494
        $conf->global->$name = '';
495
        return 1;
496
    } else {
497
        dol_print_error($db);
498
        return -1;
499
    }
500
}
501
502
/**
503
 *  Get the value of a setup constant from database
504
 *
505
 * @param DoliDB $db Database handler
506
 * @param string $name Name of constant
507
 * @param int $entity Multi company id
508
 * @return     string                  Value of constant
509
 *
510
 * @see        dolibarr_del_const(), dolibarr_set_const(), dol_set_user_param()
511
 */
512
function dolibarr_get_const($db, $name, $entity = 1)
513
{
514
    $value = '';
515
516
    $sql = "SELECT " . $db->decrypt('value') . " as value";
517
    $sql .= " FROM " . MAIN_DB_PREFIX . "const";
518
    $sql .= " WHERE name = " . $db->encrypt($name);
519
    $sql .= " AND entity = " . ((int)$entity);
520
521
    dol_syslog("admin.lib::dolibarr_get_const", LOG_DEBUG);
522
    $resql = $db->query($sql);
523
    if ($resql) {
524
        $obj = $db->fetch_object($resql);
525
        if ($obj) {
526
            include_once DOL_DOCUMENT_ROOT . '/core/lib/security.lib.php';
527
            $value = dolDecrypt($obj->value);
528
        }
529
    }
530
    return $value;
531
}
532
533
/**
534
 *  Insert a parameter (key,value) into database (delete old key then insert it again).
535
 *
536
 * @param DoliDB $db Database handler
537
 * @param string $name Name of constant
538
 * @param string $value Value of constant
539
 * @param string $type Type of constant. Deprecated, only strings are allowed for $value. Caller must json encode/decode to store other type of data.
540
 * @param int $visible Is constant visible in Setup->Other page (0 by default)
541
 * @param string $note Note on parameter
542
 * @param int $entity Multi company id (0 means all entities)
543
 * @return     int                     -1 if KO, 1 if OK
544
 *
545
 * @see        dolibarr_del_const(), dolibarr_get_const(), dol_set_user_param()
546
 */
547
function dolibarr_set_const($db, $name, $value, $type = 'chaine', $visible = 0, $note = '', $entity = 1)
548
{
549
    global $conf;
550
551
    // Clean parameters
552
    $name = trim($name);
553
554
    // Check parameters
555
    if (empty($name)) {
556
        dol_print_error($db, "Error: Call to function dolibarr_set_const with wrong parameters");
557
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
558
    }
559
560
    //dol_syslog("dolibarr_set_const name=$name, value=$value type=$type, visible=$visible, note=$note entity=$entity");
561
562
    $db->begin();
563
564
    $sql = "DELETE FROM " . MAIN_DB_PREFIX . "const";
565
    $sql .= " WHERE name = " . $db->encrypt($name);
566
    if ($entity >= 0) {
567
        $sql .= " AND entity = " . ((int)$entity);
568
    }
569
570
    dol_syslog("admin.lib::dolibarr_set_const", LOG_DEBUG);
571
    $resql = $db->query($sql);
572
573
    if (strcmp($value, '')) {   // true if different. Must work for $value='0' or $value=0
574
        if (!preg_match('/^(MAIN_LOGEVENTS|MAIN_AGENDA_ACTIONAUTO)/', $name) && (preg_match('/(_KEY|_EXPORTKEY|_SECUREKEY|_SERVERKEY|_PASS|_PASSWORD|_PW|_PW_TICKET|_PW_EMAILING|_SECRET|_SECURITY_TOKEN|_WEB_TOKEN)$/', $name))) {
575
            // This seems a sensitive constant, we encrypt its value
576
            // To list all sensitive constant, you can make a
577
            // WHERE name like '%\_KEY' or name like '%\_EXPORTKEY' or name like '%\_SECUREKEY' or name like '%\_SERVERKEY' or name like '%\_PASS' or name like '%\_PASSWORD' or name like '%\_SECRET'
578
            // or name like '%\_SECURITY_TOKEN' or name like '%\WEB_TOKEN'
579
            include_once DOL_DOCUMENT_ROOT . '/core/lib/security.lib.php';
580
            $newvalue = dolEncrypt($value);
581
        } else {
582
            $newvalue = $value;
583
        }
584
585
        $sql = "INSERT INTO " . MAIN_DB_PREFIX . "const(name, value, type, visible, note, entity)";
586
        $sql .= " VALUES (";
587
        $sql .= $db->encrypt($name);
588
        $sql .= ", " . $db->encrypt($newvalue);
589
        $sql .= ", '" . $db->escape($type) . "', " . ((int)$visible) . ", '" . $db->escape($note) . "', " . ((int)$entity) . ")";
590
591
        //print "sql".$value."-".pg_escape_string($value)."-".$sql;exit;
592
        //print "xx".$db->escape($value);
593
        dol_syslog("admin.lib::dolibarr_set_const", LOG_DEBUG);
594
        $resql = $db->query($sql);
595
    }
596
597
    if ($resql) {
598
        $db->commit();
599
        $conf->global->$name = $value;
600
        return 1;
601
    } else {
602
        $error = $db->lasterror();
603
        $db->rollback();
604
        return -1;
605
    }
606
}
607
608
/**
609
 * Prepare array with list of tabs
610
 *
611
 * @param int $nbofactivatedmodules Number if activated modules
612
 * @param int $nboftotalmodules Nb of total modules
613
 * @param int $nbmodulesnotautoenabled Nb of modules not auto enabled that are activated
614
 * @return  array                               Array of tabs to show
615
 */
616
function modules_prepare_head($nbofactivatedmodules, $nboftotalmodules, $nbmodulesnotautoenabled)
617
{
618
    global $langs, $form;
619
620
    $desc = $langs->trans("ModulesDesc", '{picto}');
621
    $desc = str_replace('{picto}', img_picto('', 'switch_off'), $desc);
622
623
    $h = 0;
624
    $head = array();
625
    $mode = getDolGlobalString('MAIN_MODULE_SETUP_ON_LIST_BY_DEFAULT', 'commonkanban');
626
    $head[$h][0] = constant('BASE_URL') . "/admin/modules.php?mode=" . $mode;
627
    if ($nbmodulesnotautoenabled <= getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled)
628
        //$head[$h][1] = $form->textwithpicto($langs->trans("AvailableModules"), $desc);
629
        $head[$h][1] = $langs->trans("AvailableModules");
630
        $head[$h][1] .= $form->textwithpicto('', $langs->trans("YouMustEnableOneModule") . '.<br><br><span class="opacitymedium">' . $desc . '</span>', 1, 'warning');
631
    } else {
632
        //$head[$h][1] = $langs->trans("AvailableModules").$form->textwithpicto('<span class="badge marginleftonly">'.$nbofactivatedmodules.' / '.$nboftotalmodules.'</span>', $desc, 1, 'help', '', 1, 3);
633
        $head[$h][1] = $langs->trans("AvailableModules") . '<span class="badge marginleftonly">' . $nbofactivatedmodules . ' / ' . $nboftotalmodules . '</span>';
634
    }
635
    $head[$h][2] = 'modules';
636
    $h++;
637
638
    $head[$h][0] = constant('BASE_URL') . "/admin/modules.php?mode=marketplace";
639
    $head[$h][1] = $langs->trans("ModulesMarketPlaces");
640
    $head[$h][2] = 'marketplace';
641
    $h++;
642
643
    $head[$h][0] = constant('BASE_URL') . "/admin/modules.php?mode=deploy";
644
    $head[$h][1] = $langs->trans("AddExtensionThemeModuleOrOther");
645
    $head[$h][2] = 'deploy';
646
    $h++;
647
648
    $head[$h][0] = constant('BASE_URL') . "/admin/modules.php?mode=develop";
649
    $head[$h][1] = $langs->trans("ModulesDevelopYourModule");
650
    $head[$h][2] = 'develop';
651
    $h++;
652
653
    return $head;
654
}
655
656
/**
657
 * Prepare array with list of tabs
658
 *
659
 * @return  array               Array of tabs to show
660
 */
661
function ihm_prepare_head()
662
{
663
    global $langs, $conf, $user;
664
    $h = 0;
665
    $head = array();
666
667
    $head[$h][0] = constant('BASE_URL') . "/admin/ihm.php?mode=other";
668
    $head[$h][1] = $langs->trans("LanguageAndPresentation");
669
    $head[$h][2] = 'other';
670
    $h++;
671
672
    $head[$h][0] = constant('BASE_URL') . "/admin/ihm.php?mode=template";
673
    $head[$h][1] = $langs->trans("SkinAndColors");
674
    $head[$h][2] = 'template';
675
    $h++;
676
677
    $head[$h][0] = constant('BASE_URL') . "/admin/ihm.php?mode=dashboard";
678
    $head[$h][1] = $langs->trans("Dashboard");
679
    $head[$h][2] = 'dashboard';
680
    $h++;
681
682
    $head[$h][0] = constant('BASE_URL') . "/admin/ihm.php?mode=login";
683
    $head[$h][1] = $langs->trans("LoginPage");
684
    $head[$h][2] = 'login';
685
    $h++;
686
687
    $head[$h][0] = constant('BASE_URL') . "/admin/ihm.php?mode=css";
688
    $head[$h][1] = $langs->trans("CSSPage");
689
    $head[$h][2] = 'css';
690
    $h++;
691
692
    complete_head_from_modules($conf, $langs, null, $head, $h, 'ihm_admin');
693
694
    complete_head_from_modules($conf, $langs, null, $head, $h, 'ihm_admin', 'remove');
695
696
697
    return $head;
698
}
699
700
/**
701
 * Prepare array with list of tabs
702
 *
703
 * @return  array               Array of tabs to show
704
 */
705
function security_prepare_head()
706
{
707
    global $db, $langs, $conf, $user;
708
    $h = 0;
709
    $head = array();
710
711
    $head[$h][0] = constant('BASE_URL') . "/admin/security_other.php";
712
    $head[$h][1] = $langs->trans("Miscellaneous");
713
    $head[$h][2] = 'misc';
714
    $h++;
715
716
    $head[$h][0] = constant('BASE_URL') . "/admin/security.php";
717
    $head[$h][1] = $langs->trans("Passwords");
718
    $head[$h][2] = 'passwords';
719
    $h++;
720
721
    $head[$h][0] = constant('BASE_URL') . "/admin/security_file.php";
722
    $head[$h][1] = $langs->trans("Files") . ' (' . $langs->trans("Upload") . ')';
723
    $head[$h][2] = 'file';
724
    $h++;
725
726
    /*
727
    $head[$h][0] = constant('BASE_URL')."/admin/security_file_download.php";
728
    $head[$h][1] = $langs->trans("Files").' ('.$langs->trans("Download").')';
729
    $head[$h][2] = 'filedownload';
730
    $h++;
731
    */
732
733
    $head[$h][0] = constant('BASE_URL') . "/admin/proxy.php";
734
    $head[$h][1] = $langs->trans("ExternalAccess");
735
    $head[$h][2] = 'proxy';
736
    $h++;
737
738
    $head[$h][0] = constant('BASE_URL') . "/admin/events.php";
739
    $head[$h][1] = $langs->trans("Audit");
740
    $head[$h][2] = 'audit';
741
    $h++;
742
743
744
    // Show permissions lines
745
    $nbPerms = 0;
746
    $sql = "SELECT COUNT(r.id) as nb";
747
    $sql .= " FROM " . MAIN_DB_PREFIX . "rights_def as r";
748
    $sql .= " WHERE r.libelle NOT LIKE 'tou%'"; // On ignore droits "tous"
749
    $sql .= " AND entity = " . ((int)$conf->entity);
750
    $sql .= " AND bydefault = 1";
751
    if (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) {
752
        $sql .= " AND r.perms NOT LIKE '%_advance'"; // Hide advanced perms if option is not enabled
753
    }
754
    $resql = $db->query($sql);
755
    if ($resql) {
756
        $obj = $db->fetch_object($resql);
757
        if ($obj) {
758
            $nbPerms = $obj->nb;
759
        }
760
    } else {
761
        dol_print_error($db);
762
    }
763
764
    if (getDolGlobalString('MAIN_SECURITY_USE_DEFAULT_PERMISSIONS')) {
765
        $head[$h][0] = constant('BASE_URL') . "/admin/perms.php";
766
        $head[$h][1] = $langs->trans("DefaultRights");
767
        if ($nbPerms > 0) {
768
            $head[$h][1] .= (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') ? '<span class="badge marginleftonlyshort">' . $nbPerms . '</span>' : '');
769
        }
770
        $head[$h][2] = 'default';
771
        $h++;
772
    }
773
774
    return $head;
775
}
776
777
/**
778
 * Prepare array with list of tabs
779
 *
780
 * @param object $object Descriptor class
781
 * @return  array               Array of tabs to show
782
 */
783
function modulehelp_prepare_head($object)
784
{
785
    global $langs, $conf;
786
    $h = 0;
787
    $head = array();
788
789
    // FIX for compatibility habitual tabs
790
    $object->id = $object->numero;
791
792
    $head[$h][0] = constant('BASE_URL') . "/admin/modulehelp.php?id=" . $object->id . '&mode=desc';
793
    $head[$h][1] = $langs->trans("Description");
794
    $head[$h][2] = 'desc';
795
    $h++;
796
797
    $head[$h][0] = constant('BASE_URL') . "/admin/modulehelp.php?id=" . $object->id . '&mode=feature';
798
    $head[$h][1] = $langs->trans("TechnicalServicesProvided");
799
    $head[$h][2] = 'feature';
800
    $h++;
801
802
    if ($object->isCoreOrExternalModule() == 'external') {
803
        $head[$h][0] = constant('BASE_URL') . "/admin/modulehelp.php?id=" . $object->id . '&mode=changelog';
804
        $head[$h][1] = $langs->trans("ChangeLog");
805
        $head[$h][2] = 'changelog';
806
        $h++;
807
    }
808
809
    complete_head_from_modules($conf, $langs, $object, $head, $h, 'modulehelp_admin');
810
811
    complete_head_from_modules($conf, $langs, $object, $head, $h, 'modulehelp_admin', 'remove');
812
813
814
    return $head;
815
}
816
817
/**
818
 * Prepare array with list of tabs
819
 *
820
 * @return  array               Array of tabs to show
821
 */
822
function translation_prepare_head()
823
{
824
    global $langs, $conf;
825
    $h = 0;
826
    $head = array();
827
828
    $head[$h][0] = constant('BASE_URL') . "/admin/translation.php?mode=searchkey";
829
    $head[$h][1] = $langs->trans("TranslationKeySearch");
830
    $head[$h][2] = 'searchkey';
831
    $h++;
832
833
    $head[$h][0] = constant('BASE_URL') . "/admin/translation.php?mode=overwrite";
834
    $head[$h][1] = '<span class="valignmiddle">' . $langs->trans("TranslationOverwriteKey") . '</span><span class="fa fa-plus-circle valignmiddle paddingleft"></span>';
835
    $head[$h][2] = 'overwrite';
836
    $h++;
837
838
    complete_head_from_modules($conf, $langs, null, $head, $h, 'translation_admin');
839
840
    complete_head_from_modules($conf, $langs, null, $head, $h, 'translation_admin', 'remove');
841
842
843
    return $head;
844
}
845
846
847
/**
848
 * Prepare array with list of tabs
849
 *
850
 * @return  array               Array of tabs to show
851
 */
852
function defaultvalues_prepare_head()
853
{
854
    global $langs, $conf, $user;
855
    $h = 0;
856
    $head = array();
857
858
    $head[$h][0] = constant('BASE_URL') . "/admin/defaultvalues.php?mode=createform";
859
    $head[$h][1] = $langs->trans("DefaultCreateForm");
860
    $head[$h][2] = 'createform';
861
    $h++;
862
863
    $head[$h][0] = constant('BASE_URL') . "/admin/defaultvalues.php?mode=filters";
864
    $head[$h][1] = $langs->trans("DefaultSearchFilters");
865
    $head[$h][2] = 'filters';
866
    $h++;
867
868
    $head[$h][0] = constant('BASE_URL') . "/admin/defaultvalues.php?mode=sortorder";
869
    $head[$h][1] = $langs->trans("DefaultSortOrder");
870
    $head[$h][2] = 'sortorder';
871
    $h++;
872
873
    if (!empty($conf->use_javascript_ajax)) {
874
        $head[$h][0] = constant('BASE_URL') . "/admin/defaultvalues.php?mode=focus";
875
        $head[$h][1] = $langs->trans("DefaultFocus");
876
        $head[$h][2] = 'focus';
877
        $h++;
878
879
        $head[$h][0] = constant('BASE_URL') . "/admin/defaultvalues.php?mode=mandatory";
880
        $head[$h][1] = $langs->trans("DefaultMandatory");
881
        $head[$h][2] = 'mandatory';
882
        $h++;
883
    }
884
885
    /*$head[$h][0] = constant('BASE_URL')."/admin/translation.php?mode=searchkey";
886
    $head[$h][1] = $langs->trans("TranslationKeySearch");
887
    $head[$h][2] = 'searchkey';
888
    $h++;*/
889
890
    complete_head_from_modules($conf, $langs, null, $head, $h, 'defaultvalues_admin');
891
892
    complete_head_from_modules($conf, $langs, null, $head, $h, 'defaultvalues_admin', 'remove');
893
894
895
    return $head;
896
}
897
898
899
/**
900
 *  Return list of session
901
 *
902
 * @return array<string,array{login:string,age:int,creation:int,modification:int,raw:string}>  Array list of sessions
903
 */
904
function listOfSessions()
905
{
906
    global $conf;
907
908
    $arrayofSessions = array();
909
    // session.save_path can be returned empty so we set a default location and work from there
910
    $sessPath = '/tmp';
911
    $iniPath = ini_get("session.save_path");
912
    if ($iniPath) {
913
        $sessPath = $iniPath;
914
    }
915
    $sessPath .= '/'; // We need the trailing slash
916
    dol_syslog('admin.lib:listOfSessions sessPath=' . $sessPath);
917
918
    $dh = @opendir(dol_osencode($sessPath));
919
    if ($dh) {
920
        while (($file = @readdir($dh)) !== false) {
921
            if (preg_match('/^sess_/i', $file) && $file != "." && $file != "..") {
922
                $fullpath = $sessPath . $file;
923
                if (!@is_dir($fullpath) && is_readable($fullpath)) {
924
                    $sessValues = file_get_contents($fullpath); // get raw session data
925
                    // Example of possible value
926
                    //$sessValues = 'newtoken|s:32:"1239f7a0c4b899200fe9ca5ea394f307";dol_loginmesg|s:0:"";newtoken|s:32:"1236457104f7ae0f328c2928973f3cb5";dol_loginmesg|s:0:"";token|s:32:"123615ad8d650c5cc4199b9a1a76783f";
927
                    // dol_login|s:5:"admin";dol_authmode|s:8:"dolibarr";dol_tz|s:1:"1";dol_tz_string|s:13:"Europe/Berlin";dol_dst|i:0;dol_dst_observed|s:1:"1";dol_dst_first|s:0:"";dol_dst_second|s:0:"";dol_screenwidth|s:4:"1920";
928
                    // dol_screenheight|s:3:"971";dol_company|s:12:"MyBigCompany";dol_entity|i:1;mainmenu|s:4:"home";leftmenuopened|s:10:"admintools";idmenu|s:0:"";leftmenu|s:10:"admintools";';
929
930
                    if (
931
                        preg_match('/dol_login/i', $sessValues) && // limit to dolibarr session
932
                        (preg_match('/dol_entity\|i:' . $conf->entity . ';/i', $sessValues) || preg_match('/dol_entity\|s:([0-9]+):"' . $conf->entity . '"/i', $sessValues)) && // limit to current entity
933
                        preg_match('/dol_company\|s:([0-9]+):"(' . getDolGlobalString('MAIN_INFO_SOCIETE_NOM') . ')"/i', $sessValues)
934
                    ) { // limit to company name
935
                        $tmp = explode('_', $file);
936
                        $idsess = $tmp[1];
937
                        $regs = array();
938
                        $loginfound = preg_match('/dol_login\|s:[0-9]+:"([A-Za-z0-9]+)"/i', $sessValues, $regs);
939
                        if ($loginfound) {
940
                            $arrayofSessions[$idsess]["login"] = $regs[1];
941
                        }
942
                        $arrayofSessions[$idsess]["age"] = time() - filectime($fullpath);
943
                        $arrayofSessions[$idsess]["creation"] = filectime($fullpath);
944
                        $arrayofSessions[$idsess]["modification"] = filemtime($fullpath);
945
                        $arrayofSessions[$idsess]["raw"] = $sessValues;
946
                    }
947
                }
948
            }
949
        }
950
        @closedir($dh);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for closedir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

950
        /** @scrutinizer ignore-unhandled */ @closedir($dh);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
951
    }
952
953
    return $arrayofSessions;
954
}
955
956
/**
957
 *  Purge existing sessions
958
 *
959
 * @param int $mysessionid To avoid to try to delete my own session
960
 * @return     int                         >0 if OK, <0 if KO
961
 */
962
function purgeSessions($mysessionid)
963
{
964
    global $conf;
965
966
    $sessPath = ini_get("session.save_path") . "/";
967
    dol_syslog('admin.lib:purgeSessions mysessionid=' . $mysessionid . ' sessPath=' . $sessPath);
968
969
    $error = 0;
970
971
    $dh = @opendir(dol_osencode($sessPath));
972
    if ($dh) {
973
        while (($file = @readdir($dh)) !== false) {
974
            if ($file != "." && $file != "..") {
975
                $fullpath = $sessPath . $file;
976
                if (!@is_dir($fullpath)) {
977
                    $sessValues = file_get_contents($fullpath); // get raw session data
978
979
                    if (
980
                        preg_match('/dol_login/i', $sessValues) && // limit to dolibarr session
981
                        preg_match('/dol_entity\|s:([0-9]+):"(' . $conf->entity . ')"/i', $sessValues) && // limit to current entity
982
                        preg_match('/dol_company\|s:([0-9]+):"(' . getDolGlobalString('MAIN_INFO_SOCIETE_NOM') . ')"/i', $sessValues)
983
                    ) { // limit to company name
984
                        $tmp = explode('_', $file);
985
                        $idsess = $tmp[1];
986
                        // We remove session if it's not ourself
987
                        if ($idsess != $mysessionid) {
988
                            $res = @unlink($fullpath);
989
                            if (!$res) {
990
                                $error++;
991
                            }
992
                        }
993
                    }
994
                }
995
            }
996
        }
997
        @closedir($dh);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for closedir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

997
        /** @scrutinizer ignore-unhandled */ @closedir($dh);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
998
    }
999
1000
    if (!$error) {
1001
        return 1;
1002
    } else {
1003
        return -$error;
1004
    }
1005
}
1006
1007
1008
/**
1009
 *  Enable a module
1010
 *
1011
 * @param string $value Name of module to activate
1012
 * @param int $withdeps Activate/Disable also all dependencies
1013
 * @param int $noconfverification Remove verification of $conf variable for module
1014
 * @return     array{nbmodules?:int,errors:string[],nbperms?:int}  array('nbmodules'=>nb modules activated with success, 'errors=>array of error messages, 'nbperms'=>Nb permission added);
1015
 */
1016
function activateModule($value, $withdeps = 1, $noconfverification = 0)
1017
{
1018
    global $db, $langs, $conf, $mysoc;
1019
1020
    $ret = array();
1021
1022
    // Check parameters
1023
    if (empty($value)) {
1024
        $ret['errors'] = array('ErrorBadParameter');
1025
        return $ret;
1026
    }
1027
1028
    $ret = array('nbmodules' => 0, 'errors' => array(), 'nbperms' => 0);
1029
    $modName = $value;
1030
    /*
1031
    $modFile = $modName . ".class.php";
1032
1033
    // Loop on each directory to fill $modulesdir
1034
    $modulesdir = dolGetModulesDirs();
1035
1036
    // Loop on each modulesdir directories
1037
    $found = false;
1038
    foreach ($modulesdir as $dir) {
1039
        if (file_exists($dir . $modFile)) {
1040
            $found = @include_once $dir . $modFile;
1041
            if ($found) {
1042
                break;
1043
            }
1044
        }
1045
    }
1046
1047
    $objMod = new $modName($db);
1048
    */
1049
    $objMod = DolibarrModules::getModule($modName);
1050
1051
    // Test if PHP version ok
1052
    $verphp = Version::arrayPhp();
1053
    $vermin = isset($objMod->phpmin) ? $objMod->phpmin : 0;
1054
    if (is_array($vermin) && Version::compare($verphp, $vermin) < 0) {
1055
        $ret['errors'][] = $langs->trans("ErrorModuleRequirePHPVersion", Version::toString($vermin));
1056
        return $ret;
1057
    }
1058
1059
    // Test if Dolibarr version ok
1060
    $verdol = Version::toArray();
1061
    $vermin = isset($objMod->need_dolibarr_version) ? $objMod->need_dolibarr_version : 0;
1062
    //print 'version: '.Version::compare($verdol,$vermin).' - '.join(',',$verdol).' - '.join(',',$vermin);exit;
1063
    if (is_array($vermin) && Version::compare($verdol, $vermin) < 0) {
1064
        $ret['errors'][] = $langs->trans("ErrorModuleRequireDolibarrVersion", Version::toString($vermin));
1065
        return $ret;
1066
    }
1067
1068
    // Test if javascript requirement ok
1069
    if (!empty($objMod->need_javascript_ajax) && empty($conf->use_javascript_ajax)) {
1070
        $ret['errors'][] = $langs->trans("ErrorModuleRequireJavascript");
1071
        return $ret;
1072
    }
1073
1074
    $const_name = $objMod->const_name;
1075
    if ($noconfverification == 0) {
1076
        if (getDolGlobalString($const_name)) {
1077
            return $ret;
1078
        }
1079
    }
1080
1081
    $result = $objMod->init(); // Enable module
1082
1083
    if ($result <= 0) {
1084
        $ret['errors'][] = $objMod->error;
1085
    } else {
1086
        if ($withdeps) {
1087
            if (isset($objMod->depends) && is_array($objMod->depends) && !empty($objMod->depends)) {
1088
                // Activation of modules this module depends on
1089
                // this->depends may be array('modModule1', 'mmodModule2') or array('always'=>array('modModule1'), 'FR'=>array('modModule2"))
1090
                foreach ($objMod->depends as $key => $modulestringorarray) {
1091
                    //var_dump((! is_numeric($key)) && ! preg_match('/^always/', $key) && $mysoc->country_code && ! preg_match('/^'.$mysoc->country_code.'/', $key));exit;
1092
                    if ((!is_numeric($key)) && !preg_match('/^always/', $key) && $mysoc->country_code && !preg_match('/^' . $mysoc->country_code . '/', $key)) {
1093
                        dol_syslog("We are not concerned by dependency with key=" . $key . " because our country is " . $mysoc->country_code);
1094
                        continue;
1095
                    }
1096
1097
                    if (!is_array($modulestringorarray)) {
1098
                        $modulestringorarray = array($modulestringorarray);
1099
                    }
1100
1101
                    foreach ($modulestringorarray as $modulestring) {
1102
                        $module = DolibarrModules::getModule($modulestring);
1103
                        if ($module === null) {
1104
                            continue;
1105
                        }
1106
1107
                        /*
1108
                        $activate = false;
1109
                        $activateerr = '';
1110
                        foreach ($modulesdir as $dir) {
1111
                            if (file_exists($dir . $modulestring . ".class.php")) {
1112
                                $resarray = activateModule($modulestring);
1113
                                if (empty($resarray['errors'])) {
1114
                                    $activate = true;
1115
                                } else {
1116
                                    $activateerr = implode(', ', $resarray['errors']);
1117
                                    foreach ($resarray['errors'] as $errorMessage) {
1118
                                        dol_syslog($errorMessage, LOG_ERR);
1119
                                    }
1120
                                }
1121
                                break;
1122
                            }
1123
                        }
1124
                        */
1125
                        $resarray = activateModule($modulestring);
1126
                        $activate = empty($resArray['errors']);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $resArray does not exist. Did you maybe mean $resarray?
Loading history...
1127
1128
                        if ($activate) {
1129
                            $ret['nbmodules'] += $resarray['nbmodules'];
1130
                            $ret['nbperms'] += $resarray['nbperms'];
1131
                        } else {
1132
                            $activateerr = implode(', ', $resarray['errors']);
1133
                            foreach ($resarray['errors'] as $errorMessage) {
1134
                                dol_syslog($errorMessage, LOG_ERR);
1135
                            }
1136
                            if ($activateerr) {
1137
                                $ret['errors'][] = $activateerr;
1138
                            }
1139
                            $ret['errors'][] = $langs->trans('activateModuleDependNotSatisfied', $objMod->name, $modulestring);
1140
                        }
1141
                    }
1142
                }
1143
            }
1144
1145
            if (isset($objMod->conflictwith) && is_array($objMod->conflictwith) && !empty($objMod->conflictwith)) {
1146
                // Deactivation des modules qui entrent en conflict
1147
                foreach ($objMod->conflictwith as $module_name) {
1148
                    unActivateModule($module_name, 0);
1149
                }
1150
            }
1151
        }
1152
    }
1153
1154
    if (!count($ret['errors'])) {
1155
        $ret['nbmodules']++;
1156
        $ret['nbperms'] += (is_array($objMod->rights) ? count($objMod->rights) : 0);
1157
    }
1158
1159
    return $ret;
1160
}
1161
1162
1163
/**
1164
 *  Disable a module
1165
 *
1166
 * @param string $value Nom du module a desactiver
1167
 * @param int $requiredby 1=Desactive aussi modules dependants
1168
 * @return     string                           Error message or '';
1169
 */
1170
function unActivateModule($value, $requiredby = 1)
1171
{
1172
    // Check parameters
1173
    if (empty($value)) {
1174
        return 'ErrorBadParameter';
1175
    }
1176
1177
    $objMod = DolibarrModules::getModule($value);
1178
    if ($objMod === null) {
1179
        DolibarrModules::forceDeactivate($value);
1180
        return 'ErrorModuleDoesNotExists';
1181
    }
1182
1183
    $ret = 0;
1184
    $result = $objMod->remove();
1185
    if ($result <= 0) {
1186
        $ret = $objMod->error;
1187
    }
1188
1189
    // Disable modules that depends on module we disable
1190
    if (!$ret && $requiredby && is_object($objMod) && is_array($objMod->requiredby)) {
1191
        $countrb = count($objMod->requiredby);
1192
        for ($i = 0; $i < $countrb; $i++) {
1193
            //var_dump($objMod->requiredby[$i]);
1194
            unActivateModule($objMod->requiredby[$i]);
1195
        }
1196
    }
1197
1198
    return $ret;
1199
}
1200
1201
1202
/**
1203
 *  Add external modules to list of dictionaries.
1204
 *  Addition is done into var $taborder, $tabname, etc... that are passed with pointers.
1205
 *
1206
 * @param array $taborder Taborder
1207
 * @param array $tabname Tabname
1208
 * @param array $tablib Tablib
1209
 * @param array $tabsql Tabsql
1210
 * @param array $tabsqlsort Tabsqlsort
1211
 * @param array $tabfield Tabfield
1212
 * @param array $tabfieldvalue Tabfieldvalue
1213
 * @param array $tabfieldinsert Tabfieldinsert
1214
 * @param array $tabrowid Tabrowid
1215
 * @param array $tabcond Tabcond
1216
 * @param array $tabhelp Tabhelp
1217
 * @param array $tabcomplete Tab complete (will replace all other in future). Key is table name.
1218
 * @return     int         1
1219
 */
1220
function complete_dictionary_with_modules(&$taborder, &$tabname, &$tablib, &$tabsql, &$tabsqlsort, &$tabfield, &$tabfieldvalue, &$tabfieldinsert, &$tabrowid, &$tabcond, &$tabhelp, &$tabcomplete)
1221
{
1222
    global $db, $modules, $conf, $langs;
1223
1224
    dol_syslog("complete_dictionary_with_modules Search external modules to complete the list of dictionary tables", LOG_DEBUG, 1);
1225
1226
    // Search modules
1227
    $modulesdir = dolGetModulesDirs();
1228
    $i = 0; // is a sequencer of modules found
1229
    $j = 0; // j is module number. Automatically affected if module number not defined.
1230
1231
    $allModules = DolibarrModules::getModules($modulesdir);
1232
    foreach ($allModules as $modName => $filename) {
1233
        $objMod = DolibarrModules::getObj($db, $modName, $filename);
1234
        if (!isset($objMod)) {
1235
            print info_admin("admin/modules.php Warning bad descriptor file : " . $filename . " (Class " . $modName . " not found into file)", 0, 0, '1', 'warning');
1236
            continue;
1237
        }
1238
1239
        if ($objMod->numero > 0) {
1240
            $j = $objMod->numero;
1241
        } else {
1242
            $j = 1000 + $i;
1243
        }
1244
1245
        $modulequalified = 1;
1246
1247
        // We discard modules according to features level (PS: if module is activated we always show it)
1248
        $const_name = 'MAIN_MODULE_' . strtoupper(preg_replace('/^mod/i', '', get_only_class($objMod)));
1249
        if ($objMod->version == 'development' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2 && !getDolGlobalString($const_name)) {
1250
            $modulequalified = 0;
1251
        }
1252
        if ($objMod->version == 'experimental' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1 && !getDolGlobalString($const_name)) {
1253
            $modulequalified = 0;
1254
        }
1255
        // If module is not activated disqualified
1256
        if (!getDolGlobalString($const_name)) {
1257
            $modulequalified = 0;
1258
        }
1259
1260
        if ($modulequalified) {
1261
            // Load languages files of module
1262
            if (isset($objMod->langfiles) && is_array($objMod->langfiles)) {
1263
                foreach ($objMod->langfiles as $langfile) {
1264
                    $langs->load($langfile);
1265
                }
1266
            }
1267
1268
            // phpcs:disable
1269
            // Complete the arrays &$tabname,&$tablib,&$tabsql,&$tabsqlsort,&$tabfield,&$tabfieldvalue,&$tabfieldinsert,&$tabrowid,&$tabcond
1270
            if (empty($objMod->dictionaries) && !empty($objMod->{"dictionnaries"})) {
1271
                $objMod->dictionaries = $objMod->{"dictionnaries"}; // For backward compatibility
1272
            }
1273
            // phpcs:enable
1274
1275
            if (!empty($objMod->dictionaries)) {
1276
                //var_dump($objMod->dictionaries['tabname']);
1277
                $nbtabname = $nbtablib = $nbtabsql = $nbtabsqlsort = $nbtabfield = $nbtabfieldvalue = $nbtabfieldinsert = $nbtabrowid = $nbtabcond = $nbtabfieldcheck = $nbtabhelp = 0;
1278
                $tabnamerelwithkey = array();
1279
                foreach ($objMod->dictionaries['tabname'] as $key => $val) {
1280
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $val);
1281
                    $nbtabname++;
1282
                    $taborder[] = max($taborder) + 1;
1283
                    $tabname[] = $val;
1284
                    $tabnamerelwithkey[$key] = $val;
1285
                    $tabcomplete[$tmptablename]['picto'] = $objMod->picto;
1286
                }       // Position
1287
                foreach ($objMod->dictionaries['tablib'] as $key => $val) {
1288
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1289
                    $nbtablib++;
1290
                    $tablib[] = $val;
1291
                    $tabcomplete[$tmptablename]['lib'] = $val;
1292
                }
1293
                foreach ($objMod->dictionaries['tabsql'] as $key => $val) {
1294
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1295
                    $nbtabsql++;
1296
                    $tabsql[] = $val;
1297
                    $tabcomplete[$tmptablename]['sql'] = $val;
1298
                }
1299
                foreach ($objMod->dictionaries['tabsqlsort'] as $key => $val) {
1300
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1301
                    $nbtabsqlsort++;
1302
                    $tabsqlsort[] = $val;
1303
                    $tabcomplete[$tmptablename]['sqlsort'] = $val;
1304
                }
1305
                foreach ($objMod->dictionaries['tabfield'] as $key => $val) {
1306
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1307
                    $nbtabfield++;
1308
                    $tabfield[] = $val;
1309
                    $tabcomplete[$tmptablename]['field'] = $val;
1310
                }
1311
                foreach ($objMod->dictionaries['tabfieldvalue'] as $key => $val) {
1312
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1313
                    $nbtabfieldvalue++;
1314
                    $tabfieldvalue[] = $val;
1315
                    $tabcomplete[$tmptablename]['value'] = $val;
1316
                }
1317
                foreach ($objMod->dictionaries['tabfieldinsert'] as $key => $val) {
1318
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1319
                    $nbtabfieldinsert++;
1320
                    $tabfieldinsert[] = $val;
1321
                    $tabcomplete[$tmptablename]['fieldinsert'] = $val;
1322
                }
1323
                foreach ($objMod->dictionaries['tabrowid'] as $key => $val) {
1324
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1325
                    $nbtabrowid++;
1326
                    $tabrowid[] = $val;
1327
                    $tabcomplete[$tmptablename]['rowid'] = $val;
1328
                }
1329
                foreach ($objMod->dictionaries['tabcond'] as $key => $val) {
1330
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1331
                    $nbtabcond++;
1332
                    $tabcond[] = $val;
1333
                    $tabcomplete[$tmptablename]['rowid'] = $val;
1334
                }
1335
                if (!empty($objMod->dictionaries['tabhelp'])) {
1336
                    foreach ($objMod->dictionaries['tabhelp'] as $key => $val) {
1337
                        $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1338
                        $nbtabhelp++;
1339
                        $tabhelp[] = $val;
1340
                        $tabcomplete[$tmptablename]['help'] = $val;
1341
                    }
1342
                }
1343
                if (!empty($objMod->dictionaries['tabfieldcheck'])) {
1344
                    foreach ($objMod->dictionaries['tabfieldcheck'] as $key => $val) {
1345
                        $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1346
                        $nbtabfieldcheck++;
1347
                        $tabcomplete[$tmptablename]['fieldcheck'] = $val;
1348
                    }
1349
                }
1350
1351
                if ($nbtabname != $nbtablib || $nbtablib != $nbtabsql || $nbtabsql != $nbtabsqlsort) {
1352
                    print 'Error in descriptor of module ' . $const_name . '. Array ->dictionaries has not same number of record for key "tabname", "tablib", "tabsql" and "tabsqlsort"';
1353
                    //print "$const_name: $nbtabname=$nbtablib=$nbtabsql=$nbtabsqlsort=$nbtabfield=$nbtabfieldvalue=$nbtabfieldinsert=$nbtabrowid=$nbtabcond=$nbtabfieldcheck=$nbtabhelp\n";
1354
                } else {
1355
                    $taborder[] = 0; // Add an empty line
1356
                }
1357
            }
1358
1359
            $j++;
1360
            $i++;
1361
        } else {
1362
            dol_syslog("Module " . get_only_class($objMod) . " not qualified");
1363
        }
1364
    }
1365
1366
    dol_syslog("", LOG_DEBUG, -1);
1367
1368
    return 1;
1369
}
1370
1371
/**
1372
 *  Activate external modules mandatory when country is country_code
1373
 *
1374
 * @param string $country_code CountryCode
1375
 * @return     int         1
1376
 */
1377
function activateModulesRequiredByCountry($country_code)
1378
{
1379
    global $db, $conf, $langs;
1380
1381
    $modulesdir = dolGetModulesDirs();
1382
    $allModules = DolibarrModules::getModules($modulesdir);
1383
    foreach ($allModules as $modName => $filename) {
1384
        $objMod = DolibarrModules::getObj($db, $modName, $filename);
1385
        if (!isset($objMod)) {
1386
            print info_admin("admin/modules.php Warning bad descriptor file : " . $filename . " (Class " . $modName . " not found into file)", 0, 0, '1', 'warning');
1387
            continue;
1388
        }
1389
1390
        $modulequalified = 1;
1391
1392
        // We discard modules according to features level (PS: if module is activated we always show it)
1393
        $const_name = 'MAIN_MODULE_' . strtoupper(preg_replace('/^mod/i', '', get_only_class($objMod)));
1394
1395
        if ($objMod->version == 'development' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2) {
1396
            $modulequalified = 0;
1397
        }
1398
        if ($objMod->version == 'experimental' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1) {
1399
            $modulequalified = 0;
1400
        }
1401
        if (getDolGlobalString($const_name)) {
1402
            $modulequalified = 0; // already activated
1403
        }
1404
1405
        if ($modulequalified) {
1406
            // Load languages files of module
1407
            if (isset($objMod->automatic_activation) && is_array($objMod->automatic_activation) && isset($objMod->automatic_activation[$country_code])) {
1408
                activateModule($modName);
1409
1410
                setEventMessages($objMod->automatic_activation[$country_code], null, 'warnings');
1411
            }
1412
        } else {
1413
            dol_syslog("Module " . get_only_class($objMod) . " not qualified");
1414
        }
1415
    }
1416
1417
    return 1;
1418
}
1419
1420
/**
1421
 *  Search external modules to complete the list of contact element
1422
 *
1423
 * @param array<string,string> $elementList elementList
1424
 * @return     int         1
1425
 */
1426
function complete_elementList_with_modules(&$elementList)
1427
{
1428
    global $db, $modules, $conf, $langs;
1429
1430
    // Search modules
1431
    $filename = array();
1432
    $modules = array();
1433
    $orders = array();
1434
    $categ = array();
1435
    $dirmod = array();
1436
1437
    $i = 0; // is a sequencer of modules found
1438
    $j = 0; // j is module number. Automatically affected if module number not defined.
1439
1440
    dol_syslog("complete_elementList_with_modules Search external modules to complete the list of contact element", LOG_DEBUG, 1);
1441
1442
    $modulesdir = dolGetModulesDirs();
1443
    $allModules = DolibarrModules::getModules($modulesdir);
1444
    foreach ($allModules as $modName => $filename) {
1445
        $objMod = DolibarrModules::getObj($db, $modName, $filename);
1446
1447
        if (!isset($objMod)) {
1448
            print info_admin("admin/modules.php Warning bad descriptor file : " . $filename . " (Class " . $modName . " not found into file)", 0, 0, '1', 'warning');
1449
            continue;
1450
        }
1451
1452
        if ($objMod->numero > 0) {
1453
            $j = $objMod->numero;
1454
        } else {
1455
            $j = 1000 + $i;
1456
        }
1457
1458
        $modulequalified = 1;
1459
1460
        // We discard modules according to features level (PS: if module is activated we always show it)
1461
        $const_name = 'MAIN_MODULE_' . strtoupper(preg_replace('/^mod/i', '', get_only_class($objMod)));
1462
        if ($objMod->version == 'development' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2 && getDolGlobalString($const_name)) {
1463
            $modulequalified = 0;
1464
        }
1465
        if ($objMod->version == 'experimental' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1 && getDolGlobalString($const_name)) {
1466
            $modulequalified = 0;
1467
        }
1468
        // If module is not activated disqualified
1469
        if (!getDolGlobalString($const_name)) {
1470
            $modulequalified = 0;
1471
        }
1472
1473
        if ($modulequalified) {
1474
            // Load languages files of module
1475
            if (isset($objMod->langfiles) && is_array($objMod->langfiles)) {
1476
                foreach ($objMod->langfiles as $langfile) {
1477
                    $langs->load($langfile);
1478
                }
1479
            }
1480
1481
            $modules[$i] = $objMod;
1482
            $filename[$i] = $modName;
1483
            $orders[$i] = $objMod->family . "_" . $j; // Sort on family then module number
1484
            $dirmod[$i] = $dir;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dir seems to be never defined.
Loading history...
1485
            //print "x".$modName." ".$orders[$i]."\n<br>";
1486
1487
            if (!empty($objMod->module_parts['contactelement'])) {
1488
                if (is_array($objMod->module_parts['contactelement'])) {
1489
                    foreach ($objMod->module_parts['contactelement'] as $elem => $title) {
1490
                        $elementList[$elem] = $langs->trans($title);
1491
                    }
1492
                } else {
1493
                    $elementList[$objMod->name] = $langs->trans($objMod->name);
1494
                }
1495
            }
1496
1497
            $j++;
1498
            $i++;
1499
        } else {
1500
            dol_syslog("Module " . get_only_class($objMod) . " not qualified");
1501
        }
1502
    }
1503
1504
    dol_syslog("", LOG_DEBUG, -1);
1505
1506
    return 1;
1507
}
1508
1509
/**
1510
 *  Show array with constants to edit
1511
 *
1512
 * @param array<string,array{type:string,label:string}> $tableau Array of constants array('key'=>array('type'=>type, 'label'=>label)
1513
 *                                                                          where type can be 'string', 'text', 'textarea', 'html', 'yesno', 'emailtemplate:xxx', ...
1514
 * @param int<2,3> $strictw3c 0=Include form into table (deprecated), 1=Form is outside table to respect W3C (deprecated), 2=No form nor button at all, 3=No form nor button at all and each field has a unique name (form is output by caller, recommended)  (typed as int<2,3> to highlight the deprecated values)
1515
 * @param string $helptext Tooltip help to use for the column name of values
1516
 * @param string $text Text to use for the column name of values
1517
 * @return void
1518
 */
1519
function form_constantes($tableau, $strictw3c = 2, $helptext = '', $text = 'Value')
1520
{
1521
    global $db, $langs, $conf, $user;
1522
    global $_Avery_Labels;
1523
1524
    $form = new Form($db);
1525
1526
    if (empty($strictw3c)) {
1527
        dol_syslog("Warning: Function 'form_constantes' was called with parameter strictw3c = 0, this is deprecated. Value must be 2 now.", LOG_DEBUG);
1528
    }
1529
    if (!empty($strictw3c) && $strictw3c == 1) {
1530
        print "\n" . '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
1531
        print '<input type="hidden" name="token" value="' . newToken() . '">';
1532
        print '<input type="hidden" name="action" value="updateall">';
1533
    }
1534
1535
    print '<div class="div-table-responsive-no-min">';
1536
    print '<table class="noborder centpercent">';
1537
    print '<tr class="liste_titre">';
1538
    print '<td class="">' . $langs->trans("Description") . '</td>';
1539
    print '<td>';
1540
    $text = $langs->trans($text);
1541
    print $form->textwithpicto($text, $helptext, 1, 'help', '', 0, 2, 'idhelptext');
1542
    print '</td>';
1543
    if (empty($strictw3c)) {
1544
        print '<td class="center" width="80">' . $langs->trans("Action") . '</td>';
1545
    }
1546
    print "</tr>\n";
1547
1548
    $label = '';
1549
    foreach ($tableau as $key => $const) {  // Loop on each param
1550
        $label = '';
1551
        // $const is a const key like 'MYMODULE_ABC'
1552
        if (is_numeric($key)) {     // Very old behaviour
1553
            $type = 'string';
1554
        } else {
1555
            if (is_array($const)) {
1556
                $type = $const['type'];
1557
                $label = $const['label'];
1558
                $const = $key;
1559
            } else {
1560
                $type = $const;
1561
                $const = $key;
1562
            }
1563
        }
1564
1565
        $sql = "SELECT ";
1566
        $sql .= "rowid";
1567
        $sql .= ", " . $db->decrypt('name') . " as name";
1568
        $sql .= ", " . $db->decrypt('value') . " as value";
1569
        $sql .= ", type";
1570
        $sql .= ", note";
1571
        $sql .= " FROM " . MAIN_DB_PREFIX . "const";
1572
        $sql .= " WHERE " . $db->decrypt('name') . " = '" . $db->escape($const) . "'";
1573
        $sql .= " AND entity IN (0, " . $conf->entity . ")";
1574
        $sql .= " ORDER BY name ASC, entity DESC";
1575
        $result = $db->query($sql);
1576
1577
        dol_syslog("List params", LOG_DEBUG);
1578
        if ($result) {
1579
            $obj = $db->fetch_object($result); // Take first result of select
1580
1581
            if (empty($obj)) {  // If not yet into table
1582
                $obj = (object)array('rowid' => '', 'name' => $const, 'value' => '', 'type' => $type, 'note' => '');
1583
            }
1584
1585
            if (empty($strictw3c)) {
1586
                print "\n" . '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
1587
                print '<input type="hidden" name="token" value="' . newToken() . '">';
1588
                print '<input type="hidden" name="page_y" value="' . newToken() . '">';
1589
            }
1590
1591
            print '<tr class="oddeven">';
1592
1593
            // Show label of parameter
1594
            print '<td>';
1595
            if (empty($strictw3c)) {
1596
                print '<input type="hidden" name="action" value="update">';
1597
            }
1598
            print '<input type="hidden" name="rowid' . (empty($strictw3c) ? '' : '[]') . '" value="' . $obj->rowid . '">';
1599
            print '<input type="hidden" name="constname' . (empty($strictw3c) ? '' : '[]') . '" value="' . $const . '">';
1600
            print '<input type="hidden" name="constnote_' . $obj->name . '" value="' . nl2br(dol_escape_htmltag($obj->note)) . '">';
1601
            print '<input type="hidden" name="consttype_' . $obj->name . '" value="' . ($obj->type ? $obj->type : 'string') . '">';
1602
            if (!empty($tableau[$key]['tooltip'])) {
1603
                print $form->textwithpicto($label ? $label : $langs->trans('Desc' . $const), $tableau[$key]['tooltip']);
1604
            } else {
1605
                print($label ? $label : $langs->trans('Desc' . $const));
1606
            }
1607
1608
            if ($const == 'ADHERENT_MAILMAN_URL') {
1609
                print '. ' . $langs->trans("Example") . ': <a href="#" id="exampleclick1">' . img_down() . '</a><br>';
1610
                //print 'http://lists.example.com/cgi-bin/mailman/admin/%LISTE%/members?adminpw=%MAILMAN_ADMINPW%&subscribees=%EMAIL%&send_welcome_msg_to_this_batch=1';
1611
                print '<div id="example1" class="hidden">';
1612
                print 'http://lists.example.com/cgi-bin/mailman/admin/%LISTE%/members/add?subscribees_upload=%EMAIL%&amp;adminpw=%MAILMAN_ADMINPW%&amp;subscribe_or_invite=0&amp;send_welcome_msg_to_this_batch=0&amp;notification_to_list_owner=0';
1613
                print '</div>';
1614
            } elseif ($const == 'ADHERENT_MAILMAN_UNSUB_URL') {
1615
                print '. ' . $langs->trans("Example") . ': <a href="#" id="exampleclick2">' . img_down() . '</a><br>';
1616
                print '<div id="example2" class="hidden">';
1617
                print 'http://lists.example.com/cgi-bin/mailman/admin/%LISTE%/members/remove?unsubscribees_upload=%EMAIL%&amp;adminpw=%MAILMAN_ADMINPW%&amp;send_unsub_ack_to_this_batch=0&amp;send_unsub_notifications_to_list_owner=0';
1618
                print '</div>';
1619
                //print 'http://lists.example.com/cgi-bin/mailman/admin/%LISTE%/members/remove?adminpw=%MAILMAN_ADMINPW%&unsubscribees=%EMAIL%';
1620
            } elseif ($const == 'ADHERENT_MAILMAN_LISTS') {
1621
                print '. ' . $langs->trans("Example") . ': <a href="#" id="exampleclick3">' . img_down() . '</a><br>';
1622
                print '<div id="example3" class="hidden">';
1623
                print 'mymailmanlist<br>';
1624
                print 'mymailmanlist1,mymailmanlist2<br>';
1625
                print 'TYPE:Type1:mymailmanlist1,TYPE:Type2:mymailmanlist2<br>';
1626
                if (isModEnabled('category')) {
1627
                    print 'CATEG:Categ1:mymailmanlist1,CATEG:Categ2:mymailmanlist2<br>';
1628
                }
1629
                print '</div>';
1630
                //print 'http://lists.example.com/cgi-bin/mailman/admin/%LISTE%/members/remove?adminpw=%MAILMAN_ADMINPW%&unsubscribees=%EMAIL%';
1631
            } elseif (in_array($const, ['ADHERENT_MAIL_FROM', 'ADHERENT_CC_MAIL_FROM'])) {
1632
                print ' ' . img_help(1, $langs->trans("EMailHelpMsgSPFDKIM"));
1633
            }
1634
1635
            print "</td>\n";
1636
1637
            // Value
1638
            if ($const == 'ADHERENT_CARD_TYPE' || $const == 'ADHERENT_ETIQUETTE_TYPE') {
1639
                print '<td>';
1640
                $arrayoflabels = array();
1641
                $_Avery_Labels = AveryLabels::getAveryLabels();
1642
                foreach (array_keys($_Avery_Labels) as $codecards) {
1643
                    $arrayoflabels[$codecards] = $_Avery_Labels[$codecards]['name'];
1644
                }
1645
                print $form->selectarray('constvalue' . (empty($strictw3c) ? '' : ($strictw3c == 3 ? '_' . $const : '[]')), $arrayoflabels, ($obj->value ? $obj->value : 'CARD'), 1, 0, 0);
1646
                print '<input type="hidden" name="consttype" value="yesno">';
1647
                print '<input type="hidden" name="constnote' . (empty($strictw3c) ? '' : '[]') . '" value="' . nl2br(dol_escape_htmltag($obj->note)) . '">';
1648
                print '</td>';
1649
            } else {
1650
                print '<td>';
1651
                print '<input type="hidden" name="consttype' . (empty($strictw3c) ? '' : ($strictw3c == 3 ? '_' . $const : '[]')) . '" value="' . ($obj->type ? $obj->type : 'string') . '">';
1652
                print '<input type="hidden" name="constnote' . (empty($strictw3c) ? '' : ($strictw3c == 3 ? '_' . $const : '[]')) . '" value="' . nl2br(dol_escape_htmltag($obj->note)) . '">';
1653
                if ($obj->type == 'textarea' || in_array($const, array('ADHERENT_CARD_TEXT', 'ADHERENT_CARD_TEXT_RIGHT', 'ADHERENT_ETIQUETTE_TEXT'))) {
1654
                    print '<textarea class="flat" name="constvalue' . (empty($strictw3c) ? '' : ($strictw3c == 3 ? '_' . $const : '[]')) . '" cols="50" rows="5" wrap="soft">' . "\n";
1655
                    print $obj->value;
1656
                    print "</textarea>\n";
1657
                } elseif ($obj->type == 'html') {
1658
                    $doleditor = new DolEditor('constvalue' . (empty($strictw3c) ? '' : ($strictw3c == 3 ? '_' . $const : '[]')), $obj->value, '', 160, 'dolibarr_notes', '', false, false, isModEnabled('fckeditor'), ROWS_5, '90%');
1659
                    $doleditor->Create();
1660
                } elseif ($obj->type == 'yesno') {
1661
                    print $form->selectyesno('constvalue' . (empty($strictw3c) ? '' : ($strictw3c == 3 ? '_' . $const : '[]')), $obj->value, 1, false, 0, 1);
1662
                } elseif (preg_match('/emailtemplate/', $obj->type)) {
1663
                    $formmail = new FormMail($db);
1664
1665
                    $tmp = explode(':', $obj->type);
1666
1667
                    $nboftemplates = $formmail->fetchAllEMailTemplate($tmp[1], $user, null, -1); // We set lang=null to get in priority record with no lang
1668
                    //$arraydefaultmessage = $formmail->getEMailTemplate($db, $tmp[1], $user, null, 0, 1, '');
1669
                    $arrayofmessagename = array();
1670
                    if (is_array($formmail->lines_model)) {
1671
                        foreach ($formmail->lines_model as $modelmail) {
1672
                            //var_dump($modelmail);
1673
                            $moreonlabel = '';
1674
                            if (!empty($arrayofmessagename[$modelmail->label])) {
1675
                                $moreonlabel = ' <span class="opacitymedium">(' . $langs->trans("SeveralLangugeVariatFound") . ')</span>';
1676
                            }
1677
                            // The 'label' is the key that is unique if we exclude the language
1678
                            $arrayofmessagename[$modelmail->label . ':' . $tmp[1]] = $langs->trans(preg_replace('/\(|\)/', '', $modelmail->label)) . $moreonlabel;
1679
                        }
1680
                    }
1681
                    //var_dump($arraydefaultmessage);
1682
                    //var_dump($arrayofmessagename);
1683
                    print $form->selectarray('constvalue' . (empty($strictw3c) ? '' : ($strictw3c == 3 ? '_' . $const : '[]')), $arrayofmessagename, $obj->value . ':' . $tmp[1], 'None', 0, 0, '', 0, 0, 0, '', '', 1);
1684
                } elseif (preg_match('/MAIL_FROM$/i', $const)) {
1685
                    print img_picto('', 'email', 'class="pictofixedwidth"') . '<input type="text" class="flat minwidth300" name="constvalue' . (empty($strictw3c) ? '' : ($strictw3c == 3 ? '_' . $const : '[]')) . '" value="' . dol_escape_htmltag($obj->value) . '">';
1686
                } else { // type = 'string' ou 'chaine'
1687
                    print '<input type="text" class="flat minwidth300" name="constvalue' . (empty($strictw3c) ? '' : ($strictw3c == 3 ? '_' . $const : '[]')) . '" value="' . dol_escape_htmltag($obj->value) . '">';
1688
                }
1689
                print '</td>';
1690
            }
1691
1692
            // Submit
1693
            if (empty($strictw3c)) {
1694
                print '<td class="center">';
1695
                print '<input type="submit" class="button small reposition" value="' . $langs->trans("Update") . '" name="update">';
1696
                print "</td>";
1697
            }
1698
1699
            print "</tr>\n";
1700
1701
            if (empty($strictw3c)) {
1702
                print "</form>\n";
1703
            }
1704
        }
1705
    }
1706
    print '</table>';
1707
    print '</div>';
1708
1709
    if (!empty($strictw3c) && $strictw3c == 1) {
1710
        print '<div align="center"><input type="submit" class="button small reposition" value="' . $langs->trans("Update") . '" name="update"></div>';
1711
        print "</form>\n";
1712
    }
1713
}
1714
1715
1716
/**
1717
 *  Show array with constants to edit
1718
 *
1719
 * @param DolibarrModules[] $modules Array of all modules
1720
 * @return string                          HTML string with warning
1721
 */
1722
function showModulesExludedForExternal($modules)
1723
{
1724
    global $conf, $langs;
1725
1726
    $text = $langs->trans("OnlyFollowingModulesAreOpenedToExternalUsers");
1727
    $listofmodules = explode(',', getDolGlobalString('MAIN_MODULES_FOR_EXTERNAL')); // List of modules qualified for external user management
1728
1729
    $i = 0;
1730
    if (!empty($modules)) {
1731
        $tmpmodules = dol_sort_array($modules, 'module_position');
1732
        foreach ($tmpmodules as $module) {      // Loop on array of modules
1733
            $moduleconst = $module->const_name;
1734
            $modulename = strtolower($module->name);
1735
            //print 'modulename='.$modulename;
1736
1737
            //if (empty($conf->global->$moduleconst)) continue;
1738
            if (!in_array($modulename, $listofmodules)) {
1739
                continue;
1740
            }
1741
            //var_dump($modulename.' - '.$langs->trans('Module'.$module->numero.'Name'));
1742
1743
            if ($i > 0) {
1744
                $text .= ', ';
1745
            } else {
1746
                $text .= ' ';
1747
            }
1748
            $i++;
1749
1750
            $tmptext = $langs->trans('Module' . $module->numero . 'Name');
1751
            if ($tmptext != 'Module' . $module->numero . 'Name') {
1752
                $text .= $langs->trans('Module' . $module->numero . 'Name');
1753
            } else {
1754
                $text .= $langs->trans($module->name);
1755
            }
1756
        }
1757
    }
1758
1759
    return $text;
1760
}
1761
1762
1763
/**
1764
 *  Add document model used by doc generator
1765
 *
1766
 * @param string $name Model name
1767
 * @param string $type Model type
1768
 * @param string $label Model label
1769
 * @param string $description Model description
1770
 * @return     int                     Return integer <0 if KO, >0 if OK
1771
 */
1772
function addDocumentModel($name, $type, $label = '', $description = '')
1773
{
1774
    global $db, $conf;
1775
1776
    $db->begin();
1777
1778
    $sql = "INSERT INTO " . MAIN_DB_PREFIX . "document_model (nom, type, entity, libelle, description)";
1779
    $sql .= " VALUES ('" . $db->escape($name) . "','" . $db->escape($type) . "'," . ((int)$conf->entity) . ", ";
1780
    $sql .= ($label ? "'" . $db->escape($label) . "'" : 'null') . ", ";
1781
    $sql .= (!empty($description) ? "'" . $db->escape($description) . "'" : "null");
1782
    $sql .= ")";
1783
1784
    dol_syslog("admin.lib::addDocumentModel", LOG_DEBUG);
1785
    $resql = $db->query($sql);
1786
    if ($resql) {
1787
        $db->commit();
1788
        return 1;
1789
    } else {
1790
        dol_print_error($db);
1791
        $db->rollback();
1792
        return -1;
1793
    }
1794
}
1795
1796
/**
1797
 *  Delete document model used by doc generator
1798
 *
1799
 * @param string $name Model name
1800
 * @param string $type Model type
1801
 * @return     int                     Return integer <0 if KO, >0 if OK
1802
 */
1803
function delDocumentModel($name, $type)
1804
{
1805
    global $db, $conf;
1806
1807
    $db->begin();
1808
1809
    $sql = "DELETE FROM " . MAIN_DB_PREFIX . "document_model";
1810
    $sql .= " WHERE nom = '" . $db->escape($name) . "'";
1811
    $sql .= " AND type = '" . $db->escape($type) . "'";
1812
    $sql .= " AND entity = " . ((int)$conf->entity);
1813
1814
    dol_syslog("admin.lib::delDocumentModel", LOG_DEBUG);
1815
    $resql = $db->query($sql);
1816
    if ($resql) {
1817
        $db->commit();
1818
        return 1;
1819
    } else {
1820
        dol_print_error($db);
1821
        $db->rollback();
1822
        return -1;
1823
    }
1824
}
1825
1826
1827
/**
1828
 *  Return the php_info into an array
1829
 *
1830
 * @return     array       Array with PHP infos
1831
 */
1832
function phpinfo_array()
1833
{
1834
    ob_start();
1835
    phpinfo();
1836
    $phpinfostring = ob_get_contents();
1837
    ob_end_clean();
1838
1839
    $info_arr = array();
1840
    $info_lines = explode("\n", strip_tags($phpinfostring, "<tr><td><h2>"));
1841
    $cat = "General";
1842
    foreach ($info_lines as $line) {
1843
        // new cat?
1844
        $title = array();
1845
        preg_match("~<h2>(.*)</h2>~", $line, $title) ? $cat = $title[1] : null;
1846
        $val = array();
1847
        if (preg_match("~<tr><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td></tr>~", $line, $val)) {
1848
            $info_arr[trim($cat)][trim($val[1])] = $val[2];
1849
        } elseif (preg_match("~<tr><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td></tr>~", $line, $val)) {
1850
            $info_arr[trim($cat)][trim($val[1])] = array("local" => $val[2], "master" => $val[3]);
1851
        }
1852
    }
1853
    return $info_arr;
1854
}
1855
1856
/**
1857
 *  Return array head with list of tabs to view object information.
1858
 *
1859
 * @return array                       head array with tabs
1860
 */
1861
function company_admin_prepare_head()
1862
{
1863
    global $langs, $conf;
1864
1865
    $h = 0;
1866
    $head = array();
1867
1868
    $head[$h][0] = constant('BASE_URL') . "/admin/company.php";
1869
    $head[$h][1] = $langs->trans("Company");
1870
    $head[$h][2] = 'company';
1871
    $h++;
1872
1873
    $head[$h][0] = constant('BASE_URL') . "/admin/openinghours.php";
1874
    $head[$h][1] = $langs->trans("OpeningHours");
1875
    $head[$h][2] = 'openinghours';
1876
    $h++;
1877
1878
    $head[$h][0] = constant('BASE_URL') . "/admin/accountant.php";
1879
    $head[$h][1] = $langs->trans("Accountant");
1880
    $head[$h][2] = 'accountant';
1881
    $h++;
1882
1883
    $head[$h][0] = constant('BASE_URL') . "/admin/company_socialnetworks.php";
1884
    $head[$h][1] = $langs->trans("SocialNetworksInformation");
1885
    $head[$h][2] = 'socialnetworks';
1886
    $h++;
1887
1888
    complete_head_from_modules($conf, $langs, null, $head, $h, 'mycompany_admin', 'add');
1889
1890
    complete_head_from_modules($conf, $langs, null, $head, $h, 'mycompany_admin', 'remove');
1891
1892
    return $head;
1893
}
1894
1895
/**
1896
 *  Return array head with list of tabs to view object information.
1897
 *
1898
 * @return array                       head array with tabs
1899
 */
1900
function email_admin_prepare_head()
1901
{
1902
    global $langs, $conf, $user;
1903
1904
    $h = 0;
1905
    $head = array();
1906
1907
    if (!empty($user->admin) && (empty($_SESSION['leftmenu']) || $_SESSION['leftmenu'] != 'email_templates')) {
1908
        $head[$h][0] = constant('BASE_URL') . "/admin/mails.php";
1909
        $head[$h][1] = $langs->trans("OutGoingEmailSetup");
1910
        $head[$h][2] = 'common';
1911
        $h++;
1912
1913
        if (isModEnabled('mailing')) {
1914
            $head[$h][0] = constant('BASE_URL') . "/admin/mails_emailing.php";
1915
            $head[$h][1] = $langs->trans("OutGoingEmailSetupForEmailing", $langs->transnoentitiesnoconv("EMailing"));
1916
            $head[$h][2] = 'common_emailing';
1917
            $h++;
1918
        }
1919
1920
        if (isModEnabled('ticket')) {
1921
            $head[$h][0] = constant('BASE_URL') . "/admin/mails_ticket.php";
1922
            $head[$h][1] = $langs->trans("OutGoingEmailSetupForEmailing", $langs->transnoentitiesnoconv("Ticket"));
1923
            $head[$h][2] = 'common_ticket';
1924
            $h++;
1925
        }
1926
    }
1927
1928
    // admin and non admin can view this menu entry, but it is not shown yet when we on user menu "Email templates"
1929
    if (empty($_SESSION['leftmenu']) || $_SESSION['leftmenu'] != 'email_templates') {
1930
        $head[$h][0] = constant('BASE_URL') . "/admin/mails_senderprofile_list.php";
1931
        $head[$h][1] = $langs->trans("EmailSenderProfiles");
1932
        $head[$h][2] = 'senderprofiles';
1933
        $h++;
1934
    }
1935
1936
    $head[$h][0] = constant('BASE_URL') . "/admin/mails_templates.php";
1937
    $head[$h][1] = $langs->trans("EMailTemplates");
1938
    $head[$h][2] = 'templates';
1939
    $h++;
1940
1941
    $head[$h][0] = constant('BASE_URL') . "/admin/mails_ingoing.php";
1942
    $head[$h][1] = $langs->trans("InGoingEmailSetup", $langs->transnoentitiesnoconv("EMailing"));
1943
    $head[$h][2] = 'common_ingoing';
1944
    $h++;
1945
1946
    complete_head_from_modules($conf, $langs, null, $head, $h, 'email_admin', 'remove');
1947
1948
    return $head;
1949
}
1950