Passed
Push — main ( 4b1ce6...7d2b49 )
by Rafael
80:06
created

unActivateModule()   B

Complexity

Conditions 9
Paths 6

Size

Total Lines 29
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 15
nc 6
nop 2
dl 0
loc 29
rs 8.0555
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\Core\Base\DolibarrModules;
27
use Dolibarr\Lib\Modules;
28
29
/**
30
 *  \file           htdocs/core/lib/admin.lib.php
31
 *  \brief          Library of admin functions
32
 */
33
34
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/functions2.lib.php';
35
36
/**
37
 *  Renvoi une version en chaine depuis une version en tableau
38
 *
39
 * @param array $versionarray Tableau de version (vermajeur,vermineur,autre)
40
 * @return     string                          Chaine version
41
 * @see versioncompare()
42
 */
43
function versiontostring($versionarray)
44
{
45
    $string = '?';
46
    if (isset($versionarray[0])) {
47
        $string = $versionarray[0];
48
    }
49
    if (isset($versionarray[1])) {
50
        $string .= '.' . $versionarray[1];
51
    }
52
    if (isset($versionarray[2])) {
53
        $string .= '.' . $versionarray[2];
54
    }
55
    return $string;
56
}
57
58
/**
59
 *  Compare 2 versions (stored into 2 arrays).
60
 *  To check if Dolibarr version is lower than (x,y,z), do "if versioncompare(versiondolibarrarray(), array(x.y.z)) <= 0"
61
 *  For example: if (versioncompare(versiondolibarrarray(),array(4,0,-5)) >= 0) is true if version is 4.0 alpha or higher.
62
 *  For example: if (versioncompare(versiondolibarrarray(),array(4,0,0)) >= 0) is true if version is 4.0 final or higher.
63
 *  For example: if (versioncompare(versiondolibarrarray(),array(4,0,1)) >= 0) is true if version is 4.0.1 or higher.
64
 *  Alternative way to compare: if ((float) DOL_VERSION >= 4.0) is true if version is 4.0 alpha or higher (works only to compare first and second level)
65
 *
66
 * @param array $versionarray1 Array of version (vermajor,verminor,patch)
67
 * @param array $versionarray2 Array of version (vermajor,verminor,patch)
68
 * @return     int                             -4,-3,-2,-1 if versionarray1<versionarray2 (value depends on level of difference)
69
 *                                              0 if same
70
 *                                              1,2,3,4 if versionarray1>versionarray2 (value depends on level of difference)
71
 * @see versiontostring()
72
 */
73
function versioncompare($versionarray1, $versionarray2)
74
{
75
    $ret = 0;
76
    $level = 0;
77
    $count1 = count($versionarray1);
78
    $count2 = count($versionarray2);
79
    $maxcount = max($count1, $count2);
80
    while ($level < $maxcount) {
81
        $operande1 = isset($versionarray1[$level]) ? $versionarray1[$level] : 0;
82
        $operande2 = isset($versionarray2[$level]) ? $versionarray2[$level] : 0;
83
        if (preg_match('/alpha|dev/i', $operande1)) {
84
            $operande1 = -5;
85
        }
86
        if (preg_match('/alpha|dev/i', $operande2)) {
87
            $operande2 = -5;
88
        }
89
        if (preg_match('/beta$/i', $operande1)) {
90
            $operande1 = -4;
91
        }
92
        if (preg_match('/beta$/i', $operande2)) {
93
            $operande2 = -4;
94
        }
95
        if (preg_match('/beta([0-9])+/i', $operande1)) {
96
            $operande1 = -3;
97
        }
98
        if (preg_match('/beta([0-9])+/i', $operande2)) {
99
            $operande2 = -3;
100
        }
101
        if (preg_match('/rc$/i', $operande1)) {
102
            $operande1 = -2;
103
        }
104
        if (preg_match('/rc$/i', $operande2)) {
105
            $operande2 = -2;
106
        }
107
        if (preg_match('/rc([0-9])+/i', $operande1)) {
108
            $operande1 = -1;
109
        }
110
        if (preg_match('/rc([0-9])+/i', $operande2)) {
111
            $operande2 = -1;
112
        }
113
        $level++;
114
        //print 'level '.$level.' '.$operande1.'-'.$operande2.'<br>';
115
        if ($operande1 < $operande2) {
116
            $ret = -$level;
117
            break;
118
        }
119
        if ($operande1 > $operande2) {
120
            $ret = $level;
121
            break;
122
        }
123
    }
124
    //print join('.',$versionarray1).'('.count($versionarray1).') / '.join('.',$versionarray2).'('.count($versionarray2).') => '.$ret.'<br>'."\n";
125
    return $ret;
126
}
127
128
129
/**
130
 *  Return version PHP
131
 *
132
 * @return     array               Tableau de version (vermajeur,vermineur,autre)
133
 * @see versioncompare()
134
 */
135
function versionphparray()
136
{
137
    return explode('.', PHP_VERSION);
138
}
139
140
/**
141
 *  Return version Dolibarr
142
 *
143
 * @return     array               Tableau de version (vermajeur,vermineur,autre)
144
 * @see versioncompare()
145
 */
146
function versiondolibarrarray()
147
{
148
    return explode('.', DOL_VERSION);
149
}
150
151
152
/**
153
 *  Launch a sql file. Function is used by:
154
 *  - Migrate process (dolibarr-xyz-abc.sql)
155
 *  - Loading sql menus (auguria)
156
 *  - Running specific Sql by a module init
157
 *  - Loading sql file of website import package
158
 *  Install process however does not use it.
159
 *  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
160
 *
161
 * @param string $sqlfile Full path to sql file
162
 * @param int $silent 1=Do not output anything, 0=Output line for update page
163
 * @param int $entity Entity targeted for multicompany module
164
 * @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).
165
 * @param string $handler Handler targeted for menu (replace __HANDLER__ with this value between quotes)
166
 * @param string $okerror Family of errors we accept ('default', 'none')
167
 * @param int $linelengthlimit Limit for length of each line (Use 0 if unknown, may be faster if defined)
168
 * @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)
169
 * @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
170
 * @param int $colspan 2=Add a colspan=2 on td
171
 * @param int $onlysqltoimportwebsite Only sql requests used to import a website template are allowed
172
 * @param string $database Database (replace __DATABASE__ with this value)
173
 * @return     int                                     Return integer <=0 if KO, >0 if OK
174
 */
175
function run_sql($sqlfile, $silent = 1, $entity = 0, $usesavepoint = 1, $handler = '', $okerror = 'default', $linelengthlimit = 32768, $nocommentremoval = 0, $offsetforchartofaccount = 0, $colspan = 0, $onlysqltoimportwebsite = 0, $database = '')
176
{
177
    global $db, $conf, $langs, $user;
178
179
    dol_syslog("Admin.lib::run_sql run sql file " . $sqlfile . " silent=" . $silent . " entity=" . $entity . " usesavepoint=" . $usesavepoint . " handler=" . $handler . " okerror=" . $okerror, LOG_DEBUG);
180
181
    if (!is_numeric($linelengthlimit)) {
182
        dol_syslog("Admin.lib::run_sql param linelengthlimit is not a numeric", LOG_ERR);
183
        return -1;
184
    }
185
186
    $ok = 0;
187
    $error = 0;
188
    $i = 0;
189
    $buffer = '';
190
    $arraysql = array();
191
192
    // Get version of database
193
    $versionarray = $db->getVersionArray();
194
195
    $fp = fopen($sqlfile, "r");
196
    if ($fp) {
197
        while (!feof($fp)) {
198
            // Warning fgets with second parameter that is null or 0 hang.
199
            if ($linelengthlimit > 0) {
200
                $buf = fgets($fp, $linelengthlimit);
201
            } else {
202
                $buf = fgets($fp);
203
            }
204
205
            // Test if request must be ran only for particular database or version (if yes, we must remove the -- comment)
206
            $reg = array();
207
            if (preg_match('/^--\sV(MYSQL|PGSQL)([^\s]*)/i', $buf, $reg)) {
208
                $qualified = 1;
209
210
                // restrict on database type
211
                if (!empty($reg[1])) {
212
                    if (!preg_match('/' . preg_quote($reg[1]) . '/i', $db->type)) {
213
                        $qualified = 0;
214
                    }
215
                }
216
217
                // restrict on version
218
                if ($qualified) {
219
                    if (!empty($reg[2])) {
220
                        if (is_numeric($reg[2])) {  // This is a version
221
                            $versionrequest = explode('.', $reg[2]);
222
                            //var_dump($versionrequest);
223
                            //var_dump($versionarray);
224
                            if (!count($versionrequest) || !count($versionarray) || versioncompare($versionrequest, $versionarray) > 0) {
225
                                $qualified = 0;
226
                            }
227
                        } else { // This is a test on a constant. For example when we have -- VMYSQLUTF8UNICODE, we test constant $conf->global->UTF8UNICODE
228
                            $dbcollation = strtoupper(preg_replace('/_/', '', $conf->db->dolibarr_main_db_collation));
229
                            //var_dump($reg[2]);
230
                            //var_dump($dbcollation);
231
                            if (empty($conf->db->dolibarr_main_db_collation) || ($reg[2] != $dbcollation)) {
232
                                $qualified = 0;
233
                            }
234
                            //var_dump($qualified);
235
                        }
236
                    }
237
                }
238
239
                if ($qualified) {
240
                    // Version qualified, delete SQL comments
241
                    $buf = preg_replace('/^--\sV(MYSQL|PGSQL)([^\s]*)/i', '', $buf);
242
                    //print "Ligne $i qualifi?e par version: ".$buf.'<br>';
243
                }
244
            }
245
246
            // Add line buf to buffer if not a comment
247
            if ($nocommentremoval || !preg_match('/^\s*--/', $buf)) {
248
                if (empty($nocommentremoval)) {
249
                    $buf = preg_replace('/([,;ERLT\)])\s*--.*$/i', '\1', $buf); //remove comment from a line that not start with -- before add it to the buffer
250
                }
251
                if ($buffer) {
252
                    $buffer .= ' ';
253
                }
254
                $buffer .= trim($buf);
255
            }
256
257
            //print $buf.'<br>';exit;
258
259
            if (preg_match('/;/', $buffer)) {   // If string contains ';', it's end of a request string, we save it in arraysql.
260
                // Found new request
261
                if ($buffer) {
262
                    $arraysql[$i] = $buffer;
263
                }
264
                $i++;
265
                $buffer = '';
266
            }
267
        }
268
269
        if ($buffer) {
270
            $arraysql[$i] = $buffer;
271
        }
272
        fclose($fp);
273
    } else {
274
        dol_syslog("Admin.lib::run_sql failed to open file " . $sqlfile, LOG_ERR);
275
    }
276
277
    // Loop on each request to see if there is a __+MAX_table__ key
278
    $listofmaxrowid = array(); // This is a cache table
279
    foreach ($arraysql as $i => $sql) {
280
        $newsql = $sql;
281
282
        // Replace __+MAX_table__ with max of table
283
        while (preg_match('/__\+MAX_([A-Za-z0-9_]+)__/i', $newsql, $reg)) {
284
            $table = $reg[1];
285
            if (!isset($listofmaxrowid[$table])) {
286
                //var_dump($db);
287
                $sqlgetrowid = 'SELECT MAX(rowid) as max from ' . preg_replace('/^llx_/', MAIN_DB_PREFIX, $table);
288
                $resql = $db->query($sqlgetrowid);
289
                if ($resql) {
290
                    $obj = $db->fetch_object($resql);
291
                    $listofmaxrowid[$table] = $obj->max;
292
                    if (empty($listofmaxrowid[$table])) {
293
                        $listofmaxrowid[$table] = 0;
294
                    }
295
                } else {
296
                    if (!$silent) {
297
                        print '<tr><td class="tdtop"' . ($colspan ? ' colspan="' . $colspan . '"' : '') . '>';
298
                        print '<div class="error">' . $langs->trans("Failed to get max rowid for " . $table) . "</div>";
299
                        print '</td></tr>';
300
                    }
301
                    $error++;
302
                    break;
303
                }
304
            }
305
            // Replace __+MAX_llx_table__ with +999
306
            $from = '__+MAX_' . $table . '__';
307
            $to = '+' . $listofmaxrowid[$table];
308
            $newsql = str_replace($from, $to, $newsql);
309
            dol_syslog('Admin.lib::run_sql New Request ' . ($i + 1) . ' (replacing ' . $from . ' to ' . $to . ')', LOG_DEBUG);
310
311
            $arraysql[$i] = $newsql;
312
        }
313
314
        if ($offsetforchartofaccount > 0) {
315
            // Replace lines
316
            // '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,...'
317
            // with
318
            // '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,...'
319
            // Note: string with 'PCG99-ABREGE','CAPIT', 1234  instead of  'PCG99-ABREGE','CAPIT', '1234' is also supported
320
            $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);
321
            $newsql = preg_replace('/([,\s])0 \+ ' . ((int)$offsetforchartofaccount) . '/ims', '\1 0', $newsql);
322
            //var_dump($newsql);
323
            $arraysql[$i] = $newsql;
324
325
            // FIXME Because we force the rowid during insert, we must also update the sequence with postgresql by running
326
            // SELECT dol_util_rebuild_sequences();
327
        }
328
    }
329
330
    // Loop on each request to execute request
331
    $cursorinsert = 0;
332
    $listofinsertedrowid = array();
333
    $keyforsql = md5($sqlfile);
334
    foreach ($arraysql as $i => $sql) {
335
        if ($sql) {
336
            // Test if the SQL is allowed SQL
337
            if ($onlysqltoimportwebsite) {
338
                $newsql = str_replace(array("\'"), '__BACKSLASHQUOTE__', $sql); // Replace the \' char
339
340
                // Remove all strings contents including the ' so we can analyse SQL instruction only later
341
                $l = strlen($newsql);
342
                $is = 0;
343
                $quoteopen = 0;
344
                $newsqlclean = '';
345
                while ($is < $l) {
346
                    $char = $newsql[$is];
347
                    if ($char == "'") {
348
                        if ($quoteopen) {
349
                            $quoteopen--;
350
                        } else {
351
                            $quoteopen++;
352
                        }
353
                    } elseif (empty($quoteopen)) {
354
                        $newsqlclean .= $char;
355
                    }
356
                    $is++;
357
                }
358
                $newsqlclean = str_replace(array("null"), '__000__', $newsqlclean);
359
                //print $newsqlclean."<br>\n";
360
361
                $qualified = 0;
362
363
                // A very small control. This can still by bypassed by adding a second SQL request concatenated
364
                if (preg_match('/^--/', $newsqlclean)) {
365
                    $qualified = 1;
366
                } elseif (preg_match('/^UPDATE llx_website SET \w+ = \d+\+\d+ WHERE rowid = \d+;$/', $newsqlclean)) {
367
                    $qualified = 1;
368
                } elseif (preg_match('/^INSERT INTO llx_website_page\([a-z0-9_\s,]+\) VALUES\([0-9_\s,\+]+\);$/', $newsqlclean)) {
369
                    // Insert must match
370
                    // 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, , , , , );
371
                    $qualified = 1;
372
                }
373
374
                // Another check to allow some legitimate original urls
375
                if (!$qualified) {
376
                    if (preg_match('/^UPDATE llx_website SET \w+ = \'[a-zA-Z,\s]*\' WHERE rowid = \d+;$/', $sql)) {
377
                        $qualified = 1;
378
                    }
379
                }
380
381
                // We also check content
382
                $extractphp = dolKeepOnlyPhpCode($sql);
383
                $extractphpold = '';
384
385
                // Security analysis
386
                $errorphpcheck = checkPHPCode($extractphpold, $extractphp); // Contains the setEventMessages
387
                if ($errorphpcheck) {
388
                    $error++;
389
                    //print 'Request '.($i + 1)." contains non allowed instructions.<br>\n";
390
                    //print "newsqlclean = ".$newsqlclean."<br>\n";
391
                    dol_syslog('Admin.lib::run_sql Request ' . ($i + 1) . " contains PHP code and checking this code returns errorphpcheck='.$errorphpcheck.'", LOG_WARNING);
392
                    dol_syslog("sql=" . $sql, LOG_DEBUG);
393
                    break;
394
                }
395
396
397
                if (!$qualified) {
398
                    $error++;
399
                    //print 'Request '.($i + 1)." contains non allowed instructions.<br>\n";
400
                    //print "newsqlclean = ".$newsqlclean."<br>\n";
401
                    dol_syslog('Admin.lib::run_sql Request ' . ($i + 1) . " contains non allowed instructions.", LOG_WARNING);
402
                    dol_syslog('$newsqlclean=' . $newsqlclean, LOG_DEBUG);
403
                    break;
404
                }
405
            }
406
407
            // Replace the prefix tables
408
            if (MAIN_DB_PREFIX != 'llx_') {
409
                $sql = preg_replace('/llx_/i', MAIN_DB_PREFIX, $sql);
410
            }
411
412
            if (!empty($handler)) {
413
                $sql = preg_replace('/__HANDLER__/i', "'" . $db->escape($handler) . "'", $sql);
414
            }
415
416
            if (!empty($database)) {
417
                $sql = preg_replace('/__DATABASE__/i', $db->escape($database), $sql);
418
            }
419
420
            $newsql = preg_replace('/__ENTITY__/i', (!empty($entity) ? $entity : (string)$conf->entity), $sql);
421
422
            // Add log of request
423
            if (!$silent) {
424
                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";
425
            }
426
            dol_syslog('Admin.lib::run_sql Request ' . ($i + 1), LOG_DEBUG);
427
            $sqlmodified = 0;
428
429
            // Replace for encrypt data
430
            if (preg_match_all('/__ENCRYPT\(\'([^\']+)\'\)__/i', $newsql, $reg)) {
431
                $num = count($reg[0]);
432
433
                for ($j = 0; $j < $num; $j++) {
434
                    $from = $reg[0][$j];
435
                    $to = $db->encrypt($reg[1][$j]);
436
                    $newsql = str_replace($from, $to, $newsql);
437
                }
438
                $sqlmodified++;
439
            }
440
441
            // Replace for decrypt data
442
            if (preg_match_all('/__DECRYPT\(\'([A-Za-z0-9_]+)\'\)__/i', $newsql, $reg)) {
443
                $num = count($reg[0]);
444
445
                for ($j = 0; $j < $num; $j++) {
446
                    $from = $reg[0][$j];
447
                    $to = $db->decrypt($reg[1][$j]);
448
                    $newsql = str_replace($from, $to, $newsql);
449
                }
450
                $sqlmodified++;
451
            }
452
453
            // Replace __x__ with the rowid of the result of the insert number x
454
            while (preg_match('/__([0-9]+)__/', $newsql, $reg)) {
455
                $cursor = $reg[1];
456
                if (empty($listofinsertedrowid[$cursor])) {
457
                    if (!$silent) {
458
                        print '<tr><td class="tdtop"' . ($colspan ? ' colspan="' . $colspan . '"' : '') . '>';
459
                        print '<div class="error">' . $langs->trans("FileIsNotCorrect") . "</div>";
460
                        print '</td></tr>';
461
                    }
462
                    $error++;
463
                    break;
464
                }
465
466
                $from = '__' . $cursor . '__';
467
                $to = $listofinsertedrowid[$cursor];
468
                $newsql = str_replace($from, $to, $newsql);
469
                $sqlmodified++;
470
            }
471
472
            if ($sqlmodified) {
473
                dol_syslog('Admin.lib::run_sql New Request ' . ($i + 1), LOG_DEBUG);
474
            }
475
476
            $result = $db->query($newsql, $usesavepoint);
477
            if ($result) {
478
                if (!$silent) {
479
                    print '<!-- Result = OK -->' . "\n";
480
                }
481
482
                if (preg_replace('/insert into ([^\s]+)/i', $newsql, $reg)) {
483
                    $cursorinsert++;
484
485
                    // It's an insert
486
                    $table = preg_replace('/([^a-zA-Z_]+)/i', '', $reg[1]);
487
                    $insertedrowid = $db->last_insert_id($table);
488
                    $listofinsertedrowid[$cursorinsert] = $insertedrowid;
489
                    dol_syslog('Admin.lib::run_sql Insert nb ' . $cursorinsert . ', done in table ' . $table . ', rowid is ' . $listofinsertedrowid[$cursorinsert], LOG_DEBUG);
490
                }
491
                //            print '<td class="right">OK</td>';
492
            } else {
493
                $errno = $db->errno();
494
                if (!$silent) {
495
                    print '<!-- Result = ' . $errno . ' -->' . "\n";
496
                }
497
498
                // Define list of errors we accept (array $okerrors)
499
                $okerrors = array(  // By default
500
                    'DB_ERROR_TABLE_ALREADY_EXISTS',
501
                    'DB_ERROR_COLUMN_ALREADY_EXISTS',
502
                    'DB_ERROR_KEY_NAME_ALREADY_EXISTS',
503
                    'DB_ERROR_TABLE_OR_KEY_ALREADY_EXISTS', // PgSql use same code for table and key already exist
504
                    'DB_ERROR_RECORD_ALREADY_EXISTS',
505
                    'DB_ERROR_NOSUCHTABLE',
506
                    'DB_ERROR_NOSUCHFIELD',
507
                    'DB_ERROR_NO_FOREIGN_KEY_TO_DROP',
508
                    'DB_ERROR_NO_INDEX_TO_DROP',
509
                    'DB_ERROR_CANNOT_CREATE', // Qd contrainte deja existante
510
                    'DB_ERROR_CANT_DROP_PRIMARY_KEY',
511
                    'DB_ERROR_PRIMARY_KEY_ALREADY_EXISTS',
512
                    'DB_ERROR_22P02'
513
                );
514
                if ($okerror == 'none') {
515
                    $okerrors = array();
516
                }
517
518
                // Is it an error we accept
519
                if (!in_array($errno, $okerrors)) {
520
                    if (!$silent) {
521
                        print '<tr><td class="tdtop"' . ($colspan ? ' colspan="' . $colspan . '"' : '') . '>';
522
                        print '<div class="error">' . $langs->trans("Error") . " " . $db->errno() . " (Req " . ($i + 1) . "): " . $newsql . "<br>" . $db->error() . "</div>";
523
                        print '</td></tr>' . "\n";
524
                    }
525
                    dol_syslog('Admin.lib::run_sql Request ' . ($i + 1) . " Error " . $db->errno() . " " . $newsql . "<br>" . $db->error(), LOG_ERR);
526
                    $error++;
527
                }
528
            }
529
        }
530
    }
531
532
    if (!$silent) {
533
        print '<tr><td>' . $langs->trans("ProcessMigrateScript") . '</td>';
534
        print '<td class="right">';
535
        if ($error == 0) {
536
            print '<span class="ok">' . $langs->trans("OK") . '</span>';
537
        } else {
538
            print '<span class="error">' . $langs->trans("Error") . '</span>';
539
        }
540
541
        //if (!empty($conf->use_javascript_ajax)) {     // use_javascript_ajax is not defined
542
        print '<script type="text/javascript">
543
		jQuery(document).ready(function() {
544
			function init_trrunsql' . $keyforsql . '()
545
			{
546
				console.log("toggle .trforrunsql' . $keyforsql . '");
547
				jQuery(".trforrunsql' . $keyforsql . '").toggle();
548
			}
549
			init_trrunsql' . $keyforsql . '();
550
			jQuery(".trforrunsqlshowhide' . $keyforsql . '").click(function() {
551
				init_trrunsql' . $keyforsql . '();
552
			});
553
		});
554
		</script>';
555
        if (count($arraysql)) {
556
            print ' - <a class="trforrunsqlshowhide' . $keyforsql . '" href="#" title="' . ($langs->trans("ShowHideTheNRequests", count($arraysql))) . '">' . $langs->trans("ShowHideDetails") . '</a>';
557
        } else {
558
            print ' - <span class="opacitymedium">' . $langs->trans("ScriptIsEmpty") . '</span>';
559
        }
560
        //}
561
562
        print '</td></tr>' . "\n";
563
    }
564
565
    if ($error == 0) {
566
        $ok = 1;
567
    } else {
568
        $ok = 0;
569
    }
570
571
    return $ok;
572
}
573
574
575
/**
576
 *  Delete a constant
577
 *
578
 * @param DoliDB $db Database handler
579
 * @param string|int $name Name of constant or rowid of line
580
 * @param int $entity Multi company id, -1 for all entities
581
 * @return     int                     Return integer <0 if KO, >0 if OK
582
 *
583
 * @see        dolibarr_get_const(), dolibarr_set_const(), dol_set_user_param()
584
 */
585
function dolibarr_del_const($db, $name, $entity = 1)
586
{
587
    global $conf;
588
589
    if (empty($name)) {
590
        dol_print_error(null, 'Error call dolibar_del_const with parameter name empty');
591
        return -1;
592
    }
593
594
    $sql = "DELETE FROM " . MAIN_DB_PREFIX . "const";
595
    $sql .= " WHERE (" . $db->decrypt('name') . " = '" . $db->escape($name) . "'";
596
    if (is_numeric($name)) {
597
        $sql .= " OR rowid = " . ((int)$name);
598
    }
599
    $sql .= ")";
600
    if ($entity >= 0) {
601
        $sql .= " AND entity = " . ((int)$entity);
602
    }
603
604
    dol_syslog("admin.lib::dolibarr_del_const", LOG_DEBUG);
605
    $resql = $db->query($sql);
606
    if ($resql) {
607
        $conf->global->$name = '';
608
        return 1;
609
    } else {
610
        dol_print_error($db);
611
        return -1;
612
    }
613
}
614
615
/**
616
 *  Get the value of a setup constant from database
617
 *
618
 * @param DoliDB $db Database handler
619
 * @param string $name Name of constant
620
 * @param int $entity Multi company id
621
 * @return     string                  Value of constant
622
 *
623
 * @see        dolibarr_del_const(), dolibarr_set_const(), dol_set_user_param()
624
 */
625
function dolibarr_get_const($db, $name, $entity = 1)
626
{
627
    $value = '';
628
629
    $sql = "SELECT " . $db->decrypt('value') . " as value";
630
    $sql .= " FROM " . MAIN_DB_PREFIX . "const";
631
    $sql .= " WHERE name = " . $db->encrypt($name);
632
    $sql .= " AND entity = " . ((int)$entity);
633
634
    dol_syslog("admin.lib::dolibarr_get_const", LOG_DEBUG);
635
    $resql = $db->query($sql);
636
    if ($resql) {
637
        $obj = $db->fetch_object($resql);
638
        if ($obj) {
639
            include_once DOL_DOCUMENT_ROOT . '/core/lib/security.lib.php';
640
            $value = dolDecrypt($obj->value);
641
        }
642
    }
643
    return $value;
644
}
645
646
647
/**
648
 *  Insert a parameter (key,value) into database (delete old key then insert it again).
649
 *
650
 * @param DoliDB $db Database handler
651
 * @param string $name Name of constant
652
 * @param string $value Value of constant
653
 * @param string $type Type of constant. Deprecated, only strings are allowed for $value. Caller must json encode/decode to store other type of data.
654
 * @param int $visible Is constant visible in Setup->Other page (0 by default)
655
 * @param string $note Note on parameter
656
 * @param int $entity Multi company id (0 means all entities)
657
 * @return     int                     -1 if KO, 1 if OK
658
 *
659
 * @see        dolibarr_del_const(), dolibarr_get_const(), dol_set_user_param()
660
 */
661
function dolibarr_set_const($db, $name, $value, $type = 'chaine', $visible = 0, $note = '', $entity = 1)
662
{
663
    global $conf;
664
665
    // Clean parameters
666
    $name = trim($name);
667
668
    // Check parameters
669
    if (empty($name)) {
670
        dol_print_error($db, "Error: Call to function dolibarr_set_const with wrong parameters");
671
        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...
672
    }
673
674
    //dol_syslog("dolibarr_set_const name=$name, value=$value type=$type, visible=$visible, note=$note entity=$entity");
675
676
    $db->begin();
677
678
    $sql = "DELETE FROM " . MAIN_DB_PREFIX . "const";
679
    $sql .= " WHERE name = " . $db->encrypt($name);
680
    if ($entity >= 0) {
681
        $sql .= " AND entity = " . ((int)$entity);
682
    }
683
684
    dol_syslog("admin.lib::dolibarr_set_const", LOG_DEBUG);
685
    $resql = $db->query($sql);
686
687
    if (strcmp($value, '')) {   // true if different. Must work for $value='0' or $value=0
688
        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))) {
689
            // This seems a sensitive constant, we encrypt its value
690
            // To list all sensitive constant, you can make a
691
            // 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'
692
            // or name like '%\_SECURITY_TOKEN' or name like '%\WEB_TOKEN'
693
            include_once DOL_DOCUMENT_ROOT . '/core/lib/security.lib.php';
694
            $newvalue = dolEncrypt($value);
695
        } else {
696
            $newvalue = $value;
697
        }
698
699
        $sql = "INSERT INTO " . MAIN_DB_PREFIX . "const(name, value, type, visible, note, entity)";
700
        $sql .= " VALUES (";
701
        $sql .= $db->encrypt($name);
702
        $sql .= ", " . $db->encrypt($newvalue);
703
        $sql .= ", '" . $db->escape($type) . "', " . ((int)$visible) . ", '" . $db->escape($note) . "', " . ((int)$entity) . ")";
704
705
        //print "sql".$value."-".pg_escape_string($value)."-".$sql;exit;
706
        //print "xx".$db->escape($value);
707
        dol_syslog("admin.lib::dolibarr_set_const", LOG_DEBUG);
708
        $resql = $db->query($sql);
709
    }
710
711
    if ($resql) {
712
        $db->commit();
713
        $conf->global->$name = $value;
714
        return 1;
715
    } else {
716
        $error = $db->lasterror();
717
        $db->rollback();
718
        return -1;
719
    }
720
}
721
722
723
/**
724
 * Prepare array with list of tabs
725
 *
726
 * @param int $nbofactivatedmodules Number if activated modules
727
 * @param int $nboftotalmodules Nb of total modules
728
 * @param int $nbmodulesnotautoenabled Nb of modules not auto enabled that are activated
729
 * @return  array                               Array of tabs to show
730
 */
731
function modules_prepare_head($nbofactivatedmodules, $nboftotalmodules, $nbmodulesnotautoenabled)
732
{
733
    global $langs, $form;
734
735
    $desc = $langs->trans("ModulesDesc", '{picto}');
736
    $desc = str_replace('{picto}', img_picto('', 'switch_off'), $desc);
737
738
    $h = 0;
739
    $head = array();
740
    $mode = getDolGlobalString('MAIN_MODULE_SETUP_ON_LIST_BY_DEFAULT', 'commonkanban');
741
    $head[$h][0] = constant('BASE_URL') . "/admin/modules.php?mode=" . $mode;
742
    if ($nbmodulesnotautoenabled <= getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled)
743
        //$head[$h][1] = $form->textwithpicto($langs->trans("AvailableModules"), $desc);
744
        $head[$h][1] = $langs->trans("AvailableModules");
745
        $head[$h][1] .= $form->textwithpicto('', $langs->trans("YouMustEnableOneModule") . '.<br><br><span class="opacitymedium">' . $desc . '</span>', 1, 'warning');
746
    } else {
747
        //$head[$h][1] = $langs->trans("AvailableModules").$form->textwithpicto('<span class="badge marginleftonly">'.$nbofactivatedmodules.' / '.$nboftotalmodules.'</span>', $desc, 1, 'help', '', 1, 3);
748
        $head[$h][1] = $langs->trans("AvailableModules") . '<span class="badge marginleftonly">' . $nbofactivatedmodules . ' / ' . $nboftotalmodules . '</span>';
749
    }
750
    $head[$h][2] = 'modules';
751
    $h++;
752
753
    $head[$h][0] = constant('BASE_URL') . "/admin/modules.php?mode=marketplace";
754
    $head[$h][1] = $langs->trans("ModulesMarketPlaces");
755
    $head[$h][2] = 'marketplace';
756
    $h++;
757
758
    $head[$h][0] = constant('BASE_URL') . "/admin/modules.php?mode=deploy";
759
    $head[$h][1] = $langs->trans("AddExtensionThemeModuleOrOther");
760
    $head[$h][2] = 'deploy';
761
    $h++;
762
763
    $head[$h][0] = constant('BASE_URL') . "/admin/modules.php?mode=develop";
764
    $head[$h][1] = $langs->trans("ModulesDevelopYourModule");
765
    $head[$h][2] = 'develop';
766
    $h++;
767
768
    return $head;
769
}
770
771
/**
772
 * Prepare array with list of tabs
773
 *
774
 * @return  array               Array of tabs to show
775
 */
776
function ihm_prepare_head()
777
{
778
    global $langs, $conf, $user;
779
    $h = 0;
780
    $head = array();
781
782
    $head[$h][0] = constant('BASE_URL') . "/admin/ihm.php?mode=other";
783
    $head[$h][1] = $langs->trans("LanguageAndPresentation");
784
    $head[$h][2] = 'other';
785
    $h++;
786
787
    $head[$h][0] = constant('BASE_URL') . "/admin/ihm.php?mode=template";
788
    $head[$h][1] = $langs->trans("SkinAndColors");
789
    $head[$h][2] = 'template';
790
    $h++;
791
792
    $head[$h][0] = constant('BASE_URL') . "/admin/ihm.php?mode=dashboard";
793
    $head[$h][1] = $langs->trans("Dashboard");
794
    $head[$h][2] = 'dashboard';
795
    $h++;
796
797
    $head[$h][0] = constant('BASE_URL') . "/admin/ihm.php?mode=login";
798
    $head[$h][1] = $langs->trans("LoginPage");
799
    $head[$h][2] = 'login';
800
    $h++;
801
802
    $head[$h][0] = constant('BASE_URL') . "/admin/ihm.php?mode=css";
803
    $head[$h][1] = $langs->trans("CSSPage");
804
    $head[$h][2] = 'css';
805
    $h++;
806
807
    complete_head_from_modules($conf, $langs, null, $head, $h, 'ihm_admin');
808
809
    complete_head_from_modules($conf, $langs, null, $head, $h, 'ihm_admin', 'remove');
810
811
812
    return $head;
813
}
814
815
816
/**
817
 * Prepare array with list of tabs
818
 *
819
 * @return  array               Array of tabs to show
820
 */
821
function security_prepare_head()
822
{
823
    global $db, $langs, $conf, $user;
824
    $h = 0;
825
    $head = array();
826
827
    $head[$h][0] = constant('BASE_URL') . "/admin/security_other.php";
828
    $head[$h][1] = $langs->trans("Miscellaneous");
829
    $head[$h][2] = 'misc';
830
    $h++;
831
832
    $head[$h][0] = constant('BASE_URL') . "/admin/security.php";
833
    $head[$h][1] = $langs->trans("Passwords");
834
    $head[$h][2] = 'passwords';
835
    $h++;
836
837
    $head[$h][0] = constant('BASE_URL') . "/admin/security_file.php";
838
    $head[$h][1] = $langs->trans("Files") . ' (' . $langs->trans("Upload") . ')';
839
    $head[$h][2] = 'file';
840
    $h++;
841
842
    /*
843
    $head[$h][0] = constant('BASE_URL')."/admin/security_file_download.php";
844
    $head[$h][1] = $langs->trans("Files").' ('.$langs->trans("Download").')';
845
    $head[$h][2] = 'filedownload';
846
    $h++;
847
    */
848
849
    $head[$h][0] = constant('BASE_URL') . "/admin/proxy.php";
850
    $head[$h][1] = $langs->trans("ExternalAccess");
851
    $head[$h][2] = 'proxy';
852
    $h++;
853
854
    $head[$h][0] = constant('BASE_URL') . "/admin/events.php";
855
    $head[$h][1] = $langs->trans("Audit");
856
    $head[$h][2] = 'audit';
857
    $h++;
858
859
860
    // Show permissions lines
861
    $nbPerms = 0;
862
    $sql = "SELECT COUNT(r.id) as nb";
863
    $sql .= " FROM " . MAIN_DB_PREFIX . "rights_def as r";
864
    $sql .= " WHERE r.libelle NOT LIKE 'tou%'"; // On ignore droits "tous"
865
    $sql .= " AND entity = " . ((int)$conf->entity);
866
    $sql .= " AND bydefault = 1";
867
    if (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) {
868
        $sql .= " AND r.perms NOT LIKE '%_advance'"; // Hide advanced perms if option is not enabled
869
    }
870
    $resql = $db->query($sql);
871
    if ($resql) {
872
        $obj = $db->fetch_object($resql);
873
        if ($obj) {
874
            $nbPerms = $obj->nb;
875
        }
876
    } else {
877
        dol_print_error($db);
878
    }
879
880
    if (getDolGlobalString('MAIN_SECURITY_USE_DEFAULT_PERMISSIONS')) {
881
        $head[$h][0] = constant('BASE_URL') . "/admin/perms.php";
882
        $head[$h][1] = $langs->trans("DefaultRights");
883
        if ($nbPerms > 0) {
884
            $head[$h][1] .= (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') ? '<span class="badge marginleftonlyshort">' . $nbPerms . '</span>' : '');
885
        }
886
        $head[$h][2] = 'default';
887
        $h++;
888
    }
889
890
    return $head;
891
}
892
893
/**
894
 * Prepare array with list of tabs
895
 *
896
 * @param object $object Descriptor class
897
 * @return  array               Array of tabs to show
898
 */
899
function modulehelp_prepare_head($object)
900
{
901
    global $langs, $conf;
902
    $h = 0;
903
    $head = array();
904
905
    // FIX for compatibility habitual tabs
906
    $object->id = $object->numero;
907
908
    $head[$h][0] = constant('BASE_URL') . "/admin/modulehelp.php?id=" . $object->id . '&mode=desc';
909
    $head[$h][1] = $langs->trans("Description");
910
    $head[$h][2] = 'desc';
911
    $h++;
912
913
    $head[$h][0] = constant('BASE_URL') . "/admin/modulehelp.php?id=" . $object->id . '&mode=feature';
914
    $head[$h][1] = $langs->trans("TechnicalServicesProvided");
915
    $head[$h][2] = 'feature';
916
    $h++;
917
918
    if ($object->isCoreOrExternalModule() == 'external') {
919
        $head[$h][0] = constant('BASE_URL') . "/admin/modulehelp.php?id=" . $object->id . '&mode=changelog';
920
        $head[$h][1] = $langs->trans("ChangeLog");
921
        $head[$h][2] = 'changelog';
922
        $h++;
923
    }
924
925
    complete_head_from_modules($conf, $langs, $object, $head, $h, 'modulehelp_admin');
926
927
    complete_head_from_modules($conf, $langs, $object, $head, $h, 'modulehelp_admin', 'remove');
928
929
930
    return $head;
931
}
932
933
/**
934
 * Prepare array with list of tabs
935
 *
936
 * @return  array               Array of tabs to show
937
 */
938
function translation_prepare_head()
939
{
940
    global $langs, $conf;
941
    $h = 0;
942
    $head = array();
943
944
    $head[$h][0] = constant('BASE_URL') . "/admin/translation.php?mode=searchkey";
945
    $head[$h][1] = $langs->trans("TranslationKeySearch");
946
    $head[$h][2] = 'searchkey';
947
    $h++;
948
949
    $head[$h][0] = constant('BASE_URL') . "/admin/translation.php?mode=overwrite";
950
    $head[$h][1] = '<span class="valignmiddle">' . $langs->trans("TranslationOverwriteKey") . '</span><span class="fa fa-plus-circle valignmiddle paddingleft"></span>';
951
    $head[$h][2] = 'overwrite';
952
    $h++;
953
954
    complete_head_from_modules($conf, $langs, null, $head, $h, 'translation_admin');
955
956
    complete_head_from_modules($conf, $langs, null, $head, $h, 'translation_admin', 'remove');
957
958
959
    return $head;
960
}
961
962
963
/**
964
 * Prepare array with list of tabs
965
 *
966
 * @return  array               Array of tabs to show
967
 */
968
function defaultvalues_prepare_head()
969
{
970
    global $langs, $conf, $user;
971
    $h = 0;
972
    $head = array();
973
974
    $head[$h][0] = constant('BASE_URL') . "/admin/defaultvalues.php?mode=createform";
975
    $head[$h][1] = $langs->trans("DefaultCreateForm");
976
    $head[$h][2] = 'createform';
977
    $h++;
978
979
    $head[$h][0] = constant('BASE_URL') . "/admin/defaultvalues.php?mode=filters";
980
    $head[$h][1] = $langs->trans("DefaultSearchFilters");
981
    $head[$h][2] = 'filters';
982
    $h++;
983
984
    $head[$h][0] = constant('BASE_URL') . "/admin/defaultvalues.php?mode=sortorder";
985
    $head[$h][1] = $langs->trans("DefaultSortOrder");
986
    $head[$h][2] = 'sortorder';
987
    $h++;
988
989
    if (!empty($conf->use_javascript_ajax)) {
990
        $head[$h][0] = constant('BASE_URL') . "/admin/defaultvalues.php?mode=focus";
991
        $head[$h][1] = $langs->trans("DefaultFocus");
992
        $head[$h][2] = 'focus';
993
        $h++;
994
995
        $head[$h][0] = constant('BASE_URL') . "/admin/defaultvalues.php?mode=mandatory";
996
        $head[$h][1] = $langs->trans("DefaultMandatory");
997
        $head[$h][2] = 'mandatory';
998
        $h++;
999
    }
1000
1001
    /*$head[$h][0] = constant('BASE_URL')."/admin/translation.php?mode=searchkey";
1002
    $head[$h][1] = $langs->trans("TranslationKeySearch");
1003
    $head[$h][2] = 'searchkey';
1004
    $h++;*/
1005
1006
    complete_head_from_modules($conf, $langs, null, $head, $h, 'defaultvalues_admin');
1007
1008
    complete_head_from_modules($conf, $langs, null, $head, $h, 'defaultvalues_admin', 'remove');
1009
1010
1011
    return $head;
1012
}
1013
1014
1015
/**
1016
 *  Return list of session
1017
 *
1018
 * @return array<string,array{login:string,age:int,creation:int,modification:int,raw:string}>  Array list of sessions
1019
 */
1020
function listOfSessions()
1021
{
1022
    global $conf;
1023
1024
    $arrayofSessions = array();
1025
    // session.save_path can be returned empty so we set a default location and work from there
1026
    $sessPath = '/tmp';
1027
    $iniPath = ini_get("session.save_path");
1028
    if ($iniPath) {
1029
        $sessPath = $iniPath;
1030
    }
1031
    $sessPath .= '/'; // We need the trailing slash
1032
    dol_syslog('admin.lib:listOfSessions sessPath=' . $sessPath);
1033
1034
    $dh = @opendir(dol_osencode($sessPath));
1035
    if ($dh) {
1036
        while (($file = @readdir($dh)) !== false) {
1037
            if (preg_match('/^sess_/i', $file) && $file != "." && $file != "..") {
1038
                $fullpath = $sessPath . $file;
1039
                if (!@is_dir($fullpath) && is_readable($fullpath)) {
1040
                    $sessValues = file_get_contents($fullpath); // get raw session data
1041
                    // Example of possible value
1042
                    //$sessValues = 'newtoken|s:32:"1239f7a0c4b899200fe9ca5ea394f307";dol_loginmesg|s:0:"";newtoken|s:32:"1236457104f7ae0f328c2928973f3cb5";dol_loginmesg|s:0:"";token|s:32:"123615ad8d650c5cc4199b9a1a76783f";
1043
                    // 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";
1044
                    // 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";';
1045
1046
                    if (
1047
                        preg_match('/dol_login/i', $sessValues) && // limit to dolibarr session
1048
                        (preg_match('/dol_entity\|i:' . $conf->entity . ';/i', $sessValues) || preg_match('/dol_entity\|s:([0-9]+):"' . $conf->entity . '"/i', $sessValues)) && // limit to current entity
1049
                        preg_match('/dol_company\|s:([0-9]+):"(' . getDolGlobalString('MAIN_INFO_SOCIETE_NOM') . ')"/i', $sessValues)
1050
                    ) { // limit to company name
1051
                        $tmp = explode('_', $file);
1052
                        $idsess = $tmp[1];
1053
                        $regs = array();
1054
                        $loginfound = preg_match('/dol_login\|s:[0-9]+:"([A-Za-z0-9]+)"/i', $sessValues, $regs);
1055
                        if ($loginfound) {
1056
                            $arrayofSessions[$idsess]["login"] = $regs[1];
1057
                        }
1058
                        $arrayofSessions[$idsess]["age"] = time() - filectime($fullpath);
1059
                        $arrayofSessions[$idsess]["creation"] = filectime($fullpath);
1060
                        $arrayofSessions[$idsess]["modification"] = filemtime($fullpath);
1061
                        $arrayofSessions[$idsess]["raw"] = $sessValues;
1062
                    }
1063
                }
1064
            }
1065
        }
1066
        @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

1066
        /** @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...
1067
    }
1068
1069
    return $arrayofSessions;
1070
}
1071
1072
/**
1073
 *  Purge existing sessions
1074
 *
1075
 * @param int $mysessionid To avoid to try to delete my own session
1076
 * @return     int                         >0 if OK, <0 if KO
1077
 */
1078
function purgeSessions($mysessionid)
1079
{
1080
    global $conf;
1081
1082
    $sessPath = ini_get("session.save_path") . "/";
1083
    dol_syslog('admin.lib:purgeSessions mysessionid=' . $mysessionid . ' sessPath=' . $sessPath);
1084
1085
    $error = 0;
1086
1087
    $dh = @opendir(dol_osencode($sessPath));
1088
    if ($dh) {
1089
        while (($file = @readdir($dh)) !== false) {
1090
            if ($file != "." && $file != "..") {
1091
                $fullpath = $sessPath . $file;
1092
                if (!@is_dir($fullpath)) {
1093
                    $sessValues = file_get_contents($fullpath); // get raw session data
1094
1095
                    if (
1096
                        preg_match('/dol_login/i', $sessValues) && // limit to dolibarr session
1097
                        preg_match('/dol_entity\|s:([0-9]+):"(' . $conf->entity . ')"/i', $sessValues) && // limit to current entity
1098
                        preg_match('/dol_company\|s:([0-9]+):"(' . getDolGlobalString('MAIN_INFO_SOCIETE_NOM') . ')"/i', $sessValues)
1099
                    ) { // limit to company name
1100
                        $tmp = explode('_', $file);
1101
                        $idsess = $tmp[1];
1102
                        // We remove session if it's not ourself
1103
                        if ($idsess != $mysessionid) {
1104
                            $res = @unlink($fullpath);
1105
                            if (!$res) {
1106
                                $error++;
1107
                            }
1108
                        }
1109
                    }
1110
                }
1111
            }
1112
        }
1113
        @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

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