rebuildObjectSql()   F
last analyzed

Complexity

Conditions 42
Paths 6209

Size

Total Lines 170
Code Lines 113

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 42
eloc 113
nc 6209
nop 7
dl 0
loc 170
rs 0
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) 2009-2010 Laurent Destailleur  <[email protected]>
4
 * Copyright (C) 2024		MDW							<[email protected]>
5
 * Copyright (C) 2024       Rafael San José             <[email protected]>
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19
 * or see https://www.gnu.org/
20
 */
21
22
use Dolibarr\Lib\ViewMain;
23
24
/**
25
 *  \file       htdocs/core/lib/modulebuilder.lib.php
26
 *  \brief      Set of function for modulebuilder management
27
 */
28
29
30
/**
31
 *  Regenerate files .class.php
32
 *
33
 * @param string $destdir Directory
34
 * @param string $module Module name
35
 * @param string $objectname Name of object
36
 * @param string $newmask New mask
37
 * @param string $readdir Directory source (use $destdir when not defined)
38
 * @param array{}|array{name:string,key:string,type:string,label:string,picot?:string,enabled:int<0,1>,notnull:int<0,1>,position:int,visible:int,noteditable?:int<0,1>,alwayseditable?:int<0,1>,default?:string,index?:int,foreignkey?:string,searchall?:int,isameasure?:int<0,1>,css?:string,cssview?:string,csslist?:string,help?:string,showoncombobox?:int<0,1>,disabled?:int<0,1>,autofocusoncreate?:int<0,1>,arrayofkeyval?:array<string,string>,validate?:int<0,1>,comment?:string} $addfieldentry Array of 1 field entry to add
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{}|array{name:strin...t<0,1>,comment?:string} at position 28 could not be parsed: Expected '}' at position 28, but found 'int'.
Loading history...
39
 * @param string $delfieldentry Id of field to remove
40
 * @return int<-7,-1>|CommonObject     Return integer <=0 if KO, Object if OK
41
 * @see rebuildObjectSql()
42
 */
43
function rebuildObjectClass($destdir, $module, $objectname, $newmask, $readdir = '', $addfieldentry = array(), $delfieldentry = '')
44
{
45
    global $db, $langs;
46
47
    if (empty($objectname)) {
48
        return -6;
49
    }
50
    if (empty($readdir)) {
51
        $readdir = $destdir;
52
    }
53
54
    if (!empty($addfieldentry['arrayofkeyval']) && !is_array($addfieldentry['arrayofkeyval'])) {
55
        dol_print_error(null, 'Bad parameter addfieldentry with a property arrayofkeyval defined but that is not an array.');
56
        return -7;
57
    }
58
59
    $error = 0;
60
61
    // Check parameters
62
    if (is_array($addfieldentry) && count($addfieldentry) > 0) {
63
        if (empty($addfieldentry['name'])) {
64
            setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv("Name")), null, 'errors');
65
            return -2;
66
        }
67
        if (empty($addfieldentry['label'])) {
68
            setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv("Label")), null, 'errors');
69
            return -2;
70
        }
71
        if (
72
            !preg_match('/^(integer|price|sellist|varchar|double|text|html|duration)/', $addfieldentry['type'])
73
            && !preg_match('/^(boolean|smallint|real|date|datetime|timestamp|phone|mail|url|ip|password)$/', $addfieldentry['type'])
74
        ) {
75
            setEventMessages($langs->trans('BadValueForType', $addfieldentry['type']), null, 'errors');
76
            return -2;
77
        }
78
    }
79
80
    $pathoffiletoeditsrc = $readdir . '/class/' . strtolower($objectname) . '.class.php';
81
    $pathoffiletoedittarget = $destdir . '/class/' . strtolower($objectname) . '.class.php' . ($readdir != $destdir ? '.new' : '');
82
    if (!dol_is_file($pathoffiletoeditsrc)) {
83
        $langs->load("errors");
84
        setEventMessages($langs->trans("ErrorFileNotFound", $pathoffiletoeditsrc), null, 'errors');
85
        return -3;
86
    }
87
88
    //$pathoffiletoedittmp=$destdir.'/class/'.strtolower($objectname).'.class.php.tmp';
89
    //dol_delete_file($pathoffiletoedittmp, 0, 1, 1);
90
91
    try {
92
        include_once $pathoffiletoeditsrc;
93
        if (class_exists($objectname)) {
94
            $object = new $objectname($db);
95
        } else {
96
            return -4;
97
        }
98
        '@phan-var-force CommonObject $object';
99
100
        // Backup old file
101
        dol_copy($pathoffiletoedittarget, $pathoffiletoedittarget . '.back', $newmask, 1);
102
103
        // Edit class files
104
        $contentclass = file_get_contents(dol_osencode($pathoffiletoeditsrc));
105
106
        // Update ->fields (to add or remove entries defined into $addfieldentry)
107
        if (count($object->fields)) {
108
            if (is_array($addfieldentry) && count($addfieldentry)) {
109
                $name = $addfieldentry['name'];
110
                unset($addfieldentry['name']);
111
112
                $object->fields[$name] = $addfieldentry;
113
            }
114
            if (!empty($delfieldentry)) {
115
                $name = $delfieldentry;
116
                unset($object->fields[$name]);
117
            }
118
        }
119
120
        dol_sort_array($object->fields, 'position');
121
122
        $i = 0;
123
        $texttoinsert = '// BEGIN MODULEBUILDER PROPERTIES' . "\n";
124
        $texttoinsert .= "\t" . '/**' . "\n";
125
        $texttoinsert .= "\t" . ' * @var array  Array with all fields and their property. Do not use it as a static var. It may be modified by constructor.' . "\n";
126
        $texttoinsert .= "\t" . ' */' . "\n";
127
        $texttoinsert .= "\t" . 'public $fields=array(' . "\n";
128
129
        if (count($object->fields)) {
130
            foreach ($object->fields as $key => $val) {
131
                $i++;
132
                $texttoinsert .= "\t\t" . '"' . $key . '" => array(';
133
                $texttoinsert .= '"type"=>"' . dol_escape_php($val['type']) . '",';
134
                $texttoinsert .= ' "label"=>"' . dol_escape_php($val['label']) . '",';
135
                if (!empty($val['picto'])) {
136
                    $texttoinsert .= ' "picto"=>"' . dol_escape_php($val['picto']) . '",';
137
                }
138
                $texttoinsert .= ' "enabled"=>"' . ($val['enabled'] !== '' ? dol_escape_php($val['enabled']) : 1) . '",';
139
                $texttoinsert .= " 'position'=>" . ($val['position'] !== '' ? (int)$val['position'] : 50) . ",";
140
                $texttoinsert .= " 'notnull'=>" . (empty($val['notnull']) ? 0 : (int)$val['notnull']) . ",";
141
                $texttoinsert .= ' "visible"=>"' . ($val['visible'] !== '' ? dol_escape_js($val['visible']) : -1) . '",';
142
                if (!empty($val['noteditable'])) {
143
                    $texttoinsert .= ' "noteditable"=>"' . dol_escape_php($val['noteditable']) . '",';
144
                }
145
                if (!empty($val['alwayseditable'])) {
146
                    $texttoinsert .= ' "alwayseditable"=>"' . dol_escape_php($val['alwayseditable']) . '",';
147
                }
148
                if (array_key_exists('default', $val) && (!empty($val['default']) || $val['default'] === '0')) {
149
                    $texttoinsert .= ' "default"=>"' . dol_escape_php($val['default']) . '",';
150
                }
151
                if (!empty($val['index'])) {
152
                    $texttoinsert .= ' "index"=>"' . (int)$val['index'] . '",';
153
                }
154
                if (!empty($val['foreignkey'])) {
155
                    $texttoinsert .= ' "foreignkey"=>"' . (int)$val['foreignkey'] . '",';
156
                }
157
                if (!empty($val['searchall'])) {
158
                    $texttoinsert .= ' "searchall"=>"' . (int)$val['searchall'] . '",';
159
                }
160
                if (!empty($val['isameasure'])) {
161
                    $texttoinsert .= ' "isameasure"=>"' . (int)$val['isameasure'] . '",';
162
                }
163
                if (!empty($val['css'])) {
164
                    $texttoinsert .= ' "css"=>"' . dol_escape_php($val['css']) . '",';
165
                }
166
                if (!empty($val['cssview'])) {
167
                    $texttoinsert .= ' "cssview"=>"' . dol_escape_php($val['cssview']) . '",';
168
                }
169
                if (!empty($val['csslist'])) {
170
                    $texttoinsert .= ' "csslist"=>"' . dol_escape_php($val['csslist']) . '",';
171
                }
172
                if (!empty($val['help'])) {
173
                    $texttoinsert .= ' "help"=>"' . dol_escape_php($val['help']) . '",';
174
                }
175
                if (!empty($val['showoncombobox'])) {
176
                    $texttoinsert .= ' "showoncombobox"=>"' . (int)$val['showoncombobox'] . '",';
177
                }
178
                if (!empty($val['disabled'])) {
179
                    $texttoinsert .= ' "disabled"=>"' . (int)$val['disabled'] . '",';
180
                }
181
                if (!empty($val['autofocusoncreate'])) {
182
                    $texttoinsert .= ' "autofocusoncreate"=>"' . (int)$val['autofocusoncreate'] . '",';
183
                }
184
                if (!empty($val['arrayofkeyval'])) {
185
                    $texttoinsert .= ' "arrayofkeyval"=>array(';
186
                    $i = 0;
187
                    foreach ($val['arrayofkeyval'] as $key2 => $val2) {
188
                        if ($i) {
189
                            $texttoinsert .= ", ";
190
                        }
191
                        $texttoinsert .= '"' . dol_escape_php($key2) . '" => "' . dol_escape_php($val2) . '"';
192
                        $i++;
193
                    }
194
                    $texttoinsert .= '),';
195
                }
196
                if (!empty($val['validate'])) {
197
                    $texttoinsert .= ' "validate"=>"' . (int)$val['validate'] . '",';
198
                }
199
                if (!empty($val['comment'])) {
200
                    $texttoinsert .= ' "comment"=>"' . dol_escape_php($val['comment']) . '"';
201
                }
202
203
                $texttoinsert .= "),\n";
204
                //print $texttoinsert;
205
            }
206
        }
207
208
        $texttoinsert .= "\t" . ');' . "\n";
209
        //print ($texttoinsert);exit;
210
211
        if (count($object->fields)) {
212
            //$typetotypephp=array('integer'=>'integer', 'duration'=>'integer', 'varchar'=>'string');
213
214
            foreach ($object->fields as $key => $val) {
215
                $i++;
216
                //$typephp=$typetotypephp[$val['type']];
217
                $texttoinsert .= "\t" . 'public $' . $key . ";";
218
                //if ($key == 'rowid')  $texttoinsert.= ' AUTO_INCREMENT PRIMARY KEY';
219
                //if ($key == 'entity') $texttoinsert.= ' DEFAULT 1';
220
                //$texttoinsert.= ($val['notnull']?' NOT NULL':'');
221
                //if ($i < count($object->fields)) $texttoinsert.=";";
222
                $texttoinsert .= "\n";
223
            }
224
        }
225
226
        $texttoinsert .= "\t" . '// END MODULEBUILDER PROPERTIES';
227
228
        //print($texttoinsert);
229
230
        $contentclass = preg_replace('/\/\/ BEGIN MODULEBUILDER PROPERTIES.*END MODULEBUILDER PROPERTIES/ims', $texttoinsert, $contentclass);
231
        //print $contentclass;
232
233
        dol_mkdir(dirname($pathoffiletoedittarget));
234
235
        //file_put_contents($pathoffiletoedittmp, $contentclass);
236
        $result = file_put_contents(dol_osencode($pathoffiletoedittarget), $contentclass);
237
238
        if ($result) {
239
            dolChmod($pathoffiletoedittarget, $newmask);
240
        } else {
241
            $error++;
242
        }
243
244
        return $error ? -1 : $object;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $error ? -1 : $object also could return the type object which is incompatible with the documented return type integer.
Loading history...
245
    } catch (Exception $e) {
246
        print $e->getMessage();
247
        return -5;
248
    }
249
}
250
251
/**
252
 *  Save data into a memory area shared by all users, all sessions on server
253
 *
254
 * @param string $destdir Directory
255
 * @param string $module Module name
256
 * @param string $objectname Name of object
257
 * @param string $newmask New mask
258
 * @param string $readdir Directory source (use $destdir when not defined)
259
 * @param Object $object If object was already loaded/known, it is pass to avoid another include and new.
260
 * @param string $moduletype 'external' or 'internal'
261
 * @return int                         Return integer <=0 if KO, >0 if OK
262
 * @see rebuildObjectClass()
263
 */
264
function rebuildObjectSql($destdir, $module, $objectname, $newmask, $readdir = '', $object = null, $moduletype = 'external')
265
{
266
    global $db, $langs;
267
268
    $error = 0;
269
270
    if (empty($objectname)) {
271
        return -1;
272
    }
273
    if (empty($readdir)) {
274
        $readdir = $destdir;
275
    }
276
277
    $pathoffiletoclasssrc = $readdir . '/class/' . strtolower($objectname) . '.class.php';
278
279
    // Edit .sql file
280
    if ($moduletype == 'internal') {
281
        $pathoffiletoeditsrc = '/../install/mysql/tables/llx_' . strtolower($module) . '_' . strtolower($objectname) . '.sql';
282
        if (!dol_is_file($readdir . $pathoffiletoeditsrc)) {
283
            $pathoffiletoeditsrc = '/../install/mysql/tables/llx_' . strtolower($module) . '_' . strtolower($objectname) . '-' . strtolower($module) . '.sql';
284
            if (!dol_is_file($readdir . $pathoffiletoeditsrc)) {
285
                $pathoffiletoeditsrc = '/../install/mysql/tables/llx_' . strtolower($module) . '-' . strtolower($module) . '.sql';
286
                if (!dol_is_file($readdir . $pathoffiletoeditsrc)) {
287
                    $pathoffiletoeditsrc = '/../install/mysql/tables/llx_' . strtolower($module) . '.sql';
288
                }
289
            }
290
        }
291
    } else {
292
        $pathoffiletoeditsrc = '/sql/llx_' . strtolower($module) . '_' . strtolower($objectname) . '.sql';
293
        if (!dol_is_file($readdir . $pathoffiletoeditsrc)) {
294
            $pathoffiletoeditsrc = '/sql/llx_' . strtolower($module) . '_' . strtolower($objectname) . '-' . strtolower($module) . '.sql';
295
            if (!dol_is_file($readdir . $pathoffiletoeditsrc)) {
296
                $pathoffiletoeditsrc = '/sql/llx_' . strtolower($module) . '-' . strtolower($module) . '.sql';
297
                if (!dol_is_file($readdir . $pathoffiletoeditsrc)) {
298
                    $pathoffiletoeditsrc = '/sql/llx_' . strtolower($module) . '.sql';
299
                }
300
            }
301
        }
302
    }
303
304
    // Complete path to be full path
305
    $pathoffiletoedittarget = $destdir . $pathoffiletoeditsrc . ($readdir != $destdir ? '.new' : '');
306
    $pathoffiletoeditsrc = $readdir . $pathoffiletoeditsrc;
307
308
    if (!dol_is_file($pathoffiletoeditsrc)) {
309
        $langs->load("errors");
310
        setEventMessages($langs->trans("ErrorFileNotFound", $pathoffiletoeditsrc), null, 'errors');
311
        return -1;
312
    }
313
314
    // Load object from myobject.class.php
315
    try {
316
        if (!is_object($object)) {
317
            include_once $pathoffiletoclasssrc;
318
            if (class_exists($objectname)) {
319
                $object = new $objectname($db);
320
            } else {
321
                return -1;
322
            }
323
        }
324
    } catch (Exception $e) {
325
        print $e->getMessage();
326
    }
327
328
    // Backup old file
329
    dol_copy($pathoffiletoedittarget, $pathoffiletoedittarget . '.back', $newmask, 1);
330
331
    $contentsql = file_get_contents(dol_osencode($pathoffiletoeditsrc));
332
333
    $i = 0;
334
    $texttoinsert = '-- BEGIN MODULEBUILDER FIELDS' . "\n";
335
    if (count($object->fields)) {
336
        foreach ($object->fields as $key => $val) {
337
            $i++;
338
339
            $type = $val['type'];
340
            $type = preg_replace('/:.*$/', '', $type); // For case type = 'integer:Societe:societe/class/societe.class.php'
341
342
            if ($type == 'html') {
343
                $type = 'text'; // html modulebuilder type is a text type in database
344
            } elseif ($type == 'price') {
345
                $type = 'double'; // html modulebuilder type is a text type in database
346
            } elseif (in_array($type, array('link', 'sellist', 'duration'))) {
347
                $type = 'integer';
348
            } elseif ($type == 'mail') {
349
                $type = 'varchar(128)';
350
            } elseif ($type == 'phone') {
351
                $type = 'varchar(20)';
352
            } elseif ($type == 'ip') {
353
                $type = 'varchar(32)';
354
            }
355
356
            $texttoinsert .= "\t" . $key . " " . $type;
357
            if ($key == 'rowid') {
358
                $texttoinsert .= ' AUTO_INCREMENT PRIMARY KEY';
359
            } elseif ($type == 'timestamp') {
360
                $texttoinsert .= ' DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP';
361
            }
362
            if ($key == 'entity') {
363
                $texttoinsert .= ' DEFAULT 1';
364
            } else {
365
                if (!empty($val['default'])) {
366
                    if (preg_match('/^null$/i', $val['default'])) {
367
                        $texttoinsert .= " DEFAULT NULL";
368
                    } elseif (preg_match('/varchar/', $type)) {
369
                        $texttoinsert .= " DEFAULT '" . $db->escape($val['default']) . "'";
370
                    } else {
371
                        $texttoinsert .= (($val['default'] > 0) ? ' DEFAULT ' . $val['default'] : '');
372
                    }
373
                }
374
            }
375
            $texttoinsert .= ((!empty($val['notnull']) && $val['notnull'] > 0) ? ' NOT NULL' : '');
376
            if ($i < count($object->fields)) {
377
                $texttoinsert .= ", ";
378
            }
379
            $texttoinsert .= "\n";
380
        }
381
    }
382
    $texttoinsert .= "\t" . '-- END MODULEBUILDER FIELDS';
383
384
    $contentsql = preg_replace('/-- BEGIN MODULEBUILDER FIELDS.*END MODULEBUILDER FIELDS/ims', $texttoinsert, $contentsql);
385
386
    $result = file_put_contents($pathoffiletoedittarget, $contentsql);
387
    if ($result) {
388
        dolChmod($pathoffiletoedittarget, $newmask);
389
    } else {
390
        $error++;
391
        setEventMessages($langs->trans("ErrorFailToCreateFile", $pathoffiletoedittarget), null, 'errors');
392
    }
393
394
    // Edit .key.sql file
395
    $pathoffiletoeditsrc = preg_replace('/\.sql$/', '.key.sql', $pathoffiletoeditsrc);
396
    $pathoffiletoedittarget = preg_replace('/\.sql$/', '.key.sql', $pathoffiletoedittarget);
397
    $pathoffiletoedittarget = preg_replace('/\.sql.new$/', '.key.sql.new', $pathoffiletoedittarget);
398
399
    $contentsql = file_get_contents(dol_osencode($pathoffiletoeditsrc));
400
401
    $i = 0;
402
    $texttoinsert = '-- BEGIN MODULEBUILDER INDEXES' . "\n";
403
    if (count($object->fields)) {
404
        foreach ($object->fields as $key => $val) {
405
            $i++;
406
            if (!empty($val['index'])) {
407
                $texttoinsert .= "ALTER TABLE llx_" . strtolower($module) . '_' . strtolower($objectname) . " ADD INDEX idx_" . strtolower($module) . '_' . strtolower($objectname) . "_" . $key . " (" . $key . ");";
408
                $texttoinsert .= "\n";
409
            }
410
            if (!empty($val['foreignkey'])) {
411
                $tmp = explode('.', $val['foreignkey']);
412
                if (!empty($tmp[0]) && !empty($tmp[1])) {
413
                    $texttoinsert .= "ALTER TABLE llx_" . strtolower($module) . '_' . strtolower($objectname) . " ADD CONSTRAINT llx_" . strtolower($module) . '_' . strtolower($objectname) . "_" . $key . " FOREIGN KEY (" . $key . ") REFERENCES llx_" . preg_replace('/^llx_/', '', $tmp[0]) . "(" . $tmp[1] . ");";
414
                    $texttoinsert .= "\n";
415
                }
416
            }
417
        }
418
    }
419
    $texttoinsert .= '-- END MODULEBUILDER INDEXES';
420
421
    $contentsql = preg_replace('/-- BEGIN MODULEBUILDER INDEXES.*END MODULEBUILDER INDEXES/ims', $texttoinsert, $contentsql);
422
423
    dol_mkdir(dirname($pathoffiletoedittarget));
424
425
    $result2 = file_put_contents($pathoffiletoedittarget, $contentsql);
426
    if ($result2) {
427
        dolChmod($pathoffiletoedittarget, $newmask);
428
    } else {
429
        $error++;
430
        setEventMessages($langs->trans("ErrorFailToCreateFile", $pathoffiletoedittarget), null, 'errors');
431
    }
432
433
    return $error ? -1 : 1;
434
}
435
436
/**
437
 * Get list of existing objects from a directory
438
 *
439
 * @param string $destdir Directory
440
 * @return  array|int                   Return integer <=0 if KO, array if OK
441
 */
442
function dolGetListOfObjectClasses($destdir)
443
{
444
    $objects = array();
445
    $listofobject = dol_dir_list($destdir . '/class', 'files', 0, '\.class\.php$');
446
    foreach ($listofobject as $fileobj) {
447
        if (preg_match('/^api_/', $fileobj['name'])) {
448
            continue;
449
        }
450
        if (preg_match('/^actions_/', $fileobj['name'])) {
451
            continue;
452
        }
453
454
        $tmpcontent = file_get_contents($fileobj['fullname']);
455
        $reg = array();
456
        if (preg_match('/class\s+([^\s]*)\s+extends\s+CommonObject/ims', $tmpcontent, $reg)) {
457
            $objectnameloop = $reg[1];
458
            $objects[$fileobj['fullname']] = $objectnameloop;
459
        }
460
    }
461
    if (count($objects) > 0) {
462
        return $objects;
463
    }
464
465
    return -1;
466
}
467
468
/**
469
 * Function to check if comment BEGIN and END exists in modMyModule class
470
 *
471
 * @param string $file Filename or path
472
 * @param int $number 0 = For Menus, 1 = For permissions, 2 = For Dictionaries
473
 * @return int                  1 if OK , -1 if KO
474
 */
475
function checkExistComment($file, $number)
476
{
477
    if (!file_exists($file)) {
478
        return -1;
479
    }
480
481
    $content = file_get_contents($file);
482
    if ($number === 0) {
483
        $ret = 0;
484
        if (
485
            strpos($content, '/* BEGIN MODULEBUILDER TOPMENU MYOBJECT */') !== false
486
            || strpos($content, '/* BEGIN MODULEBUILDER TOPMENU */') !== false
487
        ) {
488
            $ret++;
489
        }
490
        if (
491
            strpos($content, '/* END MODULEBUILDER TOPMENU MYOBJECT */') !== false
492
            || strpos($content, '/* END MODULEBUILDER TOPMENU */') !== false
493
        ) {
494
            $ret++;
495
        }
496
        if (strpos($content, '/* BEGIN MODULEBUILDER LEFTMENU MYOBJECT */') !== false) {
497
            $ret++;
498
        }
499
        if (strpos($content, '/* END MODULEBUILDER LEFTMENU MYOBJECT */') !== false) {
500
            $ret++;
501
        }
502
503
        if ($ret == 4) {
504
            return 1;
505
        }
506
    } elseif ($number === 1) {
507
        if (strpos($content, '/* BEGIN MODULEBUILDER PERMISSIONS */') !== false && strpos($content, '/* END MODULEBUILDER PERMISSIONS */') !== false) {
508
            return 1;
509
        }
510
    } elseif ($number == 2) {
511
        if (strpos($content, '/* BEGIN MODULEBUILDER DICTIONARIES */') !== false && strpos($content, '/* END MODULEBUILDER DICTIONARIES */') !== false) {
512
            return 1;
513
        }
514
    }
515
    return -1;
516
}
517
518
/**
519
 * Delete all permissions
520
 *
521
 * @param string $file file with path
522
 * @return void
523
 */
524
function deletePerms($file)
525
{
526
    $start = "/* BEGIN MODULEBUILDER PERMISSIONS */";
527
    $end = "/* END MODULEBUILDER PERMISSIONS */";
528
    $i = 1;
529
    $array = array();
530
    $lines = file($file);
531
    // Search for start and end lines
532
    foreach ($lines as $i => $line) {
533
        if (strpos($line, $start) !== false) {
534
            $start_line = $i + 1;
535
536
            // Copy lines until the end on array
537
            while (($line = $lines[++$i]) !== false) {
538
                if (strpos($line, $end) !== false) {
539
                    $end_line = $i + 1;
540
                    break;
541
                }
542
                $array[] = $line;
543
            }
544
            break;
545
        }
546
    }
547
    $allContent = implode("", $array);
548
    dolReplaceInFile($file, array($allContent => ''));
549
}
550
551
/**
552
 *  Compare two value
553
 * @param int|string $a value 1
554
 * @param int|string $b value 2
555
 * @return int      less 0 if str1 is less than str2; > 0 if str1 is greater than str2, and 0 if they are equal.
556
 */
557
function compareFirstValue($a, $b)
558
{
559
    return strcmp($a[0], $b[0]);
560
}
561
562
/**
563
 * Rewriting all permissions after any actions
564
 * @param string $file filename or path
565
 * @param array<int,string[]> $permissions permissions existing in file
566
 * @param int|null $key key for permission needed
567
 * @param array{0:string,1:string}|null $right $right to update or add
568
 * @param string|null $objectname name of object
569
 * @param string|null $module name of module
570
 * @param int<-2,2> $action 0 for delete, 1 for add, 2 for update, -1 when delete object completely, -2 for generate rights after add
571
 * @return int<-1,1>                   1 if OK,-1 if KO
572
 */
573
function reWriteAllPermissions($file, $permissions, $key, $right, $objectname, $module, $action)
574
{
575
    $error = 0;
576
    $rights = array();
577
    if ($action == 0) {
578
        // delete right from permissions array
579
        array_splice($permissions, array_search($permissions[$key], $permissions), 1);
580
    } elseif ($action == 1) {
581
        array_push($permissions, $right);
582
    } elseif ($action == 2 && !empty($right)) {
583
        // update right from permissions array
584
        array_splice($permissions, array_search($permissions[$key], $permissions), 1, $right);
585
    } elseif ($action == -1 && !empty($objectname)) {
586
        // when delete object
587
        $key = null;
588
        $right = null;
589
        foreach ($permissions as $perms) {
590
            if ($perms[4] === strtolower($objectname)) {
591
                array_splice($permissions, array_search($perms, $permissions), 1);
592
            }
593
        }
594
    } elseif ($action == -2 && !empty($objectname) && !empty($module)) {
595
        $key = null;
596
        $right = null;
597
        $objectOfRights = array();
598
        //check if object already declared in rights file
599
        foreach ($permissions as $right) {
0 ignored issues
show
introduced by
$right is overwriting one of the parameters of this function.
Loading history...
600
            $objectOfRights[] = $right[4];
601
        }
602
        if (in_array(strtolower($objectname), $objectOfRights)) {
603
            $error++;
604
        } else {
605
            $permsToadd = array();
606
            $perms = array(
607
                'read' => 'Read ' . $objectname . ' object of ' . ucfirst($module),
608
                'write' => 'Create/Update ' . $objectname . ' object of ' . ucfirst($module),
609
                'delete' => 'Delete ' . $objectname . ' object of ' . ucfirst($module)
610
            );
611
            $i = 0;
612
            foreach ($perms as $index => $value) {
613
                $permsToadd[$i][0] = '';
614
                $permsToadd[$i][1] = $value;
615
                $permsToadd[$i][4] = strtolower($objectname);
616
                $permsToadd[$i][5] = $index;
617
                array_push($permissions, $permsToadd[$i]);
618
                $i++;
619
            }
620
        }
621
    } else {
622
        $error++;
623
    }
624
    '@phan-var-force array<int,string[]> $permissions';
625
    if (!$error) {
626
        // prepare permissions array
627
        $count_perms = count($permissions);
628
        foreach (array_keys($permissions) as $i) {
629
            $permissions[$i][0] = "\$this->rights[\$r][0] = \$this->numero . sprintf('%02d', \$r + 1)";
630
            $permissions[$i][1] = "\$this->rights[\$r][1] = '" . $permissions[$i][1] . "'";
631
            $permissions[$i][4] = "\$this->rights[\$r][4] = '" . $permissions[$i][4] . "'";
632
            $permissions[$i][5] = "\$this->rights[\$r][5] = '" . $permissions[$i][5] . "';\n\t\t";
633
        }
634
        // for group permissions by object
635
        $perms_grouped = array();
636
        foreach ($permissions as $perms) {
637
            $object = $perms[4];
638
            if (!isset($perms_grouped[$object])) {
639
                $perms_grouped[$object] = [];
640
            }
641
            $perms_grouped[$object][] = $perms;
642
        }
643
        //$perms_grouped = array_values($perms_grouped);
644
        $permissions = $perms_grouped;
645
646
647
        // parcourir les objects
648
        $o = 0;
649
        foreach ($permissions as &$object) {
650
            // récupérer la permission de l'objet
651
            $p = 1;
652
            foreach ($object as &$obj) {
653
                if (str_contains($obj[5], 'read')) {
654
                    $obj[0] = "\$this->rights[\$r][0] = \$this->numero . sprintf('%02d', (" . $o . " * 10) + 0 + 1)";
655
                } elseif (str_contains($obj[5], 'write')) {
656
                    $obj[0] = "\$this->rights[\$r][0] = \$this->numero . sprintf('%02d', (" . $o . " * 10) + 1 + 1)";
657
                } elseif (str_contains($obj[5], 'delete')) {
658
                    $obj[0] = "\$this->rights[\$r][0] = \$this->numero . sprintf('%02d', (" . $o . " * 10) + 2 + 1)";
659
                } else {
660
                    $obj[0] = "\$this->rights[\$r][0] = \$this->numero . sprintf('%02d', (" . $o . " * 10) + " . $p . " + 1)";
661
                    $p++;
662
                }
663
            }
664
            usort($object, 'compareFirstValue');
665
            $o++;
666
        }
667
668
        //convert to string
669
        foreach ($permissions as $perms) {
670
            foreach ($perms as $per) {
671
                $rights[] = implode(";\n\t\t", $per);
672
                $rights[] = "\$r++;\n\t\t";
673
            }
674
        }
675
        $rights_str = implode("", $rights);
676
        // delete all permissions from file
677
        deletePerms($file);
678
        // rewrite all permissions again
679
        dolReplaceInFile($file, array('/* BEGIN MODULEBUILDER PERMISSIONS */' => '/* BEGIN MODULEBUILDER PERMISSIONS */' . "\n\t\t" . $rights_str));
680
        return 1;
681
    } else {
682
        return -1;
683
    }
684
}
685
686
/**
687
 * Converts a formatted properties string into an associative array.
688
 *
689
 * @param string $string The formatted properties string.
690
 * @return array<string,bool|int|float|string|mixed[]> The resulting associative array.
691
 */
692
function parsePropertyString($string)
693
{
694
    $string = str_replace("'", '', $string);
695
696
    // Uses a regular expression to capture keys and values
697
    preg_match_all('/\s*([^\s=>]+)\s*=>\s*([^,]+),?/', $string, $matches, PREG_SET_ORDER);
698
    $propertyArray = [];
699
700
    foreach ($matches as $match) {
701
        $key = trim($match[1]);
702
        $value = trim($match[2]);
703
704
        if (strpos($value, 'array(') === 0) {
705
            $nestedArray = substr($value, 6);
706
            $nestedArray = parsePropertyString($nestedArray);
707
            $value = $nestedArray;
708
        } elseif (strpos($value, '"Id")') !== false) {
709
            $value = str_replace(')', '', $value);
710
        } else {
711
            if (is_numeric($value)) {
712
                if (strpos($value, '.') !== false) {
713
                    $value = (float)$value;
714
                } else {
715
                    $value = (int)$value;
716
                }
717
            } else {
718
                if ($value === 'true') {
719
                    $value = true;
720
                } elseif ($value === 'false') {
721
                    $value = false;
722
                }
723
            }
724
        }
725
        $propertyArray[$key] = $value;
726
    }
727
728
    return $propertyArray;
729
}
730
731
/**
732
 * Write all properties of the object in AsciiDoc format
733
 * @param string $file path of the class
734
 * @param string $objectname name of the objectClass
735
 * @param string $destfile file where write table of properties
736
 * @return int                      1 if OK, -1 if KO
737
 */
738
function writePropsInAsciiDoc($file, $objectname, $destfile)
739
{
740
741
    // stock all properties in array
742
    $attributesUnique = array('type', 'label', 'enabled', 'position', 'notnull', 'visible', 'noteditable', 'index', 'default', 'foreignkey', 'arrayofkeyval', 'alwayseditable', 'validate', 'searchall', 'comment', 'isameasure', 'css', 'cssview', 'csslist', 'help', 'showoncombobox', 'picto');
743
744
    $start = "public \$fields=array(";
745
    $end = ");";
746
    $i = 1;
747
    $keys = array();
748
    $lines = file($file);
749
    // Search for start and end lines
750
    foreach ($lines as $i => $line) {
751
        if (strpos($line, $start) !== false) {
752
            // Copy lines until the end on array
753
            while (($line = $lines[++$i]) !== false) {
754
                if (strpos($line, $end) !== false) {
755
                    break;
756
                }
757
                $keys[] = $line;
758
            }
759
            break;
760
        }
761
    }
762
    // write the begin of table with specifics options
763
    $table = "== DATA SPECIFICATIONS\n";
764
    $table .= "=== Table of fields with properties for object *$objectname* : \n";
765
    $table .= "[options='header',grid=rows,frame=topbot,width=100%,caption=Organisation]\n";
766
    $table .= "|===\n";
767
    $table .= "|code";
768
    // write all properties in the header of the table
769
    foreach ($attributesUnique as $attUnique) {
770
        $table .= "|" . $attUnique;
771
    }
772
    $table .= "\n";
773
    $valuesModif = array();
774
    foreach ($keys as $string) {
775
        $string = trim($string, "'");
776
        $string = rtrim($string, ",");
777
778
        $array = parsePropertyString($string);
779
780
        // Iterate through the array to merge all key to one array
781
        $code = '';
782
        foreach ($array as $key => $value) {
783
            if (is_array($value)) {
784
                $code = $key;
785
                continue;
786
            } else {
787
                $array[$code][$key] = $value;
788
                unset($array[$key]);
789
            }
790
        }
791
        // check if is array after parsing the string
792
        if (!is_array($array)) {
793
            return -1;
794
        }
795
        $field = array_keys($array);
796
        if ($field[0] === '') {
797
            $field[0] = 'label';
798
        }
799
        $values = array_values($array)[0];
800
801
        // check each field has all properties and add it if missed
802
        foreach ($attributesUnique as $attUnique) {
803
            if ($attUnique == 'type' && $field[0] === 'label') {
804
                $values[$attUnique] = 'varchar(255)';
805
            }
806
            if (!array_key_exists($attUnique, $values)) {
807
                $valuesModif[$attUnique] = '';
808
            } else {
809
                $valuesModif[$attUnique] = $values[$attUnique];
810
            }
811
        }
812
        $table .= "|*" . $field[0] . "*|";
813
        $table .= implode("|", $valuesModif) . "\n";
814
    }
815
816
    // end table
817
    $table .= "|===\n";
818
    $table .= "__ end table for object $objectname\n";
819
820
    //write in file @phan-suppress-next-line PhanPluginSuspiciousParamPosition
821
    $writeInFile = dolReplaceInFile($destfile, array('== DATA SPECIFICATIONS' => $table));
822
    if ($writeInFile < 0) {
823
        return -1;
824
    }
825
    return 1;
826
}
827
828
829
/**
830
 * Delete property and permissions from documentation ascii file if we delete an object
831
 *
832
 * @param string $file file or path
833
 * @param string $objectname name of object wants to deleted
834
 * @return void
835
 */
836
function deletePropsAndPermsFromDoc($file, $objectname)
837
{
838
    if (dol_is_file($file)) {
839
        $start = "== Table of fields and their properties for object *" . ucfirst($objectname) . "* : ";
840
        $end = "__ end table for object " . ucfirst($objectname);
841
842
        $str = file_get_contents($file);
843
844
        $search = '/' . preg_quote($start, '/') . '(.*?)' . preg_quote($end, '/') . '/s';
845
        $new_contents = preg_replace($search, '', $str);
846
        file_put_contents($file, $new_contents);
847
848
        //perms If Exist
849
        $perms = "|*" . strtolower($objectname) . "*|";
850
        $search_pattern_perms = '/' . preg_quote($perms, '/') . '.*?\n/';
851
        $new_contents = preg_replace($search_pattern_perms, '', $new_contents);
852
        file_put_contents($file, $new_contents);
853
    }
854
}
855
856
857
/**
858
 * Search a string and return all lines needed from file. Does not include line $start nor $end
859
 *
860
 * @param string $file file for searching
861
 * @param string $start start line if exist
862
 * @param string $end end line if exist
863
 * @param string $excludestart Ignore if start line is $excludestart
864
 * @param int $includese Include start and end line
865
 * @return  string                  Return the lines between first line with $start and $end. "" if not found.
866
 */
867
function getFromFile($file, $start, $end, $excludestart = '', $includese = 0)
868
{
869
    $keys = array();
870
871
    //$lines = file(dol_osencode($file));
872
    $fhandle = fopen(dol_osencode($file), 'r');
873
    if ($fhandle) {
874
        // Search for start and end lines
875
        //foreach ($lines as $i => $line) {
876
        while ($line = fgets($fhandle)) {
877
            if (strpos($line, $start) !== false && (empty($excludestart) || strpos($line, $excludestart) === false)) {
878
                if ($includese) {
879
                    $keys[] = $line;
880
                }
881
                // Copy lines until we reach the end
882
                while (($line = fgets($fhandle)) !== false) {
883
                    if (strpos($line, $end) !== false) {
884
                        if ($includese) {
885
                            $keys[] = $line;
886
                        }
887
                        break;
888
                    }
889
                    $keys[] = $line;
890
                }
891
                break;
892
            }
893
        }
894
    }
895
    fclose($fhandle);
896
897
    $content = implode("", $keys);
898
    return $content;
899
}
900
901
/**
902
 * Write all permissions of each object in AsciiDoc format
903
 * @param string $file path of the class
904
 * @param string $destfile file where write table of permissions
905
 * @return int<-1,1>                1 if OK, -1 if KO
906
 */
907
function writePermsInAsciiDoc($file, $destfile)
908
{
909
    global $langs;
910
    //search and get all permissions in string
911
    $start = '/* BEGIN MODULEBUILDER PERMISSIONS */';
912
    $end = '/* END MODULEBUILDER PERMISSIONS */';
913
    $content = getFromFile($file, $start, $end);
914
    if (empty($content)) {
915
        return -1;
916
    }
917
    //prepare table
918
    $string = "[options='header',grid=rows,width=60%,caption=Organisation]\n";
919
    $string .= "|===\n";
920
    // header for table
921
    $header = array($langs->trans('Objects'), $langs->trans('Permission'));
922
    foreach ($header as $h) {
923
        $string .= "|" . $h;
924
    }
925
    $string .= "\n";
926
    //content table
927
    $array = explode(";", $content);
928
    $permissions = array_filter($array);
929
    // delete  occurrences "$r++" and ID
930
    $permissions = str_replace('$r++', '1', $permissions);
931
932
    $permsN = array();
933
    foreach ($permissions as $i => $element) {
934
        if ($element == 1) {
935
            unset($permissions[$i]);
936
        }
937
        if (str_contains($element, '$this->numero')) {
938
            unset($permissions[$i]);
939
        }
940
        if (str_contains($element, '$this->rights[$r][5]')) {
941
            unset($permissions[$i]);
942
        }
943
    }
944
    // cleaning the string on each element
945
    foreach ($permissions as $key => $element) {
946
        $element = str_replace(" '", '', $element);
947
        $element = trim($element, "'");
948
        $permsN[] = substr($element, strpos($element, "=") + 1);
949
    }
950
    array_pop($permsN);
951
952
    // Group permissions by Object and add it to string
953
    $final_array = [];
954
    $index = 0;
955
    while ($index < count($permsN)) {
956
        $temp_array = [$permsN[$index], $permsN[$index + 1]];
957
        $final_array[] = $temp_array;
958
        $index += 2;
959
    }
960
961
    $result = array();
962
    foreach ($final_array as $subarray) {
963
        // found object
964
        $key = $subarray[1];
965
        // add sub array to object
966
        $result[$key][] = $subarray;
967
    }
968
    foreach ($result as $i => $pems) {
969
        $string .= "|*" . $i . "*|";
970
        foreach ($pems as $tab) {
971
            $string .= $tab[0] . " , ";
972
        }
973
        $string .= "\n";
974
    }
975
    // end table
976
    $string .= "\n|===\n";
977
    // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
978
    $write = dolReplaceInFile($destfile, array('__DATA_PERMISSIONS__' => $string));
979
    if ($write < 0) {
980
        return -1;
981
    }
982
    return 1;
983
}
984
985
/**
986
 * Add Object in ModuleApi File
987
 *
988
 * @param string $srcfile Source file to use as example
989
 * @param string $file Path of modified file
990
 * @param string[] $objects Array of objects in the module
991
 * @param string $modulename Name of module
992
 * @return  int<-1,1>               Return 1 if OK, -1 if KO
993
 */
994
function addObjectsToApiFile($srcfile, $file, $objects, $modulename)
995
{
996
    global $langs, $user;
997
998
    if (!file_exists($file)) {
999
        return -1;
1000
    }
1001
1002
    $now = dol_now();
1003
    $content = file($file); // $content is an array
1004
1005
    $includeClass = "dol_include_once\(\'\/\w+\/class\/\w+\.class\.php\'\);";
1006
    $props = 'public\s+\$\w+;';
1007
    $varcommented = '@var\s+\w+\s+\$\w+\s+{@type\s+\w+}';
1008
    $constructObj = '\$this->\w+\s+=\s+new\s+\w+\(\$this->db\);';
1009
1010
    // add properties and declare them in constructor
1011
    foreach ($content as $lineNumber => &$lineContent) {
1012
        if (preg_match('/' . $varcommented . '/', $lineContent)) {
1013
            $lineContent = '';
1014
            foreach ($objects as $objectname) {
1015
                $lineContent .= "\t * @var " . $objectname . " \$" . strtolower($objectname) . " {@type " . $objectname . "}" . PHP_EOL;
1016
            }
1017
            //var_dump($lineContent);exit;
1018
        } elseif (preg_match('/' . $props . '/', $lineContent)) {
1019
            $lineContent = '';
1020
            foreach ($objects as $objectname) {
1021
                $lineContent .= "\tpublic \$" . strtolower($objectname) . ";" . PHP_EOL;
1022
            }
1023
        } elseif (preg_match('/' . $constructObj . '/', $lineContent)) {
1024
            $lineContent = '';
1025
            foreach ($objects as $objectname) {
1026
                $lineContent .= "\t\t\$this->" . strtolower($objectname) . " = new " . $objectname . "(\$this->db);" . PHP_EOL;
1027
            }
1028
        } elseif (preg_match('/' . $includeClass . '/', $lineContent)) {
1029
            $lineContent = '';
1030
            foreach ($objects as $objectname) {
1031
                $lineContent .= "dol_include_once('/" . strtolower($modulename) . "/class/" . strtolower($objectname) . ".class.php');" . PHP_EOL;
1032
            }
1033
        }
1034
    }
1035
1036
    $allContent = implode("", $content);
1037
    file_put_contents($file, $allContent);
1038
1039
    // Add methods for each object
1040
    $allContent = getFromFile($srcfile, '/* BEGIN MODULEBUILDER API MYOBJECT */', '/* END MODULEBUILDER API MYOBJECT */');
1041
    foreach ($objects as $objectname) {
1042
        $arrayreplacement = array(
1043
            'mymodule' => strtolower($modulename),
1044
            'MyModule' => $modulename,
1045
            'MYMODULE' => strtoupper($modulename),
1046
            'My module' => $modulename,
1047
            'my module' => $modulename,
1048
            'Mon module' => $modulename,
1049
            'mon module' => $modulename,
1050
            'htdocs/modulebuilder/template' => strtolower($modulename),
1051
            'myobject' => strtolower($objectname),
1052
            'MyObject' => $objectname,
1053
            'MYOBJECT' => strtoupper($objectname),
1054
            '---Put here your own copyright and developer email---' => dol_print_date($now, '%Y') . ' ' . $user->getFullName($langs) . ($user->email ? ' <' . $user->email . '>' : '')
1055
        );
1056
        $contentReplaced = make_substitutions($allContent, $arrayreplacement, null);
1057
        //$contentReplaced = str_replace(["myobject","MyObject"], [strtolower($object),$object], $allContent);
1058
1059
        dolReplaceInFile($file, array(
1060
            '/* BEGIN MODULEBUILDER API MYOBJECT */' => '/* BEGIN MODULEBUILDER API ' . strtoupper($objectname) . ' */' . $contentReplaced . "\t" . '/* END MODULEBUILDER API ' . strtoupper($objectname) . ' */' . "\n\n\n\t" . '/* BEGIN MODULEBUILDER API MYOBJECT */'
1061
        ));
1062
    }
1063
1064
    // Remove the block $allContent found in src file
1065
    // TODO Replace with a replacement of all text including into /* BEGIN MODULEBUILDER API MYOBJECT */ and /* END MODULEBUILDER API MYOBJECT */
1066
    dolReplaceInFile($file, array($allContent => ''));
1067
1068
    return 1;
1069
}
1070
1071
/**
1072
 * Remove   Object variables and methods from API_Module File
1073
 *
1074
 * @param string $file File api module
1075
 * @param string[] $objects Array of objects in the module
1076
 * @param string $objectname Name of object want to remove
1077
 * @return  int<-1,1>                   1 if OK, -1 if KO
1078
 */
1079
function removeObjectFromApiFile($file, $objects, $objectname)
1080
{
1081
    if (!file_exists($file)) {
1082
        return -1;
1083
    }
1084
1085
    $content = file($file); // $content is an array
1086
1087
    $includeClass = "dol_include_once\(\'\/\w+\/class\/" . strtolower($objectname) . "\.class\.php\'\);";
1088
    $props = 'public\s+\$' . strtolower($objectname);
1089
    $varcommented = '@var\s+\w+\s+\$' . strtolower($objectname) . '\s+{@type\s+\w+}';
1090
    $constructObj = '\$this->' . strtolower($objectname) . '\s+=\s+new\s+\w+\(\$this->db\);';
1091
1092
    // add properties and declare them in constructor
1093
    foreach ($content as $lineNumber => &$lineContent) {
1094
        if (preg_match('/' . $varcommented . '/i', $lineContent)) {
1095
            $lineContent = '';
1096
        } elseif (preg_match('/' . $props . '/i', $lineContent)) {
1097
            $lineContent = '';
1098
        } elseif (preg_match('/' . $constructObj . '/i', $lineContent)) {
1099
            $lineContent = '';
1100
        } elseif (preg_match('/' . $includeClass . '/i', $lineContent)) {
1101
            $lineContent = '';
1102
        }
1103
    }
1104
1105
    $allContent = implode("", $content);
1106
    file_put_contents($file, $allContent);
1107
1108
    // for delete methods of object
1109
    $begin = '/* BEGIN MODULEBUILDER API ' . strtoupper($objectname) . ' */';
1110
    $end = '/* END MODULEBUILDER API ' . strtoupper($objectname) . ' */';
1111
    $allContent = getFromFile($file, $begin, $end);
1112
    $check = dolReplaceInFile($file, array($allContent => ''));
1113
    if ($check) {
1114
        dolReplaceInFile($file, array($begin => '', $end => ''));
1115
    }
1116
1117
    return 1;
1118
}
1119
1120
1121
/**
1122
 * @param string $file path of filename
1123
 * @param array<int,array{commentgroup:string,fk_menu:string,type:string,titre:string,mainmenu:string,leftmenu:string,url:string,langs:string,position:int,enabled:int,perms:string,target:string,user:int}> $menus all menus for module
1124
 * @param mixed|null $menuWantTo menu get for do actions
1125
 * @param int|null $key key for the concerned menu
1126
 * @param int<-1,2> $action for specify what action (0 = delete perm, 1 = add perm, 2 = update perm, -1 = when we delete object)
1127
 * @return  int<-1,1>                   1 if OK, -1 if KO
1128
 */
1129
function reWriteAllMenus($file, $menus, $menuWantTo, $key, $action)
1130
{
1131
    $errors = 0;
1132
    $counter = 0;
1133
    if (!file_exists($file)) {
1134
        return -1;
1135
    }
1136
1137
    if ($action == 0 && !empty($key)) {
1138
        // delete menu manually
1139
        array_splice($menus, array_search($menus[$key], $menus), 1);
1140
    } elseif ($action == 1) {
1141
        // add menu manually
1142
        array_push($menus, $menuWantTo);
1143
    } elseif ($action == 2 && !empty($key) && !empty($menuWantTo)) {
1144
        // update right from permissions array
1145
        $urlCounter = 0;
1146
        // check if the values already exists
1147
        foreach ($menus as $index => $menu) {
1148
            if ($index !== $key) {
1149
                if ($menu['type'] === $menuWantTo['type']) {
1150
                    if (strcasecmp(str_replace(' ', '', $menu['titre']), str_replace(' ', '', $menuWantTo['titre'])) === 0) {
1151
                        $counter++;
1152
                    }
1153
                    if (strcasecmp(str_replace(' ', '', $menu['url']), str_replace(' ', '', $menuWantTo['url'])) === 0) {
1154
                        $urlCounter++;
1155
                    }
1156
                }
1157
            }
1158
        }
1159
        if (!$counter && $urlCounter < 2) {
1160
            $menus[$key] = $menuWantTo;
1161
        } else {
1162
            $errors++;
1163
        }
1164
    } elseif ($action == -1 && !empty($menuWantTo)) {
1165
        // delete menus when delete Object
1166
        foreach ($menus as $index => $menu) {
1167
            if ((strpos(strtolower($menu['fk_menu']), strtolower($menuWantTo)) !== false) || (strpos(strtolower($menu['leftmenu']), strtolower($menuWantTo)) !== false)) {
1168
                array_splice($menus, array_search($menu, $menus), 1);
1169
            }
1170
        }
1171
    } else {
1172
        $errors++;
1173
    }
1174
    if (!$errors) {
1175
        // delete All LEFT Menus (except for commented template MYOBJECT)
1176
        $beginMenu = '/* BEGIN MODULEBUILDER LEFTMENU';
1177
        $excludeBeginMenu = '/* BEGIN MODULEBUILDER LEFTMENU MYOBJECT';
1178
        $endMenu = '/* END MODULEBUILDER LEFTMENU';
1179
        $protection = 0;
1180
        while ($protection <= 1000 && $allMenus = getFromFile($file, $beginMenu, $endMenu, $excludeBeginMenu, 1)) {
1181
            $protection++;
1182
            dolReplaceInFile($file, array($allMenus => ''));
1183
        }
1184
1185
        // forge the menu code in a string
1186
        $str_menu = "";
1187
        foreach ($menus as $index => $menu) {
1188
            $menu['position'] = "1000 + \$r";
1189
            if ($menu['type'] === 'left') {
1190
                $start = "\t\t" . '/* BEGIN MODULEBUILDER LEFTMENU ' . strtoupper(empty($menu['object']) ? $menu['titre'] : $menu['object']) . ' */';
1191
                $end = "\t\t" . '/* END MODULEBUILDER LEFTMENU ' . strtoupper(empty($menu['object']) ? $menu['titre'] : $menu['object']) . ' */';
1192
1193
                $val_actuel = $menu;
1194
                $next_val = empty($menus[$index + 1]) ? null : $menus[$index + 1];
1195
                //var_dump(dol_escape_php($menu['perms'], 1)); exit;
1196
1197
                $str_menu .= $start . "\n";
1198
                $str_menu .= "\t\t\$this->menu[\$r++]=array(\n";
1199
                $str_menu .= "\t\t\t 'fk_menu' => '" . dol_escape_php($menu['fk_menu'], 1) . "',\n";
1200
                $str_menu .= "\t\t\t 'type' => '" . dol_escape_php($menu['type'], 1) . "',\n";
1201
                $str_menu .= "\t\t\t 'titre' => '" . dol_escape_php($menu['titre'], 1) . "',\n";
1202
                $str_menu .= "\t\t\t 'mainmenu' => '" . dol_escape_php($menu['mainmenu'], 1) . "',\n";
1203
                $str_menu .= "\t\t\t 'leftmenu' => '" . dol_escape_php($menu['leftmenu'], 1) . "',\n";
1204
                $str_menu .= "\t\t\t 'url' => '" . dol_escape_php($menu['url'], 1) . "',\n";
1205
                $str_menu .= "\t\t\t 'langs' => '" . dol_escape_php($menu['langs'], 1) . "',\n";
1206
                $str_menu .= "\t\t\t 'position' => " . ((int)$menu['position']) . ",\n";
1207
                $str_menu .= "\t\t\t 'enabled' => '" . dol_escape_php($menu['enabled'], 1) . "',\n";
1208
                $str_menu .= "\t\t\t 'perms' => '" . dol_escape_php($menu['perms'], 1) . "',\n";
1209
                $str_menu .= "\t\t\t 'target' => '" . dol_escape_php($menu['target'], 1) . "',\n";
1210
                $str_menu .= "\t\t\t 'user' => " . ((int)$menu['user']) . ",\n";
1211
                $str_menu .= "\t\t\t 'object' => '" . dol_escape_php($menu['object'], 1) . "',\n";
1212
                $str_menu .= "\t\t);\n";
1213
1214
                if (is_null($next_val) || $val_actuel['leftmenu'] !== $next_val['leftmenu']) {
1215
                    $str_menu .= $end . "\n";
1216
                }
1217
            }
1218
        }
1219
1220
        dolReplaceInFile($file, array('/* BEGIN MODULEBUILDER LEFTMENU MYOBJECT */' => $str_menu . "\n\t\t/* BEGIN MODULEBUILDER LEFTMENU MYOBJECT */"));
1221
        return 1;
1222
    }
1223
    return -1;
1224
}
1225
1226
/**
1227
 * Updates a dictionary in a module descriptor file.
1228
 *
1229
 * @param string $module The name of the module.
1230
 * @param string $file The path to the module descriptor file.
1231
 * @param array<string,string|array<string,int|string>> $dicts The dictionary data to be updated.
1232
 * @return int Returns the number of replacements made in the file.
1233
 */
1234
function updateDictionaryInFile($module, $file, $dicts)
1235
{
1236
    $isEmpty = false;
1237
    $dicData = "\t\t\$this->dictionaries=array(\n";
1238
    $module = strtolower($module);
1239
    foreach ($dicts as $key => $value) {
1240
        if (empty($value)) {
1241
            $isEmpty = true;
1242
            $dicData = "\t\t\$this->dictionaries=array();";
1243
            break;
1244
        }
1245
1246
        $dicData .= "\t\t\t'$key'=>";
1247
1248
        if ($key === 'tabcond') {
1249
            $conditions = array_map(
1250
            /**
1251
             * @param mixed $val
1252
             * @return string|int
1253
             */
1254
                function ($val) use ($module) {
1255
                    return is_bool($val) ? "isModEnabled('$module')" : $val;
1256
                },
1257
                $value
1258
            );
1259
            $dicData .= "array(" . implode(",", $conditions) . ")";
1260
        } elseif ($key === 'tabhelp') {
1261
            $helpItems = array();
1262
            foreach ($value as $helpValue) {
1263
                $helpItems[] = "array('code'=>\$langs->trans('" . $helpValue['code'] . "'), 'field2' => 'field2tooltip')";
1264
            }
1265
            $dicData .= "array(" . implode(",", $helpItems) . ")";
1266
        } else {
1267
            if (is_array($value)) {
1268
                $dicData .= "array(" . implode(
1269
                        ",",
1270
                        array_map(
1271
                        /**
1272
                         * @param string $val
1273
                         * @return string
1274
                         */
1275
                            static function ($val) {
1276
                                return "'$val'";
1277
                            },
1278
                            $value
1279
                        )
1280
                    ) . ")";
1281
            } else {
1282
                $dicData .= "'$value'";
1283
            }
1284
        }
1285
        $dicData .= ",\n";
1286
    }
1287
    $dicData .= (!$isEmpty ? "\t\t);" : '');
1288
1289
    $stringDic = getFromFile($file, '/* BEGIN MODULEBUILDER DICTIONARIES */', '/* END MODULEBUILDER DICTIONARIES */');
1290
    $writeInfile = dolReplaceInFile($file, array($stringDic => $dicData . "\n"));
1291
1292
    return $writeInfile;
1293
}
1294
1295
/**
1296
 * Creates a new dictionary table.
1297
 *
1298
 * for creating a new dictionary table in Dolibarr. It generates the necessary SQL code to define the table structure,
1299
 * including columns such as 'rowid', 'code', 'label', 'position', 'use_default', 'active', etc. The table name is constructed based on the provided $namedic parameter.
1300
 *
1301
 * @param string $modulename The lowercase name of the module for which the dictionary table is being created.
1302
 * @param string $file The file path to the Dolibarr module builder file where the dictionaries are defined.
1303
 * @param string $namedic The name of the dictionary, which will also be used as the base for the table name.
1304
 * @param array<string,string|array<string,int|string>> $dictionnaires An optional array containing pre-existing dictionary data, including 'tabname', 'tablib', 'tabsql', etc.
1305
 * @return  int<-1,-1>                  Return int < 0 if error, return nothing on success
1306
 */
1307
function createNewDictionnary($modulename, $file, $namedic, $dictionnaires = null)
1308
{
1309
    global $db, $langs;
1310
1311
    if (empty($namedic)) {
1312
        setEventMessages($langs->trans("ErrorEmptyNameDic"), null, 'errors');
1313
        return -1;
1314
    }
1315
    if (!file_exists($file)) {
1316
        return -1;
1317
    }
1318
    $modulename = strtolower($modulename);
1319
1320
    if (empty($dictionnaires)) {
1321
        $dictionnaires = array('langs' => '', 'tabname' => array(), 'tablib' => array(), 'tabsql' => array(), 'tabsqlsort' => array(), 'tabfield' => array(), 'tabfieldvalue' => array(), 'tabfieldinsert' => array(), 'tabrowid' => array(), 'tabcond' => array(), 'tabhelp' => array());
1322
    }
1323
1324
    $columns = array(
1325
        'rowid' => array('type' => 'integer', 'value' => 11, 'extra' => 'AUTO_INCREMENT'),
1326
        'code' => array('type' => 'varchar', 'value' => 255, 'null' => 'NOT NULL'),
1327
        'label' => array('type' => 'varchar', 'value' => 255, 'null' => 'NOT NULL'),
1328
        'position' => array('type' => 'integer', 'value' => 11, 'null' => 'NULL'),
1329
        'use_default' => array('type' => 'varchar', 'value' => 11, 'default' => '1'),
1330
        'active' => array('type' => 'integer', 'value' => 3)
1331
    );
1332
1333
    $primaryKey = 'rowid';
1334
    foreach ($columns as $key => $value) {
1335
        if ($key === 'rowid') {
1336
            $primaryKey = 'rowid';
1337
            break;
1338
        }
1339
        if (!array_key_exists('rowid', $columns)) {
1340
            $primaryKey = array_key_first($columns);
1341
            break;
1342
        }
1343
    }
1344
1345
    // check if tablename exist in Database and create it if not
1346
    $checkTable = $db->DDLDescTable(MAIN_DB_PREFIX . strtolower($namedic));
1347
    if ($checkTable && $db->num_rows($checkTable) > 0) {
1348
        setEventMessages($langs->trans("ErrorTableExist", $namedic), null, 'errors');
1349
        return -1;
1350
    } else {
1351
        $_results = $db->DDLCreateTable(MAIN_DB_PREFIX . strtolower($namedic), $columns, $primaryKey, "");
1352
        if ($_results < 0) {
1353
            dol_print_error($db);
1354
            $langs->load("errors");
1355
            setEventMessages($langs->trans("ErrorTableNotFound", $namedic), null, 'errors');
1356
        }
1357
    }
1358
1359
    // rewrite dictionary if
1360
    $dictionnaires['langs'] = $modulename . '@' . $modulename;
1361
    $dictionnaires['tabname'][] = strtolower($namedic);
1362
    $dictionnaires['tablib'][] = ucfirst(substr($namedic, 2));
1363
    $dictionnaires['tabsql'][] = 'SELECT t.rowid as rowid, t.code, t.label, t.active FROM ' . MAIN_DB_PREFIX . strtolower($namedic) . ' as t';
1364
    $dictionnaires['tabsqlsort'][] = (array_key_exists('label', $columns) ? 'label ASC' : '');
1365
    $dictionnaires['tabfield'][] = (array_key_exists('code', $columns) && array_key_exists('label', $columns) ? 'code,label' : '');
1366
    $dictionnaires['tabfieldvalue'][] = (array_key_exists('code', $columns) && array_key_exists('label', $columns) ? 'code,label' : '');
1367
    $dictionnaires['tabfieldinsert'][] = (array_key_exists('code', $columns) && array_key_exists('label', $columns) ? 'code,label' : '');
1368
    $dictionnaires['tabrowid'][] = $primaryKey;
1369
    $dictionnaires['tabcond'][] = isModEnabled('$modulename');  // @phan-suppress-current-line UnknownModuleName
1370
    $dictionnaires['tabhelp'][] = (array_key_exists('code', $columns) ? array('code' => $langs->trans('CodeTooltipHelp'), 'field2' => 'field2tooltip') : '');
1371
1372
    // Build the dictionary string
1373
    $writeInfile = updateDictionaryInFile($modulename, $file, $dictionnaires);
1374
    if ($writeInfile > 0) {
1375
        setEventMessages($langs->trans("DictionariesCreated", ucfirst(substr($namedic, 2))), null);
1376
    }
1377
1378
    return -1;
1379
}
1380
1381
/**
1382
 * Generate Urls and add them to documentation module
1383
 *
1384
 * @param string $file_api filename or path of api
1385
 * @param string $file_doc filename or path of documentation
1386
 * @return int<-1,1>         -1 if KO, 1 if OK, 0 if nothing change
1387
 */
1388
function writeApiUrlsInDoc($file_api, $file_doc)
1389
{
1390
    $error = 0;
1391
    if (!dol_is_file($file_api) || !dol_is_file($file_doc)) {
1392
        $error++;
1393
    }
1394
    $string = getFromFile($file_api, '/*begin methods CRUD*/', '/*end methods CRUD*/');
1395
    $extractUrls = explode("\n", $string);
1396
1397
    // extract urls from file
1398
    $urlValues = [];
1399
    foreach ($extractUrls as $key => $line) {
1400
        $lineWithoutTabsSpaces = preg_replace('/^[\t\s]+/', '', $line);
1401
        if (strpos($lineWithoutTabsSpaces, '* @url') === 0) {
1402
            $urlValue = trim(substr($lineWithoutTabsSpaces, strlen('* @url')));
1403
            $urlValues[] = $urlValue;
1404
        }
1405
    }
1406
1407
    // get urls by object
1408
    $str = $_SERVER['HTTP_HOST'] . '/api/index.php/';
1409
    $groupedUrls = [];
1410
    foreach ($urlValues as $url) {
1411
        if (preg_match('/(?:GET|POST|PUT|DELETE) (\w+)s/', $url, $matches)) {
1412
            $objectName = $matches[1];
1413
            $url = $str . trim(strstr($url, ' '));
1414
            $groupedUrls[$objectName][] = $url;
1415
        }
1416
    }
1417
    if (empty($groupedUrls)) {
1418
        $error++;
1419
    }
1420
1421
    // build format asciidoc for urls in table
1422
    if (!$error) {
1423
        $asciiDocTable = "[options=\"header\"]\n|===\n|Object | URLs\n";  // phpcs:ignore
1424
        foreach ($groupedUrls as $objectName => $urls) {
1425
            $urlsList = implode(" +\n*", $urls);
1426
            $asciiDocTable .= "|$objectName | \n*$urlsList +\n";
1427
        }
1428
        $asciiDocTable .= "|===\n";
1429
        $file_write = dolReplaceInFile($file_doc, array('__API_DOC__' => '__API_DOC__' . "\n" . $asciiDocTable));
1430
        if ($file_write < 0) {
1431
            return -1;
1432
        }
1433
        return 1;
1434
    }
1435
    return -1;
1436
}
1437
1438
1439
/**
1440
 * count directories or files in modulebuilder folder
1441
 * @param string $path path of directory
1442
 * @param int $type type of file 1= file,2=directory
1443
 * @return int|bool
1444
 */
1445
function countItemsInDirectory($path, $type = 1)
1446
{
1447
    if (!is_dir($path)) {
1448
        return false;
1449
    }
1450
1451
    $allFilesAndDirs = scandir($path);
1452
    $count = 0;
1453
1454
    foreach ($allFilesAndDirs as $item) {
1455
        if ($item != '.' && $item != '..') {
1456
            if ($type == 1 && is_file($path . DIRECTORY_SEPARATOR . $item) && strpos($item, '.back') === false) {
1457
                $count++;
1458
            } elseif ($type == 2 && is_dir($path . DIRECTORY_SEPARATOR . $item)) {
1459
                $count++;
1460
            }
1461
        }
1462
    }
1463
    return $count;
1464
}
1465