Passed
Push — master ( 043752...2a5ce0 )
by Alxarafe
27:58
created

htdocs/core/modules/DolibarrModules.class.php (2 issues)

1
<?php
2
/* Copyright (C) 2003-2007  Rodolphe Quiedeville    <[email protected]>
3
 * Copyright (C) 2004       Sebastien Di Cintio     <[email protected]>
4
 * Copyright (C) 2004       Benoit Mortier          <[email protected]>
5
 * Copyright (C) 2004       Eric Seigne             <[email protected]>
6
 * Copyright (C) 2005-2013  Laurent Destailleur     <[email protected]>
7
 * Copyright (C) 2005-2012  Regis Houssin           <[email protected]>
8
 * Copyright (C) 2014       Raphaël Doursenaud      <[email protected]>
9
 * Copyright (C) 2018       Josep Lluís Amador      <[email protected]>
10
 * Copyright (C) 2019       Alxarafe                <[email protected]>
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 3 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24
 */
25
defined('BASE_PATH') or die('Single entry point through the index.php of the main folder');
26
27
/**
28
 * \file           htdocs/core/modules/DolibarrModules.class.php
29
 * \brief          File of parent class of module descriptor class files
30
 */
31
32
/**
33
 * Class DolibarrModules
34
 *
35
 * Parent class for module descriptor class files
36
 */
37
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.
38
{
39
40
    /**
41
     * @var DoliDb Database handler
42
     */
43
    public $db;
44
45
    /**
46
     * @var int Module unique ID
47
     * @see https://wiki.dolibarr.org/index.php/List_of_modules_id
48
     */
49
    public $numero;
50
51
    /**
52
     * @var   string Publisher name
53
     * @since 4.0.0
54
     */
55
    public $editor_name;
56
57
    /**
58
     * @var   string URL of module at publisher site
59
     * @since 4.0.0
60
     */
61
    public $editor_url;
62
63
    /**
64
     * @var string Family
65
     * @see familyinfo
66
     *
67
     * Native values: 'crm', 'financial', 'hr', 'projects', 'products', 'ecm', 'technic', 'other'.
68
     * Use familyinfo to declare a custom value.
69
     */
70
    public $family;
71
72
    /**
73
     * @var array Custom family informations
74
     * @see family
75
     *
76
     * e.g.:
77
     * array(
78
     *     'myownfamily' => array(
79
     *         'position' => '001',
80
     *         'label' => $langs->trans("MyOwnFamily")
81
     *     )
82
     * );
83
     */
84
    public $familyinfo;
85
86
    /**
87
     * @var string    Module position on 2 digits
88
     */
89
    public $module_position = '50';
90
91
    /**
92
     * @var string Module name
93
     *
94
     * Only used if Module[ID]Name translation string is not found.
95
     *
96
     * You can use the following code to automatically derive it from your module's class name:
97
     * preg_replace('/^mod/i', '', get_class($this))
98
     */
99
    public $name;
100
101
    /**
102
     * @var string[] Paths to create when module is activated
103
     *
104
     * e.g.: array('/mymodule/temp')
105
     */
106
    public $dirs = array();
107
108
    /**
109
     * @var array Module boxes
110
     */
111
    public $boxes = array();
112
113
    /**
114
     * @var array Module constants
115
     */
116
    public $const = array();
117
118
    /**
119
     * @var array Module cron jobs entries
120
     */
121
    public $cronjobs = array();
122
123
    /**
124
     * @var array Module access rights
125
     */
126
    public $rights;
127
128
    /**
129
     * @var string Module access rights family
130
     */
131
    public $rights_class;
132
133
    /**
134
     * @var array Module menu entries
135
     */
136
    public $menu = array();
137
138
    /**
139
     * @var array Module parts
140
     *  array(
141
     *      // Set this to 1 if module has its own trigger directory (/mymodule/core/triggers)
142
     *      'triggers' => 0,
143
     *      // Set this to 1 if module has its own login method directory (/mymodule/core/login)
144
     *      'login' => 0,
145
     *      // Set this to 1 if module has its own substitution function file (/mymodule/core/substitutions)
146
     *      'substitutions' => 0,
147
     *      // Set this to 1 if module has its own menus handler directory (/mymodule/core/menus)
148
     *      'menus' => 0,
149
     *      // Set this to 1 if module has its own theme directory (/mymodule/theme)
150
     *      'theme' => 0,
151
     *      // Set this to 1 if module overwrite template dir (/mymodule/core/tpl)
152
     *      'tpl' => 0,
153
     *      // Set this to 1 if module has its own barcode directory (/mymodule/core/modules/barcode)
154
     *      'barcode' => 0,
155
     *      // Set this to 1 if module has its own models directory (/mymodule/core/modules/xxx)
156
     *      'models' => 0,
157
     *      // Set this to relative path of css file if module has its own css file
158
     *      'css' => '/mymodule/css/mymodule.css.php',
159
     *      // Set this to relative path of js file if module must load a js on all pages
160
     *      'js' => '/mymodule/js/mymodule.js',
161
     *      // Set here all hooks context managed by module
162
     *      'hooks' => array('hookcontext1','hookcontext2')
163
     *  )
164
     */
165
    public $module_parts = array();
166
167
    /**
168
     * @var        string Module documents ?
169
     * @deprecated Seems unused anywhere
170
     */
171
    public $docs;
172
173
    /**
174
     * @var        string ?
175
     * @deprecated Seems unused anywhere
176
     */
177
    public $dbversion = "-";
178
179
    /**
180
     * @var string Error message
181
     */
182
    public $error;
183
184
    /**
185
     * @var string Module version
186
     * @see http://semver.org
187
     *
188
     * The following keywords can also be used:
189
     * 'development'
190
     * 'experimental'
191
     * 'dolibarr': only for core modules that share its version
192
     * 'dolibarr_deprecated': only for deprecated core modules
193
     */
194
    public $version;
195
196
    /**
197
     * @var string Module description (short text)
198
     *
199
     * Only used if Module[ID]Desc translation string is not found.
200
     */
201
    public $description;
202
203
    /**
204
     * @var   string Module description (long text)
205
     * @since 4.0.0
206
     *
207
     * HTML content supported.
208
     */
209
    public $descriptionlong;
210
211
212
    // For exports
213
214
    /**
215
     * @var string Module export code
216
     */
217
    public $export_code;
218
219
    /**
220
     * @var string Module export label
221
     */
222
    public $export_label;
223
    public $export_permission;
224
    public $export_fields_array;
225
    public $export_TypeFields_array;
226
    public $export_entities_array;
227
    public $export_special_array;           // special or computed field
228
    public $export_dependencies_array;
229
    public $export_sql_start;
230
    public $export_sql_end;
231
    public $export_sql_order;
232
233
234
    // For import
235
236
    /**
237
     * @var string Module import code
238
     */
239
    public $import_code;
240
241
    /**
242
     * @var string Module import label
243
     */
244
    public $import_label;
245
246
    /**
247
     * @var string Module constant name
248
     */
249
    public $const_name;
250
251
    /**
252
     * @var bool Module can't be disabled
253
     */
254
    public $always_enabled;
255
256
    /**
257
     * @var int Module is enabled globally (Multicompany support)
258
     */
259
    public $core_enabled;
260
261
    /**
262
     * @var        string Relative path to module style sheet
263
     * @deprecated
264
     * @see        module_parts
265
     */
266
    public $style_sheet = '';
267
268
    /**
269
     * @var        0|1|2|3 Where to display the module in setup page
270
     * @deprecated @since 4.0.0
271
     * @see        family
272
     * @see        familyinfo
273
     *
274
     * 0: common
275
     * 1: interface
276
     * 2: others
277
     * 3: very specific
278
     */
279
    public $special;
280
281
    /**
282
     * @var string Name of image file used for this module
283
     *
284
     * If file is in theme/yourtheme/img directory under name object_pictoname.png use 'pictoname'
285
     * If file is in module/img directory under name object_pictoname.png use 'pictoname@module'
286
     */
287
    public $picto;
288
289
    /**
290
     * @var string[] List of config pages
291
     *
292
     * Name of php pages stored into module/admin directory, used to setup module.
293
     * e.g.: "admin.php@module"
294
     */
295
    public $config_page_url;
296
297
    /**
298
     * @var string[] List of module class names that must be enabled if this module is enabled.
299
     *
300
     * e.g.: array('modAnotherModule', 'FR'=>'modYetAnotherModule')
301
     */
302
    public $depends;
303
304
    /**
305
     * @var int[] List of module ids to disable if this one is disabled.
306
     */
307
    public $requiredby;
308
309
    /**
310
     * @var string[] List of module class names as string this module is in conflict with.
311
     * @see depends
312
     */
313
    public $conflictwith;
314
315
    /**
316
     * @var string[] Module language files
317
     */
318
    public $langfiles;
319
320
    /**
321
     * @var array<string,string> Array of warnings to show when we activate the module
322
     *
323
     * array('always'='text') or array('FR'='text')
324
     */
325
    public $warnings_activation;
326
327
    /**
328
     * @var array<string,string> Array of warnings to show when we activate an external module
329
     *
330
     * array('always'='text') or array('FR'='text')
331
     */
332
    public $warnings_activation_ext;
333
334
    /**
335
     * @var array() Minimum version of PHP required by module.
336
     * e.g.: PHP ≥ 5.4 = array(5, 4)
337
     */
338
    public $phpmin;
339
340
    /**
341
     * @var array Minimum version of Dolibarr required by module.
342
     * e.g.: Dolibarr ≥ 3.6 = array(3, 6)
343
     */
344
    public $need_dolibarr_version;
345
346
    /**
347
     * @var bool Whether to hide the module.
348
     */
349
    public $hidden = false;
350
351
    /**
352
     * Constructor. Define names, constants, directories, boxes, permissions
353
     *
354
     * @param DoliDB $db Database handler
355
     */
356
    public function __construct($db)
357
    {
358
        $this->db = $db;
359
    }
360
    // We should but can't set this as abstract because this will make dolibarr hang
361
    // after migration due to old module not implementing. We must wait PHP is able to make
362
    // a try catch on Fatal error to manage this correctly.
363
    // We need constructor into function unActivateModule into admin.lib.php
364
365
    /**
366
     * Enables a module.
367
     * Inserts all informations into database
368
     *
369
     * @param array  $array_sql SQL requests to be executed when enabling module
370
     * @param string $options   String with options when disabling module:
371
     *                          - 'noboxes' = Do not insert boxes -
372
     *                          'newboxdefonly' = For boxes, insert def of
373
     *                          boxes only and not boxes activation
374
     *
375
     * @return int                         1 if OK, 0 if KO
376
     */
377
    function _init($array_sql, $options = '')
378
    {
379
        global $conf;
380
        $err = 0;
381
382
        $this->db->begin();
383
384
        // Insert activation module constant
385
        if (!$err) {
386
            $err += $this->_active();
387
        }
388
389
        // Insert new pages for tabs (into llx_const)
390
        if (!$err) {
391
            $err += $this->insert_tabs();
392
        }
393
394
        // Insert activation of module's parts
395
        if (!$err) {
396
            $err += $this->insert_module_parts();
397
        }
398
399
        // Insert constant defined by modules (into llx_const)
400
        if (!$err && !preg_match('/newboxdefonly/', $options)) {
401
            $err += $this->insert_const();    // Test on newboxdefonly to avoid to erase value during upgrade
402
        }
403
404
        // Insert boxes def into llx_boxes_def and boxes setup (into llx_boxes)
405
        if (!$err && !preg_match('/noboxes/', $options)) {
406
            $err += $this->insert_boxes($options);
407
        }
408
409
        // Insert cron job entries (entry in llx_cronjobs)
410
        if (!$err) {
411
            $err += $this->insert_cronjobs();
412
        }
413
414
        // Insert permission definitions of module into llx_rights_def. If user is admin, grant this permission to user.
415
        if (!$err) {
416
            $err += $this->insert_permissions(1, null, 1);
417
        }
418
419
        // Insert specific menus entries into database
420
        if (!$err) {
421
            $err += $this->insert_menus();
422
        }
423
424
        // Create module's directories
425
        if (!$err) {
426
            $err += $this->create_dirs();
427
        }
428
429
        // Execute addons requests
430
        $num = count($array_sql);
431
        for ($i = 0; $i < $num; $i++) {
432
            if (!$err) {
433
                $val = $array_sql[$i];
434
                $sql = $val;
435
                $ignoreerror = 0;
436
                if (is_array($val)) {
437
                    $sql = $val['sql'];
438
                    $ignoreerror = $val['ignoreerror'];
439
                }
440
                // Add current entity id
441
                $sql = str_replace('__ENTITY__', $conf->entity, $sql);
442
443
                dol_syslog(get_class($this) . "::_init ignoreerror=" . $ignoreerror . "", LOG_DEBUG);
444
                $result = $this->db->query($sql, $ignoreerror);
445
                if (!$result) {
446
                    if (!$ignoreerror) {
447
                        $this->error = $this->db->lasterror();
448
                        $err++;
449
                    } else {
450
                        dol_syslog(get_class($this) . "::_init Warning " . $this->db->lasterror(), LOG_WARNING);
451
                    }
452
                }
453
            }
454
        }
455
456
        // Return code
457
        if (!$err) {
458
            $this->db->commit();
459
            return 1;
460
        } else {
461
            $this->db->rollback();
462
            return 0;
463
        }
464
    }
465
466
    /**
467
     * Disable function. Deletes the module constants and boxes from the database.
468
     *
469
     * @param string[] $array_sql SQL requests to be executed when module is disabled
470
     * @param string   $options   Options when disabling module:
471
     *
472
     * @return int                     1 if OK, 0 if KO
473
     */
474
    function _remove($array_sql, $options = '')
475
    {
476
        $err = 0;
477
478
        $this->db->begin();
479
480
        // Remove activation module line (constant MAIN_MODULE_MYMODULE in llx_const)
481
        if (!$err) {
482
            $err += $this->_unactive();
483
        }
484
485
        // Remove activation of module's new tabs (MAIN_MODULE_MYMODULE_TABS_XXX in llx_const)
486
        if (!$err) {
487
            $err += $this->delete_tabs();
488
        }
489
490
        // Remove activation of module's parts (MAIN_MODULE_MYMODULE_XXX in llx_const)
491
        if (!$err) {
492
            $err += $this->delete_module_parts();
493
        }
494
495
        // Remove constants defined by modules
496
        if (!$err) {
497
            $err += $this->delete_const();
498
        }
499
500
        // Remove list of module's available boxes (entry in llx_boxes)
501
        if (!$err && !preg_match('/(newboxdefonly|noboxes)/', $options)) {
502
            $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
503
        }
504
505
        // Remove list of module's cron job entries (entry in llx_cronjobs)
506
        if (!$err) {
507
            $err += $this->delete_cronjobs();
508
        }
509
510
        // Remove module's permissions from list of available permissions (entries in llx_rights_def)
511
        if (!$err) {
512
            $err += $this->delete_permissions();
513
        }
514
515
        // Remove module's menus (entries in llx_menu)
516
        if (!$err) {
517
            $err += $this->delete_menus();
518
        }
519
520
        // Remove module's directories
521
        if (!$err) {
522
            $err += $this->delete_dirs();
523
        }
524
525
        // Run complementary sql requests
526
        $num = count($array_sql);
527
        for ($i = 0; $i < $num; $i++) {
528
            if (!$err) {
529
                dol_syslog(get_class($this) . "::_remove", LOG_DEBUG);
530
                $result = $this->db->query($array_sql[$i]);
531
                if (!$result) {
532
                    $this->error = $this->db->error();
533
                    $err++;
534
                }
535
            }
536
        }
537
538
        // Return code
539
        if (!$err) {
540
            $this->db->commit();
541
            return 1;
542
        } else {
543
            $this->db->rollback();
544
            return 0;
545
        }
546
    }
547
548
    /**
549
     * Gives the translated module name if translation exists in admin.lang or into language files of module.
550
     * Otherwise return the module key name.
551
     *
552
     * @return string  Translated module name
553
     */
554
    function getName()
555
    {
556
        global $langs;
557
        $langs->load("admin");
558
559
        if ($langs->transnoentitiesnoconv("Module" . $this->numero . "Name") != ("Module" . $this->numero . "Name")) {
560
            // If module name translation exists
561
            return $langs->transnoentitiesnoconv("Module" . $this->numero . "Name");
562
        } else {
563
            // If module name translation using it's unique id does not exist, we try to use its name to find translation
564
            if (is_array($this->langfiles)) {
565
                foreach ($this->langfiles as $val) {
566
                    if ($val) {
567
                        $langs->load($val);
568
                    }
569
                }
570
            }
571
572
            if ($langs->trans("Module" . $this->name . "Name") != ("Module" . $this->name . "Name")) {
573
                // If module name translation exists
574
                return $langs->transnoentitiesnoconv("Module" . $this->name . "Name");
575
            }
576
577
            // Last chance with simple label
578
            return $langs->transnoentitiesnoconv($this->name);
579
        }
580
    }
581
582
    /**
583
     * Gives the translated module description if translation exists in admin.lang or the default module description
584
     *
585
     * @return string  Translated module description
586
     */
587
    function getDesc()
588
    {
589
        global $langs;
590
        $langs->load("admin");
591
592
        if ($langs->transnoentitiesnoconv("Module" . $this->numero . "Desc") != ("Module" . $this->numero . "Desc")) {
593
            // If module description translation exists
594
            return $langs->transnoentitiesnoconv("Module" . $this->numero . "Desc");
595
        } else {
596
            // If module description translation does not exist using its unique id, we can use its name to find translation
597
            if (is_array($this->langfiles)) {
598
                foreach ($this->langfiles as $val) {
599
                    if ($val) {
600
                        $langs->load($val);
601
                    }
602
                }
603
            }
604
605
            if ($langs->transnoentitiesnoconv("Module" . $this->name . "Desc") != ("Module" . $this->name . "Desc")) {
606
                // If module name translation exists
607
                return $langs->trans("Module" . $this->name . "Desc");
608
            }
609
610
            // Last chance with simple label
611
            return $langs->trans($this->description);
612
        }
613
    }
614
615
    /**
616
     * Gives the long description of a module. First check README-la_LA.md then README.md
617
     * If no markdown files found, it returns translated value of the key ->descriptionlong.
618
     *
619
     * @return string     Long description of a module from README.md of from property.
620
     */
621
    function getDescLong()
622
    {
623
        global $langs;
624
        $langs->load("admin");
625
626
        include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
627
        include_once DOL_DOCUMENT_ROOT . '/core/lib/geturl.lib.php';
628
629
        $pathoffile = $this->getDescLongReadmeFound();
630
631
        if ($pathoffile) {     // Mostly for external modules
632
            $content = file_get_contents($pathoffile);
633
634
            if ((float) DOL_VERSION >= 6.0) {
635
                @include_once DOL_DOCUMENT_ROOT . '/core/lib/parsemd.lib.php';
636
637
                $content = dolMd2Html(
638
                    $content, 'parsedown', array(
639
                    'doc/' => dol_buildpath(strtolower($this->name) . '/doc/', 1),
640
                    'img/' => dol_buildpath(strtolower($this->name) . '/img/', 1),
641
                    'images/' => dol_buildpath(strtolower($this->name) . '/imgages/', 1),
642
                    )
643
                );
644
            } else {
645
                $content = nl2br($content);
646
            }
647
        } else {                // Mostly for internal modules
648
            if (!empty($this->descriptionlong)) {
649
                if (is_array($this->langfiles)) {
650
                    foreach ($this->langfiles as $val) {
651
                        if ($val) {
652
                            $langs->load($val);
653
                        }
654
                    }
655
                }
656
657
                $content = $langs->transnoentitiesnoconv($this->descriptionlong);
658
            }
659
        }
660
661
        return $content;
662
    }
663
664
    /**
665
     * Return path of file if a README file was found.
666
     *
667
     * @return string      Path of file if a README file was found.
668
     */
669
    function getDescLongReadmeFound()
670
    {
671
        global $langs;
672
673
        $filefound = false;
674
675
        // Define path to file README.md.
676
        // First check README-la_LA.md then README-la.md then README.md
677
        $pathoffile = dol_buildpath(strtolower($this->name) . '/README-' . $langs->defaultlang . '.md', 0);
678
        if (dol_is_file($pathoffile)) {
679
            $filefound = true;
680
        }
681
        if (!$filefound) {
682
            $tmp = explode('_', $langs->defaultlang);
683
            $pathoffile = dol_buildpath(strtolower($this->name) . '/README-' . $tmp[0] . '.md', 0);
684
            if (dol_is_file($pathoffile)) {
685
                $filefound = true;
686
            }
687
        }
688
        if (!$filefound) {
689
            $pathoffile = dol_buildpath(strtolower($this->name) . '/README.md', 0);
690
            if (dol_is_file($pathoffile)) {
691
                $filefound = true;
692
            }
693
        }
694
695
        return ($filefound ? $pathoffile : '');
696
    }
697
698
    /**
699
     * Gives the changelog. First check ChangeLog-la_LA.md then ChangeLog.md
700
     *
701
     * @return string  Content of ChangeLog
702
     */
703
    function getChangeLog()
704
    {
705
        global $langs;
706
        $langs->load("admin");
707
708
        include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
709
        include_once DOL_DOCUMENT_ROOT . '/core/lib/geturl.lib.php';
710
711
        $filefound = false;
712
713
        // Define path to file README.md.
714
        // First check README-la_LA.md then README.md
715
        $pathoffile = dol_buildpath(strtolower($this->name) . '/ChangeLog-' . $langs->defaultlang . '.md', 0);
716
        if (dol_is_file($pathoffile)) {
717
            $filefound = true;
718
        }
719
        if (!$filefound) {
720
            $pathoffile = dol_buildpath(strtolower($this->name) . '/ChangeLog.md', 0);
721
            if (dol_is_file($pathoffile)) {
722
                $filefound = true;
723
            }
724
        }
725
726
        if ($filefound) {     // Mostly for external modules
727
            $content = file_get_contents($pathoffile);
728
729
            if ((float) DOL_VERSION >= 6.0) {
730
                @include_once DOL_DOCUMENT_ROOT . '/core/lib/parsemd.lib.php';
731
                $content = dolMd2Html($content, 'parsedown', array('doc/' => dol_buildpath(strtolower($this->name) . '/doc/', 1)));
732
            } else {
733
                $content = nl2br($content);
734
            }
735
        }
736
737
        return $content;
738
    }
739
740
    /**
741
     * Gives the publisher name
742
     *
743
     * @return string  Publisher name
744
     */
745
    function getPublisher()
746
    {
747
        return $this->editor_name;
748
    }
749
750
    /**
751
     * Gives the publisher url
752
     *
753
     * @return string  Publisher url
754
     */
755
    function getPublisherUrl()
756
    {
757
        return $this->editor_url;
758
    }
759
760
    /**
761
     * Gives module version (translated if param $translated is on)
762
     * For 'experimental' modules, gives 'experimental' translation
763
     * For 'dolibarr' modules, gives Dolibarr version
764
     *
765
     * @param  int $translated 1=Special version keys are translated, 0=Special version keys are not translated
766
     * @return string                  Module version
767
     */
768
    function getVersion($translated = 1)
769
    {
770
        global $langs;
771
        $langs->load("admin");
772
773
        $ret = '';
774
775
        $newversion = preg_replace('/_deprecated/', '', $this->version);
776
        if ($newversion == 'experimental') {
777
            $ret = ($translated ? $langs->transnoentitiesnoconv("VersionExperimental") : $newversion);
778
        } elseif ($newversion == 'development') {
779
            $ret = ($translated ? $langs->transnoentitiesnoconv("VersionDevelopment") : $newversion);
780
        } elseif ($newversion == 'dolibarr') {
781
            $ret = DOL_VERSION;
782
        } elseif ($newversion) {
783
            $ret = $newversion;
784
        } else {
785
            $ret = ($translated ? $langs->transnoentitiesnoconv("VersionUnknown") : 'unknown');
786
        }
787
788
        if (preg_match('/_deprecated/', $this->version)) {
789
            $ret .= ($translated ? ' (' . $langs->transnoentitiesnoconv("Deprecated") . ')' : $this->version);
790
        }
791
        return $ret;
792
    }
793
794
    /**
795
     * Tells if module is core or external
796
     *
797
     * @return string  'core', 'external' or 'unknown'
798
     */
799
    function isCoreOrExternalModule()
800
    {
801
        if ($this->version == 'dolibarr' || $this->version == 'dolibarr_deprecated') {
802
            return 'core';
803
        }
804
        if (!empty($this->version) && !in_array($this->version, array('experimental', 'development'))) {
805
            return 'external';
806
        }
807
        if (!empty($this->editor_name) || !empty($this->editor_url)) {
808
            return 'external';
809
        }
810
        if ($this->numero >= 100000) {
811
            return 'external';
812
        }
813
        return 'unknown';
814
    }
815
816
    /**
817
     * Gives module related language files list
818
     *
819
     * @return string[]    Language files list
820
     */
821
    function getLangFilesArray()
822
    {
823
        return $this->langfiles;
824
    }
825
826
    /**
827
     * Gives translated label of an export dataset
828
     *
829
     * @param int $r Dataset index
830
     *
831
     * @return string       Translated databaset label
832
     */
833
    function getExportDatasetLabel($r)
834
    {
835
        global $langs;
836
837
        $langstring = "ExportDataset_" . $this->export_code[$r];
838
        if ($langs->trans($langstring) == $langstring) {
839
            // Translation not found
840
            return $langs->trans($this->export_label[$r]);
841
        } else {
842
            // Translation found
843
            return $langs->trans($langstring);
844
        }
845
    }
846
847
    /**
848
     * Gives translated label of an import dataset
849
     *
850
     * @param int $r Dataset index
851
     *
852
     * @return string      Translated dataset label
853
     */
854
    function getImportDatasetLabel($r)
855
    {
856
        global $langs;
857
858
        $langstring = "ImportDataset_" . $this->import_code[$r];
859
        //print "x".$langstring;
860
        if ($langs->trans($langstring) == $langstring) {
861
            // Translation not found
862
            return $langs->transnoentitiesnoconv($this->import_label[$r]);
863
        } else {
864
            // Translation found
865
            return $langs->transnoentitiesnoconv($langstring);
866
        }
867
    }
868
869
    /**
870
     * Gives the last date of activation
871
     *
872
     * @return timestamp       Date of last activation
873
     */
874
    function getLastActivationDate()
875
    {
876
        global $conf;
877
878
        $sql = "SELECT tms FROM " . MAIN_DB_PREFIX . "const";
879
        $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
880
        $sql .= " AND entity IN (0, " . $conf->entity . ")";
881
882
        dol_syslog(get_class($this) . "::getLastActiveDate", LOG_DEBUG);
883
        $resql = $this->db->query($sql);
884
        if (!$resql) {
885
            $err++;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $err seems to be never defined.
Loading history...
886
        } else {
887
            $obj = $this->db->fetch_object($resql);
888
            if ($obj) {
889
                return $this->db->jdate($obj->tms);
890
            }
891
        }
892
893
        return '';
894
    }
895
896
    /**
897
     * Gives the last author of activation
898
     *
899
     * @return array       Array array('authorid'=>Id of last activation user, 'lastactivationdate'=>Date of last activation)
900
     */
901
    function getLastActivationInfo()
902
    {
903
        global $conf;
904
905
        $sql = "SELECT tms, note FROM " . MAIN_DB_PREFIX . "const";
906
        $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
907
        $sql .= " AND entity IN (0, " . $conf->entity . ")";
908
909
        dol_syslog(get_class($this) . "::getLastActiveDate", LOG_DEBUG);
910
        $resql = $this->db->query($sql);
911
        if (!$resql) {
912
            $err++;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $err seems to be never defined.
Loading history...
913
        } else {
914
            $obj = $this->db->fetch_object($resql);
915
            $tmp = array();
916
            if ($obj->note) {
917
                $tmp = json_decode($obj->note, true);
918
            }
919
            if ($obj) {
920
                return array('authorid' => $tmp['authorid'], 'ip' => $tmp['ip'], 'lastactivationdate' => $this->db->jdate($obj->tms));
921
            }
922
        }
923
924
        return array();
925
    }
926
927
    /**
928
     * Insert constants for module activation
929
     *
930
     * @return int Error count (0 if OK)
931
     */
932
    function _active()
933
    {
934
        global $conf, $user;
935
936
        $err = 0;
937
938
        // Common module
939
        $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
940
941
        $sql = "DELETE FROM " . MAIN_DB_PREFIX . "const";
942
        $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
943
        $sql .= " AND entity IN (0, " . $entity . ")";
944
945
        dol_syslog(get_class($this) . "::_active delete activation constant", LOG_DEBUG);
946
        $resql = $this->db->query($sql);
947
        if (!$resql) {
948
            $err++;
949
        }
950
951
        $note = json_encode(array('authorid' => (is_object($user) ? $user->id : 0), 'ip' => (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR'])));
952
953
        $sql = "INSERT INTO " . MAIN_DB_PREFIX . "const (name, value, visible, entity, note) VALUES";
954
        $sql .= " (" . $this->db->encrypt($this->const_name, 1);
955
        $sql .= ", " . $this->db->encrypt('1', 1);
956
        $sql .= ", 0, " . $entity;
957
        $sql .= ", '" . $this->db->escape($note) . "')";
958
959
        dol_syslog(get_class($this) . "::_active insert activation constant", LOG_DEBUG);
960
        $resql = $this->db->query($sql);
961
        if (!$resql) {
962
            $err++;
963
        }
964
965
        return $err;
966
    }
967
968
    /**
969
     * Module deactivation
970
     *
971
     * @return int Error count (0 if OK)
972
     */
973
    function _unactive()
974
    {
975
        global $conf;
976
977
        $err = 0;
978
979
        // Common module
980
        $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
981
982
        $sql = "DELETE FROM " . MAIN_DB_PREFIX . "const";
983
        $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
984
        $sql .= " AND entity IN (0, " . $entity . ")";
985
986
        dol_syslog(get_class($this) . "::_unactive", LOG_DEBUG);
987
        $this->db->query($sql);
988
989
        return $err;
990
    }
991
992
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
993
    /**
994
     * Create tables and keys required by module.
995
     * Files module.sql and module.key.sql with create table and create keys
996
     * commands must be stored in directory reldir='/module/sql/'
997
     * This function is called by this->init
998
     *
999
     * @param  string $reldir Relative directory where to scan files
1000
     * @return int             <=0 if KO, >0 if OK
1001
     */
1002
    function _load_tables($reldir)
1003
    {
1004
        // phpcs:enable
1005
        global $conf;
1006
1007
        $error = 0;
1008
        $dirfound = 0;
1009
1010
        if (empty($reldir)) {
1011
            return 1;
1012
        }
1013
1014
        include_once DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php';
1015
1016
        $ok = 1;
1017
        foreach ($conf->file->dol_document_root as $dirroot) {
1018
            if ($ok) {
1019
                $dir = $dirroot . $reldir;
1020
                $ok = 0;
1021
1022
                $handle = @opendir($dir);         // Dir may not exists
1023
                if (is_resource($handle)) {
1024
                    $dirfound++;
1025
1026
                    // Run llx_mytable.sql files, then llx_mytable_*.sql
1027
                    $files = array();
1028
                    while (($file = readdir($handle)) !== false) {
1029
                        $files[] = $file;
1030
                    }
1031
                    sort($files);
1032
                    foreach ($files as $file) {
1033
                        if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'llx_' && substr($file, 0, 4) != 'data') {
1034
                            $result = run_sql($dir . $file, empty($conf->global->MAIN_DISPLAY_SQL_INSTALL_LOG) ? 1 : 0, '', 1);
1035
                            if ($result <= 0) {
1036
                                $error++;
1037
                            }
1038
                        }
1039
                    }
1040
1041
                    rewinddir($handle);
1042
1043
                    // Run llx_mytable.key.sql files (Must be done after llx_mytable.sql) then then llx_mytable_*.key.sql
1044
                    $files = array();
1045
                    while (($file = readdir($handle)) !== false) {
1046
                        $files[] = $file;
1047
                    }
1048
                    sort($files);
1049
                    foreach ($files as $file) {
1050
                        if (preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'llx_' && substr($file, 0, 4) != 'data') {
1051
                            $result = run_sql($dir . $file, empty($conf->global->MAIN_DISPLAY_SQL_INSTALL_LOG) ? 1 : 0, '', 1);
1052
                            if ($result <= 0) {
1053
                                $error++;
1054
                            }
1055
                        }
1056
                    }
1057
1058
                    rewinddir($handle);
1059
1060
                    // Run data_xxx.sql files (Must be done after llx_mytable.key.sql)
1061
                    $files = array();
1062
                    while (($file = readdir($handle)) !== false) {
1063
                        $files[] = $file;
1064
                    }
1065
                    sort($files);
1066
                    foreach ($files as $file) {
1067
                        if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'data') {
1068
                            $result = run_sql($dir . $file, empty($conf->global->MAIN_DISPLAY_SQL_INSTALL_LOG) ? 1 : 0, '', 1);
1069
                            if ($result <= 0) {
1070
                                $error++;
1071
                            }
1072
                        }
1073
                    }
1074
1075
                    rewinddir($handle);
1076
1077
                    // Run update_xxx.sql files
1078
                    $files = array();
1079
                    while (($file = readdir($handle)) !== false) {
1080
                        $files[] = $file;
1081
                    }
1082
                    sort($files);
1083
                    foreach ($files as $file) {
1084
                        if (preg_match('/\.sql$/i', $file) && !preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 6) == 'update') {
1085
                            $result = run_sql($dir . $file, empty($conf->global->MAIN_DISPLAY_SQL_INSTALL_LOG) ? 1 : 0, '', 1);
1086
                            if ($result <= 0) {
1087
                                $error++;
1088
                            }
1089
                        }
1090
                    }
1091
1092
                    closedir($handle);
1093
                }
1094
1095
                if ($error == 0) {
1096
                    $ok = 1;
1097
                }
1098
            }
1099
        }
1100
1101
        if (!$dirfound) {
1102
            dol_syslog("A module ask to load sql files into " . $reldir . " but this directory was not found.", LOG_WARNING);
1103
        }
1104
        return $ok;
1105
    }
1106
1107
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1108
    /**
1109
     * Adds boxes
1110
     *
1111
     * @param string $option Options when disabling module ('newboxdefonly'=insert only boxes definition)
1112
     *
1113
     * @return int             Error count (0 if OK)
1114
     */
1115
    function insert_boxes($option = '')
1116
    {
1117
        // phpcs:enable
1118
        include_once DOL_DOCUMENT_ROOT . '/core/class/infobox.class.php';
1119
1120
        global $conf;
1121
1122
        $err = 0;
1123
1124
        if (is_array($this->boxes)) {
1125
            dol_syslog(get_class($this) . "::insert_boxes", LOG_DEBUG);
1126
1127
            $pos_name = InfoBox::getListOfPagesForBoxes();
1128
1129
            foreach ($this->boxes as $key => $value) {
1130
                $file = isset($this->boxes[$key]['file']) ? $this->boxes[$key]['file'] : '';
1131
                $note = isset($this->boxes[$key]['note']) ? $this->boxes[$key]['note'] : '';
1132
                $enabledbydefaulton = isset($this->boxes[$key]['enabledbydefaulton']) ? $this->boxes[$key]['enabledbydefaulton'] : 'Home';
1133
1134
                if (empty($file)) {
1135
                    $file = isset($this->boxes[$key][1]) ? $this->boxes[$key][1] : '';    // For backward compatibility
1136
                }
1137
                if (empty($note)) {
1138
                    $note = isset($this->boxes[$key][2]) ? $this->boxes[$key][2] : '';    // For backward compatibility
1139
                }
1140
1141
                // Search if boxes def already present
1142
                $sql = "SELECT count(*) as nb FROM " . MAIN_DB_PREFIX . "boxes_def";
1143
                $sql .= " WHERE file = '" . $this->db->escape($file) . "'";
1144
                $sql .= " AND entity = " . $conf->entity;
1145
                if ($note) {
1146
                    $sql .= " AND note ='" . $this->db->escape($note) . "'";
1147
                }
1148
1149
                $result = $this->db->query($sql);
1150
                if ($result) {
1151
                    $obj = $this->db->fetch_object($result);
1152
                    if ($obj->nb == 0) {
1153
                        $this->db->begin();
1154
1155
                        if (!$err) {
1156
                            $sql = "INSERT INTO " . MAIN_DB_PREFIX . "boxes_def (file, entity, note)";
1157
                            $sql .= " VALUES ('" . $this->db->escape($file) . "', ";
1158
                            $sql .= $conf->entity . ", ";
1159
                            $sql .= $note ? "'" . $this->db->escape($note) . "'" : "null";
1160
                            $sql .= ")";
1161
1162
                            dol_syslog(get_class($this) . "::insert_boxes", LOG_DEBUG);
1163
                            $resql = $this->db->query($sql);
1164
                            if (!$resql) {
1165
                                $err++;
1166
                            }
1167
                        }
1168
                        if (!$err && !preg_match('/newboxdefonly/', $option)) {
1169
                            $lastid = $this->db->last_insert_id(MAIN_DB_PREFIX . "boxes_def", "rowid");
1170
1171
                            foreach ($pos_name as $key2 => $val2) {
1172
                                //print 'key2='.$key2.'-val2='.$val2."<br>\n";
1173
                                if ($enabledbydefaulton && $val2 != $enabledbydefaulton) {
1174
                                    continue;        // Not enabled by default onto this page.
1175
                                }
1176
1177
                                $sql = "INSERT INTO " . MAIN_DB_PREFIX . "boxes (box_id,position,box_order,fk_user,entity)";
1178
                                $sql .= " VALUES (" . $lastid . ", " . $key2 . ", '0', 0, " . $conf->entity . ")";
1179
1180
                                dol_syslog(get_class($this) . "::insert_boxes onto page " . $key2 . "=" . $val2 . "", LOG_DEBUG);
1181
                                $resql = $this->db->query($sql);
1182
                                if (!$resql) {
1183
                                    $err++;
1184
                                }
1185
                            }
1186
                        }
1187
1188
                        if (!$err) {
1189
                            $this->db->commit();
1190
                        } else {
1191
                            $this->error = $this->db->lasterror();
1192
                            $this->db->rollback();
1193
                        }
1194
                    }
1195
                    // else box already registered into database
1196
                } else {
1197
                    $this->error = $this->db->lasterror();
1198
                    $err++;
1199
                }
1200
            }
1201
        }
1202
1203
        return $err;
1204
    }
1205
1206
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1207
    /**
1208
     * Removes boxes
1209
     *
1210
     * @return int Error count (0 if OK)
1211
     */
1212
    function delete_boxes()
1213
    {
1214
        // phpcs:enable
1215
        global $conf;
1216
1217
        $err = 0;
1218
1219
        if (is_array($this->boxes)) {
1220
            foreach ($this->boxes as $key => $value) {
1221
                //$titre = $this->boxes[$key][0];
1222
                $file = $this->boxes[$key]['file'];
1223
                //$note  = $this->boxes[$key][2];
1224
                // TODO If the box is also included by another module and the other module is still on, we should not remove it.
1225
                // For the moment, we manage this with hard coded exception
1226
                //print "Remove box ".$file.'<br>';
1227
                if ($file == 'box_graph_product_distribution.php') {
1228
                    if (!empty($conf->produit->enabled) || !empty($conf->service->enabled)) {
1229
                        dol_syslog("We discard disabling of module " . $file . " because another module still active require it.");
1230
                        continue;
1231
                    }
1232
                }
1233
1234
                if (empty($file)) {
1235
                    $file = isset($this->boxes[$key][1]) ? $this->boxes[$key][1] : '';    // For backward compatibility
1236
                }
1237
1238
                if ($this->db->type == 'sqlite3') {
1239
                    // sqlite doesn't support "USING" syntax.
1240
                    // TODO: remove this dependency.
1241
                    $sql = "DELETE FROM " . MAIN_DB_PREFIX . "boxes ";
1242
                    $sql .= "WHERE " . MAIN_DB_PREFIX . "boxes.box_id IN (";
1243
                    $sql .= "SELECT " . MAIN_DB_PREFIX . "boxes_def.rowid ";
1244
                    $sql .= "FROM " . MAIN_DB_PREFIX . "boxes_def ";
1245
                    $sql .= "WHERE " . MAIN_DB_PREFIX . "boxes_def.file = '" . $this->db->escape($file) . "') ";
1246
                    $sql .= "AND " . MAIN_DB_PREFIX . "boxes.entity = " . $conf->entity;
1247
                } else {
1248
                    $sql = "DELETE FROM " . MAIN_DB_PREFIX . "boxes";
1249
                    $sql .= " USING " . MAIN_DB_PREFIX . "boxes, " . MAIN_DB_PREFIX . "boxes_def";
1250
                    $sql .= " WHERE " . MAIN_DB_PREFIX . "boxes.box_id = " . MAIN_DB_PREFIX . "boxes_def.rowid";
1251
                    $sql .= " AND " . MAIN_DB_PREFIX . "boxes_def.file = '" . $this->db->escape($file) . "'";
1252
                    $sql .= " AND " . MAIN_DB_PREFIX . "boxes.entity = " . $conf->entity;
1253
                }
1254
1255
                dol_syslog(get_class($this) . "::delete_boxes", LOG_DEBUG);
1256
                $resql = $this->db->query($sql);
1257
                if (!$resql) {
1258
                    $this->error = $this->db->lasterror();
1259
                    $err++;
1260
                }
1261
1262
                $sql = "DELETE FROM " . MAIN_DB_PREFIX . "boxes_def";
1263
                $sql .= " WHERE file = '" . $this->db->escape($file) . "'";
1264
                $sql .= " AND entity = " . $conf->entity;
1265
1266
                dol_syslog(get_class($this) . "::delete_boxes", LOG_DEBUG);
1267
                $resql = $this->db->query($sql);
1268
                if (!$resql) {
1269
                    $this->error = $this->db->lasterror();
1270
                    $err++;
1271
                }
1272
            }
1273
        }
1274
1275
        return $err;
1276
    }
1277
1278
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1279
    /**
1280
     * Adds cronjobs
1281
     *
1282
     * @return int             Error count (0 if OK)
1283
     */
1284
    function insert_cronjobs()
1285
    {
1286
        // phpcs:enable
1287
        include_once DOL_DOCUMENT_ROOT . '/core/class/infobox.class.php';
1288
1289
        global $conf;
1290
1291
        $err = 0;
1292
1293
        if (is_array($this->cronjobs)) {
1294
            dol_syslog(get_class($this) . "::insert_cronjobs", LOG_DEBUG);
1295
1296
            foreach ($this->cronjobs as $key => $value) {
1297
                $entity = isset($this->cronjobs[$key]['entity']) ? $this->cronjobs[$key]['entity'] : $conf->entity;
1298
                $label = isset($this->cronjobs[$key]['label']) ? $this->cronjobs[$key]['label'] : '';
1299
                $jobtype = isset($this->cronjobs[$key]['jobtype']) ? $this->cronjobs[$key]['jobtype'] : '';
1300
                $class = isset($this->cronjobs[$key]['class']) ? $this->cronjobs[$key]['class'] : '';
1301
                $objectname = isset($this->cronjobs[$key]['objectname']) ? $this->cronjobs[$key]['objectname'] : '';
1302
                $method = isset($this->cronjobs[$key]['method']) ? $this->cronjobs[$key]['method'] : '';
1303
                $command = isset($this->cronjobs[$key]['command']) ? $this->cronjobs[$key]['command'] : '';
1304
                $parameters = isset($this->cronjobs[$key]['parameters']) ? $this->cronjobs[$key]['parameters'] : '';
1305
                $comment = isset($this->cronjobs[$key]['comment']) ? $this->cronjobs[$key]['comment'] : '';
1306
                $frequency = isset($this->cronjobs[$key]['frequency']) ? $this->cronjobs[$key]['frequency'] : '';
1307
                $unitfrequency = isset($this->cronjobs[$key]['unitfrequency']) ? $this->cronjobs[$key]['unitfrequency'] : '';
1308
                $priority = isset($this->cronjobs[$key]['priority']) ? $this->cronjobs[$key]['priority'] : '';
1309
                $datestart = isset($this->cronjobs[$key]['datestart']) ? $this->cronjobs[$key]['datestart'] : '';
1310
                $dateend = isset($this->cronjobs[$key]['dateend']) ? $this->cronjobs[$key]['dateend'] : '';
1311
                $status = isset($this->cronjobs[$key]['status']) ? $this->cronjobs[$key]['status'] : '';
1312
                $test = isset($this->cronjobs[$key]['test']) ? $this->cronjobs[$key]['test'] : '';                    // Line must be enabled or not (so visible or not)
1313
                // Search if cron entry already present
1314
                $sql = "SELECT count(*) as nb FROM " . MAIN_DB_PREFIX . "cronjob";
1315
                $sql .= " WHERE module_name = '" . $this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class) . "'";
1316
                if ($class) {
1317
                    $sql .= " AND classesname = '" . $this->db->escape($class) . "'";
1318
                }
1319
                if ($objectname) {
1320
                    $sql .= " AND objectname = '" . $this->db->escape($objectname) . "'";
1321
                }
1322
                if ($method) {
1323
                    $sql .= " AND methodename = '" . $this->db->escape($method) . "'";
1324
                }
1325
                if ($command) {
1326
                    $sql .= " AND command = '" . $this->db->escape($command) . "'";
1327
                }
1328
                $sql .= " AND entity = " . $entity;    // Must be exact entity
1329
1330
                $now = dol_now();
1331
1332
                $result = $this->db->query($sql);
1333
                if ($result) {
1334
                    $obj = $this->db->fetch_object($result);
1335
                    if ($obj->nb == 0) {
1336
                        $this->db->begin();
1337
1338
                        if (!$err) {
1339
                            $sql = "INSERT INTO " . MAIN_DB_PREFIX . "cronjob (module_name, datec, datestart, dateend, label, jobtype, classesname, objectname, methodename, command, params, note,";
1340
                            if (is_int($frequency)) {
1341
                                $sql .= ' frequency,';
1342
                            }
1343
                            if (is_int($unitfrequency)) {
1344
                                $sql .= ' unitfrequency,';
1345
                            }
1346
                            if (is_int($priority)) {
1347
                                $sql .= ' priority,';
1348
                            }
1349
                            if (is_int($status)) {
1350
                                $sql .= ' status,';
1351
                            }
1352
                            $sql .= " entity, test)";
1353
                            $sql .= " VALUES (";
1354
                            $sql .= "'" . $this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class) . "', ";
1355
                            $sql .= "'" . $this->db->idate($now) . "', ";
1356
                            $sql .= ($datestart ? "'" . $this->db->idate($datestart) . "'" : "'" . $this->db->idate($now) . "'") . ", ";
1357
                            $sql .= ($dateend ? "'" . $this->db->idate($dateend) . "'" : "NULL") . ", ";
1358
                            $sql .= "'" . $this->db->escape($label) . "', ";
1359
                            $sql .= "'" . $this->db->escape($jobtype) . "', ";
1360
                            $sql .= ($class ? "'" . $this->db->escape($class) . "'" : "null") . ",";
1361
                            $sql .= ($objectname ? "'" . $this->db->escape($objectname) . "'" : "null") . ",";
1362
                            $sql .= ($method ? "'" . $this->db->escape($method) . "'" : "null") . ",";
1363
                            $sql .= ($command ? "'" . $this->db->escape($command) . "'" : "null") . ",";
1364
                            $sql .= ($parameters ? "'" . $this->db->escape($parameters) . "'" : "null") . ",";
1365
                            $sql .= ($comment ? "'" . $this->db->escape($comment) . "'" : "null") . ",";
1366
                            if (is_int($frequency)) {
1367
                                $sql .= "'" . $this->db->escape($frequency) . "', ";
1368
                            }
1369
                            if (is_int($unitfrequency)) {
1370
                                $sql .= "'" . $this->db->escape($unitfrequency) . "', ";
1371
                            }
1372
                            if (is_int($priority)) {
1373
                                $sql .= "'" . $this->db->escape($priority) . "', ";
1374
                            }
1375
                            if (is_int($status)) {
1376
                                $sql .= "'" . $this->db->escape($status) . "', ";
1377
                            }
1378
                            $sql .= $entity . ",";
1379
                            $sql .= "'" . $this->db->escape($test) . "'";
1380
                            $sql .= ")";
1381
1382
                            $resql = $this->db->query($sql);
1383
                            if (!$resql) {
1384
                                $err++;
1385
                            }
1386
                        }
1387
1388
                        if (!$err) {
1389
                            $this->db->commit();
1390
                        } else {
1391
                            $this->error = $this->db->lasterror();
1392
                            $this->db->rollback();
1393
                        }
1394
                    }
1395
                    // else box already registered into database
1396
                } else {
1397
                    $this->error = $this->db->lasterror();
1398
                    $err++;
1399
                }
1400
            }
1401
        }
1402
1403
        return $err;
1404
    }
1405
1406
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1407
    /**
1408
     * Removes boxes
1409
     *
1410
     * @return int Error count (0 if OK)
1411
     */
1412
    function delete_cronjobs()
1413
    {
1414
        // phpcs:enable
1415
        global $conf;
1416
1417
        $err = 0;
1418
1419
        if (is_array($this->cronjobs)) {
1420
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . "cronjob";
1421
            $sql .= " WHERE module_name = '" . $this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class) . "'";
1422
            $sql .= " AND entity = " . $conf->entity;
1423
            $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.
1424
            // 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.
1425
1426
            dol_syslog(get_class($this) . "::delete_cronjobs", LOG_DEBUG);
1427
            $resql = $this->db->query($sql);
1428
            if (!$resql) {
1429
                $this->error = $this->db->lasterror();
1430
                $err++;
1431
            }
1432
        }
1433
1434
        return $err;
1435
    }
1436
1437
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1438
    /**
1439
     * Removes tabs
1440
     *
1441
     * @return int Error count (0 if OK)
1442
     */
1443
    function delete_tabs()
1444
    {
1445
        // phpcs:enable
1446
        global $conf;
1447
1448
        $err = 0;
1449
1450
        $sql = "DELETE FROM " . MAIN_DB_PREFIX . "const";
1451
        $sql .= " WHERE " . $this->db->decrypt('name') . " like '" . $this->db->escape($this->const_name) . "_TABS_%'";
1452
        $sql .= " AND entity = " . $conf->entity;
1453
1454
        dol_syslog(get_class($this) . "::delete_tabs", LOG_DEBUG);
1455
        if (!$this->db->query($sql)) {
1456
            $this->error = $this->db->lasterror();
1457
            $err++;
1458
        }
1459
1460
        return $err;
1461
    }
1462
1463
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1464
    /**
1465
     * Adds tabs
1466
     *
1467
     * @return int  Error count (0 if ok)
1468
     */
1469
    function insert_tabs()
1470
    {
1471
        // phpcs:enable
1472
        global $conf;
1473
1474
        $err = 0;
1475
1476
        if (!empty($this->tabs)) {
1477
            dol_syslog(get_class($this) . "::insert_tabs", LOG_DEBUG);
1478
1479
            $i = 0;
1480
            foreach ($this->tabs as $key => $value) {
1481
                if (is_array($value) && count($value) == 0) {
1482
                    continue;    // Discard empty arrays
1483
                }
1484
1485
                $entity = $conf->entity;
1486
                $newvalue = $value;
1487
1488
                if (is_array($value)) {
1489
                    $newvalue = $value['data'];
1490
                    if (isset($value['entity'])) {
1491
                        $entity = $value['entity'];
1492
                    }
1493
                }
1494
1495
                if ($newvalue) {
1496
                    $sql = "INSERT INTO " . MAIN_DB_PREFIX . "const (";
1497
                    $sql .= "name";
1498
                    $sql .= ", type";
1499
                    $sql .= ", value";
1500
                    $sql .= ", note";
1501
                    $sql .= ", visible";
1502
                    $sql .= ", entity";
1503
                    $sql .= ")";
1504
                    $sql .= " VALUES (";
1505
                    $sql .= $this->db->encrypt($this->const_name . "_TABS_" . $i, 1);
1506
                    $sql .= ", 'chaine'";
1507
                    $sql .= ", " . $this->db->encrypt($newvalue, 1);
1508
                    $sql .= ", null";
1509
                    $sql .= ", '0'";
1510
                    $sql .= ", " . $entity;
1511
                    $sql .= ")";
1512
1513
                    $resql = $this->db->query($sql);
1514
                    if (!$resql) {
1515
                        dol_syslog($this->db->lasterror(), LOG_ERR);
1516
                        if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1517
                            $this->error = $this->db->lasterror();
1518
                            $this->errors[] = $this->db->lasterror();
1519
                            $err++;
1520
                            break;
1521
                        }
1522
                    }
1523
                }
1524
                $i++;
1525
            }
1526
        }
1527
        return $err;
1528
    }
1529
1530
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1531
    /**
1532
     * Adds constants
1533
     *
1534
     * @return int Error count (0 if OK)
1535
     */
1536
    function insert_const()
1537
    {
1538
        // phpcs:enable
1539
        global $conf;
1540
1541
        $err = 0;
1542
1543
        if (empty($this->const)) {
1544
            return 0;
1545
        }
1546
1547
        dol_syslog(get_class($this) . "::insert_const", LOG_DEBUG);
1548
1549
        foreach ($this->const as $key => $value) {
1550
            $name = $this->const[$key][0];
1551
            $type = $this->const[$key][1];
1552
            $val = $this->const[$key][2];
1553
            $note = isset($this->const[$key][3]) ? $this->const[$key][3] : '';
1554
            $visible = isset($this->const[$key][4]) ? $this->const[$key][4] : 0;
1555
            $entity = (!empty($this->const[$key][5]) && $this->const[$key][5] != 'current') ? 0 : $conf->entity;
1556
1557
            // Clean
1558
            if (empty($visible)) {
1559
                $visible = '0';
1560
            }
1561
            if (empty($val) && $val != '0') {
1562
                $val = '';
1563
            }
1564
1565
            $sql = "SELECT count(*)";
1566
            $sql .= " FROM " . MAIN_DB_PREFIX . "const";
1567
            $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($name) . "'";
1568
            $sql .= " AND entity = " . $entity;
1569
1570
            $result = $this->db->query($sql);
1571
            if ($result) {
1572
                $row = $this->db->fetch_row($result);
1573
1574
                if ($row[0] == 0) {   // If not found
1575
                    $sql = "INSERT INTO " . MAIN_DB_PREFIX . "const (name,type,value,note,visible,entity)";
1576
                    $sql .= " VALUES (";
1577
                    $sql .= $this->db->encrypt($name, 1);
1578
                    $sql .= ",'" . $type . "'";
1579
                    $sql .= "," . (($val != '') ? $this->db->encrypt($val, 1) : "''");
1580
                    $sql .= "," . ($note ? "'" . $this->db->escape($note) . "'" : "null");
1581
                    $sql .= ",'" . $visible . "'";
1582
                    $sql .= "," . $entity;
1583
                    $sql .= ")";
1584
1585
                    if (!$this->db->query($sql)) {
1586
                        $err++;
1587
                    }
1588
                } else {
1589
                    dol_syslog(get_class($this) . "::insert_const constant '" . $name . "' already exists", LOG_WARNING);
1590
                }
1591
            } else {
1592
                $err++;
1593
            }
1594
        }
1595
1596
        return $err;
1597
    }
1598
1599
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1600
    /**
1601
     * Removes constants tagged 'deleteonunactive'
1602
     *
1603
     * @return int <0 if KO, 0 if OK
1604
     */
1605
    function delete_const()
1606
    {
1607
        // phpcs:enable
1608
        global $conf;
1609
1610
        $err = 0;
1611
1612
        if (empty($this->const)) {
1613
            return 0;
1614
        }
1615
1616
        foreach ($this->const as $key => $value) {
1617
            $name = $this->const[$key][0];
1618
            $deleteonunactive = (!empty($this->const[$key][6])) ? 1 : 0;
1619
1620
            if ($deleteonunactive) {
1621
                $sql = "DELETE FROM " . MAIN_DB_PREFIX . "const";
1622
                $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $name . "'";
1623
                $sql .= " AND entity in (0, " . $conf->entity . ")";
1624
                dol_syslog(get_class($this) . "::delete_const", LOG_DEBUG);
1625
                if (!$this->db->query($sql)) {
1626
                    $this->error = $this->db->lasterror();
1627
                    $err++;
1628
                }
1629
            }
1630
        }
1631
1632
        return $err;
1633
    }
1634
1635
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1636
    /**
1637
     * Adds access rights
1638
     *
1639
     * @param  int $reinitadminperms If 1, we also grant them to all admin users
1640
     * @param  int $force_entity     Force current entity
1641
     * @param  int $notrigger        1=Does not execute triggers, 0= execute triggers
1642
     * @return int                     Error count (0 if OK)
1643
     */
1644
    function insert_permissions($reinitadminperms = 0, $force_entity = null, $notrigger = 0)
1645
    {
1646
        // phpcs:enable
1647
        global $conf, $user;
1648
1649
        $err = 0;
1650
        $entity = (!empty($force_entity) ? $force_entity : $conf->entity);
1651
1652
        dol_syslog(get_class($this) . "::insert_permissions", LOG_DEBUG);
1653
1654
        // Test if module is activated
1655
        $sql_del = "SELECT " . $this->db->decrypt('value') . " as value";
1656
        $sql_del .= " FROM " . MAIN_DB_PREFIX . "const";
1657
        $sql_del .= " WHERE " . $this->db->decrypt('name') . " = '" . $this->db->escape($this->const_name) . "'";
1658
        $sql_del .= " AND entity IN (0," . $entity . ")";
1659
1660
        $resql = $this->db->query($sql_del);
1661
1662
        if ($resql) {
1663
            $obj = $this->db->fetch_object($resql);
1664
            if ($obj !== null && !empty($obj->value) && !empty($this->rights)) {
1665
                // If the module is active
1666
                foreach ($this->rights as $key => $value) {
1667
                    $r_id = $this->rights[$key][0];
1668
                    $r_desc = $this->rights[$key][1];
1669
                    $r_type = isset($this->rights[$key][2]) ? $this->rights[$key][2] : '';
1670
                    $r_def = $this->rights[$key][3];
1671
                    $r_perms = $this->rights[$key][4];
1672
                    $r_subperms = isset($this->rights[$key][5]) ? $this->rights[$key][5] : '';
1673
                    $r_modul = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
1674
1675
                    if (empty($r_type)) {
1676
                        $r_type = 'w';
1677
                    }
1678
1679
                    // Search if perm already present
1680
                    $sql = "SELECT count(*) as nb FROM " . MAIN_DB_PREFIX . "rights_def";
1681
                    $sql .= " WHERE id = " . $r_id . " AND entity = " . $entity;
1682
1683
                    $resqlselect = $this->db->query($sql);
1684
                    if ($resqlselect) {
1685
                        $objcount = $this->db->fetch_object($resqlselect);
1686
                        if ($objcount && $objcount->nb == 0) {
1687
                            if (dol_strlen($r_perms)) {
1688
                                if (dol_strlen($r_subperms)) {
1689
                                    $sql = "INSERT INTO " . MAIN_DB_PREFIX . "rights_def";
1690
                                    $sql .= " (id, entity, libelle, module, type, bydefault, perms, subperms)";
1691
                                    $sql .= " VALUES ";
1692
                                    $sql .= "(" . $r_id . "," . $entity . ",'" . $this->db->escape($r_desc) . "','" . $r_modul . "','" . $r_type . "'," . $r_def . ",'" . $r_perms . "','" . $r_subperms . "')";
1693
                                } else {
1694
                                    $sql = "INSERT INTO " . MAIN_DB_PREFIX . "rights_def";
1695
                                    $sql .= " (id, entity, libelle, module, type, bydefault, perms)";
1696
                                    $sql .= " VALUES ";
1697
                                    $sql .= "(" . $r_id . "," . $entity . ",'" . $this->db->escape($r_desc) . "','" . $r_modul . "','" . $r_type . "'," . $r_def . ",'" . $r_perms . "')";
1698
                                }
1699
                            } else {
1700
                                $sql = "INSERT INTO " . MAIN_DB_PREFIX . "rights_def ";
1701
                                $sql .= " (id, entity, libelle, module, type, bydefault)";
1702
                                $sql .= " VALUES ";
1703
                                $sql .= "(" . $r_id . "," . $entity . ",'" . $this->db->escape($r_desc) . "','" . $r_modul . "','" . $r_type . "'," . $r_def . ")";
1704
                            }
1705
1706
                            $resqlinsert = $this->db->query($sql, 1);
1707
1708
                            if (!$resqlinsert) {
1709
                                if ($this->db->errno() != "DB_ERROR_RECORD_ALREADY_EXISTS") {
1710
                                    $this->error = $this->db->lasterror();
1711
                                    $err++;
1712
                                    break;
1713
                                } else {
1714
                                    dol_syslog(get_class($this) . "::insert_permissions record already exists", LOG_INFO);
1715
                                }
1716
                            }
1717
1718
                            $this->db->free($resqlinsert);
1719
                        }
1720
1721
                        $this->db->free($resqlselect);
1722
                    }
1723
1724
                    // If we want to init permissions on admin users
1725
                    if ($reinitadminperms) {
1726
                        if (!class_exists('User')) {
1727
                            include_once DOL_DOCUMENT_ROOT . '/user/class/user.class.php';
1728
                        }
1729
                        $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "user WHERE admin = 1";
1730
                        dol_syslog(get_class($this) . "::insert_permissions Search all admin users", LOG_DEBUG);
1731
                        $resqlseladmin = $this->db->query($sql, 1);
1732
                        if ($resqlseladmin) {
1733
                            $num = $this->db->num_rows($resqlseladmin);
1734
                            $i = 0;
1735
                            while ($i < $num) {
1736
                                $obj2 = $this->db->fetch_object($resqlseladmin);
1737
                                dol_syslog(get_class($this) . "::insert_permissions Add permission to user id=" . $obj2->rowid);
1738
1739
                                $tmpuser = new User($this->db);
1740
                                $result = $tmpuser->fetch($obj2->rowid);
1741
                                if ($result > 0) {
1742
                                    $tmpuser->addrights($r_id, '', '', 0, 1);
1743
                                } else {
1744
                                    dol_syslog(get_class($this) . "::insert_permissions Failed to add the permission to user because fetch return an error", LOG_ERR);
1745
                                }
1746
                                $i++;
1747
                            }
1748
                        } else {
1749
                            dol_print_error($this->db);
1750
                        }
1751
                    }
1752
                }
1753
1754
                if ($reinitadminperms && !empty($user->admin)) {  // Reload permission for current user if defined
1755
                    // We reload permissions
1756
                    $user->clearrights();
1757
                    $user->getrights();
1758
                }
1759
            }
1760
            $this->db->free($resql);
1761
        } else {
1762
            $this->error = $this->db->lasterror();
1763
            $err++;
1764
        }
1765
1766
        return $err;
1767
    }
1768
1769
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1770
    /**
1771
     * Removes access rights
1772
     *
1773
     * @return int                     Error count (0 if OK)
1774
     */
1775
    function delete_permissions()
1776
    {
1777
        // phpcs:enable
1778
        global $conf;
1779
1780
        $err = 0;
1781
1782
        $sql = "DELETE FROM " . MAIN_DB_PREFIX . "rights_def";
1783
        $sql .= " WHERE module = '" . $this->db->escape(empty($this->rights_class) ? strtolower($this->name) : $this->rights_class) . "'";
1784
        $sql .= " AND entity = " . $conf->entity;
1785
        dol_syslog(get_class($this) . "::delete_permissions", LOG_DEBUG);
1786
        if (!$this->db->query($sql)) {
1787
            $this->error = $this->db->lasterror();
1788
            $err++;
1789
        }
1790
1791
        return $err;
1792
    }
1793
1794
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1795
    /**
1796
     * Adds menu entries
1797
     *
1798
     * @return int     Error count (0 if OK)
1799
     */
1800
    function insert_menus()
1801
    {
1802
        // phpcs:enable
1803
        global $user;
1804
1805
        if (!is_array($this->menu) || empty($this->menu)) {
1806
            return 0;
1807
        }
1808
1809
        include_once DOL_DOCUMENT_ROOT . '/core/class/menubase.class.php';
1810
1811
        dol_syslog(get_class($this) . "::insert_menus", LOG_DEBUG);
1812
1813
        $err = 0;
1814
1815
        $this->db->begin();
1816
1817
        foreach ($this->menu as $key => $value) {
1818
            $menu = new Menubase($this->db);
1819
            $menu->menu_handler = 'all';
1820
1821
            //$menu->module=strtolower($this->name);    TODO When right_class will be same than module name
1822
            $menu->module = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
1823
1824
            if (!$this->menu[$key]['fk_menu']) {
1825
                $menu->fk_menu = 0;
1826
            } else {
1827
                $foundparent = 0;
1828
                $fk_parent = $this->menu[$key]['fk_menu'];
1829
                if (preg_match('/^r=/', $fk_parent)) {    // old deprecated method
1830
                    $fk_parent = str_replace('r=', '', $fk_parent);
1831
                    if (isset($this->menu[$fk_parent]['rowid'])) {
1832
                        $menu->fk_menu = $this->menu[$fk_parent]['rowid'];
1833
                        $foundparent = 1;
1834
                    }
1835
                } elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+),fk_leftmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
1836
                    $menu->fk_menu = -1;
1837
                    $menu->fk_mainmenu = $reg[1];
1838
                    $menu->fk_leftmenu = $reg[2];
1839
                    $foundparent = 1;
1840
                } elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
1841
                    $menu->fk_menu = -1;
1842
                    $menu->fk_mainmenu = $reg[1];
1843
                    $menu->fk_leftmenu = '';
1844
                    $foundparent = 1;
1845
                }
1846
                if (!$foundparent) {
1847
                    $this->error = "ErrorBadDefinitionOfMenuArrayInModuleDescriptor";
1848
                    dol_syslog(get_class($this) . "::insert_menus " . $this->error . " " . $this->menu[$key]['fk_menu'], LOG_ERR);
1849
                    $err++;
1850
                }
1851
            }
1852
            $menu->type = $this->menu[$key]['type'];
1853
            $menu->mainmenu = isset($this->menu[$key]['mainmenu']) ? $this->menu[$key]['mainmenu'] : (isset($menu->fk_mainmenu) ? $menu->fk_mainmenu : '');
1854
            $menu->leftmenu = isset($this->menu[$key]['leftmenu']) ? $this->menu[$key]['leftmenu'] : '';
1855
            $menu->titre = $this->menu[$key]['titre'];
1856
            $menu->url = $this->menu[$key]['url'];
1857
            $menu->langs = $this->menu[$key]['langs'];
1858
            $menu->position = $this->menu[$key]['position'];
1859
            $menu->perms = $this->menu[$key]['perms'];
1860
            $menu->target = isset($this->menu[$key]['target']) ? $this->menu[$key]['target'] : '';
1861
            $menu->user = $this->menu[$key]['user'];
1862
            $menu->enabled = isset($this->menu[$key]['enabled']) ? $this->menu[$key]['enabled'] : 0;
1863
            $menu->position = $this->menu[$key]['position'];
1864
1865
            if (!$err) {
1866
                $result = $menu->create($user);    // Save menu entry into table llx_menu
1867
                if ($result > 0) {
1868
                    $this->menu[$key]['rowid'] = $result;
1869
                } else {
1870
                    $this->error = $menu->error;
1871
                    dol_syslog(get_class($this) . '::insert_menus result=' . $result . " " . $this->error, LOG_ERR);
1872
                    $err++;
1873
                    break;
1874
                }
1875
            }
1876
        }
1877
1878
        if (!$err) {
1879
            $this->db->commit();
1880
        } else {
1881
            dol_syslog(get_class($this) . "::insert_menus " . $this->error, LOG_ERR);
1882
            $this->db->rollback();
1883
        }
1884
1885
        return $err;
1886
    }
1887
1888
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1889
    /**
1890
     * Removes menu entries
1891
     *
1892
     * @return int Error count (0 if OK)
1893
     */
1894
    function delete_menus()
1895
    {
1896
        // phpcs:enable
1897
        global $conf;
1898
1899
        $err = 0;
1900
1901
        //$module=strtolower($this->name);        TODO When right_class will be same than module name
1902
        $module = empty($this->rights_class) ? strtolower($this->name) : $this->rights_class;
1903
1904
        $sql = "DELETE FROM " . MAIN_DB_PREFIX . "menu";
1905
        $sql .= " WHERE module = '" . $this->db->escape($module) . "'";
1906
        $sql .= " AND entity = " . $conf->entity;
1907
1908
        dol_syslog(get_class($this) . "::delete_menus", LOG_DEBUG);
1909
        $resql = $this->db->query($sql);
1910
        if (!$resql) {
1911
            $this->error = $this->db->lasterror();
1912
            $err++;
1913
        }
1914
1915
        return $err;
1916
    }
1917
1918
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1919
    /**
1920
     * Creates directories
1921
     *
1922
     * @return int Error count (0 if OK)
1923
     */
1924
    function create_dirs()
1925
    {
1926
        // phpcs:enable
1927
        global $langs, $conf;
1928
1929
        $err = 0;
1930
1931
        if (isset($this->dirs) && is_array($this->dirs)) {
1932
            foreach ($this->dirs as $key => $value) {
1933
                $addtodatabase = 0;
1934
1935
                if (!is_array($value)) {
1936
                    $dir = $value;    // Default simple mode
1937
                } else {
1938
                    $constname = $this->const_name . "_DIR_";
1939
                    $dir = $this->dirs[$key][1];
1940
                    $addtodatabase = empty($this->dirs[$key][2]) ? '' : $this->dirs[$key][2]; // Create constante in llx_const
1941
                    $subname = empty($this->dirs[$key][3]) ? '' : strtoupper($this->dirs[$key][3]); // Add submodule name (ex: $conf->module->submodule->dir_output)
1942
                    $forcename = empty($this->dirs[$key][4]) ? '' : strtoupper($this->dirs[$key][4]); // Change the module name if different
1943
1944
                    if (!empty($forcename)) {
1945
                        $constname = 'MAIN_MODULE_' . $forcename . "_DIR_";
1946
                    }
1947
                    if (!empty($subname)) {
1948
                        $constname = $constname . $subname . "_";
1949
                    }
1950
1951
                    $name = $constname . strtoupper($this->dirs[$key][0]);
1952
                }
1953
1954
                // Define directory full path ($dir must start with "/")
1955
                if (empty($conf->global->MAIN_MODULE_MULTICOMPANY) || $conf->entity == 1) {
1956
                    $fulldir = DOL_DATA_ROOT . $dir;
1957
                } else {
1958
                    $fulldir = DOL_DATA_ROOT . "/" . $conf->entity . $dir;
1959
                }
1960
                // Create dir if it does not exists
1961
                if (!empty($fulldir) && !file_exists($fulldir)) {
1962
                    if (dol_mkdir($fulldir, DOL_DATA_ROOT) < 0) {
1963
                        $this->error = $langs->trans("ErrorCanNotCreateDir", $fulldir);
1964
                        dol_syslog(get_class($this) . "::_init " . $this->error, LOG_ERR);
1965
                        $err++;
1966
                    }
1967
                }
1968
1969
                // Define the constant in database if requested (not the default mode)
1970
                if (!empty($addtodatabase)) {
1971
                    $result = $this->insert_dirs($name, $dir);
1972
                    if ($result) {
1973
                        $err++;
1974
                    }
1975
                }
1976
            }
1977
        }
1978
1979
        return $err;
1980
    }
1981
1982
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1983
    /**
1984
     * Adds directories definitions
1985
     *
1986
     * @param string $name Name
1987
     * @param string $dir  Directory
1988
     *
1989
     * @return int             Error count (0 if OK)
1990
     */
1991
    function insert_dirs($name, $dir)
1992
    {
1993
        // phpcs:enable
1994
        global $conf;
1995
1996
        $err = 0;
1997
1998
        $sql = "SELECT count(*)";
1999
        $sql .= " FROM " . MAIN_DB_PREFIX . "const";
2000
        $sql .= " WHERE " . $this->db->decrypt('name') . " = '" . $name . "'";
2001
        $sql .= " AND entity = " . $conf->entity;
2002
2003
        dol_syslog(get_class($this) . "::insert_dirs", LOG_DEBUG);
2004
        $result = $this->db->query($sql);
2005
        if ($result) {
2006
            $row = $this->db->fetch_row($result);
2007
2008
            if ($row[0] == 0) {
2009
                $sql = "INSERT INTO " . MAIN_DB_PREFIX . "const (name,type,value,note,visible,entity)";
2010
                $sql .= " VALUES (" . $this->db->encrypt($name, 1) . ",'chaine'," . $this->db->encrypt($dir, 1) . ",'Directory for module " . $this->name . "','0'," . $conf->entity . ")";
2011
2012
                dol_syslog(get_class($this) . "::insert_dirs", LOG_DEBUG);
2013
                $this->db->query($sql);
2014
            }
2015
        } else {
2016
            $this->error = $this->db->lasterror();
2017
            $err++;
2018
        }
2019
2020
        return $err;
2021
    }
2022
2023
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2024
    /**
2025
     * Removes directories
2026
     *
2027
     * @return int Error count (0 if OK)
2028
     */
2029
    function delete_dirs()
2030
    {
2031
        // phpcs:enable
2032
        global $conf;
2033
2034
        $err = 0;
2035
2036
        $sql = "DELETE FROM " . MAIN_DB_PREFIX . "const";
2037
        $sql .= " WHERE " . $this->db->decrypt('name') . " LIKE '" . $this->db->escape($this->const_name) . "_DIR_%'";
2038
        $sql .= " AND entity = " . $conf->entity;
2039
2040
        dol_syslog(get_class($this) . "::delete_dirs", LOG_DEBUG);
2041
        if (!$this->db->query($sql)) {
2042
            $this->error = $this->db->lasterror();
2043
            $err++;
2044
        }
2045
2046
        return $err;
2047
    }
2048
2049
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2050
    /**
2051
     * Adds generic parts
2052
     *
2053
     * @return int Error count (0 if OK)
2054
     */
2055
    function insert_module_parts()
2056
    {
2057
        // phpcs:enable
2058
        global $conf;
2059
2060
        $error = 0;
2061
2062
        if (is_array($this->module_parts) && !empty($this->module_parts)) {
2063
            foreach ($this->module_parts as $key => $value) {
2064
                if (is_array($value) && count($value) == 0) {
2065
                    continue;    // Discard empty arrays
2066
                }
2067
2068
                $entity = $conf->entity; // Reset the current entity
2069
                $newvalue = $value;
2070
2071
                // Serialize array parameters
2072
                if (is_array($value)) {
2073
                    // Can defined other parameters
2074
                    // Example when $key='hooks', then $value is an array('data'=>array('hookcontext1','hookcontext2'), 'entity'=>X)
2075
                    if (isset($value['data']) && is_array($value['data'])) {
2076
                        $newvalue = json_encode($value['data']);
2077
                        if (isset($value['entity'])) {
2078
                            $entity = $value['entity'];
2079
                        }
2080
                    } else if (isset($value['data']) && !is_array($value['data'])) {
2081
                        $newvalue = $value['data'];
2082
                        if (isset($value['entity'])) {
2083
                            $entity = $value['entity'];
2084
                        }
2085
                    } else {    // when hook is declared with syntax 'hook'=>array('hookcontext1','hookcontext2',...)
2086
                        $newvalue = json_encode($value);
2087
                    }
2088
                }
2089
2090
                $sql = "INSERT INTO " . MAIN_DB_PREFIX . "const (";
2091
                $sql .= "name";
2092
                $sql .= ", type";
2093
                $sql .= ", value";
2094
                $sql .= ", note";
2095
                $sql .= ", visible";
2096
                $sql .= ", entity";
2097
                $sql .= ")";
2098
                $sql .= " VALUES (";
2099
                $sql .= $this->db->encrypt($this->const_name . "_" . strtoupper($key), 1);
2100
                $sql .= ", 'chaine'";
2101
                $sql .= ", " . $this->db->encrypt($newvalue, 1);
2102
                $sql .= ", null";
2103
                $sql .= ", '0'";
2104
                $sql .= ", " . $entity;
2105
                $sql .= ")";
2106
2107
                dol_syslog(get_class($this) . "::insert_module_parts for key=" . $this->const_name . "_" . strtoupper($key), LOG_DEBUG);
2108
2109
                $resql = $this->db->query($sql, 1);
2110
                if (!$resql) {
2111
                    if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2112
                        $error++;
2113
                        $this->error = $this->db->lasterror();
2114
                    } else {
2115
                        dol_syslog(get_class($this) . "::insert_module_parts for " . $this->const_name . "_" . strtoupper($key) . " Record already exists.", LOG_WARNING);
2116
                    }
2117
                }
2118
            }
2119
        }
2120
        return $error;
2121
    }
2122
2123
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2124
    /**
2125
     * Removes generic parts
2126
     *
2127
     * @return int Error count (0 if OK)
2128
     */
2129
    function delete_module_parts()
2130
    {
2131
        // phpcs:enable
2132
        global $conf;
2133
2134
        $err = 0;
2135
        $entity = $conf->entity;
2136
2137
        if (is_array($this->module_parts) && !empty($this->module_parts)) {
2138
            foreach ($this->module_parts as $key => $value) {
2139
                // If entity is defined
2140
                if (is_array($value) && isset($value['entity'])) {
2141
                    $entity = $value['entity'];
2142
                }
2143
2144
                $sql = "DELETE FROM " . MAIN_DB_PREFIX . "const";
2145
                $sql .= " WHERE " . $this->db->decrypt('name') . " LIKE '" . $this->db->escape($this->const_name) . "_" . strtoupper($key) . "'";
2146
                $sql .= " AND entity = " . $entity;
2147
2148
                dol_syslog(get_class($this) . "::delete_const_" . $key . "", LOG_DEBUG);
2149
                if (!$this->db->query($sql)) {
2150
                    $this->error = $this->db->lasterror();
2151
                    $err++;
2152
                }
2153
            }
2154
        }
2155
        return $err;
2156
    }
2157
2158
    /**
2159
     * Function called when module is enabled.
2160
     * The init function adds tabs, constants, boxes, permissions and menus (defined in constructor) into Dolibarr database.
2161
     * It also creates data directories
2162
     *
2163
     * @param  string $options Options when enabling module ('', 'newboxdefonly', 'noboxes')
2164
     *                         'noboxes' = Do not insert boxes 'newboxdefonly' = For boxes,
2165
     *                         insert def of boxes only and not boxes activation
2166
     * @return int                1 if OK, 0 if KO
2167
     */
2168
    public function init($options = '')
2169
    {
2170
        return $this->_init(array(), $options);
2171
    }
2172
2173
    /**
2174
     * Function called when module is disabled.
2175
     * The remove function removes tabs, constants, boxes, permissions and menus from Dolibarr database.
2176
     * Data directories are not deleted
2177
     *
2178
     * @param  string $options Options when enabling module ('', 'noboxes')
2179
     * @return int                     1 if OK, 0 if KO
2180
     */
2181
    public function remove($options = '')
2182
    {
2183
        return $this->_remove(array(), $options);
2184
    }
2185
}
2186