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

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

1115
        /** @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...
1116
    }
1117
1118
    if (!$error) {
1119
        return 1;
1120
    } else {
1121
        return -$error;
1122
    }
1123
}
1124
1125
1126
/**
1127
 *  Enable a module
1128
 *
1129
 * @param string $value Name of module to activate
1130
 * @param int $withdeps Activate/Disable also all dependencies
1131
 * @param int $noconfverification Remove verification of $conf variable for module
1132
 * @return     array{nbmodules?:int,errors:string[],nbperms?:int}  array('nbmodules'=>nb modules activated with success, 'errors=>array of error messages, 'nbperms'=>Nb permission added);
1133
 */
1134
function activateModule($value, $withdeps = 1, $noconfverification = 0)
1135
{
1136
    global $db, $langs, $conf, $mysoc;
1137
1138
    $ret = array();
1139
1140
    // Check parameters
1141
    if (empty($value)) {
1142
        $ret['errors'] = array('ErrorBadParameter');
1143
        return $ret;
1144
    }
1145
1146
    $ret = array('nbmodules' => 0, 'errors' => array(), 'nbperms' => 0);
1147
    $modName = $value;
1148
    /*
1149
    $modFile = $modName . ".class.php";
1150
1151
    // Loop on each directory to fill $modulesdir
1152
    $modulesdir = dolGetModulesDirs();
1153
1154
    // Loop on each modulesdir directories
1155
    $found = false;
1156
    foreach ($modulesdir as $dir) {
1157
        if (file_exists($dir . $modFile)) {
1158
            $found = @include_once $dir . $modFile;
1159
            if ($found) {
1160
                break;
1161
            }
1162
        }
1163
    }
1164
1165
    $objMod = new $modName($db);
1166
    */
1167
    $objMod = DolibarrModules::getModule($modName);
1168
1169
    // Test if PHP version ok
1170
    $verphp = versionphparray();
1171
    $vermin = isset($objMod->phpmin) ? $objMod->phpmin : 0;
1172
    if (is_array($vermin) && versioncompare($verphp, $vermin) < 0) {
1173
        $ret['errors'][] = $langs->trans("ErrorModuleRequirePHPVersion", versiontostring($vermin));
1174
        return $ret;
1175
    }
1176
1177
    // Test if Dolibarr version ok
1178
    $verdol = versiondolibarrarray();
1179
    $vermin = isset($objMod->need_dolibarr_version) ? $objMod->need_dolibarr_version : 0;
1180
    //print 'version: '.versioncompare($verdol,$vermin).' - '.join(',',$verdol).' - '.join(',',$vermin);exit;
1181
    if (is_array($vermin) && versioncompare($verdol, $vermin) < 0) {
1182
        $ret['errors'][] = $langs->trans("ErrorModuleRequireDolibarrVersion", versiontostring($vermin));
1183
        return $ret;
1184
    }
1185
1186
    // Test if javascript requirement ok
1187
    if (!empty($objMod->need_javascript_ajax) && empty($conf->use_javascript_ajax)) {
1188
        $ret['errors'][] = $langs->trans("ErrorModuleRequireJavascript");
1189
        return $ret;
1190
    }
1191
1192
    $const_name = $objMod->const_name;
1193
    if ($noconfverification == 0) {
1194
        if (getDolGlobalString($const_name)) {
1195
            return $ret;
1196
        }
1197
    }
1198
1199
    $result = $objMod->init(); // Enable module
1200
1201
    if ($result <= 0) {
1202
        $ret['errors'][] = $objMod->error;
1203
    } else {
1204
        if ($withdeps) {
1205
            if (isset($objMod->depends) && is_array($objMod->depends) && !empty($objMod->depends)) {
1206
                // Activation of modules this module depends on
1207
                // this->depends may be array('modModule1', 'mmodModule2') or array('always'=>array('modModule1'), 'FR'=>array('modModule2"))
1208
                foreach ($objMod->depends as $key => $modulestringorarray) {
1209
                    //var_dump((! is_numeric($key)) && ! preg_match('/^always/', $key) && $mysoc->country_code && ! preg_match('/^'.$mysoc->country_code.'/', $key));exit;
1210
                    if ((!is_numeric($key)) && !preg_match('/^always/', $key) && $mysoc->country_code && !preg_match('/^' . $mysoc->country_code . '/', $key)) {
1211
                        dol_syslog("We are not concerned by dependency with key=" . $key . " because our country is " . $mysoc->country_code);
1212
                        continue;
1213
                    }
1214
1215
                    if (!is_array($modulestringorarray)) {
1216
                        $modulestringorarray = array($modulestringorarray);
1217
                    }
1218
1219
                    foreach ($modulestringorarray as $modulestring) {
1220
                        $module = DolibarrModules::getModule($modulestring);
1221
                        if ($module === null) {
1222
                            continue;
1223
                        }
1224
1225
                        /*
1226
                        $activate = false;
1227
                        $activateerr = '';
1228
                        foreach ($modulesdir as $dir) {
1229
                            if (file_exists($dir . $modulestring . ".class.php")) {
1230
                                $resarray = activateModule($modulestring);
1231
                                if (empty($resarray['errors'])) {
1232
                                    $activate = true;
1233
                                } else {
1234
                                    $activateerr = implode(', ', $resarray['errors']);
1235
                                    foreach ($resarray['errors'] as $errorMessage) {
1236
                                        dol_syslog($errorMessage, LOG_ERR);
1237
                                    }
1238
                                }
1239
                                break;
1240
                            }
1241
                        }
1242
                        */
1243
                        $resarray = activateModule($modulestring);
1244
                        $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...
1245
1246
                        if ($activate) {
1247
                            $ret['nbmodules'] += $resarray['nbmodules'];
1248
                            $ret['nbperms'] += $resarray['nbperms'];
1249
                        } else {
1250
                            $activateerr = implode(', ', $resarray['errors']);
1251
                            foreach ($resarray['errors'] as $errorMessage) {
1252
                                dol_syslog($errorMessage, LOG_ERR);
1253
                            }
1254
                            if ($activateerr) {
1255
                                $ret['errors'][] = $activateerr;
1256
                            }
1257
                            $ret['errors'][] = $langs->trans('activateModuleDependNotSatisfied', $objMod->name, $modulestring);
1258
                        }
1259
                    }
1260
                }
1261
            }
1262
1263
            if (isset($objMod->conflictwith) && is_array($objMod->conflictwith) && !empty($objMod->conflictwith)) {
1264
                // Deactivation des modules qui entrent en conflict
1265
                foreach ($objMod->conflictwith as $module_name) {
1266
                    unActivateModule($module_name, 0);
1267
                }
1268
            }
1269
        }
1270
    }
1271
1272
    if (!count($ret['errors'])) {
1273
        $ret['nbmodules']++;
1274
        $ret['nbperms'] += (is_array($objMod->rights) ? count($objMod->rights) : 0);
1275
    }
1276
1277
    return $ret;
1278
}
1279
1280
1281
/**
1282
 *  Disable a module
1283
 *
1284
 * @param string $value Nom du module a desactiver
1285
 * @param int $requiredby 1=Desactive aussi modules dependants
1286
 * @return     string                           Error message or '';
1287
 */
1288
function unActivateModule($value, $requiredby = 1)
1289
{
1290
    // Check parameters
1291
    if (empty($value)) {
1292
        return 'ErrorBadParameter';
1293
    }
1294
1295
    $objMod = DolibarrModules::getModule($value);
1296
    if ($objMod === null) {
1297
        DolibarrModules::forceDeactivate($value);
1298
        return 'ErrorModuleDoesNotExists';
1299
    }
1300
1301
    $ret = 0;
1302
    $result = $objMod->remove();
1303
    if ($result <= 0) {
1304
        $ret = $objMod->error;
1305
    }
1306
1307
    // Disable modules that depends on module we disable
1308
    if (!$ret && $requiredby && is_object($objMod) && is_array($objMod->requiredby)) {
1309
        $countrb = count($objMod->requiredby);
1310
        for ($i = 0; $i < $countrb; $i++) {
1311
            //var_dump($objMod->requiredby[$i]);
1312
            unActivateModule($objMod->requiredby[$i]);
1313
        }
1314
    }
1315
1316
    return $ret;
1317
}
1318
1319
1320
/**
1321
 *  Add external modules to list of dictionaries.
1322
 *  Addition is done into var $taborder, $tabname, etc... that are passed with pointers.
1323
 *
1324
 * @param array $taborder Taborder
1325
 * @param array $tabname Tabname
1326
 * @param array $tablib Tablib
1327
 * @param array $tabsql Tabsql
1328
 * @param array $tabsqlsort Tabsqlsort
1329
 * @param array $tabfield Tabfield
1330
 * @param array $tabfieldvalue Tabfieldvalue
1331
 * @param array $tabfieldinsert Tabfieldinsert
1332
 * @param array $tabrowid Tabrowid
1333
 * @param array $tabcond Tabcond
1334
 * @param array $tabhelp Tabhelp
1335
 * @param array $tabcomplete Tab complete (will replace all other in future). Key is table name.
1336
 * @return     int         1
1337
 */
1338
function complete_dictionary_with_modules(&$taborder, &$tabname, &$tablib, &$tabsql, &$tabsqlsort, &$tabfield, &$tabfieldvalue, &$tabfieldinsert, &$tabrowid, &$tabcond, &$tabhelp, &$tabcomplete)
1339
{
1340
    global $db, $modules, $conf, $langs;
1341
1342
    dol_syslog("complete_dictionary_with_modules Search external modules to complete the list of dictionary tables", LOG_DEBUG, 1);
1343
1344
    // Search modules
1345
    $modulesdir = dolGetModulesDirs();
1346
    $i = 0; // is a sequencer of modules found
1347
    $j = 0; // j is module number. Automatically affected if module number not defined.
1348
1349
    $allModules = DolibarrModules::getModules($modulesdir);
1350
    foreach ($allModules as $modName => $filename) {
1351
        $objMod = DolibarrModules::getObj($db, $modName, $filename);
1352
        if (!isset($objMod)) {
1353
            print info_admin("admin/modules.php Warning bad descriptor file : " . $filename . " (Class " . $modName . " not found into file)", 0, 0, '1', 'warning');
1354
            continue;
1355
        }
1356
1357
        if ($objMod->numero > 0) {
1358
            $j = $objMod->numero;
1359
        } else {
1360
            $j = 1000 + $i;
1361
        }
1362
1363
        $modulequalified = 1;
1364
1365
        // We discard modules according to features level (PS: if module is activated we always show it)
1366
        $const_name = 'MAIN_MODULE_' . strtoupper(preg_replace('/^mod/i', '', get_class($objMod)));
1367
        if ($objMod->version == 'development' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2 && !getDolGlobalString($const_name)) {
1368
            $modulequalified = 0;
1369
        }
1370
        if ($objMod->version == 'experimental' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1 && !getDolGlobalString($const_name)) {
1371
            $modulequalified = 0;
1372
        }
1373
        // If module is not activated disqualified
1374
        if (!getDolGlobalString($const_name)) {
1375
            $modulequalified = 0;
1376
        }
1377
1378
        if ($modulequalified) {
1379
            // Load languages files of module
1380
            if (isset($objMod->langfiles) && is_array($objMod->langfiles)) {
1381
                foreach ($objMod->langfiles as $langfile) {
1382
                    $langs->load($langfile);
1383
                }
1384
            }
1385
1386
            // phpcs:disable
1387
            // Complete the arrays &$tabname,&$tablib,&$tabsql,&$tabsqlsort,&$tabfield,&$tabfieldvalue,&$tabfieldinsert,&$tabrowid,&$tabcond
1388
            if (empty($objMod->dictionaries) && !empty($objMod->{"dictionnaries"})) {
1389
                $objMod->dictionaries = $objMod->{"dictionnaries"}; // For backward compatibility
1390
            }
1391
            // phpcs:enable
1392
1393
            if (!empty($objMod->dictionaries)) {
1394
                //var_dump($objMod->dictionaries['tabname']);
1395
                $nbtabname = $nbtablib = $nbtabsql = $nbtabsqlsort = $nbtabfield = $nbtabfieldvalue = $nbtabfieldinsert = $nbtabrowid = $nbtabcond = $nbtabfieldcheck = $nbtabhelp = 0;
1396
                $tabnamerelwithkey = array();
1397
                foreach ($objMod->dictionaries['tabname'] as $key => $val) {
1398
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $val);
1399
                    $nbtabname++;
1400
                    $taborder[] = max($taborder) + 1;
1401
                    $tabname[] = $val;
1402
                    $tabnamerelwithkey[$key] = $val;
1403
                    $tabcomplete[$tmptablename]['picto'] = $objMod->picto;
1404
                }       // Position
1405
                foreach ($objMod->dictionaries['tablib'] as $key => $val) {
1406
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1407
                    $nbtablib++;
1408
                    $tablib[] = $val;
1409
                    $tabcomplete[$tmptablename]['lib'] = $val;
1410
                }
1411
                foreach ($objMod->dictionaries['tabsql'] as $key => $val) {
1412
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1413
                    $nbtabsql++;
1414
                    $tabsql[] = $val;
1415
                    $tabcomplete[$tmptablename]['sql'] = $val;
1416
                }
1417
                foreach ($objMod->dictionaries['tabsqlsort'] as $key => $val) {
1418
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1419
                    $nbtabsqlsort++;
1420
                    $tabsqlsort[] = $val;
1421
                    $tabcomplete[$tmptablename]['sqlsort'] = $val;
1422
                }
1423
                foreach ($objMod->dictionaries['tabfield'] as $key => $val) {
1424
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1425
                    $nbtabfield++;
1426
                    $tabfield[] = $val;
1427
                    $tabcomplete[$tmptablename]['field'] = $val;
1428
                }
1429
                foreach ($objMod->dictionaries['tabfieldvalue'] as $key => $val) {
1430
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1431
                    $nbtabfieldvalue++;
1432
                    $tabfieldvalue[] = $val;
1433
                    $tabcomplete[$tmptablename]['value'] = $val;
1434
                }
1435
                foreach ($objMod->dictionaries['tabfieldinsert'] as $key => $val) {
1436
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1437
                    $nbtabfieldinsert++;
1438
                    $tabfieldinsert[] = $val;
1439
                    $tabcomplete[$tmptablename]['fieldinsert'] = $val;
1440
                }
1441
                foreach ($objMod->dictionaries['tabrowid'] as $key => $val) {
1442
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1443
                    $nbtabrowid++;
1444
                    $tabrowid[] = $val;
1445
                    $tabcomplete[$tmptablename]['rowid'] = $val;
1446
                }
1447
                foreach ($objMod->dictionaries['tabcond'] as $key => $val) {
1448
                    $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1449
                    $nbtabcond++;
1450
                    $tabcond[] = $val;
1451
                    $tabcomplete[$tmptablename]['rowid'] = $val;
1452
                }
1453
                if (!empty($objMod->dictionaries['tabhelp'])) {
1454
                    foreach ($objMod->dictionaries['tabhelp'] as $key => $val) {
1455
                        $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1456
                        $nbtabhelp++;
1457
                        $tabhelp[] = $val;
1458
                        $tabcomplete[$tmptablename]['help'] = $val;
1459
                    }
1460
                }
1461
                if (!empty($objMod->dictionaries['tabfieldcheck'])) {
1462
                    foreach ($objMod->dictionaries['tabfieldcheck'] as $key => $val) {
1463
                        $tmptablename = preg_replace('/' . MAIN_DB_PREFIX . '/', '', $tabnamerelwithkey[$key]);
1464
                        $nbtabfieldcheck++;
1465
                        $tabcomplete[$tmptablename]['fieldcheck'] = $val;
1466
                    }
1467
                }
1468
1469
                if ($nbtabname != $nbtablib || $nbtablib != $nbtabsql || $nbtabsql != $nbtabsqlsort) {
1470
                    print 'Error in descriptor of module ' . $const_name . '. Array ->dictionaries has not same number of record for key "tabname", "tablib", "tabsql" and "tabsqlsort"';
1471
                    //print "$const_name: $nbtabname=$nbtablib=$nbtabsql=$nbtabsqlsort=$nbtabfield=$nbtabfieldvalue=$nbtabfieldinsert=$nbtabrowid=$nbtabcond=$nbtabfieldcheck=$nbtabhelp\n";
1472
                } else {
1473
                    $taborder[] = 0; // Add an empty line
1474
                }
1475
            }
1476
1477
            $j++;
1478
            $i++;
1479
        } else {
1480
            dol_syslog("Module " . get_class($objMod) . " not qualified");
1481
        }
1482
    }
1483
1484
    dol_syslog("", LOG_DEBUG, -1);
1485
1486
    return 1;
1487
}
1488
1489
/**
1490
 *  Activate external modules mandatory when country is country_code
1491
 *
1492
 * @param string $country_code CountryCode
1493
 * @return     int         1
1494
 */
1495
function activateModulesRequiredByCountry($country_code)
1496
{
1497
    global $db, $conf, $langs;
1498
1499
    $modulesdir = dolGetModulesDirs();
1500
    $allModules = DolibarrModules::getModules($modulesdir);
1501
    foreach ($allModules as $modName => $filename) {
1502
        $objMod = DolibarrModules::getObj($db, $modName, $filename);
1503
        if (!isset($objMod)) {
1504
            print info_admin("admin/modules.php Warning bad descriptor file : " . $filename . " (Class " . $modName . " not found into file)", 0, 0, '1', 'warning');
1505
            continue;
1506
        }
1507
1508
        $modulequalified = 1;
1509
1510
        // We discard modules according to features level (PS: if module is activated we always show it)
1511
        $const_name = 'MAIN_MODULE_' . strtoupper(preg_replace('/^mod/i', '', get_class($objMod)));
1512
1513
        if ($objMod->version == 'development' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2) {
1514
            $modulequalified = 0;
1515
        }
1516
        if ($objMod->version == 'experimental' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1) {
1517
            $modulequalified = 0;
1518
        }
1519
        if (getDolGlobalString($const_name)) {
1520
            $modulequalified = 0; // already activated
1521
        }
1522
1523
        if ($modulequalified) {
1524
            // Load languages files of module
1525
            if (isset($objMod->automatic_activation) && is_array($objMod->automatic_activation) && isset($objMod->automatic_activation[$country_code])) {
1526
                activateModule($modName);
1527
1528
                setEventMessages($objMod->automatic_activation[$country_code], null, 'warnings');
1529
            }
1530
        } else {
1531
            dol_syslog("Module " . get_class($objMod) . " not qualified");
1532
        }
1533
    }
1534
1535
    return 1;
1536
}
1537
1538
/**
1539
 *  Search external modules to complete the list of contact element
1540
 *
1541
 * @param array<string,string> $elementList elementList
1542
 * @return     int         1
1543
 */
1544
function complete_elementList_with_modules(&$elementList)
1545
{
1546
    global $db, $modules, $conf, $langs;
1547
1548
    // Search modules
1549
    $filename = array();
1550
    $modules = array();
1551
    $orders = array();
1552
    $categ = array();
1553
    $dirmod = array();
1554
1555
    $i = 0; // is a sequencer of modules found
1556
    $j = 0; // j is module number. Automatically affected if module number not defined.
1557
1558
    dol_syslog("complete_elementList_with_modules Search external modules to complete the list of contact element", LOG_DEBUG, 1);
1559
1560
    $modulesdir = dolGetModulesDirs();
1561
    $allModules = DolibarrModules::getModules($modulesdir);
1562
    foreach ($allModules as $modName => $filename) {
1563
        $objMod = DolibarrModules::getObj($db, $modName, $filename);
1564
1565
        if (!isset($objMod)) {
1566
            print info_admin("admin/modules.php Warning bad descriptor file : " . $filename . " (Class " . $modName . " not found into file)", 0, 0, '1', 'warning');
1567
            continue;
1568
        }
1569
1570
        if ($objMod->numero > 0) {
1571
            $j = $objMod->numero;
1572
        } else {
1573
            $j = 1000 + $i;
1574
        }
1575
1576
        $modulequalified = 1;
1577
1578
        // We discard modules according to features level (PS: if module is activated we always show it)
1579
        $const_name = 'MAIN_MODULE_' . strtoupper(preg_replace('/^mod/i', '', get_class($objMod)));
1580
        if ($objMod->version == 'development' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2 && getDolGlobalString($const_name)) {
1581
            $modulequalified = 0;
1582
        }
1583
        if ($objMod->version == 'experimental' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1 && getDolGlobalString($const_name)) {
1584
            $modulequalified = 0;
1585
        }
1586
        // If module is not activated disqualified
1587
        if (!getDolGlobalString($const_name)) {
1588
            $modulequalified = 0;
1589
        }
1590
1591
        if ($modulequalified) {
1592
            // Load languages files of module
1593
            if (isset($objMod->langfiles) && is_array($objMod->langfiles)) {
1594
                foreach ($objMod->langfiles as $langfile) {
1595
                    $langs->load($langfile);
1596
                }
1597
            }
1598
1599
            $modules[$i] = $objMod;
1600
            $filename[$i] = $modName;
1601
            $orders[$i] = $objMod->family . "_" . $j; // Sort on family then module number
1602
            $dirmod[$i] = $dir;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dir seems to be never defined.
Loading history...
1603
            //print "x".$modName." ".$orders[$i]."\n<br>";
1604
1605
            if (!empty($objMod->module_parts['contactelement'])) {
1606
                if (is_array($objMod->module_parts['contactelement'])) {
1607
                    foreach ($objMod->module_parts['contactelement'] as $elem => $title) {
1608
                        $elementList[$elem] = $langs->trans($title);
1609
                    }
1610
                } else {
1611
                    $elementList[$objMod->name] = $langs->trans($objMod->name);
1612
                }
1613
            }
1614
1615
            $j++;
1616
            $i++;
1617
        } else {
1618
            dol_syslog("Module " . get_class($objMod) . " not qualified");
1619
        }
1620
    }
1621
1622
    dol_syslog("", LOG_DEBUG, -1);
1623
1624
    return 1;
1625
}
1626
1627
/**
1628
 *  Show array with constants to edit
1629
 *
1630
 * @param array<string,array{type:string,label:string}> $tableau Array of constants array('key'=>array('type'=>type, 'label'=>label)
1631
 *                                                                          where type can be 'string', 'text', 'textarea', 'html', 'yesno', 'emailtemplate:xxx', ...
1632
 * @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)
1633
 * @param string $helptext Tooltip help to use for the column name of values
1634
 * @param string $text Text to use for the column name of values
1635
 * @return void
1636
 */
1637
function form_constantes($tableau, $strictw3c = 2, $helptext = '', $text = 'Value')
1638
{
1639
    global $db, $langs, $conf, $user;
1640
    global $_Avery_Labels;
1641
1642
    $form = new Form($db);
1643
1644
    if (empty($strictw3c)) {
1645
        dol_syslog("Warning: Function 'form_constantes' was called with parameter strictw3c = 0, this is deprecated. Value must be 2 now.", LOG_DEBUG);
1646
    }
1647
    if (!empty($strictw3c) && $strictw3c == 1) {
1648
        print "\n" . '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
1649
        print '<input type="hidden" name="token" value="' . newToken() . '">';
1650
        print '<input type="hidden" name="action" value="updateall">';
1651
    }
1652
1653
    print '<div class="div-table-responsive-no-min">';
1654
    print '<table class="noborder centpercent">';
1655
    print '<tr class="liste_titre">';
1656
    print '<td class="">' . $langs->trans("Description") . '</td>';
1657
    print '<td>';
1658
    $text = $langs->trans($text);
1659
    print $form->textwithpicto($text, $helptext, 1, 'help', '', 0, 2, 'idhelptext');
1660
    print '</td>';
1661
    if (empty($strictw3c)) {
1662
        print '<td class="center" width="80">' . $langs->trans("Action") . '</td>';
1663
    }
1664
    print "</tr>\n";
1665
1666
    $label = '';
1667
    foreach ($tableau as $key => $const) {  // Loop on each param
1668
        $label = '';
1669
        // $const is a const key like 'MYMODULE_ABC'
1670
        if (is_numeric($key)) {     // Very old behaviour
1671
            $type = 'string';
1672
        } else {
1673
            if (is_array($const)) {
1674
                $type = $const['type'];
1675
                $label = $const['label'];
1676
                $const = $key;
1677
            } else {
1678
                $type = $const;
1679
                $const = $key;
1680
            }
1681
        }
1682
1683
        $sql = "SELECT ";
1684
        $sql .= "rowid";
1685
        $sql .= ", " . $db->decrypt('name') . " as name";
1686
        $sql .= ", " . $db->decrypt('value') . " as value";
1687
        $sql .= ", type";
1688
        $sql .= ", note";
1689
        $sql .= " FROM " . MAIN_DB_PREFIX . "const";
1690
        $sql .= " WHERE " . $db->decrypt('name') . " = '" . $db->escape($const) . "'";
1691
        $sql .= " AND entity IN (0, " . $conf->entity . ")";
1692
        $sql .= " ORDER BY name ASC, entity DESC";
1693
        $result = $db->query($sql);
1694
1695
        dol_syslog("List params", LOG_DEBUG);
1696
        if ($result) {
1697
            $obj = $db->fetch_object($result); // Take first result of select
1698
1699
            if (empty($obj)) {  // If not yet into table
1700
                $obj = (object)array('rowid' => '', 'name' => $const, 'value' => '', 'type' => $type, 'note' => '');
1701
            }
1702
1703
            if (empty($strictw3c)) {
1704
                print "\n" . '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
1705
                print '<input type="hidden" name="token" value="' . newToken() . '">';
1706
                print '<input type="hidden" name="page_y" value="' . newToken() . '">';
1707
            }
1708
1709
            print '<tr class="oddeven">';
1710
1711
            // Show label of parameter
1712
            print '<td>';
1713
            if (empty($strictw3c)) {
1714
                print '<input type="hidden" name="action" value="update">';
1715
            }
1716
            print '<input type="hidden" name="rowid' . (empty($strictw3c) ? '' : '[]') . '" value="' . $obj->rowid . '">';
1717
            print '<input type="hidden" name="constname' . (empty($strictw3c) ? '' : '[]') . '" value="' . $const . '">';
1718
            print '<input type="hidden" name="constnote_' . $obj->name . '" value="' . nl2br(dol_escape_htmltag($obj->note)) . '">';
1719
            print '<input type="hidden" name="consttype_' . $obj->name . '" value="' . ($obj->type ? $obj->type : 'string') . '">';
1720
            if (!empty($tableau[$key]['tooltip'])) {
1721
                print $form->textwithpicto($label ? $label : $langs->trans('Desc' . $const), $tableau[$key]['tooltip']);
1722
            } else {
1723
                print($label ? $label : $langs->trans('Desc' . $const));
1724
            }
1725
1726
            if ($const == 'ADHERENT_MAILMAN_URL') {
1727
                print '. ' . $langs->trans("Example") . ': <a href="#" id="exampleclick1">' . img_down() . '</a><br>';
1728
                //print 'http://lists.example.com/cgi-bin/mailman/admin/%LISTE%/members?adminpw=%MAILMAN_ADMINPW%&subscribees=%EMAIL%&send_welcome_msg_to_this_batch=1';
1729
                print '<div id="example1" class="hidden">';
1730
                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';
1731
                print '</div>';
1732
            } elseif ($const == 'ADHERENT_MAILMAN_UNSUB_URL') {
1733
                print '. ' . $langs->trans("Example") . ': <a href="#" id="exampleclick2">' . img_down() . '</a><br>';
1734
                print '<div id="example2" class="hidden">';
1735
                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';
1736
                print '</div>';
1737
                //print 'http://lists.example.com/cgi-bin/mailman/admin/%LISTE%/members/remove?adminpw=%MAILMAN_ADMINPW%&unsubscribees=%EMAIL%';
1738
            } elseif ($const == 'ADHERENT_MAILMAN_LISTS') {
1739
                print '. ' . $langs->trans("Example") . ': <a href="#" id="exampleclick3">' . img_down() . '</a><br>';
1740
                print '<div id="example3" class="hidden">';
1741
                print 'mymailmanlist<br>';
1742
                print 'mymailmanlist1,mymailmanlist2<br>';
1743
                print 'TYPE:Type1:mymailmanlist1,TYPE:Type2:mymailmanlist2<br>';
1744
                if (isModEnabled('category')) {
1745
                    print 'CATEG:Categ1:mymailmanlist1,CATEG:Categ2:mymailmanlist2<br>';
1746
                }
1747
                print '</div>';
1748
                //print 'http://lists.example.com/cgi-bin/mailman/admin/%LISTE%/members/remove?adminpw=%MAILMAN_ADMINPW%&unsubscribees=%EMAIL%';
1749
            } elseif (in_array($const, ['ADHERENT_MAIL_FROM', 'ADHERENT_CC_MAIL_FROM'])) {
1750
                print ' ' . img_help(1, $langs->trans("EMailHelpMsgSPFDKIM"));
1751
            }
1752
1753
            print "</td>\n";
1754
1755
            // Value
1756
            if ($const == 'ADHERENT_CARD_TYPE' || $const == 'ADHERENT_ETIQUETTE_TYPE') {
1757
                print '<td>';
1758
                // List of possible labels (defined into $_Avery_Labels variable set into format_cards.lib.php)
1759
                require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/format_cards.lib.php';
1760
                $arrayoflabels = array();
1761
                foreach (array_keys($_Avery_Labels) as $codecards) {
1762
                    $arrayoflabels[$codecards] = $_Avery_Labels[$codecards]['name'];
1763
                }
1764
                print $form->selectarray('constvalue' . (empty($strictw3c) ? '' : ($strictw3c == 3 ? '_' . $const : '[]')), $arrayoflabels, ($obj->value ? $obj->value : 'CARD'), 1, 0, 0);
1765
                print '<input type="hidden" name="consttype" value="yesno">';
1766
                print '<input type="hidden" name="constnote' . (empty($strictw3c) ? '' : '[]') . '" value="' . nl2br(dol_escape_htmltag($obj->note)) . '">';
1767
                print '</td>';
1768
            } else {
1769
                print '<td>';
1770
                print '<input type="hidden" name="consttype' . (empty($strictw3c) ? '' : ($strictw3c == 3 ? '_' . $const : '[]')) . '" value="' . ($obj->type ? $obj->type : 'string') . '">';
1771
                print '<input type="hidden" name="constnote' . (empty($strictw3c) ? '' : ($strictw3c == 3 ? '_' . $const : '[]')) . '" value="' . nl2br(dol_escape_htmltag($obj->note)) . '">';
1772
                if ($obj->type == 'textarea' || in_array($const, array('ADHERENT_CARD_TEXT', 'ADHERENT_CARD_TEXT_RIGHT', 'ADHERENT_ETIQUETTE_TEXT'))) {
1773
                    print '<textarea class="flat" name="constvalue' . (empty($strictw3c) ? '' : ($strictw3c == 3 ? '_' . $const : '[]')) . '" cols="50" rows="5" wrap="soft">' . "\n";
1774
                    print $obj->value;
1775
                    print "</textarea>\n";
1776
                } elseif ($obj->type == 'html') {
1777
                    $doleditor = new DolEditor('constvalue' . (empty($strictw3c) ? '' : ($strictw3c == 3 ? '_' . $const : '[]')), $obj->value, '', 160, 'dolibarr_notes', '', false, false, isModEnabled('fckeditor'), ROWS_5, '90%');
1778
                    $doleditor->Create();
1779
                } elseif ($obj->type == 'yesno') {
1780
                    print $form->selectyesno('constvalue' . (empty($strictw3c) ? '' : ($strictw3c == 3 ? '_' . $const : '[]')), $obj->value, 1, false, 0, 1);
1781
                } elseif (preg_match('/emailtemplate/', $obj->type)) {
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