dolibarr 24.0.0-beta
modules_boxes.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2004-2013 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
4 * Copyright (C) 2014 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
5 * Copyright (C) 2015-2025 Frédéric France <frederic.france@free.fr>
6 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 * or see https://www.gnu.org/
21 */
22
35class ModeleBoxes // Can't be abstract as it is instantiated to build "empty" boxes
36{
40 public $db;
41
47 public $version;
48
52 public $param;
53
57 public $info_box_head = array();
58
62 public $info_box_contents = array();
63
67 public $error = '';
68
72 public $max = 5;
73
77 public $enabled = 1;
78
82 public $hidden = false;
83
87 public $rowid;
88
93 public $id;
94
98 public $position;
99
103 public $box_order;
104
108 public $fk_user;
109
113 public $sourcefile;
114
118 public $class;
119
123 public $box_id;
124
128 public $lang;
129
133 public $boxcode;
134
138 public $note;
139
143 public $widgettype = '';
144
145
147
152 public $boximg;
156 public $boxlabel;
160 public $depends;
164 public $urltoaddentry;
168 public $msgNoRecords = 'NoRecordFound';
169
170
171
178 public function __construct($db, $param = '') // @phpstan-ignore constructor.unusedParameter
179 {
180 $this->db = $db;
181 }
182
183
184
191 public function loadBox($max = 5)
192 {
193 // Must be implemented in derived classes
194 $msg = get_class($this)."::".__FUNCTION__." not implemented";
195 dol_syslog($msg, LOG_ERR);
196 }
197
198
204 public function error()
205 {
206 return $this->error;
207 }
208
209
217 public function fetch($rowid)
218 {
219 global $conf;
220
221 // Recupere liste des boites d'un user si ce dernier a sa propre liste
222 $sql = "SELECT b.rowid as id, b.box_id, b.position, b.box_order, b.fk_user";
223 $sql .= " FROM ".MAIN_DB_PREFIX."boxes as b";
224 $sql .= " WHERE b.entity = ".$conf->entity;
225 $sql .= " AND b.rowid = ".((int) $rowid);
226
227 dol_syslog(get_class($this)."::fetch rowid=".((int) $rowid));
228
229 $resql = $this->db->query($sql);
230 if ($resql) {
231 $obj = $this->db->fetch_object($resql);
232 if ($obj) {
233 $this->id = $obj->id;
234 $this->rowid = $obj->id; // For backward compatibility
235 $this->box_id = $obj->box_id;
236 $this->position = $obj->position;
237 $this->box_order = $obj->box_order;
238 $this->fk_user = $obj->fk_user;
239 return 1;
240 } else {
241 return -1;
242 }
243 } else {
244 return -1;
245 }
246 }
247
256 public function showBox($head = null, $contents = null, $nooutput = 0)
257 {
258 global $langs, $user, $conf;
259
260 if (!empty($this->hidden)) {
261 return "\n<!-- Box ".get_class($this)." hidden -->\n"; // Nothing done if hidden (for example when user has no permission)
262 }
263
264 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
265
266 $MAXLENGTHBOX = 0; // When set to 0: no length limit
267
268 $cachetime = getDolGlobalInt('MAIN_ACTIVATE_FILECACHE_DELAY', 900); // 900 : 15mn
269 $cachedir = DOL_DATA_ROOT.'/users/temp/widgets';
270 $fileid = get_class($this).'id-'.$this->box_id.'-e'.$conf->entity.'-u'.$user->id.'-s'.$user->socid.'.cache';
271 $filename = '/box-'.$fileid;
272 $refresh = dol_cache_refresh($cachedir, $filename, $cachetime);
273 $out = '';
274
275 if ($contents === null) {
276 $contents = array();
277 }
278
279 if ($refresh) {
280 dol_syslog(get_class($this).'::showBox');
281
282 // Define nbcol and nblines of the box to show
283 $nbcol = 0;
284 if (isset($contents[0])) {
285 $nbcol = count($contents[0]);
286 }
287 $nblines = count($contents);
288
289 $out .= "\n<!-- Box ".get_class($this)." start -->\n";
290
291 $out .= '<div class="box divboxtable boxdraggable" id="boxto_'.$this->box_id.'">'."\n";
292 if (!empty($head['text']) || !empty($head['sublink']) || !empty($head['subpicto']) || $nblines) {
293 $out .= '<table summary="boxtable'.$this->box_id.'" class="noborder boxtable centpercent">'."\n";
294 }
295
296 // Show box title
297 if (!empty($head['text']) || !empty($head['sublink']) || !empty($head['subpicto'])) {
298 $s = '';
299 if (!empty($head['text']) && !is_array($head['text'])) {
300 $s = dol_trunc($head['text'], isset($head['limit']) ? $head['limit'] : $MAXLENGTHBOX);
301 }
302
303 $out .= '<tr class="liste_titre box_titre">';
304 $out .= '<th';
305 if (!empty($head['nbcol'])) {
306 $nbcol = $head['nbcol'];
307 }
308 if ($nbcol > 1) {
309 $out .= ' colspan="'.$nbcol.'"';
310 }
311 $out .= ' title="'.dolPrintHTMLForAttribute($s).'"';
312 $out .= '>';
313 if (!empty($conf->use_javascript_ajax)) {
314 $out .= '<div class="tdoverflowmax400 maxwidth250onsmartphone float">';
315 }
316 if (!empty($head['text']) && !is_array($head['text'])) {
317 $out .= $s;
318 }
319 if (!empty($conf->use_javascript_ajax)) {
320 $out .= '</div>';
321 }
322
323 if (!empty($conf->use_javascript_ajax)) {
324 $sublink = '';
325 if (!empty($head['sublink'])) {
326 $sublink .= '<a href="'.$head['sublink'].'"'.(empty($head['target']) ? '' : ' target="'.$head['target'].'"').'>';
327 }
328 if (!empty($head['subpicto']) && !is_array($head['subtext']) && !is_array($head['subpicto'])) {
329 $sublink .= img_picto($head['subtext'], $head['subpicto'], 'class="opacitymedium marginleftonly '.(empty($head['subclass']) ? '' : $head['subclass']).'" id="idsubimg'.$this->boxcode.'"');
330 }
331 if (!empty($head['sublink'])) {
332 $sublink .= '</a>';
333 }
334
335 $out .= '<div class="nocellnopadd boxclose floatright nowraponall">';
336 $out .= $sublink;
337 // The image must have the class 'boxhandle' because it's value used in DOM draggable objects to define the area used to catch the full object
338 $out .= img_picto($langs->trans("MoveBox", $this->box_id), 'grip_title', 'class="opacitymedium boxhandle hideonsmartphone cursormove marginleftonly"');
339 $out .= img_picto($langs->trans("CloseBox", $this->box_id), 'close_title', 'class="opacitymedium boxclose cursorpointer marginleftonly" rel="x:y" id="imgclose'.$this->box_id.'"');
340 $label = $head['text'];
341 if (!empty($head['graph'])) {
342 $label .= ' <span class="opacitymedium fas fa-chart-bar"></span>';
343 }
344 $out .= '<input type="hidden" id="boxlabelentry'.$this->box_id.'" value="'.dolPrintHTMLForAttribute($label).'">';
345 $out .= '</div>';
346 }
347
348 $out .= "</th>";
349 $out .= "</tr>\n";
350 }
351
352 // Show box lines
353 if ($nblines) {
354 // Loop on each record
355 foreach (array_keys($contents) as $i) {
356 if (isset($contents[$i]) && is_array($contents[$i])) {
357 // TR
358 if (isset($contents[$i][0]['tr'])) {
359 $out .= '<tr '.$contents[$i][0]['tr'].'>';
360 } else {
361 $out .= '<tr class="oddeven">';
362 }
363
364 // Loop on each TD
365 $nbcolthisline = count($contents[$i]);
366 foreach (array_keys($contents[$i]) as $j) {
367 // Define tdparam
368 $tdparam = '';
369 if (!empty($contents[$i][$j]['td'])) {
370 $tdparam .= ' '.$contents[$i][$j]['td'];
371 }
372
373 $text = isset($contents[$i][$j]['text']) ? $contents[$i][$j]['text'] : '';
374 $textwithnotags = preg_replace('/<([^>]+)>/i', '', $text);
375 $text2 = isset($contents[$i][$j]['text2']) ? $contents[$i][$j]['text2'] : '';
376 $text2withnotags = preg_replace('/<([^>]+)>/i', '', $text2);
377
378 $textnoformat = isset($contents[$i][$j]['textnoformat']) ? $contents[$i][$j]['textnoformat'] : '';
379 //$out.= "xxx $textwithnotags y";
380 if (empty($contents[$i][$j]['tooltip'])) {
381 $contents[$i][$j]['tooltip'] = "";
382 }
383 $tooltip = isset($contents[$i][$j]['tooltip']) ? $contents[$i][$j]['tooltip'] : '';
384
385 $out .= '<td'.$tdparam.'>'."\n";
386
387 // Url
388 if (!empty($contents[$i][$j]['url']) && empty($contents[$i][$j]['logo'])) {
389 $out .= '<a href="'.$contents[$i][$j]['url'].'"';
390 if (!empty($tooltip)) {
391 $out .= ' title="'.dolPrintHTMLForAttribute($langs->trans("Show").' '.$tooltip, 1).'" class="classfortooltip"';
392 }
393 //$out.= ' alt="'.$textwithnotags.'"'; // Pas de alt sur un "<a href>"
394 $out .= isset($contents[$i][$j]['target']) ? ' target="'.$contents[$i][$j]['target'].'"' : '';
395 $out .= '>';
396 }
397
398 // Logo
399 if (!empty($contents[$i][$j]['logo'])) {
400 $logo = preg_replace("/^object_/i", "", $contents[$i][$j]['logo']);
401 $out .= '<a href="'.$contents[$i][$j]['url'].'">';
402 $out .= img_object($langs->trans("Show").' '.$tooltip, $logo, 'class="classfortooltip"');
403 }
404
405 $maxlength = $MAXLENGTHBOX;
406 if (isset($contents[$i][$j]['maxlength'])) {
407 $maxlength = $contents[$i][$j]['maxlength'];
408 }
409
410 if ($maxlength) {
411 $textwithnotags = dol_trunc($textwithnotags, $maxlength);
412 }
413 if (preg_match('/^<(img|div|span)/i', $text) || !empty($contents[$i][$j]['asis'])) {
414 $out .= $text; // show text with no html cleaning
415 } else {
416 $out .= $textwithnotags; // show text with html cleaning
417 }
418
419 // End Url
420 if (!empty($contents[$i][$j]['url'])) {
421 $out .= '</a>';
422 }
423
424 if (preg_match('/^<(img|div|span)/i', $text2) || !empty($contents[$i][$j]['asis2'])) {
425 $out .= $text2; // show text with no html cleaning
426 } else {
427 $out .= $text2withnotags; // show text with html cleaning
428 }
429
430 if (!empty($textnoformat)) {
431 $out .= "\n".$textnoformat."\n";
432 }
433
434 $out .= "</td>\n";
435 }
436
437 $out .= "</tr>\n";
438 }
439 }
440 } else {
441 if (!empty($head['text']) || !empty($head['sublink']) || !empty($head['subpicto']) || $nblines) {
442 $out .= '<tr><td colspan="2" class="center"><span class="opacitymedium">'.$langs->trans($this->msgNoRecords).' </span>';
443
444 // Check if $urltoaddentry is defined for the widget
445 if (!empty($this->urltoaddentry)) {
446 $out .= '<a href="'.$this->urltoaddentry.'">'.img_picto($langs->trans("New"), 'add', 'pictofixedwidth').'</a>';
447 }
448
449 $out .= '</td></tr>';
450 }
451 }
452
453 if (!empty($head['text']) || !empty($head['sublink']) || !empty($head['subpicto']) || $nblines) {
454 $out .= "</table>\n";
455 }
456
457 // If invisible box with no contents
458 if (empty($head['text']) && empty($head['sublink']) && empty($head['subpicto']) && !$nblines) {
459 $out .= "<br>\n";
460 }
461
462 $out .= "</div>\n";
463
464 $out .= "<!-- Box ".get_class($this)." end -->\n\n";
465 if (getDolGlobalString('MAIN_ACTIVATE_FILECACHE')) {
466 dol_filecache($cachedir, $filename, $out);
467 }
468 } else {
469 dol_syslog(get_class($this).'::showBoxCached');
470 $out = "<!-- Box ".get_class($this)." from cache -->";
471 $out .= dol_readcachefile($cachedir, $filename);
472 }
473
474 if ($nooutput) {
475 return $out;
476 } else {
477 print $out;
478 }
479
480 return '';
481 }
482
483
491 public static function getWidgetsList($forcedirwidget = null)
492 {
493 global $langs, $db;
494
495 $files = array();
496 $fullpath = array();
497 $relpath = array();
498 $iscoreorexternal = array();
499 $modules = array();
500 $orders = array();
501 $i = 0;
502
503 //$dirwidget=array_merge(array('/core/boxes/'), $conf->modules_parts['widgets']);
504 $dirwidget = array('/core/boxes/'); // $conf->modules_parts['widgets'] is not required
505 if (is_array($forcedirwidget)) {
506 $dirwidget = $forcedirwidget;
507 }
508
509 foreach ($dirwidget as $reldir) {
510 $dir = dol_buildpath($reldir, 0);
511 $newdir = dol_osencode($dir);
512
513 // Check if directory exists (we do not use dol_is_dir to avoid loading files.lib.php at each call)
514 if (!is_dir($newdir)) {
515 continue;
516 }
517
518 $handle = opendir($newdir);
519 if (is_resource($handle)) {
520 while (($file = readdir($handle)) !== false) {
521 $reg = array();
522 if (is_readable($newdir.'/'.$file) && preg_match('/^(.+)\.php/', $file, $reg)) {
523 if (preg_match('/\.back$/', $file) || preg_match('/^(.+)\.disabled\.php/', $file)) {
524 continue;
525 }
526
527 $part1 = $reg[1];
528
529 $modName = ucfirst($reg[1]);
530 //print "file=$file"; print "modName=$modName"; exit;
531 if (in_array($modName, $modules)) {
532 $langs->load("errors");
533 print '<div class="error">'.$langs->trans("Error").' : '.$langs->trans("ErrorDuplicateWidget", $modName, "").'</div>';
534 } else {
535 try {
536 include_once $newdir.'/'.$file;
537 } catch (Exception $e) {
538 print $e->getMessage();
539 }
540 }
541
542 $files[$i] = $file;
543 $fullpath[$i] = $dir.'/'.$file;
544 $relpath[$i] = preg_replace('/^\//', '', $reldir).'/'.$file;
545 $iscoreorexternal[$i] = ($reldir == '/core/boxes/' ? 'internal' : 'external');
546 $modules[$i] = $modName;
547 $orders[$i] = $part1; // Set sort criteria value
548
549 $i++;
550 }
551 }
552 closedir($handle);
553 }
554 }
555 //echo "<pre>";print_r($modules);echo "</pre>";
556
557 asort($orders);
558
559 $widget = array();
560 $j = 0;
561
562 // Loop on each widget
563 foreach ($orders as $key => $value) {
564 $modName = $modules[$key];
565 if (empty($modName)) {
566 continue;
567 }
568
569 if (!class_exists($modName)) {
570 print 'Error: A widget file was found but its class "'.$modName.'" was not found.'."<br>\n";
571 continue;
572 }
573
574 $objMod = new $modName($db);
575 '@phan-var-force ModeleBoxes $objMod';
576 if (is_object($objMod)) {
577 // Define disabledbyname and disabledbymodule
578 $disabledbyname = 0;
579 $disabledbymodule = 0; // TODO Set to 2 if module is not enabled
580 $module = '';
581
582 // Check if widget file is disabled by name
583 if (preg_match('/NORUN$/i', $files[$key])) {
584 $disabledbyname = 1;
585 }
586
587 // We set info of modules @phan-suppress-next-line PhanUndeclaredProperty
588 $widget[$j]['picto'] = ((!property_exists($objMod, 'picto') || empty($objMod->picto)) ? (empty($objMod->boximg) ? img_object('', 'generic') : $objMod->boximg) : img_object('', $objMod->picto));
589 $widget[$j]['file'] = $files[$key];
590 $widget[$j]['fullpath'] = $fullpath[$key];
591 $widget[$j]['relpath'] = $relpath[$key];
592 $widget[$j]['iscoreorexternal'] = $iscoreorexternal[$key];
593 $widget[$j]['version'] = empty($objMod->version) ? '' : $objMod->version;
594 $widget[$j]['status'] = img_picto($langs->trans("Active"), 'tick');
595 if ($disabledbyname > 0 || $disabledbymodule > 1) {
596 $widget[$j]['status'] = '';
597 }
598
599 $text = '<b>'.$langs->trans("Description").':</b><br>';
600 $text .= $objMod->boxlabel.'<br>';
601 $text .= '<br><b>'.$langs->trans("Status").':</b><br>';
602 if ($disabledbymodule == 2) {
603 $text .= $langs->trans("WidgetDisabledAsModuleDisabled", $module).'<br>';
604 }
605
606 $widget[$j]['info'] = $text;
607 }
608 $j++;
609 }
610
611 return $widget;
612 }
613}
print $object position
Definition edit.php:206
Class ModeleBoxes.
showBox($head=null, $contents=null, $nooutput=0)
Standard method to show a box (usage by boxes not mandatory, a box can still use its own showBox func...
fetch($rowid)
Load a box line from its rowid.
error()
Return last error message.
__construct($db, $param='')
Constructor.
loadBox($max=5)
Load data for box to show them later.
static getWidgetsList($forcedirwidget=null)
Return list of widget.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
dol_filecache($directory, $filename, $object)
Store object in file.
dol_readcachefile($directory, $filename)
Read object from cachefile.
dol_cache_refresh($directory, $filename, $cachetime)
Test if Refresh needed.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dolPrintHTMLForAttribute($s, $escapeonlyhtmltags=0, $allowothertags=array())
Return a string ready to be output into an HTML attribute (alt, title, data-html, ....
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.