dolibarr 25.0.0-alpha
modulebuilder.lib.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2009-2010 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
4 * Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 * or see https://www.gnu.org/
19 */
20
26require_once DOL_DOCUMENT_ROOT . '/core/lib/functions.lib.php';
27require_once DOL_DOCUMENT_ROOT . '/modulebuilder/class/NamingContract.class.php';
28
43function dolModuleBuilderSqlIdentifier(string $prefix, string $module, string $table, string $key): string
44{
45 // The name of the field is used as is, as it was before, because it may hold uppercase characters
47 $table = dol_strtolower($table);
48
49 $identifier = $prefix.$module.'_'.$table.'_'.$key;
50 if (dol_strlen($identifier) <= 64) {
51 return $identifier;
52 }
53
54 $module = dol_substr($module, 0, 30);
55 $table = dol_substr($table, 0, 20);
56 // 2 = the two '_' separators. The prefix plus the maximum lengths of the module and of the table already
57 // reach 56 characters, so the key gets what is left instead of a fixed length of its own.
58 $key = dol_substr($key, 0, 64 - dol_strlen($prefix) - dol_strlen($module) - dol_strlen($table) - 2);
59
60 return $prefix.$module.'_'.$table.'_'.$key;
61}
62
76function rebuildObjectClass($destdir, $module, $objectname, $newmask, $readdir = '', $addfieldentry = array(), $delfieldentry = '')
77{
78 global $db, $langs;
79
80 if (empty($objectname)) {
81 return -6;
82 }
83 if (empty($readdir)) {
84 $readdir = $destdir;
85 }
86
87 if (!empty($addfieldentry['arrayofkeyval']) && !is_array($addfieldentry['arrayofkeyval'])) {
88 dol_print_error(null, 'Bad parameter addfieldentry with a property arrayofkeyval defined but that is not an array.');
89 return -7;
90 }
91
92 $error = 0;
93
94 // Check parameters into $addfieldentry (this provided array is filled by modulebuilder/index.php)
95 if (is_array($addfieldentry) && count($addfieldentry) > 0) {
96 if (empty($addfieldentry['name'])) {
97 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv("Name")), null, 'errors');
98 return -2;
99 }
100 if (empty($addfieldentry['label'])) {
101 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv("Label")), null, 'errors');
102 return -2;
103 }
104 if (!preg_match('/^(integer|price|sellist|varchar|double|text|html|duration|stars)/', $addfieldentry['type'])
105 && !preg_match('/^(boolean|smallint|real|date|datetime|timestamp|phone|email|url|ip|password)$/', $addfieldentry['type'])) { // Use email for email, mail is kept for compatibility
106 setEventMessages($langs->trans('BadValueForType', $addfieldentry['type']), null, 'errors');
107 return -2;
108 }
109 // Check for type stars(NumberOfStars), NumberOfStars must be an integer between 1 and 10
110 $matches = array();
111 if (preg_match('/^stars\‍((.+)\‍)$/', $addfieldentry['type'], $matches)) {
112 if (!preg_match('/^[0-9]+$/', $matches[1]) || $matches[1] < 1 || $matches[1] > 10) {
113 setEventMessages($langs->trans('BadValueForType', $addfieldentry['type']), null, 'errors');
114 return -2;
115 }
116 }
117 }
118
119 $pathoffiletoeditsrc = $readdir.'/class/'.dol_strtolower($objectname).'.class.php';
120 $pathoffiletoedittarget = $destdir.'/class/'.dol_strtolower($objectname).'.class.php'.($readdir != $destdir ? '.new' : '');
121 if (!dol_is_file($pathoffiletoeditsrc)) {
122 $langs->load("errors");
123 setEventMessages($langs->trans("ErrorFileNotFound", $pathoffiletoeditsrc), null, 'errors');
124 return -3;
125 }
126
127 //$pathoffiletoedittmp = $destdir.'/class/'.dol_strtolower($objectname).'.class.php.tmp';
128 //dol_delete_file($pathoffiletoedittmp, 0, 1, 1);
129
130 try {
131 include_once $pathoffiletoeditsrc;
132 if (class_exists($objectname)) {
133 $object = new $objectname($db);
134 } else {
135 return -4;
136 }
137 '@phan-var-force CommonObject $object';
138
139 // Backup old file
140 dol_copy($pathoffiletoedittarget, $pathoffiletoedittarget.'.back', $newmask, 1);
141
142 // Edit class files
143 $contentclass = file_get_contents(dol_osencode($pathoffiletoeditsrc));
144
145 // Update ->fields (to add or remove entries defined into $addfieldentry)
146 if (count($object->fields)) {
147 if (is_array($addfieldentry) && count($addfieldentry)) {
148 $name = $addfieldentry['name'];
149 unset($addfieldentry['name']);
150
151 $object->fields[$name] = $addfieldentry;
152 }
153 if (!empty($delfieldentry)) {
154 $name = $delfieldentry;
155 unset($object->fields[$name]);
156 }
157 }
158
159 dol_sort_array($object->fields, 'position');
160
161 $i = 0;
162 $texttoinsert = '// BEGIN MODULEBUILDER PROPERTIES'."\n";
163 $texttoinsert .= "\t".'
166'."\n";
167 $texttoinsert .= "\t".'public $fields = array('."\n";
168
169 if (count($object->fields)) {
170 foreach ($object->fields as $key => $val) {
171 $i++;
172 $texttoinsert .= "\t\t".'"'.$key.'" => array(';
173 $texttoinsert .= '"type" => "'.dol_escape_php($val['type']).'",';
174 $texttoinsert .= ' "label" => "'.dol_escape_php($val['label']).'",';
175 if (!empty($val['picto'])) {
176 $texttoinsert .= ' "picto" => "'.dol_escape_php($val['picto']).'",';
177 }
178 $texttoinsert .= ' "enabled" => "'.dol_escape_php($val['enabled'] !== '' ? $val['enabled'] : '1').'",';
179 $texttoinsert .= " 'position' => ".($val['position'] !== '' ? (int) $val['position'] : 50).",";
180 $texttoinsert .= " 'notnull' => ".(empty($val['notnull']) ? 0 : (int) $val['notnull']).",";
181 $texttoinsert .= ' "visible" => "'.dol_escape_php($val['visible'] !== '' ? $val['visible'] : '-1').'",';
182 if (!empty($val['noteditable'])) {
183 $texttoinsert .= ' "noteditable" => "'.dol_escape_php((string) $val['noteditable']).'",';
184 }
185 if (!empty($val['alwayseditable'])) {
186 $texttoinsert .= ' "alwayseditable" => "'.dol_escape_php((string) $val['alwayseditable']).'",';
187 }
188 if (array_key_exists('default', $val) && (!empty($val['default']) || $val['default'] === '0')) {
189 $texttoinsert .= ' "default" => "'.dol_escape_php($val['default']).'",';
190 }
191 if (!empty($val['index'])) {
192 $texttoinsert .= ' "index" => "'.(int) $val['index'].'",';
193 }
194 if (!empty($val['foreignkey'])) {
195 $texttoinsert .= ' "foreignkey" => "'.dol_escape_php($val['foreignkey']).'",';
196 }
197 if (!empty($val['searchall'])) {
198 $texttoinsert .= ' "searchall" => "'.(int) $val['searchall'].'",';
199 }
200 if (!empty($val['isameasure'])) {
201 $texttoinsert .= ' "isameasure" => "'.(int) $val['isameasure'].'",';
202 }
203 if (!empty($val['css'])) {
204 $texttoinsert .= ' "css" => "'.dol_escape_php($val['css']).'",';
205 }
206 if (!empty($val['cssview'])) {
207 $texttoinsert .= ' "cssview" => "'.dol_escape_php($val['cssview']).'",';
208 }
209 if (!empty($val['csslist'])) {
210 $texttoinsert .= ' "csslist" => "'.dol_escape_php($val['csslist']).'",';
211 }
212 if (!empty($val['help'])) {
213 $texttoinsert .= ' "help" => "'.dol_escape_php($val['help']).'",';
214 }
215 if (!empty($val['showoncombobox'])) {
216 $texttoinsert .= ' "showoncombobox" => "'.(int) $val['showoncombobox'].'",';
217 }
218 if (!empty($val['disabled'])) {
219 $texttoinsert .= ' "disabled" => "'.(int) $val['disabled'].'",';
220 }
221 if (!empty($val['autofocusoncreate'])) {
222 $texttoinsert .= ' "autofocusoncreate" => "'.(int) $val['autofocusoncreate'].'",';
223 }
224 if (!empty($val['arrayofkeyval'])) {
225 $texttoinsert .= ' "arrayofkeyval" => array(';
226 $i = 0;
227 foreach ($val['arrayofkeyval'] as $key2 => $val2) {
228 if ($i) {
229 $texttoinsert .= ", ";
230 }
231 $texttoinsert .= '"'.dol_escape_php($key2).'" => "'.dol_escape_php($val2).'"';
232 $i++;
233 }
234 $texttoinsert .= '),';
235 }
236 if (!empty($val['validate'])) {
237 $texttoinsert .= ' "validate" => "'.(int) $val['validate'].'",';
238 }
239 if (!empty($val['comment'])) {
240 $texttoinsert .= ' "comment" => "'.dol_escape_php($val['comment']).'"';
241 }
242
243 $texttoinsert .= "),\n";
244 //print $texttoinsert;
245 }
246 }
247
248 $texttoinsert .= "\t".');'."\n";
249 //print ($texttoinsert);exit;
250
251 if (count($object->fields)) {
252 //$typetotypephp = array('integer' => 'integer', 'duration' => 'integer', 'varchar' => 'string');
253
254 foreach ($object->fields as $key => $val) {
255 $i++;
256 //$typephp = $typetotypephp[$val['type']];
257 $texttoinsert .= "\t".'public $'.$key.";";
258 //if ($key == 'rowid') $texttoinsert.= ' AUTO_INCREMENT PRIMARY KEY';
259 //if ($key == 'entity') $texttoinsert.= ' DEFAULT 1';
260 //$texttoinsert.= ($val['notnull']?' NOT NULL':'');
261 //if ($i < count($object->fields)) $texttoinsert. = ";";
262 $texttoinsert .= "\n";
263 }
264 }
265
266 $texttoinsert .= "\t".'// END MODULEBUILDER PROPERTIES';
267
268 //print($texttoinsert);
269
270 $contentclass = preg_replace('/\/\/ BEGIN MODULEBUILDER PROPERTIES.*END MODULEBUILDER PROPERTIES/ims', $texttoinsert, $contentclass);
271 //print $contentclass;
272
273 dol_mkdir(dirname($pathoffiletoedittarget));
274
275 //file_put_contents($pathoffiletoedittmp, $contentclass);
276 $result = file_put_contents(dol_osencode($pathoffiletoedittarget), $contentclass);
277
278 if ($result) {
279 dolChmod($pathoffiletoedittarget, $newmask);
280 } else {
281 $error++;
282 }
283
284 return $error ? -1 : $object;
285 } catch (Exception $e) {
286 print $e->getMessage();
287 return -5;
288 }
289}
290
304function rebuildObjectSql($destdir, $module, $objectname, $newmask, $readdir = '', $object = null, $moduletype = 'external')
305{
306 global $db, $langs;
307
308 $error = 0;
309
310 if (empty($objectname)) {
311 return -1;
312 }
313 if (empty($readdir)) {
314 $readdir = $destdir;
315 }
316
317 $pathoffiletoclasssrc = $readdir.'/class/'.dol_strtolower($objectname).'.class.php';
318
319 // Edit .sql file
320 if ($moduletype == 'internal') {
321 $pathoffiletoeditsrc = '/../install/mysql/tables/llx_'.dol_strtolower($module).'_'.dol_strtolower($objectname).'.sql';
322 if (! dol_is_file($readdir.$pathoffiletoeditsrc)) {
323 $pathoffiletoeditsrc = '/../install/mysql/tables/llx_'.dol_strtolower($module).'_'.dol_strtolower($objectname).'-'.dol_strtolower($module).'.sql';
324 if (! dol_is_file($readdir.$pathoffiletoeditsrc)) {
325 $pathoffiletoeditsrc = '/../install/mysql/tables/llx_'.dol_strtolower($module).'-'.dol_strtolower($module).'.sql';
326 if (! dol_is_file($readdir.$pathoffiletoeditsrc)) {
327 $pathoffiletoeditsrc = '/../install/mysql/tables/llx_'.dol_strtolower($module).'.sql';
328 }
329 }
330 }
331 } else {
332 $pathoffiletoeditsrc = '/sql/llx_'.dol_strtolower($module).'_'.dol_strtolower($objectname).'.sql';
333 if (! dol_is_file($readdir.$pathoffiletoeditsrc)) {
334 $pathoffiletoeditsrc = '/sql/llx_'.dol_strtolower($module).'_'.dol_strtolower($objectname).'-'.dol_strtolower($module).'.sql';
335 if (! dol_is_file($readdir.$pathoffiletoeditsrc)) {
336 $pathoffiletoeditsrc = '/sql/llx_'.dol_strtolower($module).'-'.dol_strtolower($module).'.sql';
337 if (! dol_is_file($readdir.$pathoffiletoeditsrc)) {
338 $pathoffiletoeditsrc = '/sql/llx_'.dol_strtolower($module).'.sql';
339 }
340 }
341 }
342 }
343
344 // Complete path to be full path
345 $pathoffiletoedittarget = $destdir.$pathoffiletoeditsrc.($readdir != $destdir ? '.new' : '');
346 $pathoffiletoeditsrc = $readdir.$pathoffiletoeditsrc;
347
348 if (!dol_is_file($pathoffiletoeditsrc)) {
349 $langs->load("errors");
350 setEventMessages($langs->trans("ErrorFileNotFound", $pathoffiletoeditsrc), null, 'errors');
351 return -1;
352 }
353
354 // Load object from myobject.class.php
355 try {
356 if (!is_object($object)) {
357 include_once $pathoffiletoclasssrc;
358 if (class_exists($objectname)) {
359 $object = new $objectname($db);
360 } else {
361 return -1;
362 }
363 }
364 } catch (Exception $e) {
365 print $e->getMessage();
366 }
367
368 // Backup old file
369 dol_copy($pathoffiletoedittarget, $pathoffiletoedittarget.'.back', $newmask, 1);
370
371 $contentsqlfile = file_get_contents(dol_osencode($pathoffiletoeditsrc));
372
373 $i = 0;
374 $texttoinsert = '-- BEGIN MODULEBUILDER FIELDS'."\n";
375 if (count($object->fields)) {
376 foreach ($object->fields as $key => $val) {
377 $i++;
378
379 $type = $val['type'];
380 $type = preg_replace('/:.*$/', '', $type); // For case type = 'integer:Societe:societe/class/societe.class.php'
381
382 if ($type == 'html') {
383 $type = 'text'; // html modulebuilder type is a text type in database
384 } elseif ($type == 'price') {
385 $type = 'double'; // html modulebuilder type is a text type in database
386 } elseif (in_array($type, array('link', 'sellist', 'duration'))) {
387 $type = 'integer';
388 } elseif ($type == 'chkbxlst') {
389 $type = 'varchar(128)';
390 } elseif ($type == 'mail' || $type == 'email') { // Prefer to use 'email'
391 $type = 'varchar(128)';
392 } elseif (strpos($type, 'stars(') === 0) {
393 $type = 'integer';
394 } elseif ($type == 'phone') {
395 $type = 'varchar(20)';
396 } elseif ($type == 'ip') {
397 $type = 'varchar(32)';
398 } elseif ($type == 'url') {
399 $type = 'varchar(255)';
400 }
401
402 $texttoinsert .= "\t".$key." ".$type;
403 if ($key == 'rowid') {
404 $texttoinsert .= ' AUTO_INCREMENT PRIMARY KEY';
405 } elseif ($type == 'timestamp') {
406 $texttoinsert .= ' DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP';
407 }
408 if ($key == 'entity') {
409 $texttoinsert .= ' DEFAULT 1';
410 } else {
411 if (!empty($val['default'])) {
412 if (preg_match('/^null$/i', $val['default'])) {
413 $texttoinsert .= " DEFAULT NULL";
414 } elseif (preg_match('/varchar/', $type)) {
415 $texttoinsert .= " DEFAULT '".$db->escape($val['default'])."'";
416 } else {
417 $texttoinsert .= (($val['default'] > 0) ? ' DEFAULT '.$val['default'] : '');
418 }
419 }
420 }
421 $texttoinsert .= ((!empty($val['notnull']) && $val['notnull'] > 0) ? ' NOT NULL' : '');
422 if ($i < count($object->fields)) {
423 $texttoinsert .= ", ";
424 }
425 $texttoinsert .= "\n";
426 }
427 }
428 $texttoinsert .= "\t".'-- END MODULEBUILDER FIELDS';
429
430 $contentsqlfile = preg_replace('/-- BEGIN MODULEBUILDER FIELDS.*END MODULEBUILDER FIELDS/ims', $texttoinsert, $contentsqlfile);
431
432 $result = file_put_contents($pathoffiletoedittarget, $contentsqlfile);
433 if ($result) {
434 dolChmod($pathoffiletoedittarget, $newmask);
435 } else {
436 $error++;
437 setEventMessages($langs->trans("ErrorFailToCreateFile", $pathoffiletoedittarget), null, 'errors');
438 }
439
440 // Edit .key.sql file
441 $pathoffiletoeditsrc = preg_replace('/\.sql$/', '.key.sql', $pathoffiletoeditsrc);
442 $pathoffiletoedittarget = preg_replace('/\.sql$/', '.key.sql', $pathoffiletoedittarget);
443 $pathoffiletoedittarget = preg_replace('/\.sql.new$/', '.key.sql.new', $pathoffiletoedittarget);
444
445 $contentsqlfile = file_get_contents(dol_osencode($pathoffiletoeditsrc));
446
447 $i = 0;
448 $texttoinsert = '-- BEGIN MODULEBUILDER INDEXES'."\n";
449 if (count($object->fields)) {
450 foreach ($object->fields as $key => $val) {
451 $i++;
452 if (!empty($val['index'])) {
453 $indexname = dolModuleBuilderSqlIdentifier(($key == 'ref' ? "uk_" : "idx_"), $module, $objectname, $key);
454 $texttoinsert .= "ALTER TABLE llx_".dol_strtolower($module).'_'.dol_strtolower($objectname)." ADD ".($key == 'ref' ? "UNIQUE INDEX " : "INDEX ").$indexname." (".$key.($key == 'ref' && array_key_exists('entity', $object->fields) ? ", entity" : "").");";
455 $texttoinsert .= "\n";
456 }
457 if (!empty($val['foreignkey'])) {
458 $tmp = explode('.', $val['foreignkey']);
459 if (!empty($tmp[0]) && !empty($tmp[1])) {
460 $constraintname = dolModuleBuilderSqlIdentifier("llx_", $module, $objectname, $key);
461 $texttoinsert .= "ALTER TABLE llx_".dol_strtolower($module).'_'.dol_strtolower($objectname)." ADD CONSTRAINT ".$constraintname." FOREIGN KEY (".$key.") REFERENCES llx_".preg_replace('/^llx_/', '', $tmp[0])."(".$tmp[1].");";
462 $texttoinsert .= "\n";
463 }
464 }
465 }
466 }
467 $texttoinsert .= '-- END MODULEBUILDER INDEXES';
468
469 $contentsqlfile = preg_replace('/-- BEGIN MODULEBUILDER INDEXES.*END MODULEBUILDER INDEXES/ims', $texttoinsert, $contentsqlfile);
470
471 dol_mkdir(dirname($pathoffiletoedittarget));
472
473 $result2 = file_put_contents($pathoffiletoedittarget, $contentsqlfile);
474 if ($result2) {
475 dolChmod($pathoffiletoedittarget, $newmask);
476 } else {
477 $error++;
478 setEventMessages($langs->trans("ErrorFailToCreateFile", $pathoffiletoedittarget), null, 'errors');
479 }
480
481 return $error ? -1 : 1;
482}
483
491{
492 $objects = array();
493 $listofobject = dol_dir_list($destdir.'/class', 'files', 0, '\.class\.php$');
494 foreach ($listofobject as $fileobj) {
495 if (preg_match('/^api_/', $fileobj['name'])) {
496 continue;
497 }
498 if (preg_match('/^actions_/', $fileobj['name'])) {
499 continue;
500 }
501
502 $tmpcontent = file_get_contents($fileobj['fullname']);
503 $reg = array();
504 if (preg_match('/class\s+([^\s]*)\s+extends\s+CommonObject/ims', $tmpcontent, $reg)) {
505 $objectnameloop = $reg[1];
506 $objects[$fileobj['fullname']] = $objectnameloop;
507 }
508 }
509 if (count($objects) > 0) {
510 return $objects;
511 }
512
513 return -1;
514}
515
523function checkExistComment($file, $number)
524{
525 if (!file_exists($file)) {
526 return -1;
527 }
528
529 $content = file_get_contents($file);
530 if ($number === 0) {
531 $ret = 0;
532 if (strpos($content, '/* BEGIN MODULEBUILDER TOPMENU MYOBJECT */') !== false
533 || strpos($content, '/* BEGIN MODULEBUILDER TOPMENU */') !== false) {
534 $ret++;
535 }
536 if (strpos($content, '/* END MODULEBUILDER TOPMENU MYOBJECT */') !== false
537 || strpos($content, '/* END MODULEBUILDER TOPMENU */') !== false) {
538 $ret++;
539 }
540 if (strpos($content, '/* BEGIN MODULEBUILDER LEFTMENU MYOBJECT */') !== false) {
541 $ret++;
542 }
543 if (strpos($content, '/* END MODULEBUILDER LEFTMENU MYOBJECT */') !== false) {
544 $ret++;
545 }
546
547 if ($ret == 4) {
548 return 1;
549 }
550 } elseif ($number === 1) {
551 if (strpos($content, '/* BEGIN MODULEBUILDER PERMISSIONS */') !== false && strpos($content, '/* END MODULEBUILDER PERMISSIONS */') !== false) {
552 return 1;
553 }
554 } elseif ($number == 2) {
555 if (strpos($content, '/* BEGIN MODULEBUILDER DICTIONARIES */') !== false && strpos($content, '/* END MODULEBUILDER DICTIONARIES */') !== false) {
556 return 1;
557 }
558 }
559 return -1;
560}
567function deletePerms($file)
568{
569 require_once DOL_DOCUMENT_ROOT.'/modulebuilder/class/PermissionsBlock.class.php';
570
571 try {
572 $block = PermissionsBlock::fromFile($file);
573 } catch (\RuntimeException $e) {
574 dol_syslog('deletePerms '.$e->getMessage(), LOG_WARNING);
575 return -1;
576 }
577
578 return $block->write('') < 0 ? -1 : 1;
579}
580
597function reWriteAllPermissions($file, $permissions, $key, $right, $objectname, $module, $action)
598{
599 require_once DOL_DOCUMENT_ROOT.'/modulebuilder/class/RightsSyncService.class.php';
600
601 $module = (string) $module;
602 $objectname = (string) $objectname;
603 $permissions = is_array($permissions) ? $permissions : array();
604 $right = is_array($right) ? $right : array();
605
606 // The legacy signature carries the module name only for action -2; the other actions recover
607 // it from the descriptor filename.
608 if ($module === '') {
609 $module = (string) preg_replace('/^mod|\.class\.php$/', '', basename($file));
610 }
611
612 try {
613 switch ((int) $action) {
614 case -2:
615 $cmd = RightsSyncCommand::forObjectCreation($module, $file, $permissions, $objectname);
616 break;
617 case -1:
618 $cmd = RightsSyncCommand::forObjectDeletion($module, $file, $permissions, $objectname);
619 break;
620 case 1:
622 $module,
623 $file,
624 $permissions,
625 (string) ($right[4] ?? ''),
626 (string) ($right[1] ?? ''),
627 (string) ($right[5] ?? '')
628 );
629 break;
630 case 2:
632 $module,
633 $file,
634 $permissions,
635 (int) $key,
636 (string) ($right[4] ?? ''),
637 (string) ($right[1] ?? ''),
638 (string) ($right[5] ?? '')
639 );
640 break;
641 case 0:
642 $cmd = RightsSyncCommand::forRightDeletion($module, $file, $permissions, (int) $key);
643 break;
644 default:
645 dol_syslog('reWriteAllPermissions got an unknown action: '.$action, LOG_WARNING);
646 return -1;
647 }
648 } catch (\InvalidArgumentException $e) {
649 dol_syslog('reWriteAllPermissions '.$e->getMessage(), LOG_WARNING);
650 return -1;
651 }
652
653 $report = (new DescriptorRightsSyncService())->sync($cmd);
654
655 // The historical contract reports a skipped object creation as a failure.
656 if ($report->skipped > 0) {
657 return -1;
658 }
659
660 return $report->toLegacyReturnCode();
661}
662
669function parsePropertyString($string)
670{
671 $string = str_replace("'", '', $string);
672
673 // Uses a regular expression to capture keys and values
674 preg_match_all('/\s*([^\s=>]+)\s*=>\s*([^,]+),?/', $string, $matches, PREG_SET_ORDER);
675 $propertyArray = array();
676
677 foreach ($matches as $match) {
678 $key = trim($match[1]);
679 $value = trim($match[2]);
680
681 if (strpos($value, 'array(') === 0) {
682 $nestedArray = dol_substr($value, 6);
683 $nestedArray = parsePropertyString($nestedArray);
684 $value = $nestedArray;
685 } elseif (strpos($value, '"Id")') !== false) {
686 $value = str_replace(')', '', $value);
687 } else {
688 if (is_numeric($value)) {
689 if (strpos($value, '.') !== false) {
690 $value = (float) $value;
691 } else {
692 $value = (int) $value;
693 }
694 } else {
695 if ($value === 'true') {
696 $value = true;
697 } elseif ($value === 'false') {
698 $value = false;
699 }
700 }
701 }
702 $propertyArray[$key] = $value;
703 }
704
705 return $propertyArray;
706}
707
715function writePropsInAsciiDoc($file, $objectname, $destfile)
716{
717
718 // stock all properties in array
719 $attributesUnique = array('type','label', 'enabled', 'position', 'notnull', 'visible', 'noteditable', 'index', 'default' , 'foreignkey', 'arrayofkeyval', 'alwayseditable','validate', 'searchall','comment', 'isameasure', 'css', 'cssview','csslist', 'help', 'showoncombobox','picto' );
720
721 $start = "public \$fields = array(";
722 $end = ");";
723 $i = 1;
724 $keys = array();
725 $lines = file($file);
726 // Search for start and end lines
727 foreach ($lines as $i => $line) {
728 if (strpos($line, $start) !== false) {
729 // Copy lines until the end on array
730 while (($line = $lines[++$i]) !== false) {
731 if (strpos($line, $end) !== false) {
732 break;
733 }
734 $keys[] = $line;
735 }
736 break;
737 }
738 }
739 // write the begin of table with specifics options
740 $table = "== DATA SPECIFICATIONS\n";
741 $table .= "=== Table of fields with properties for object *$objectname* : \n";
742 $table .= "[options='header',grid=rows,frame=topbot,width=100%,caption=Organisation]\n";
743 $table .= "|===\n";
744 $table .= "|code";
745 // write all properties in the header of the table
746 foreach ($attributesUnique as $attUnique) {
747 $table .= "|".$attUnique;
748 }
749 $table .= "\n";
750 $valuesModif = array();
751 foreach ($keys as $string) {
752 $string = trim($string, "'");
753 $string = rtrim($string, ",");
754
755 $array = parsePropertyString($string);
756
757 // Iterate through the array to merge all key to one array
758 $code = '';
759 foreach ($array as $key => $value) {
760 if (is_array($value)) {
761 $code = $key;
762 continue;
763 } else {
764 $array[$code][$key] = $value;
765 unset($array[$key]);
766 }
767 }
768 // check if is array after parsing the string
769 if (!is_array($array)) {
770 return -1;
771 }
772 $field = array_keys($array);
773 if ($field[0] === '') {
774 $field[0] = 'label';
775 }
776 $values = array_values($array)[0];
777
778 // check each field has all properties and add it if missed
779 foreach ($attributesUnique as $attUnique) {
780 if ($attUnique == 'type' && $field[0] === 'label') {
781 $values[$attUnique] = 'varchar(255)';
782 }
783 if (!array_key_exists($attUnique, $values)) {
784 $valuesModif[$attUnique] = '';
785 } else {
786 $valuesModif[$attUnique] = $values[$attUnique];
787 }
788 }
789 $table .= "|*" . $field[0] . "*|";
790 $table .= implode("|", $valuesModif) . "\n";
791 }
792
793 // end table
794 $table .= "|===\n";
795 $table .= "__ end table for object $objectname\n";
796
797 //write in file @phan-suppress-next-line PhanPluginSuspiciousParamPosition
798 $writeInFile = dolReplaceInFile($destfile, array('== DATA SPECIFICATIONS' => $table));
799 if ($writeInFile < 0) {
800 return -1;
801 }
802 return 1;
803}
804
805
813function deletePropsAndPermsFromDoc($file, $objectname)
814{
815 if (dol_is_file($file)) {
816 $start = "== Table of fields and their properties for object *".dol_ucfirst($objectname)."* : ";
817 $end = "__ end table for object ".dol_ucfirst($objectname);
818
819 $str = file_get_contents($file);
820
821 $search = '/' . preg_quote($start, '/') . '(.*?)' . preg_quote($end, '/') . '/s';
822 $new_contents = preg_replace($search, '', $str);
823 file_put_contents($file, $new_contents);
824 dolChmod($file);
825
826 //perms If Exist
827 $perms = "|*".dol_strtolower($objectname)."*|";
828 $search_pattern_perms = '/' . preg_quote($perms, '/') . '.*?\n/';
829 $new_contents = preg_replace($search_pattern_perms, '', $new_contents);
830 file_put_contents($file, $new_contents);
831 dolChmod($file);
832 }
833}
834
835
836
847function getFromFile($file, $start, $end, $excludestart = '', $includese = 0)
848{
849 $keys = array();
850
851 //$lines = file(dol_osencode($file));
852 $fhandle = fopen(dol_osencode($file), 'r');
853 if ($fhandle) {
854 // Search for start and end lines
855 //foreach ($lines as $i => $line) {
856 while ($line = fgets($fhandle)) {
857 if (strpos($line, $start) !== false && (empty($excludestart) || strpos($line, $excludestart) === false)) {
858 if ($includese) {
859 $keys[] = $line;
860 }
861 // Copy lines until we reach the end
862 while (($line = fgets($fhandle)) !== false) {
863 if (strpos($line, $end) !== false) {
864 if ($includese) {
865 $keys[] = $line;
866 }
867 break;
868 }
869 $keys[] = $line;
870 }
871 break;
872 }
873 }
874 }
875 fclose($fhandle);
876
877 $content = implode("", $keys);
878 return $content;
879}
880
887function writePermsInAsciiDoc($file, $destfile)
888{
889 global $langs;
890 //search and get all permissions in string
891 $start = '/* BEGIN MODULEBUILDER PERMISSIONS */';
892 $end = '/* END MODULEBUILDER PERMISSIONS */';
893 $content = getFromFile($file, $start, $end);
894 if (empty($content)) {
895 return -1;
896 }
897 //prepare table
898 $string = "[options='header',grid=rows,width=60%,caption=Organisation]\n";
899 $string .= "|===\n";
900 // header for table
901 $header = array($langs->trans('Objects'),$langs->trans('Permission'));
902 foreach ($header as $h) {
903 $string .= "|".$h;
904 }
905 $string .= "\n";
906 //content table
907 $array = explode(";", $content);
908 $permissions = array_filter($array);
909 // delete occurrences "$r++" and ID
910 $permissions = str_replace('$r++', '1', $permissions);
911
912 $permsN = array();
913 foreach ($permissions as $i => $element) {
914 if ($element == 1) {
915 unset($permissions[$i]);
916 }
917 if (str_contains($element, '$this->numero')) {
918 unset($permissions[$i]);
919 }
920 if (str_contains($element, '$this->rights[$r][5]')) {
921 unset($permissions[$i]);
922 }
923 }
924 // cleaning the string on each element
925 foreach ($permissions as $key => $element) {
926 $element = str_replace(" '", '', $element);
927 $element = trim($element, "'");
928 $permsN[] = dol_substr($element, strpos($element, "=") + 1);
929 }
930 array_pop($permsN);
931
932 // Group permissions by Object and add it to string
933 $final_array = array();
934 $index = 0;
935 while ($index < count($permsN)) {
936 $temp_array = array($permsN[$index], $permsN[$index + 1]);
937 $final_array[] = $temp_array;
938 $index += 2;
939 }
940
941 $result = array();
942 foreach ($final_array as $subarray) {
943 // found object
944 $key = $subarray[1];
945 // add sub array to object
946 $result[$key][] = $subarray;
947 }
948 foreach ($result as $i => $pems) {
949 $string .= "|*".$i."*|";
950 foreach ($pems as $tab) {
951 $string .= $tab[0]." , ";
952 }
953 $string .= "\n";
954 }
955 // end table
956 $string .= "\n|===\n";
957 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
958 $write = dolReplaceInFile($destfile, array('__DATA_PERMISSIONS__' => $string));
959 if ($write < 0) {
960 return -1;
961 }
962 return 1;
963}
964
974function addObjectsToApiFile($srcfile, $file, $objects, $modulename)
975{
976 global $langs, $user;
977
978 if (!file_exists($file)) {
979 return -1;
980 }
981
982 $now = dol_now();
983 $content = file($file); // $content is an array
984
985 $includeClass = "dol_include_once\‍(\'\/\w+\/class\/\w+\.class\.php\'\‍);";
986 $props = 'public\s+\$\w+;';
987 $varcommented = '@var\s+\w+\s+\$\w+\s+{@type\s+\w+}';
988 $constructObj = '\$this->\w+\s+=\s+new\s+\w+\‍(\$this->db\‍);';
989
990 // add properties and declare them in constructor
991 foreach ($content as $lineNumber => &$lineContent) {
992 if (preg_match('/'.$varcommented.'/', $lineContent)) {
993 $lineContent = '';
994 foreach ($objects as $objectname) {
995 $lineContent .= "\t * @var ".$objectname." \$".dol_strtolower($objectname)." {@type ".$objectname."}". PHP_EOL;
996 }
997 //var_dump($lineContent);exit;
998 } elseif (preg_match('/'.$props.'/', $lineContent)) {
999 $lineContent = '';
1000 foreach ($objects as $objectname) {
1001 $lineContent .= "\t/*".PHP_EOL."\t * @var mixed TODO: set type".PHP_EOL."\t */".PHP_EOL."\tpublic \$".dol_strtolower($objectname).";". PHP_EOL;
1002 }
1003 } elseif (preg_match('/'.$constructObj.'/', $lineContent)) {
1004 $lineContent = '';
1005 foreach ($objects as $objectname) {
1006 $lineContent .= "\t\t\$this->".dol_strtolower($objectname)." = new ".$objectname."(\$this->db);". PHP_EOL;
1007 }
1008 } elseif (preg_match('/'.$includeClass.'/', $lineContent)) {
1009 $lineContent = '';
1010 foreach ($objects as $objectname) {
1011 $lineContent .= "dol_include_once('/".dol_strtolower($modulename)."/class/".dol_strtolower($objectname).".class.php');". PHP_EOL;
1012 }
1013 }
1014 }
1015
1016 $allContent = implode("", $content);
1017 file_put_contents($file, $allContent);
1018 dolChmod($file);
1019
1020 // Add methods for each object
1021 $allContent = getFromFile($srcfile, '/* BEGIN MODULEBUILDER API MYOBJECT */', '/* END MODULEBUILDER API MYOBJECT */');
1022 foreach ($objects as $objectname) {
1023 if (dol_strtolower($modulename) === dol_strtolower($objectname)) {
1024 dol_syslog('addObjectsToApiFile: skipping object "' . $objectname . '" — name collides with module "' . $modulename . '"', LOG_WARNING);
1025 continue;
1026 }
1027 $nc = new NamingContract($modulename, $objectname);
1028 $extraMap = [
1029 'htdocs/modulebuilder/template' => $nc->moduleNameLower,
1030 '---Replace with your own copyright and developer email---' => dol_print_date($now, '%Y') . ' ' . $user->getFullName($langs) . ($user->email ? ' <' . $user->email . '>' : ''),
1031 ];
1032 $fullMap = array_merge($nc->getSubstitutionMap(), $extraMap);
1033 $contentReplaced = str_replace(array_keys($fullMap), array_values($fullMap), $allContent);
1034
1035 dolReplaceInFile($file, array(
1036 '/* BEGIN MODULEBUILDER API MYOBJECT */' => '/* BEGIN MODULEBUILDER API '.dol_strtoupper($objectname).' */'.$contentReplaced."\t".'/* END MODULEBUILDER API '.dol_strtoupper($objectname).' */'."\n\n\n\t".'/* BEGIN MODULEBUILDER API MYOBJECT */'
1037 ));
1038 }
1039
1040 // Remove the block $allContent found in src file
1041 // TODO Replace with a replacement of all text including into /* BEGIN MODULEBUILDER API MYOBJECT */ and /* END MODULEBUILDER API MYOBJECT */
1042 dolReplaceInFile($file, array($allContent => ''));
1043
1044 return 1;
1045}
1046
1055function removeObjectFromApiFile($file, $objects, $objectname)
1056{
1057 if (!file_exists($file)) {
1058 return -1;
1059 }
1060
1061 $content = file($file); // $content is an array
1062
1063 $includeClass = "dol_include_once\‍(\'\/\w+\/class\/".dol_strtolower($objectname)."\.class\.php\'\‍);";
1064 $props = 'public\s+\$'.dol_strtolower($objectname);
1065 $varcommented = '@var\s+\w+\s+\$'.dol_strtolower($objectname).'\s+{@type\s+\w+}';
1066 $constructObj = '\$this->'.dol_strtolower($objectname).'\s+=\s+new\s+\w+\‍(\$this->db\‍);';
1067
1068 // add properties and declare them in constructor
1069 foreach ($content as $lineNumber => &$lineContent) {
1070 if (preg_match('/'.$varcommented.'/i', $lineContent)) {
1071 $lineContent = '';
1072 } elseif (preg_match('/'.$props.'/i', $lineContent)) {
1073 $lineContent = '';
1074 } elseif (preg_match('/'.$constructObj.'/i', $lineContent)) {
1075 $lineContent = '';
1076 } elseif (preg_match('/'.$includeClass.'/i', $lineContent)) {
1077 $lineContent = '';
1078 }
1079 }
1080
1081 $allContent = implode("", $content);
1082 file_put_contents($file, $allContent);
1083 dolChmod($file);
1084
1085 // for delete methods of object
1086 $begin = '/* BEGIN MODULEBUILDER API '.dol_strtoupper($objectname).' */';
1087 $end = '/* END MODULEBUILDER API '.dol_strtoupper($objectname).' */';
1088 $allContent = getFromFile($file, $begin, $end);
1089 $check = dolReplaceInFile($file, array($allContent => ''));
1090 if ($check) {
1091 dolReplaceInFile($file, array($begin => '', $end => ''));
1092 }
1093
1094 return 1;
1095}
1096
1097
1106function reWriteAllMenus($file, $menus, $menuWantTo, $key, $action)
1107{
1108 $errors = 0;
1109 $counter = 0;
1110 if (!file_exists($file)) {
1111 return -1;
1112 }
1113
1114 if ($action == 0 && !empty($key)) {
1115 // delete menu manually
1116 array_splice($menus, array_search($menus[$key], $menus), 1);
1117 } elseif ($action == 1) {
1118 // add menu manually
1119 array_push($menus, $menuWantTo);
1120 } elseif ($action == 2 && !empty($key) && !empty($menuWantTo)) {
1121 // update right from permissions array
1122 $urlCounter = 0;
1123 // check if the values already exists
1124 foreach ($menus as $index => $menu) {
1125 if ($index !== $key) {
1126 if ($menu['type'] === $menuWantTo['type']) {
1127 if (strcasecmp(str_replace(' ', '', $menu['titre']), str_replace(' ', '', $menuWantTo['titre'])) === 0) {
1128 $counter++;
1129 }
1130 if (strcasecmp(str_replace(' ', '', $menu['url']), str_replace(' ', '', $menuWantTo['url'])) === 0) {
1131 $urlCounter++;
1132 }
1133 }
1134 }
1135 }
1136 if (!$counter && $urlCounter < 2) {
1137 $menus[$key] = $menuWantTo;
1138 } else {
1139 $errors++;
1140 }
1141 } elseif ($action == -1 && !empty($menuWantTo) && is_string($menuWantTo)) {
1142 // delete menus when delete Object
1143 foreach ($menus as $index => $menu) {
1144 if ((strpos(dol_strtolower($menu['fk_menu']), dol_strtolower($menuWantTo)) !== false) || (strpos(dol_strtolower($menu['leftmenu']), dol_strtolower($menuWantTo)) !== false)) {
1145 array_splice($menus, array_search($menu, $menus), 1);
1146 }
1147 }
1148 } else {
1149 $errors++;
1150 }
1151 if (!$errors) {
1152 // delete All LEFT Menus (except for commented template MYOBJECT)
1153 $beginMenu = '/* BEGIN MODULEBUILDER LEFTMENU';
1154 $excludeBeginMenu = '/* BEGIN MODULEBUILDER LEFTMENU MYOBJECT';
1155 $endMenu = '/* END MODULEBUILDER LEFTMENU';
1156 $protection = 0;
1157 while ($protection <= 1000 && $allMenus = getFromFile($file, $beginMenu, $endMenu, $excludeBeginMenu, 1)) {
1158 $protection++;
1159 dolReplaceInFile($file, array($allMenus => ''));
1160 }
1161
1162 // forge the menu code in a string
1163 $str_menu = "";
1164 foreach ($menus as $index => $menu) {
1165 $menu['position'] = "1000 + \$r";
1166 if ($menu['type'] === 'left') {
1167 $start = "\t\t".'/* BEGIN MODULEBUILDER LEFTMENU '.dol_strtoupper(empty($menu['object']) ? $menu['titre'] : $menu['object']).' */';
1168 $end = "\t\t".'/* END MODULEBUILDER LEFTMENU '.dol_strtoupper(empty($menu['object']) ? $menu['titre'] : $menu['object']).' */';
1169
1170 $val_actuel = $menu;
1171 $next_val = empty($menus[$index + 1]) ? null : $menus[$index + 1];
1172 //var_dump(dol_escape_php($menu['perms'], 1)); exit;
1173
1174 $str_menu .= $start."\n";
1175 $str_menu .= "\t\t\$this->menu[\$r++] = array(\n";
1176 $str_menu .= "\t\t\t'fk_menu' => '".dol_escape_php($menu['fk_menu'], 1)."',\n";
1177 $str_menu .= "\t\t\t'type' => '".dol_escape_php($menu['type'], 1)."',\n";
1178 $str_menu .= "\t\t\t'titre' => '".dol_escape_php($menu['titre'], 1)."',\n";
1179 $str_menu .= "\t\t\t'mainmenu' => '".dol_escape_php($menu['mainmenu'], 1)."',\n";
1180 $str_menu .= "\t\t\t'leftmenu' => '".dol_escape_php($menu['leftmenu'], 1)."',\n";
1181 $str_menu .= "\t\t\t'url' => '".dol_escape_php($menu['url'], 1)."',\n";
1182 $str_menu .= "\t\t\t'langs' => '".dol_escape_php($menu['langs'], 1)."',\n";
1183 $str_menu .= "\t\t\t'position' => ".((int) $menu['position']).",\n";
1184 $str_menu .= "\t\t\t'enabled' => '".dol_escape_php((string) $menu['enabled'], 1)."',\n";
1185 $str_menu .= "\t\t\t'perms' => '".dol_escape_php($menu['perms'], 1)."',\n";
1186 $str_menu .= "\t\t\t'target' => '".dol_escape_php($menu['target'], 1)."',\n";
1187 $str_menu .= "\t\t\t'user' => ".((int) $menu['user']).",\n";
1188 $str_menu .= "\t\t\t'object' => '".dol_escape_php($menu['object'], 1)."',\n";
1189 $str_menu .= "\t\t);\n";
1190
1191 if (is_null($next_val) || $val_actuel['leftmenu'] !== $next_val['leftmenu']) {
1192 $str_menu .= $end."\n";
1193 }
1194 }
1195 }
1196
1197 dolReplaceInFile($file, array('/* BEGIN MODULEBUILDER LEFTMENU MYOBJECT */' => $str_menu."\n\t\t/* BEGIN MODULEBUILDER LEFTMENU MYOBJECT */"));
1198 return 1;
1199 }
1200 return -1;
1201}
1202
1211function updateDictionaryInFile($module, $file, $dicts)
1212{
1213 $isEmpty = false;
1214 $dicData = "\t\t\$this->dictionaries = array(\n";
1216 foreach ($dicts as $key => $value) {
1217 if (empty($value)) {
1218 $isEmpty = true;
1219 $dicData = "\t\t\$this->dictionaries = array();";
1220 break;
1221 }
1222
1223 $dicData .= "\t\t\t'$key' => ";
1224
1225 if ($key === 'tabcond') {
1226 $conditions = array_map(
1231 static function ($val) use ($module) {
1232 return is_bool($val) ? "isModEnabled('$module')" : $val;
1233 },
1234 $value
1235 );
1236 $dicData .= "array(" . implode(", ", $conditions) . ")";
1237 } elseif ($key === 'tabhelp') {
1238 $helpItems = array();
1239 foreach ($value as $helpValue) {
1240 $helpItems[] = "array('code' => \$langs->trans('".$helpValue['code']."'), 'field2' => 'field2tooltip')";
1241 }
1242 $dicData .= "array(" . implode(",", $helpItems) . ")";
1243 } else {
1244 if (is_array($value)) {
1245 $dicData .= "array(" . implode(
1246 ",",
1247 array_map(
1252 static function ($val) {
1253 return "'$val'";
1254 },
1255 $value
1256 )
1257 ) . ")";
1258 } else {
1259 $dicData .= "'$value'";
1260 }
1261 }
1262 $dicData .= ",\n";
1263 }
1264 $dicData .= (!$isEmpty ? "\t\t);" : '');
1265
1266 $stringDic = getFromFile($file, '/* BEGIN MODULEBUILDER DICTIONARIES */', '/* END MODULEBUILDER DICTIONARIES */');
1267 $writeInfile = dolReplaceInFile($file, array($stringDic => $dicData."\n"));
1268
1269 return $writeInfile;
1270}
1271
1284function createNewDictionnary($modulename, $file, $namedic, $dictionnaires = null)
1285{
1286 global $db, $langs;
1287
1288 if (empty($namedic)) {
1289 setEventMessages($langs->trans("ErrorEmptyNameDic"), null, 'errors');
1290 return -1;
1291 }
1292 if (!file_exists($file)) {
1293 return -1;
1294 }
1295 $modulename = dol_strtolower($modulename);
1296
1297 if (empty($dictionnaires)) {
1298 $dictionnaires = array('langs' => '', 'tabname' => array(), 'tablib' => array(), 'tabsql' => array(), 'tabsqlsort' => array(), 'tabfield' => array(), 'tabfieldvalue' => array(), 'tabfieldinsert' => array(), 'tabrowid' => array(), 'tabcond' => array(), 'tabhelp' => array());
1299 }
1300
1301 $columns = array(
1302 'rowid' => array('type' => 'integer', 'value' => 11, 'extra' => 'AUTO_INCREMENT'),
1303 'code' => array('type' => 'varchar', 'value' => 255, 'null' => 'NOT NULL'),
1304 'label' => array('type' => 'varchar', 'value' => 255, 'null' => 'NOT NULL'),
1305 'position' => array('type' => 'integer', 'value' => 11, 'null' => 'NULL'),
1306 'use_default' => array('type' => 'varchar', 'value' => 11, 'default' => '1'),
1307 'active' => array('type' => 'integer', 'value' => 3)
1308 );
1309
1310 $primaryKey = 'rowid';
1311 foreach ($columns as $key => $value) {
1312 if ($key === 'rowid') {
1313 $primaryKey = 'rowid';
1314 break;
1315 }
1316 if (!array_key_exists('rowid', $columns)) {
1317 $primaryKey = array_key_first($columns);
1318 break;
1319 }
1320 }
1321
1322 // check if tablename exist in Database and create it if not
1323 $checkTable = $db->DDLDescTable(MAIN_DB_PREFIX.dol_strtolower($namedic));
1324 if ($checkTable && $db->num_rows($checkTable) > 0) {
1325 setEventMessages($langs->trans("ErrorTableExist", $namedic), null, 'errors');
1326 return -1;
1327 } else {
1328 $_results = $db->DDLCreateTable(MAIN_DB_PREFIX.dol_strtolower($namedic), $columns, $primaryKey, "");
1329 if ($_results < 0) {
1330 dol_print_error($db);
1331 $langs->load("errors");
1332 setEventMessages($langs->trans("ErrorTableNotFound", $namedic), null, 'errors');
1333 }
1334 }
1335
1336 // rewrite dictionary if
1337 $dictionnaires['langs'] = $modulename.'@'.$modulename;
1338 $dictionnaires['tabname'][] = dol_strtolower($namedic);
1339 $dictionnaires['tablib'][] = dol_ucfirst(dol_substr($namedic, 2));
1340 $dictionnaires['tabsql'][] = 'SELECT t.rowid as rowid, t.code, t.label, t.active FROM '.MAIN_DB_PREFIX.dol_strtolower($namedic).' as t';
1341 $dictionnaires['tabsqlsort'][] = (array_key_exists('label', $columns) ? 'label ASC' : '');
1342 $dictionnaires['tabfield'][] = (array_key_exists('code', $columns) && array_key_exists('label', $columns) ? 'code,label' : '');
1343 $dictionnaires['tabfieldvalue'][] = (array_key_exists('code', $columns) && array_key_exists('label', $columns) ? 'code,label' : '');
1344 $dictionnaires['tabfieldinsert'][] = (array_key_exists('code', $columns) && array_key_exists('label', $columns) ? 'code,label' : '');
1345 $dictionnaires['tabrowid'][] = $primaryKey;
1346 $dictionnaires['tabcond'][] = isModEnabled('$modulename'); // @phan-suppress-current-line UnknownModuleName
1347 $dictionnaires['tabhelp'][] = (array_key_exists('code', $columns) ? array('code' => $langs->trans('CodeTooltipHelp'), 'field2' => 'field2tooltip') : '');
1348
1349 // Build the dictionary string
1350 $writeInfile = updateDictionaryInFile($modulename, $file, $dictionnaires);
1351 if ($writeInfile > 0) {
1352 setEventMessages($langs->trans("DictionariesCreated", dol_ucfirst(dol_substr($namedic, 2))), null);
1353 }
1354
1355 return -1;
1356}
1357
1365function writeApiUrlsInDoc($file_api, $file_doc)
1366{
1367 $error = 0;
1368 if (!dol_is_file($file_api) || !dol_is_file($file_doc)) {
1369 $error++;
1370 }
1371 $string = getFromFile($file_api, '/*begin methods CRUD*/', '/*end methods CRUD*/');
1372 $extractUrls = explode("\n", $string);
1373
1374 // extract urls from file
1375 $urlValues = array();
1376 foreach ($extractUrls as $key => $line) {
1377 $lineWithoutTabsSpaces = preg_replace('/^[\t\s]+/', '', $line);
1378 if (strpos($lineWithoutTabsSpaces, '* @url') === 0) {
1379 $urlValue = trim(dol_substr($lineWithoutTabsSpaces, dol_strlen('* @url')));
1380 $urlValues[] = $urlValue;
1381 }
1382 }
1383
1384 // get urls by object
1385 $str = $_SERVER['HTTP_HOST'].'/api/index.php/';
1386 $groupedUrls = array();
1387 foreach ($urlValues as $url) {
1388 if (preg_match('/(?:GET|POST|PUT|DELETE) (\w+)s/', $url, $matches)) {
1389 $objectName = $matches[1];
1390 $url = $str.trim(strstr($url, ' '));
1391 $groupedUrls[$objectName][] = $url;
1392 }
1393 }
1394 if (empty($groupedUrls)) {
1395 $error++;
1396 }
1397
1398 // build format asciidoc for urls in table
1399 if (!$error) {
1400 $asciiDocTable = "[options=\"header\"]\n|===\n|Object | URLs\n"; // phpcs:ignore
1401 foreach ($groupedUrls as $objectName => $urls) {
1402 $urlsList = implode(" +\n*", $urls);
1403 $asciiDocTable .= "|$objectName | \n*$urlsList +\n";
1404 }
1405 $asciiDocTable .= "|===\n";
1406 $file_write = dolReplaceInFile($file_doc, array('__API_DOC__' => '__API_DOC__'."\n".$asciiDocTable));
1407 if ($file_write < 0) {
1408 return -1;
1409 }
1410 return 1;
1411 }
1412 return -1;
1413}
1414
1415
1422function countItemsInDirectory($path, $type = 1)
1423{
1424 if (!dol_is_dir($path)) {
1425 return false;
1426 }
1427
1428 $allFilesAndDirs = scandir($path);
1429 $count = 0;
1430
1431 foreach ($allFilesAndDirs as $item) {
1432 if ($item != '.' && $item != '..') {
1433 if ($type == 1 && dol_is_file($path . DIRECTORY_SEPARATOR . $item) && strpos($item, '.back') === false) {
1434 $count++;
1435 } elseif ($type == 2 && dol_is_dir($path . DIRECTORY_SEPARATOR . $item)) {
1436 $count++;
1437 }
1438 }
1439 }
1440 return $count;
1441}
1442
1451{
1452 return array(
1453 'contact' => array('file' => 'myobject_contact.php', 'var' => 'showtabofpagecontact', 'marker' => 'CONTACT', 'label' => 'Contacts'),
1454 'note' => array('file' => 'myobject_note.php', 'var' => 'showtabofpagenote', 'marker' => 'NOTE', 'label' => 'Notes'),
1455 'document' => array('file' => 'myobject_document.php', 'var' => 'showtabofpagedocument', 'marker' => 'DOCUMENT', 'label' => 'Documents'),
1456 'agenda' => array('file' => 'myobject_agenda.php', 'var' => 'showtabofpageagenda', 'marker' => 'AGENDA', 'label' => 'Events'),
1457 );
1458}
1459
1472{
1473 return array(
1474 'sendmail' => array('marker' => 'SENDMAIL', 'label' => 'SendMail', 'default' => 1, 'mode' => 'remove'),
1475 'clone' => array('marker' => 'CLONE', 'label' => 'ToClone', 'default' => 1, 'mode' => 'remove'),
1476 'validate' => array('marker' => 'VALIDATE', 'label' => 'Validate', 'default' => 1, 'mode' => 'remove'),
1477 'setdraft' => array('marker' => 'SETDRAFT', 'label' => 'SetToDraft', 'default' => 1, 'mode' => 'remove'),
1478 'statuschange' => array('marker' => 'STATUSCHANGE', 'label' => 'CancelAndReopen', 'default' => 0, 'mode' => 'activate'),
1479 'delete' => array('marker' => 'DELETE', 'label' => 'Delete', 'default' => 1, 'mode' => 'remove'),
1480 );
1481}
1482
1491{
1492 $defaults = array();
1493 foreach ($map as $key => $meta) {
1494 if (!isset($meta['default']) || !empty($meta['default'])) {
1495 $defaults[] = $key;
1496 }
1497 }
1498 return $defaults;
1499}
1500
1510{
1511 $quotedmarker = preg_quote($marker, '/');
1512 // The trailing \R must not be a \s*: that would also eat the indentation of the next line
1513 return '/\h*\/\/ BEGIN MODULEBUILDER ACTION '.$quotedmarker.'.*?\/\/ END MODULEBUILDER ACTION '.$quotedmarker.'\h*\R(?:\h*\R)?/s';
1514}
1515
1524{
1525 $quotedmarker = preg_quote($marker, '/');
1526 // \R and not \n, to stay aligned with getModuleBuilderCardActionBlockPattern()
1527 return array(
1528 '/\h*\/\* BEGIN COMMENTED '.$quotedmarker.'\h*\R/',
1529 '/\h*END COMMENTED '.$quotedmarker.'\h*\*\/\h*\R/',
1530 );
1531}
1532
1542{
1543 return '/\/\/BEGIN MODULEBUILDER LINES.*?\/\/END MODULEBUILDER LINES\s*/s';
1544}
1545
1554function filterEnabledKeys($requested, $map)
1555{
1556 $valid = array();
1557 if (!is_array($requested) || empty($requested)) {
1558 return $valid;
1559 }
1560 foreach (array_keys($map) as $key) {
1561 if (in_array($key, $requested, true)) {
1562 $valid[] = $key;
1563 }
1564 }
1565 return $valid;
1566}
1567
1576function filterEnabledTabs($requested, $map)
1577{
1578 return filterEnabledKeys($requested, $map);
1579}
1580
1591function dolReplaceInFilePreservingModuleBuilderMarkers($file, $arrayreplacement)
1592{
1593 if (!file_exists($file)) {
1594 return -1;
1595 }
1596 $content = file_get_contents($file);
1597 if ($content === false) {
1598 return -1;
1599 }
1600
1601 // Hide every "/* BEGIN|END MODULEBUILDER ... */" marker behind a sentinel before substituting
1602 $foundmarkers = array();
1603 preg_match_all('/\/\*\s*(?:BEGIN|END) MODULEBUILDER [^*]*\*\//', $content, $foundmarkers);
1604 $sentinels = array();
1605 foreach (array_values(array_unique($foundmarkers[0])) as $index => $marker) {
1606 $key = "\0MODULEBUILDERMARKER".$index."\0";
1607 $sentinels[$key] = $marker;
1608 $content = str_replace($marker, $key, $content);
1609 }
1610
1611 $content = str_replace(array_keys($arrayreplacement), array_values($arrayreplacement), $content);
1612
1613 // Restore the protected markers untouched
1614 if (!empty($sentinels)) {
1615 $content = strtr($content, $sentinels);
1616 }
1617
1618 if (file_put_contents($file, $content) === false) {
1619 return -1;
1620 }
1621 return 1;
1622}
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
Syncs rights straight into the mod<Module>.class.php descriptor file.
Immutable value object holding all case variants of a module/object pair.
static fromFile(string $file)
Read a descriptor and locate its permissions block.
static forObjectDeletion(string $module, string $descriptorFile, array $permissions, string $objectName)
Drop every right attached to an object being deleted.
static forRightDeletion(string $module, string $descriptorFile, array $permissions, int $rightKey)
Remove one right, addressed by its index in the rights array.
static forRightUpdate(string $module, string $descriptorFile, array $permissions, int $rightKey, string $objectName, string $label, string $crud)
Replace one right, addressed by its index in the rights array.
static forRightAddition(string $module, string $descriptorFile, array $permissions, string $objectName, string $label, string $crud)
Append one right to the descriptor.
static forObjectCreation(string $module, string $descriptorFile, array $permissions, string $objectName)
Declare the three CRUD rights of a newly generated object.
if(! $sortfield) if(! $sortorder) $module
Definition list.php:193
dol_copy($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=0)
Copy a file to another file.
dol_is_file($pathoffile)
Return if path is a file.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:65
dol_is_dir($folder)
Test if filename is a directory.
dolReplaceInFile($srcfile, $arrayreplacement, $destfile='', $newmask='0', $indexdatabase=0, $arrayreplacementisregex=0)
Make replacement of strings into a file.
dol_now($mode='gmt')
Return date for now.
dol_ucfirst($string, $encoding="UTF-8")
Convert first character of the first word of a string to upper.
dol_strtolower($string, $encoding="UTF-8")
Convert a string to lower.
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
dolChmod($filepath, $newmask='')
Change mod of a file.
dol_escape_php($stringtoescape, $stringforquotes=2)
Returns text escaped for inclusion into a php string, build with double quotes " or '.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
dol_strtoupper($string, $encoding="UTF-8")
Convert a string to upper.
dol_substr($string, $start, $length=null, $stringencoding='', $trunconbytes=0)
Make a substring.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
getModuleBuilderDefaultEnabledKeys($map)
Return the keys enabled by default in a ModuleBuilder generation map.
updateDictionaryInFile($module, $file, $dicts)
Updates a dictionary in a module descriptor file.
deletePropsAndPermsFromDoc($file, $objectname)
Delete property and permissions from documentation ascii file if we delete an object.
filterEnabledTabs($requested, $map)
Filter a list of requested tab keys against the known optional tabs map.
getModuleBuilderCardActionUncommentPatterns($marker)
Build the regular expressions removing the comment fence of a card action generated in 'activate' mod...
createNewDictionnary($modulename, $file, $namedic, $dictionnaires=null)
Create a new dictionary table.
filterEnabledKeys($requested, $map)
Filter a list of requested keys against a known map of generable elements.
countItemsInDirectory($path, $type=1)
count directories or files in modulebuilder folder
getModuleBuilderCardActionBlockPattern($marker)
Build the regular expression matching every block of a card action in the object card template.
writePermsInAsciiDoc($file, $destfile)
Write all permissions of each object in AsciiDoc format.
reWriteAllMenus($file, $menus, $menuWantTo, $key, $action)
parsePropertyString($string)
Converts a formatted properties string into an associative array.
rebuildObjectSql($destdir, $module, $objectname, $newmask, $readdir='', $object=null, $moduletype='external')
Save data into a memory area shared by all users, all sessions on server.
writeApiUrlsInDoc($file_api, $file_doc)
Generate Urls and add them to documentation module.
getFromFile($file, $start, $end, $excludestart='', $includese=0)
Search a string and return all lines needed from file.
dolGetListOfObjectClasses($destdir)
Get list of existing objects from a directory.
deletePerms($file)
Delete all permissions declared in the permissions section of a module descriptor.
getModuleBuilderLinesBlockPattern()
Return the regular expression matching a block of object lines code in the templates,...
writePropsInAsciiDoc($file, $objectname, $destfile)
Write all properties of the object in AsciiDoc format.
dolModuleBuilderSqlIdentifier(string $prefix, string $module, string $table, string $key)
Build the name of an index or of a foreign key constraint for a table generated by the module builder...
removeObjectFromApiFile($file, $objects, $objectname)
Remove Object variables and methods from API_Module File.
getModuleBuilderObjectCardActions()
Return the map of optional action buttons that can be generated on the card page of a ModuleBuilder o...
dolReplaceInFilePreservingModuleBuilderMarkers($file, $arrayreplacement)
Apply substitutions to a module descriptor file while preserving the MODULEBUILDER comment markers.
rebuildObjectClass($destdir, $module, $objectname, $newmask, $readdir='', $addfieldentry=array(), $delfieldentry='')
Regenerate files .class.php.
reWriteAllPermissions($file, $permissions, $key, $right, $objectname, $module, $action)
Rewriting all permissions after any actions.
checkExistComment($file, $number)
Function to check if comment BEGIN and END exists in modMyModule class.
getModuleBuilderObjectTabs()
Return the map of optional tabs that can be generated for a ModuleBuilder object.
addObjectsToApiFile($srcfile, $file, $objects, $modulename)
Add Object in ModuleApi File.