Passed
Push — main ( d07ee1...38048d )
by Rafael
50:23
created

DolibarrModules::_load_tables()   D

Complexity

Conditions 54
Paths 5

Size

Total Lines 172
Code Lines 96

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 54
eloc 96
nc 5
nop 2
dl 0
loc 172
rs 4.1666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/* Copyright (C) 2003-2007  Rodolphe Quiedeville    <[email protected]>
4
 * Copyright (C) 2004       Sebastien Di Cintio     <[email protected]>
5
 * Copyright (C) 2004       Benoit Mortier          <[email protected]>
6
 * Copyright (C) 2004       Eric Seigne             <[email protected]>
7
 * Copyright (C) 2005-2013  Laurent Destailleur     <[email protected]>
8
 * Copyright (C) 2005-2012  Regis Houssin           <[email protected]>
9
 * Copyright (C) 2014       Raphaël Doursenaud      <[email protected]>
10
 * Copyright (C) 2018       Josep Lluís Amador      <[email protected]>
11
 * Copyright (C) 2019-2024  Frédéric France         <[email protected]>
12
 * Copyright (C) 2024		MDW						<[email protected]>
13
 * Copyright (C) 2024       Rafael San José         <[email protected]>
14
 *
15
 * This program is free software; you can redistribute it and/or modify
16
 * it under the terms of the GNU General Public License as published by
17
 * the Free Software Foundation; either version 3 of the License, or
18
 * (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU General Public License
26
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
27
 */
28
29
/**
30
 * \file           htdocs/core/modules/DolibarrModules.class.php
31
 * \brief          File of parent class of module descriptor class files
32
 */
33
34
use DoliCore\Base\Config;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Config. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
35
use DoliCore\Model\InfoBox;
36
use DoliCore\Model\Menubase;
37
use DoliModules\User\Model\User;
38
39
require_once BASE_PATH . '/../Dolibarr/Core/Menu/standard/eldy_menu.php';
40
41
/**
42
 * Class DolibarrModules
43
 *
44
 * Parent class for module descriptor class files
45
 */
46
class DolibarrModules // Can not be abstract, because we need to instantiate it into unActivateModule to be able to disable a module whose files were removed.
47
{
48
    /**
49
     * @var DoliDB Database handler
50
     */
51
    public $db;
52
53
    /**
54
     * @var int Module unique ID
55
     * @see https://wiki.dolibarr.org/index.php/List_of_modules_id
56
     */
57
    public $numero;
58
59
    /**
60
     * @var   string Publisher name
61
     * @since 4.0.0
62
     */
63
    public $editor_name;
64
65
    /**
66
     * @var   string URL of module at publisher site
67
     * @since 4.0.0
68
     */
69
    public $editor_url;
70
71
    /**
72
     * @var string  Family
73
     * @see $familyinfo
74
     *
75
     * Native values: 'crm', 'financial', 'hr', 'projects', 'products', 'ecm', 'technic', 'other'.
76
     * Use familyinfo to declare a custom value.
77
     */
78
    public $family;
79
80
    /**
81
     * @var array<string,array{position:string,label:string}> Custom family information
82
     * @see $family
83
     *
84
     * e.g.:
85
     * array(
86
     *     'myownfamily' => array(
87
     *         'position' => '001',
88
     *         'label' => $langs->trans("MyOwnFamily")
89
     *     )
90
     * );
91
     */
92
    public $familyinfo;
93
94
    /**
95
     * @var string    Module position on 2 digits
96
     */
97
    public $module_position = '50';
98
99
    /**
100
     * @var string Module name
101
     *
102
     * Only used if Module[ID]Name translation string is not found.
103
     *
104
     * You can use the following code to automatically derive it from your module's class name:
105
     * preg_replace('/^mod/i', '', get_class($this))
106
     */
107
    public $name;
108
109
    /**
110
     * @var string[] Paths to create when module is activated
111
     *
112
     * e.g.: array('/mymodule/temp')
113
     */
114
    public $dirs = [];
115
116
    /**
117
     * @var array Module boxes
118
     */
119
    public $boxes = [];
120
121
    /**
122
     * @var array Module constants
123
     */
124
    public $const = [];
125
126
    /**
127
     * @var array Module cron jobs entries
128
     */
129
    public $cronjobs = [];
130
131
    /**
132
     * @var array   Module access rights
133
     */
134
    public $rights;
135
136
    /**
137
     * @var int     1=Admin is always granted of permission of modules (even when module is disabled)
138
     */
139
    public $rights_admin_allowed;
140
141
    /**
142
     * @var string  Module access rights family
143
     */
144
    public $rights_class;
145
146
    /**
147
     * @var array|int   Module menu entries (1 means the menu entries are not declared into module descriptor but are
148
     *      hardcoded into menu manager)
149
     */
150
    public $menu = [];
151
152
    /**
153
     * @var array{triggers?:int<0,1>,login?:int<0,1>,substitutions?:int<0,1>,menus?:int<0,1>,theme?:int<0,1>,tpl?:int<0,1>,barcode?:int<0,1>,models?:int<0,1>,printing?:int<0,1>,css?:string[],js?:string[],hooks?:array{data?:string[],entity?:string},moduleforexternal?:int<0,1>,websitetemplates?:int<0,1>,contactelement?:int<0,1>}
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{triggers?:int<0,1>...ntactelement?:int<0,1>} at position 4 could not be parsed: Expected '}' at position 4, but found 'int'.
Loading history...
154
     *      Module parts array(
155
     *      // Set this to 1 if module has its own trigger directory (/mymodule/core/triggers)
156
     *      'triggers' => 0,
157
     *      // Set this to 1 if module has its own login method directory (/mymodule/core/login)
158
     *      'login' => 0,
159
     *      // Set this to 1 if module has its own substitution function file (/mymodule/core/substitutions)
160
     *      'substitutions' => 0,
161
     *      // Set this to 1 if module has its own menus handler directory (/mymodule/core/menus)
162
     *      'menus' => 0,
163
     *      // Set this to 1 if module has its own theme directory (/mymodule/theme)
164
     *      'theme' => 0,
165
     *      // Set this to 1 if module overwrite template dir (/mymodule/core/tpl)
166
     *      'tpl' => 0,
167
     *      // Set this to 1 if module has its own barcode directory (/mymodule/core/modules/barcode)
168
     *      'barcode' => 0,
169
     *      // Set this to 1 if module has its own models directory (/mymodule/core/modules/xxx)
170
     *      'models' => 0,
171
     *      // Set this to relative path of css file if module has its own css file
172
     *      'css' => '/mymodule/css/mymodule.css.php',
173
     *      // Set this to relative path of js file if module must load a js on all pages
174
     *      'js' => '/mymodule/js/mymodule.js',
175
     *      // Set here all hooks context managed by module
176
     *      'hooks' => array('hookcontext1','hookcontext2')
177
     *      )
178
     */
179
    public $module_parts = [];
180
181
    /**
182
     * @var        string Module documents ?
183
     * @deprecated Seems unused anywhere
184
     */
185
    public $docs;
186
187
    /**
188
     * @var        string ?
189
     * @deprecated Seems unused anywhere
190
     */
191
    public $dbversion = "-";
192
193
    /**
194
     * @var string Error message
195
     */
196
    public $error;
197
198
    /**
199
     * @var string[] Array of Errors messages
200
     */
201
    public $errors;
202
203
    /**
204
     * @var string Module version
205
     * @see http://semver.org
206
     *
207
     * The following keywords can also be used:
208
     * 'development'
209
     * 'experimental'
210
     * 'dolibarr': only for core modules that share its version
211
     * 'dolibarr_deprecated': only for deprecated core modules
212
     */
213
    public $version;
214
215
    /**
216
     * Module last version
217
     * @var string $lastVersion
218
     */
219
    public $lastVersion = '';
220
221
    /**
222
     * true indicate this module need update
223
     * @var bool $needUpdate
224
     */
225
    public $needUpdate = false;
226
227
    /**
228
     * @var string Module description (short text)
229
     *
230
     * Only used if Module[ID]Desc translation string is not found.
231
     */
232
    public $description;
233
234
    /**
235
     * @var   string Module description (long text)
236
     * @since 4.0.0
237
     *
238
     * HTML content supported.
239
     */
240
    public $descriptionlong;
241
242
    /**
243
     * @var array dictionaries description
244
     */
245
    public $dictionaries;
246
247
    /**
248
     * @var array tabs description
249
     */
250
    public $tabs;
251
252
    // For exports
253
254
    /**
255
     * @var string Module export code
256
     */
257
    public $export_code;
258
259
    /**
260
     * @var string[] Module export label
261
     */
262
    public $export_label;
263
264
    public $export_icon;
265
266
    /**
267
     * @var array export enabled
268
     */
269
    public $export_enabled;
270
    public $export_permission;
271
    public $export_fields_array;
272
    public $export_TypeFields_array; // Array of key=>type where type can be 'Numeric', 'Date', 'Text', 'Boolean', 'Status', 'List:xxx:login:rowid'
273
    public $export_entities_array;
274
    public $export_aggregate_array;
275
    public $export_examplevalues_array;
276
    public $export_help_array;
277
    public $export_special_array; // special or computed field
278
    public $export_dependencies_array;
279
    public $export_sql_start;
280
    public $export_sql_end;
281
    public $export_sql_order;
282
283
284
    // For import
285
286
    /**
287
     * @var string Module import code
288
     */
289
    public $import_code;
290
291
    /**
292
     * @var string[] Module import label
293
     */
294
    public $import_label;
295
296
    public $import_icon;
297
    public $import_entities_array;
298
    public $import_tables_array;
299
    public $import_tables_creator_array;
300
    public $import_fields_array;
301
    public $import_fieldshidden_array;
302
    public $import_convertvalue_array;
303
    public $import_regex_array;
304
    public $import_examplevalues_array;
305
    public $import_updatekeys_array;
306
    public $import_run_sql_after_array;
307
    public $import_TypeFields_array;
308
    public $import_help_array;
309
310
    /**
311
     * @var string Module constant name
312
     */
313
    public $const_name;
314
315
    /**
316
     * @var bool Module can't be disabled
317
     */
318
    public $always_enabled;
319
320
    /**
321
     * @var bool Module is disabled
322
     */
323
    public $disabled;
324
325
    /**
326
     * @var int Module is enabled globally (Multicompany support)
327
     */
328
    public $core_enabled;
329
330
    /**
331
     * @var string Name of image file used for this module
332
     *
333
     * If file is in theme/yourtheme/img directory under name object_pictoname.png use 'pictoname'
334
     * If file is in module/img directory under name object_pictoname.png use 'pictoname@module'
335
     */
336
    public $picto;
337
338
    /**
339
     * @var string[]|string     List of config pages (Old modules uses a string. New one must use an array)
340
     *
341
     * Name of php pages stored into module/admin directory, used to setup module.
342
     * e.g.: array("setup.php@mymodule")
343
     */
344
    public $config_page_url;
345
346
347
    /**
348
     * @var array   List of module class names that must be enabled if this module is enabled. e.g.:
349
     *      array('modAnotherModule', 'FR'=>'modYetAnotherModule') Another example : array('always'=>array("modBanque",
350
     *      "modFacture", "modProduct", "modCategorie"), 'FR'=>array('modBlockedLog'));
351
     * @see $requiredby
352
     */
353
    public $depends;
354
355
    /**
356
     * @var string[] List of module class names to disable if the module is disabled.
357
     * @see $depends
358
     */
359
    public $requiredby;
360
361
    /**
362
     * @var string[] List of module class names as string this module is in conflict with.
363
     * @see $depends
364
     */
365
    public $conflictwith;
366
367
    /**
368
     * @var string[] Module language files
369
     */
370
    public $langfiles;
371
372
    /**
373
     * @var array<string,string> Array of warnings to show when we activate the module
374
     *
375
     * array('always'='text') or array('FR'='text')
376
     */
377
    public $warnings_activation;
378
379
    /**
380
     * @var array<string,string> Array of warnings to show when we activate an external module
381
     *
382
     * array('always'='text') or array('FR'='text')
383
     */
384
    public $warnings_activation_ext;
385
386
    /**
387
     * @var array<string,string> Array of warnings to show when we disable the module
388
     *
389
     * array('always'='text') or array('FR'='text')
390
     */
391
    public $warnings_unactivation;
392
393
    /**
394
     * @var array Minimum version of PHP required by module.
395
     * e.g.: PHP ≥ 7.0 = array(7, 0)
396
     */
397
    public $phpmin;
398
399
    public $phpmax;
400
401
    /**
402
     * @var array Minimum version of Dolibarr required by module.
403
     * e.g.: Dolibarr ≥ 3.6 = array(3, 6)
404
     */
405
    public $need_dolibarr_version;
406
407
    public $need_javascript_ajax;
408
409
    public $enabled_bydefault;
410
411
    /**
412
     * @var bool|int Whether to hide the module.
413
     */
414
    public $hidden = false;
415
416
    /**
417
     * @var string url to check for module update
418
     */
419
    public $url_last_version;
420
421
422
    /**
423
     * Constructor. Define names, constants, directories, boxes, permissions
424
     *
425
     * @param DoliDB $db Database handler
426
     */
427
    public function __construct($db)
428
    {
429
        $this->db = $db;
430
    }
431
    // We should but can't set this as abstract because this will make dolibarr hang
432
    // after migration due to old module not implementing. We must wait PHP is able to make
433
    // a try catch on Fatal error to manage this correctly.
434
    // We need constructor into function unActivateModule into admin.lib.php
435
436
437
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
438
439
    /**
440
     * Gives the long description of a module. First check README-la_LA.md then README.md
441
     * If no markdown files found, it returns translated value of the key ->descriptionlong.
442
     *
443
     * @return string     Long description of a module from README.md of from property.
444
     */
445
    public function getDescLong()
446
    {
447
        global $langs;
448
        $langs->load("admin");
449
450
        include_once BASE_PATH . '/../Dolibarr/Lib/Files.php';
451
        include_once BASE_PATH . '/../Dolibarr/Lib/GetUrl.php';
452
453
        $content = '';
454
        $pathoffile = $this->getDescLongReadmeFound();
455
456
        if ($pathoffile) {     // Mostly for external modules
457
            $content = file_get_contents($pathoffile);
458
459
            if ((float)DOL_VERSION >= 6.0) {
460
                @include_once BASE_PATH . '/../Dolibarr/Lib/ParseMd.php';
461
462
                $content = dolMd2Html(
463
                    $content,
464
                    'parsedown',
465
                    [
466
                        'doc/' => dol_buildpath(strtolower($this->name) . '/doc/', 1),
467
                        'img/' => dol_buildpath(strtolower($this->name) . '/img/', 1),
468
                        'images/' => dol_buildpath(strtolower($this->name) . '/images/', 1),
469
                    ]
470
                );
471
472
                $content = preg_replace('/<a href="/', '<a target="_blank" rel="noopener noreferrer" href="', $content);
473
            } else {
474
                $content = nl2br($content);
475
            }
476
        } else {
477
            // Mostly for internal modules
478
            if (!empty($this->descriptionlong)) {
479
                if (is_array($this->langfiles)) {
480
                    foreach ($this->langfiles as $val) {
481
                        if ($val) {
482
                            $langs->load($val);
483
                        }
484
                    }
485
                }
486
487
                $content = $langs->transnoentitiesnoconv($this->descriptionlong);
488
            }
489
        }
490
491
        return $content;
492
    }
493
494
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
495
496
    /**
497
     * Return path of file if a README file was found.
498
     *
499
     * @return string      Path of file if a README file was found.
500
     */
501
    public function getDescLongReadmeFound()
502
    {
503
        global $langs;
504
505
        $filefound = false;
506
507
        // Define path to file README.md.
508
        // First check README-la_LA.md then README-la.md then README.md
509
        $pathoffile = dol_buildpath(strtolower($this->name) . '/README-' . $langs->defaultlang . '.md', 0);
510
        if (dol_is_file($pathoffile)) {
511
            $filefound = true;
512
        }
513
        if (!$filefound) {
514
            $tmp = explode('_', $langs->defaultlang);
515
            $pathoffile = dol_buildpath(strtolower($this->name) . '/README-' . $tmp[0] . '.md', 0);
516
            if (dol_is_file($pathoffile)) {
517
                $filefound = true;
518
            }
519
        }
520
        if (!$filefound) {
521
            $pathoffile = dol_buildpath(strtolower($this->name) . '/README.md', 0);
522
            if (dol_is_file($pathoffile)) {
523
                $filefound = true;
524
            }
525
        }
526
527
        return ($filefound ? $pathoffile : '');
528
    }
529
530
    /**
531
     * Gives the changelog. First check ChangeLog-la_LA.md then ChangeLog.md
532
     *
533
     * @return string  Content of ChangeLog
534
     */
535
    public function getChangeLog()
536
    {
537
        global $langs;
538
        $langs->load("admin");
539
540
        include_once BASE_PATH . '/../Dolibarr/Lib/Files.php';
541
        include_once BASE_PATH . '/../Dolibarr/Lib/GetUrl.php';
542
543
        $filefound = false;
544
545
        // Define path to file README.md.
546
        // First check ChangeLog-la_LA.md then ChangeLog.md
547
        $pathoffile = dol_buildpath(strtolower($this->name) . '/ChangeLog-' . $langs->defaultlang . '.md', 0);
548
        if (dol_is_file($pathoffile)) {
549
            $filefound = true;
550
        }
551
        if (!$filefound) {
552
            $pathoffile = dol_buildpath(strtolower($this->name) . '/ChangeLog.md', 0);
553
            if (dol_is_file($pathoffile)) {
554
                $filefound = true;
555
            }
556
        }
557
558
        if ($filefound) {     // Mostly for external modules
559
            $content = file_get_contents($pathoffile);
560
561
            if ((float)DOL_VERSION >= 6.0) {
562
                @include_once BASE_PATH . '/../Dolibarr/Lib/ParseMd.php';
563
564
                $content = dolMd2Html($content, 'parsedown', ['doc/' => dol_buildpath(strtolower($this->name) . '/doc/', 1)]);
565
            } else {
566
                $content = nl2br($content);
567
            }
568
        }
569
570
        return $content;
571
    }
572
573
    /**
574
     * Gives the publisher name
575
     *
576
     * @return string  Publisher name
577
     */
578
    public function getPublisher()
579
    {
580
        return $this->editor_name;
581
    }
582
583
    /**
584
     * Gives the publisher url
585
     *
586
     * @return string  Publisher url
587
     */
588
    public function getPublisherUrl()
589
    {
590
        return $this->editor_url;
591
    }
592
593
    /**
594
     * Gives the module position
595
     *
596
     * @return string   Module position (an external module should never return a value lower than 100000. 1-100000 are
597
     *                  reserved for core)
598
     */
599
    public function getModulePosition()
600
    {
601
        if (in_array($this->version, ['dolibarr', 'experimental', 'development'])) {   // core module
602
            return $this->module_position;
603
        } else {                                                                            // external module
604
            if ($this->module_position >= 100000) {
605
                return $this->module_position;
606
            } else {
607
                $position = intval($this->module_position) + 100000;
608
                return strval($position);
609
            }
610
        }
611
    }
612
613
    /**
614
     * Gives module related language files list
615
     *
616
     * @return string[]    Language files list
617
     */
618
    public function getLangFilesArray()
619
    {
620
        return $this->langfiles;
621
    }
622
623
    /**
624
     * Gives translated label of an export dataset
625
     *
626
     * @param int $r Dataset index
627
     *
628
     * @return string       Translated databaset label
629
     */
630
    public function getExportDatasetLabel($r)
631
    {
632
        global $langs;
633
634
        $langstring = "ExportDataset_" . $this->export_code[$r];
635
        if ($langs->trans($langstring) == $langstring) {
636
            // Translation not found
637
            return $langs->trans($this->export_label[$r]);
638
        } else {
639
            // Translation found
640
            return $langs->trans($langstring);
641
        }
642
    }
643
644
    /**
645
     * Gives translated label of an import dataset
646
     *
647
     * @param int $r Dataset index
648
     *
649
     * @return string      Translated dataset label
650
     */
651
    public function getImportDatasetLabel($r)
652
    {
653
        global $langs;
654
655
        $langstring = "ImportDataset_" . $this->import_code[$r];
656
        //print "x".$langstring;
657
        if ($langs->trans($langstring) == $langstring) {
658
            // Translation not found
659
            return $langs->transnoentitiesnoconv($this->import_label[$r]);
660
        } else {
661
            // Translation found
662
            return $langs->transnoentitiesnoconv($langstring);
663
        }
664
    }
665
666
    /**
667
     * Gives the last date of activation
668
     *
669
     * @return  int|string          Date of last activation or '' if module was never activated
670
     */
671
    public function getLastActivationDate()
672
    {
673
        global $conf;
674
        $dbPrefix = $conf->db->prefix;
675
676
        $err = 0;
677
678
        $sql = "SELECT tms FROM " . $dbPrefix . "const";
679
        $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
680
        $sql .= " AND entity IN (0, " . ((int)$conf->entity) . ")";
681
682
        dol_syslog(get_class($this) . "::getLastActiveDate", LOG_DEBUG);
683
        $resql = $this->db->query($sql);
684
        if (!$resql) {
685
            $err++;
686
        } else {
687
            $obj = $this->db->fetch_object($resql);
688
            if ($obj) {
689
                return $this->db->jdate($obj->tms);
690
            }
691
        }
692
693
        return '';
694
    }
695
696
    /**
697
     * Gives the last author of activation
698
     *
699
     * @return array       Array array('authorid'=>Id of last activation user, 'lastactivationdate'=>Date of last
700
     *                     activation)
701
     */
702
    public function getLastActivationInfo()
703
    {
704
        global $conf;
705
        $dbPrefix = $conf->db->prefix;
706
707
        $err = 0;
708
709
        $sql = "SELECT tms, note FROM " . $dbPrefix . "const";
710
        $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
711
        $sql .= " AND entity IN (0, " . $conf->entity . ")";
712
713
        dol_syslog(get_class($this) . "::getLastActiveDate", LOG_DEBUG);
714
        $resql = $this->db->query($sql);
715
        if (!$resql) {
716
            $err++;
717
        } else {
718
            $obj = $this->db->fetch_object($resql);
719
            if ($obj) {
720
                $tmp = [];
721
                if ($obj->note) {
722
                    $tmp = json_decode($obj->note, true);
723
                }
724
                return [
725
                    'authorid' => empty($tmp['authorid']) ? '' : $tmp['authorid'],
726
                    'ip' => empty($tmp['ip']) ? '' : $tmp['ip'],
727
                    'lastactivationdate' => $this->db->jdate($obj->tms),
728
                    'lastactivationversion' => (!empty($tmp['lastactivationversion']) ? $tmp['lastactivationversion'] : 'unknown'),
729
                ];
730
            }
731
        }
732
733
        return [];
734
    }
735
736
    /**
737
     * Function called when module is enabled.
738
     * The init function adds tabs, constants, boxes, permissions and menus (defined in constructor) into Dolibarr
739
     * database. It also creates data directories
740
     *
741
     * @param string $options Options when enabling module ('', 'newboxdefonly', 'noboxes', 'menuonly')
742
     *                         'noboxes' = Do not insert boxes 'newboxdefonly' = For boxes, insert def of boxes only
743
     *                         and not boxes activation
744
     *
745
     * @return int                1 if OK, 0 if KO
746
     */
747
    public function init($options = '')
748
    {
749
        return $this->_init([], $options);
750
    }
751
752
    /**
753
     * Enables a module.
754
     * Inserts all information into database.
755
     *
756
     * @param array $array_sql SQL requests to be executed when enabling module
757
     * @param string $options String with options when disabling module:
758
     *                              - 'noboxes' = Do all actions but do not insert boxes
759
     *                              - 'newboxdefonly' = Do all actions but for boxes, insert def of boxes only and not
760
     *                              boxes activation
761
     *
762
     * @return int                  1 if OK, 0 if KO
763
     */
764
    protected function _init($array_sql, $options = '')
765
    {
766
        // phpcs:enable
767
        $conf = \DoliCore\Base\Config::getConf();
0 ignored issues
show
Deprecated Code introduced by
The function DoliCore\Base\Config::getConf() has been deprecated: Use ( Ignorable by Annotation )

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

767
        $conf = /** @scrutinizer ignore-deprecated */ \DoliCore\Base\Config::getConf();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
768
769
        $err = 0;
770
771
        $this->db->begin();
772
773
        // Insert activation module constant
774
        if (!$err) {
775
            $err += $this->_active();
776
        }
777
778
        // Insert new pages for tabs (into llx_const)
779
        if (!$err) {
780
            $err += $this->insert_tabs();
781
        }
782
783
        // Insert activation of module's parts. Copy website templates into doctemplates.
784
        if (!$err) {
785
            $err += $this->insert_module_parts();
786
        }
787
788
        // Insert constant defined by modules (into llx_const)
789
        if (!$err && !preg_match('/newboxdefonly/', $options)) {
790
            $err += $this->insert_const(); // Test on newboxdefonly to avoid to erase value during upgrade
791
        }
792
793
        // Insert boxes def (into llx_boxes_def) and boxes setup (into llx_boxes)
794
        if (!$err && !preg_match('/noboxes/', $options)) {
795
            $err += $this->insert_boxes($options);
796
        }
797
798
        // Insert cron job entries (entry in llx_cronjobs)
799
        if (!$err) {
800
            $err += $this->insert_cronjobs();
801
        }
802
803
        // Insert permission definitions of module into llx_rights_def. If user is admin, grant this permission to user.
804
        if (!$err) {
805
            $err += $this->insert_permissions(1, null, 1);
806
        }
807
808
        // Insert specific menus entries into database
809
        if (!$err) {
810
            $err += $this->insert_menus();
811
        }
812
813
        // Create module's directories
814
        if (!$err) {
815
            $err += $this->create_dirs();
816
        }
817
818
        // Execute addons requests
819
        $num = count($array_sql);
820
        for ($i = 0; $i < $num; $i++) {
821
            if (!$err) {
822
                $val = $array_sql[$i];
823
                $sql = $val;
824
                $ignoreerror = 0;
825
                if (is_array($val)) {
826
                    $sql = $val['sql'];
827
                    $ignoreerror = $val['ignoreerror'];
828
                }
829
                // Add current entity id
830
                $sql = str_replace('__ENTITY__', $conf->entity, $sql);
831
832
                dol_syslog(get_class($this) . "::_init ignoreerror=" . $ignoreerror, LOG_DEBUG);
833
                $result = $this->db->query($sql, $ignoreerror);
834
                if (!$result) {
835
                    if (!$ignoreerror) {
836
                        $this->error = $this->db->lasterror();
837
                        $err++;
838
                    } else {
839
                        dol_syslog(get_class($this) . "::_init Warning " . $this->db->lasterror(), LOG_WARNING);
840
                    }
841
                }
842
            }
843
        }
844
845
        // Return code
846
        if (!$err) {
847
            $this->db->commit();
848
            return 1;
849
        } else {
850
            $this->db->rollback();
851
            return 0;
852
        }
853
    }
854
855
    /**
856
     * Insert constants for module activation
857
     *
858
     * @return int Error count (0 if OK)
859
     */
860
    protected function _active()
861
    {
862
        // phpcs:enable
863
        global $conf, $user;
864
        global $config;
865
        $dbPrefix = $config->db->prefix;
866
867
        $err = 0;
868
869
        // Common module
870
        $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
871
872
        $sql = "DELETE FROM " . $dbPrefix . "const";
873
        $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
874
        $sql .= " AND entity IN (0, " . $entity . ")";
875
876
        dol_syslog(get_class($this) . "::_active delete activation constant", LOG_DEBUG);
877
        $resql = $this->db->query($sql);
878
        if (!$resql) {
879
            $err++;
880
        }
881
882
        $note = json_encode(
883
            [
884
                'authorid' => (is_object($user) ? $user->id : 0),
885
                'ip' => (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']),
886
                'lastactivationversion' => $this->version,
887
            ]
888
        );
889
890
        $sql = "INSERT INTO " . $dbPrefix . "const (name, value, visible, entity, note) VALUES";
891
        $sql .= " (" . $this->db->encrypt($this->const_name);
892
        $sql .= ", " . $this->db->encrypt('1');
893
        $sql .= ", 0, " . ((int)$entity);
894
        $sql .= ", '" . $this->db->escape($note) . "')";
895
896
        dol_syslog(get_class($this) . "::_active insert activation constant", LOG_DEBUG);
897
898
        $resql = $this->db->query($sql);
899
900
        if (!$resql) {
901
            $err++;
902
        }
903
904
        return $err;
905
    }
906
907
    /**
908
     * Adds tabs
909
     *
910
     * @return int  Error count (0 if ok)
911
     */
912
    public function insert_tabs()
913
    {
914
        // phpcs:enable
915
        global $conf;
916
        $dbPrefix = $conf->db->prefix;
917
918
        $err = 0;
919
920
        if (!empty($this->tabs)) {
921
            dol_syslog(get_class($this) . "::insert_tabs", LOG_DEBUG);
922
923
            $i = 0;
924
            foreach ($this->tabs as $key => $value) {
925
                if (is_array($value) && count($value) == 0) {
926
                    continue; // Discard empty arrays
927
                }
928
929
                $entity = $conf->entity;
930
                $newvalue = $value;
931
932
                if (is_array($value)) {
933
                    $newvalue = $value['data'];
934
                    if (isset($value['entity'])) {
935
                        $entity = $value['entity'];
936
                    }
937
                }
938
939
                if ($newvalue) {
940
                    $sql = "INSERT INTO " . $dbPrefix . "const (";
941
                    $sql .= "name";
942
                    $sql .= ", type";
943
                    $sql .= ", value";
944
                    $sql .= ", note";
945
                    $sql .= ", visible";
946
                    $sql .= ", entity";
947
                    $sql .= ")";
948
                    $sql .= " VALUES (";
949
                    $sql .= $this->db->encrypt($this->const_name . "_TABS_" . $i);
950
                    $sql .= ", 'chaine'";
951
                    $sql .= ", " . $this->db->encrypt($newvalue);
952
                    $sql .= ", null";
953
                    $sql .= ", '0'";
954
                    $sql .= ", " . ((int)$entity);
955
                    $sql .= ")";
956
957
                    $resql = $this->db->query($sql);
958
                    if (!$resql) {
959
                        dol_syslog($this->db->lasterror(), LOG_ERR);
960
                        if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
961
                            $this->error = $this->db->lasterror();
962
                            $this->errors[] = $this->db->lasterror();
963
                            $err++;
964
                            break;
965
                        }
966
                    }
967
                }
968
                $i++;
969
            }
970
        }
971
        return $err;
972
    }
973
974
    /**
975
     * Save configuration for generic features.
976
     * This also generate website templates if the module provide some.
977
     *
978
     * @return int Error count (0 if OK)
979
     */
980
    public function insert_module_parts()
981
    {
982
        // phpcs:enable
983
        global $conf, $langs;
984
        $dbPrefix = $conf->db->prefix;
985
986
        $error = 0;
987
988
        if (is_array($this->module_parts)) {
989
            if (empty($this->module_parts['icon']) && !empty($this->picto) && preg_match('/^fa\-/', $this->picto)) {
990
                $this->module_parts['icon'] = $this->picto;
991
            }
992
993
            foreach ($this->module_parts as $key => $value) {
994
                if (is_array($value) && count($value) == 0) {
995
                    continue; // Discard empty arrays
996
                }
997
998
                // If module brings website templates, we must generate the zip like we do whenenabling the website module
999
                if ($key == 'websitetemplates' && $value == 1) {
1000
                    $srcroot = dol_buildpath('/' . strtolower($this->name) . '/doctemplates/websites');
1001
1002
                    // Copy templates in dir format (recommended) into zip file
1003
                    $docs = dol_dir_list($srcroot, 'directories', 0, 'website_.*$');
1004
                    foreach ($docs as $cursorfile) {
1005
                        $src = $srcroot . '/' . $cursorfile['name'];
1006
                        $dest = DOL_DATA_ROOT . '/doctemplates/websites/' . $cursorfile['name'];
1007
1008
                        dol_delete_file($dest . '.zip');
1009
1010
                        // Compress it
1011
                        global $errormsg;
1012
                        $errormsg = '';
1013
                        $result = dol_compress_dir($src, $dest . '.zip', 'zip');
1014
                        if ($result < 0) {
1015
                            $error++;
1016
                            $this->error = ($errormsg ? $errormsg : $langs->trans('ErrorFailToCreateZip', $dest));
1017
                            $this->errors[] = ($errormsg ? $errormsg : $langs->trans('ErrorFailToCreateZip', $dest));
1018
                        }
1019
                    }
1020
1021
                    // Copy also the preview website_xxx.jpg file
1022
                    $docs = dol_dir_list($srcroot, 'files', 0, 'website_.*\.jpg$');
1023
                    foreach ($docs as $cursorfile) {
1024
                        $src = $srcroot . '/' . $cursorfile['name'];
1025
                        $dest = DOL_DATA_ROOT . '/doctemplates/websites/' . $cursorfile['name'];
1026
1027
                        dol_copy($src, $dest);
1028
                    }
1029
                }
1030
1031
                $entity = $conf->entity; // Reset the current entity
1032
                $newvalue = $value;
1033
1034
                // Serialize array parameters
1035
                if (is_array($value)) {
1036
                    // Can defined other parameters
1037
                    // Example when $key='hooks', then $value is an array('data'=>array('hookcontext1','hookcontext2'), 'entity'=>X)
1038
                    if (isset($value['data']) && is_array($value['data'])) {
1039
                        $newvalue = json_encode($value['data']);
1040
                        if (isset($value['entity'])) {
1041
                            $entity = $value['entity'];
1042
                        }
1043
                    } elseif (isset($value['data']) && !is_array($value['data'])) {
1044
                        $newvalue = $value['data'];
1045
                        if (isset($value['entity'])) {
1046
                            $entity = $value['entity'];
1047
                        }
1048
                    } else { // when hook is declared with syntax 'hook'=>array('hookcontext1','hookcontext2',...)
1049
                        $newvalue = json_encode($value);
1050
                    }
1051
                }
1052
1053
                if (!empty($newvalue)) {
1054
                    $sql = "INSERT INTO " . $dbPrefix . "const (";
1055
                    $sql .= "name";
1056
                    $sql .= ", type";
1057
                    $sql .= ", value";
1058
                    $sql .= ", note";
1059
                    $sql .= ", visible";
1060
                    $sql .= ", entity";
1061
                    $sql .= ")";
1062
                    $sql .= " VALUES (";
1063
                    $sql .= " " . $this->db->encrypt($this->const_name . "_" . strtoupper($key), 1);
1064
                    $sql .= ", 'chaine'";
1065
                    $sql .= ", " . $this->db->encrypt($newvalue, 1);
1066
                    $sql .= ", null";
1067
                    $sql .= ", '0'";
1068
                    $sql .= ", " . ((int)$entity);
1069
                    $sql .= ")";
1070
1071
                    dol_syslog(get_class($this) . "::insert_module_parts for key=" . $this->const_name . "_" . strtoupper($key), LOG_DEBUG);
1072
1073
                    $resql = $this->db->query($sql, 1);
1074
                    if (!$resql) {
1075
                        if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1076
                            $error++;
1077
                            $this->error = $this->db->lasterror();
1078
                        } else {
1079
                            dol_syslog(get_class($this) . "::insert_module_parts for " . $this->const_name . "_" . strtoupper($key) . " Record already exists.", LOG_WARNING);
1080
                        }
1081
                    }
1082
                }
1083
            }
1084
        }
1085
        return $error;
1086
    }
1087
1088
    /**
1089
     * Adds constants
1090
     *
1091
     * @return int Error count (0 if OK)
1092
     */
1093
    public function insert_const()
1094
    {
1095
        // phpcs:enable
1096
        global $conf;
1097
        $dbPrefix = $conf->db->prefix;
1098
1099
        $err = 0;
1100
1101
        if (empty($this->const)) {
1102
            return 0;
1103
        }
1104
1105
        dol_syslog(__METHOD__, LOG_DEBUG);
1106
1107
        foreach ($this->const as $key => $value) {
1108
            $name = $this->const[$key][0];
1109
            $type = $this->const[$key][1];
1110
            $val = $this->const[$key][2];
1111
            $note = isset($this->const[$key][3]) ? $this->const[$key][3] : '';
1112
            $visible = isset($this->const[$key][4]) ? $this->const[$key][4] : 0;
1113
            $entity = (!empty($this->const[$key][5]) && $this->const[$key][5] != 'current') ? 0 : $conf->entity;
1114
1115
            // Clean
1116
            if (empty($visible)) {
1117
                $visible = '0';
1118
            }
1119
            if (empty($val) && $val != '0') {
1120
                $val = '';
1121
            }
1122
1123
            $sql = "SELECT count(*) as nb";
1124
            $sql .= " FROM " . $dbPrefix . "const";
1125
            $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($name) . "'";
1126
            $sql .= " AND entity = " . ((int)$entity);
1127
1128
            $result = $this->db->query($sql);
1129
            if ($result) {
1130
                $row = $this->db->fetch_row($result);
1131
1132
                if ($row[0] == 0) {   // If not found
1133
                    $sql = "INSERT INTO " . $dbPrefix . "const (name,type,value,note,visible,entity)";
1134
                    $sql .= " VALUES (";
1135
                    $sql .= $this->db->encrypt($name);
1136
                    $sql .= ",'" . $this->db->escape($type) . "'";
1137
                    $sql .= "," . (($val != '') ? $this->db->encrypt($val) : "''");
1138
                    $sql .= "," . ($note ? "'" . $this->db->escape($note) . "'" : "null");
1139
                    $sql .= ",'" . $this->db->escape($visible) . "'";
1140
                    $sql .= "," . $entity;
1141
                    $sql .= ")";
1142
1143
                    if (!$this->db->query($sql)) {
1144
                        $err++;
1145
                    }
1146
                } else {
1147
                    dol_syslog(__METHOD__ . " constant '" . $name . "' already exists", LOG_DEBUG);
1148
                }
1149
            } else {
1150
                $err++;
1151
            }
1152
        }
1153
1154
        return $err;
1155
    }
1156
1157
1158
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1159
1160
    /**
1161
     * Adds boxes
1162
     *
1163
     * @param string $option Options when disabling module ('newboxdefonly'=insert only boxes definition)
1164
     *
1165
     * @return int             Error count (0 if OK)
1166
     */
1167
    public function insert_boxes($option = '')
1168
    {
1169
        // phpcs:enable
1170
        $conf = Config::getConf();
0 ignored issues
show
Deprecated Code introduced by
The function DoliCore\Base\Config::getConf() has been deprecated: Use ( Ignorable by Annotation )

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

1170
        $conf = /** @scrutinizer ignore-deprecated */ Config::getConf();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1171
        $dbPrefix = $conf->db->prefix;
1172
1173
        $err = 0;
1174
1175
        if (is_array($this->boxes)) {
1176
            dol_syslog(get_class($this) . "::insert_boxes", LOG_DEBUG);
1177
1178
            $pos_name = InfoBox::getListOfPagesForBoxes();
1179
1180
            foreach ($this->boxes as $key => $value) {
1181
                $file = isset($this->boxes[$key]['file']) ? $this->boxes[$key]['file'] : '';
1182
                $note = isset($this->boxes[$key]['note']) ? $this->boxes[$key]['note'] : '';
1183
                $enabledbydefaulton = isset($this->boxes[$key]['enabledbydefaulton']) ? $this->boxes[$key]['enabledbydefaulton'] : 'Home';
1184
1185
                if (empty($file)) {
1186
                    $file = isset($this->boxes[$key][1]) ? $this->boxes[$key][1] : ''; // For backward compatibility
1187
                }
1188
                if (empty($note)) {
1189
                    $note = isset($this->boxes[$key][2]) ? $this->boxes[$key][2] : ''; // For backward compatibility
1190
                }
1191
1192
                // Search if boxes def already present
1193
                $sql = "SELECT count(*) as nb FROM " . $dbPrefix . "boxes_def";
1194
                $sql .= " WHERE file = '" . $this->db->escape($file) . "'";
1195
                $sql .= " AND entity = " . $conf->entity;
1196
                if ($note) {
1197
                    $sql .= " AND note ='" . $this->db->escape($note) . "'";
1198
                }
1199
1200
                $result = $this->db->query($sql);
1201
                if ($result) {
1202
                    $obj = $this->db->fetch_object($result);
1203
                    if ($obj->nb == 0) {
1204
                        $this->db->begin();
1205
1206
                        if (!$err) {
1207
                            $sql = "INSERT INTO " . $dbPrefix . "boxes_def (file, entity, note)";
1208
                            $sql .= " VALUES ('" . $this->db->escape($file) . "', ";
1209
                            $sql .= $conf->entity . ", ";
1210
                            $sql .= $note ? "'" . $this->db->escape($note) . "'" : "null";
1211
                            $sql .= ")";
1212
1213
                            dol_syslog(get_class($this) . "::insert_boxes", LOG_DEBUG);
1214
                            $resql = $this->db->query($sql);
1215
                            if (!$resql) {
1216
                                $err++;
1217
                            }
1218
                        }
1219
                        if (!$err && !preg_match('/newboxdefonly/', $option)) {
1220
                            $lastid = $this->db->last_insert_id($dbPrefix . "boxes_def", "rowid");
1221
1222
                            foreach ($pos_name as $key2 => $val2) {
1223
                                //print 'key2='.$key2.'-val2='.$val2."<br>\n";
1224
                                if ($enabledbydefaulton && $val2 != $enabledbydefaulton) {
1225
                                    continue; // Not enabled by default onto this page.
1226
                                }
1227
1228
                                $sql = "INSERT INTO " . $dbPrefix . "boxes (box_id, position, box_order, fk_user, entity)";
1229
                                $sql .= " VALUES (" . ((int)$lastid) . ", " . ((int)$key2) . ", '0', 0, " . ((int)$conf->entity) . ")";
1230
1231
                                dol_syslog(get_class($this) . "::insert_boxes onto page " . $key2 . "=" . $val2, LOG_DEBUG);
1232
                                $resql = $this->db->query($sql);
1233
                                if (!$resql) {
1234
                                    $err++;
1235
                                }
1236
                            }
1237
                        }
1238
1239
                        if (!$err) {
1240
                            $this->db->commit();
1241
                        } else {
1242
                            $this->error = $this->db->lasterror();
1243
                            $this->db->rollback();
1244
                        }
1245
                    }
1246
                    // else box already registered into database
1247
                } else {
1248
                    $this->error = $this->db->lasterror();
1249
                    $err++;
1250
                }
1251
            }
1252
        }
1253
1254
        return $err;
1255
    }
1256
1257
1258
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1259
1260
    /**
1261
     * Adds cronjobs
1262
     *
1263
     * @return int             Error count (0 if OK)
1264
     */
1265
    public function insert_cronjobs()
1266
    {
1267
        global $conf;
1268
        $dbPrefix = $conf->db->prefix;
1269
1270
        $err = 0;
1271
1272
        if (is_array($this->cronjobs)) {
1273
            dol_syslog(get_class($this) . "::insert_cronjobs", LOG_DEBUG);
1274
1275
            foreach ($this->cronjobs as $key => $value) {
1276
                $entity = isset($this->cronjobs[$key]['entity']) ? $this->cronjobs[$key]['entity'] : $conf->entity;
1277
                $label = isset($this->cronjobs[$key]['label']) ? $this->cronjobs[$key]['label'] : '';
1278
                $jobtype = isset($this->cronjobs[$key]['jobtype']) ? $this->cronjobs[$key]['jobtype'] : '';
1279
                $class = isset($this->cronjobs[$key]['class']) ? $this->cronjobs[$key]['class'] : '';
1280
                $objectname = isset($this->cronjobs[$key]['objectname']) ? $this->cronjobs[$key]['objectname'] : '';
1281
                $method = isset($this->cronjobs[$key]['method']) ? $this->cronjobs[$key]['method'] : '';
1282
                $command = isset($this->cronjobs[$key]['command']) ? $this->cronjobs[$key]['command'] : '';
1283
                $parameters = isset($this->cronjobs[$key]['parameters']) ? $this->cronjobs[$key]['parameters'] : '';
1284
                $comment = isset($this->cronjobs[$key]['comment']) ? $this->cronjobs[$key]['comment'] : '';
1285
                $frequency = isset($this->cronjobs[$key]['frequency']) ? $this->cronjobs[$key]['frequency'] : '';
1286
                $unitfrequency = isset($this->cronjobs[$key]['unitfrequency']) ? $this->cronjobs[$key]['unitfrequency'] : '';
1287
                $priority = isset($this->cronjobs[$key]['priority']) ? $this->cronjobs[$key]['priority'] : '';
1288
                $datestart = isset($this->cronjobs[$key]['datestart']) ? $this->cronjobs[$key]['datestart'] : '';
1289
                $dateend = isset($this->cronjobs[$key]['dateend']) ? $this->cronjobs[$key]['dateend'] : '';
1290
                $status = isset($this->cronjobs[$key]['status']) ? $this->cronjobs[$key]['status'] : '';
1291
                $test = isset($this->cronjobs[$key]['test']) ? $this->cronjobs[$key]['test'] : ''; // Line must be enabled or not (so visible or not)
1292
1293
                // Search if cron entry already present
1294
                $sql = "SELECT count(*) as nb FROM " . $dbPrefix . "cronjob";
1295
                //$sql .= " WHERE module_name = '".$this->db->escape(empty($this->rights_class) ?strtolower($this->name) : $this->rights_class)."'";
1296
                $sql .= " WHERE label = '" . $this->db->escape($label) . "'";
1297
                /*if ($class) {
1298
                    $sql .= " AND classesname = '".$this->db->escape($class)."'";
1299
                }
1300
                if ($objectname) {
1301
                    $sql .= " AND objectname = '".$this->db->escape($objectname)."'";
1302
                }
1303
                if ($method) {
1304
                    $sql .= " AND methodename = '".$this->db->escape($method)."'";
1305
                }
1306
                if ($command) {
1307
                    $sql .= " AND command = '".$this->db->escape($command)."'";
1308
                }
1309
                if ($parameters) {
1310
                    $sql .= " AND params = '".$this->db->escape($parameters)."'";
1311
                }*/
1312
                $sql .= " AND entity = " . ((int)$entity); // Must be exact entity
1313
1314
                $now = dol_now();
1315
1316
                $result = $this->db->query($sql);
1317
                if ($result) {
1318
                    $obj = $this->db->fetch_object($result);
1319
                    if ($obj->nb == 0) {
1320
                        $this->db->begin();
1321
1322
                        if (!$err) {
1323
                            $sql = "INSERT INTO " . $dbPrefix . "cronjob (module_name, datec, datestart, dateend, label, jobtype, classesname, objectname, methodename, command, params, note,";
1324
                            if (is_int($frequency)) {
1325
                                $sql .= ' frequency,';
1326
                            }
1327
                            if (is_int($unitfrequency)) {
1328
                                $sql .= ' unitfrequency,';
1329
                            }
1330
                            if (is_int($priority)) {
1331
                                $sql .= ' priority,';
1332
                            }
1333
                            if (is_int($status)) {
1334
                                $sql .= ' status,';
1335
                            }
1336
                            $sql .= " entity, test)";
1337
                            $sql .= " VALUES (";
1338
                            $sql .= "'" . $this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class) . "', ";
1339
                            $sql .= "'" . $this->db->idate($now) . "', ";
1340
                            $sql .= ($datestart ? "'" . $this->db->idate($datestart) . "'" : "'" . $this->db->idate($now) . "'") . ", ";
1341
                            $sql .= ($dateend ? "'" . $this->db->idate($dateend) . "'" : "NULL") . ", ";
1342
                            $sql .= "'" . $this->db->escape($label) . "', ";
1343
                            $sql .= "'" . $this->db->escape($jobtype) . "', ";
1344
                            $sql .= ($class ? "'" . $this->db->escape($class) . "'" : "null") . ",";
1345
                            $sql .= ($objectname ? "'" . $this->db->escape($objectname) . "'" : "null") . ",";
1346
                            $sql .= ($method ? "'" . $this->db->escape($method) . "'" : "null") . ",";
1347
                            $sql .= ($command ? "'" . $this->db->escape($command) . "'" : "null") . ",";
1348
                            $sql .= ($parameters ? "'" . $this->db->escape($parameters) . "'" : "null") . ",";
1349
                            $sql .= ($comment ? "'" . $this->db->escape($comment) . "'" : "null") . ",";
1350
                            if (is_int($frequency)) {
1351
                                $sql .= "'" . $this->db->escape($frequency) . "', ";
1352
                            }
1353
                            if (is_int($unitfrequency)) {
1354
                                $sql .= "'" . $this->db->escape($unitfrequency) . "', ";
1355
                            }
1356
                            if (is_int($priority)) {
1357
                                $sql .= "'" . $this->db->escape($priority) . "', ";
1358
                            }
1359
                            if (is_int($status)) {
1360
                                $sql .= ((int)$status) . ", ";
1361
                            }
1362
                            $sql .= $entity . ",";
1363
                            $sql .= "'" . $this->db->escape($test) . "'";
1364
                            $sql .= ")";
1365
1366
                            $resql = $this->db->query($sql);
1367
                            if (!$resql) {
1368
                                $err++;
1369
                            }
1370
                        }
1371
1372
                        if (!$err) {
1373
                            $this->db->commit();
1374
                        } else {
1375
                            $this->error = $this->db->lasterror();
1376
                            $this->db->rollback();
1377
                        }
1378
                    }
1379
                    // else box already registered into database
1380
                } else {
1381
                    $this->error = $this->db->lasterror();
1382
                    $err++;
1383
                }
1384
            }
1385
        }
1386
1387
        return $err;
1388
    }
1389
1390
1391
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps,PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1392
1393
    /**
1394
     * Adds access rights
1395
     *
1396
     * @param int $reinitadminperms If 1, we also grant them to all admin users
1397
     * @param int $force_entity Force current entity
1398
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
1399
     *
1400
     * @return int                      Error count (0 if OK)
1401
     */
1402
    public function insert_permissions($reinitadminperms = 0, $force_entity = null, $notrigger = 0)
1403
    {
1404
        // phpcs:enable
1405
        global $user;
1406
        $conf = Config::getConf();
0 ignored issues
show
Deprecated Code introduced by
The function DoliCore\Base\Config::getConf() has been deprecated: Use ( Ignorable by Annotation )

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

1406
        $conf = /** @scrutinizer ignore-deprecated */ Config::getConf();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1407
        $dbPrefix = $conf->db->prefix;
1408
1409
        $err = 0;
1410
        $entity = (!empty($force_entity) ? $force_entity : $conf->entity);
1411
1412
        dol_syslog(get_class($this) . "::insert_permissions", LOG_DEBUG);
1413
1414
        // Test if module is activated
1415
        $sql_del = "SELECT " . $this->db->decrypt('value') . " as value";
1416
        $sql_del .= " FROM " . $dbPrefix . "const";
1417
        $sql_del .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
1418
        $sql_del .= " AND entity IN (0," . $entity . ")";
1419
1420
        $resql = $this->db->query($sql_del);
1421
        if ($resql) {
1422
            $obj = $this->db->fetch_object($resql);
1423
1424
            if ($obj !== null && !empty($obj->value) && !empty($this->rights)) {
1425
                // If the module is active
1426
                foreach ($this->rights as $key => $value) {
1427
                    $r_id = $this->rights[$key][0];   // permission id in llx_rights_def (not unique because primary key is couple id-entity)
1428
                    $r_desc = $this->rights[$key][1];
1429
                    $r_type = isset($this->rights[$key][2]) ? $this->rights[$key][2] : '';
1430
                    $r_def = empty($this->rights[$key][3]) ? 0 : $this->rights[$key][3];
1431
                    $r_perms = $this->rights[$key][4];
1432
                    $r_subperms = isset($this->rights[$key][5]) ? $this->rights[$key][5] : '';
1433
                    $r_modul = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
1434
1435
                    if (empty($r_type)) {
1436
                        $r_type = 'w';
1437
                    }
1438
1439
                    // Search if perm already present
1440
                    $sql = "SELECT count(*) as nb FROM " . $dbPrefix . "rights_def";
1441
                    $sql .= " WHERE id = " . ((int)$r_id) . " AND entity = " . ((int)$entity);
1442
1443
                    $resqlselect = $this->db->query($sql);
1444
                    if ($resqlselect) {
1445
                        $objcount = $this->db->fetch_object($resqlselect);
1446
                        if ($objcount && $objcount->nb == 0) {
1447
                            if (dol_strlen($r_perms)) {
1448
                                if (dol_strlen($r_subperms)) {
1449
                                    $sql = "INSERT INTO " . $dbPrefix . "rights_def";
1450
                                    $sql .= " (id, entity, libelle, module, type, bydefault, perms, subperms)";
1451
                                    $sql .= " VALUES ";
1452
                                    $sql .= "(" . $r_id . "," . $entity . ",'" . $this->db->escape($r_desc) . "','" . $this->db->escape($r_modul) . "','" . $this->db->escape($r_type) . "'," . $r_def . ",'" . $this->db->escape($r_perms) . "','" . $this->db->escape($r_subperms) . "')";
1453
                                } else {
1454
                                    $sql = "INSERT INTO " . $dbPrefix . "rights_def";
1455
                                    $sql .= " (id, entity, libelle, module, type, bydefault, perms)";
1456
                                    $sql .= " VALUES ";
1457
                                    $sql .= "(" . $r_id . "," . $entity . ",'" . $this->db->escape($r_desc) . "','" . $this->db->escape($r_modul) . "','" . $this->db->escape($r_type) . "'," . $r_def . ",'" . $this->db->escape($r_perms) . "')";
1458
                                }
1459
                            } else {
1460
                                $sql = "INSERT INTO " . $dbPrefix . "rights_def ";
1461
                                $sql .= " (id, entity, libelle, module, type, bydefault)";
1462
                                $sql .= " VALUES ";
1463
                                $sql .= "(" . $r_id . "," . $entity . ",'" . $this->db->escape($r_desc) . "','" . $this->db->escape($r_modul) . "','" . $this->db->escape($r_type) . "'," . $r_def . ")";
1464
                            }
1465
1466
                            $resqlinsert = $this->db->query($sql, 1);
1467
1468
                            if (!$resqlinsert) {
1469
                                if ($this->db->errno() != "DB_ERROR_RECORD_ALREADY_EXISTS") {
1470
                                    $this->error = $this->db->lasterror();
1471
                                    $err++;
1472
                                    break;
1473
                                } else {
1474
                                    dol_syslog(get_class($this) . "::insert_permissions record already exists", LOG_INFO);
1475
                                }
1476
                            }
1477
1478
                            $this->db->free($resqlinsert);
1479
                        }
1480
1481
                        $this->db->free($resqlselect);
1482
                    }
1483
1484
                    // If we want to init permissions on admin users
1485
                    if ($reinitadminperms) {
1486
                        $sql = "SELECT rowid FROM " . $dbPrefix . "user WHERE admin = 1";
1487
                        dol_syslog(get_class($this) . "::insert_permissions Search all admin users", LOG_DEBUG);
1488
1489
                        $resqlseladmin = $this->db->query($sql, 1);
1490
1491
                        if ($resqlseladmin) {
1492
                            $num = $this->db->num_rows($resqlseladmin);
1493
                            $i = 0;
1494
                            while ($i < $num) {
1495
                                $obj2 = $this->db->fetch_object($resqlseladmin);
1496
                                dol_syslog(get_class($this) . "::insert_permissions Add permission id " . $r_id . " to user id=" . $obj2->rowid);
1497
1498
                                $tmpuser = new User($this->db);
1499
                                $result = $tmpuser->fetch($obj2->rowid);
1500
                                if ($result > 0) {
1501
                                    $tmpuser->addrights($r_id, '', '', 0, 1);
1502
                                } else {
1503
                                    dol_syslog(get_class($this) . "::insert_permissions Failed to add the permission to user because fetch return an error", LOG_ERR);
1504
                                }
1505
                                $i++;
1506
                            }
1507
                        } else {
1508
                            dol_print_error($this->db);
1509
                        }
1510
                    }
1511
                }
1512
1513
                if ($reinitadminperms && !empty($user->admin)) {  // Reload permission for current user if defined
1514
                    // We reload permissions
1515
                    $user->clearrights();
1516
                    $user->getrights();
1517
                }
1518
            }
1519
            $this->db->free($resql);
1520
        } else {
1521
            $this->error = $this->db->lasterror();
1522
            $err++;
1523
        }
1524
1525
        return $err;
1526
    }
1527
1528
1529
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1530
1531
    /**
1532
     * Adds menu entries
1533
     *
1534
     * @return int     Error count (0 if OK)
1535
     */
1536
    public function insert_menus()
1537
    {
1538
        // phpcs:enable
1539
        global $conf, $user;
1540
        $dbPrefix = $conf->db->prefix;
1541
1542
        if (!is_array($this->menu) || empty($this->menu)) {
1543
            return 0;
1544
        }
1545
1546
        include_once DOL_DOCUMENT_ROOT . '/core/class/menubase.class.php';
1547
1548
        dol_syslog(get_class($this) . "::insert_menus", LOG_DEBUG);
1549
1550
        $err = 0;
1551
1552
        // Common module
1553
        $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
1554
1555
        $this->db->begin();
1556
1557
        foreach ($this->menu as $key => $value) {
1558
            $menu = new Menubase($this->db);
1559
            $menu->menu_handler = 'all';
1560
1561
            //$menu->module=strtolower($this->name);    TODO When right_class will be same than module name
1562
            $menu->module = (empty($this->rights_class) ? strtolower($this->name) : $this->rights_class);
1563
1564
            if (!$this->menu[$key]['fk_menu']) {
1565
                $menu->fk_menu = 0;
1566
            } else {
1567
                $foundparent = 0;
1568
                $fk_parent = $this->menu[$key]['fk_menu'];
1569
                $reg = [];
1570
                if (preg_match('/^r=/', $fk_parent)) {    // old deprecated method
1571
                    $fk_parent = str_replace('r=', '', $fk_parent);
1572
                    if (isset($this->menu[$fk_parent]['rowid'])) {
1573
                        $menu->fk_menu = $this->menu[$fk_parent]['rowid'];
1574
                        $foundparent = 1;
1575
                    }
1576
                } elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+),fk_leftmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
1577
                    $menu->fk_menu = -1;
1578
                    $menu->fk_mainmenu = $reg[1];
1579
                    $menu->fk_leftmenu = $reg[2];
1580
                    $foundparent = 1;
1581
                } elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
1582
                    $menu->fk_menu = -1;
1583
                    $menu->fk_mainmenu = $reg[1];
1584
                    $menu->fk_leftmenu = '';
1585
                    $foundparent = 1;
1586
                }
1587
                if (!$foundparent) {
1588
                    $this->error = "ErrorBadDefinitionOfMenuArrayInModuleDescriptor";
1589
                    dol_syslog(get_class($this) . "::insert_menus " . $this->error . " " . $this->menu[$key]['fk_menu'], LOG_ERR);
1590
                    $err++;
1591
                }
1592
            }
1593
            $menu->type = $this->menu[$key]['type'];
1594
            $menu->mainmenu = isset($this->menu[$key]['mainmenu']) ? $this->menu[$key]['mainmenu'] : (isset($menu->fk_mainmenu) ? $menu->fk_mainmenu : '');
1595
            $menu->leftmenu = isset($this->menu[$key]['leftmenu']) ? $this->menu[$key]['leftmenu'] : '';
1596
            $menu->title = $this->menu[$key]['titre'];
1597
            $menu->prefix = isset($this->menu[$key]['prefix']) ? $this->menu[$key]['prefix'] : '';
1598
            $menu->url = $this->menu[$key]['url'];
1599
            $menu->langs = isset($this->menu[$key]['langs']) ? $this->menu[$key]['langs'] : '';
1600
            $menu->position = $this->menu[$key]['position'];
1601
            $menu->perms = $this->menu[$key]['perms'];
1602
            $menu->target = isset($this->menu[$key]['target']) ? $this->menu[$key]['target'] : '';
1603
            $menu->user = $this->menu[$key]['user'];
1604
            $menu->enabled = isset($this->menu[$key]['enabled']) ? $this->menu[$key]['enabled'] : 0;
1605
            $menu->position = $this->menu[$key]['position'];
1606
            $menu->entity = $entity;
1607
1608
            if (!$err) {
1609
                $result = $menu->create($user); // Save menu entry into table llx_menu
1610
                if ($result > 0) {
1611
                    $this->menu[$key]['rowid'] = $result;
1612
                } else {
1613
                    $this->error = $menu->error;
1614
                    dol_syslog(get_class($this) . '::insert_menus result=' . $result . " " . $this->error, LOG_ERR);
1615
                    $err++;
1616
                    break;
1617
                }
1618
            }
1619
        }
1620
1621
        if (!$err) {
1622
            $this->db->commit();
1623
        } else {
1624
            dol_syslog(get_class($this) . "::insert_menus " . $this->error, LOG_ERR);
1625
            $this->db->rollback();
1626
        }
1627
1628
        return $err;
1629
    }
1630
1631
1632
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1633
1634
    /**
1635
     * Creates directories
1636
     *
1637
     * @return int Error count (0 if OK)
1638
     */
1639
    public function create_dirs()
1640
    {
1641
        // phpcs:enable
1642
        global $langs, $conf;
1643
1644
        $err = 0;
1645
        $name = '';
1646
1647
        if (isset($this->dirs) && is_array($this->dirs)) {
1648
            foreach ($this->dirs as $key => $value) {
1649
                $addtodatabase = 0;
1650
1651
                if (!is_array($value)) {
1652
                    $dir = $value; // Default simple mode
1653
                } else {
1654
                    $constname = $this->const_name . "_DIR_";
1655
                    $dir = $this->dirs[$key][1];
1656
                    $addtodatabase = empty($this->dirs[$key][2]) ? '' : $this->dirs[$key][2]; // Create constante in llx_const
1657
                    $subname = empty($this->dirs[$key][3]) ? '' : strtoupper($this->dirs[$key][3]); // Add submodule name (ex: $conf->module->submodule->dir_output)
1658
                    $forcename = empty($this->dirs[$key][4]) ? '' : strtoupper($this->dirs[$key][4]); // Change the module name if different
1659
1660
                    if (!empty($forcename)) {
1661
                        $constname = 'MAIN_MODULE_' . $forcename . "_DIR_";
1662
                    }
1663
                    if (!empty($subname)) {
1664
                        $constname = $constname . $subname . "_";
1665
                    }
1666
1667
                    $name = $constname . strtoupper($this->dirs[$key][0]);
1668
                }
1669
1670
                // Define directory full path ($dir must start with "/")
1671
                if (!getDolGlobalString('MAIN_MODULE_MULTICOMPANY') || $conf->entity == 1) {
1672
                    $fulldir = DOL_DATA_ROOT . $dir;
1673
                } else {
1674
                    $fulldir = DOL_DATA_ROOT . "/" . $conf->entity . $dir;
1675
                }
1676
                // Create dir if it does not exists
1677
                if (!empty($fulldir) && !file_exists($fulldir)) {
1678
                    if (dol_mkdir($fulldir, DOL_DATA_ROOT) < 0) {
1679
                        $this->error = $langs->trans("ErrorCanNotCreateDir", $fulldir);
1680
                        dol_syslog(get_class($this) . "::_init " . $this->error, LOG_ERR);
1681
                        $err++;
1682
                    }
1683
                }
1684
1685
                // Define the constant in database if requested (not the default mode)
1686
                if (!empty($addtodatabase) && !empty($name)) {
1687
                    $result = $this->insert_dirs($name, $dir);
1688
                    if ($result) {
1689
                        $err++;
1690
                    }
1691
                }
1692
            }
1693
        }
1694
1695
        return $err;
1696
    }
1697
1698
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1699
1700
    /**
1701
     * Adds directories definitions
1702
     *
1703
     * @param string $name Name
1704
     * @param string $dir Directory
1705
     *
1706
     * @return int             Error count (0 if OK)
1707
     */
1708
    public function insert_dirs($name, $dir)
1709
    {
1710
        // phpcs:enable
1711
        global $conf;
1712
        $dbPrefix = $conf->db->prefix;
1713
1714
        $err = 0;
1715
1716
        $sql = "SELECT count(*)";
1717
        $sql .= " FROM " . $dbPrefix . "const";
1718
        $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($name) . "'";
1719
        $sql .= " AND entity = " . $conf->entity;
1720
1721
        dol_syslog(get_class($this) . "::insert_dirs", LOG_DEBUG);
1722
        $result = $this->db->query($sql);
1723
        if ($result) {
1724
            $row = $this->db->fetch_row($result);
1725
1726
            if ($row[0] == 0) {
1727
                $sql = "INSERT INTO " . $dbPrefix . "const (name, type, value, note, visible, entity)";
1728
                $sql .= " VALUES (" . $this->db->encrypt($name) . ", 'chaine', " . $this->db->encrypt($dir) . ", '" . $this->db->escape("Directory for module " . $this->name) . "', '0', " . ((int)$conf->entity) . ")";
1729
1730
                dol_syslog(get_class($this) . "::insert_dirs", LOG_DEBUG);
1731
                $this->db->query($sql);
1732
            }
1733
        } else {
1734
            $this->error = $this->db->lasterror();
1735
            $err++;
1736
        }
1737
1738
        return $err;
1739
    }
1740
1741
1742
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1743
1744
    /**
1745
     * Function called when module is disabled.
1746
     * The remove function removes tabs, constants, boxes, permissions and menus from Dolibarr database.
1747
     * Data directories are not deleted
1748
     *
1749
     * @param string $options Options when enabling module ('', 'noboxes')
1750
     *
1751
     * @return int                     1 if OK, 0 if KO
1752
     */
1753
    public function remove($options = '')
1754
    {
1755
        return $this->_remove([], $options);
1756
    }
1757
1758
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1759
1760
    /**
1761
     * Disable function. Deletes the module constants and boxes from the database.
1762
     *
1763
     * @param string[] $array_sql SQL requests to be executed when module is disabled
1764
     * @param string $options Options when disabling module:
1765
     *
1766
     * @return int                     1 if OK, 0 if KO
1767
     */
1768
    protected function _remove($array_sql, $options = '')
1769
    {
1770
        // phpcs:enable
1771
        $err = 0;
1772
1773
        $this->db->begin();
1774
1775
        // Remove activation module line (constant MAIN_MODULE_MYMODULE in llx_const)
1776
        if (!$err) {
1777
            $err += $this->_unactive();
1778
        }
1779
1780
        // Remove activation of module's new tabs (MAIN_MODULE_MYMODULE_TABS_XXX in llx_const)
1781
        if (!$err) {
1782
            $err += $this->delete_tabs();
1783
        }
1784
1785
        // Remove activation of module's parts (MAIN_MODULE_MYMODULE_XXX in llx_const)
1786
        if (!$err) {
1787
            $err += $this->delete_module_parts();
1788
        }
1789
1790
        // Remove constants defined by modules
1791
        if (!$err) {
1792
            $err += $this->delete_const();
1793
        }
1794
1795
        // Remove list of module's available boxes (entry in llx_boxes)
1796
        if (!$err && !preg_match('/(newboxdefonly|noboxes)/', $options)) {
1797
            $err += $this->delete_boxes(); // We don't have to delete if option ask to keep boxes safe or ask to add new box def only
1798
        }
1799
1800
        // Remove list of module's cron job entries (entry in llx_cronjobs)
1801
        if (!$err) {
1802
            $err += $this->delete_cronjobs();
1803
        }
1804
1805
        // Remove module's permissions from list of available permissions (entries in llx_rights_def)
1806
        if (!$err) {
1807
            $err += $this->delete_permissions();
1808
        }
1809
1810
        // Remove module's menus (entries in llx_menu)
1811
        if (!$err) {
1812
            $err += $this->delete_menus();
1813
        }
1814
1815
        // Remove module's directories
1816
        if (!$err) {
1817
            $err += $this->delete_dirs();
1818
        }
1819
1820
        // Run complementary sql requests
1821
        $num = count((array)$array_sql);
1822
        for ($i = 0; $i < $num; $i++) {
1823
            if (!$err) {
1824
                dol_syslog(get_class($this) . "::_remove", LOG_DEBUG);
1825
                $result = $this->db->query($array_sql[$i]);
1826
                if (!$result) {
1827
                    $this->error = $this->db->error();
1828
                    $err++;
1829
                }
1830
            }
1831
        }
1832
1833
        // Return code
1834
        if (!$err) {
1835
            $this->db->commit();
1836
            return 1;
1837
        } else {
1838
            $this->db->rollback();
1839
            return 0;
1840
        }
1841
    }
1842
1843
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1844
1845
    /**
1846
     * Module deactivation
1847
     *
1848
     * @return int Error count (0 if OK)
1849
     */
1850
    protected function _unactive()
1851
    {
1852
        // phpcs:enable
1853
        global $conf;
1854
        $dbPrefix = $conf->db->prefix;
1855
1856
        $err = 0;
1857
1858
        // Common module
1859
        $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
1860
1861
        $sql = "DELETE FROM " . $dbPrefix . "const";
1862
        $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
1863
        $sql .= " AND entity IN (0, " . $entity . ")";
1864
1865
        dol_syslog(get_class($this) . "::_unactive", LOG_DEBUG);
1866
        $this->db->query($sql);
1867
1868
        return $err;
1869
    }
1870
1871
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1872
1873
    /**
1874
     * Removes tabs
1875
     *
1876
     * @return int Error count (0 if OK)
1877
     */
1878
    public function delete_tabs()
1879
    {
1880
        // phpcs:enable
1881
        $conf = Config::getConf();
0 ignored issues
show
Deprecated Code introduced by
The function DoliCore\Base\Config::getConf() has been deprecated: Use ( Ignorable by Annotation )

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

1881
        $conf = /** @scrutinizer ignore-deprecated */ Config::getConf();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1882
        $dbPrefix = $conf->db->prefix;
1883
1884
        $err = 0;
1885
1886
        $sql = "DELETE FROM " . $dbPrefix . "const";
1887
        $sql .= " WHERE " . $this->db->decrypt('name') . " like '" . $this->db->escape($this->const_name) . "_TABS_%'";
1888
        $sql .= " AND entity = " . $conf->entity;
1889
1890
        dol_syslog(get_class($this) . "::delete_tabs", LOG_DEBUG);
1891
        if (!$this->db->query($sql)) {
1892
            $this->error = $this->db->lasterror();
1893
            $err++;
1894
        }
1895
1896
        return $err;
1897
    }
1898
1899
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1900
1901
    /**
1902
     * Removes generic parts
1903
     *
1904
     * @return int Error count (0 if OK)
1905
     */
1906
    public function delete_module_parts()
1907
    {
1908
        // phpcs:enable
1909
        global $conf;
1910
        $dbPrefix = $conf->db->prefix;
1911
1912
        $err = 0;
1913
1914
        if (is_array($this->module_parts)) {
1915
            dol_syslog(get_class($this) . "::delete_module_parts", LOG_DEBUG);
1916
1917
            if (empty($this->module_parts['icon']) && !empty($this->picto) && preg_match('/^fa\-/', $this->picto)) {
1918
                $this->module_parts['icon'] = $this->picto;
1919
            }
1920
1921
            foreach ($this->module_parts as $key => $value) {
1922
                // If entity is defined
1923
                if (is_array($value) && isset($value['entity'])) {
1924
                    $entity = $value['entity'];
1925
                } else {
1926
                    $entity = $conf->entity;
1927
                }
1928
1929
                $sql = "DELETE FROM " . $dbPrefix . "const";
1930
                $sql .= " WHERE " . $this->db->decrypt('name') . " LIKE '" . $this->db->escape($this->const_name) . "_" . strtoupper($key) . "'";
1931
                $sql .= " AND entity = " . ((int)$entity);
1932
1933
                if (!$this->db->query($sql)) {
1934
                    $this->error = $this->db->lasterror();
1935
                    $err++;
1936
                }
1937
            }
1938
        }
1939
        return $err;
1940
    }
1941
1942
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1943
1944
    /**
1945
     * Removes constants tagged 'deleteonunactive'
1946
     *
1947
     * @return int Return integer <0 if KO, 0 if OK
1948
     */
1949
    public function delete_const()
1950
    {
1951
        // phpcs:enable
1952
        global $conf;
1953
        $dbPrefix = $conf->db->prefix;
1954
1955
        $err = 0;
1956
1957
        if (empty($this->const)) {
1958
            return 0;
1959
        }
1960
1961
        foreach ($this->const as $key => $value) {
1962
            $name = $this->const[$key][0];
1963
            $deleteonunactive = (!empty($this->const[$key][6])) ? 1 : 0;
1964
1965
            if ($deleteonunactive) {
1966
                $sql = "DELETE FROM " . $dbPrefix . "const";
1967
                $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($name) . "'";
1968
                $sql .= " AND entity in (0, " . $conf->entity . ")";
1969
                dump($sql);
1970
                dol_syslog(get_class($this) . "::delete_const", LOG_DEBUG);
1971
                if (!$this->db->query($sql)) {
1972
                    $this->error = $this->db->lasterror();
1973
                    $err++;
1974
                }
1975
            }
1976
        }
1977
1978
        return $err;
1979
    }
1980
1981
1982
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1983
1984
    /**
1985
     * Removes boxes
1986
     *
1987
     * @return int Error count (0 if OK)
1988
     */
1989
    public function delete_boxes()
1990
    {
1991
        // phpcs:enable
1992
        global $conf;
1993
        $dbPrefix = $conf->db->prefix;
1994
1995
        $err = 0;
1996
1997
        if (is_array($this->boxes)) {
1998
            foreach ($this->boxes as $key => $value) {
1999
                //$titre = $this->boxes[$key][0];
2000
                if (empty($this->boxes[$key]['file'])) {
2001
                    $file = isset($this->boxes[$key][1]) ? $this->boxes[$key][1] : ''; // For backward compatibility
2002
                } else {
2003
                    $file = $this->boxes[$key]['file'];
2004
                }
2005
2006
                //$note  = $this->boxes[$key][2];
2007
2008
                // TODO If the box is also included by another module and the other module is still on, we should not remove it.
2009
                // For the moment, we manage this with hard coded exception
2010
                //print "Remove box ".$file.'<br>';
2011
                if ($file == 'box_graph_product_distribution.php') {
2012
                    if (isModEnabled("product") || isModEnabled("service")) {
2013
                        dol_syslog("We discard deleting module " . $file . " because another module still active requires it.");
2014
                        continue;
2015
                    }
2016
                }
2017
2018
                if ($this->db->type == 'sqlite3') {
2019
                    // sqlite doesn't support "USING" syntax.
2020
                    // TODO: remove this dependency.
2021
                    $sql = "DELETE FROM " . $dbPrefix . "boxes ";
2022
                    $sql .= "WHERE " . $dbPrefix . "boxes.box_id IN (";
2023
                    $sql .= "SELECT " . $dbPrefix . "boxes_def.rowid ";
2024
                    $sql .= "FROM " . $dbPrefix . "boxes_def ";
2025
                    $sql .= "WHERE " . $dbPrefix . "boxes_def.file = '" . $this->db->escape($file) . "') ";
2026
                    $sql .= "AND " . $dbPrefix . "boxes.entity = " . $conf->entity;
2027
                } else {
2028
                    $sql = "DELETE FROM " . $dbPrefix . "boxes";
2029
                    $sql .= " USING " . $dbPrefix . "boxes, " . $dbPrefix . "boxes_def";
2030
                    $sql .= " WHERE " . $dbPrefix . "boxes.box_id = " . $dbPrefix . "boxes_def.rowid";
2031
                    $sql .= " AND " . $dbPrefix . "boxes_def.file = '" . $this->db->escape($file) . "'";
2032
                    $sql .= " AND " . $dbPrefix . "boxes.entity = " . $conf->entity;
2033
                }
2034
2035
                dol_syslog(get_class($this) . "::delete_boxes", LOG_DEBUG);
2036
                $resql = $this->db->query($sql);
2037
                if (!$resql) {
2038
                    $this->error = $this->db->lasterror();
2039
                    $err++;
2040
                }
2041
2042
                $sql = "DELETE FROM " . $dbPrefix . "boxes_def";
2043
                $sql .= " WHERE file = '" . $this->db->escape($file) . "'";
2044
                $sql .= " AND entity = " . $conf->entity;     // Do not use getEntity here, we want to delete only in current company
2045
2046
                dol_syslog(get_class($this) . "::delete_boxes", LOG_DEBUG);
2047
                $resql = $this->db->query($sql);
2048
                if (!$resql) {
2049
                    $this->error = $this->db->lasterror();
2050
                    $err++;
2051
                }
2052
            }
2053
        }
2054
2055
        return $err;
2056
    }
2057
2058
2059
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2060
2061
    /**
2062
     * Removes boxes
2063
     *
2064
     * @return int Error count (0 if OK)
2065
     */
2066
    public function delete_cronjobs()
2067
    {
2068
        // phpcs:enable
2069
        global $conf;
2070
        $dbPrefix = $conf->db->prefix;
2071
2072
        $err = 0;
2073
2074
        if (is_array($this->cronjobs)) {
2075
            $sql = "DELETE FROM " . $dbPrefix . "cronjob";
2076
            $sql .= " WHERE module_name = '" . $this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class) . "'";
2077
            $sql .= " AND entity = " . $conf->entity;
2078
            $sql .= " AND test = '1'"; // We delete on lines that are not set with a complete test that is '$conf->module->enabled' so when module is disabled, the cron is also removed.
2079
            // For crons declared with a '$conf->module->enabled', there is no need to delete the line, so we don't loose setup if we reenable module.
2080
2081
            dol_syslog(get_class($this) . "::delete_cronjobs", LOG_DEBUG);
2082
            $resql = $this->db->query($sql);
2083
            if (!$resql) {
2084
                $this->error = $this->db->lasterror();
2085
                $err++;
2086
            }
2087
        }
2088
2089
        return $err;
2090
    }
2091
2092
2093
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2094
2095
    /**
2096
     * Removes access rights
2097
     *
2098
     * @return int                     Error count (0 if OK)
2099
     */
2100
    public function delete_permissions()
2101
    {
2102
        // phpcs:enable
2103
        global $conf;
2104
        $dbPrefix = $conf->db->prefix;
2105
2106
        $err = 0;
2107
2108
        $sql = "DELETE FROM " . $dbPrefix . "rights_def";
2109
        $sql .= " WHERE module = '" . $this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class) . "'";
2110
        $sql .= " AND entity = " . $conf->entity;
2111
        dol_syslog(get_class($this) . "::delete_permissions", LOG_DEBUG);
2112
        if (!$this->db->query($sql)) {
2113
            $this->error = $this->db->lasterror();
2114
            $err++;
2115
        }
2116
2117
        return $err;
2118
    }
2119
2120
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2121
2122
    /**
2123
     * Removes menu entries
2124
     *
2125
     * @return int Error count (0 if OK)
2126
     */
2127
    public function delete_menus()
2128
    {
2129
        // phpcs:enable
2130
        global $conf;
2131
        $dbPrefix = $conf->db->prefix;
2132
2133
        $err = 0;
2134
2135
        //$module=strtolower($this->name);        TODO When right_class will be same than module name
2136
        $module = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
2137
2138
        $sql = "DELETE FROM " . $dbPrefix . "menu";
2139
        $sql .= " WHERE module = '" . $this->db->escape($module) . "'";
2140
        $sql .= " AND entity IN (0, " . $conf->entity . ")";
2141
2142
        dol_syslog(get_class($this) . "::delete_menus", LOG_DEBUG);
2143
        $resql = $this->db->query($sql);
2144
        if (!$resql) {
2145
            $this->error = $this->db->lasterror();
2146
            $err++;
2147
        }
2148
2149
        return $err;
2150
    }
2151
2152
2153
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2154
2155
    /**
2156
     * Removes directories
2157
     *
2158
     * @return int Error count (0 if OK)
2159
     */
2160
    public function delete_dirs()
2161
    {
2162
        // phpcs:enable
2163
        global $conf;
2164
        $dbPrefix = $conf->db->prefix;
2165
2166
        $err = 0;
2167
2168
        $sql = "DELETE FROM " . $dbPrefix . "const";
2169
        $sql .= " WHERE " . $this->db->decrypt('name') . " LIKE '" . $this->db->escape($this->const_name) . "_DIR_%'";
2170
        $sql .= " AND entity = " . $conf->entity;
2171
2172
        dol_syslog(get_class($this) . "::delete_dirs", LOG_DEBUG);
2173
        if (!$this->db->query($sql)) {
2174
            $this->error = $this->db->lasterror();
2175
            $err++;
2176
        }
2177
2178
        return $err;
2179
    }
2180
2181
2182
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2183
2184
    /**
2185
     * Return Kanban view of a module
2186
     *
2187
     * @param string $codeenabledisable HTML code for button to enable/disable module
2188
     * @param string $codetoconfig HTML code to go to config page
2189
     *
2190
     * @return  string                          HTML code of Kanban view
2191
     */
2192
    public function getKanbanView($codeenabledisable = '', $codetoconfig = '')
2193
    {
2194
        global $conf, $langs;
2195
        $dbPrefix = $conf->db->prefix;
2196
2197
        // Define imginfo
2198
        $imginfo = "info";
2199
        if ($this->isCoreOrExternalModule() == 'external') {
2200
            $imginfo = "info_black";
2201
        }
2202
2203
        $const_name = 'MAIN_MODULE_' . strtoupper(preg_replace('/^mod/i', '', get_class($this)));
2204
2205
        $version = $this->getVersion(0);
2206
        $versiontrans = '';
2207
        if (preg_match('/development/i', $version)) {
2208
            $versiontrans .= 'warning';
2209
        }
2210
        if (preg_match('/experimental/i', $version)) {
2211
            $versiontrans .= 'warning';
2212
        }
2213
        if (preg_match('/deprecated/i', $version)) {
2214
            $versiontrans .= 'warning';
2215
        }
2216
2217
        $return = '
2218
    	<div class="box-flex-item info-box-module'
2219
            . (getDolGlobalString($const_name) ? '' : ' --disabled')
2220
            . ($this->isCoreOrExternalModule() == 'external' ? ' --external' : '')
2221
            . ($this->needUpdate ? ' --need-update' : '')
2222
            . '">
2223
	    <div class="info-box info-box-sm info-box-module">
2224
	    <div class="info-box-icon' . (!getDolGlobalString($const_name) ? '' : ' info-box-icon-module-enabled' . ($versiontrans ? ' info-box-icon-module-warning' : '')) . '">';
2225
2226
        $alttext = '';
2227
        //if (is_array($objMod->need_dolibarr_version)) $alttext.=($alttext?' - ':'').'Dolibarr >= '.join('.',$objMod->need_dolibarr_version);
2228
        //if (is_array($objMod->phpmin)) $alttext.=($alttext?' - ':'').'PHP >= '.join('.',$objMod->phpmin);
2229
        if (!empty($this->picto)) {
2230
            if (preg_match('/^\//i', $this->picto)) {
2231
                $return .= img_picto($alttext, $this->picto, 'class="inline-block valignmiddle"', 1);
2232
            } else {
2233
                $return .= img_object($alttext, $this->picto, 'class="inline-block valignmiddle"');
2234
            }
2235
        } else {
2236
            $return .= img_object($alttext, 'generic', 'class="inline-block valignmiddle"');
2237
        }
2238
2239
        if ($this->isCoreOrExternalModule() == 'external' || preg_match('/development|experimental|deprecated/i', $version)) {
2240
            $versionTitle = $langs->trans("Version") . ' ' . $this->getVersion(1);
2241
            if ($this->needUpdate) {
2242
                $versionTitle .= '<br>' . $langs->trans('ModuleUpdateAvailable') . ' : ' . $this->lastVersion;
2243
            }
2244
2245
            $return .= '<span class="info-box-icon-version' . ($versiontrans ? ' ' . $versiontrans : '') . ' classfortooltip" title="' . dol_escape_js($versionTitle) . '" >';
2246
            $return .= $this->getVersion(1);
2247
            $return .= '</span>';
2248
        }
2249
2250
        $return .= '</div>
2251
	    <div class="info-box-content info-box-text-module' . (!getDolGlobalString($const_name) ? '' : ' info-box-module-enabled' . ($versiontrans ? ' info-box-content-warning' : '')) . '">
2252
	    <span class="info-box-title">' . $this->getName() . '</span>
2253
	    <span class="info-box-desc twolinesmax opacitymedium" title="' . dol_escape_htmltag($this->getDesc()) . '">' . nl2br($this->getDesc()) . '</span>';
2254
2255
        $return .= '<div class="valignmiddle inline-block info-box-more">';
2256
        //if ($versiontrans) print img_warning($langs->trans("Version").' '.$this->getVersion(1)).' ';
2257
        $return .= '<a class="valignmiddle inline-block" href="javascript:document_preview(\'' . DOL_URL_ROOT . '/admin/modulehelp.php?id=' . ((int)$this->numero) . '\',\'text/html\',\'' . dol_escape_js($langs->trans("Module")) . '\')">' . img_picto(($this->isCoreOrExternalModule() == 'external' ? $langs->trans("ExternalModule") . ' - ' : '') . $langs->trans("ClickToShowDescription"), $imginfo) . '</a>';
2258
        $return .= '</div><br>';
2259
2260
        $return .= '<div class="valignmiddle inline-block info-box-actions">';
2261
        $return .= '<div class="valignmiddle inline-block info-box-setup">';
2262
        $return .= $codetoconfig;
2263
        $return .= '</div>';
2264
        $return .= '<div class="valignmiddle inline-block marginleftonly marginrightonly">';
2265
        $return .= $codeenabledisable;
2266
        $return .= '</div>';
2267
        $return .= '</div>';
2268
2269
        $return .= '
2270
	    </div><!-- /.info-box-content -->
2271
	    </div><!-- /.info-box -->
2272
	    </div>';
2273
2274
        return $return;
2275
    }
2276
2277
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2278
2279
    /**
2280
     * Tells if module is core or external.
2281
     * 'dolibarr' and 'dolibarr_deprecated' is core
2282
     * 'experimental' and 'development' is core
2283
     *
2284
     * @return string  'core', 'external' or 'unknown'
2285
     */
2286
    public function isCoreOrExternalModule()
2287
    {
2288
        if ($this->version == 'dolibarr' || $this->version == 'dolibarr_deprecated') {
2289
            return 'core';
2290
        }
2291
        if (!empty($this->version) && !in_array($this->version, ['experimental', 'development'])) {
2292
            return 'external';
2293
        }
2294
        if (!empty($this->editor_name) || !empty($this->editor_url)) {
2295
            return 'external';
2296
        }
2297
        if ($this->numero >= 100000) {
2298
            return 'external';
2299
        }
2300
        return 'unknown';
2301
    }
2302
2303
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2304
2305
    /**
2306
     * Gives module version (translated if param $translated is on)
2307
     * For 'experimental' modules, gives 'experimental' translation
2308
     * For 'dolibarr' modules, gives Dolibarr version
2309
     *
2310
     * @param int $translated 1=Special version keys are translated, 0=Special version keys are not translated
2311
     *
2312
     * @return string                       Module version
2313
     */
2314
    public function getVersion($translated = 1)
2315
    {
2316
        global $langs;
2317
        $langs->load("admin");
2318
2319
        $ret = '';
2320
2321
        $newversion = preg_replace('/_deprecated/', '', $this->version);
2322
        if ($newversion == 'experimental') {
2323
            $ret = ($translated ? $langs->transnoentitiesnoconv("VersionExperimental") : $newversion);
2324
        } elseif ($newversion == 'development') {
2325
            $ret = ($translated ? $langs->transnoentitiesnoconv("VersionDevelopment") : $newversion);
2326
        } elseif ($newversion == 'dolibarr') {
2327
            $ret = DOL_VERSION;
2328
        } elseif ($newversion) {
2329
            $ret = $newversion;
2330
        } else {
2331
            $ret = ($translated ? $langs->transnoentitiesnoconv("VersionUnknown") : 'unknown');
2332
        }
2333
2334
        if (preg_match('/_deprecated/', $this->version)) {
2335
            $ret .= ($translated ? ' (' . $langs->transnoentitiesnoconv("Deprecated") . ')' : $this->version);
2336
        }
2337
        return $ret;
2338
    }
2339
2340
    /**
2341
     * Gives the translated module name if translation exists in admin.lang or into language files of module.
2342
     * Otherwise return the module key name.
2343
     *
2344
     * @return string  Translated module name
2345
     */
2346
    public function getName()
2347
    {
2348
        global $langs;
2349
        $langs->load("admin");
2350
2351
        if ($langs->transnoentitiesnoconv("Module" . $this->numero . "Name") != "Module" . $this->numero . "Name") {
2352
            // If module name translation exists
2353
            return $langs->transnoentitiesnoconv("Module" . $this->numero . "Name");
2354
        } else {
2355
            // If module name translation using it's unique id does not exist, we try to use its name to find translation
2356
            if (is_array($this->langfiles)) {
2357
                foreach ($this->langfiles as $val) {
2358
                    if ($val) {
2359
                        $langs->load($val);
2360
                    }
2361
                }
2362
            }
2363
2364
            if ($langs->trans("Module" . $this->name . "Name") != "Module" . $this->name . "Name") {
2365
                // If module name translation exists
2366
                return $langs->transnoentitiesnoconv("Module" . $this->name . "Name");
2367
            }
2368
2369
            // Last chance with simple label
2370
            return $langs->transnoentitiesnoconv($this->name);
2371
        }
2372
    }
2373
2374
    /**
2375
     * Gives the translated module description if translation exists in admin.lang or the default module description
2376
     *
2377
     * @return string  Translated module description
2378
     */
2379
    public function getDesc()
2380
    {
2381
        global $langs;
2382
        $langs->load("admin");
2383
2384
        if ($langs->transnoentitiesnoconv("Module" . $this->numero . "Desc") != "Module" . $this->numero . "Desc") {
2385
            // If module description translation exists
2386
            return $langs->transnoentitiesnoconv("Module" . $this->numero . "Desc");
2387
        } else {
2388
            // If module description translation does not exist using its unique id, we can use its name to find translation
2389
            if (is_array($this->langfiles)) {
2390
                foreach ($this->langfiles as $val) {
2391
                    if ($val) {
2392
                        $langs->load($val);
2393
                    }
2394
                }
2395
            }
2396
2397
            if ($langs->transnoentitiesnoconv("Module" . $this->name . "Desc") != "Module" . $this->name . "Desc") {
2398
                // If module name translation exists
2399
                return $langs->trans("Module" . $this->name . "Desc");
2400
            }
2401
2402
            // Last chance with simple label
2403
            return $langs->trans($this->description);
2404
        }
2405
    }
2406
2407
    /**
2408
     * Check for module update
2409
     * TODO : store results for $this->url_last_version and $this->needUpdate
2410
     * Add a cron task to monitor for updates
2411
     *
2412
     * @return int Return integer <0 if Error, 0 == no update needed,  >0 if need update
2413
     */
2414
    public function checkForUpdate()
2415
    {
2416
        require_once BASE_PATH . '/../Dolibarr/Lib/GetUrl.php';
2417
        if (!empty($this->url_last_version)) {
2418
            $lastVersion = getURLContent($this->url_last_version, 'GET', '', 1, [], ['http', 'https'], 0);    // Accept http or https links on external remote server only
2419
            if (isset($lastVersion['content']) && strlen($lastVersion['content']) < 30) {
2420
                // Security warning :  be careful with remote data content, the module editor could be hacked (or evil) so limit to a-z A-Z 0-9 _ . -
2421
                $this->lastVersion = preg_replace("/[^a-zA-Z0-9_\.\-]+/", "", $lastVersion['content']);
2422
                if (version_compare($this->lastVersion, $this->version) > 0) {
2423
                    $this->needUpdate = true;
2424
                    return 1;
2425
                } else {
2426
                    $this->needUpdate = false;
2427
                    return 0;
2428
                }
2429
            } else {
2430
                return -1;
2431
            }
2432
        }
2433
        return 0;
2434
    }
2435
2436
    /**
2437
     * Create tables and keys required by module:
2438
     * - Files table.sql or table-module.sql with create table instructions
2439
     * - Then table.key.sql or table-module.key.sql with create keys instructions
2440
     * - Then data_xxx.sql (usually provided by external modules only)
2441
     * - Then update_xxx.sql (usually provided by external modules only)
2442
     * Files must be stored in subdirectory 'tables' or 'data' into directory $reldir (Example: '/install/mysql/' or
2443
     * '/module/sql/') This function may also be called by :
2444
     * - _load_tables('/install/mysql/', 'modulename') into the this->init() of core module descriptors.
2445
     * - _load_tables('/mymodule/sql/') into the this->init() of external module descriptors.
2446
     *
2447
     * @param string $reldir Relative directory where to scan files. Example: '/install/mysql/' or
2448
     *                               '/module/sql/'
2449
     * @param string $onlywithsuffix Only with the defined suffix
2450
     *
2451
     * @return  int                         Return integer <=0 if KO, >0 if OK
2452
     */
2453
    protected function _load_tables($reldir, $onlywithsuffix = '')
2454
    {
2455
        // phpcs:enable
2456
        global $conf;
2457
        $dbPrefix = $conf->db->prefix;
2458
2459
        $error = 0;
2460
        $dirfound = 0;
2461
2462
        if (empty($reldir)) {
2463
            return 1;
2464
        }
2465
2466
        include_once BASE_PATH . '/../Dolibarr/Lib/Admin.php';
2467
2468
        $ok = 1;
2469
        if (isset($conf->file->dol_document_root)) {
2470
            foreach ($conf->file->dol_document_root as $dirroot) {
2471
                if ($ok) {
2472
                    $dirsql = $dirroot . $reldir;
2473
                    $ok = 0;
2474
2475
                    // We will loop on xxx/, xxx/tables/, xxx/data/
2476
                    $listofsubdir = ['', 'tables/', 'data/'];
2477
                    if ($this->db->type == 'pgsql') {
2478
                        $listofsubdir[] = '../pgsql/functions/';
2479
                    }
2480
2481
                    foreach ($listofsubdir as $subdir) {
2482
                        $dir = $dirsql . $subdir;
2483
2484
                        $handle = @opendir($dir); // Dir may not exists
2485
                        if (is_resource($handle)) {
2486
                            $dirfound++;
2487
2488
                            // Run llx_mytable.sql files, then llx_mytable_*.sql
2489
                            $files = [];
2490
                            while (($file = readdir($handle)) !== false) {
2491
                                $files[] = $file;
2492
                            }
2493
                            sort($files);
2494
                            foreach ($files as $file) {
2495
                                if ($onlywithsuffix) {
2496
                                    if (!preg_match('/\-' . preg_quote($onlywithsuffix, '/') . '\./i', $file)) {
2497
                                        //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
2498
                                        continue;
2499
                                    } else {
2500
                                        //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
2501
                                    }
2502
                                }
2503
                                if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'llx_') {
2504
                                    $result = run_sql($dir . $file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
2505
                                    if ($result <= 0) {
2506
                                        $error++;
2507
                                    }
2508
                                }
2509
                            }
2510
2511
                            rewinddir($handle);
2512
2513
                            // Run llx_mytable.key.sql files (Must be done after llx_mytable.sql) then then llx_mytable_*.key.sql
2514
                            $files = [];
2515
                            while (($file = readdir($handle)) !== false) {
2516
                                $files[] = $file;
2517
                            }
2518
                            sort($files);
2519
                            foreach ($files as $file) {
2520
                                if ($onlywithsuffix) {
2521
                                    if (!preg_match('/\-' . preg_quote($onlywithsuffix, '/') . '\./i', $file)) {
2522
                                        //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
2523
                                        continue;
2524
                                    } else {
2525
                                        //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
2526
                                    }
2527
                                }
2528
                                if (preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'llx_') {
2529
                                    $result = run_sql($dir . $file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
2530
                                    if ($result <= 0) {
2531
                                        $error++;
2532
                                    }
2533
                                }
2534
                            }
2535
2536
                            rewinddir($handle);
2537
2538
                            // Run functions-xxx.sql files (Must be done after llx_mytable.key.sql)
2539
                            $files = [];
2540
                            while (($file = readdir($handle)) !== false) {
2541
                                $files[] = $file;
2542
                            }
2543
                            sort($files);
2544
                            foreach ($files as $file) {
2545
                                if ($onlywithsuffix) {
2546
                                    if (!preg_match('/\-' . preg_quote($onlywithsuffix, '/') . '\./i', $file)) {
2547
                                        //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
2548
                                        continue;
2549
                                    } else {
2550
                                        //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
2551
                                    }
2552
                                }
2553
                                if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 9) == 'functions') {
2554
                                    $result = run_sql($dir . $file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
2555
                                    if ($result <= 0) {
2556
                                        $error++;
2557
                                    }
2558
                                }
2559
                            }
2560
2561
                            rewinddir($handle);
2562
2563
                            // Run data_xxx.sql files (Must be done after llx_mytable.key.sql)
2564
                            $files = [];
2565
                            while (($file = readdir($handle)) !== false) {
2566
                                $files[] = $file;
2567
                            }
2568
                            sort($files);
2569
                            foreach ($files as $file) {
2570
                                if ($onlywithsuffix) {
2571
                                    if (!preg_match('/\-' . preg_quote($onlywithsuffix, '/') . '\./i', $file)) {
2572
                                        //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
2573
                                        continue;
2574
                                    } else {
2575
                                        //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
2576
                                    }
2577
                                }
2578
                                if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'data') {
2579
                                    $result = run_sql($dir . $file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
2580
                                    if ($result <= 0) {
2581
                                        $error++;
2582
                                    }
2583
                                }
2584
                            }
2585
2586
                            rewinddir($handle);
2587
2588
                            // Run update_xxx.sql files
2589
                            $files = [];
2590
                            while (($file = readdir($handle)) !== false) {
2591
                                $files[] = $file;
2592
                            }
2593
                            sort($files);
2594
                            foreach ($files as $file) {
2595
                                if ($onlywithsuffix) {
2596
                                    if (!preg_match('/\-' . preg_quote($onlywithsuffix, '/') . '\./i', $file)) {
2597
                                        //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded<br>'."\n";
2598
                                        continue;
2599
                                    } else {
2600
                                        //print 'File '.$file.' match suffix '.$onlywithsuffix.' so we keep it<br>'."\n";
2601
                                    }
2602
                                }
2603
                                if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 6) == 'update') {
2604
                                    $result = run_sql($dir . $file, !getDolGlobalString('MAIN_DISPLAY_SQL_INSTALL_LOG') ? 1 : 0, '', 1);
2605
                                    if ($result <= 0) {
2606
                                        $error++;
2607
                                    }
2608
                                }
2609
                            }
2610
2611
                            closedir($handle);
2612
                        }
2613
                    }
2614
2615
                    if ($error == 0) {
2616
                        $ok = 1;
2617
                    }
2618
                }
2619
            }
2620
        }
2621
        if (!$dirfound) {
2622
            dol_syslog("A module ask to load sql files into " . $reldir . " but this directory was not found.", LOG_WARNING);
2623
        }
2624
        return $ok;
2625
    }
2626
}
2627