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

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

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