dolibarr 24.0.0-beta
documentation.class.php
1<?php
2/* Copyright (C) 2024 Anthony Damhet <a.damhet@progiseize.fr>
3 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) 2026 MDW <mdeweerd@users.noreply.github.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
30{
36 public $view = array();
37
43 public $menu = array();
44
50 public $summary = array();
51
55 public $db;
56
60 public $baseUrl = 'admin/tools/ui';
61
68 public function __construct(DoliDB $db)
69 {
70 $this->db = $db;
71
72 // https://www.figma.com/community/file/1393171578760389765/dolibarr-ui-ux-kit
73
74 // Menu Constructor
75 $this->setMenu();
76 }
77
83 private function setMenu()
84 {
85 global $hookmanager;
86
87 $hookmanager->initHooks(array('uidocumentation'));
88
89 // Go back to Dolibarr
90 $this->menu['BackToDolibarr'] = array(
91 'url' => dol_buildpath('modulebuilder/index.php', 1),
92 'icon' => 'fas fa-arrow-left',
93 'submenu' => array(),
94 );
95
96 // Home for Ui documentation
97 $this->menu['DocumentationHome'] = array(
98 'url' => dol_buildpath($this->baseUrl.'/index.php', 1),
99 'icon' => 'fas fa-book',
100 'submenu' => array(),
101 );
102
103 // Components
104 $this->menu['Components'] = array(
105 'url' => dol_buildpath($this->baseUrl.'/components/index.php', 1),
106 'icon' => 'fas fa-th-large',
107 'submenu' => array(
108 'Badges' => array(
109 'url' => dol_buildpath($this->baseUrl.'/components/badges.php', 1),
110 'icon' => 'fas fa-certificate',
111 'submenu' => array(),
112 'summary' => array(
113 'DocBasicUsage' => '#badgesection-basicusage',
114 'DocBadgeContextualVariations' => '#badgesection-contextvariations',
115 'DocBadgeDefaultStatus' => '#badgesection-defaultstatus',
116 'DocBadgePillBadges' => '#badgesection-pill',
117 'DocBadgeDotBadges' => '#badgesection-dot',
118 'DocBadgeLinks' => '#badgesection-links',
119 'DocBadgeHelper' => '#badgesection-dolgetbadge'
120 ),
121 ),
122 'Buttons' => array(
123 'url' => dol_buildpath($this->baseUrl.'/components/buttons.php', 1),
124 'icon' => 'fas fa-mouse',
125 'submenu' => array(),
126 'summary' => array(
127 'DocBasicUsage' => '#buttonsection-basicusage',
128 'DocButtonModal' => '#buttonsection-modals',
129 'DocButtonSubmenu' => '#buttonsection-submenu',
130 ),
131 ),
132 'Icons' => array(
133 'url' => dol_buildpath($this->baseUrl.'/components/icons.php', 1),
134 'icon' => 'far fa-flag',
135 'submenu' => array(),
136 'summary' => array(
137 'DocIconsList' => '#img-picto-section-list',
138 'DocIconsFontAwesomeList' => '#icon-section-list',
139 ),
140 ),
141 'Progress' => array(
142 'url' => dol_buildpath($this->baseUrl.'/components/progress-bars.php', 1),
143 'icon' => 'fas fa-battery-half',
144 'submenu' => array(),
145 'summary' => array(
146 'DocBasicUsage' => '#progresse-section-basic-usage',
147 'DocColorVariants' => '#progress-section-color',
148 'DocStripedVariants' => '#progresse-section-stripped',
149 ),
150 ),
151 'Event Message' => array(
152 'url' => dol_buildpath($this->baseUrl.'/components/event-message.php', 1),
153 'icon' => 'fas fa-comments',
154 'submenu' => array(),
155 'summary' => array(
156 'DocBasicUsage' => '#seteventmessagesection-basicusage',
157 'DocSetEventMessageContextualVariations' => '#seteventmessagesection-contextvariations',
158 'DocSetEventMessageJsContext' => '#titlesection-tool-seteventmessage',
159 )
160 ),
161 'Inputs' => array(
162 'url' => dol_buildpath($this->baseUrl.'/components/inputs.php', 1),
163 'icon' => 'far fa-edit',
164 'submenu' => array(),
165 'summary' => array(
166 'DocBasicUsage' => '#setinputssection-basicusage',
167 'DocHelperFunctionsInputUsage' => '#setinputssection-helperfunctions',
168 'DocHelperFunctionsGetSearchFilterToolInput' => '#setinputssection-getSearchFilterToolInput',
169 )
170 ),
171 'ExperimentalUxInputAjaxFeedback' => array(
172 'url' => dol_buildpath($this->baseUrl.'/content/input-feedback.php', 1),
173 'icon' => 'far fa-share-square',
174 'submenu' => array(),
175 'summary' => array(),
176 ),
177 ),
178 );
179
180 // Elements
181 $this->menu['Content'] = array(
182 'url' => dol_buildpath($this->baseUrl.'/content/index.php', 1),
183 'icon' => 'far fa-file-alt',
184 'submenu' => array(
185 'Titles' => array(
186 'url' => dol_buildpath('admin/tools/ui/content/titles.php', 1),
187 'icon' => 'fas fa-heading',
188 'submenu' => array(),
189 'summary' => array(
190 'DocBasicUsage' => '#titlesection-basicusage',
191 'DocTitleWithFilters' => '#titlesection-withfilters',
192 ),
193 ),
194 'Tables' => array(
195 'url' => dol_buildpath('admin/tools/ui/content/tables.php', 1),
196 'icon' => 'fas fa-table',
197 'submenu' => array(),
198 'summary' => array(
199 'DocBasicUsage' => '#tablesection-basicusage',
200 'DocTableWithFilters' => '#tablesection-withfilters',
201 'DocTableBeforeFilters' => '#tablesection-beforefilters',
202 'DocTableCSSClass' => '#tablesection-cssclasses',
203 ),
204 ),
205
206 'TableRowIntuitiveSelect' => array(
207 'url' => dol_buildpath($this->baseUrl.'/content/intuitive-table-row-select.php', 1),
208 'icon' => 'far fa-check-square',
209 'submenu' => array(),
210 'summary' => array(),
211 ),
212
213 'FreezeTooltip' => array(
214 'url' => dol_buildpath($this->baseUrl.'/content/freeze-tooltip.php', 1),
215 'icon' => 'far fa-comment',
216 'submenu' => array(),
217 'summary' => array(),
218 ),
219 )
220 );
221
222 // Elements
223 $this->menu['Resources'] = array(
224 'url' => dol_buildpath($this->baseUrl.'/resources/index.php', 1),
225 'icon' => 'fas fa-wrench',
226 'submenu' => array(
227 'Contributing' => array(
228 'url' => dol_buildpath($this->baseUrl.'/resources/contributing.php', 1),
229 'icon' => 'fas fa-code',
230 'submenu' => array(),
231 'summary' => array(
232 'DocContributeStep1' => '#contributesection-step1',
233 'DocContributeStep2' => '#contributesection-step2',
234 'DocContributeStep3' => '#contributesection-step3',
235 ),
236 ),
237 )
238 );
239
240 // Elements
241 $this->menu['UxDolibarrContext'] = array(
242 'url' => dol_buildpath($this->baseUrl.'/dolibarr-context/index.php', 1),
243 'icon' => 'fab fa-fort-awesome',
244 'submenu' => array(
245 'UxDolibarrContextHowItWork' => array(
246 'url' => dol_buildpath($this->baseUrl.'/dolibarr-context/index.php', 1),
247 'icon' => 'fab fa-fort-awesome',
248 'submenu' => array(),
249 'summary' => array(
250 'Introduction' => '#titlesection-basicusage',
251 'ConsoleHelp' => '#titlesection-console-help',
252 'JSDolibarrhooks' => '#titlesection-hooks',
253 'JSDolibarrhooksReadyVsInit' => '#titlesection-event-init-vs-ready',
254 'JSDolibarrAwaitHooks' => '#titlesection-await-hooks',
255 'JSDolibarrhooksAjaxSpecial' => '#titlesection-dom-initnewcontent',
256 'ExampleOfCreatingNewContextTool' => '#titlesection-create-tool-example',
257 'SetEventMessageTool' => '#titlesection-tool-seteventmessage',
258 'SetAndUseContextVars' => '#titlesection-contextvars',
259 ),
260 ),
261 'UxDolibarrContextLangsTool' => array(
262 'url' => dol_buildpath($this->baseUrl.'/dolibarr-context/langs-tool.php', 1),
263 'icon' => 'far fa-flag',
264 'submenu' => array(),
265 'summary' => array(),
266 ),
267 'UxDolibarrContextKnowsHooks' => array(
268 'url' => dol_buildpath($this->baseUrl.'/dolibarr-context/knows-hooks.php', 1),
269 'icon' => 'fa fa-anchor',
270 'submenu' => array(),
271 'summary' => array(),
272 ),
273 )
274 );
275
276 // Elements
277 $this->menu['ExperimentalUx'] = array(
278 'url' => dol_buildpath($this->baseUrl.'/experimental/index.php', 1),
279 'icon' => 'fas fa-flask',
280 'submenu' => array(
281 'ExperimentalUxIntroductionMenu' => array(
282 'url' => dol_buildpath($this->baseUrl.'/experimental/index.php', 1),
283 'icon' => 'fas fa-flask',
284 'submenu' => array(),
285 'summary' => array(
286 'Index' => '#top',
287 'ExperimentalUxIntroductionTitle' => '#experimental-ux-introduction',
288 'ExperimentalUxContributionTitle' => '#experimental-ux-contribution',
289 ),
290 ),
291 'UxMenuTooltipTheme' => array(
292 'url' => dol_buildpath($this->baseUrl.'/experimental/tooltip-themes/index.php', 1),
293 'icon' => 'fas fa-comment',
294 'submenu' => array(),
295 'summary' => array(
296 'Introduction' => '#ux-introduction',
297 'TooltipThemesAndOrientation' => '#tooltip-themes',),
298 ),
299 )
300 );
301
302 $parameters = array(
303 'baseUrl' => $this->baseUrl,
304 );
305 $action = '';
306
307 $reshook = $hookmanager->executeHooks('setMenu', $parameters, $this, $action);
308 if ($reshook < 0) {
309 return false;
310 }
311
312 return true;
313 }
314
324 public function docHeader($title = '', $arrayofjs = [], $arrayofcss = [], $hidenavmenu = '')
325 {
326 global $langs;
327 $title = (!empty($title)) ? dol_escape_htmltag($title) : $langs->trans('Documentation');
328
329 $arrayofcss[] = 'admin/tools/ui/css/documentation.css';
330
331 top_htmlhead('', $title, 0, 0, $arrayofjs, $arrayofcss);
332
333 print '<body class="dolibarr-doc'.($hidenavmenu ? "-bis" : "").'">';
334 }
335
340 public function docFooter()
341 {
342 global $langs;
343
344 // DIV FOR SCROLL ANIMATION
345 print '<div id="documentation-scrollwrapper">';
346 print '<div id="documentation-scroll"></div>';
347 print '</div>';
348
349 // JS
350 print '<script src="'.dol_buildpath('admin/tools/ui/js/documentation.js', 1).'"></script>';
351 print '<script src="'.DOL_URL_ROOT.'/core/js/lib_foot.js.php?lang='.$langs->defaultlang.'"></script>';
352
353 print '</body>';
354 print '</html>';
355
357 }
358
364 public function showSidebar()
365 {
366 print '<div class="doc-sidebar">';
367
368 // LOGO
369 print '<div class="sidebar-logo">';
370 if (is_readable(DOL_DOCUMENT_ROOT.'/theme/dolibarr_logo.svg')) {
371 $urllogo = DOL_URL_ROOT.'/theme/dolibarr_logo.svg';
372 print '<img src="'.$urllogo.'" />';
373 }
374 print '</div>';
375
376 // NAVIGATION
377 print '<nav>';
378 if (!empty($this->menu)) {
379 $this->displayMenu($this->menu);
380 }
381 print '</nav>';
382
383 print '</div>';
384 }
385
393 private function displayMenu($menu, $level = 0)
394 {
395 global $langs;
396 $level++;
397
398 print '<ul>';
399 foreach ($menu as $key => $item) {
400 $levelclass = (!empty($item['submenu'])) ? 'li-withsubmenu' : '';
401 $levelclass .= (in_array($key, $this->view)) ? ' active' : '';
402 $levelclass .= ($key == 'BackToDolibarr') ? ' li-withseparator' : '';
403
404 print '<li class="'.trim($levelclass).' level-'.$level.'">';
405 print '<a href="'.$item['url'].'" class="'.((!empty($item['submenu'])) ? 'link-withsubmenu' : '').'">';
406 print ((!empty($item['icon'])) ? '<i class="menu-icon '.$item['icon'].' pictofixedwidth" aria-hidden="true"></i>' : '');
407 print '<span class="label">'.$langs->transnoentities($key).'</span>';
408 print ((!empty($item['submenu'])) ? '<i class="submenu-toggle fas fa-chevron-right" aria-hidden="true"></i>' : '');
409 print '</a>';
410 if (!empty($item['submenu'])) {
411 $this->displayMenu($item['submenu'], $level); // Recursive call to show submenu
412 }
413 echo '</li>';
414 }
415 print '</ul>';
416 }
417
423 public function showBreadcrumb()
424 {
425 global $langs;
426
427 print '<nav class="doc-breadcrumbs">';
428 print '<ul>';
429 print '<li class="breadcrumb-item"><a href="'.$this->menu['DocumentationHome']['url'].'"><i class="'.$this->menu['DocumentationHome']['icon'].'" aria-hidden="true"></i></a></li>';
430 if (!empty($this->view)) {
431 $nb_entries = count($this->view);
432 $i = 0;
433
434 $menu_entry = $this->menu;
435 foreach ($this->view as $page) {
436 $i++;
437 if ($i < $nb_entries && isset($menu_entry[$page])) {
438 print '<li class="breadcrumb-item"><a href="'.$menu_entry[$page]['url'].'">'.$langs->transnoentities($page).'</a></li>';
439 $menu_entry = $menu_entry[$page]['submenu'];
440 } else {
441 print '<li class="breadcrumb-item">'.$langs->transnoentities($page).'</li>';
442 }
443 }
444 } else {
445 print '<li class="breadcrumb-item">'.$langs->trans('Documentation').'</li>';
446 }
447 print '</ul>';
448 print '</nav>';
449 }
450
458 public function showSummary($showsubmenu = 1, $showsubmenu_summary = 1)
459 {
460 $i = 0;
461 $menu_entry = [];
462 if (!empty($this->view)) {
463 // Set the correct menu depth (level)
464 foreach ($this->view as $view) {
465 $i++;
466 if ($i == 1) {
467 $menu_entry = $this->menu[$view] ?? [];
468 } else {
469 $menu_entry = $menu_entry['submenu'][$view] ?? [];
470 }
471 }
472 }
473
474 if (!empty($menu_entry['summary']) || (!empty($menu_entry['submenu']) && $showsubmenu)) {
475 print '<div class="summary-wrapper">';
476 $this->displaySummary($menu_entry);
477 print '</div>';
478 }
479 }
480
481
491 public function displaySummary($menu, $level = 0, $showsubmenu = 1, $showsubmenu_summary = 1)
492 {
493 global $langs;
494
495 $level++;
496 print '<ul class="documentation-summary level-'.$level.'"">';
497
498 if (!empty($menu['summary'])) {
499 foreach ($menu['summary'] as $summary_label => $summary_link) {
500 /*
501 if ($summary_link[0] == '#') {
502 $tmp_summary_link = $menu['url'];
503 if (GETPOSTINT('hidenavmenu')) {
504 $tmp_summary_link .= (strpos($tmp_summary_link, '?') === false ? '?' : '&').'hidenavmenu=1';
505 }
506 if (GETPOSTINT('displayMode')) {
507 $tmp_summary_link .= (strpos($tmp_summary_link, '?') === false ? '?' : '&').'displayMode=1';
508 }
509 $summary_link = $tmp_summary_link;
510 }
511 */
512
513 print '<li><a href="'.$summary_link.'">'.$langs->trans($summary_label).'</a></li>';
514 }
515 }
516
517 if ($showsubmenu && !empty($menu['submenu'])) {
518 foreach ($menu['submenu'] as $key => $item) {
519 print '<li class="summary-title ">';
520
521 if (!empty($item['url'])) {
522 print '<h3 class="level-'.$level.'"><a href="'.dolBuildUrl($item['url']).'" >'.$langs->trans($key).'</a></h3>';
523 } else {
524 print '<h3 class="level-'.$level.'">'.$langs->trans($key).'</h3>';
525 }
526
527 if ($showsubmenu_summary) {
528 $this->displaySummary($item, $level);
529 }
530 print '</li>';
531 }
532 }
533 print '</ul>';
534 }
535
543 public function showCode($lines = array(), $option = 'html')
544 {
545 require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
546 print '<div class="documentation-code">';
547
548 if (isset($lines[0])) {
549 if ($option === 'html' && strpos(strtolower($lines[0]), '<!doctype') === false) {
550 array_unshift($lines, '<!DOCTYPE html>', '');
551 }
552 }
553
554 $content = implode("\n", $lines) . "\n";
555 $doleditor = new DolEditor(md5($content), $content, '', 0, 'Basic', 'In', true, false, 'ace', 0, '99%', 1);
556 print $doleditor->Create(1, '', false, '', $option);
557 print '</div>';
558 }
559
560
569 static public function generateLoremIpsum($paragraphCount = 3, $wordsPerParagraph = 50, $html = true)
570 {
571 $baseText = "Lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur Excepteur sint occaecat cupidatat non proident sunt in culpa qui officia deserunt mollit anim id est laborum";
572
573 $words = explode(" ", $baseText);
574 $paragraphs = [];
575
576 for ($p = 0; $p < $paragraphCount; $p++) {
577 $sentence = [];
578 for ($i = 0; $i < $wordsPerParagraph; $i++) {
579 $word = $words[array_rand($words)];
580
581 // Randomly add a comma
582 if ($i > 2 && rand(0, 10) > 8) {
583 $word .= ",";
584 }
585
586 $sentence[] = $word;
587 }
588
589 $paragraphText = ucfirst(implode(" ", $sentence)) . ".";
590 if ($html) {
591 $paragraphText = "<p>$paragraphText</p>";
592 }
593 $paragraphs[] = $paragraphText;
594 }
595
596 return implode($html ? "\n" : "\n\n", $paragraphs);
597 }
598}
Class to manage UI documentation.
showBreadcrumb()
Output breadcrumb.
displayMenu($menu, $level=0)
Recursive function to set Menu.
showSummary($showsubmenu=1, $showsubmenu_summary=1)
Output summary.
docFooter()
Output close body + html.
showCode($lines=array(), $option='html')
Output a View Code area.
docHeader($title='', $arrayofjs=[], $arrayofcss=[], $hidenavmenu='')
Output header + body.
showSidebar()
Output sidebar.
__construct(DoliDB $db)
Constructor.
setMenu()
Set Documentation Menu.
static generateLoremIpsum($paragraphCount=3, $wordsPerParagraph=50, $html=true)
Generate lorem ipsum.
displaySummary($menu, $level=0, $showsubmenu=1, $showsubmenu_summary=1)
Recursive function for Automatic Summary.
Class to manage a WYSIWYG editor.
Class to manage Dolibarr database access.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
dolBuildUrl($url, $params=[], $addtoken=false, $anchor='')
Return path of url.
dol_htmloutput_events($disabledoutputofmessages=0)
Print formatted messages to output (Used to show messages on html output).
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
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...
top_htmlhead($head, $title='', $disablejs=0, $disablehead=0, $arrayofjs=array(), $arrayofcss=array(), $disableforlogin=0, $disablenofollow=0, $disablenoindex=0)
Output html header of a page.