dolibarr 25.0.0-alpha
html.lib.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2026 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2026 Frédéric France <frederic.france@free.fr>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 * or see https://www.gnu.org/
18 */
19
44function dolPrintLabel($s, $escapeonlyhtmltags = 0)
45{
46 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', $escapeonlyhtmltags, 1);
47}
48
57function dolPrintText($s)
58{
59 return dol_escape_htmltag(dol_string_nohtmltag($s, 2, 'UTF-8', 0, 0), 0, 1, '', 0, 1);
60}
61
73function dolPrintHTML($s, $allowiframe = 0, $moreallowedtags = array())
74{
75 // If text is already HTML, we want to escape only dangerous chars else we want to escape all content.
76 //$isAlreadyHTML = dol_textishtml($s);
77
78 // dol_htmlentitiesbr encode all chars except "'" if string is not already HTML, but
79 // encode only special char like accented chars but not &, <, >, ", ' if already HTML.
80 $stringWithEntitesForSpecialChar = dol_htmlentitiesbr((string) $s);
81
82 $allowedtags = 'common';
83 if (!empty($moreallowedtags)) {
84 $allowedtags .= ','.implode(',', $moreallowedtags);
85 }
86 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags($stringWithEntitesForSpecialChar, 1, 1, 1, $allowiframe, $allowedtags)), 1, 1, $allowedtags, 0, 1);
87}
88
99function dolPrintHTMLForAttribute($s, $escapeonlyhtmltags = 0, $allowothertags = array())
100{
101 $allowedtags = array('br', 'b', 'font', 'hr', 'span');
102 if (!empty($allowothertags) && is_array($allowothertags)) {
103 $allowedtags = array_merge($allowedtags, $allowothertags);
104 }
105 // The dol_htmlentitiesbr will convert simple text into html, including switching accent into HTML entities
106 // The dol_escape_htmltag will escape html tags.
107 if ($escapeonlyhtmltags) {
108 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 0, 0, 0, $allowedtags), 1, -1, '', 1, 1);
109 } else {
110 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, $allowedtags), 1, -1, '', 0, 1);
111 }
112}
113
123{
124 // The dol_htmlentitiesbr has been removed compared to dolPrintHTMLForAttribute because we know content is a HTML URL string (even if we have no way to detect it automatically)
125 // The dol_escape_htmltag will escape html chars.
126 $escapeonlyhtmltags = 1;
127 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 1, 1, 0, array()), 0, 0, '', $escapeonlyhtmltags, 1);
128}
129
139function dolPrintHTMLForTextArea($s, $allowiframe = 0)
140{
141 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
142}
143
151{
152 return htmlspecialchars($s, ENT_HTML5, 'UTF-8');
153}
154
155
172function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
173{
174 $reg = array();
175 if (preg_match('/^common([a-z,]*)/', $noescapetags, $reg)) {
176 $noescapetags = 'html,body,a,b,em,hr,i,u,ul,ol,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody,h1,h2,h3,h4,h5,h6,h7,h8,h9';
177 // Add also html5 tags
178 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
179 if (!empty($reg[1])) {
180 $noescapetags .= $reg[1];
181 }
182 }
183 if ($cleanalsojavascript) {
184 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
185 }
186
187 // escape quotes and backslashes, newlines, etc.
188 if ($escapeonlyhtmltags) {
189 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
190 } else {
191 // We make a manipulation by calling the html_entity_decode() to convert content into NON HTML UTF8 string.
192 // Because content can be or not already HTML.
193 // For example, this decode &egrave; into its UTF-8 char so string is UTF8 (but numbers entities like &#39; is not decoded).
194 // In a future, we should not need this
195
196 $tmp = (string) $stringtoescape;
197
198 // We protect the 6 special entities that we don't want to decode.
199 $tmp = str_ireplace('&lt', '__DONOTDECODELT', $tmp);
200 $tmp = str_ireplace('&gt', '__DONOTDECODEGT', $tmp);
201 $tmp = str_ireplace('&amp', '__DONOTDECODEAMP', $tmp);
202 $tmp = str_ireplace('&quot', '__DONOTDECODEQUOT', $tmp);
203 $tmp = str_ireplace('&apos', '__DONOTDECODEAPOS', $tmp);
204 $tmp = str_ireplace('&#39', '__DONOTDECODE39', $tmp);
205
206 $tmp = html_entity_decode((string) $tmp, ENT_COMPAT, 'UTF-8'); // Convert entities into UTF8
207
208 // We restore the 6 special entities that we don't want to have been decoded by previous command
209 $tmp = str_ireplace('__DONOTDECODELT', '&lt', $tmp);
210 $tmp = str_ireplace('__DONOTDECODEGT', '&gt', $tmp);
211 $tmp = str_ireplace('__DONOTDECODEAMP', '&amp', $tmp);
212 $tmp = str_ireplace('__DONOTDECODEQUOT', '&quot', $tmp);
213 $tmp = str_ireplace('__DONOTDECODEAPOS', '&apos', $tmp);
214 $tmp = str_ireplace('__DONOTDECODE39', '&#39', $tmp);
215
216 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE__', $tmp); // HTML 4
217 }
218 if (!$keepb) {
219 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
220 }
221 if (!$keepn) {
222 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
223 } elseif ($keepn == -1) {
224 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
225 }
226
227 if ($escapeonlyhtmltags) {
228 $tmp = htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
229 return $tmp;
230 } else {
231 // Now we protect all the tags we want to keep
232 $tmparrayoftags = array();
233 if ($noescapetags) {
234 $tmparrayoftags = explode(',', $noescapetags);
235 }
236
237 if (count($tmparrayoftags)) {
238 // Now we will protect tags (defined into $tmparrayoftags) that we want to keep untouched
239
240 $reg = array();
241 // Remove reserved keywords. They are forbidden in a source string
242 $tmp = str_ireplace(array('__DOUBLEQUOTE', '__BEGINTAGTOREPLACE', '__ENDTAGTOREPLACE', '__BEGINENDTAGTOREPLACE'), '', $tmp);
243
244 foreach ($tmparrayoftags as $tagtoreplace) {
245 // For case of tag without attributes '<abc>', '</abc>', '<abc />', we protect them to avoid transformation by htmlentities() later
246 $tmp = preg_replace('/<' . preg_quote($tagtoreplace, '/') . '>/', '__BEGINTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
247 $tmp = str_ireplace('</' . $tagtoreplace . '>', '__ENDTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
248 $tmp = preg_replace('/<' . preg_quote($tagtoreplace, '/') . ' \/>/', '__BEGINENDTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
249
250 // For case of tag with attributes.
251 // All the occurrences are protected in a single pass: the replacement string contains no '<', so it
252 // can never build a new tag to protect (a loop replacing one distinct attribute string per round was
253 // rescanning the whole content for each of them, so the cost was quadratic on large contents).
254 $tmp = preg_replace_callback(
255 '/<'.preg_quote($tagtoreplace, '/').'(\s+)([^>]+)>/',
260 static function ($reg) use ($tagtoreplace) {
261 // We want to protect the attribute part ... in '<xxx ...>' to avoid transformation by htmlentities() later
262 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[2]); // We must never have [ ] inside the attribute string
263 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE__', $tmpattributes);
264 $tmpattributes = preg_replace('/[^a-z0-9_%,\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
265 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
266 return '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__';
267 },
268 $tmp
269 ) ?? $tmp;
270 }
271
272 $tmp = str_ireplace('&amp', '__ANDNOSEMICOLON__', $tmp);
273 $tmp = str_ireplace('&quot', '__DOUBLEQUOTENOSEMICOLON__', $tmp);
274 $tmp = str_ireplace('&lt', '__LESSTHAN__', $tmp);
275 $tmp = str_ireplace('&gt', '__GREATERTHAN__', $tmp);
276 }
277
278 // Warning: htmlentities encode all special chars that remains (except "'" with ENT_COMPAT).
279 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
280
281 //print $result;
282
283 if (count($tmparrayoftags)) {
284 // Restore protected tags
285 foreach ($tmparrayoftags as $tagtoreplace) {
286 $result = str_ireplace('__BEGINTAGTOREPLACE' . $tagtoreplace . '__', '<' . $tagtoreplace . '>', $result);
287 $result = preg_replace('/__BEGINTAGTOREPLACE' . $tagtoreplace . '\[([^\]]*)\]__/', '<' . $tagtoreplace . ' \1>', $result);
288 $result = str_ireplace('__ENDTAGTOREPLACE' . $tagtoreplace . '__', '</' . $tagtoreplace . '>', $result);
289 $result = str_ireplace('__BEGINENDTAGTOREPLACE' . $tagtoreplace . '__', '<' . $tagtoreplace . ' />', $result);
290 $result = preg_replace('/__BEGINENDTAGTOREPLACE' . $tagtoreplace . '\[([^\]]*)\]__/', '<' . $tagtoreplace . ' \1 />', $result);
291 }
292
293 $result = str_ireplace('__DOUBLEQUOTE__', '"', $result);
294
295 $result = str_ireplace('__ANDNOSEMICOLON__', '&amp', $result);
296 $result = str_ireplace('__DOUBLEQUOTENOSEMICOLON__', '&quot', $result);
297 $result = str_ireplace('__LESSTHAN__', '&lt', $result);
298 $result = str_ireplace('__GREATERTHAN__', '&gt', $result);
299 }
300
301 $result = str_ireplace('__SIMPLEQUOTE__', '&#39;', $result);
302
303 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
304
305 return $result;
306 }
307}
308
309
321function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
322{
323 global $langs, $db;
324
325 $form = new Form($db);
326
327 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
328 if (empty($templatenameforexport)) {
329 $templatenameforexport = 'website_' . $website->ref;
330 }
331
332 $out = '';
333 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="' . dol_escape_htmltag($buttonstring) . '"/>';
334
335 // for generate popup
336 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
337 $out .= 'jQuery(document).ready(function () {';
338 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
339 $out .= ' var dialogHtml = \'';
340
341 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
342 $dialogcontent .= ' <div style="margin-top: 20px;">';
343 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>' . $langs->trans("ExportSiteLabel") . '...</label><br>';
344 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
345 $dialogcontent .= ' </div>';
346 $dialogcontent .= ' <br>';
347 $dialogcontent .= ' <div style="margin-top: 20px;">';
348 $dialogcontent .= ' <strong>' . $langs->trans("ExportSiteGitLabel") . ' ' . $form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '') . '</strong><br>';
349 $dialogcontent .= ' <form action="' . dol_escape_htmltag($overwriteGitUrl) . '" method="POST">';
350 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
351 $dialogcontent .= ' <input type="hidden" name="token" value="' . newToken() . '">';
352 $dialogcontent .= ' <input type="text" autofocus name="export_path" id="export-path-' . $name . '" placeholder="' . $langs->trans('ExportPath') . '" style="width:400px " value="' . dol_escape_htmltag($templatenameforexport) . '"/><br>';
353 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
354 $dialogcontent .= ' </form>';
355 $dialogcontent .= ' </div>';
356 $dialogcontent .= ' </div>';
357
358 $out .= dol_escape_js($dialogcontent);
359
360 $out .= '\';';
361
362
363 // Add the content of the dialog to the body of the page
364 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
365 $out .= ' if ($dialog.length > 0) {
366 $dialog.remove();
367 }
368 jQuery("body").append(dialogHtml);';
369
370 // Configuration of popup
371 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
372 $out .= ' autoOpen: false,';
373 $out .= ' modal: true,';
374 $out .= ' height: 290,';
375 $out .= ' width: "40%",';
376 $out .= ' title: "' . dol_escape_js($label) . '",';
377 $out .= ' });';
378
379 // Simulate a click on the original "submit" input to export the site.
380 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
381 $out .= ' console.log("Clic on exportsite.");';
382 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
383 $out .= ' console.log("element founded:", target.length > 0);';
384 $out .= ' if (target.length > 0) { target.click(); }';
385 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
386 $out .= ' });';
387
388 // open popup
389 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
390 $out .= ' return false;';
391 $out .= ' });';
392 $out .= '});';
393 $out .= '</script>';
394
395 return $out;
396}
397
398
415function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $jsonclose = '', $accesskey = '')
416{
417 global $conf;
418
419 if (strpos($url, '?') > 0) {
420 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup=' . urlencode($name);
421 } else {
422 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup=' . urlencode($name);
423 }
424
425 if (preg_match('/^https/i', $url)) {
426 $urltoopen = $url;
427 } else {
428 $urltoopen = DOL_URL_ROOT . $url;
429 }
430
431 $out = '';
432
433 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
434 $out .= '<!-- a link for button to open url into a dialog popup -->';
435 $out .= '<a ' . ($accesskey ? ' accesskey="' . $accesskey . '"' : '') . ' class="cursorpointer reposition button_' . $name . ($morecss ? ' ' . $morecss : '') . '"' . $disabled . ' title="' . dol_escape_htmltag($label) . '"';
436 if (empty($conf->use_javascript_ajax)) {
437 $out .= ' href="' . $urltoopen . '" target="_blank"';
438 } elseif ($jsonopen) {
439 $out .= ' href="#" onclick="' . $jsonopen . '"';
440 } else {
441 $out .= ' href="#"';
442 }
443 $out .= '>' . $buttonstring . '</a>';
444
445 if (!empty($conf->use_javascript_ajax)) {
446 // Add code to open url using the popup.
447 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
448 $out .= '<div id="idfordialog' . $name . '" class="hidden">' . (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '') . '</div>';
449
450 $out .= '<!-- Add js code to open dialog popup on dialog -->';
451 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">
452 jQuery(document).ready(function () {
453 jQuery(".button_' . $name . '").click(function () {
454 console.log(\'Open popup with jQuery(...).dialog() on URL ' . dol_escape_js($urltoopen) . '\');
455 var $tmpdialog = $(\'#idfordialog' . $name . '\');
456 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog' . $name . '" style="border: 0px;" src="' . $urltoopen . '" width="100%" height="98%"></iframe>\');
457 $tmpdialog.dialog({
458 autoOpen: false,
459 modal: true,
460 height: (window.innerHeight - 150),
461 width: \'80%\',
462 title: \'' . dol_escape_js($label) . '\',
463 open: function (event, ui) {
464 console.log("open popup name=' . $name . '");
465 },
466 close: function (event, ui) {
467 console.log("Popup is closed, run jsonclose = ' . $jsonclose . '");
468 ' . (empty($jsonclose) || preg_match('/^TODO/', $jsonclose) ? '' : $jsonclose . ';') . '
469 }
470 });
471
472 $tmpdialog.dialog(\'open\');
473 return false;
474 });
475 });
476 </script>';
477 }
478 return $out;
479}
480
497function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
498{
499 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
500}
501
519function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0, $morecssdiv = '')
520{
521 global $conf, $langs, $hookmanager;
522
523 // Show title
524 $showtitle = 1;
525 if (!empty($conf->dol_optimize_smallscreen)) {
526 $showtitle = 0;
527 }
528
529 $out = "\n" . '<!-- dol_fiche_head - dol_get_fiche_head -->';
530
531 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
532 $out .= '<div class="tabs' . ($picto ? '' : ' nopaddingleft') . '" data-role="controlgroup" data-type="horizontal">' . "\n";
533 }
534
535 // Show right part
536 if ($morehtmlright) {
537 $out .= '<div class="inline-block floatright tabsElem">' . $morehtmlright . '</div>'; // Output right area first so when space is missing, text is in front of tabs and not under.
538 }
539
540 // Show tabs
541
542 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
543 $maxkey = -1;
544 if (is_array($links) && !empty($links)) {
545 $keys = array_keys($links);
546 if (count($keys)) {
547 $maxkey = max($keys);
548 }
549 }
550
551 // Show tabs
552 // if =0 we don't use the feature
553 if (empty($limittoshow)) {
554 $limittoshow = getDolGlobalInt('MAIN_MAXTABS_IN_CARD', 99);
555 }
556 if (!empty($conf->dol_optimize_smallscreen)) { // If on smartphone, we limit to 1 tab to show
557 $limittoshow = 1;
558 }
559
560 $displaytab = 0;
561 $nbintab = 0;
562 $popuptab = 0;
563 $outmore = '';
564 for ($i = 0; $i <= $maxkey; $i++) {
565 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
566 // If active tab is already present
567 if ($i >= $limittoshow) {
568 $limittoshow--;
569 }
570 }
571 }
572
573 for ($i = 0; $i <= $maxkey; $i++) {
574 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
575 $isactive = true;
576 } else {
577 $isactive = false;
578 }
579
580 if ($i < $limittoshow || $isactive) {
581 // Output entry with a visible tab
582 $out .= '<div class="inline-block tabsElem' . ($isactive ? ' tabsElemActive' : '') . ((!$isactive && getDolGlobalString('MAIN_HIDE_INACTIVETAB_ON_PRINT')) ? ' hideonprint' : '') . '"><!-- id tab = ' . (empty($links[$i][2]) ? '' : dol_escape_htmltag($links[$i][2])) . ' -->';
583
584 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
585 if (!empty($links[$i][0])) {
586 $out .= '<a class="tabimage' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">' . $links[$i][1] . '</a>' . "\n";
587 } else {
588 $out .= '<span class="tabspan">' . $links[$i][1] . '</span>' . "\n";
589 }
590 } elseif (!empty($links[$i][1])) {
591 //print "x $i $active ".$links[$i][2]." z";
592 $out .= '<div class="tab tab' . ($isactive ? 'active' : 'unactive') . '" style="margin: 0 !important">';
593
594 if (!empty($links[$i][0])) {
595 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
596 $out .= '<a' . (!empty($links[$i][2]) ? ' id="' . $links[$i][2] . '"' : '') . ' class="tab inline-block valignmiddle' . ($morecss ? ' ' . $morecss : '') . (!empty($links[$i][5]) ? ' ' . $links[$i][5] : '') . '" href="' . $links[$i][0] . '" title="' . dol_escape_htmltag($titletoshow) . '">';
597 }
598
599 if ($displaytab == 0 && $picto) {
600 $out .= img_picto($title, $picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle paddingright marginrightonlyshort');
601 }
602
603 $out .= $links[$i][1];
604 if (!empty($links[$i][0])) {
605 $out .= '</a>' . "\n";
606 }
607 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
608 $out .= '</div>';
609 }
610
611 $out .= '</div>';
612 } else {
613 // Add entry into the combo popup with the other tabs
614 if (!$popuptab) {
615 $popuptab = 1;
616 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
617 }
618 $outmore_content = '';
619
620 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
621 if (!empty($links[$i][0])) {
622 $outmore_content .= '<a class="tabimage' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">' . $links[$i][1] . '</a>' . "\n";
623 } else {
624 $outmore_content .= '<span class="tabspan">' . $links[$i][1] . '</span>' . "\n";
625 }
626 } elseif (!empty($links[$i][1])) {
627 $outmore_content .= '<a' . (!empty($links[$i][2]) ? ' id="' . $links[$i][2] . '"' : '') . ' class="wordwrap inline-block' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">';
628 $outmore_content .= preg_replace('/([a-z])\|([a-z])/i', '\\1 | \\2', $links[$i][1]); // Replace x|y with x | y to allow wrap on long composed texts.
629 $outmore_content .= '</a>' . "\n";
630 }
631 if ($outmore_content !== '') {
632 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">' . $outmore_content . '</div>';
633 }
634
635 $nbintab++;
636 }
637
638 $displaytab = $i + 1;
639 }
640 if ($popuptab) {
641 $outmore .= '</div>';
642 }
643
644 if ($popuptab) { // If there is some tabs not shown
645 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
646 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
647 $widthofpopup = 240;
648
649 $tabsname = $moretabssuffix;
650 if (empty($tabsname)) {
651 $tabsname = str_replace("@", "", $picto);
652 }
653 $out .= '<div id="moretabs' . $tabsname . '" class="inline-block tabsElem valignmiddle">';
654 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
655 $out .= '<div class="tab valignmiddle"><a href="#" class="tab moretab inline-block tabunactive valignmiddle"><span class="fa fa-angle-down"></span> <span class="opacitymedium">+' . $nbintab . '</span></a></div>'; // Do not use "reposition" class in the "More".
656 }
657 $out .= '<div id="moretabsList' . $tabsname . '" style="width: ' . $widthofpopup . 'px; position: absolute; ' . $left . ': -999em; text-align: ' . $left . '; margin:0px; padding:2px; z-index:10;">';
658 $out .= $outmore;
659 $out .= '</div>';
660 $out .= '<div></div>';
661 $out .= "</div>\n";
662
663 $out .= '<script nonce="' . getNonce() . '">';
664 $out .= "$('#moretabs" . $tabsname . "').mouseenter( function() {
665 var x = this.offsetLeft, y = this.offsetTop;
666 console.log('mouseenter " . $left . " x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
667 if ((window.innerWidth - x) < " . ($widthofpopup + 10) . ") {
668 $('#moretabsList" . $tabsname . "').css('" . $right . "','8px');
669 }
670 $('#moretabsList" . $tabsname . "').css('" . $left . "','auto');
671 });
672 ";
673 $out .= "$('#moretabs" . $tabsname . "').mouseleave( function() { console.log('mouseleave " . $left . "'); $('#moretabsList" . $tabsname . "').css('" . $left . "','-999em');});";
674 $out .= "</script>";
675 }
676
677 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
678 $out .= "</div>\n";
679 }
680
681 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3 || $notab == -4) {
682 $out .= "\n" . '<div id="dragDropAreaTabBar" class="tabBar' . ($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ((($notab == -3 || $notab == -4) ? ' noborderbottom' : '') . ($notab == -4 ? '' : ' tabBarWithBottom'))));
683 $out .= ($morecssdiv ? ' ' . $morecssdiv : '');
684 $out .= '">' . "\n";
685 }
686 if (!empty($dragdropfile)) {
687 include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
688 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
689 }
690 $parameters = array('tabname' => $active, 'out' => $out);
691 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
692 if ($reshook > 0) {
693 $out = $hookmanager->resPrint;
694 }
695
696 return $out;
697}
698
706function dol_fiche_end($notab = 0)
707{
708 print dol_get_fiche_end($notab);
709}
710
717function dol_get_fiche_end($notab = 0)
718{
719 if (!$notab || $notab == -1) {
720 return "\n</div>\n";
721 } else {
722 return '';
723 }
724}
725
745function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
746{
747 global $conf, $form, $user, $langs, $hookmanager, $action;
748
749 $error = 0;
750
751 $maxvisiblephotos = 1;
752 $showimage = 1;
753 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
754 // @phan-suppress-next-line PhanUndeclaredMethod
755 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
756 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
757 $showbarcode = 0;
758 }
759 $modulepart = 'unknown';
760
761 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
762 $modulepart = $object->element;
763 } elseif ($object->element == 'member') {
764 $modulepart = 'memberphoto';
765 } elseif ($object->element == 'user') {
766 $modulepart = 'userphoto';
767 }
768
769 if (class_exists("Imagick")) {
770 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
771 $modulepart = $object->element;
772 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
773 $modulepart = 'ficheinter';
774 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
775 $modulepart = 'contract';
776 } elseif ($object->element == 'order_supplier') {
777 $modulepart = 'supplier_order';
778 } elseif ($object->element == 'invoice_supplier') {
779 $modulepart = 'supplier_invoice';
780 }
781 }
782
783 if ($object->element == 'product') {
785 '@phan-var-force Product $object';
786 $width = 80;
787 $cssclass = 'photowithmargin photoref';
788 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
789 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
790 if ($conf->browser->layout == 'phone') {
791 $maxvisiblephotos = 1;
792 }
793 $useLinkPathPhoto = getDolGlobalInt('PRODUCT_USE_LINK_PATH_FOR_PHOTO');
794 if ($showimage || $useLinkPathPhoto) {
795 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $object->show_photos('product', $conf->product->multidir_output[$entity], 1, $maxvisiblephotos, 0, 0, 0, 0, $width, 0, '') . '</div>';
796 } else {
797 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
798 $nophoto = '';
799 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
800 } else { // Show no photo link
801 $nophoto = '/public/theme/common/nophoto.png';
802 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo' . $modulepart . ' ' . $cssclass . '" title="' . dol_escape_htmltag($langs->trans("UploadAnImageToSeeAPhotoHere", $langs->transnoentitiesnoconv("Documents"))) . '" alt="No photo" style="width: ' . $width . 'px" src="' . DOL_URL_ROOT . $nophoto . '"></div>';
803 }
804 }
805 } elseif ($object->element == 'category') {
807 '@phan-var-force Categorie $object';
808 $width = 80;
809 $cssclass = 'photowithmargin photoref';
810 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
811 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
812 if ($conf->browser->layout == 'phone') {
813 $maxvisiblephotos = 1;
814 }
815 if ($showimage) {
816 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $object->show_photos('category', $conf->categorie->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, 0, $width, 0, '') . '</div>';
817 } else {
818 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
819 $nophoto = '';
820 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
821 } else { // Show no photo link
822 $nophoto = '/public/theme/common/nophoto.png';
823 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo' . $modulepart . ' ' . $cssclass . '" title="' . dol_escape_htmltag($langs->trans("UploadAnImageToSeeAPhotoHere", $langs->transnoentitiesnoconv("Documents"))) . '" alt="No photo" style="width: ' . $width . 'px" src="' . DOL_URL_ROOT . $nophoto . '"></div>';
824 }
825 }
826 } elseif ($object->element == 'bom') {
828 '@phan-var-force Bom $object';
829 $width = 80;
830 $cssclass = 'photowithmargin photoref';
831 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
832 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
833 if ($conf->browser->layout == 'phone') {
834 $maxvisiblephotos = 1;
835 }
836 if ($showimage) {
837 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $object->show_photos('bom', $conf->bom->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, 0, $width, 0, '') . '</div>';
838 } else {
839 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
840 $nophoto = '';
841 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
842 } else { // Show no photo link
843 $nophoto = '/public/theme/common/nophoto.png';
844 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo' . $modulepart . ' ' . $cssclass . '" title="' . dol_escape_htmltag($langs->trans("UploadAnImageToSeeAPhotoHere", $langs->transnoentitiesnoconv("Documents"))) . '" alt="No photo" style="width: ' . $width . 'px" src="' . DOL_URL_ROOT . $nophoto . '"></div>';
845 }
846 }
847 } elseif ($object->element == 'ticket') {
848 $width = 80;
849 $cssclass = 'photoref';
851 '@phan-var-force Ticket $object';
852 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity] . '/' . $object->ref);
853 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
854 if ($conf->browser->layout == 'phone') {
855 $maxvisiblephotos = 1;
856 }
857
858 if ($showimage) {
859 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
860 if ($object->nbphoto > 0) {
861 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $showphoto . '</div>';
862 } else {
863 $showimage = 0;
864 }
865 }
866 if (!$showimage) {
867 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
868 $nophoto = '';
869 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
870 } else { // Show no photo link
871 $nophoto = img_picto('No photo', 'object_ticket');
872 $morehtmlleft .= '<!-- No photo to show -->';
873 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
874 $morehtmlleft .= $nophoto;
875 $morehtmlleft .= '</div></div>';
876 }
877 }
878 } else {
879 // $modulepart may have been set previously if Imagick class exists (see before).
880 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
881 $phototoshow = '';
882 // Check if a preview file is available
883 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
884 $objectref = dol_sanitizeFileName($object->ref);
885 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity]) . "/";
886 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
887 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
888 $subdir .= ((!empty($subdir) && !preg_match('/\/$/', $subdir)) ? '/' : '') . $objectref; // the objectref dir is not included into get_exdir when used with level=2, so we add it at end
889 } else {
890 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
891 }
892 if (empty($subdir)) {
893 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
894 }
895
896 $filepath = $dir_output . $subdir . "/";
897
898 $filepdf = $filepath . $objectref . ".pdf";
899 $relativepath = $subdir . '/' . $objectref . '.pdf';
900
901 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
902 $fileimage = $filepdf . '_preview.png';
903 $relativepathimage = $relativepath . '_preview.png';
904
905 $pdfexists = file_exists($filepdf);
906
907 // If PDF file exists
908 if ($pdfexists) {
909 // Conversion du PDF en image png si fichier png non existent
910 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
911 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
912 include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
913 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
914 if ($ret < 0) {
915 $error++;
916 }
917 }
918 }
919 }
920
921 if ($pdfexists && !$error) {
922 $heightforphotref = 80;
923 if (!empty($conf->dol_optimize_smallscreen)) {
924 $heightforphotref = 60;
925 }
926 // If the preview file is found
927 if (file_exists($fileimage)) {
928 $phototoshow = '<div class="photoref">';
929 $phototoshow .= '<img height="' . $heightforphotref . '" class="photo photowithborder" src="' . DOL_URL_ROOT . '/viewimage.php?modulepart=apercu' . $modulepart . '&amp;file=' . urlencode($relativepathimage) . '">';
930 $phototoshow .= '</div>';
931 }
932 }
933 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
934 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0);
935 }
936
937 if ($phototoshow) {
938 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
939 $morehtmlleft .= $phototoshow;
940 $morehtmlleft .= '</div>';
941 }
942 }
943
944 if (empty($phototoshow)) { // Show No photo link (picto of object)
945 if ($object->element == 'action') {
946 $width = 80;
947 $cssclass = 'photorefcenter';
948 $nophoto = img_picto('No photo', 'title_agenda');
949 } else {
950 $width = 14;
951 $cssclass = 'photorefcenter';
952 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
953 $prefix = 'object_';
954 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
955 $picto = 'project'; // instead of projectpub
956 }
957 if (strpos($picto, 'fontawesome_') !== false) {
958 $prefix = '';
959 }
960 $nophoto = img_picto('No photo', $prefix . $picto);
961 }
962 $morehtmlleft .= '<!-- No photo to show -->';
963 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
964 $morehtmlleft .= $nophoto;
965 $morehtmlleft .= '</div></div>';
966 }
967 }
968
969 if (getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && (getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') == '1' || preg_match('/' . preg_quote($object->element, '/') . '/i', getDolGlobalString('MAIN_SHOW_TECHNICAL_ID'))) && !empty($object->id)) {
970 $morehtmlref .= '<div style="clear: both;"></div>';
971 $morehtmlref .= '<div class="smallimp refidno opacitymedium banner-object-technical-id">';
972 $morehtmlref .= $langs->trans("TechnicalID") . ': ' . ((int) $object->id);
973 $morehtmlref .= '</div>';
974 }
975
976
977 // Show barcode
978 if ($showbarcode) {
979 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $form->showbarcode($object, 100, 'photoref valignmiddle') . '</div>';
980 }
981
982 if ($object->element == 'societe') {
984 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
985 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
986 } else {
987 $morehtmlstatus .= $object->getLibStatut(6);
988 }
989 } elseif ($object->element == 'product') {
991 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
992 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
993 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
994 } else {
995 $morehtmlstatus .= '<span class="statusrefsell">' . $object->getLibStatut(6, 0) . '</span>';
996 }
997 $morehtmlstatus .= ' &nbsp; ';
998 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
999 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
1000 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'status_buy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
1001 } else {
1002 $morehtmlstatus .= '<span class="statusrefbuy">' . $object->getLibStatut(6, 1) . '</span>';
1003 }
1004 } elseif (in_array($object->element, array('salary'))) {
1006 '@phan-var-force Salary $object';
1007 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
1008 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
1009 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
1010 }
1011 $morehtmlstatus .= $tmptxt;
1012 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) {
1014 '@phan-var-force Facture|FactureFournisseur|CommonInvoice $object';
1015 if (!isset($object->alreadypaid)) {
1016 $object->totalpaid = $object->getSommePaiement(0);
1017 $object->totalcreditnotes = $object->getSumCreditNotesUsed(0);
1018 $object->totaldeposits = $object->getSumDepositsUsed(0);
1019 $object->alreadypaid = $object->totalpaid + $object->totalcreditnotes + $object->totaldeposits;
1020 }
1021 $tmptxt = $object->getLibStatut(6, (float) $object->alreadypaid);
1022 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
1023 $tmptxt = $object->getLibStatut(5, (float) $object->alreadypaid);
1024 }
1025 $morehtmlstatus .= $tmptxt;
1026 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid like for invoices
1028 '@phan-var-force ChargeSociales|Loan|Tva $object';
1029 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
1030 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
1031 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
1032 }
1033 $morehtmlstatus .= $tmptxt;
1034 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
1036 if ($object->status == 0) {
1037 $morehtmlstatus .= $object->getLibStatut(5);
1038 } else {
1039 $morehtmlstatus .= $object->getLibStatut(4);
1040 }
1041 } elseif ($object->element == 'facturerec') {
1043 '@phan-var-force FactureRec $object';
1044 if ($object->frequency == 0) {
1045 $morehtmlstatus .= $object->getLibStatut(2);
1046 } else {
1047 $morehtmlstatus .= $object->getLibStatut(5);
1048 }
1049 } elseif ($object->element == 'project_task') {
1051 $tmptxt = $object->getLibStatut(4);
1052 $morehtmlstatus .= $tmptxt;
1053 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
1054 $tmptxt = $object->getLibStatut(6);
1055 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
1056 $tmptxt = $object->getLibStatut(5);
1057 }
1058 $morehtmlstatus .= $tmptxt;
1059 }
1060
1061 // Say if object was dispatched/transferred "into accountancy"
1062 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
1063 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
1064 if (method_exists($object, 'getVentilExportCompta')) {
1065 $accounted = $object->getVentilExportCompta(1);
1066 $langs->load("accountancy");
1067 $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">' . ($accounted > 0 ? '<a href="' . DOL_URL_ROOT . '/accountancy/bookkeeping/list.php?search_mvt_num=' . ((int) $accounted) . '">' . $langs->trans("Accounted") . '</a>' : $langs->trans("NotYetAccounted")) . '</span>';
1068 }
1069 }
1070
1071 // Add alias for thirdparty
1072 if (!empty($object->name_alias)) {
1074 '@phan-var-force Societe $object';
1075 $morehtmlref .= '<div class="refidno opacitymedium banner-object-name-alias">' . dol_escape_htmltag($object->name_alias) . '</div>';
1076 }
1077
1078 // Add label
1079 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
1081 if (!empty($object->label)) {
1082 $morehtmlref .= '<div class="refidno banner-object-label">' . $object->label . '</div>';
1083 }
1084 }
1085 // Show address and email
1086 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
1087 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
1088 if ($moreaddress) {
1089 $morehtmlref .= '<div class="refidno refaddress">';
1090 $morehtmlref .= $moreaddress;
1091 $morehtmlref .= '</div>';
1092 }
1093 }
1094
1095 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
1096 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
1097 if ($reshook < 0) {
1098 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1099 } elseif (empty($reshook)) {
1100 $morehtmlref .= $hookmanager->resPrint;
1101 } elseif ($reshook > 0) {
1102 $morehtmlref = $hookmanager->resPrint;
1103 }
1104
1105 // $morehtml is the right part (link "Back to list")
1106 // $morehtmlref is the part after the ref
1107 // $morehtmlleft is the picto or photo of banner
1108 // $morehtmlstatus is part under the status
1109 // $morehtmlright is part of htmlright
1110
1111 print '<div class="' . ($onlybanner ? 'arearefnobottom ' : 'arearef ') . 'heightref valignmiddle centpercent object-banner-tab-container" data-module-part="'.dolPrintHTMLForAttribute($modulepart).'">';
1112 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
1113 print '</div>';
1114 print '<div class="underrefbanner clearboth"></div>';
1115}
1116
1126function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
1127{
1128 global $langs;
1129 $ret = '';
1130 if ($fieldrequired) {
1131 $ret .= '<span class="fieldrequired">';
1132 }
1133 $ret .= '<label for="' . $fieldkey . '">';
1134 $ret .= $langs->trans($langkey);
1135 $ret .= '</label>';
1136 if ($fieldrequired) {
1137 $ret .= '</span>';
1138 }
1139 return $ret;
1140}
1141
1142
1158function dolOutputDates($datep, $datef = null, $fullday = 0, $addseconds = 0, $pictotoadd = '', $tzoutput = 'tzuserrel', $reduceformat = 0)
1159{
1160 $tmpa = dol_getdate($datep);
1161 if (empty($datef)) {
1162 $tmpb = $tmpa;
1163 } else {
1164 $tmpb = dol_getdate($datef);
1165 }
1166
1167 $s = '';
1168
1169 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
1170 // The same day
1171 $s .= '<div class="center inline-block">';
1172 if ($tmpa['hours'] != $tmpb['hours'] || $tmpa['minutes'] != $tmpb['minutes']) {
1173 // Not the same hour
1174 $s .= dol_print_date($datep, 'day'.($reduceformat ? 'reduceformat' : ''), $tzoutput);
1175 $s .= $pictotoadd;
1176 if (empty($fullday)) {
1177 $s .= '<br><span class="small opacitymedium">';
1178 $s .= dol_print_date($datep, 'hour'.($addseconds ? 'sec' : '').'reduceformat', $tzoutput);
1179 $s .= '-'.dol_print_date($datef, 'hour'.($addseconds ? 'sec' : '').'reduceformat', $tzoutput);
1180 $s .= '</span>';
1181 }
1182 } else {
1183 // The same hour
1184 $s .= dol_print_date($datep, 'day'.($reduceformat ? 'reduceformat' : ''), 'tzuserrel');
1185 $s .= $pictotoadd;
1186 if (empty($fullday)) {
1187 $s .= '<br><span class="small opacitymedium">';
1188 $s .= dol_print_date($datep, 'hour'.($addseconds ? 'sec' : '').'reduceformat', $tzoutput);
1189 $s .= '</span>';
1190 }
1191 }
1192 $s .= '</div>';
1193 } else {
1194 // Not the same day
1195 $s .= '<div class="center inline-block dateborderright">';
1196 $s .= dol_print_date($datep, 'day'.($reduceformat ? 'reduceformat' : ''), $tzoutput);
1197 if (empty($fullday)) {
1198 $s .= '<br><span class="small opacitymedium">';
1199 $s .= dol_print_date($datep, 'hour'.($addseconds ? 'sec' : '').'reduceformat', $tzoutput);
1200 $s .= '</span>';
1201 }
1202 $s .= '</div>';
1203 $s .= '<div class="center inline-block dateborderleft">';
1204 $s .= dol_print_date($datef, 'day'.($reduceformat ? 'reduceformat' : ''), 'tzuserrel');
1205 $s .= $pictotoadd;
1206 if (empty($fullday)) {
1207 $s .= '<br><span class="small opacitymedium">';
1208 $s .= dol_print_date($datef, 'hour'.($addseconds ? 'sec' : '').'reduceformat', $tzoutput);
1209 $s .= '</span>';
1210 }
1211 $s .= '</div>';
1212 }
1213
1214 return $s;
1215}
1216
1217
1225function getPictoForType($key, $morecss = '')
1226{
1227 // Set array with type -> picto
1228 $type2picto = array(
1229 'varchar' => 'font',
1230 'text' => 'font',
1231 'html' => 'code',
1232 'int' => 'sort-numeric-down',
1233 'double' => 'sort-numeric-down',
1234 'price' => 'currency',
1235 'pricecy' => 'multicurrency',
1236 'password' => 'key',
1237 'boolean' => 'check-square',
1238 'date' => 'calendar',
1239 'datetime' => 'calendar',
1240 'duration' => 'hourglass',
1241 'phone' => 'phone',
1242 'mail' => 'email',
1243 'url' => 'url',
1244 'ip' => 'country',
1245 'select' => 'list',
1246 'sellist' => 'list',
1247 'stars' => 'fontawesome_star_fas',
1248 'radio' => 'check-circle',
1249 'checkbox' => 'list',
1250 'chkbxlst' => 'list',
1251 'link' => 'link',
1252 'icon' => "question",
1253 'point' => "country",
1254 'multipts' => 'country',
1255 'linestrg' => "country",
1256 'polygon' => "country",
1257 'separate' => 'minus'
1258 );
1259
1260 if (!empty($type2picto[$key])) {
1261 return img_picto('', $type2picto[$key], 'class="pictofixedwidth' . ($morecss ? ' ' . $morecss : '') . '"');
1262 }
1263
1264 return img_picto('', 'generic', 'class="pictofixedwidth' . ($morecss ? ' ' . $morecss : '') . '"');
1265}
1266
1267
1291function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2, $allowothertags = array())
1292{
1293 global $conf;
1294
1295 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
1296 $url = DOL_URL_ROOT;
1297 $theme = isset($conf->theme) ? $conf->theme : null;
1298 $path = 'theme/' . $theme;
1299 if (empty($picto)) {
1300 $picto = 'generic';
1301 }
1302
1303 // Define fullpathpicto to use into src
1304 if ($pictoisfullpath) {
1305 // Clean parameters
1306 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
1307 $picto .= '.png';
1308 }
1309 $fullpathpicto = $picto;
1310 $reg = array();
1311 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
1312 $morecss .= ($morecss ? ' ' : '') . $reg[1];
1313 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
1314 }
1315 } else {
1316 // $picto can not be null since replaced with 'generic' in that case
1317 // $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
1318 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
1319 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
1320 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
1321
1322 // Fix some values of $pictowithouttext
1323 $pictoconvertkey = array(
1324 'facture' => 'bill',
1325 'shipping' => 'shipment',
1326 'fichinter' => 'intervention',
1327 'agenda' => 'calendar',
1328 'invoice_supplier' => 'supplier_invoice',
1329 'order_supplier' => 'supplier_order');
1330 if (in_array($pictowithouttext, array_keys($pictoconvertkey))) {
1331 $pictowithouttext = $pictoconvertkey[$pictowithouttext];
1332 }
1333
1334 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
1335 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
1336 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
1337 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
1338
1339 // Compatibility with old fontawesome versions
1340 if ($pictowithouttext == 'file-o') {
1341 $pictowithouttext = 'file';
1342 }
1343
1344 $pictowithouttextarray = explode('_', $pictowithouttext);
1345 $marginleftonlyshort = 0;
1346
1347 if (!empty($pictowithouttextarray[1])) {
1348 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
1349 $fakey = 'fa-' . $pictowithouttextarray[0];
1350 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
1351 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
1352 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
1353 } else {
1354 $fakey = 'fa-' . $pictowithouttext;
1355 $faprefix = 'fas';
1356 $facolor = '';
1357 $fasize = '';
1358 }
1359
1360 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
1361 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
1362 $morestyle = '';
1363 $reg = array();
1364 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
1365 $morecss .= ($morecss ? ' ' : '') . $reg[1];
1366 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
1367 }
1368 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
1369 $morestyle = $reg[1];
1370 $moreatt = str_replace('style="' . $reg[1] . '"', '', $moreatt);
1371 }
1372 $moreatt = trim($moreatt);
1373
1374 $enabledisablehtml = '<span class="' . $faprefix . ' ' . $fakey;
1375 $enabledisablehtml .= ($morecss ? ' ' . $morecss : '') . '" style="' . ($fasize ? ('font-size: ' . $fasize . ';') : '') . ($facolor ? (' color: ' . $facolor . ';') : '') . ($morestyle ? ' ' . $morestyle : '') . '"' . (($notitle || empty($titlealt)) ? '' : ' title="' . dol_escape_htmltag($titlealt) . '"') . ($moreatt ? ' ' . $moreatt : '') . '>';
1376 $enabledisablehtml .= '</span>';
1377
1378 return $enabledisablehtml;
1379 }
1380
1381 if (empty($srconly) && !preg_match('/[\.\/@]/', $picto)) { // If original picto code does not contains a / and no . inside, it is not a path to an image file on disk
1382 $fakey = $pictowithouttext;
1383 $facolor = '';
1384 $fasize = '';
1385 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
1386 if (in_array($pictowithouttext, array('card', 'bell', 'clock', 'establishment', 'file', 'file-o', 'generic', 'minus-square', 'object_generic', 'pdf', 'plus-square', 'timespent', 'note', 'off', 'on', 'object_bookmark', 'bookmark', 'vcard'))) {
1387 $fa = 'far';
1388 }
1389 if (in_array($pictowithouttext, array('black-tie', 'discord', 'facebook', 'flickr', 'github', 'google', 'google-plus-g', 'instagram', 'linkedin', 'meetup', 'microsoft', 'pinterest', 'skype', 'slack', 'twitter', 'reddit', 'snapchat', 'stripe', 'stripe-s', 'tumblr', 'viadeo', 'whatsapp', 'youtube'))) {
1390 $fa = 'fab';
1391 }
1392
1393 $arrayconvpictotofa = getImgPictoConv('fa');
1394
1395 if ($pictowithouttext == 'off') {
1396 $fakey = 'fa-square';
1397 $fasize = '1.3em';
1398 } elseif ($pictowithouttext == 'on') {
1399 $fakey = 'fa-check-square';
1400 $fasize = '1.3em';
1401 } elseif ($pictowithouttext == 'listlight') {
1402 $fakey = 'fa-download';
1403 $marginleftonlyshort = 1;
1404 } elseif ($pictowithouttext == 'printer') {
1405 $fakey = 'fa-print';
1406 $fasize = '1.2em';
1407 } elseif ($pictowithouttext == 'note') {
1408 $fakey = 'fa-sticky-note';
1409 $marginleftonlyshort = 1;
1410 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
1411 $convertarray = array('1uparrow' => 'caret-up', '1downarrow' => 'caret-down', '1leftarrow' => 'caret-left', '1rightarrow' => 'caret-right', '1uparrow_selected' => 'caret-up', '1downarrow_selected' => 'caret-down', '1leftarrow_selected' => 'caret-left', '1rightarrow_selected' => 'caret-right');
1412 $fakey = 'fa-' . $convertarray[$pictowithouttext];
1413 if (preg_match('/selected/', $pictowithouttext)) {
1414 $facolor = '#888';
1415 }
1416 $marginleftonlyshort = 1;
1417 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
1418 $fakey = 'fa-' . $arrayconvpictotofa[$pictowithouttext];
1419 } else {
1420 $fakey = 'fa-' . $pictowithouttext;
1421 }
1422
1423 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
1424 $morecss .= ' em092';
1425 }
1426 if (in_array($pictowithouttext, array('conferenceorbooth', 'eventorganization', 'holiday', 'info', 'info_black', 'project', 'workstation'))) {
1427 $morecss .= ' em088';
1428 }
1429 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
1430 $morecss .= ' em080';
1431 }
1432
1433 // Define $marginleftonlyshort
1434 $arrayconvpictotomarginleftonly = array(
1435 'bank',
1436 'check',
1437 'delete',
1438 'generic',
1439 'grip',
1440 'grip_title',
1441 'jabber',
1442 'grip_title',
1443 'grip',
1444 'listlight',
1445 'note',
1446 'on',
1447 'off',
1448 'playdisabled',
1449 'printer',
1450 'resize',
1451 'sign-out',
1452 'stats',
1453 'switch_on',
1454 'switch_on_grey',
1455 'switch_on_red',
1456 'switch_off',
1457 'switch_off_grey',
1458 'switch_off_red',
1459 'uparrow',
1460 '1uparrow',
1461 '1downarrow',
1462 '1leftarrow',
1463 '1rightarrow',
1464 '1uparrow_selected',
1465 '1downarrow_selected',
1466 '1leftarrow_selected',
1467 '1rightarrow_selected'
1468 );
1469 if (!array_key_exists($pictowithouttext, $arrayconvpictotomarginleftonly)) {
1470 $marginleftonlyshort = 0;
1471 }
1472
1473 // Add CSS
1474 $arrayconvpictotomorcess = array(
1475 'action' => 'infobox-action',
1476 'account' => 'infobox-bank_account',
1477 'accounting_account' => 'infobox-bank_account',
1478 'accountline' => 'infobox-bank_account',
1479 'accountancy' => 'infobox-bank_account',
1480 'admin' => 'opacitymedium',
1481 'asset' => 'infobox-bank_account',
1482 'bank_account' => 'infobox-bank_account',
1483 'bill' => 'infobox-commande',
1484 'billa' => 'infobox-commande',
1485 'billr' => 'infobox-commande',
1486 'billd' => 'infobox-commande',
1487 'bookcal' => 'infobox-portal',
1488 'margin' => 'infobox-bank_account',
1489 'conferenceorbooth' => 'infobox-project',
1490 'cash-register' => 'infobox-portal',
1491 'contract' => 'infobox-contrat',
1492 'check' => 'font-status4',
1493 'conversation' => 'infobox-contrat',
1494 'donation' => 'infobox-commande',
1495 'dolly' => 'infobox-commande',
1496 'dollyrevert' => 'flip infobox-order_supplier',
1497 'ecm' => 'infobox-action',
1498 'eventorganization' => 'infobox-project',
1499 'hrm' => 'infobox-adherent',
1500 'group' => 'infobox-adherent',
1501 'intervention' => 'infobox-contrat',
1502 'incoterm' => 'infobox-supplier_proposal',
1503 'intracommreport' => 'infobox-bank_account',
1504 'currency' => 'infobox-bank_account',
1505 'multicurrency' => 'infobox-bank_account',
1506 'members' => 'infobox-adherent',
1507 'member' => 'infobox-adherent',
1508 'money-bill-alt' => 'infobox-bank_account',
1509 'order' => 'infobox-commande',
1510 'user' => 'infobox-adherent',
1511 'users' => 'infobox-adherent',
1512 'error' => 'pictoerror',
1513 'warning' => 'pictowarning',
1514 'switch_on' => 'font-status4',
1515 'switch_on_warning' => 'font-status4 warning',
1516 'switch_on_red' => 'font-status8',
1517 'switch_off_warning' => 'font-status4 warning',
1518 'switch_off_red' => 'font-status8',
1519 'holiday' => 'infobox-holiday',
1520 'info' => 'opacityhigh',
1521 'info_black' => 'purple',
1522 'invoice' => 'infobox-commande',
1523 'knowledgemanagement' => 'infobox-contrat rotate90',
1524 'loan' => 'infobox-commande',
1525 'payment' => 'infobox-bank_account',
1526 'payment_vat' => 'infobox-bank_account',
1527 'poll' => 'infobox-portal',
1528 'pos' => 'infobox-bank_account',
1529 'project' => 'infobox-project',
1530 'projecttask' => 'infobox-project',
1531 'propal' => 'infobox-propal',
1532 'proposal' => 'infobox-propal',
1533 'private' => 'infobox-project',
1534 'reception' => 'flip infobox-order_supplier',
1535 'recruitmentjobposition' => 'infobox-adherent',
1536 'recruitmentcandidature' => 'infobox-adherent',
1537 'resource' => 'infobox-action',
1538 'salary' => 'infobox-commande',
1539 'shapes' => 'infobox-adherent',
1540 'shipment' => 'infobox-commande',
1541 'store' => 'infobox-portal',
1542 'stripe' => 'infobox-bank_account',
1543 'supplier_invoice' => 'infobox-order_supplier',
1544 'supplier_invoicea' => 'infobox-order_supplier',
1545 'supplier_invoiced' => 'infobox-order_supplier',
1546 'supplier_invoicer' => 'infobox-order_supplier',
1547 'supplier' => 'infobox-order_supplier',
1548 'supplier_order' => 'infobox-order_supplier',
1549 'supplier_proposal' => 'infobox-supplier_proposal',
1550 'ticket' => 'infobox-contrat',
1551 'title_accountancy' => 'infobox-bank_account',
1552 'title_hrm' => 'infobox-holiday',
1553 'expensereport' => 'infobox-expensereport',
1554 'trip' => 'infobox-expensereport',
1555 'title_agenda' => 'infobox-action',
1556 'vat' => 'infobox-bank_account',
1557 'webportal' => 'infobox-portal',
1558 'website' => 'infobox-portal',
1559 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
1560 'list-alt' => 'imgforviewmode',
1561 'calendar' => 'imgforviewmode',
1562 'calendarweek' => 'imgforviewmode',
1563 'calendarmonth' => 'imgforviewmode',
1564 'calendarday' => 'imgforviewmode',
1565 'calendarperuser' => 'imgforviewmode',
1566 'calendarpertype' => 'imgforviewmode'
1567 );
1568 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
1569 $morecss .= ($morecss ? ' ' : '') . $arrayconvpictotomorcess[$pictowithouttext];
1570 }
1571
1572 // Define $color
1573 $arrayconvpictotocolor = array(
1574 'address' => '#6c6aa8',
1575 'building' => '#6c6aa8',
1576 'bom' => '#a69944',
1577 'clone' => '#999',
1578 'cog' => '#999',
1579 'companies' => '#6c6aa8',
1580 'company' => '#6c6aa8',
1581 'contact' => '#6c6aa8',
1582 'cron' => '#555',
1583 'dynamicprice' => '#a69944',
1584 'edit' => '#444',
1585 'note' => '#999',
1586 'error' => '',
1587 'help' => '#bbb',
1588 'listlight' => '#999',
1589 'language' => '#555',
1590 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
1591 'lock' => '#ddd',
1592 'lot' => '#a69944',
1593 'map-marker-alt' => '#aaa',
1594 'mrp' => '#a69944',
1595 'product' => '#a69944',
1596 'service' => '#a69944',
1597 'inventory' => '#a69944',
1598 'stock' => '#a69944',
1599 'movement' => '#a69944',
1600 'other' => '#ddd',
1601 'world' => '#986c6a',
1602 'partnership' => '#6c6aa8',
1603 'playdisabled' => '#ccc',
1604 'printer' => '#444',
1605 'projectpub' => '#986c6a',
1606 'resize' => '#444',
1607 'rss' => '#cba',
1608 //'shipment'=>'#a69944',
1609 'search-plus' => '#808080',
1610 'security' => '#999',
1611 'square' => '#888',
1612 'stop-circle' => '#888',
1613 'stats' => '#444',
1614 'superadmin' => '#600',
1615 'switch_off' => '#999',
1616 'technic' => '#999',
1617 'tick' => '#282',
1618 'timespent' => '#555',
1619 'uncheck' => '#800',
1620 'uparrow' => '#555',
1621 'user-cog' => '#999',
1622 'country' => '#aaa',
1623 'globe-americas' => '#aaa',
1624 'region' => '#aaa',
1625 'state' => '#aaa',
1626 //'website' => '#304',
1627 'workstation' => '#a69944'
1628 );
1629 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
1630 $facolor = $arrayconvpictotocolor[$pictowithouttext];
1631 }
1632
1633 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
1634 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
1635 $morestyle = '';
1636 $reg = array();
1637 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
1638 $morecss .= ($morecss ? ' ' : '') . $reg[1];
1639 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
1640 }
1641 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
1642 $morestyle = $reg[1];
1643 $moreatt = str_replace('style="' . $reg[1] . '"', '', $moreatt);
1644 }
1645 $moreatt = trim($moreatt);
1646
1647 $enabledisablehtml = '<span class="' . $fa . ' ' . $fakey . ($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
1648 $enabledisablehtml .= ($morecss ? ' ' . $morecss : '') . '" style="' . ($fasize ? ('font-size: ' . $fasize . ';') : '') . ($facolor ? (' color: ' . $facolor . ';') : '') . ($morestyle ? ' ' . $morestyle : '') . '"' . (($notitle || empty($titlealt)) ? '' : ' title="' . dol_escape_htmltag($titlealt) . '"') . ($moreatt ? ' ' . $moreatt : '') . '>';
1649 $enabledisablehtml .= '</span>';
1650
1651 return $enabledisablehtml;
1652 }
1653
1654 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
1655 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/' . $theme; // If the theme does not have the same name as the module
1656 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
1657 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_RES') . '/theme/' . getDolGlobalString('MAIN_OVERWRITE_THEME_RES'); // To allow an external module to overwrite image resources whatever is activated theme
1658 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
1659 $path = $theme . '/theme/' . $theme; // If the theme have the same name as the module
1660 }
1661
1662 // If we ask an image into $url/$mymodule/img (instead of default path)
1663 $regs = array();
1664 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
1665 $picto = $regs[1];
1666 $path = $regs[2]; // $path is $mymodule
1667 }
1668
1669 // Clean parameters
1670 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
1671 $picto .= '.png';
1672 }
1673 // If alt path are defined, define url where img file is, according to physical path
1674 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
1675 foreach ($conf->file->dol_document_root as $type => $dirroot) {
1676 if ($type == 'main') {
1677 continue;
1678 }
1679 // This consumes a lot of time, that's why enabling alternative dir like "custom" dir should be avoid
1680 if (file_exists($dirroot . '/' . $path . '/img/' . $picto) && !empty($conf->file->dol_url_root)) {
1681 $url = DOL_URL_ROOT . $conf->file->dol_url_root[$type];
1682 break;
1683 }
1684 }
1685
1686 // $url is '' or '/custom', $path is current theme or
1687 $fullpathpicto = $url . '/' . $path . '/img/' . $picto;
1688 }
1689
1690 if ($srconly) {
1691 return $fullpathpicto;
1692 }
1693
1694 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
1695 return '<img src="' . $fullpathpicto . '"' . ($notitle ? '' : ' alt="' . dolPrintHTMLForAttribute($alt, 0, $allowothertags) . '"') . (($notitle || empty($titlealt)) ? '' : ' title="' . dolPrintHTMLForAttribute($titlealt, 0, $allowothertags) . '"') . ($moreatt ? ' ' . $moreatt . ($morecss ? ' class="' . $morecss . '"' : '') : ' class="inline-block' . ($morecss ? ' ' . $morecss : '') . '"') . '>'; // Alt is used for accessibility, title for popup
1696}
1697
1705function getImgPictoConv($mode = 'fa')
1706{
1707 global $conf;
1708
1709 if (empty($mode) || $mode == 'fa') {
1710 // Array when the fa picto key is different than the Dolibarr picto key.
1711 $arrayconvpictotofa = array(
1712 'account' => 'university',
1713 'accounting_account' => 'clipboard-list',
1714 'accountline' => 'receipt',
1715 'accountancy' => 'search-dollar',
1716 'action' => 'calendar-alt',
1717 'add' => 'plus-circle',
1718 'address' => 'address-book',
1719 'ai' => 'magic',
1720 'admin' => 'star',
1721 'asset' => 'money-check-alt',
1722 'autofill' => 'fill',
1723 'back' => 'arrow-left',
1724 'bank_account' => 'university',
1725 'bill' => 'file-invoice-dollar',
1726 'billa' => 'file-excel',
1727 'billr' => 'file-invoice-dollar',
1728 'billd' => 'file-medical',
1729 'blockedlog' => 'file-archive',
1730 'bookcal' => 'calendar-check',
1731 'supplier_invoice' => 'file-invoice-dollar',
1732 'supplier_invoicea' => 'file-excel',
1733 'supplier_invoicer' => 'file-invoice-dollar',
1734 'supplier_invoiced' => 'file-medical',
1735 'bom' => 'shapes',
1736 'card' => 'address-card',
1737 'chart' => 'chart-line',
1738 'company' => 'building',
1739 'contact' => 'address-book',
1740 'contract' => 'suitcase',
1741 'collab' => 'people-arrows',
1742 'conversation' => 'comments',
1743 'country' => 'globe-americas',
1744 'cron' => 'business-time',
1745 'cross' => 'times',
1746 'chevron-double-left' => 'angle-double-left',
1747 'chevron-double-right' => 'angle-double-right',
1748 'chevron-double-down' => 'angle-double-down',
1749 'chevron-double-top' => 'angle-double-up',
1750 'donation' => 'gift',
1751 'dynamicprice' => 'hand-holding-usd',
1752 'setup' => 'cog',
1753 'companies' => 'building',
1754 'products' => 'cube',
1755 'commercial' => 'suitcase',
1756 'invoicing' => 'coins',
1757 'accounting' => 'search-dollar',
1758 'category' => 'tag',
1759 'dollyrevert' => 'dolly',
1760 'file-o' => 'file',
1761 'generate' => 'plus-square',
1762 'hrm' => 'user-tie',
1763 'incoterm' => 'truck-loading',
1764 'margin' => 'calculator',
1765 'members' => 'user-friends',
1766 'ticket' => 'ticket-alt',
1767 'globe' => 'external-link-alt',
1768 'lot' => 'barcode',
1769 'email' => 'at',
1770 'establishment' => 'building',
1771 'edit' => 'pencil-alt',
1772 'entity' => 'globe',
1773 'graph' => 'chart-line',
1774 'grip_title' => 'arrows-alt',
1775 'grip' => 'arrows-alt',
1776 'help' => 'question-circle',
1777 'generic' => 'file',
1778 'holiday' => 'umbrella-beach',
1779 'info' => 'info-circle',
1780 'info_black' => 'info-circle',
1781 'inventory' => 'boxes',
1782 'intracommreport' => 'globe-europe',
1783 'jobprofile' => 'cogs',
1784 'knowledgemanagement' => 'ticket-alt',
1785 'label' => 'layer-group',
1786 'layout' => 'columns',
1787 'line' => 'bars',
1788 'loan' => 'money-bill-alt',
1789 'member' => 'user-alt',
1790 'meeting' => 'chalkboard-teacher',
1791 'mrp' => 'cubes',
1792 'next' => 'arrow-alt-circle-right',
1793 'trip' => 'wallet',
1794 'expensereport' => 'wallet',
1795 'group' => 'users',
1796 'movement' => 'people-carry',
1797 'sign-out' => 'sign-out-alt',
1798 'superadmin' => 'star',
1799 'switch_off' => 'toggle-off',
1800 'switch_off_grey' => 'toggle-off',
1801 'switch_off_warning' => 'toggle-off',
1802 'switch_off_red' => 'toggle-off',
1803 'switch_on' => 'toggle-on',
1804 'switch_on_grey' => 'toggle-on',
1805 'switch_on_warning' => 'toggle-on',
1806 'switch_on_red' => 'toggle-on',
1807 'check' => 'check',
1808 'bookmark' => 'star',
1809 'bank' => 'university',
1810 'close_title' => 'times',
1811 'delete' => 'trash',
1812 'filter' => 'filter',
1813 'list-alt' => 'list-alt',
1814 'calendarlist' => 'bars',
1815 'calendar' => 'calendar-alt',
1816 'calendarmonth' => 'calendar-alt',
1817 'calendarweek' => 'calendar-week',
1818 'calendarday' => 'calendar-day',
1819 'calendarperuser' => 'table',
1820 'calendarpertype' => 'table',
1821 'intervention' => 'ambulance',
1822 'invoice' => 'file-invoice-dollar',
1823 'order' => 'file-invoice',
1824 'error' => 'exclamation-triangle',
1825 'warning' => 'exclamation-triangle',
1826 'other' => 'square',
1827 'playdisabled' => 'play',
1828 'pdf' => 'file-pdf',
1829 'poll' => 'check-double',
1830 'pos' => 'cash-register',
1831 'preview' => 'binoculars',
1832 'project' => 'project-diagram',
1833 'projectpub' => 'project-diagram',
1834 'projecttask' => 'tasks',
1835 'propal' => 'file-signature',
1836 'proposal' => 'file-signature',
1837 'partnership' => 'handshake',
1838 'payment' => 'money-check-alt',
1839 'payment_vat' => 'money-check-alt',
1840 'pictoconfirm' => 'check-square',
1841 'phoning' => 'phone',
1842 'phoning_mobile' => 'mobile-alt',
1843 'phoning_fax' => 'fax',
1844 'previous' => 'arrow-alt-circle-left',
1845 'printer' => 'print',
1846 'product' => 'cube',
1847 'puce' => 'angle-right',
1848 'recent' => 'check-square',
1849 'reception' => 'dolly',
1850 'recruitmentjobposition' => 'id-card-alt',
1851 'recruitmentcandidature' => 'id-badge',
1852 'resize' => 'crop',
1853 'supplier_order' => 'dol-order_supplier',
1854 'supplier_proposal' => 'file-signature',
1855 'refresh' => 'redo',
1856 'region' => 'map-marked',
1857 'replacement' => 'exchange-alt',
1858 'resource' => 'laptop-house',
1859 'recurring' => 'history',
1860 'service' => 'concierge-bell',
1861 'skill' => 'shapes',
1862 'state' => 'map-marked-alt',
1863 'security' => 'key',
1864 'salary' => 'wallet',
1865 'shipment' => 'dolly',
1866 'stock' => 'box-open',
1867 'stats' => 'chart-bar',
1868 'split' => 'code-branch',
1869 'status' => 'stop-circle',
1870 'stripe' => 'stripe-s',
1871 'supplier' => 'building',
1872 'technic' => 'cogs',
1873 'tick' => 'check',
1874 'timespent' => 'clock',
1875 'title_setup' => 'tools',
1876 'title_accountancy' => 'money-check-alt',
1877 'title_bank' => 'university',
1878 'title_hrm' => 'umbrella-beach',
1879 'title_agenda' => 'calendar-alt',
1880 'uncheck' => 'times',
1881 'uparrow' => 'share',
1882 'url' => 'external-link-alt',
1883 'vat' => 'money-check-alt',
1884 'vcard' => 'arrow-alt-circle-down',
1885 'jabber' => 'comment',
1886 'website' => 'globe-americas',
1887 'workstation' => 'pallet',
1888 'webhook' => 'bullseye',
1889 'world' => 'globe',
1890 'private' => 'user-lock',
1891 'conferenceorbooth' => 'chalkboard-teacher',
1892 'eventorganization' => 'project-diagram',
1893 'webportal' => 'door-open'
1894 );
1895
1896 if ($conf->currency == 'EUR') {
1897 $arrayconvpictotofa['currency'] = 'euro-sign';
1898 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
1899 } else {
1900 $arrayconvpictotofa['currency'] = 'dollar-sign';
1901 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
1902 }
1903 } else {
1904 $arrayconvpictotofa = array();
1905 }
1906
1907 return $arrayconvpictotofa;
1908}
1909
1910
1925function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $allowothertags = array())
1926{
1927 if (strpos($picto, '^') === 0) {
1928 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
1929 } else {
1930 return img_picto($titlealt, 'object_' . $picto, $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
1931 }
1932}
1933
1945function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
1946{
1947 global $conf;
1948
1949 if (is_numeric($picto)) {
1950 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
1951 //$picto = $leveltopicto[$picto];
1952 return '<i class="fa fa-weather-level' . $picto . '"></i>';
1953 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
1954 $picto .= '.png';
1955 }
1956
1957 $path = DOL_URL_ROOT . '/theme/' . $conf->theme . '/img/weather/' . $picto;
1958
1959 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
1960}
1961
1973function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
1974{
1975 global $conf;
1976
1977 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
1978 $picto .= '.png';
1979 }
1980
1981 if ($pictoisfullpath) {
1982 $path = $picto;
1983 } else {
1984 $path = DOL_URL_ROOT . '/theme/common/' . $picto;
1985
1986 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
1987 $themepath = DOL_DOCUMENT_ROOT . '/theme/' . $conf->theme . '/img/' . $picto;
1988
1989 if (file_exists($themepath)) {
1990 $path = $themepath;
1991 }
1992 }
1993 }
1994
1995 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
1996}
1997
2011function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
2012{
2013 global $langs;
2014
2015 if (empty($titlealt) || $titlealt == 'default') {
2016 if ($numaction == '-1' || $numaction == 'ST_NO') {
2017 $numaction = -1;
2018 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
2019 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
2020 $numaction = 0;
2021 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
2022 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
2023 $numaction = 1;
2024 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
2025 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
2026 $numaction = 2;
2027 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
2028 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
2029 $numaction = 3;
2030 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
2031 } else {
2032 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus ' . $numaction);
2033 $numaction = 0;
2034 }
2035 }
2036 if (!is_numeric($numaction)) {
2037 $numaction = 0;
2038 }
2039
2040 return img_picto($titlealt, (empty($picto) ? 'stcomm' . $numaction . '.png' : $picto), $moreatt);
2041}
2042
2050function img_edit_add($titlealt = 'default', $other = '')
2051{
2052 global $langs;
2053
2054 if ($titlealt == 'default') {
2055 $titlealt = $langs->trans('Add');
2056 }
2057
2058 return img_picto($titlealt, 'edit_add.png', $other);
2059}
2067function img_edit_remove($titlealt = 'default', $other = '')
2068{
2069 global $langs;
2070
2071 if ($titlealt == 'default') {
2072 $titlealt = $langs->trans('Remove');
2073 }
2074
2075 return img_picto($titlealt, 'edit_remove.png', $other);
2076}
2077
2086function img_edit($titlealt = 'default', $float = 0, $other = '')
2087{
2088 global $langs;
2089
2090 if ($titlealt == 'default') {
2091 $titlealt = $langs->trans('Modify');
2092 }
2093
2094 return img_picto($titlealt, 'edit', ($float ? 'style="float: ' . ($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right') . '"' : "") . ($other ? ' ' . $other : ''));
2095}
2096
2105function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
2106{
2107 global $langs;
2108
2109 if ($titlealt == 'default') {
2110 $titlealt = $langs->trans('View');
2111 }
2112
2113 $moreatt = ($float ? 'style="float: right" ' : '') . $other;
2114
2115 return img_picto($titlealt, 'eye', $moreatt);
2116}
2117
2126function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
2127{
2128 global $langs;
2129
2130 if ($titlealt == 'default') {
2131 $titlealt = $langs->trans('Delete');
2132 }
2133
2134 return img_picto($titlealt, 'delete', $other, 0, 0, 0, '', $morecss);
2135}
2136
2144function img_printer($titlealt = "default", $other = '')
2145{
2146 global $langs;
2147 if ($titlealt == "default") {
2148 $titlealt = $langs->trans("Print");
2149 }
2150 return img_picto($titlealt, 'printer', $other);
2151}
2152
2160function img_split($titlealt = 'default', $other = 'class="pictosplit"')
2161{
2162 global $langs;
2163
2164 if ($titlealt == 'default') {
2165 $titlealt = $langs->trans('Split');
2166 }
2167
2168 return img_picto($titlealt, 'split', $other);
2169}
2170
2178function img_help($usehelpcursor = 1, $usealttitle = 1)
2179{
2180 global $langs;
2181
2182 if ($usealttitle) {
2183 if (is_string($usealttitle)) {
2184 $usealttitle = dol_escape_htmltag($usealttitle);
2185 } else {
2186 $usealttitle = $langs->trans('Info');
2187 }
2188 }
2189
2190 return img_picto($usealttitle, 'info', 'style="vertical-align: middle;' . ($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')) . '"');
2191}
2192
2199function img_info($titlealt = 'default')
2200{
2201 global $langs;
2202
2203 if ($titlealt == 'default') {
2204 $titlealt = $langs->trans('Informations');
2205 }
2206
2207 return img_picto($titlealt, 'info', 'style="vertical-align: middle;"');
2208}
2209
2218function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
2219{
2220 global $langs;
2221
2222 if ($titlealt == 'default') {
2223 $titlealt = $langs->trans('Warning');
2224 }
2225
2226 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
2227 return img_picto($titlealt, 'warning', 'class="' . $morecss . '"' . ($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' ' . $moreatt) : ''));
2228}
2229
2236function img_error($titlealt = 'default')
2237{
2238 global $langs;
2239
2240 if ($titlealt == 'default') {
2241 $titlealt = $langs->trans('Error');
2242 }
2243
2244 return img_picto($titlealt, 'error');
2245}
2246
2254function img_next($titlealt = 'default', $moreatt = '')
2255{
2256 global $langs;
2257
2258 if ($titlealt == 'default') {
2259 $titlealt = $langs->trans('Next');
2260 }
2261
2262 //return img_picto($titlealt, 'next.png', $moreatt);
2263 return '<span class="fa fa-chevron-right paddingright paddingleft" title="' . dol_escape_htmltag($titlealt) . '"></span>';
2264}
2265
2273function img_previous($titlealt = 'default', $moreatt = '')
2274{
2275 global $langs;
2276
2277 if ($titlealt == 'default') {
2278 $titlealt = $langs->trans('Previous');
2279 }
2280
2281 //return img_picto($titlealt, 'previous.png', $moreatt);
2282 return '<span class="fa fa-chevron-left paddingright paddingleft" title="' . dol_escape_htmltag($titlealt) . '"></span>';
2283}
2284
2293function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
2294{
2295 global $langs;
2296
2297 if ($titlealt == 'default') {
2298 $titlealt = $langs->trans('Down');
2299 }
2300
2301 return img_picto($titlealt, ($selected ? '1downarrow_selected' : '1downarrow'), 'class="imgdown' . ($moreclass ? " " . $moreclass : "") . '"');
2302}
2303
2312function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
2313{
2314 global $langs;
2315
2316 if ($titlealt == 'default') {
2317 $titlealt = $langs->trans('Up');
2318 }
2319
2320 return img_picto($titlealt, ($selected ? '1uparrow_selected' : '1uparrow'), 'class="imgup' . ($moreclass ? " " . $moreclass : "") . '"');
2321}
2322
2331function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
2332{
2333 global $langs;
2334
2335 if ($titlealt == 'default') {
2336 $titlealt = $langs->trans('Left');
2337 }
2338
2339 return img_picto($titlealt, ($selected ? '1leftarrow_selected' : '1leftarrow'), $moreatt);
2340}
2341
2350function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
2351{
2352 global $langs;
2353
2354 if ($titlealt == 'default') {
2355 $titlealt = $langs->trans('Right');
2356 }
2357
2358 return img_picto($titlealt, ($selected ? '1rightarrow_selected' : '1rightarrow'), $moreatt);
2359}
2360
2368function img_allow($allow, $titlealt = 'default')
2369{
2370 global $langs;
2371
2372 if ($titlealt == 'default') {
2373 $titlealt = $langs->trans('Active');
2374 }
2375
2376 if ($allow == 1) {
2377 return img_picto($titlealt, 'tick');
2378 }
2379
2380 return '-';
2381}
2382
2390function img_credit_card($brand, $morecss = 'fa-2x inline-block valignmiddle')
2391{
2392 if (is_null($morecss)) {
2393 $morecss = 'fa-2x';
2394 }
2395
2396 if ($brand == 'visa' || $brand == 'Visa') {
2397 $brand = 'cc-visa';
2398 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
2399 $brand = 'cc-mastercard';
2400 } elseif ($brand == 'amex' || $brand == 'American Express') {
2401 $brand = 'cc-amex';
2402 } elseif ($brand == 'discover' || $brand == 'Discover') {
2403 $brand = 'cc-discover';
2404 } elseif ($brand == 'jcb' || $brand == 'JCB') {
2405 $brand = 'cc-jcb';
2406 } elseif ($brand == 'diners' || $brand == 'Diners club') {
2407 $brand = 'cc-diners-club';
2408 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
2409 $brand = 'credit-card';
2410 }
2411
2412 return '<span class="fa fa-' . $brand . ' fa-fw' . ($morecss ? ' ' . $morecss : '') . '"></span>';
2413}
2414
2423function img_mime($file, $titlealt = '', $morecss = '')
2424{
2425 require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
2426
2427 $mimetype = dol_mimetype($file, '', 1);
2428 //$mimeimg = dol_mimetype($file, '', 2);
2429 $mimefa = dol_mimetype($file, '', 4);
2430
2431 if (empty($titlealt)) {
2432 $titlealt = 'Mime type: ' . $mimetype;
2433 }
2434
2435 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
2436 return '<i class="fa fa-' . $mimefa . ' ' . (preg_match('/pictofixedwidth/', $morecss) ? '' : 'paddingright ') . ($morecss ? ' ' . $morecss : '') . '"' . ($titlealt ? ' title="' . dolPrintHTMLForAttribute($titlealt) . '"' : '') . '></i>';
2437}
2438
2439
2447function img_search($titlealt = 'default', $other = '')
2448{
2449 global $langs;
2450
2451 if ($titlealt == 'default') {
2452 $titlealt = $langs->trans('Search');
2453 }
2454
2455 $img = img_picto($titlealt, 'search', $other, 0, 1);
2456
2457 $input = '<input type="image" class="liste_titre" name="button_search" src="' . $img . '" ';
2458 $input .= 'value="' . dol_escape_htmltag($titlealt) . '" title="' . dol_escape_htmltag($titlealt) . '" >';
2459
2460 return $input;
2461}
2462
2470function img_searchclear($titlealt = 'default', $other = '')
2471{
2472 global $langs;
2473
2474 if ($titlealt == 'default') {
2475 $titlealt = $langs->trans('Search');
2476 }
2477
2478 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
2479
2480 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="' . $img . '" ';
2481 $input .= 'value="' . dol_escape_htmltag($titlealt) . '" title="' . dol_escape_htmltag($titlealt) . '" >';
2482
2483 return $input;
2484}
2485
2500function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '', $textonpictotooltip = '', $cssfordropdown = 'info_admin')
2501{
2502 global $conf, $langs;
2503
2504 if ($infoonimgalt) {
2505 $result = img_picto($text, 'info', 'class="' . ($morecss ? ' ' . $morecss : '') . '"');
2506 } else {
2507 if (empty($conf->use_javascript_ajax)) {
2508 $textfordropdown = '';
2509 }
2510
2511 $class = (empty($admin) ? 'undefined' : ((string) $admin == '1' ? 'info' : $admin));
2512 $fa = 'info-circle';
2513 if ($picto == 'warning') {
2514 $fa = 'exclamation-triangle';
2515 }
2516 $result = ($nodiv ? '' : '<div class="wordbreak ' . $class . ($cssfordropdown ? ' ' . $cssfordropdown : '') . ($morecss ? ' ' . $morecss : '') . ($textfordropdown ? ' hidden' : '') . '">');
2517 $result .= img_picto(((string) $admin ? $langs->trans('InfoAdmin') : $langs->trans('Note')).($textonpictotooltip ? ' : '.$textonpictotooltip : ''), $fa);
2518 $result .= ' ';
2519 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
2520 $result .= ($nodiv ? '' : '</div>');
2521
2522 if ($textfordropdown) {
2523 $tmpresult = '<span class="' . $class . ' '. $cssfordropdown.'text opacitymedium cursorpointer">' . $langs->trans($textfordropdown) . ' ' . img_picto($langs->trans($textfordropdown), '1downarrow') . '</span>';
2524 $tmpresult .= '<script nonce="' . getNonce() . '" type="text/javascript">
2525 jQuery(document).ready(function() {
2526 jQuery(".' . $cssfordropdown . 'text").click(function() {
2527 console.log("toggle text of .'.$cssfordropdown.'");
2528 jQuery(".' . $cssfordropdown . '").toggle().removeClass("hidden");
2529 });
2530 });
2531 </script>';
2532
2533 $result = $tmpresult . $result;
2534 }
2535 }
2536
2537 return $result;
2538}
2539
2540
2552function dol_print_error($db = null, $error = '', $errors = null)
2553{
2554 global $conf, $langs, $user, $argv;
2555 global $dolibarr_main_prod;
2556
2557 $out = '';
2558 $syslog = '';
2559
2560 // If error occurs before the $lang object was loaded
2561 if (!$langs) {
2562 require_once DOL_DOCUMENT_ROOT . '/core/class/translate.class.php';
2563 $langs = new Translate('', $conf);
2564 $langs->load("main");
2565 }
2566
2567 // Load translation files required by the error messages
2568 $langs->loadLangs(array('main', 'errors'));
2569
2570 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
2571 $out .= $langs->trans("DolibarrHasDetectedError") . ".<br>\n";
2572 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
2573 $out .= "You use an experimental or develop level of features, so please do NOT report any bugs or vulnerability, except if problem is confirmed after moving option MAIN_FEATURES_LEVEL back to 0.<br>\n";
2574 }
2575 $out .= $langs->trans("InformationToHelpDiagnose") . ":<br>\n";
2576
2577 $out .= "<b>" . $langs->trans("Date") . ":</b> " . dol_print_date(time(), 'dayhourlog') . "<br>\n";
2578 $out .= "<b>" . $langs->trans("Dolibarr") . ":</b> " . DOL_VERSION . " - https://www.dolibarr.org<br>\n";
2579 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
2580 $out .= "<b>" . $langs->trans("LevelOfFeature") . ":</b> " . getDolGlobalInt('MAIN_FEATURES_LEVEL') . "<br>\n";
2581 }
2582 if ($user instanceof User) {
2583 $out .= "<b>" . $langs->trans("Login") . ":</b> " . $user->login . "<br>\n";
2584 }
2585 if (function_exists("phpversion")) {
2586 $out .= "<b>" . $langs->trans("PHP") . ":</b> " . phpversion() . "<br>\n";
2587 }
2588 $out .= "<b>" . $langs->trans("Server") . ":</b> " . (isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '') . "<br>\n";
2589 if (function_exists("php_uname")) {
2590 $out .= "<b>" . $langs->trans("OS") . ":</b> " . php_uname() . "<br>\n";
2591 }
2592 $out .= "<b>" . $langs->trans("UserAgent") . ":</b> " . (isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '') . "<br>\n";
2593 $out .= "<br>\n";
2594 $out .= "<b>" . $langs->trans("RequestedUrl") . ":</b> " . (isset($_SERVER["REQUEST_URI"]) ? dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT) : '') . "<br>\n";
2595 $out .= "<b>" . $langs->trans("Referer") . ":</b> " . (isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '') . "<br>\n";
2596 $out .= "<b>" . $langs->trans("MenuManager") . ":</b> " . (isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '') . "<br>\n";
2597 $out .= "<br>\n";
2598 $syslog .= "url=" . (isset($_SERVER["REQUEST_URI"]) ? dol_escape_htmltag($_SERVER["REQUEST_URI"]) : '');
2599 $syslog .= ", query_string=" . (isset($_SERVER["QUERY_STRING"]) ? dol_escape_htmltag($_SERVER["QUERY_STRING"]) : '');
2600 } else { // Mode CLI
2601 $out .= '> ' . $langs->transnoentities("ErrorInternalErrorDetected") . ":\n" . $argv[0] . "\n";
2602 $syslog .= "pid=" . dol_getmypid();
2603 }
2604
2605 if (!empty($conf->modules)) {
2606 $out .= "<b>" . $langs->trans("Modules") . ":</b> " . implode(', ', $conf->modules) . "<br>\n";
2607 }
2608
2609 if (is_object($db)) {
2610 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
2611 $out .= "<b>" . $langs->trans("DatabaseTypeManager") . ":</b> " . $db->type . "<br>\n";
2612 $lastqueryerror = $db->lastqueryerror();
2613 if (!utf8_check($lastqueryerror)) {
2614 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
2615 }
2616 $out .= "<b>" . $langs->trans("RequestLastAccessInError") . ":</b> " . ($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
2617 $out .= "<b>" . $langs->trans("ReturnCodeLastAccessInError") . ":</b> " . ($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
2618 $out .= "<b>" . $langs->trans("InformationLastAccessInError") . ":</b> " . ($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
2619 $out .= "<br>\n";
2620 } else { // Mode CLI
2621 // No dol_escape_htmltag for output, we are in CLI mode
2622 $out .= '> ' . $langs->transnoentities("DatabaseTypeManager") . ":\n" . $db->type . "\n";
2623 $out .= '> ' . $langs->transnoentities("RequestLastAccessInError") . ":\n" . ($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
2624 $out .= '> ' . $langs->transnoentities("ReturnCodeLastAccessInError") . ":\n" . ($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
2625 $out .= '> ' . $langs->transnoentities("InformationLastAccessInError") . ":\n" . ($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
2626 }
2627 $syslog .= ", sql=" . $db->lastquery();
2628 $syslog .= ", db_error=" . $db->lasterror();
2629 }
2630
2631 if ($error || $errors) {
2632 // Merge all into $errors array
2633 if (is_array($error) && is_array($errors)) {
2634 $errors = array_merge($error, $errors);
2635 } elseif (is_array($error)) { // deprecated, use second parameters
2636 $errors = $error;
2637 } elseif (is_array($errors) && !empty($error)) {
2638 $errors = array_merge(array($error), $errors);
2639 } elseif (!empty($error)) {
2640 $errors = array_merge(array($error), array($errors));
2641 }
2642
2643 $langs->load("errors");
2644
2645 foreach ($errors as $msg) {
2646 if (empty($msg)) {
2647 continue;
2648 }
2649 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
2650 $out .= "<b>" . $langs->trans("Message") . ":</b> " . dol_escape_htmltag($msg) . "<br>\n";
2651 } else { // Mode CLI
2652 $out .= '> ' . $langs->transnoentities("Message") . ":\n" . $msg . "\n";
2653 }
2654 $syslog .= ", msg=" . $msg;
2655 }
2656 }
2657 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
2658 xdebug_print_function_stack();
2659 $out .= '<b>XDebug information:</b>' . "<br>\n";
2660 $out .= 'File: ' . xdebug_call_file() . "<br>\n";
2661 $out .= 'Line: ' . xdebug_call_line() . "<br>\n";
2662 $out .= 'Function: ' . xdebug_call_function() . "<br>\n";
2663 $out .= "<br>\n";
2664 }
2665
2666 // Return a http header with error code if possible
2667 if (!headers_sent()) {
2668 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
2669 top_httphead();
2670 }
2671 //http_response_code(500); // If we use 500, message is not output with some command line tools
2672 http_response_code(202); // If we use 202, this is not really an error message, but this allow to output message on command line tools
2673 }
2674
2675 if (empty($dolibarr_main_prod)) {
2676 print $out;
2677 } else {
2678 if (empty($langs->defaultlang)) {
2679 $langs->setDefaultLang();
2680 }
2681 $langs->loadLangs(array("main", "errors")); // Reload main because language may have been set only on previous line so we have to reload files we need.
2682 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
2683 print 'This website or feature is currently temporarily not available or failed after a technical error.<br><br>This may be due to a maintenance operation. Current status of operation (' . dol_print_date(dol_now(), 'dayhourrfc') . ') are on next line...<br><br>' . "\n";
2684 print $langs->trans("DolibarrHasDetectedError") . '. ';
2685 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
2686 if (!defined("MAIN_CORE_ERROR")) {
2687 define("MAIN_CORE_ERROR", 1);
2688 }
2689 }
2690
2691 dol_syslog("Error " . $syslog, LOG_ERR);
2692}
2693
2704function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
2705{
2706 global $langs;
2707
2708 if (empty($email)) {
2709 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
2710 }
2711
2712 $langs->load("errors");
2713 $now = dol_now();
2714
2715 print '<br><div class="center login_main_message"><div class="' . $morecss . '">';
2716 print $langs->trans("ErrorContactEMail", $email, $prefixcode . '-' . dol_print_date($now, '%Y%m%d%H%M%S'));
2717 if ($errormessage) {
2718 print '<br><br>' . $errormessage;
2719 }
2720 if (is_array($errormessages) && count($errormessages)) {
2721 foreach ($errormessages as $mesgtoshow) {
2722 print '<br><br>' . $mesgtoshow;
2723 }
2724 }
2725 print '</div></div>';
2726}
2727
2744function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $param = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
2745{
2746 print getTitleFieldOfList($name, 0, $file, $field, $begin, $param, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
2747}
2748
2767function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
2768{
2769 global $langs, $form;
2770 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
2771
2772 if ($moreattrib == 'class="right"') {
2773 $prefix .= 'right '; // For backward compatibility
2774 }
2775
2776 $tooltip = (string) $tooltip; // In case $tooltip is null
2777
2778 $sortorder = strtoupper((string) $sortorder);
2779 $out = '';
2780 $sortimg = '';
2781
2782 $tag = 'th';
2783 if ($thead == 2) {
2784 $tag = 'div';
2785 }
2786
2787 $tmpsortfield = explode(',', (string) $sortfield);
2788 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
2789 $tmpfield = explode(',', $field);
2790 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
2791
2792 if (strpos((string) $tooltip, ':') !== false) {
2793 $tmptooltip = explode(':', (string) $tooltip);
2794 } else {
2795 $tmptooltip = array($tooltip);
2796 }
2797
2798 $wrapcolumntitle = (empty($forcenowrapcolumntitle) || (!empty($tmptooltip[2]) && $tmptooltip[2] == '-1'));
2799
2800 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && $wrapcolumntitle) {
2801 $prefix = 'wrapcolumntitle ' . $prefix;
2802 }
2803
2804 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
2805 // If field is used as sort criteria we use a specific css class liste_titre_sel
2806 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
2807 $liste_titre = 'liste_titre';
2808 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
2809 $liste_titre = 'liste_titre_sel';
2810 }
2811
2812 $tagstart = '<' . $tag . ' class="' . $prefix . $liste_titre . '" ' . $moreattrib;
2813 //$out .= (($field && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && preg_match('/^[a-zA-Z_0-9\s\.\-:&;]*$/', $name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
2814 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && $wrapcolumntitle && !dol_textishtml($name)) ? ' title="' . dolPrintHTMLForAttribute($langs->trans($name)) . '"' : '';
2815 $tagstart .= '>';
2816
2817 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
2818 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
2819 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
2820 $options = preg_replace('/&+/i', '&', $options);
2821 if (!preg_match('/^&/', $options)) {
2822 $options = '&' . $options;
2823 }
2824
2825 $sortordertouseinlink = '';
2826 if ($field1 != $sortfield1) { // We are on another field than current sorted field
2827 if (preg_match('/^DESC/i', $sortorder)) {
2828 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
2829 } else { // We reverse the var $sortordertouseinlink
2830 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
2831 }
2832 } else { // We are on field that is the first current sorting criteria
2833 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
2834 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
2835 } else {
2836 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
2837 }
2838 }
2839 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
2840 $out .= '<a class="reposition" href="' . dolBuildUrl($file, ['sortfield' => $field, 'sortorder' => $sortordertouseinlink, 'begin' => $begin]) . $options . '"';
2841 //$out .= (getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') ? '' : ' title="'.dol_escape_htmltag($langs->trans($name)).'"');
2842 $out .= '>';
2843 }
2844 if ($tooltip && $tmptooltip[0]) {
2845 // You can also use 'TranslationString:[keyfortooltiponclick]:[tooltipdirection]' for a tooltip on click or to change tooltip position.
2846 $out .= $form->textwithpicto($langs->trans((string) $name), $langs->trans((string) $tmptooltip[0]), (empty($tmptooltip[2]) ? '1' : $tmptooltip[2]), 'help', ((!empty($tmptooltip[2]) && $tmptooltip[2] == '-1') ? 'paddingrightonly' : ''), 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_' . str_replace('.', '_', $field) . '_' . $tmptooltip[1]));
2847 } else {
2848 $out .= $langs->trans((string) $name);
2849 }
2850
2851 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
2852 $out .= '</a>';
2853 }
2854
2855 if (empty($thead) && $field) { // If this is a sort field
2856 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
2857 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
2858 $options = preg_replace('/&+/i', '&', $options);
2859 if (!preg_match('/^&/', $options)) {
2860 $options = '&' . $options;
2861 }
2862
2863 if (!$sortorder || ($field1 != $sortfield1)) {
2864 // Nothing
2865 } else {
2866 if (preg_match('/^DESC/', $sortorder)) {
2867 $sortimg .= '<span class="nowrap">' . img_up("Z-A", 0, 'paddingright') . '</span>';
2868 }
2869 if (preg_match('/^ASC/', $sortorder)) {
2870 $sortimg .= '<span class="nowrap">' . img_down("A-Z", 0, 'paddingright') . '</span>';
2871 }
2872 }
2873 }
2874
2875 $tagend = '</' . $tag . '>';
2876
2877 $out = $tagstart . $sortimg . $out . $tagend;
2878
2879 return $out;
2880}
2881
2890function print_titre($title)
2891{
2892 dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
2893
2894 print '<div class="titre">' . $title . '</div>';
2895}
2896
2908function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
2909{
2910 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
2911}
2912
2927function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '', $morecssonpicto = 'widthpictotitle')
2928{
2929 $return = '';
2930
2931 if ($picto == 'setup') {
2932 $picto = 'generic';
2933 }
2934
2935 $return .= "\n";
2936 $return .= '<table ' . ($id ? 'id="' . $id . '" ' : '') . 'class="centpercent notopnoleftnoright table-fiche-title' . ($morecssontable ? ' ' . $morecssontable : '') . '">'; // margin bottom must be same than into print_barre_list
2937 $return .= '<tr class="toptitle">';
2938 if ($picto) {
2939 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">' . img_picto('', $picto, 'class="valignmiddle pictotitle'.($morecssonpicto ? ' '.$morecssonpicto : '').'"', $pictoisfullpath) . '</td>';
2940 }
2941 $return .= '<td class="nobordernopadding valignmiddle col-title">';
2942 $return .= '<div class="titre inline-block">';
2943 $return .= '<span class="inline-block valignmiddle print-barre-liste">' . $title . '</span>'; // $title is already HTML sanitized content
2944 $return .= '</div>';
2945 $return .= '</td>';
2946 if (dol_strlen($morehtmlcenter)) {
2947 $return .= '<td class="nobordernopadding center valignmiddle col-center">' . $morehtmlcenter . '</td>';
2948 }
2949 if (dol_strlen($morehtmlright)) {
2950 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">' . $morehtmlright . '</td>';
2951 }
2952 $return .= '</tr></table>' . "\n";
2953
2954 return $return;
2955}
2956
2980function print_barre_liste($title, $page, $file, $options = '', $sortfield = '', $sortorder = '', $morehtmlcenter = '', $num = -1, $totalnboflines = '', $picto = 'generic', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limit = -1, $selectlimitsuffix = 0, $hidenavigation = 0, $pagenavastextinput = 0, $morehtmlrightbeforearrow = '')
2981{
2982 global $conf, $langs;
2983
2984 $savlimit = $limit;
2985 $savtotalnboflines = $totalnboflines;
2986 if (is_numeric($totalnboflines)) {
2987 $totalnboflines = abs($totalnboflines);
2988 }
2989
2990 // Detect if there is a subtitle
2991 $subtitle = '';
2992 $tmparray = preg_split('/<br>/i', $title, 2);
2993 if (!empty($tmparray[1])) {
2994 $title = $tmparray[0];
2995 $subtitle = $tmparray[1];
2996 }
2997
2998 $page = (int) $page;
2999
3000 if ($picto == 'setup') {
3001 $picto = 'title_setup';
3002 }
3003 if (($conf->browser->name == 'ie') && $picto == 'generic') {
3004 $picto = 'title.gif';
3005 }
3006 if ($limit < 0) {
3007 $limit = $conf->liste_limit;
3008 }
3009
3010 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
3011 $nextpage = 1;
3012 } else {
3013 $nextpage = 0;
3014 }
3015 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-selectlimitsuffix='.$selectlimitsuffix.'-hidenavigation='.$hidenavigation;
3016
3017 print "\n";
3018 print "<!-- Begin print_barre_liste -->\n";
3019 print '<table class="centpercent notopnoleftnoright table-fiche-title' . ($morecss ? ' ' . $morecss : '') . '">';
3020 print '<tr class="toptitle">'; // margin bottom must be same than into load_fiche_tire
3021
3022 // Left
3023
3024 if ($picto && $title) {
3025 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">';
3026 print img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath);
3027 print '</td>';
3028 }
3029
3030 print '<td class="nobordernopadding valignmiddle col-title">';
3031 print '<div class="titre inline-block nowrap">';
3032 print '<span class="inline-block valignmiddle print-barre-liste">' . $title . '</span>'; // $title may contains HTML like a combo list from page consumption.php, so we do not use dolPrintLabel here()
3033 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') {
3034 if (is_numeric($totalnboflines) && (int) $totalnboflines > 0) {
3035 print '<span class="opacitymedium colorblack marginleftonly totalnboflines valignmiddle" title="' . $langs->trans("NbRecordQualified") . '">(' . $totalnboflines . ')</span>';
3036 } else {
3037 print '<span class="opacitymedium colorblack marginleftonly totalnboflines valignmiddle">(' . $totalnboflines . ')</span>';
3038 }
3039 }
3040 print '</div>';
3041 if (!empty($subtitle)) {
3042 print '<br><div class="subtitle inline-block hideonsmartphone">' . $subtitle . '</div>';
3043 }
3044 print '</td>';
3045
3046 // Center
3047 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
3048 print '<td class="nobordernopadding center valignmiddle col-center">' . $morehtmlcenter . '</td>';
3049 }
3050
3051 // Right
3052 print '<td class="nobordernopadding valignmiddle right col-right">';
3053 print '<input type="hidden" name="pageplusoneold" value="' . ((int) $page + 1) . '">';
3054 $query = [];
3055 parse_str($options, $query);
3056 if ($sortfield) {
3057 $query += ['sortfield' => $sortfield];
3058 }
3059 if ($sortorder) {
3060 $query += ['sortorder' => $sortorder];
3061 }
3062
3063 $options = '&' . http_build_query($query);
3064 if ($page) {
3065 $query = array_merge($query, ['page' => $page]);
3066 }
3067 // Show navigation bar
3068 $pagelist = '';
3069 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
3070 if ($totalnboflines) { // If we know total nb of lines
3071 // Define nb of extra page links before and after selected page + ... + first or last
3072 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
3073
3074 if ($limit > 0) {
3075 $nbpages = ceil($totalnboflines / $limit);
3076 } else {
3077 $nbpages = 1;
3078 }
3079 $cpt = ($page - $maxnbofpage);
3080 if ($cpt < 0) {
3081 $cpt = 0;
3082 }
3083
3084 if ($cpt >= 1) {
3085 if (empty($pagenavastextinput)) {
3086 $query['page'] = 0;
3087 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">1</a></li>';
3088 if ($cpt > 2) {
3089 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
3090 } elseif ($cpt == 2) {
3091 $query['page'] = 0;
3092 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">2</a></li>';
3093 }
3094 }
3095 }
3096
3097 do {
3098 if ($pagenavastextinput) {
3099 if ($cpt == $page) {
3100 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="' . ($totalnboflines > 100 ? 'width40' : 'width25') . ' center pageplusone heightofcombo" name="pageplusone" value="' . ($page + 1) . '"></li>';
3101 $pagelist .= '/';
3102 }
3103 } else {
3104 if ($cpt == $page) {
3105 $pagelist .= '<li class="pagination"><span class="active">' . ($page + 1) . '</span></li>';
3106 } else {
3107 $query['page'] = $cpt;
3108 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . ($cpt + 1) . '</a></li>';
3109 }
3110 }
3111 $cpt++;
3112 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
3113
3114 if (empty($pagenavastextinput)) {
3115 if ($cpt < $nbpages) {
3116 if ($cpt < $nbpages - 2) {
3117 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
3118 } elseif ($cpt == $nbpages - 2) {
3119 $query['page'] = ($nbpages - 2);
3120 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . ($nbpages - 1) . '</a></li>';
3121 }
3122 $query['page'] = ($nbpages - 1);
3123 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . $nbpages . '</a></li>';
3124 }
3125 } else {
3126 $query['page'] = ($nbpages - 1);
3127 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . $nbpages . '</a></li>';
3128 }
3129 } else {
3130 $pagelist .= '<li class="pagination"><span class="active">' . ($page + 1) . "</li>";
3131 }
3132 }
3133
3134 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
3135 // Show the combolist to select number of record per page and the navigation arrows.
3136 print_fleche_navigation($page, $file, $options, $nextpage, $pagelist, $morehtmlright, $savlimit, $totalnboflines, $selectlimitsuffix, $morehtmlrightbeforearrow, $hidenavigation); // output the div and ul for previous/last completed with page numbers into $pagelist
3137 }
3138
3139 // js to autoselect page field on focus
3140 if ($pagenavastextinput) {
3141 print ajax_autoselect('.pageplusone');
3142 }
3143
3144 print '</td>';
3145 print '</tr>';
3146
3147 print "</table>\n";
3148
3149 // Center
3150 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
3151 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">' . $morehtmlcenter . '</div>';
3152 }
3153
3154 print "<!-- End title -->\n\n";
3155}
3156
3173function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $selectlimitsuffix = '', $beforearrows = '', $hidenavigation = 0)
3174{
3175 global $conf, $langs;
3176
3177 print '<div class="pagination"><ul>';
3178 if ($beforearrows) {
3179 print '<li class="paginationbeforearrows">';
3180 print $beforearrows;
3181 print '</li>';
3182 }
3183
3184 if (empty($hidenavigation)) {
3185 if ((int) $limit > 0 && (empty($selectlimitsuffix) || !is_numeric($selectlimitsuffix))) {
3186 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
3187 $pagesizechoices .= ',5000:5000';
3188 //$pagesizechoices .= ',10000:10000'; // Memory trouble on most browsers
3189 //$pagesizechoices .= ',20000:20000'; // Memory trouble on most browsers
3190 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
3191 //$pagesizechoices .= ',2:2';
3192 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
3193 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
3194 }
3195
3196 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
3197 print '<li class="pagination">';
3198 print '<input onfocus="this.value=null;" onchange="this.blur();" class="flat selectlimit nopadding maxwidth75 right pageplusone" id="limit" name="limit" list="limitlist" title="' . dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")) . '" value="' . $limit . '">';
3199 print '<datalist id="limitlist">';
3200 } else {
3201 print '<li class="paginationcombolimit valignmiddle">';
3202 print '<select id="limit' . (is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix) . '" name="'.(is_numeric($selectlimitsuffix) ? 'limit' : $selectlimitsuffix).'" class="flat selectlimit nopadding maxwidth75 center' . (is_numeric($selectlimitsuffix) ? '' : ' ' . $selectlimitsuffix) . '" title="' . dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")) . '">';
3203 }
3204 $tmpchoice = explode(',', $pagesizechoices);
3205 $tmpkey = $limit . ':' . $limit;
3206 if (!in_array($tmpkey, $tmpchoice)) {
3207 $tmpchoice[$tmpkey] = $tmpkey;
3208 }
3209 $tmpkey = $conf->liste_limit . ':' . $conf->liste_limit;
3210 if (!in_array($tmpkey, $tmpchoice)) {
3211 $tmpchoice[$tmpkey] = $tmpkey;
3212 }
3213 asort($tmpchoice, SORT_NUMERIC);
3214 foreach ($tmpchoice as $val) {
3215 $selected = '';
3216 $tmp = explode(':', $val);
3217 $key = $tmp[0];
3218 $val = $tmp[1];
3219 if ($key != '' && $val != '') {
3220 if ((int) $key == (int) $limit) {
3221 $selected = ' selected="selected"';
3222 }
3223 print '<option name="' . $key . '"' . $selected . '>' . dol_escape_htmltag($val) . '</option>' . "\n";
3224 }
3225 }
3226 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
3227 print '</datalist>';
3228 } else {
3229 print '</select>';
3230 print ajax_combobox("limit" . (is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix), array(), 0, 0, 'resolve', '-1', 'limit');
3231 //print ajax_combobox("limit");
3232 }
3233
3234 if ($conf->use_javascript_ajax) {
3235 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
3236 <script>
3237 jQuery(document).ready(function () {
3238 jQuery(".selectlimit").change(function() {
3239 console.log("We change limit so we submit the form");
3240 $(this).parents(\'form:first\').submit();
3241 });
3242 });
3243 </script>
3244 ';
3245 }
3246 print '</li>';
3247 }
3248 if ($page > 0) {
3249 print '<li class="pagination paginationpage paginationpageleft"><a class="paginationprevious reposition" href="' . $file . '?page=' . ($page - 1) . $options . '"><i class="fa fa-chevron-left" title="' . dol_escape_htmltag($langs->trans("Previous")) . '"></i></a></li>';
3250 }
3251 if ($betweenarrows) {
3252 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
3253 print $betweenarrows;
3254 print '<!--</div>-->';
3255 }
3256 if ($nextpage > 0) {
3257 print '<li class="pagination paginationpage paginationpageright"><a class="paginationnext reposition" href="' . $file . '?page=' . ($page + 1) . $options . '"><i class="fa fa-chevron-right" title="' . dol_escape_htmltag($langs->trans("Next")) . '"></i></a></li>';
3258 }
3259 if ($afterarrows) {
3260 print '<li class="paginationafterarrows">';
3261 print $afterarrows;
3262 print '</li>';
3263 }
3264 }
3265 print '</ul></div>' . "\n";
3266}
3267
3268
3275function showTotalAmount($amount)
3276{
3277 return '<span class="amount">'.$amount.'</span>';
3278}
3279
3292function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
3293{
3294 require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
3295
3296 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
3297 $dimension *= 1000000;
3298 $unit -= 6;
3299 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
3300 $dimension *= 1000;
3301 $unit -= 3;
3302 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
3303 $dimension /= 1000000;
3304 $unit += 6;
3305 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
3306 $dimension /= 1000;
3307 $unit += 3;
3308 }
3309 // Special case when we want output unit into pound or ounce
3310 /* TODO
3311 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
3312 {
3313 $dimension = // convert dimension from standard unit into ounce or pound
3314 $unit = $forceunitoutput;
3315 }
3316 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
3317 {
3318 $dimension = // convert dimension from standard unit into ounce or pound
3319 $unit = $forceunitoutput;
3320 }*/
3321
3322 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
3323 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
3324 $ret .= ' ' . measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
3325
3326 return $ret;
3327}
3328
3329
3330
3339function yn($yesno, $format = 1, $color = 0)
3340{
3341 global $langs;
3342
3343 $result = 'unknown';
3344 $classname = '';
3345 if ($yesno === true || (int) $yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // To set to 'no' before the test because of the '== 0'
3346 $result = $langs->trans('yes');
3347 if ($format == 1 || $format == 3) {
3348 $result = $langs->trans("Yes");
3349 }
3350 if ($format == 2) {
3351 $result = '<input type="checkbox" value="1" checked disabled>';
3352 }
3353 if ($format == 3) {
3354 $result = '<input type="checkbox" value="1" checked disabled> ' . $result;
3355 }
3356 if ($format == 4 || !is_numeric($format)) {
3357 $result = img_picto(is_numeric($format) ? '' : $format, 'check');
3358 }
3359
3360 $classname = 'ok';
3361 } else {
3362 $result = $langs->trans("no");
3363 if ($format == 1 || $format == 3) {
3364 $result = $langs->trans("No");
3365 }
3366 if ($format == 2) {
3367 $result = '<input type="checkbox" value="0" disabled>';
3368 }
3369 if ($format == 3) {
3370 $result = '<input type="checkbox" value="0" disabled> ' . $result;
3371 }
3372 if ($format == 4 || !is_numeric($format)) {
3373 $result = img_picto(is_numeric($format) ? '' : $format, 'uncheck');
3374 }
3375
3376 if ($color == 2) {
3377 $classname = 'ok';
3378 } else {
3379 $classname = 'error';
3380 }
3381 }
3382 if ($color) {
3383 return '<span class="' . $classname . '">' . $result . '</span>';
3384 }
3385 return $result;
3386}
3387
3388
3401function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0, $attop = 0)
3402{
3403 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
3404 if (!is_array($mesgs)) {
3405 $mesgs = trim((string) $mesgs);
3406 // If mesgs is a not an empty string
3407 if ($mesgs) {
3408 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
3409 return;
3410 }
3411 if ($attop) {
3412 array_unshift($_SESSION['dol_events'][$style], $mesgs);
3413 } else {
3414 $_SESSION['dol_events'][$style][] = $mesgs;
3415 }
3416 }
3417 } else {
3418 // If mesgs is an array
3419 foreach ($mesgs as $mesg) {
3420 $mesg = trim((string) $mesg);
3421 if ($mesg) {
3422 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
3423 return;
3424 }
3425 if ($attop) {
3426 array_unshift($_SESSION['dol_events'][$style], $mesgs);
3427 } else {
3428 $_SESSION['dol_events'][$style][] = $mesg;
3429 }
3430 }
3431 }
3432 }
3433}
3434
3448function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0, $attop = 0)
3449{
3450 if (empty($mesg) && empty($mesgs)) {
3451 dol_syslog("Try to add a message in stack, but value to add is empty message" . getCallerInfoString(), LOG_WARNING);
3452 } else {
3453 if ($messagekey) {
3454 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
3455 // TODO
3456 $mesg .= '';
3457 }
3458 if (empty($messagekey) || empty($_COOKIE["DOLUSER_HIDEMESSAGE" . $messagekey])) {
3459 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
3460 dol_print_error(null, 'Bad parameter style=' . $style . ' for setEventMessages');
3461 }
3462 if (empty($mesgs)) {
3463 setEventMessage((string) $mesg, $style, $noduplicate, $attop);
3464 } else {
3465 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
3466 setEventMessage($mesg, $style, $noduplicate, $attop); // Add message string if not already into array
3467 }
3468 setEventMessage($mesgs, $style, $noduplicate, $attop);
3469 }
3470 }
3471 }
3472}
3473
3483function dol_htmloutput_events($disabledoutputofmessages = 0)
3484{
3485 // Show mesgs
3486 if (isset($_SESSION['dol_events']['mesgs'])) {
3487 if (empty($disabledoutputofmessages)) {
3488 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
3489 }
3490 unset($_SESSION['dol_events']['mesgs']);
3491 }
3492 // Show errors
3493 if (isset($_SESSION['dol_events']['errors'])) {
3494 if (empty($disabledoutputofmessages)) {
3495 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
3496 }
3497 unset($_SESSION['dol_events']['errors']);
3498 }
3499
3500 // Show warnings
3501 if (isset($_SESSION['dol_events']['warnings'])) {
3502 if (empty($disabledoutputofmessages)) {
3503 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
3504 }
3505 unset($_SESSION['dol_events']['warnings']);
3506 }
3507}
3508
3523function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
3524{
3525 global $conf, $langs;
3526
3527 $ret = 0;
3528 $return = '';
3529 $out = '';
3530 $divstart = $divend = '';
3531
3532 // If inline message with no format, we add it.
3533 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
3534 $divstart = '<div class="' . $style . ' clearboth">';
3535 $divend = '</div>';
3536 }
3537
3538 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
3539 $langs->load("errors");
3540 $out .= $divstart;
3541 if (is_array($mesgarray) && count($mesgarray)) {
3542 foreach ($mesgarray as $message) {
3543 $ret++;
3544 $out .= $langs->trans($message);
3545 if ($ret < count($mesgarray)) {
3546 $out .= "<br>\n";
3547 }
3548 }
3549 }
3550 if ($mesgstring) {
3551 $ret++;
3552 $out .= $langs->trans($mesgstring);
3553 }
3554 $out .= $divend;
3555 }
3556
3557 if ($out) {
3558 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
3559 if ($style == "ok") {
3560 // For success messages (green), allow manual click to close immediately without fade
3561 $return = '<script nonce="' . getNonce() . '">
3562 /* jnotify(message, params) */
3563 $(document).ready(function() {
3564 $.jnotify(\'' . dol_escape_js($out) . '\', {
3565 delay: 3000,
3566 type: \'' . dol_escape_js($style) . '\',
3567 sticky: false,
3568 create: function($note) {
3569 $note.css("cursor", "pointer").click(function(e) {
3570 e.stopPropagation();
3571 $note.remove();
3572 });
3573 }
3574 });
3575 });
3576 </script>';
3577 } else {
3578 // For error and warning messages, close immediately on click without fade
3579 $return = '<script nonce="' . getNonce() . '">
3580 $(document).ready(function() {
3581 $.jnotify(\'' . dol_escape_js($out) . '\', {
3582 delay: 3000,
3583 type: \'' . dol_escape_js($style) . '\',
3584 sticky: true,
3585 create: function($note) {
3586 $note.find("a.jnotify-close").click(function(e) {
3587 e.stopPropagation();
3588 $note.remove();
3589 });
3590 }
3591 });
3592 });
3593 </script>';
3594 }
3595 } else {
3596 $return = $out;
3597 }
3598 }
3599
3600 return $return;
3601}
3602
3614function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
3615{
3616 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
3617}
3618
3632function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
3633{
3634 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
3635 return;
3636 }
3637
3638 $iserror = 0;
3639 $iswarning = 0;
3640 if (is_array($mesgarray)) {
3641 foreach ($mesgarray as $val) {
3642 if ($val && preg_match('/class="error"/i', $val)) {
3643 $iserror++;
3644 break;
3645 }
3646 if ($val && preg_match('/class="warning"/i', $val)) {
3647 $iswarning++;
3648 break;
3649 }
3650 }
3651 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
3652 $iserror++;
3653 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
3654 $iswarning++;
3655 }
3656 if ($style == 'error' || $style == 'errors') {
3657 $iserror++;
3658 }
3659 if ($style == 'warning' || $style == 'warnings') {
3660 $iswarning++;
3661 }
3662
3663 if ($iserror || $iswarning) {
3664 // Remove div from texts
3665 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
3666 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
3667 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
3668 // Remove div from texts array
3669 if (is_array($mesgarray)) {
3670 $newmesgarray = array();
3671 foreach ($mesgarray as $val) {
3672 if (is_string($val)) {
3673 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
3674 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
3675 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
3676 $newmesgarray[] = $tmpmesgstring;
3677 } else {
3678 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
3679 }
3680 }
3681 $mesgarray = $newmesgarray;
3682 }
3683 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
3684 } else {
3685 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
3686 }
3687}
3688
3700function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
3701{
3702 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
3703}
3704
3705
3706
3715function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
3716{
3717 if (empty($codelang)) {
3718 return '';
3719 }
3720
3721 if ($codelang == 'auto') {
3722 return '<span class="fa fa-language"></span>';
3723 }
3724
3725 $langtocountryflag = array(
3726 'ar_AR' => '',
3727 'ca_ES' => 'catalonia',
3728 'da_DA' => 'dk',
3729 'fr_CA' => 'mq',
3730 'sv_SV' => 'se',
3731 'sw_SW' => 'unknown',
3732 'AQ' => 'unknown',
3733 'CW' => 'unknown',
3734 'IM' => 'unknown',
3735 'JE' => 'unknown',
3736 'MF' => 'unknown',
3737 'BL' => 'unknown',
3738 'SX' => 'unknown'
3739 );
3740
3741 if (isset($langtocountryflag[$codelang])) {
3742 $flagImage = $langtocountryflag[$codelang];
3743 } else {
3744 $tmparray = explode('_', $codelang);
3745 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
3746 }
3747
3748 $morecss = '';
3749 $reg = array();
3750 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
3751 $morecss = $reg[1];
3752 $moreatt = "";
3753 }
3754
3755 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
3756 return '<span class="flag-sprite ' . strtolower($flagImage) . ($morecss ? ' ' . $morecss : '') . '"' . ($moreatt ? ' ' . $moreatt : '') . (!$notitlealt ? ' title="' . $codelang . '"' : '') . '></span>';
3757}
3758
3759
3771function printCommonFooter($zone = 'private')
3772{
3773 global $conf, $hookmanager, $user, $langs;
3774 global $action;
3775 global $micro_start_time;
3776
3777 if ($zone == 'private') {
3778 print "\n" . '<!-- Common footer for private page -->' . "\n";
3779 } else {
3780 print "\n" . '<!-- Common footer for public page -->' . "\n";
3781 }
3782
3783 // A div to store page_y POST parameter so we can read it using javascript
3784 print "\n<!-- A div to store page_y POST parameter -->\n";
3785 print '<div id="page_y" style="display: none;">' . (GETPOST('page_y') ? GETPOST('page_y') : '') . '</div>' . "\n";
3786
3787 $parameters = array('zone' => $zone);
3788 $tmpobject = null;
3789 // @phan-suppress-next-line PhanPluginConstantVariableNull
3790 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters, $tmpobject, $action); // Note that $action and $object may have been modified by some hooks
3791 if (empty($reshook)) {
3792 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
3793 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
3794 }
3795
3796 print "\n";
3797 if (!empty($conf->use_javascript_ajax)) {
3798 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
3799 print '<script>' . "\n";
3800 print 'jQuery(document).ready(function() {' . "\n";
3801
3802 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
3803 print "\n";
3804 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */' . "\n";
3805 print 'jQuery("li.menuhider").click(function(event) {';
3806 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }' . "\n";
3807 print ' console.log("We click on .menuhider");' . "\n";
3808 print ' $("body").toggleClass("sidebar-collapse")' . "\n";
3809 print '});' . "\n";
3810 }
3811
3812 // Management of focus and mandatory for fields
3813 if ($action == 'create' || $action == 'add' || $action == 'edit' || (empty($action) && (preg_match('/new\.php/', $_SERVER["PHP_SELF"]))) || ((empty($action) || $action == 'addline') && (preg_match('/card\.php/', $_SERVER["PHP_SELF"])))) {
3814 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */' . "\n";
3815 $relativepathstring = $_SERVER["PHP_SELF"];
3816 // Clean $relativepathstring
3817 if (constant('DOL_URL_ROOT')) {
3818 $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_URL_ROOT'), '/') . '/', '', $relativepathstring);
3819 }
3820 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
3821 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
3822 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
3823
3824 if (!empty($user->default_values[$relativepathstring]['focus'])) {
3825 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
3826 $qualified = 0;
3827 if ($defkey != '_noquery_') {
3828 $tmpqueryarraytohave = explode('&', $defkey);
3829 $foundintru = 0;
3830 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
3831 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
3832 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
3833 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
3834 $foundintru = 1;
3835 }
3836 }
3837 if (!$foundintru) {
3838 $qualified = 1;
3839 }
3840 //var_dump($defkey.'-'.$qualified);
3841 } else {
3842 $qualified = 1;
3843 }
3844
3845 if ($qualified) {
3846 print 'console.log("set the focus by executing jQuery(...).focus();")' . "\n";
3847 foreach ($defval as $paramkey => $paramval) {
3848 // Set focus on field
3849 print 'jQuery("input[name=\'' . $paramkey . '\']").focus();' . "\n";
3850 print 'jQuery("textarea[name=\'' . $paramkey . '\']").focus();' . "\n"; // TODO KO with ckeditor
3851 print 'jQuery("select[name=\'' . $paramkey . '\']").focus();' . "\n"; // Not really useful, but we keep it in case of.
3852 }
3853 }
3854 }
3855 }
3856 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
3857 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
3858 $qualified = 0;
3859 if ($defkey != '_noquery_') {
3860 $tmpqueryarraytohave = explode('&', $defkey);
3861 $foundintru = 0;
3862 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
3863 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
3864 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
3865 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
3866 $foundintru = 1;
3867 }
3868 }
3869 if (!$foundintru) {
3870 $qualified = 1;
3871 }
3872 //var_dump($defkey.'-'.$qualified);
3873 } else {
3874 $qualified = 1;
3875 }
3876
3877 if ($qualified) {
3878 print 'console.log("set the js code to manage fields that are set as mandatory");' . "\n";
3879
3880 foreach ($defval as $paramkey => $paramval) {
3881 // Solution 1: Add handler on submit to check if mandatory fields are empty
3882 print 'var form = $(\'[name="'.dol_escape_js($paramkey).'"]\').closest("form");'."\n";
3883 print "form.on('submit', function(event) {
3884 var submitter = \$(this).find(':submit:focus').get(0);
3885 var buttonName = submitter ? \$(submitter).attr('name') : 'save';
3886
3887 if (buttonName == 'cancel') {
3888 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
3889 return true;
3890 }
3891
3892 console.log('We did not click on cancel button but on something else, we check that field [name=".dol_escape_js($paramkey)."] is not empty');
3893
3894 var tmpvalue = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').val();
3895 let tmptypefield = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
3896
3897 if (tmptypefield == 'textarea') {
3898 // We must instead check the content of ckeditor
3899 var tmpeditor = (typeof CKEDITOR !== 'undefined') ? CKEDITOR.instances['".dol_escape_js($paramkey)."'] : null;
3900 if (tmpeditor) {
3901 tmpvalue = tmpeditor.getData();
3902 console.log('For textarea tmpvalue is '+tmpvalue);
3903 }
3904 }
3905
3906 let tmpvalueisempty = false;
3907 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '' || tmpvalue === -1 || tmpvalue === '-1') {
3908 tmpvalueisempty = true;
3909 }
3910 if (tmpvalue === '0' && (tmptypefield == 'select' || tmptypefield == 'input')) {
3911 tmpvalueisempty = true;
3912 }
3913 if (tmpvalueisempty && buttonName !== 'cancel') {
3914 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
3915 event.preventDefault(); // Stop submission of form to allow custom code to decide.
3916 event.stopPropagation(); // Stop other handlers.
3917
3918 alert('".dol_escape_js($langs->transnoentitiesnoconv("ErrorFieldRequired", $paramkey).' ('.$langs->transnoentitiesnoconv("CustomMandatoryFieldRule").')')."');
3919
3920 return false;
3921 }
3922 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
3923 return true;
3924 });
3925 \n";
3926
3927 // Solution 2: Add property 'required' on input
3928 // so browser will check value and try to focus on it when submitting the form.
3929 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
3930 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
3931 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
3932 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
3933 //print '// required on a select works only if key is "", so we add the required attributes but also we reset the key -1 or 0 to an empty string'."\n";
3934 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
3935 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
3936 // Add 'field required' class on closest td for all input elements : input, textarea and select
3937 //print '}, 500);'; // 500 milliseconds delay
3938
3939 // Now set the class "fieldrequired"
3940 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");' . "\n";
3941 }
3942
3943 // If we submit using the cancel button, we remove the required attributes
3944 print 'jQuery("input[name=\'cancel\']").click(function() {
3945 console.log("We click on cancel button so removed all required attribute");
3946 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
3947 });' . "\n";
3948 }
3949 }
3950 }
3951 }
3952
3953 print '});' . "\n";
3954
3955 // End of tuning
3956 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
3957 print "\n";
3958 print "/* JS CODE TO ENABLE to add memory info */\n";
3959 print 'window.console && console.log("';
3960 if (getDolGlobalString('MEMCACHED_SERVER')) {
3961 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER') . ' - ';
3962 }
3963 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
3964 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
3965 $micro_end_time = microtime(true);
3966 print ' - Build time: ' . ceil(1000 * ($micro_end_time - $micro_start_time)) . ' ms';
3967 }
3968
3969 if (function_exists("memory_get_usage")) {
3970 print ' - Mem: ' . memory_get_usage(); // Do not use true here, it seems it takes the peak amount
3971 }
3972 if (function_exists("memory_get_peak_usage")) {
3973 print ' - Real mem peak: ' . memory_get_peak_usage(true);
3974 }
3975 if (function_exists("zend_loader_file_encoded")) {
3976 print ' - Zend encoded file: ' . (zend_loader_file_encoded() ? 'yes' : 'no');
3977 }
3978 print '");' . "\n";
3979 }
3980
3981 print "\n" . '</script>' . "\n";
3982
3983 // Google Analytics
3984 // TODO Remove this, can be replaced with the hook printCommonFooter
3985 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
3986 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
3987 foreach ($tmptagarray as $tmptag) {
3988 print "\n";
3989 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
3990 print '
3991 <!-- Global site tag (gtag.js) - Google Analytics -->
3992 <script nonce="' . getNonce() . '" async src="https://www.googletagmanager.com/gtag/js?id=' . trim($tmptag) . '"></script>
3993 <script>
3994 window.dataLayer = window.dataLayer || [];
3995 function gtag(){dataLayer.push(arguments);}
3996 gtag(\'js\', new Date());
3997
3998 gtag(\'config\', \'' . trim($tmptag) . '\');
3999 </script>';
4000 print "\n";
4001 }
4002 }
4003 }
4004
4005 // Add Xdebug coverage of code
4006 if (defined('XDEBUGCOVERAGE')) {
4007 print_r(xdebug_get_code_coverage());
4008 }
4009
4010 // Output string from hooks
4011 if (!empty($hookmanager->resPrint)) {
4012 print $hookmanager->resPrint;
4013 }
4014
4015 // Add DebugBar data
4016 if ($user->hasRight('debugbar', 'read')) {
4017 global $debugbar;
4018 if ($debugbar instanceof DebugBar\DebugBar) {
4019 if (isset($debugbar['time'])) {
4020 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
4021 $debugbar['time']->stopMeasure('pageaftermaster');
4022 }
4023 print '<!-- Output debugbar data -->' . "\n";
4024 $renderer = $debugbar->getJavascriptRenderer();
4025 print $renderer->render();
4026 }
4027 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
4028 print "\n";
4029 print "<!-- Start of log output\n";
4030 //print '<div class="hidden">'."\n";
4031 foreach ($conf->logbuffer as $logline) {
4032 print $logline . "<br>\n";
4033 }
4034 //print '</div>'."\n";
4035 print "End of log output -->\n";
4036 }
4037 }
4038}
4039
4040
4047function dol_set_focus($selector)
4048{
4049 print "\n" . '<!-- Set focus onto a specific field -->' . "\n";
4050 print '<script nonce="' . getNonce() . '">jQuery(document).ready(function() { console.log("Force focus by dol_set_focus"); jQuery("' . dol_escape_js($selector) . '").focus(); });</script>' . "\n";
4051}
4052
4053
4054
4062function showSimpleHTMLTable($outputlangs, $object)
4063{
4064 global $conf;
4065
4066 $discountIsAvailable = false;
4067 $orderPositionHasNoPrice = false;
4068
4069 if (!property_exists($object->lines[0], "remise_percent") ||
4070 !property_exists($object->lines[0], "fk_unit") ||
4071 !property_exists($object->lines[0], "multicurrency_total_ttc") ||
4072 !property_exists($object->lines[0], "description") ||
4073 !property_exists($object->lines[0], "qty")) {
4074 return"";
4075 }
4076
4077 foreach ($object->lines as $order_position) {
4078 if (!property_exists($order_position, "price")) {
4079 $orderPositionHasNoPrice = true;
4080 break;
4081 }
4082
4083 if (!empty($order_position->remise_percent)) {
4084 $discountIsAvailable = true;
4085 break;
4086 }
4087 };
4088
4089 if ($orderPositionHasNoPrice) {
4090 return "";
4091 }
4092
4093 $discountHeader = $discountIsAvailable ? '<th style="width:120px">'.$outputlangs->trans("Discount").'</th>' : '';
4094
4095 $table = '<table border="0" cellpadding="1" cellspacing="1">';
4096 $table .= '
4097 <thead>
4098 <tr>
4099 <th style="width:50px; text-align:left">#</th>
4100 <th style="text-align:left">'.$outputlangs->trans("Description").'</th>
4101 <th style="width:120px; text-align:right;">'.$outputlangs->trans("Price").'</th>
4102 <th style="width:100px; text-align:right;">'.$outputlangs->trans("Quantity").'</th>
4103 <th style="width:120px; text-align:right;">'.$outputlangs->trans("Unit").'</th>'.
4104 $discountHeader.'
4105 <th style="width:120px; text-align:right;">'.$outputlangs->trans("Sum").'</th>
4106 </tr>
4107 </thead>
4108 <tbody>';
4109
4110 foreach ($object->lines as $index => $order_position) {
4111 $position = $index + 1;
4112 $price = price($order_position->price, 0, $outputlangs, 0, -1, -1, $conf->currency);
4113 $unit = measuringUnitString($order_position->fk_unit, '', null, 1);
4114 $total = price($order_position->multicurrency_total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency);
4115 $discount = $discountIsAvailable ? '<td style="text-align:center">'.$order_position->remise_percent.'%</td>' : "";
4116
4117 $table .= '
4118 <tr>
4119 <td>'.$position.'</td>
4120 <td>'.$order_position->description.'</td>
4121 <td style="text-align:right">'.$price.'</td>
4122 <td style="text-align:right">'.$order_position->qty.'</td>
4123 <td style="text-align:right">'.$unit.'</td>'.
4124 $discount.'
4125 <td style="text-align:right">'.$total.'</td>
4126 </tr>';
4127 }
4128 $table .= '</tbody></table>';
4129
4130 return $table;
4131}
4132
4139function showDirectDownloadLink($object)
4140{
4141 global $langs;
4142
4143 $out = '';
4144 $url = $object->getLastMainDocLink($object->element);
4145
4146 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe') . ' <span class="opacitymedium">' . $langs->trans("DirectDownloadLink") . '</span><br>';
4147 if ($url) {
4148 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="' . $url . '"></div>';
4149 $out .= ajax_autoselect("directdownloadlink", '');
4150 } else {
4151 $out .= '<div class="urllink">' . $langs->trans("FileNotShared") . '</div>';
4152 }
4153
4154 return $out;
4155}
4156
4157
4158
4168function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
4169{
4170 global $conf, $langs;
4171
4172 if (empty($conf->use_javascript_ajax)) {
4173 return '';
4174 }
4175
4176 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
4177
4178 if ($alldata == 1) {
4179 if ($isAllowedForPreview) {
4180 return array('target' => '_blank', 'css' => 'documentpreview', 'url' => DOL_URL_ROOT . '/document.php?modulepart=' . urlencode($modulepart) . '&attachment=0&file=' . urlencode($relativepath) . ($param ? '&' . $param : ''), 'mime' => dol_mimetype($relativepath));
4181 } else {
4182 return array();
4183 }
4184 }
4185
4186 // old behavior, return a string
4187 if ($isAllowedForPreview) {
4188 $tmpurl = DOL_URL_ROOT . '/document.php?modulepart=' . urlencode($modulepart) . '&attachment=0&file=' . urlencode($relativepath) . ($param ? '&' . $param : '');
4189 $title = $langs->transnoentities("Preview");
4190 //$title = '%27-alert(document.domain)-%27'; // An example of js injection into a corrupted title string, that should be blocked by the dol_escape_uri().
4191 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg"); // An example of tmpurl that should be blocked by the dol_escape_uri()
4192
4193 // We need to do a dol_escape_uri() on the full string after the javascript: because such parts are the URI and when we click on such links, a RFC3986 decode is done,
4194 // by the browser, converting the %27 (like when having param file=abc%27def), or when having a corrupted title), into a ', BEFORE interpreting the content that can be a js code.
4195 // Using the dol_escape_uri guarantee that we encode for URI so decode retrieve original expected value.
4196 return 'javascript:' . dol_escape_uri('document_preview(\'' . dol_escape_js($tmpurl) . '\', \'' . dol_escape_js(dol_mimetype($relativepath)) . '\', \'' . dol_escape_js($title) . '\')');
4197 } else {
4198 return '';
4199 }
4200}
4201
4202
4212function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
4213{
4214 global $langs;
4215 $out = '<script nonce="' . getNonce() . '">
4216 jQuery(document).ready(function () {
4217 jQuery("' . ((strpos($htmlname, '.') === 0 ? '' : '#') . $htmlname) . '").click(function() { jQuery(this).select(); } );
4218 });
4219 </script>';
4220 if ($addlink) {
4221 if ($textonlink === 'image') {
4222 $out .= ' <a href="' . $addlink . '" target="_blank" rel="noopener noreferrer">' . img_picto('', 'globe') . '</a>';
4223 } else {
4224 $out .= ' <a href="' . $addlink . '" target="_blank" rel="noopener noreferrer">' . $langs->trans("Link") . '</a>';
4225 }
4226 }
4227 return $out;
4228}
4229
4230
4231
4243function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
4244{
4245 $csstouse = 'badge';
4246 $csstouse .= (!empty($mode) ? ' badge-' . $mode : '');
4247 $csstouse .= (!empty($type) ? ' badge-' . $type : '');
4248 $csstouse .= (empty($params['css']) ? '' : ' ' . $params['css']);
4249
4250 $attr = array(
4251 'class' => $csstouse
4252 );
4253
4254 if (empty($html)) {
4255 $html = $label;
4256 }
4257
4258 if (!empty($url)) {
4259 $attr['href'] = $url;
4260 }
4261
4262 if ($mode === 'dot') {
4263 $attr['class'] .= ' classfortooltip';
4264 $attr['title'] = $html;
4265 $attr['aria-label'] = $label;
4266 $html = '';
4267 }
4268
4269 // Override attr
4270 if (!empty($params['attr']) && is_array($params['attr'])) {
4271 foreach ($params['attr'] as $key => $value) {
4272 if ($key == 'class') {
4273 $attr['class'] .= ' ' . $value;
4274 } elseif ($key == 'classOverride') {
4275 $attr['class'] = $value;
4276 } else {
4277 $attr[$key] = $value;
4278 }
4279 }
4280 }
4281
4282 // TODO: add hook
4283
4284 // escape all attribute
4285 $attr = array_map('dolPrintHTMLForAttribute', $attr);
4286
4287 $TCompiledAttr = array();
4288 foreach ($attr as $key => $value) {
4289 $TCompiledAttr[] = $key . '="' . $value . '"';
4290 }
4291
4292 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
4293
4294 $tag = !empty($url) ? 'a' : 'span';
4295
4296 return '<' . $tag . ' ' . $compiledAttributes . '>' . $html . '</' . $tag . '>';
4297}
4298
4299
4312function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
4313{
4314 global $conf;
4315
4316 $return = '';
4317 $dolGetBadgeParams = array();
4318
4319 if (!empty($params['badgeParams'])) {
4320 $dolGetBadgeParams = $params['badgeParams'];
4321 }
4322
4323 // TODO : add a hook
4324 if ($displayMode == 0) {
4325 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
4326 } elseif ($displayMode == 1) {
4327 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
4328 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
4329 // Use status with images (for backward compatibility)
4330 $return = '';
4331 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '') . (!empty($html) ? $html : $statusLabel) . (in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
4332 $htmlLabelShort = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '') . (!empty($html) ? $html : (!empty($statusLabelShort) ? $statusLabelShort : $statusLabel)) . (in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
4333
4334 // For small screen, we always use the short label instead of long label.
4335 if (!empty($conf->dol_optimize_smallscreen)) {
4336 if ($displayMode == 0) {
4337 $displayMode = 1;
4338 } elseif ($displayMode == 4) {
4339 $displayMode = 2;
4340 } elseif ($displayMode == 6) {
4341 $displayMode = 5;
4342 }
4343 }
4344
4345 // For backward compatibility. Image's filename are still in French, so we use this array to convert
4346 $statusImg = array(
4347 'status0' => 'statut0',
4348 'status1' => 'statut1',
4349 'status2' => 'statut2',
4350 'status3' => 'statut3',
4351 'status4' => 'statut4',
4352 'status5' => 'statut5',
4353 'status6' => 'statut6',
4354 'status7' => 'statut7',
4355 'status8' => 'statut8',
4356 'status9' => 'statut9'
4357 );
4358
4359 if (!empty($statusImg[$statusType])) {
4360 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
4361 } else {
4362 $htmlImg = img_picto($statusLabel, $statusType);
4363 }
4364
4365 if ($displayMode === 2) {
4366 $return = $htmlImg . ' ' . $htmlLabelShort;
4367 } elseif ($displayMode === 3) {
4368 $return = $htmlImg;
4369 } elseif ($displayMode === 4) {
4370 $return = $htmlImg . ' ' . $htmlLabel;
4371 } elseif ($displayMode === 5) {
4372 $return = $htmlLabelShort . ' ' . $htmlImg;
4373 } else { // $displayMode >= 6
4374 $return = $htmlLabel . ' ' . $htmlImg;
4375 }
4376 } elseif (!empty($displayMode)) {
4377 // Use new badge (MAIN_STATUS_USES_IMAGES already handled by the previous branch)
4378 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
4379
4380 $dolGetBadgeParams['attr']['class'] = 'badge-status';
4381 if (empty($dolGetBadgeParams['attr']['title'])) {
4382 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
4383 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
4384 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
4385 // And if we use tooltip, we can output title in HTML @phan-suppress-next-line PhanTypeInvalidDimOffset
4386 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr((string) $dolGetBadgeParams['attr']['title'], 1);
4387 }
4388
4389 if ($displayMode == 3) {
4390 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
4391 } elseif ($displayMode === 5) {
4392 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
4393 } else {
4394 $return = dolGetBadge(((empty($conf->dol_optimize_smallscreen) && $displayMode != 2) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
4395 }
4396 }
4397
4398 return $return;
4399}
4400
4401
4437function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
4438{
4439 global $hookmanager, $action, $object, $langs;
4440
4441 // If $url is an array, we must build a dropdown button or recursively iterate over each value
4442 if (is_array($url)) {
4443 // Loop on $url array to remove entries of disabled modules
4444 foreach ($url as $key => $subbutton) {
4445 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
4446 unset($url[$key]);
4447 }
4448 }
4449
4450 $out = '';
4451
4452 if (array_key_exists('areDropdownButtons', $params) && $params["areDropdownButtons"] === false) { // @phan-suppress-current-line PhanTypeInvalidDimOffset
4453 foreach ($url as $button) {
4454 if (!empty($button['lang'])) {
4455 $langs->load($button['lang']);
4456 }
4457 $label = $langs->trans($button['label']);
4458 $text = $button['text'] ?? '';
4459 $actionType = $button['actionType'] ?? '';
4460 $tmpUrl = DOL_URL_ROOT . $button['url'] . (empty($params['backtopage']) ? '' : '&amp;backtopage=' . urlencode($params['backtopage']));
4461 $id = $button['id'] ?? '';
4462 $userRight = $button['perm'] ?? 1;
4463 $button['params'] = $button['params'] ?? []; // @phan-suppress-current-line PhanPluginDuplicateExpressionAssignmentOperation
4464
4465 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
4466 }
4467 return $out;
4468 }
4469
4470 if (count($url) > 1) {
4471 $out .= '<div class="dropdown inline-block dropdown-holder">';
4472 $out .= '<a style="margin-right: auto;" class="dropdown-toggle classfortooltip butAction' . ($userRight ? '' : 'Refused') . '" title="' . dol_escape_htmltag($label) . '" data-toggle="dropdown">' . ($text ? $text : $label) . '</a>';
4473 $out .= '<div class="dropdown-content">';
4474 foreach ($url as $subbutton) {
4475 if (!empty($subbutton['lang'])) {
4476 $langs->load($subbutton['lang']);
4477 }
4478
4479 if (!empty($subbutton['urlraw'])) {
4480 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
4481 } else {
4482 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
4483 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
4484 }
4485
4486 $subbuttonparam = array();
4487 if (!empty($subbutton['attr'])) {
4488 $subbuttonparam['attr'] = $subbutton['attr'];
4489 }
4490 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown'] ?? false) : $params['isDropDown']);
4491
4492 $out .= dolGetButtonAction($subbutton['text'] ?? '', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
4493 }
4494 $out .= "</div>";
4495 $out .= "</div>";
4496 } else {
4497 foreach ($url as $subbutton) { // Should loop on 1 record only
4498 if (!empty($subbutton['lang'])) {
4499 $langs->load($subbutton['lang']);
4500 }
4501
4502 if (!empty($subbutton['urlraw'])) {
4503 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
4504 } else {
4505 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
4506 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
4507 }
4508
4509 $label = $langs->trans($subbutton['label']);
4510 $text = $subbutton['text'] ?? '';
4511 if (empty($text)) {
4512 $text = $label;
4513 $label = '';
4514 }
4515
4516 $out .= dolGetButtonAction($label, $text, 'default', $tmpurl, '', $subbutton['perm'], $params);
4517 }
4518 }
4519
4520 return $out;
4521 }
4522
4523 // Here, $url is a simple link
4524 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
4525 $class = "dropdown-item";
4526 } else {
4527 $class = 'butAction';
4528 if ($actionType == 'edit') {
4529 $class = 'butAction butActionEdit';
4530 } elseif ($actionType == 'email') {
4531 $class = 'butAction butActionEmail';
4532 } elseif ($actionType == 'clone') {
4533 $class = 'butAction butActionClone';
4534 } elseif ($actionType == 'cancel') {
4535 $class = 'butAction butActionDelete';
4536 } elseif ($actionType == 'danger' || $actionType == 'delete') {
4537 $class = 'butAction butActionDelete';
4538 if (!empty($url) && strpos($url, 'token=') === false) {
4539 $url .= '&token=' . newToken();
4540 }
4541 }
4542 }
4543 $attr = array(
4544 'class' => $class,
4545 'href' => empty($url) ? '' : $url,
4546 'title' => $label
4547 );
4548
4549 if (empty($text)) {
4550 $text = $label;
4551 $attr['title'] = ''; // if html not set, using label on title is redundant
4552 } else {
4553 $attr['title'] = $label;
4554 $attr['aria-label'] = $label;
4555 }
4556
4557 if (empty($userRight) || $userRight < 0) {
4558 $attr['class'] = 'butActionRefused';
4559 $attr['href'] = '';
4560 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
4561 $attr['title'] = ($attr['title'] ? $attr['title'] . (empty($userRight) ? '<br>' : '') : '');
4562 $attr['title'] .= ((empty($userRight) && empty($label)) ? $langs->trans('NotEnoughPermissions') : '');
4563 }
4564
4565 if (!empty($id)) {
4566 $attr['id'] = $id;
4567 }
4568
4569 // Override attr
4570 if (!empty($params['attr']) && is_array($params['attr'])) {
4571 foreach ($params['attr'] as $key => $value) {
4572 if ($key == 'class') {
4573 $attr['class'] .= ' ' . $value;
4574 } elseif ($key == 'classOverride') {
4575 $attr['class'] = $value;
4576 } else {
4577 $attr[$key] = $value;
4578 }
4579 }
4580 }
4581
4582 // automatic add tooltip when title is detected
4583 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
4584 $attr['class'] .= ' classfortooltip';
4585 }
4586
4587 // Js Confirm button
4588 if ($userRight && !empty($params['confirm'])) {
4589 if (!is_array($params['confirm'])) {
4590 $params['confirm'] = array();
4591 }
4592
4593 if (empty($params['confirm']['url'])) {
4594 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
4595 }
4596
4597 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
4598 $attr['data-confirm-url'] = $params['confirm']['url'];
4599 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
4600 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
4601 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
4602 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
4603 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
4604 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
4605
4606 $attr['class'] .= ' butActionConfirm';
4607 }
4608
4609 if (isset($attr['href']) && empty($attr['href'])) {
4610 unset($attr['href']);
4611 }
4612
4613 // TODO replace this $TCompiledAttr generation block by commonHtmlAttributeBuilder like line below
4614 // $TCompiledAttr = commonHtmlAttributeBuilder($attr, $params['use_unsecured_unescapedattr'] ?? []);
4615 $TCompiledAttr = array();
4616 foreach ($attr as $key => $value) {
4617 if (!empty($params['use_unsecured_unescapedattr']) && is_array($params['use_unsecured_unescapedattr']) && in_array($key, $params['use_unsecured_unescapedattr'])) {
4618 // Deprecated, forbidden.
4619 $value = dol_htmlentities($value, ENT_QUOTES | ENT_SUBSTITUTE);
4620 } elseif ($key == 'href') {
4621 $value = dolPrintHTMLForAttributeUrl($value);
4622 } else {
4623 $value = dolPrintHTMLForAttribute($value);
4624 }
4625
4626 $TCompiledAttr[] = $key . '="' . $value . '"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
4627 }
4628 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
4629
4630 $tag = !empty($attr['href']) ? 'a' : 'span';
4631
4632 $parameters = array(
4633 'TCompiledAttr' => $TCompiledAttr, // array
4634 'compiledAttributes' => $compiledAttributes, // string
4635 'attr' => $attr,
4636 'tag' => $tag,
4637 'label' => $label,
4638 'html' => $text,
4639 'actionType' => $actionType,
4640 'url' => $url,
4641 'id' => $id,
4642 'userRight' => $userRight,
4643 'params' => $params
4644 );
4645
4646 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
4647 if ($reshook < 0) {
4648 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
4649 }
4650
4651 if (empty($reshook)) {
4652 if (dol_textishtml($text)) { // If content already HTML encoded
4653 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . $text . '</span></' . $tag . '>';
4654 } else {
4655 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . dol_escape_htmltag($text) . '</span></' . $tag . '>';
4656 }
4657 } else {
4658 return $hookmanager->resPrint;
4659 }
4660}
4661
4692function commonHtmlAttributeBuilder($attr, array $unescapedAttr = [])
4693{
4694 $TCompiledAttr = array();
4695 if (empty($attr)) {
4696 return [];
4697 }
4698
4699 foreach ($attr as $key => $value) {
4700 // special boolean attributes case
4701 if (in_array($key, getListOfHtmlBooleanAttributes())) {
4702 if ($value) {
4703 $TCompiledAttr[$key] = $key;
4704 }
4705 continue;
4706 }
4707
4708 if (!empty($unescapedAttr) && in_array($key, $unescapedAttr)) {
4709 // Not recommended
4710 $value = dol_htmlentities((string) $value, ENT_QUOTES | ENT_SUBSTITUTE);
4711 } elseif ($key == 'href') {
4712 $value = dolPrintHTMLForAttributeUrl((string) $value);
4713 } else {
4714 $value = dolPrintHTMLForAttribute((string) $value);
4715 }
4716
4717 $TCompiledAttr[$key] = $key . '="' . $value . '"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
4718 }
4719
4720 return $TCompiledAttr;
4721}
4722
4738{
4739 return [
4740 // Input / Form
4741 'checked',
4742 'disabled',
4743 'readonly',
4744 'required',
4745 'autofocus',
4746 'multiple',
4747
4748 // Option
4749 'selected',
4750
4751 // Form / General
4752 'novalidate',
4753 'formnovalidate',
4754
4755 // Media
4756 'autoplay',
4757 'controls',
4758 'loop',
4759 'muted',
4760 'playsinline',
4761
4762 // Other elements
4763 'hidden',
4764 'open',
4765 'ismap',
4766 'reversed',
4767 'allowfullscreen',
4768 'itemscope',
4769 'nomodule',
4770 'defer',
4771 'async',
4772 'default',
4773 'inert',
4774 ];
4775}
4776
4777
4786function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
4787{
4788 if (empty($url)) {
4789 return '';
4790 }
4791
4792 $parsedUrl = parse_url($url);
4793 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
4794 return $url;
4795 }
4796
4797 if (!empty($parsedUrl['query'])) {
4798 // Use parse_str() function to parse the string passed via URL
4799 $urlQuery = '';
4800 parse_str($parsedUrl['query'], $urlQuery);
4801 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
4802 $url .= '&amp;backtopage=' . urlencode($params['backtopage']);
4803 }
4804 }
4805
4806 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
4807 $url = DOL_URL_ROOT . $url;
4808 }
4809
4810 return $url;
4811}
4812
4813
4820function dolGetButtonTitleSeparator($moreClass = "")
4821{
4822 return '<span class="button-title-separator ' . $moreClass . '" ></span>';
4823}
4824
4831function getFieldErrorIcon($fieldValidationErrorMsg)
4832{
4833 $out = '';
4834 if (!empty($fieldValidationErrorMsg)) {
4835 $out .= '<span class="field-error-icon classfortooltip" title="' . dol_escape_htmltag($fieldValidationErrorMsg, 1) . '" role="alert" >'; // role alert is used for accessibility
4836 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
4837 $out .= '</span>';
4838 }
4839
4840 return $out;
4841}
4842
4855function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
4856{
4857 global $langs, $user;
4858
4859 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
4860 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
4861 return '';
4862 }
4863 // Fix old picto fa-th-list to use fa-grid-vertical instead
4864 if ($iconClass == 'fa fa-th-list imgforviewmode') {
4865 $iconClass = ' fa fa-grip-horizontal imgforviewmode';
4866 }
4867
4868 $class = 'btnTitle';
4869 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
4870 $class .= ' btnTitlePlus';
4871 }
4872 $useclassfortooltip = 1;
4873
4874 if (!empty($params['morecss'])) {
4875 $class .= ' ' . $params['morecss'];
4876 }
4877
4878 $attr = array(
4879 'class' => $class,
4880 'href' => empty($url) ? '' : $url
4881 );
4882
4883 if (!empty($helpText)) {
4884 $attr['title'] = $helpText;
4885 } elseif ($label) { // empty($attr['title']) &&
4886 $attr['title'] = $label;
4887 $useclassfortooltip = 0;
4888 }
4889
4890 if ($status == 2) {
4891 $attr['class'] .= ' btnTitleSelected';
4892 } elseif ($status <= 0) {
4893 $attr['class'] .= ' refused';
4894
4895 $attr['href'] = '';
4896
4897 if ($status == -1) { // disable
4898 $attr['title'] = $langs->transnoentitiesnoconv("FeatureDisabled");
4899 } elseif ($status == 0) { // Not enough permissions
4900 $attr['title'] = $langs->transnoentitiesnoconv("NotEnoughPermissions");
4901 }
4902 }
4903
4904 if (!empty($attr['title']) && $useclassfortooltip) {
4905 $attr['class'] .= ' classfortooltip';
4906 }
4907
4908 if (!empty($id)) {
4909 $attr['id'] = $id;
4910 }
4911
4912 // Override attr
4913 if (!empty($params['attr']) && is_array($params['attr'])) {
4914 foreach ($params['attr'] as $key => $value) {
4915 if ($key == 'class') {
4916 $attr['class'] .= ' ' . $value;
4917 } elseif ($key == 'classOverride') {
4918 $attr['class'] = $value;
4919 } else {
4920 $attr[$key] = $value;
4921 }
4922 }
4923 }
4924
4925 if (isset($attr['href']) && empty($attr['href'])) {
4926 unset($attr['href']);
4927 }
4928
4929 // TODO : add a hook
4930
4931 // Generate attributes with escapement
4932 $TCompiledAttr = array();
4933 foreach ($attr as $key => $value) {
4934 $TCompiledAttr[] = $key . '="' . dol_escape_htmltag($value) . '"'; // Do not use dolPrintHTMLForAttribute() here, we must accept "javascript:string"
4935 }
4936
4937 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
4938
4939 $tag = (empty($attr['href']) ? 'span' : 'a');
4940
4941 $button = '<' . $tag . ' ' . $compiledAttributes . '>';
4942 $button .= '<span class="' . $iconClass . ' valignmiddle btnTitle-icon"></span>';
4943 if (!empty($params['forcenohideoftext'])) {
4944 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label' . (empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '') . '">' . $label . '</span>';
4945 }
4946 $button .= '</' . $tag . '>';
4947
4948 return $button;
4949}
4950
4951
4952
4966function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
4967{
4968 global $langs;
4969
4970 print '<div class="div-table-responsive-no-min">';
4971 print '<table class="noborder centpercent">';
4972 print '<tr class="liste_titre">';
4973
4974 print ($emptyColumns < 1) ? '<th>' : '<th colspan="' . ($emptyColumns + 1) . '">';
4975
4976 print '<span class="valignmiddle">' . $langs->trans($header) . '</span>';
4977
4978 if (!empty($link)) {
4979 if (!empty($arguments)) {
4980 print '<a href="' . DOL_URL_ROOT . '/' . $link . '?' . $arguments . '">';
4981 } else {
4982 print '<a href="' . DOL_URL_ROOT . '/' . $link . '">';
4983 }
4984 }
4985
4986 if ($number > -1) {
4987 print '<span class="badge marginleftonlyshort">' . $number . '</span>';
4988 } elseif (!empty($link)) {
4989 print '<span class="badge marginleftonlyshort">...</span>';
4990 }
4991
4992 if (!empty($link)) {
4993 print '</a>';
4994 }
4995
4996 print '</th>';
4997
4998 if ($number < 0 && !empty($link)) {
4999 print '<th class="right">';
5000 print '</th>';
5001 }
5002
5003 print '</tr>';
5004}
5005
5014function finishSimpleTable($addLineBreak = false)
5015{
5016 print '</table>';
5017 print '</div>';
5018
5019 if ($addLineBreak) {
5020 print '<br>';
5021 }
5022}
5023
5035function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
5036{
5037 global $langs;
5038
5039 if ($num === 0) {
5040 print '<tr class="oddeven">';
5041 print '<td colspan="' . $tableColumnCount . '"><span class="opacitymedium">' . $langs->trans($noneWord) . '</span></td>';
5042 print '</tr>';
5043 return;
5044 }
5045
5046 if ($nbofloop === 0) {
5047 // don't show a summary line
5048 return;
5049 }
5050
5051 /* Case already handled above, commented to satisfy phpstan.
5052 if ($num === 0) {
5053 $colspan = $tableColumnCount;
5054 } else
5055 */
5056 if ($num > $nbofloop) {
5057 $colspan = $tableColumnCount;
5058 } else {
5059 $colspan = $tableColumnCount - 1;
5060 }
5061
5062 if ($extraRightColumn) {
5063 $colspan--;
5064 }
5065
5066 print '<tr class="liste_total">';
5067
5068 if ($nbofloop > 0 && $num > $nbofloop) {
5069 print '<td colspan="' . $colspan . '" class="right">' . $langs->trans("XMoreLines", ($num - $nbofloop)) . '</td>';
5070 } else {
5071 print '<td colspan="' . $colspan . '" class="right"> ' . $langs->trans("Total") . '</td>';
5072 print '<td class="right centpercent">' . price($total) . '</td>';
5073 }
5074
5075 if ($extraRightColumn) {
5076 print '<td></td>';
5077 }
5078
5079 print '</tr>';
5080}
5081
5082
5083
5093function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
5094{
5095 global $langs;
5096
5097 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
5098
5099 $result = '<span class="clipboardCP' . ($showonlyonhover ? ' clipboardCPShowOnHover valignmiddle' : '') . '">';
5100 if ($texttoshow === 'none') {
5101 $result .= '<' . $tag . ' class="clipboardCPValue hidewithsize">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
5102 $result .= '<span class="clipboardCPValueToPrint"></span>';
5103 } elseif ($texttoshow) {
5104 $result .= '<' . $tag . ' class="clipboardCPValue hidewithsize">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
5105 $result .= '<span class="clipboardCPValueToPrint">' . dol_escape_htmltag($texttoshow, 1, 1) . '</span>';
5106 } else {
5107 $result .= '<' . $tag . ' class="clipboardCPValue">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
5108 }
5109 $result .= '<span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft pictomodule" title="' . dolPrintHTML($langs->trans("ClickToCopyToClipboard")) . '"></span>';
5110 $result .= img_picto('', 'tick', 'class="clipboardCPTick hidden paddingleft pictomodule"');
5111 $result .= '<span class="clipboardCPText"></span>';
5112 $result .= '</span>';
5113
5114 return $result;
5115}
5116
5117
5118
5136function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
5137{
5138 dol_syslog('show_actions_messaging::begin', LOG_DEBUG);
5139 global $user, $conf;
5140 global $form;
5141
5142 global $param, $massactionbutton;
5143
5144 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
5145
5146 // Check parameters
5147 if (!is_object($filterobj) && !is_object($objcon)) {
5148 dol_print_error(null, 'BadParameter');
5149 }
5150
5151 $histo = array();
5152 '@phan-var-force array<int,array{type:string,tododone:string,id:string,datestart:int|string,dateend:int|string,fulldayevent:int,note:string,message:string,percent:string,userid:string,login:string,userfirstname:string,userlastname:string,userphoto:string,msg_from?:string,contact_id?:string,socpeopleassigned?:int[],lastname?:string,firstname?:string,fk_element?:int,elementtype?:string,acode:string,alabel?:string,libelle?:string,apicto?:string}> $histo';
5153
5154 $numaction = 0;
5155 $now = dol_now();
5156
5157 $sortfield_list = explode(',', $sortfield);
5158 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
5159 $sanitized_sortfield_new_list = array();
5160 foreach ($sortfield_list as $sortfield_value) {
5161 $sanitized_sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)]; //@phan-suppress-current-line SqlInjection
5162 }
5163 $sanitized_sortfield_new = implode(',', $sanitized_sortfield_new_list);
5164
5165 $sql = null;
5166 $sql2 = null;
5167
5168 if (isModEnabled('agenda')) {
5169 // Search histo on actioncomm
5170 if (is_object($objcon) && $objcon->id > 0) {
5171 $sql = "SELECT DISTINCT a.id, a.label as label,";
5172 } else {
5173 $sql = "SELECT a.id, a.label as label,";
5174 }
5175 $sql .= " a.datep as dp,";
5176 $sql .= " a.note as message,";
5177 $sql .= " a.datep2 as dp2,";
5178 $sql .= " a.percent as percent, 'action' as type,";
5179 $sql .= " a.fk_element, a.elementtype,";
5180 $sql .= " a.fk_contact, a.fulldayevent,";
5181 $sql .= " a.email_from as msg_from,";
5182 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
5183 $sql .= " u.rowid as user_id, u.login as user_login, u.photo as user_photo, u.firstname as user_firstname, u.lastname as user_lastname";
5184 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
5185 $sql .= ", sp.lastname, sp.firstname";
5186 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
5187 $sql .= ", m.lastname, m.firstname";
5188 } elseif (is_object($filterobj) && in_array(get_class($filterobj), array('Commande', 'CommandeFournisseur', 'Product', 'Ticket', 'BOM', 'Contrat', 'Facture', 'FactureFournisseur', 'Propal', 'Expedition'))) {
5189 $sql .= ", o.ref";
5190 } else {
5191 if (is_object($filterobj) && !empty($filterobj->table_element) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
5192 $sql .= ", o.ref";
5193 }
5194 }
5195 $sql .= " FROM " . MAIN_DB_PREFIX . "actioncomm as a";
5196 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "user as u on u.rowid = a.fk_user_action";
5197 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_actioncomm as c ON a.fk_action = c.id";
5198
5199 $force_filter_contact = $filterobj instanceof User;
5200
5201 if (is_object($objcon) && $objcon->id > 0) {
5202 $force_filter_contact = true;
5203 $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "actioncomm_resources as r ON a.id = r.fk_actioncomm";
5204 $sql .= " AND r.element_type = '" . $db->escape($objcon->table_element) . "' AND r.fk_element = " . ((int) $objcon->id);
5205 }
5206
5207 if ((is_object($filterobj) && get_class($filterobj) == 'Societe') || (is_object($filterobj) && get_class($filterobj) == 'Contact')) {
5208 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "socpeople as sp ON a.fk_contact = sp.rowid";
5209 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
5210 $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "element_resources as er";
5211 $sql .= " ON er.resource_type = 'dolresource'";
5212 $sql .= " AND er.element_id = a.id";
5213 $sql .= " AND er.resource_id = " . ((int) $filterobj->id);
5214 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
5215 $sql .= ", " . MAIN_DB_PREFIX . "adherent as m";
5216 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
5217 $sql .= ", " . MAIN_DB_PREFIX . "commande_fournisseur as o";
5218 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
5219 $sql .= ", " . MAIN_DB_PREFIX . "product as o";
5220 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
5221 $sql .= ", " . MAIN_DB_PREFIX . "ticket as o";
5222 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
5223 $sql .= ", " . MAIN_DB_PREFIX . "bom_bom as o";
5224 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
5225 $sql .= ", " . MAIN_DB_PREFIX . "contrat as o";
5226 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
5227 $sql .= ", " . MAIN_DB_PREFIX . "facture as o";
5228 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
5229 $sql .= ", " . MAIN_DB_PREFIX . "facture_fourn as o";
5230 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
5231 $sql .= ", " . MAIN_DB_PREFIX . "commande as o";
5232 } elseif (is_object($filterobj) && get_class($filterobj) == 'Expedition') {
5233 $sql .= ", " . MAIN_DB_PREFIX . "expedition as o";
5234 } elseif (is_object($filterobj) && get_class($filterobj) == 'Propal') {
5235 $sql .= ", " . MAIN_DB_PREFIX . "propal as o";
5236 } else {
5237 if (is_object($filterobj) && !empty($filterobj->table_element) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
5238 $sql .= ", " . MAIN_DB_PREFIX . $filterobj->table_element . " as o";
5239 }
5240 }
5241 $sql .= " WHERE a.entity IN (" . getEntity('agenda') . ")";
5242 if (!$force_filter_contact) {
5243 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
5244 $sql .= " AND a.fk_soc = " . ((int) $filterobj->id);
5245 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
5246 $sql .= " AND a.fk_project = o.rowid AND a.fk_project = " . ((int) $filterobj->id);
5247 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
5248 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
5249 if ($filterobj->id) {
5250 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
5251 }
5252 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
5253 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order'";
5254 if ($filterobj->id) {
5255 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
5256 }
5257 } elseif (is_object($filterobj) && get_class($filterobj) == 'Expedition') {
5258 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'shipping'";
5259 if ($filterobj->id) {
5260 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
5261 }
5262 } elseif (is_object($filterobj) && get_class($filterobj) == 'Propal') {
5263 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'propal'";
5264 if ($filterobj->id) {
5265 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
5266 }
5267 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
5268 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
5269 if ($filterobj->id) {
5270 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
5271 }
5272 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
5273 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
5274 if ($filterobj->id) {
5275 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
5276 }
5277 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
5278 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
5279 if ($filterobj->id) {
5280 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
5281 }
5282 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
5283 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
5284 if ($filterobj->id) {
5285 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
5286 }
5287 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
5288 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
5289 if ($filterobj->id) {
5290 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
5291 }
5292 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contact' && $filterobj->id) {
5293 $sql .= " AND a.fk_contact = sp.rowid";
5294 $sql .= " AND a.fk_contact = " . ((int) $filterobj->id);
5295 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
5296 $sql .= " AND a.fk_element = o.rowid";
5297 if ($filterobj->id) {
5298 $sql .= " AND a.fk_element = " . ((int) $filterobj->id) . " AND a.elementtype = 'invoice'";
5299 }
5300 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
5301 $sql .= " AND a.fk_element = o.rowid";
5302 if ($filterobj->id) {
5303 $sql .= " AND a.fk_element = " . ((int) $filterobj->id) . " AND a.elementtype = 'invoice_supplier'";
5304 }
5305 } else {
5306 if (is_object($filterobj) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
5307 $sql .= " AND a.fk_element = o.rowid";
5308 $sql .= " AND a.elementtype = '" . $db->escape($filterobj->element) . "'";
5309 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
5310 }
5311 }
5312 } else {
5313 $sql .= " AND u.rowid = " . ((int) $filterobj->id);
5314 }
5315
5316 // Condition on actioncode
5317 if (!empty($actioncode) && $actioncode != '-1') {
5318 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
5319 if ($actioncode == 'AC_NON_AUTO') {
5320 $sql .= " AND c.type != 'systemauto'";
5321 } elseif ($actioncode == 'AC_ALL_AUTO') {
5322 $sql .= " AND c.type = 'systemauto'";
5323 } else {
5324 if ($actioncode == 'AC_OTH') {
5325 $sql .= " AND c.type != 'systemauto'";
5326 } elseif ($actioncode == 'AC_OTH_AUTO') {
5327 $sql .= " AND c.type = 'systemauto'";
5328 }
5329 }
5330 } else {
5331 if ($actioncode == 'AC_NON_AUTO') {
5332 $sql .= " AND c.type != 'systemauto'";
5333 } elseif ($actioncode == 'AC_ALL_AUTO') {
5334 $sql .= " AND c.type = 'systemauto'";
5335 } else {
5336 $sql .= " AND c.code = '" . $db->escape($actioncode) . "'";
5337 }
5338 }
5339 }
5340 if ($donetodo == 'todo') {
5341 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '" . $db->idate($now) . "'))";
5342 } elseif ($donetodo == 'done') {
5343 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '" . $db->idate($now) . "'))";
5344 }
5345 if (is_array($filters) && $filters['search_agenda_label']) {
5346 $sql .= natural_search('a.label', $filters['search_agenda_label']);
5347 }
5348 }
5349
5350 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
5351 if (
5352 isModEnabled('mailing') && !empty($objcon->email)
5353 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')
5354 ) {
5355 $langs->load("mails");
5356
5357 $sql2 = "SELECT m.rowid as id, m.titre as label, mc.date_envoi as dp, mc.date_envoi as dp2, '100' as percent, 'mailing' as type";
5358 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
5359 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
5360 $sql2 .= ", u.rowid as user_id, u.login as user_login, u.photo as user_photo, u.firstname as user_firstname, u.lastname as user_lastname"; // User that valid action
5361 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
5362 $sql2 .= ", '' as lastname, '' as firstname";
5363 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
5364 $sql2 .= ", '' as lastname, '' as firstname";
5365 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
5366 $sql2 .= ", '' as ref";
5367 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
5368 $sql2 .= ", '' as ref";
5369 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
5370 $sql2 .= ", '' as ref";
5371 }
5372 $sql2 .= " FROM " . MAIN_DB_PREFIX . "mailing as m, " . MAIN_DB_PREFIX . "mailing_cibles as mc, " . MAIN_DB_PREFIX . "user as u";
5373 $sql2 .= " WHERE mc.email = '" . $db->escape($objcon->email) . "'"; // Search is done on email.
5374 $sql2 .= " AND mc.statut = 1";
5375 $sql2 .= " AND u.rowid = m.fk_user_valid";
5376 $sql2 .= " AND mc.fk_mailing=m.rowid";
5377 }
5378
5379 $num = 0;
5380 $MAXWITHOUTPAGINATION = getDolGlobalInt('AGENDA_MAX_EVENTS_ON_PAGE_WITHOUT_PAGINATION', 100);
5381
5382 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
5383 if (!empty($sql) && !empty($sql2)) {
5384 $sql = $sql . " UNION " . $sql2;
5385 } elseif (empty($sql) && !empty($sql2)) {
5386 $sql = $sql2;
5387 }
5388
5389 //TODO Add navigation with this limits...
5390 $offset = 0;
5391 $limit = $MAXWITHOUTPAGINATION;
5392
5393 // Complete request and execute it with limit
5394 $sql .= $db->order($sanitized_sortfield_new, $sortorder);
5395 if ($limit) {
5396 $sql .= $db->plimit($limit + 1, $offset);
5397 }
5398
5399 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
5400
5401 $resql = $db->query($sql);
5402 if ($resql) {
5403 $i = 0;
5404 $num = $db->num_rows($resql);
5405
5406 $imaxinloop = ($limit ? min($num, $limit) : $num);
5407 while ($i < $imaxinloop) {
5408 $obj = $db->fetch_object($resql);
5409
5410 if ($obj->type == 'action') {
5411 $contactaction = new ActionComm($db);
5412 $contactaction->id = $obj->id;
5413 $result = $contactaction->fetchResources();
5414 if ($result < 0) {
5416 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
5417 }
5418
5419 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
5420 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
5421 $tododone = '';
5422 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
5423 $tododone = 'todo';
5424 }
5425
5426 $histo[$numaction] = array(
5427 'type' => $obj->type,
5428 'tododone' => $tododone,
5429 'id' => $obj->id,
5430 'datestart' => $db->jdate($obj->dp),
5431 'dateend' => $db->jdate($obj->dp2),
5432 'fulldayevent' => (int) $obj->fulldayevent,
5433 'note' => $obj->label,
5434 'message' => $obj->message,
5435 'percent' => $obj->percent,
5436
5437 'userid' => $obj->user_id,
5438 'login' => $obj->user_login,
5439 'userfirstname' => $obj->user_firstname,
5440 'userlastname' => $obj->user_lastname,
5441 'userphoto' => $obj->user_photo,
5442 'msg_from' => $obj->msg_from,
5443
5444 'contact_id' => $obj->fk_contact,
5445 'socpeopleassigned' => $contactaction->socpeopleassigned,
5446 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
5447 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
5448 'fk_element' => $obj->fk_element,
5449 'elementtype' => $obj->elementtype,
5450 // Type of event
5451 'acode' => $obj->acode,
5452 'alabel' => $obj->alabel,
5453 'libelle' => $obj->alabel, // deprecated
5454 'apicto' => $obj->apicto
5455 );
5456 } else {
5457 $histo[$numaction] = array(
5458 'type' => $obj->type,
5459 'tododone' => 'done',
5460 'id' => $obj->id,
5461 'datestart' => $db->jdate($obj->dp),
5462 'dateend' => $db->jdate($obj->dp2),
5463 'fulldayevent' => (int) $obj->fulldayevent,
5464 'note' => $obj->label,
5465 'message' => $obj->message,
5466 'percent' => $obj->percent,
5467 'acode' => $obj->acode,
5468
5469 'userid' => $obj->user_id,
5470 'login' => $obj->user_login,
5471 'userfirstname' => $obj->user_firstname,
5472 'userlastname' => $obj->user_lastname,
5473 'userphoto' => $obj->user_photo
5474 );
5475 }
5476
5477 $numaction++;
5478 $i++;
5479 }
5480 } else {
5482 }
5483 }
5484
5485 // Set $out to show events
5486 $out = '';
5487
5488 if (!isModEnabled('agenda')) {
5489 $langs->loadLangs(array("admin", "errors"));
5490 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
5491 }
5492
5493 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
5494 $delay_warning = getDolGlobalInt('MAIN_DELAY_ACTIONS_TODO') * 24 * 60 * 60;
5495
5496 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
5497 include_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
5498 require_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php';
5499 require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
5500
5501 $formactions = new FormActions($db);
5502
5503 $actionstatic = new ActionComm($db);
5504 $userstatic = new User($db);
5505 $contactstatic = new Contact($db);
5506 $userGetNomUrlCache = array();
5507 $contactGetNomUrlCache = array();
5508
5509 $out .= '<div class="filters-container" >';
5510 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
5511 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
5512
5513 if (
5514 $objcon && get_class($objcon) == 'Contact' &&
5515 (is_null($filterobj) || get_class($filterobj) == 'Societe')
5516 ) {
5517 $out .= '<input type="hidden" name="id" value="' . $objcon->id . '" />';
5518 } else {
5519 $out .= '<input type="hidden" name="id" value="' . $filterobj->id . '" />';
5520 }
5521 if (($filterobj && get_class($filterobj) == 'Societe')) {
5522 $out .= '<input type="hidden" name="socid" value="' . $filterobj->id . '" />';
5523 } else {
5524 $out .= '<input type="hidden" name="userid" value="' . $filterobj->id . '" />';
5525 }
5526
5527 $out .= "\n";
5528
5529 $out .= '<div class="div-table-responsive-no-min">';
5530 $out .= '<table class="noborder borderbottom centpercent">';
5531
5532 $out .= '<tr class="liste_titre">';
5533
5534 // Action column
5535 if ($conf->main_checkbox_left_column) {
5536 $out .= '<th class="liste_titre width50 middle">';
5537 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
5538 $out .= $searchpicto;
5539 $out .= '</th>';
5540 }
5541
5542 // Date
5543 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, 'nowraponall nopaddingleftimp ') . "\n";
5544
5545 $out .= '<th class="liste_titre hideonsmartphone"><strong class="hideonsmartphone">' . $langs->trans("Search") . ' : </strong></th>';
5546 if ($donetodo) {
5547 $out .= '<th class="liste_titre"></th>';
5548 }
5549 // Type of event
5550 $out .= '<th class="liste_titre">';
5551 $out .= '<span class="fas fa-square inline-block fawidth30 hideonsmartphone" style="color: #ddd;" title="' . $langs->trans("ActionType") . '"></span>';
5552 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? -1 : 1, 0, 0, 1, 'selecttype minwidth100', $langs->trans("Type"));
5553 $out .= '</th>';
5554 // Label
5555 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
5556 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="' . $filters['search_agenda_label'] . '" placeholder="' . $langs->trans("Label") . '">';
5557 $out .= '</th>';
5558
5559 // Action column
5560 if (!$conf->main_checkbox_left_column) {
5561 $out .= '<th class="liste_titre width50 middle">';
5562 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
5563 $out .= $searchpicto;
5564 $out .= '</th>';
5565 }
5566
5567 $out .= '</tr>';
5568
5569 $out .= '</table>';
5570
5571 $out .= '</form>';
5572 $out .= '</div>';
5573
5574 $out .= "\n";
5575
5576 $out .= '<ul class="timeline">';
5577
5578 if ($donetodo) {
5579 $tmp = '';
5580 if ($filterobj instanceof Societe) {
5581 $tmp .= '<a href="' . DOL_URL_ROOT . '/comm/action/list.php?mode=show_list&socid=' . $filterobj->id . '&status=done">';
5582 }
5583 if ($filterobj instanceof User) {
5584 $tmp .= '<a href="' . DOL_URL_ROOT . '/comm/action/list.php?mode=show_list&socid=' . $filterobj->id . '&status=done">';
5585 }
5586 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
5587 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
5588 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
5589 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
5590 if ($filterobj instanceof Societe) {
5591 $tmp .= '</a>';
5592 }
5593 if ($filterobj instanceof User) {
5594 $tmp .= '</a>';
5595 }
5596 $out .= getTitleFieldOfList($tmp);
5597 }
5598
5599 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/cactioncomm.class.php';
5600 $caction = new CActionComm($db);
5601 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
5602
5603 $actualCycleDate = false;
5604
5605 // Loop on each event to show it
5606 foreach ($histo as $key => $value) {
5607 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
5608
5609 $actionstatic->type_picto = $histo[$key]['apicto'];
5610 $actionstatic->type_code = $histo[$key]['acode'];
5611
5612 $labeltype = $actionstatic->type_code;
5613 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
5614 $labeltype = 'AC_OTH';
5615 }
5616 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
5617 $labeltype = $langs->trans("Message");
5618 } else {
5619 if (!empty($arraylist[$labeltype])) {
5620 $labeltype = $arraylist[$labeltype];
5621 }
5622 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
5623 $labeltype .= ' - ' . $arraylist[$actionstatic->code]; // Use code in priority on type_code
5624 }
5625 }
5626
5627 $url = DOL_URL_ROOT . '/comm/action/card.php?id=' . $histo[$key]['id'];
5628
5629 $tmpa = dol_getdate($histo[$key]['datestart'], false);
5630
5631 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'] . '-' . $tmpa['yday']) {
5632 $actualCycleDate = $tmpa['year'] . '-' . $tmpa['yday'];
5633 $out .= '<!-- timeline time label -->';
5634 $out .= '<li class="time-label">';
5635 $out .= '<span class="timeline-badge-date">';
5636 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
5637 $out .= '</span>';
5638 $out .= '</li>';
5639 $out .= '<!-- /.timeline-label -->';
5640 }
5641
5642
5643 $out .= '<!-- timeline item -->' . "\n";
5644 $out .= '<li class="timeline-code-' . (!empty($actionstatic->code) ? strtolower($actionstatic->code) : "none") . '">';
5645
5646 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
5647 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
5648 //$out .= $timelineicon;
5649 //var_dump($timelineicon);
5650 $out .= $typeicon;
5651
5652 $out .= '<div class="timeline-item">' . "\n";
5653
5654 $out .= '<span class="time timeline-header-action2">';
5655
5656 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
5657 $out .= '<a class="paddingleft paddingright timeline-btn2 editfielda" href="' . DOL_URL_ROOT . '/comm/mailing/card.php?id=' . $histo[$key]['id'] . '">' . img_object($langs->trans("ShowEMailing"), "email") . ' ';
5658 $out .= $histo[$key]['id'];
5659 $out .= '</a> ';
5660 } else {
5661 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle') . ' ';
5662 }
5663
5664 if (
5665 $user->hasRight('agenda', 'allactions', 'create') ||
5666 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))
5667 ) {
5668 $out .= '<a class="paddingleft paddingright timeline-btn2 editfielda" href="' . DOL_MAIN_URL_ROOT . '/comm/action/card.php?action=edit&token=' . newToken() . '&id=' . $actionstatic->id . '&backtopage=' . urlencode($_SERVER["PHP_SELF"] . '?' . $param) . '">';
5669 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
5670 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
5671 $out .= '</a>';
5672 }
5673
5674 $out .= '</span>';
5675
5676 // Date
5677 $out .= '<span class="time"><i class="fa fa-clock valignmiddle"></i> ';
5678 $out .= '<span class="valignmiddle marginrightonly">';
5679 $out .= dol_print_date($histo[$key]['datestart'], 'day', 'tzuserrel');
5680 //$out .= '</span>';
5681 //$out .= '<span class="valignmiddle">'.
5682 $out .= ' '.dol_print_date($histo[$key]['datestart'], 'hour', 'tzuserrel', null, false, 'opacitymedium');
5683 //$out .= '</span>';
5684 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
5685 $tmpa = dol_getdate($histo[$key]['datestart'], true);
5686 $tmpb = dol_getdate($histo[$key]['dateend'], true);
5687 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
5688 $out .= ' - ' . dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel', null, false, 1);
5689 } else {
5690 $out .= ' - ' . dol_print_date($histo[$key]['dateend'], 'day', 'tzuserrel');
5691 //$out .= '<span class="valignmiddle marginrightonly">';
5692 $out .= ' '.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel', null, false, 'opacitymedium');
5693 //$out .= '</span>';
5694 }
5695 }
5696 $late = 0;
5697 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
5698 $late = 1;
5699 }
5700 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
5701 $late = 1;
5702 }
5703 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
5704 $late = 1;
5705 }
5706 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
5707 $late = 1;
5708 }
5709 if ($late) {
5710 $out .= img_warning($langs->trans("Late")) . ' ';
5711 }
5712 $out .= "</span></span>\n";
5713
5714 $out .= '<span class="time">';
5715 $out .= $actionstatic->getLibStatut(2);
5716 $out .= '</span>';
5717
5718 // Ref
5719 $out .= '<h3 class="timeline-header">';
5720
5721 // Author of event
5722 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
5723 if ($histo[$key]['userid'] > 0) {
5724 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
5725 $userstatic->fetch($histo[$key]['userid']);
5726 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
5727 }
5728 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
5729 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
5730 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
5731 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
5732 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
5733 } else {
5734 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
5735 }
5736 }
5737 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
5738 } else {
5739 $out .= '<img class="photomemberphoto userphoto" alt="" src="/public/theme/common/user_anonymous.png">'.$langs->trans("Anonymous");
5740 }
5741 $out .= '</div>';
5742
5743 // Title
5744 $out .= ' <div class="messaging-title inline-block">';
5745 //$out .= $actionstatic->getTypePicto(); // The type of event is already into the timeline on left.
5746 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
5747 $out .= $labeltype . ' - ';
5748 }
5749
5750 $tmplabel = '';
5751
5752 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
5753 $out .= $langs->trans('TicketNewMessage').' - <em>'.img_picto($langs->trans('Private'), 'lock', 'class="valignmiddle"').' '.$langs->trans('Private').'</em>';
5754 $summary = preg_replace('/\[[^\]]*\]\s*/', '', $actionstatic->label);
5755 //if ($summary != $object->title) {
5756 $out .= ' - '.dolPrintHTML($summary);
5757 //}
5758 } elseif (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
5759 $out .= $langs->trans('TicketNewMessage');
5760 } elseif (isset($histo[$key]['type'])) {
5761 if ($histo[$key]['type'] == 'action') {
5762 $transcode = $langs->transnoentitiesnoconv("Action" . $histo[$key]['acode']);
5763 //$tmplabel = ($transcode != "Action" . $histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
5764 $tmplabel = $histo[$key]['note'];
5765 $actionstatic->id = $histo[$key]['id'];
5766 if ($tmplabel != $labeltype) {
5767 $out .= dol_escape_htmltag(dol_trunc($tmplabel, 120));
5768 }
5769 } elseif ($histo[$key]['type'] == 'mailing') {
5770 $out .= '<a href="' . DOL_URL_ROOT . '/comm/mailing/card.php?id=' . $histo[$key]['id'] . '">' . img_object($langs->trans("ShowEMailing"), "email") . ' ';
5771 $transcode = $langs->transnoentitiesnoconv("Action" . $histo[$key]['acode']);
5772 $tmplabel = ($transcode != "Action" . $histo[$key]['acode'] ? $transcode : 'Send mass mailing');
5773 $out .= dol_escape_htmltag(dol_trunc($tmplabel, 120));
5774 } else {
5775 $tmplabel .= $histo[$key]['note'];
5776 $out .= dol_escape_htmltag(dol_trunc($tmplabel, 120));
5777 }
5778 }
5779 $out = preg_replace('/ - $/', '', $out); // Remove ending ' - '
5780
5781 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
5782 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
5783 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
5784 } else {
5785 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
5786 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
5787 }
5788 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
5789 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
5790 }
5791
5792 // We do not show if link if on object we are filtering on (no need to show the link to ticket X when we are on page of events for the ticket X)
5793 $showlink = 1;
5794 if (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
5795 if ($histo[$key]['elementtype'] == 'ticket') {
5796 $showlink = 0;
5797 }
5798 }
5799
5800 if ($link && $showlink) {
5801 $out .= ' - ' . $link;
5802 }
5803 }
5804
5805 $out .= '</div>';
5806
5807 $out .= '</h3>';
5808
5809 // Message
5810 if ($actionstatic->code == 'AC_TICKET_CREATE') {
5811 $newmess = $filterobj->message;
5812 } else {
5813 $newmess = $histo[$key]['message'];
5814 }
5815 if (
5816 !empty($newmess && $newmess != $tmplabel)
5817 && $actionstatic->code != 'AC_TICKET_MODIFY'
5818 ) {
5819 $out .= '<div class="timeline-body wordbreak small">';
5820 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
5821 $truncatedText = dolGetFirstLineOfText($newmess, $truncateLines);
5822 // dolGetFirstLineOfText() cuts on <br> without caring about tag balance, so a message wrapped in
5823 // a block tag leaves the excerpt with an unclosed tag. The browser then nests the read more link
5824 // and the full text inside the excerpt, and hiding the excerpt hides the whole message (#39035).
5825 $truncatedText = dolCloseUnclosedHtmlTags($truncatedText);
5826 if ($truncateLines > 0 && strlen($newmess) > strlen($truncatedText)) {
5827 $out .= '<div class="readmore-block --closed" >';
5828 $out .= ' <div class="readmore-block__excerpt">';
5829 $out .= dolPrintHTML($truncatedText, 0, array('pre', 'code'));
5830 $out .= ' <br><a class="read-more-link" data-read-more-action="open" href="' . DOL_MAIN_URL_ROOT . '/comm/action/card.php?id=' . $actionstatic->id . '&backtopage=' . urlencode($_SERVER["PHP_SELF"] . '?' . $param) . '" >' . $langs->trans("ReadMore") . ' <span class="fa fa-chevron-right" aria-hidden="true"></span></a>';
5831 $out .= ' </div>';
5832 $out .= ' <div class="readmore-block__full-text" >';
5833
5834 $out .= dolPrintHTML($newmess, 0, array('pre', 'code'));
5835
5836 $out .= ' <a class="read-less-link" data-read-more-action="close" href="#" ><span class="fa fa-chevron-up" aria-hidden="true"></span> ' . $langs->trans("ReadLess") . '</a>';
5837 $out .= ' </div>';
5838 $out .= '</div>';
5839 } else {
5840 $out .= dolPrintHTML($newmess, 0, array('pre', 'code'));
5841 }
5842 $out .= '</div>';
5843 }
5844
5845 // Timeline footer
5846 $footer = '';
5847
5848 // Contact for this action
5849 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
5850 $contactList = '';
5851 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
5852 if (empty($conf->cache['contact'][$cid])) {
5853 $contact = new Contact($db);
5854 $result = $contact->fetch($cid);
5855 $conf->cache['contact'][$cid] = $contact;
5856 } else {
5857 $contact = $conf->cache['contact'][$cid];
5858 $result = ($contact instanceof Contact) ? $contact->id : 0;
5859 }
5860
5861 if ($result > 0) {
5862 $contactList .= !empty($contactList) ? ', ' : '';
5863 $contactList .= $contact->getNomUrl(1);
5864 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
5865 if (!empty($contact->phone_pro)) {
5866 $contactList .= '(' . dol_print_phone($contact->phone_pro) . ')';
5867 }
5868 }
5869 }
5870 }
5871
5872 $footer .= $langs->trans('ActionOnContact') . ' : ' . $contactList;
5873 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
5874 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
5875 $contact = new Contact($db);
5876 $result = $contact->fetch($histo[$key]['contact_id']);
5877 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
5878 } else {
5879 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
5880 $result = ($contact instanceof Contact) ? $contact->id : 0;
5881 }
5882
5883 if ($result > 0) {
5884 $footer .= $contact->getNomUrl(1);
5885 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
5886 if (!empty($contact->phone_pro)) {
5887 $footer .= '(' . dol_print_phone($contact->phone_pro) . ')';
5888 }
5889 }
5890 }
5891 }
5892
5893 $documents = getActionCommEcmList($actionstatic);
5894 if (!empty($documents)) {
5895 $footer .= '<div class="timeline-documents-container">';
5896 foreach ($documents as $doc) {
5897 $footer .= '<span id="document_' . $doc->id . '" class="timeline-documents" ';
5898 $footer .= ' data-id="' . $doc->id . '" ';
5899 $footer .= ' data-path="' . $doc->filepath . '"';
5900 $footer .= ' data-filename="' . dol_escape_htmltag($doc->filename) . '" ';
5901 $footer .= '>';
5902
5903 $filePath = DOL_DATA_ROOT . '/' . $doc->filepath . '/' . $doc->filename;
5904 $mime = dol_mimetype($filePath);
5905 if (empty($doc->agenda_id)) {
5906 $dir_ref = $actionstatic->id;
5907 $modulepart = 'actions';
5908 } else {
5909 $split_dir = explode('/', $doc->filepath);
5910 $modulepart = array_shift($split_dir);
5911 $dir_ref = implode('/', $split_dir);
5912 }
5913
5914 $file = $dir_ref . '/' . $doc->filename;
5915 $thumb = $dir_ref . '/thumbs/' . substr($doc->filename, 0, strrpos($doc->filename, '.')) . '_mini' . substr($doc->filename, strrpos($doc->filename, '.'));
5916 $doclink = dol_buildpath('document.php', 1) . '?modulepart=' . $modulepart . '&attachment=0&file=' . urlencode($file) . '&entity=' . $conf->entity;
5917 $viewlink = dol_buildpath('viewimage.php', 1) . '?modulepart=' . $modulepart . '&file=' . urlencode($thumb) . '&entity=' . $conf->entity;
5918
5919
5920
5921 $mimeAttr = ' mime="' . $mime . '" ';
5922 $class = '';
5923 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
5924 $class .= ' documentpreview';
5925 }
5926
5927 $footer .= '<a href="' . $doclink . '" class="btn-link ' . $class . '" target="_blank" rel="noopener noreferrer" ' . $mimeAttr . ' >';
5928 $footer .= img_mime($filePath) . ' ' . $doc->filename;
5929 $footer .= '</a>';
5930
5931 $footer .= '</span>';
5932 }
5933 $footer .= '</div>';
5934 }
5935
5936 if (!empty($footer)) {
5937 $out .= '<div class="timeline-footer">' . $footer . '</div>';
5938 }
5939
5940 $out .= '</div>' . "\n"; // end timeline-item
5941
5942 $out .= '</li>';
5943 $out .= '<!-- END timeline item -->';
5944 }
5945
5946 $out .= "</ul>\n";
5947
5948 // Code to manage the click on button data-read-more-action to show full description of an event
5949 $out .= '<script>
5950 jQuery(document).ready(function () {
5951 $(document).on("click", "[data-read-more-action]", function(e){
5952 console.log("We click on data-read-more-action");
5953 let readMoreBloc = $(this).closest(".readmore-block");
5954 if(readMoreBloc.length > 0){
5955 e.preventDefault();
5956 if($(this).attr("data-read-more-action") == "close"){
5957 readMoreBloc.addClass("--closed").removeClass("--open");
5958 $("html, body").animate({
5959 scrollTop: readMoreBloc.offset().top - 200
5960 }, 100);
5961 }else{
5962 readMoreBloc.addClass("--open").removeClass("--closed");
5963 }
5964 }
5965 });
5966 });
5967 </script>';
5968
5969
5970 if (empty($histo)) {
5971 $out .= '<span class="opacitymedium">' . $langs->trans("NoRecordFound") . '</span>';
5972 }
5973
5974 if ($num > $MAXWITHOUTPAGINATION) {
5975 $langs->load("errors");
5976 $out .= '<center><span class="opacitymedium">...' . $langs->trans("WarningTooManyDataPleaseUseMoreFilters", $MAXWITHOUTPAGINATION) . '...</span></center>';
5977 }
5978 }
5979
5980 if ($noprint) {
5981 return $out;
5982 } else {
5983 print $out;
5984 return null;
5985 }
5986}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition ajax.lib.php:476
ajax_object_onoff($object, $code, $field, $text_on, $text_off, $input=array(), $morecss='', $htmlname='', $forcenojs=0, $moreparam='', $readonly=0)
On/off button to change a property status of an object This uses the ajax service objectonoff....
Definition ajax.lib.php:804
Class to manage agenda events (actions)
Class to manage different types of events.
Class to manage contact/addresses.
Class to manage building of HTML components.
Class to manage generation of HTML components Only common components must be here.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage translations.
Class to manage Dolibarr users.
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_convert_file($fileinput, $ext='png', $fileoutput='', $page='')
Convert a PDF file into another image format.
dragAndDropFileUpload($htmlname)
Function to manage the drag and drop of a file.
dolGetElementUrl($objectid, $objecttype, $withpicto=0, $option='')
Return link url to an object.
dol_now($mode='gmt')
Return date for now.
dol_print_phone($phone, $countrycode='', $contactid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0, $morecss='paddingright')
Format phone numbers according to country.
dol_mimetype($file, $default='application/octet-stream', $mode=0)
Return MIME type of a file from its name with extension.
dolGetFirstLineOfText($text, $nboflines=1, $charset='UTF-8')
Return first line of text.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
getCallerInfoString()
Get caller info as a string that can be appended to a log message.
dolBuildUrl($url, $params=[], $addtoken=false, $anchor='')
Return path of url.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
natural_search($fields, $value, $mode=0, $nofirstand=0, $sqltoadd='')
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into JavaScript code.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dol_htmlentities($string, $flags=ENT_QUOTES|ENT_SUBSTITUTE, $encoding='UTF-8', $double_encode=false)
Replace htmlentities functions.
dol_textishtml($msg, $option=0)
Return if a text is a html content.
dol_escape_uri($stringtoescape)
Returns text escaped by RFC 3986 for inclusion into a clickable link.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
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).
dolCloseUnclosedHtmlTags($text)
Close the HTML tags left open in a truncated HTML string.
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.
getNonce()
Return a random string to be used as a nonce value for js.
dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
This function is called to encode a string into a HTML string but differs from htmlentities because a...
dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox=0, $check='restricthtml')
Sanitize a HTML to remove js, dangerous content and external links.
getActionCommEcmList($object)
getActionCommEcmList
dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles=1, $removeclassattribute=1, $cleanalsojavascript=0, $allowiframe=0, $allowed_tags=array(), $allowlink=0, $allowscript=0, $allowstyle=0, $allowphp=0)
Clean a string to keep only desirable HTML tags.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
utf8_check($str)
Check if a string is in UTF8.
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_getdate($timestamp, $fast=false, $forcetimezone='')
Return an array with locale date info.
multi select button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
treeview li table
No Email.
dol_fiche_end($notab=0)
Show tab footer of a card.
Definition html.lib.php:706
img_weather($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $morecss='')
Show weather picto.
finishSimpleTable($addLineBreak=false)
Add the correct HTML close tags for "startSimpleTable(...)" (use after the last table line)
show_actions_messaging($conf, $langs, $db, $filterobj, $objcon=null, $noprint=0, $actioncode='', $donetodo='done', $filters=array(), $sortfield='a.datep, a.id', $sortorder='DESC')
Show html area with actions in messaging format.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
img_credit_card($brand, $morecss='fa-2x inline-block valignmiddle')
Return image of a credit card according to its brand name.
print_liste_field_titre($name, $file="", $field="", $begin="", $param="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
img_right($titlealt='default', $selected=0, $moreatt='')
Show right arrow logo.
commonHtmlAttributeBuilder($attr, array $unescapedAttr=[])
Builds an array of safe and properly escaped HTML attributes from a key-value pair list.
print_barre_liste($title, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='generic', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $selectlimitsuffix=0, $hidenavigation=0, $pagenavastextinput=0, $morehtmlrightbeforearrow='')
Print a title with navigation controls for pagination.
img_help($usehelpcursor=1, $usealttitle=1)
Show help logo with cursor "?".
showValueWithClipboardCPButton($valuetocopy, $showonlyonhover=1, $texttoshow='')
Create a button to copy $valuetocopy in the clipboard (for copy and paste feature).
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)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
img_left($titlealt='default', $selected=0, $moreatt='')
Show left arrow logo.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
getListOfHtmlBooleanAttributes()
Returns a list of HTML boolean attributes.
dolPrintHTML($s, $allowiframe=0, $moreallowedtags=array())
Return a string (that can be on several lines) ready to be output on a HTML page.
Definition html.lib.php:73
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0, $morecssdiv='')
Show tabs of a record.
Definition html.lib.php:519
dolPrintHTMLForTextArea($s, $allowiframe=0)
Return a string ready to be output on input textarea.
Definition html.lib.php:139
dolGetButtonTitle($label, $helpText='', $iconClass='fa fa-file', $url='', $id='', $status=1, $params=array())
Function dolGetButtonTitle : this kind of buttons are used in title in list.
dolOutputDates($datep, $datef=null, $fullday=0, $addseconds=0, $pictotoadd='', $tzoutput='tzuserrel', $reduceformat=0)
Print decorated date-hour.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
Definition html.lib.php:717
img_action($titlealt, $numaction, $picto='', $moreatt='')
Show logo action.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
setEventMessage($mesgs, $style='mesgs', $noduplicate=0, $attop=0)
Set event message in dol_events session object.
dolGetBadge($label, $html='', $type='primary', $mode='', $url='', $params=array())
Function dolGetBadge.
img_down($titlealt='default', $selected=0, $moreclass='')
Show down arrow logo.
dolPrintText($s)
Return a string label (possible on several lines and that should not contains any HTML) ready to be o...
Definition html.lib.php:57
getPictoForType($key, $morecss='')
Return the picto for a data type.
img_allow($allow, $titlealt='default')
Show tick logo if allowed.
dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
Create a dialog with two buttons for export and overwrite of a website.
Definition html.lib.php:321
dol_fiche_head($links=array(), $active='0', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='')
Show tab header of a card.
Definition html.lib.php:497
img_mime($file, $titlealt='', $morecss='')
Show MIME img of a file.
ajax_autoselect($htmlname, $addlink='', $textonlink='Link')
Make content of an input box selected when we click into input field.
img_view($titlealt='default', $float=0, $other='class="valignmiddle"')
Show logo view card.
dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot=true)
An function to complete dropdown url in dolGetButtonAction.
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no', $use_short_label=0)
Output a dimension with best unit.
img_picto_common($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $notitle=0)
Show picto (generic function)
img_search($titlealt='default', $other='')
Show search logo.
dolGetButtonAction($label, $text='', $actionType='default', $url='', $id='', $userRight=1, $params=array())
Function dolGetButtonAction.
showTotalAmount($amount)
Style total amount of an object.
img_previous($titlealt='default', $moreatt='')
Show previous logo.
dolPrintHTMLForAttribute($s, $escapeonlyhtmltags=0, $allowothertags=array())
Return a string ready to be output into an HTML attribute (alt, title, data-html, ....
Definition html.lib.php:99
fieldLabel($langkey, $fieldkey, $fieldrequired=0)
Show a string with the label tag dedicated to the HTML edit field.
dolPrintLabel($s, $escapeonlyhtmltags=0)
Return a string label (so on 1 line only and that should not contains any HTML) ready to be output on...
Definition html.lib.php:44
yn($yesno, $format=1, $color=0)
Return yes or no in current language.
img_printer($titlealt="default", $other='')
Show printer logo.
dol_htmloutput_events($disabledoutputofmessages=0)
Print formatted messages to output (Used to show messages on html output).
getTitleFieldOfList($name, $thead=0, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $disablesortlink=0, $tooltip='', $forcenowrapcolumntitle=0)
Get title line of an array.
getImgPictoConv($mode='fa')
Get array to convert the Dolibarr picto keys into Font awesome keys.
dolGetButtonTitleSeparator($moreClass="")
Add space between dolGetButtonTitle.
img_split($titlealt='default', $other='class="pictosplit"')
Show split logo.
dolPrintPassword($s)
Return a string ready to be output on an HTML attribute (alt, title, ...)
Definition html.lib.php:150
dol_print_error_email($prefixcode, $errormessage='', $errormessages=array(), $morecss='error', $email='')
Show a public email and error code to contact if technical error.
print_titre($title)
Show a title.
img_error($titlealt='default')
Show error logo.
dol_htmloutput_mesg($mesgstring='', $mesgarray=array(), $style='ok', $keepembedded=0)
Print formatted messages to output (Used to show messages on html output).
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_next($titlealt='default', $moreatt='')
Show next logo.
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
get_htmloutput_mesg($mesgstring='', $mesgarray=[], $style='ok', $keepembedded=0)
Get formatted messages to output (Used to show messages on html output).
print_fleche_navigation($page, $file, $options='', $nextpage=0, $betweenarrows='', $afterarrows='', $limit=-1, $totalnboflines=0, $selectlimitsuffix='', $beforearrows='', $hidenavigation=0)
Function to show navigation arrows into lists.
addSummaryTableLine($tableColumnCount, $num, $nbofloop=0, $total=0, $noneWord="None", $extraRightColumn=false)
Add a summary line to the current open table ("None", "XMoreLines" or "Total xxx")
img_edit_add($titlealt='default', $other='')
Show logo "+".
print_fiche_titre($title, $mesg='', $picto='generic', $pictoisfullpath=0, $id='')
Show a title with picto.
img_searchclear($titlealt='default', $other='')
Show search logo.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
img_up($titlealt='default', $selected=0, $moreclass='')
Show top arrow logo.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='', $textonpictotooltip='', $cssfordropdown='info_admin')
Show information in HTML for admin users or standard users.
startSimpleTable($header, $link="", $arguments="", $emptyColumns=0, $number=-1, $pictofulllist='')
Start a table with headers and a optional clickable number (don't forget to use "finishSimpleTable()"...
getFieldErrorIcon($fieldValidationErrorMsg)
get field error icon
dolPrintHTMLForAttributeUrl($s)
Return a string ready to be output on a href attribute (this one need a special because we need conte...
Definition html.lib.php:122
img_edit_remove($titlealt='default', $other='')
Show logo "-".
img_info($titlealt='default')
Show info logo.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
Definition html.lib.php:172
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
measuringUnitString($unitid, $measuring_style='', $unitscale=null, $use_short_label=0, $outputlangs=null)
Return translation label of a unit key.
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:130
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:133