menu.php 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338
  1. <?php
  2. /*
  3. FusionPBX
  4. Version: MPL 1.1
  5. The contents of this file are subject to the Mozilla Public License Version
  6. 1.1 (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.mozilla.org/MPL/
  9. Software distributed under the License is distributed on an "AS IS" basis,
  10. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. for the specific language governing rights and limitations under the
  12. License.
  13. The Original Code is FusionPBX
  14. The Initial Developer of the Original Code is
  15. Mark J Crane <[email protected]>
  16. Copyright (C) 2010 - 2023
  17. All Rights Reserved.
  18. Contributor(s):
  19. Mark J Crane <[email protected]>
  20. */
  21. /**
  22. * menu class
  23. *
  24. * @method null delete
  25. * @method null toggle
  26. * @method null copy
  27. */
  28. if (!class_exists('menu')) {
  29. class menu {
  30. /**
  31. * declare the variables
  32. */
  33. private $app_name;
  34. private $app_uuid;
  35. private $name;
  36. private $table;
  37. private $toggle_field;
  38. private $toggle_values;
  39. private $description_field;
  40. private $location;
  41. public $menu_uuid;
  42. public $menu_language;
  43. public $text;
  44. /**
  45. * Set in the constructor. Must be a database object and cannot be null.
  46. * @var database Database Object
  47. */
  48. private $database;
  49. /**
  50. * called when the object is created
  51. */
  52. public function __construct($setting_array = []) {
  53. //assign the variables
  54. $this->app_name = 'menus';
  55. $this->app_uuid = 'f4b3b3d2-6287-489c-2a00-64529e46f2d7';
  56. $this->location = 'menus.php';
  57. //open a database connection
  58. if (empty($setting_array['database'])) {
  59. $this->database = database::new();
  60. } else {
  61. $this->database = $setting_array['database'];
  62. }
  63. }
  64. /**
  65. * delete rows from the database
  66. */
  67. public function delete($records) {
  68. //assign the variables
  69. $this->name = 'menu';
  70. $this->table = 'menus';
  71. if (permission_exists($this->name.'_delete')) {
  72. //add multi-lingual support
  73. $language = new text;
  74. $text = $language->get();
  75. //validate the token
  76. $token = new token;
  77. if (!$token->validate($_SERVER['PHP_SELF'])) {
  78. message::add($text['message-invalid_token'],'negative');
  79. header('Location: '.$this->location);
  80. exit;
  81. }
  82. //delete multiple records
  83. if (is_array($records) && @sizeof($records) != 0) {
  84. //build the delete array
  85. $x = 0;
  86. foreach ($records as $record) {
  87. if (!empty($record['checked']) && $record['checked'] == 'true' && is_uuid($record['uuid'])) {
  88. //remove menu languages
  89. $array['menu_languages'][$x][$this->name.'_uuid'] = $record['uuid'];
  90. //remove menu item groups
  91. $array['menu_item_groups'][$x][$this->name.'_uuid'] = $record['uuid'];
  92. //remove menu items
  93. $array['menu_items'][$x][$this->name.'_uuid'] = $record['uuid'];
  94. //build array to remove the menu
  95. $array[$this->table][$x][$this->name.'_uuid'] = $record['uuid'];
  96. //increment
  97. $x++;
  98. }
  99. }
  100. //delete the checked rows
  101. if (is_array($array) && @sizeof($array) != 0) {
  102. //grant temporary permissions
  103. $p = permissions::new();
  104. $p->add('menu_item_delete', 'temp');
  105. $p->add('menu_item_group_delete', 'temp');
  106. $p->add('menu_language_delete', 'temp');
  107. //execute delete
  108. $this->database->app_name = $this->app_name;
  109. $this->database->app_uuid = $this->app_uuid;
  110. $this->database->delete($array);
  111. unset($array);
  112. //revoke temporary permissions
  113. $p->delete('menu_item_delete', 'temp');
  114. $p->delete('menu_item_group_delete', 'temp');
  115. $p->delete('menu_language_delete', 'temp');
  116. //set message
  117. message::add($text['message-delete']);
  118. }
  119. unset($records);
  120. }
  121. }
  122. }
  123. public function delete_items($records) {
  124. //assign the variables
  125. $this->name = 'menu_item';
  126. $this->table = 'menu_items';
  127. if (permission_exists($this->name.'_delete')) {
  128. //add multi-lingual support
  129. $language = new text;
  130. $text = $language->get();
  131. //validate the token
  132. $token = new token;
  133. if (!$token->validate('/core/menu/menu_item_list.php')) {
  134. message::add($text['message-invalid_token'],'negative');
  135. header('Location: '.$this->location);
  136. exit;
  137. }
  138. //delete multiple records
  139. if (is_array($records) && @sizeof($records) != 0) {
  140. //build the delete array
  141. $x = 0;
  142. foreach ($records as $record) {
  143. if (!empty($record['checked']) && $record['checked'] == 'true' && is_uuid($record['uuid'])) {
  144. //build array
  145. $uuids[] = "'".$record['uuid']."'";
  146. //remove menu languages
  147. $array['menu_languages'][$x][$this->name.'_uuid'] = $record['uuid'];
  148. //remove menu item groups
  149. $array['menu_item_groups'][$x][$this->name.'_uuid'] = $record['uuid'];
  150. //remove menu items
  151. $array[$this->table][$x][$this->name.'_uuid'] = $record['uuid'];
  152. //increment
  153. $x++;
  154. }
  155. }
  156. //include child menu items
  157. if (!empty($uuids) && @sizeof($uuids) != 0) {
  158. $sql = "select menu_item_uuid as uuid from v_".$this->table." ";
  159. $sql .= "where menu_item_parent_uuid in (".implode(', ', $uuids).") ";
  160. $rows = $this->database->select($sql, null, 'all');
  161. if (!empty($rows) && @sizeof($rows) != 0) {
  162. foreach ($rows as $row) {
  163. //remove menu languages
  164. $array['menu_languages'][$x][$this->name.'_uuid'] = $row['uuid'];
  165. //remove menu item groups
  166. $array['menu_item_groups'][$x][$this->name.'_uuid'] = $row['uuid'];
  167. //remove menu items
  168. $array[$this->table][$x][$this->name.'_uuid'] = $row['uuid'];
  169. //increment
  170. $x++;
  171. }
  172. }
  173. }
  174. //delete the checked rows
  175. if (!empty($array) && is_array($array) && @sizeof($array) != 0) {
  176. //grant temporary permissions
  177. $p = permissions::new();
  178. $p->add('menu_language_delete', 'temp');
  179. $p->add('menu_item_group_delete', 'temp');
  180. //execute delete
  181. $this->database->app_name = $this->app_name;
  182. $this->database->app_uuid = $this->app_uuid;
  183. $this->database->delete($array);
  184. unset($array);
  185. //revoke temporary permissions
  186. $p->delete('menu_language_delete', 'temp');
  187. $p->delete('menu_item_group_delete', 'temp');
  188. //set message
  189. message::add($text['message-delete']);
  190. }
  191. unset($records);
  192. }
  193. }
  194. }
  195. /**
  196. * toggle a field between two values
  197. */
  198. public function toggle_items($records) {
  199. //assign the variables
  200. $this->name = 'menu_item';
  201. $this->table = 'menu_items';
  202. $this->toggle_field = 'menu_item_protected';
  203. $this->toggle_values = ['true','false'];
  204. if (permission_exists($this->name.'_edit')) {
  205. //add multi-lingual support
  206. $language = new text;
  207. $text = $language->get();
  208. //validate the token
  209. $token = new token;
  210. if (!$token->validate('/core/menu/menu_item_list.php')) {
  211. message::add($text['message-invalid_token'],'negative');
  212. header('Location: '.$this->location);
  213. exit;
  214. }
  215. //toggle the checked records
  216. if (is_array($records) && @sizeof($records) != 0) {
  217. //get current toggle state
  218. foreach ($records as $record) {
  219. if (!empty($record['checked']) && $record['checked'] == 'true' && is_uuid($record['uuid'])) {
  220. $uuids[] = "'".$record['uuid']."'";
  221. }
  222. }
  223. if (!empty($uuids) && is_array($uuids) && @sizeof($uuids) != 0) {
  224. $sql = "select ".$this->name."_uuid as uuid, ".$this->toggle_field." as toggle from v_".$this->table." ";
  225. $sql .= "where ".$this->name."_uuid in (".implode(', ', $uuids).") ";
  226. $parameters = null;
  227. $rows = $this->database->select($sql, $parameters, 'all');
  228. if (is_array($rows) && @sizeof($rows) != 0) {
  229. foreach ($rows as $row) {
  230. $states[$row['uuid']] = $row['toggle'] == '' ? $this->toggle_values[1] : $row['toggle'];
  231. }
  232. }
  233. unset($sql, $parameters, $rows, $row);
  234. }
  235. //build update array
  236. $x = 0;
  237. if (!empty($states) && is_array($states) && @sizeof($states) != 0) {
  238. foreach ($states as $uuid => $state) {
  239. //create the array
  240. $array[$this->table][$x][$this->name.'_uuid'] = $uuid;
  241. $array[$this->table][$x][$this->toggle_field] = $state == $this->toggle_values[0] ? $this->toggle_values[1] : $this->toggle_values[0];
  242. //increment
  243. $x++;
  244. }
  245. }
  246. //save the changes
  247. if (!empty($array) && is_array($array) && @sizeof($array) != 0) {
  248. //save the array
  249. $this->database->app_name = $this->app_name;
  250. $this->database->app_uuid = $this->app_uuid;
  251. $this->database->save($array);
  252. unset($array);
  253. //set message
  254. message::add($text['message-toggle']);
  255. }
  256. unset($records, $states);
  257. }
  258. }
  259. }
  260. /**
  261. * delete items in the menu that are not protected
  262. */
  263. public function delete_unprotected() {
  264. //remove existing menu languages
  265. $sql = "delete from v_menu_languages ";
  266. $sql .= "where menu_uuid = :menu_uuid ";
  267. $sql .= "and menu_item_uuid in ( ";
  268. $sql .= " select menu_item_uuid ";
  269. $sql .= " from v_menu_items ";
  270. $sql .= " where menu_uuid = :menu_uuid ";
  271. $sql .= " and ( ";
  272. $sql .= " menu_item_protected <> 'true' ";
  273. $sql .= " or menu_item_protected is null ";
  274. $sql .= " ) ";
  275. $sql .= ") ";
  276. $parameters['menu_uuid'] = $this->menu_uuid;
  277. $this->database->execute($sql, $parameters);
  278. unset($sql, $parameters);
  279. //remove existing unprotected menu item groups
  280. $sql = "delete from v_menu_item_groups ";
  281. $sql .= "where menu_uuid = :menu_uuid ";
  282. $sql .= "and menu_item_uuid in ( ";
  283. $sql .= " select menu_item_uuid ";
  284. $sql .= " from v_menu_items ";
  285. $sql .= " where menu_uuid = :menu_uuid ";
  286. $sql .= " and ( ";
  287. $sql .= " menu_item_protected <> 'true' ";
  288. $sql .= " or menu_item_protected is null ";
  289. $sql .= " ) ";
  290. $sql .= ") ";
  291. $parameters['menu_uuid'] = $this->menu_uuid;
  292. $this->database->execute($sql, $parameters);
  293. unset($sql, $parameters);
  294. //remove existing unprotected menu items
  295. $sql = "delete from v_menu_items ";
  296. $sql .= "where menu_uuid = :menu_uuid ";
  297. $sql .= "and ( ";
  298. $sql .= " menu_item_protected <> 'true' ";
  299. $sql .= " or menu_item_protected is null ";
  300. $sql .= ") ";
  301. $parameters['menu_uuid'] = $this->menu_uuid;
  302. $this->database->execute($sql, $parameters);
  303. unset($sql, $parameters);
  304. }
  305. /**
  306. * restore the menu
  307. */
  308. public function restore() {
  309. //get the $apps array from the installed apps from the core and mod directories
  310. $config_list = glob($_SERVER["DOCUMENT_ROOT"].PROJECT_PATH."/*/*/app_menu.php");
  311. $x = 0;
  312. if (is_array($config_list)) {
  313. foreach ($config_list as $config_path) {
  314. $app_path = dirname($config_path);
  315. $app_path = preg_replace('/\A.*(\/.*\/.*)\z/', '$1', $app_path);
  316. $y = 0;
  317. try {
  318. //echo "[".$x ."] ".$config_path."\n";
  319. include($config_path);
  320. $x++;
  321. }
  322. catch (Exception $e) {
  323. echo 'exception caught: ' . $e->getMessage() . "\n";
  324. exit;
  325. }
  326. }
  327. }
  328. //get the list of languages
  329. $language = new text;
  330. //create a uuid array of the original uuid used as the key and new uuid as the value
  331. if (is_array($apps)) {
  332. $x = 0;
  333. foreach ($apps as $row) {
  334. if (is_array($row['menu'])) {
  335. foreach ($row['menu'] as $menu) {
  336. $uuid_array[$menu['uuid']] = uuid();
  337. }
  338. }
  339. }
  340. }
  341. //if the item uuid is not currently in the db then add it
  342. $sql = "select * from v_menu_items ";
  343. $sql .= "where menu_uuid = :menu_uuid ";
  344. $parameters['menu_uuid'] = $this->menu_uuid;
  345. $menu_items = $this->database->select($sql, $parameters, 'all');
  346. //use the app array to restore the default menu
  347. if (is_array($apps)) {
  348. $x = 0;
  349. foreach ($apps as $row) {
  350. if (is_array($row['menu'])) {
  351. foreach ($row['menu'] as $menu) {
  352. //set the variables
  353. if (!empty($menu['title'][$this->menu_language])) {
  354. $menu_item_title = $menu['title'][$this->menu_language];
  355. }
  356. else {
  357. $menu_item_title = $menu['title']['en-us'];
  358. }
  359. $uuid = $menu['uuid'];
  360. $menu_item_uuid = $uuid_array[$menu['uuid']];
  361. $menu_item_parent_uuid = $uuid_array[$menu['parent_uuid']] ?? null;
  362. $menu_item_category = $menu['category'];
  363. $menu_item_icon = $menu['icon'] ?? null;
  364. $menu_item_path = $menu['path'];
  365. $menu_item_order = $menu['order'] ?? null;
  366. $menu_item_description = $menu['desc'] ?? null;
  367. //sanitize the menu link
  368. $menu_item_path = preg_replace('#[^a-zA-Z0-9_:\-\.\&\=\?\/]#', '', $menu_item_path);
  369. //check if the menu item exists and if it does set the row array
  370. $menu_item_exists = false;
  371. foreach ($menu_items as $item) {
  372. if ($item['uuid'] == $menu['uuid']) {
  373. $menu_item_exists = true;
  374. $row = $item;
  375. }
  376. }
  377. //item exists in the database
  378. if ($menu_item_exists) {
  379. //get parent_menu_item_protected
  380. foreach ($menu_items as $item) {
  381. if ($item['uuid'] == $menu['parent_uuid']) {
  382. $parent_menu_item_protected = $item['menu_item_protected'];
  383. }
  384. }
  385. //parent is not protected so the parent uuid needs to be updated
  386. if (is_uuid($menu_item_parent_uuid) && $menu_item_parent_uuid != $row['menu_item_parent_uuid'] && $parent_menu_item_protected != 'true') {
  387. $array['menu_items'][$x]['menu_item_uuid'] = $row['menu_item_uuid'];
  388. $array['menu_items'][$x]['menu_item_parent_uuid'] = $menu_item_parent_uuid;
  389. $x++;
  390. }
  391. }
  392. //item does not exist in the database
  393. if (!$menu_item_exists) {
  394. if ($menu_item_uuid != $menu_item_parent_uuid) {
  395. $array['menu_items'][$x]['menu_item_uuid'] = $menu_item_uuid;
  396. $array['menu_items'][$x]['menu_uuid'] = $this->menu_uuid;
  397. $array['menu_items'][$x]['uuid'] = $uuid;
  398. $array['menu_items'][$x]['menu_item_title'] = $menu_item_title;
  399. $array['menu_items'][$x]['menu_item_link'] = $menu_item_path;
  400. $array['menu_items'][$x]['menu_item_category'] = $menu_item_category;
  401. $array['menu_items'][$x]['menu_item_icon'] = $menu_item_icon;
  402. if (!empty($menu_item_order)) {
  403. $array['menu_items'][$x]['menu_item_order'] = $menu_item_order;
  404. }
  405. if (is_uuid($menu_item_parent_uuid)) {
  406. $array['menu_items'][$x]['menu_item_parent_uuid'] = $menu_item_parent_uuid;
  407. }
  408. $array['menu_items'][$x]['menu_item_description'] = $menu_item_description;
  409. $x++;
  410. }
  411. }
  412. unset($field, $parameters, $num_rows);
  413. //set the menu languages
  414. if (!$menu_item_exists && is_array($language->languages)) {
  415. foreach ($language->languages as $menu_language) {
  416. //set the menu item title
  417. if (!empty($menu["title"][$menu_language])) {
  418. $menu_item_title = $menu["title"][$menu_language];
  419. }
  420. else {
  421. $menu_item_title = $menu["title"]['en-us'];
  422. }
  423. //build insert array
  424. $array['menu_languages'][$x]['menu_language_uuid'] = uuid();
  425. $array['menu_languages'][$x]['menu_item_uuid'] = $menu_item_uuid;
  426. $array['menu_languages'][$x]['menu_uuid'] = $this->menu_uuid;
  427. $array['menu_languages'][$x]['menu_language'] = $menu_language;
  428. $array['menu_languages'][$x]['menu_item_title'] = $menu_item_title;
  429. $x++;
  430. }
  431. }
  432. }
  433. }
  434. }
  435. if (is_array($array) && @sizeof($array) != 0) {
  436. //grant temporary permissions
  437. $p = permissions::new();
  438. $p->add('menu_item_add', 'temp');
  439. $p->add('menu_language_add', 'temp');
  440. //execute insert
  441. $this->database->app_name = 'menu';
  442. $this->database->app_uuid = 'f4b3b3d2-6287-489c-2a00-64529e46f2d7';
  443. $this->database->save($array);
  444. unset($array);
  445. //revoke temporary permissions
  446. $p->delete('menu_item_add', 'temp');
  447. $p->delete('menu_language_add', 'temp');
  448. }
  449. }
  450. //make sure the default user groups exist
  451. $group = new groups;
  452. $group->defaults();
  453. //get default global group_uuids
  454. $sql = "select group_uuid, group_name from v_groups ";
  455. $sql .= "where domain_uuid is null ";
  456. $result = $this->database->select($sql, null, 'all');
  457. if (is_array($result) && @sizeof($result) != 0) {
  458. foreach ($result as $row) {
  459. $group_uuids[$row['group_name']] = $row['group_uuid'];
  460. }
  461. }
  462. unset($sql, $result, $row);
  463. //if there are no groups listed in v_menu_item_groups under menu_item_uuid then add the default groups
  464. if (is_array($apps)) {
  465. $x = 0;
  466. foreach($apps as $app) {
  467. if (is_array($apps)) {
  468. foreach ($app['menu'] as $sub_row) {
  469. if (isset($sub_row['groups'])) {
  470. foreach ($sub_row['groups'] as $group) {
  471. $sql = "select count(*) from v_menu_item_groups ";
  472. $sql .= "where menu_item_uuid = :menu_item_uuid ";
  473. $sql .= "and menu_uuid = :menu_uuid ";
  474. $sql .= "and group_name = :group_name ";
  475. $sql .= "and group_uuid = :group_uuid ";
  476. $parameters['menu_item_uuid'] = $uuid_array[$sub_row['uuid']];
  477. $parameters['menu_uuid'] = $this->menu_uuid;
  478. $parameters['group_name'] = $group;
  479. $parameters['group_uuid'] = $group_uuids[$group] ?? null;
  480. $num_rows = $this->database->select($sql, $parameters, 'column');
  481. if ($num_rows == 0) {
  482. //no menu item groups found, build insert array for defaults
  483. $array['menu_item_groups'][$x]['menu_item_group_uuid'] = uuid();
  484. $array['menu_item_groups'][$x]['menu_uuid'] = $this->menu_uuid;
  485. $array['menu_item_groups'][$x]['menu_item_uuid'] = $uuid_array[$sub_row['uuid']];
  486. $array['menu_item_groups'][$x]['group_name'] = $group;
  487. $array['menu_item_groups'][$x]['group_uuid'] = $group_uuids[$group] ?? null;
  488. $x++;
  489. }
  490. unset($sql, $parameters, $num_rows);
  491. }
  492. }
  493. }
  494. }
  495. }
  496. if (is_array($array) && @sizeof($array) != 0) {
  497. //grant temporary permissions
  498. $p = permissions::new();
  499. $p->add('menu_item_group_add', 'temp');
  500. //execute insert
  501. $this->database->app_name = 'menu';
  502. $this->database->app_uuid = 'f4b3b3d2-6287-489c-2a00-64529e46f2d7';
  503. $this->database->save($array);
  504. unset($array);
  505. //revoke temporary permissions
  506. $p->delete('menu_item_group_add', 'temp');
  507. }
  508. }
  509. }
  510. /**
  511. * create the menu
  512. */
  513. public function build_html($menu_item_level = 0) {
  514. $menu_html_full = '';
  515. $menu_array = $this->menu_array();
  516. if (!isset($_SESSION['groups'])) {
  517. $_SESSION['groups'][0]['group_name'] = 'public';
  518. }
  519. if (is_array($menu_array)) {
  520. foreach($menu_array as $menu_field) {
  521. //set the variables
  522. $menu_item_link = $menu_field['menu_item_link'];
  523. $menu_item_category = $menu_field['menu_item_category'];
  524. $menu_items = $menu_field['menu_items'];
  525. //prepare the protected menus
  526. $menu_item_title = ($menu_field['menu_item_protected'] == "true") ? $menu_field['menu_item_title'] : $menu_field['menu_language_title'];
  527. //prepare the menu_tags according to the category
  528. $menu_tags = '';
  529. switch ($menu_item_category) {
  530. case "internal":
  531. $menu_tags = "href='".PROJECT_PATH.$submenu_item_link."'";
  532. break;
  533. case "external":
  534. if (substr($submenu_item_link, 0,1) == "/") {
  535. $submenu_item_link = PROJECT_PATH.$submenu_item_link;
  536. }
  537. $menu_tags = "href='".$submenu_item_link."' target='_blank'";
  538. break;
  539. case "email":
  540. $menu_tags = "href='mailto:".$submenu_item_link."'";
  541. break;
  542. }
  543. if ($menu_item_level == 0) {
  544. $menu_html = "<ul class='menu_main'>\n";
  545. $menu_html .= "<li>\n";
  546. if (!isset($_SESSION["username"])) {
  547. $_SESSION["username"] = '';
  548. }
  549. if (empty($_SESSION["username"])) {
  550. $menu_html .= "<a $menu_tags style='padding: 0px 0px; border-style: none; background: none;'><h2 align='center' style=''>".$menu_item_title."</h2></a>\n";
  551. }
  552. else {
  553. if ($submenu_item_link == "/login.php" || $submenu_item_link == "/users/signup.php") {
  554. //hide login and sign-up when the user is logged in
  555. }
  556. else {
  557. if (empty($submenu_item_link)) {
  558. $menu_html .= "<h2 align='center' style=''>".$menu_item_title."</h2>\n";
  559. }
  560. else {
  561. $menu_html .= "<a ".$menu_tags." style='padding: 0px 0px; border-style: none; background: none;'><h2 align='center' style=''>".$menu_item_title."</h2></a>\n";
  562. }
  563. }
  564. }
  565. }
  566. if (is_array($menu_field['menu_items']) && count($menu_field['menu_items']) > 0) {
  567. $menu_html .= $this->build_child_html($menu_item_level, $menu_field['menu_items']);
  568. }
  569. if ($menu_item_level == 0) {
  570. $menu_html .= "</li>\n";
  571. $menu_html .= "</ul>\n\n";
  572. }
  573. $menu_html_full .= $menu_html;
  574. } //end for each
  575. }
  576. return $menu_html_full;
  577. }
  578. /**
  579. * create the sub menus
  580. */
  581. private function build_child_html($menu_item_level, $submenu_array) {
  582. $menu_item_level = $menu_item_level+1;
  583. if (count($_SESSION['groups']) == 0) {
  584. $_SESSION['groups'][0]['group_name'] = 'public';
  585. }
  586. if (is_array($submenu_array)) {
  587. //child menu found
  588. $submenu_html = "<ul class='menu_sub'>\n";
  589. foreach($submenu_array as $submenu_field) {
  590. //set the variables
  591. $menu_item_link = $submenu_field['menu_item_link'];
  592. $menu_item_category = $submenu_field['menu_item_category'];
  593. $menu_items = $submenu_field['menu_items'];
  594. //prepare the protected menus
  595. $menu_item_title = ($submenu_field['menu_item_protected'] == "true") ? $submenu_field['menu_item_title'] : $submenu_field['menu_language_title'];
  596. //prepare the menu_tags according to the category
  597. switch ($menu_item_category) {
  598. case "internal":
  599. $menu_tags = "href='".PROJECT_PATH.$menu_item_link."'";
  600. break;
  601. case "external":
  602. if (substr($menu_item_link, 0,1) == "/") {
  603. $menu_item_link = PROJECT_PATH.$menu_item_link;
  604. }
  605. $menu_tags = "href='".$menu_item_link."' target='_blank'";
  606. break;
  607. case "email":
  608. $menu_tags = "href='mailto:".$menu_item_link."'";
  609. break;
  610. }
  611. $submenu_html .= "<li>";
  612. //get sub menu for children
  613. if (is_array($menu_items) && count($menu_items) > 0) {
  614. $str_child_menu = $this->build_child_html($menu_item_level, $menu_items);
  615. }
  616. if (strlen($str_child_menu) > 1) {
  617. $submenu_html .= "<a ".$menu_tags.">".$menu_item_title."</a>";
  618. $submenu_html .= $str_child_menu;
  619. unset($str_child_menu);
  620. }
  621. else {
  622. $submenu_html .= "<a ".$menu_tags.">".$menu_item_title."</a>";
  623. }
  624. $submenu_html .= "</li>\n";
  625. }
  626. unset($submenu_array);
  627. $submenu_html .="</ul>\n";
  628. return $submenu_html;
  629. }
  630. }
  631. /**
  632. * create the menu array
  633. */
  634. public function menu_array($menu_item_level = 0) {
  635. //if there are no groups then set the public group
  636. if (!isset($_SESSION['groups'][0]['group_name'])) {
  637. $_SESSION['groups'][0]['group_name'] = 'public';
  638. }
  639. //get the menu from the database
  640. $sql = "select i.menu_item_link, l.menu_item_title as menu_language_title, ";
  641. $sql .= "i.menu_item_title, i.menu_item_protected, i.menu_item_category, ";
  642. $sql .= "i.menu_item_icon, i.menu_item_uuid, i.menu_item_parent_uuid ";
  643. $sql .= "from v_menu_items as i, v_menu_languages as l ";
  644. $sql .= "where i.menu_item_uuid = l.menu_item_uuid ";
  645. $sql .= "and l.menu_language = :menu_language ";
  646. $sql .= "and l.menu_uuid = :menu_uuid ";
  647. $sql .= "and i.menu_uuid = :menu_uuid ";
  648. $sql .= "and i.menu_item_parent_uuid is null ";
  649. $sql .= "and i.menu_item_uuid in ";
  650. $sql .= "( ";
  651. $sql .= "select menu_item_uuid ";
  652. $sql .= "from v_menu_item_groups ";
  653. $sql .= "where menu_uuid = :menu_uuid ";
  654. $x = 0;
  655. foreach($_SESSION['groups'] as $row) {
  656. $sql_where_or[] = "group_name = :group_name_".$x;
  657. $parameters['group_name_'.$x] = $row['group_name'];
  658. $x++;
  659. }
  660. if (is_array($sql_where_or) && @sizeof($sql_where_or) != 0) {
  661. $sql .= "and ( ";
  662. $sql .= implode(' or ', $sql_where_or);
  663. $sql .= ") ";
  664. }
  665. $sql .= "and menu_item_uuid is not null ";
  666. $sql .= ") ";
  667. $sql .= "order by i.menu_item_order asc ";
  668. $parameters['menu_language'] = $_SESSION['domain']['language']['code'] ?? null;
  669. $parameters['menu_uuid'] = $this->menu_uuid;
  670. $result = $this->database->select($sql, $parameters, 'all');
  671. unset($sql, $parameters);
  672. //save the menu into an array
  673. $x = 0;
  674. $a = Array();
  675. if (is_array($result) && @sizeof($result) != 0) {
  676. foreach($result as $row) {
  677. //add the row to the array
  678. $a[$x] = $row;
  679. //add the sub menus to the array
  680. $menu_item_level = 0;
  681. if (!empty($row['menu_item_uuid'])) {
  682. $a[$x]['menu_items'] = $this->menu_child_array($menu_item_level, $row['menu_item_uuid']);
  683. }
  684. //increment the row number
  685. $x++;
  686. }
  687. }
  688. unset($result, $row);
  689. //return the array
  690. return $a;
  691. }
  692. /**
  693. * create the sub menus
  694. */
  695. private function menu_child_array($menu_item_level, $menu_item_uuid) {
  696. //set the level
  697. $menu_item_level = $menu_item_level + 1;
  698. //if there are no groups then set the public group
  699. if (!isset($_SESSION['groups'][0]['group_name'])) {
  700. $_SESSION['groups'][0]['group_name'] = 'public';
  701. }
  702. //get the child menu from the database
  703. $sql = "select i.menu_item_link, l.menu_item_title as menu_language_title, i.menu_item_title, i.menu_item_protected, i.menu_item_category, i.menu_item_icon, i.menu_item_uuid, i.menu_item_parent_uuid ";
  704. $sql .= "from v_menu_items as i, v_menu_languages as l ";
  705. $sql .= "where i.menu_item_uuid = l.menu_item_uuid ";
  706. $sql .= "and l.menu_language = :menu_language ";
  707. $sql .= "and l.menu_uuid = :menu_uuid ";
  708. $sql .= "and i.menu_uuid = :menu_uuid ";
  709. $sql .= "and i.menu_item_parent_uuid = :menu_item_parent_uuid ";
  710. $sql .= "and i.menu_item_uuid in ";
  711. $sql .= "( ";
  712. $sql .= "select menu_item_uuid ";
  713. $sql .= "from v_menu_item_groups ";
  714. $sql .= "where menu_uuid = :menu_uuid ";
  715. $x = 0;
  716. foreach($_SESSION['groups'] as $row) {
  717. $sql_where_or[] = "group_name = :group_name_".$x;
  718. $parameters['group_name_'.$x] = $row['group_name'];
  719. $x++;
  720. }
  721. if (is_array($sql_where_or) && @sizeof($sql_where_or) != 0) {
  722. $sql .= "and ( ";
  723. $sql .= implode(' or ', $sql_where_or);
  724. $sql .= ") ";
  725. }
  726. $sql .= ") ";
  727. $sql .= "order by l.menu_item_title, i.menu_item_order asc ";
  728. $parameters['menu_language'] = $_SESSION['domain']['language']['code'];
  729. $parameters['menu_uuid'] = $this->menu_uuid;
  730. $parameters['menu_item_parent_uuid'] = $menu_item_uuid;
  731. $sub_result = $this->database->select($sql, $parameters, 'all');
  732. unset($sql, $parameters);
  733. //save the child menu into an array
  734. $x = 0;
  735. $a = Array();
  736. if (is_array($sub_result) && @sizeof($sub_result) != 0) {
  737. foreach($sub_result as $row) {
  738. //set the variables
  739. $menu_item_link = $row['menu_item_link'];
  740. $menu_item_category = $row['menu_item_category'];
  741. $menu_item_icon = $row['menu_item_icon'];
  742. $menu_item_uuid = $row['menu_item_uuid'];
  743. $menu_item_parent_uuid = $row['menu_item_parent_uuid'];
  744. //add the row to the array
  745. $a[$x] = $row;
  746. //prepare the protected menus
  747. if ($row['menu_item_protected'] == "true") {
  748. $a[$x]['menu_item_title'] = $row['menu_item_title'];
  749. }
  750. else {
  751. $a[$x]['menu_item_title'] = $row['menu_language_title'];
  752. }
  753. //get sub menu for children
  754. if (!empty($menu_item_uuid)) {
  755. $a[$x]['menu_items'] = $this->menu_child_array($menu_item_level, $menu_item_uuid);
  756. }
  757. //increment the row
  758. $x++;
  759. }
  760. }
  761. unset($sub_result, $row);
  762. //return the array
  763. return $a;
  764. }
  765. /**
  766. * add the default menu when no menu exists
  767. */
  768. public function menu_default() {
  769. //set the default menu_uuid
  770. $this->menu_uuid = 'b4750c3f-2a86-b00d-b7d0-345c14eca286';
  771. //check to see if any menu exists
  772. $sql = "select count(*) as count from v_menus ";
  773. $sql .= "where menu_uuid = :menu_uuid ";
  774. $parameters['menu_uuid'] = $this->menu_uuid;
  775. $num_rows = $this->database->select($sql, $parameters, 'column');
  776. if ($num_rows == 0) {
  777. //built insert array
  778. $array['menus'][0]['menu_uuid'] = $this->menu_uuid;
  779. $array['menus'][0]['menu_name'] = 'default';
  780. $array['menus'][0]['menu_language'] = 'en-us';
  781. $array['menus'][0]['menu_description'] = 'Default Menu';
  782. //grant temporary permissions
  783. $p = permissions::new();
  784. $p->add('menu_add', 'temp');
  785. //execute insert
  786. $this->database->app_name = 'menu';
  787. $this->database->app_uuid = 'f4b3b3d2-6287-489c-2a00-64529e46f2d7';
  788. $this->database->save($array);
  789. unset($array);
  790. //revoke temporary permissions
  791. $p->delete('menu_add', 'temp');
  792. //add the menu items
  793. $this->restore();
  794. }
  795. unset($sql, $parameters, $result, $row);
  796. }
  797. /**
  798. * build the fixed, static or inline horizontal menu html
  799. */
  800. public function menu_horizontal($menu_array) {
  801. //add multi-lingual support
  802. $language = new text;
  803. $text = $language->get();
  804. //determine menu behavior
  805. $menu_style = !empty($_SESSION['theme']['menu_style']['text']) ? $_SESSION['theme']['menu_style']['text'] : 'fixed';
  806. switch ($menu_style) {
  807. case 'inline':
  808. $menu_type = 'default';
  809. $menu_width = 'calc(100% - 20px)';
  810. $menu_brand = false;
  811. $menu_corners = null;
  812. break;
  813. case 'static':
  814. $menu_type = 'static-top';
  815. $menu_width = 'calc(100% - 40px)';
  816. $menu_brand = true;
  817. $menu_corners = "style='-webkit-border-radius: 0 0 4px 4px; -moz-border-radius: 0 0 4px 4px; border-radius: 0 0 4px 4px;'";
  818. break;
  819. case 'fixed':
  820. default:
  821. $menu_type = 'fixed-'.(!empty($_SESSION['theme']['menu_position']['text']) ? $_SESSION['theme']['menu_position']['text'] : 'top');
  822. if (!http_user_agent('mobile')) {
  823. $menu_width = !empty($_SESSION['theme']['menu_width_fixed']['text']) ? $_SESSION['theme']['menu_width_fixed']['text'] : 'calc(90% - 20px)';
  824. }
  825. $menu_brand = true;
  826. $menu_corners = null;
  827. }
  828. //begin navbar code
  829. $html = "<nav class='navbar navbar-expand-sm ".$menu_type."' ".$menu_corners.">\n";
  830. $html .= " <div class='container-fluid' style='width: ".($menu_width ?? '100%')."; padding: 0;'>\n";
  831. $html .= " <div class='navbar-brand'>\n";
  832. if ($menu_brand) {
  833. //define menu brand mark
  834. $menu_brand_text = (!empty($_SESSION['theme']['menu_brand_text']['text'])) ? escape($_SESSION['theme']['menu_brand_text']['text']) : "FusionPBX";
  835. switch ($_SESSION['theme']['menu_brand_type']['text'] ?? null) {
  836. case 'text':
  837. $html .= " <a class='navbar-brand-text' href='".PROJECT_PATH."/'>".$menu_brand_text."</a>\n";
  838. break;
  839. case 'image_text':
  840. $menu_brand_image = (!empty($_SESSION['theme']['menu_brand_image']['text'])) ? escape($_SESSION['theme']['menu_brand_image']['text']) : PROJECT_PATH."/themes/default/images/logo.png";
  841. $html .= " <a href='".PROJECT_PATH."/'>";
  842. $html .= " <img id='menu_brand_image' class='navbar-logo' src='".$menu_brand_image."' title=\"".escape($menu_brand_text)."\">";
  843. if (!empty($_SESSION['theme']['menu_brand_image_hover']['text'])) {
  844. $html .= "<img id='menu_brand_image_hover' class='navbar-logo' style='display: none;' src='".$_SESSION['theme']['menu_brand_image_hover']['text']."' title=\"".escape($menu_brand_text)."\">";
  845. }
  846. $html .= "</a>\n";
  847. $html .= " <a class='navbar-brand-text' href='".PROJECT_PATH."/'>".$menu_brand_text."</a>\n";
  848. break;
  849. case 'none':
  850. break;
  851. case 'image':
  852. default:
  853. $menu_brand_image = !empty($_SESSION['theme']['menu_brand_image']['text']) ? escape($_SESSION['theme']['menu_brand_image']['text']) : PROJECT_PATH."/themes/default/images/logo.png";
  854. $html .= " <a href='".PROJECT_PATH."/'>";
  855. $html .= " <img id='menu_brand_image' class='navbar-logo' src='".$menu_brand_image."' title=\"".escape($menu_brand_text)."\">";
  856. if (isset($_SESSION['theme']['menu_brand_image_hover']['text']) && !empty($_SESSION['theme']['menu_brand_image_hover']['text'])) {
  857. $html .= "<img id='menu_brand_image_hover' class='navbar-logo' style='display: none;' src='".$_SESSION['theme']['menu_brand_image_hover']['text']."' title=\"".escape($menu_brand_text)."\">";
  858. }
  859. $html .= "</a>\n";
  860. $html .= " <a style='margin: 0;'></a>\n";
  861. }
  862. }
  863. $html .= " </div>\n";
  864. $html .= " <button type='button' class='navbar-toggler' data-toggle='collapse' data-target='#main_navbar' aria-expanded='false' aria-controls='main_navbar' aria-label='Toggle Menu' onclick=\"$('#body_header_user_menu').fadeOut(200);\">\n";
  865. $html .= " <span class='fa-solid fa-bars'></span>\n";
  866. $html .= " </button>\n";
  867. $html .= " <div class='collapse navbar-collapse' id='main_navbar'>\n";
  868. $html .= " <ul class='navbar-nav'>\n";
  869. if (!empty($menu_array) && sizeof($menu_array) != 0) {
  870. foreach ($menu_array as $index_main => $menu_parent) {
  871. $mod_li = "nav-item";
  872. $mod_a_1 = "";
  873. $submenu = false;
  874. if (!empty($menu_parent['menu_items']) && sizeof($menu_parent['menu_items']) > 0) {
  875. $mod_li = "nav-item dropdown ";
  876. $mod_a_1 = "data-toggle='dropdown' ";
  877. $submenu = true;
  878. }
  879. $mod_a_2 = (!empty($menu_parent['menu_item_link']) && !$submenu) ? $menu_parent['menu_item_link'] : '#';
  880. $mod_a_3 = ($menu_parent['menu_item_category'] == 'external') ? "target='_blank' " : null;
  881. if (isset($_SESSION['theme']['menu_main_icons']['boolean']) && $_SESSION['theme']['menu_main_icons']['boolean'] == 'true') {
  882. if (!empty($menu_parent['menu_item_icon']) && substr($menu_parent['menu_item_icon'], 0, 3) == 'fa-') { // font awesome icon
  883. $menu_main_icon = "<span class='".escape($menu_parent['menu_item_icon'])."' title=\"".escape($menu_parent['menu_language_title'])."\"></span>";
  884. }
  885. else {
  886. $menu_main_icon = null;
  887. }
  888. $menu_main_item = "<span class='d-sm-none d-md-none d-lg-inline' style='margin-left: 5px;'>".$menu_parent['menu_language_title']."</span>\n";
  889. }
  890. else {
  891. $menu_main_item = $menu_parent['menu_language_title'];
  892. }
  893. $html .= " <li class='".$mod_li."'>\n";
  894. $html .= " <a class='nav-link' ".$mod_a_1." href='".$mod_a_2."' ".$mod_a_3.">\n";
  895. $html .= " ".$menu_main_icon.$menu_main_item;
  896. $html .= " </a>\n";
  897. if ($submenu) {
  898. $columns = @sizeof($menu_parent['menu_items']) > 20 ? 2 : 1;
  899. $column_current = 1;
  900. $mod_ul = $columns > 1 ? 'multi-column' : null;
  901. $html .= " <ul class='dropdown-menu ".$mod_ul."'>\n";
  902. if ($columns > 1) {
  903. $html .= " <div class='row'>\n";
  904. $html .= " <div class='col-12 col-sm-6 pr-sm-0'>\n";
  905. $html .= " <ul class='multi-column-dropdown'>\n";
  906. }
  907. foreach ($menu_parent['menu_items'] as $index_sub => $menu_sub) {
  908. $mod_a_2 = $menu_sub['menu_item_link'];
  909. if ($mod_a_2 == '') {
  910. $mod_a_2 = '#';
  911. }
  912. $mod_a_3 = ($menu_sub['menu_item_category'] == 'external') ? "target='_blank' " : null;
  913. $menu_sub_icon = null;
  914. if ($_SESSION['theme']['menu_sub_icons']['boolean'] != 'false') {
  915. if (!empty($menu_sub['menu_item_icon']) && substr($menu_sub['menu_item_icon'], 0, 3) == 'fa-') { // font awesome icon
  916. $menu_sub_icon = "<span class='".escape($menu_sub['menu_item_icon'])."'></span>";
  917. }
  918. else {
  919. $menu_sub_icon = null;
  920. }
  921. }
  922. $html .= " <li class='nav-item'><a class='nav-link' href='".$mod_a_2."' ".$mod_a_3.">".($_SESSION['theme']['menu_sub_icons']['boolean'] != 'false' ? "<span class='fa-solid fa-minus d-inline-block d-sm-none float-left' style='margin: 4px 10px 0 25px;'></span>" : null).escape($menu_sub['menu_language_title']).$menu_sub_icon."</a></li>\n";
  923. if ($columns > 1 && $column_current == 1 && ($index_sub+1) > (ceil(@sizeof($menu_parent['menu_items'])/2)-1)) {
  924. $html .= " </ul>\n";
  925. $html .= " </div>\n";
  926. $html .= " <div class='col-12 col-sm-6 pl-sm-0'>\n";
  927. $html .= " <ul class='multi-column-dropdown'>\n";
  928. $column_current = 2;
  929. }
  930. }
  931. if ($columns > 1) {
  932. $html .= " </ul>\n";
  933. $html .= " </div>\n";
  934. $html .= " </div>\n";
  935. }
  936. $html .= " </ul>\n";
  937. }
  938. $html .= " </li>\n";
  939. }
  940. }
  941. $html .= " </ul>\n";
  942. $html .= " <ul class='navbar-nav ml-auto'>\n";
  943. //current user (latter condition for backward compatibility)
  944. if (
  945. !empty($_SESSION['username']) &&
  946. (
  947. isset($_SESSION['theme']['header_user_visible']['text']) &&
  948. $_SESSION['theme']['header_user_visible']['text'] == 'true'
  949. ) || (
  950. isset($_SESSION['theme']['user_visible']['text']) &&
  951. $_SESSION['theme']['user_visible']['text'] == 'true'
  952. )) {
  953. //set (default) user graphic size and icon
  954. $user_graphic = "<i class='".(!empty($_SESSION['theme']['body_header_icon_user']['text']) ? $_SESSION['theme']['body_header_icon_user']['text'] : 'fa-solid fa-user-circle')."'></i>";
  955. //overwrite user graphic with image from session, if exists
  956. if ($_SESSION['theme']['body_header_user_image']['boolean'] == true && !empty($_SESSION['user']['contact_image']) && is_uuid($_SESSION['user']['contact_image'])) {
  957. $user_graphic = "<span style=\"display: inline-block; vertical-align: middle; width: 15px; height: 15px; border-radius: 50%; margin-top: -2px; background-image: url('".PROJECT_PATH."/app/contacts/contact_attachment.php?id=".$_SESSION['user']['contact_image']."&action=download&sid=".session_id()."'); background-repeat: no-repeat; background-size: cover; background-position: center;\"></span>";
  958. }
  959. $html .= " <li class='nav-item'>\n";
  960. $html .= " <a class='nav-link header_user d-block d-sm-none' href='show:usermenu' title=\"".$_SESSION['username']."\" style='border-top: 1px solid ".($_SESSION['theme']['menu_sub_background_color']['text'] ?? 'rgba(0,0,0,0.90)')."' data-toggle='collapse' data-target='#main_navbar' onclick=\"event.preventDefault(); $('#body_header_user_menu').toggleFadeSlide();\">".($user_graphic ?? null)."<span style='margin-left: 7px;'>".escape($_SESSION['username'])."</span></a>";
  961. $html .= " <a class='nav-link header_user d-none d-sm-block' href='show:usermenu' title=\"".$_SESSION['username']."\" onclick=\"event.preventDefault(); $('#body_header_user_menu').toggleFadeSlide();\">".($user_graphic ?? null)."<span class='d-none d-md-inline' style='margin-left: 7px;'>".escape($_SESSION['username'])."</span></a>";
  962. $html .= " </li>\n";
  963. }
  964. //domain name/selector
  965. if (!empty($_SESSION['username']) && permission_exists('domain_select') && count($_SESSION['domains']) > 1 && $_SESSION['theme']['domain_visible']['text'] == 'true') {
  966. $html .= " <li class='nav-item'>\n";
  967. $html .= " <a class='nav-link header_domain header_domain_selector_domain d-block d-sm-none' href='select:domain' onclick='event.preventDefault();' data-toggle='collapse' data-target='#main_navbar' title='".$this->text['theme-label-open_selector']."'><span class='".(!empty($_SESSION['theme']['body_header_icon_domain']['text']) ? $_SESSION['theme']['body_header_icon_domain']['text'] : 'fa-solid fa-earth-americas')."'></span><span style='margin-left: 7px;'>".escape($_SESSION['domain_name'])."</span></a>";
  968. $html .= " <a class='nav-link header_domain header_domain_selector_domain d-none d-sm-block' href='select:domain' onclick='event.preventDefault();' title='".$this->text['theme-label-open_selector']."'><span class='".(!empty($_SESSION['theme']['body_header_icon_domain']['text']) ? $_SESSION['theme']['body_header_icon_domain']['text'] : 'fa-solid fa-earth-americas')."'></span><span class='d-none d-md-inline' style='margin-left: 7px;'>".escape($_SESSION['domain_name'])."</span></a>";
  969. $html .= " </li>\n";
  970. }
  971. //logout icon
  972. if (!empty($_SESSION['username']) && isset($_SESSION['theme']['logout_icon_visible']) && $_SESSION['theme']['logout_icon_visible']['text'] == "true") {
  973. $username_full = $_SESSION['username'].((count($_SESSION['domains']) > 1) ? "@".$_SESSION["user_context"] : null);
  974. $html .= " <li class='nav-item'>\n";
  975. $html .= " <a class='logout_icon' href='#' title=\"".$this->text['theme-label-logout']."\" onclick=\"modal_open('modal-logout','btn_logout');\"><span class='fa-solid fa-right-from-bracket'></span></a>";
  976. $html .= " </li>\n";
  977. unset($username_full);
  978. }
  979. $html .= " </ul>\n";
  980. $html .= " </div>\n";
  981. $html .= " </div>\n";
  982. $html .= "</nav>\n";
  983. //user menu on menu bar
  984. //styles below are defined here to prevent caching (following a permission change, etc)
  985. $html .= "<style>\n";
  986. $html .= "div#body_header_user_menu {\n";
  987. $html .= " right: ".(permission_exists('domain_select') ? '170px' : '30px')." !important;\n";
  988. $html .= " }\n";
  989. $html .= "@media (max-width: 575.98px) {\n";
  990. $html .= " div#body_header_user_menu {\n";
  991. $html .= " right: 10px !important;;\n";
  992. $html .= " }\n";
  993. $html .= " }\n";
  994. $html .= "</style>\n";
  995. $html .= "<div id='body_header_user_menu'>\n";
  996. $html .= " <div class='row m-0'>\n";
  997. if (!empty($_SESSION['user']['contact_image']) && is_uuid($_SESSION['user']['contact_image'])) {
  998. $html .= " <div class='col-5 col-sm-6 p-0' style=\"min-width: 130px; background-image: url('".PROJECT_PATH."/app/contacts/contact_attachment.php?id=".$_SESSION['user']['contact_image']."&action=download&sid=".session_id()."'); background-repeat: no-repeat; background-size: cover; background-position: center;\"></div>\n";
  999. }
  1000. else {
  1001. $html .= " <div class='col-5 col-sm-6 p-0 pt-1' style=\"min-width: 130px; cursor: help;\" title=\"".$text['label-primary-contact-attachment-image']."\"><i class='fa-solid fa-user-circle fa-8x' style='opacity: 0.1;'></i></div>\n";
  1002. }
  1003. // $html .= " <div class='".(!empty($_SESSION['user']['contact_image']) && is_uuid($_SESSION['user']['contact_image']) ? 'col-7 col-sm-6 pr-0' : 'col-12 p-0')." ' style='min-width: 130px; text-align: left;'>\n";
  1004. $html .= " <div class='col-7 col-sm-6 pr-0' style='min-width: 130px; text-align: left;'>\n";
  1005. if (!empty($_SESSION['user']['contact_name'])) {
  1006. $html .= " <div style='line-height: 95%;'><strong>".$_SESSION['user']['contact_name']."</strong></div>\n";
  1007. }
  1008. if (!empty($_SESSION['user']['contact_organization'])) {
  1009. $html .= " <div class='mt-2' style='font-size: 85%; line-height: 95%;'>".$_SESSION['user']['contact_organization']."</div>\n";
  1010. }
  1011. if (!empty($_SESSION['user']['extension'][0]['destination'])) {
  1012. $html .= " <div class='mt-2' style='font-size: 90%;'><i class='fa-solid fa-phone' style='margin-right: 5px; color: #00b043;'></i><strong>".$_SESSION['user']['extension'][0]['destination']."</strong></div>\n";
  1013. }
  1014. $html .= " <div class='pt-2 mt-3' style='border-top: 1px solid ".color_adjust($_SESSION['theme']['body_header_shadow_color']['text'], 0.05).";'>\n";
  1015. $html .= " <a href='".PROJECT_PATH."/core/users/user_edit.php?id=user'>".$text['title-account_settings']."</a><br>\n";
  1016. $html .= " <a href='".PROJECT_PATH."/logout.php'>".$text['title-logout']."</a>\n";
  1017. $html .= " </div>";
  1018. $html .= " </div>";
  1019. $html .= " </div>";
  1020. $html .= "</div>";
  1021. //modal for logout icon (above)
  1022. if (!empty($_SESSION['username']) && isset($_SESSION['theme']['logout_icon_visible']) && $_SESSION['theme']['logout_icon_visible']['text'] == "true") {
  1023. $html .= modal::create(['id'=>'modal-logout','type'=>'general','message'=>$this->text['theme-confirm-logout'],'actions'=>button::create(['type'=>'button','label'=>$this->text['theme-label-logout'],'icon'=>'fa-solid fa-right-from-bracket','id'=>'btn_logout','style'=>'float: right; margin-left: 15px;','collapse'=>'never','link'=>PROJECT_PATH.'/logout.php','onclick'=>"modal_close();"])]);
  1024. }
  1025. return $html;
  1026. unset($html);
  1027. }
  1028. /**
  1029. * build the vertical side menu html
  1030. */
  1031. public function menu_vertical($menu_array) {
  1032. //add multi-lingual support
  1033. $language = new text;
  1034. $text = $language->get();
  1035. //menu brand image and/or text
  1036. $html .= " <div id='menu_side_control_container'>\n";
  1037. $html .= " <div class='menu_side_control_state' style='float: right; ".($_SESSION['theme']['menu_side_state']['text'] != 'expanded' ? 'display: none' : null)."'>\n";
  1038. if ($_SESSION['theme']['menu_brand_type']['text'] != 'none') {
  1039. $html .= " <a class='menu_side_item_main menu_side_contract' onclick='menu_side_contract();' style='height: 60px; padding: 19px 16px 8px 16px !important; ".($_SESSION['theme']['menu_side_state']['text'] != 'expanded' ? "display: none;" : null)."'><i class='fa-solid fa-bars fa-fw'></i></a>";
  1040. }
  1041. $html .= " </div>\n";
  1042. $menu_brand_text = !empty($_SESSION['theme']['menu_brand_text']['text']) ? escape($_SESSION['theme']['menu_brand_text']['text']) : "FusionPBX";
  1043. switch ($_SESSION['theme']['menu_brand_type']['text']) {
  1044. case 'none':
  1045. $html .= "<a class='menu_side_item_main menu_side_contract' onclick='menu_side_contract();' style='".($_SESSION['theme']['menu_side_state']['text'] != 'expanded' ? "display: none;" : null)." height: 60px; min-width: ".($_SESSION['theme']['menu_side_width_contracted']['text'] ?? 60)."px;' title=\"".$this->text['theme-label-contract_menu']."\"><i class='fa-solid fa-bars fa-fw' style='z-index: 99800; padding-left: 1px; padding-top: 11px;'></i></a>";
  1046. $html .= "<a class='menu_side_item_main menu_side_expand' onclick='menu_side_expand();' style='".($_SESSION['theme']['menu_side_state']['text'] == 'expanded' || $_SESSION['theme']['menu_side_state']['text'] == 'hidden' ? "display: none;" : null)." height: 60px;' title=\"".$text['theme-label-expand_menu']."\"><i class='fa-solid fa-bars fa-fw' style='z-index: 99800; padding-left: 1px; padding-top: 11px;'></i></a>";
  1047. break;
  1048. case 'text':
  1049. $html .= "<a class='menu_brand_text' ".($_SESSION['theme']['menu_side_state']['text'] != 'expanded' ? "style='display: none;'" : null)." href='".PROJECT_PATH."/'>".escape($menu_brand_text)."</a>\n";
  1050. $html .= "<a class='menu_side_item_main menu_side_expand' style='height: 60px; padding-top: 19px; ".($_SESSION['theme']['menu_side_state']['text'] == 'expanded' ? "display: none" : null)."' onclick='menu_side_expand();' title=\"".$this->text['theme-label-expand_menu']."\"><i class='fa-solid fa-bars fa-fw' style='z-index: 99800; padding-left: 1px;'></i></a>";
  1051. break;
  1052. case 'image_text':
  1053. $menu_brand_image_contracted = !empty($_SESSION['theme']['menu_side_brand_image_contracted']['text']) ? $_SESSION['theme']['menu_side_brand_image_contracted']['text'] : PROJECT_PATH."/themes/default/images/logo_side_contracted.png";
  1054. $html .= "<a class='menu_brand_image' href='".PROJECT_PATH."/'>";
  1055. $html .= "<img id='menu_brand_image_contracted' style='".($_SESSION['theme']['menu_side_state']['text'] == 'expanded' ? "display: none;" : null)."' src='".escape($menu_brand_image_contracted)."' title=\"".escape($menu_brand_text)."\">";
  1056. $html .= "<span id='menu_brand_image_expanded' class='menu_brand_text' ".($_SESSION['theme']['menu_side_state']['text'] != 'expanded' ? "style='display: none;'" : null).">".escape($menu_brand_text)."</span>";
  1057. $html .= "</a>\n";
  1058. break;
  1059. case 'image':
  1060. default:
  1061. $menu_brand_image_contracted = !empty($_SESSION['theme']['menu_side_brand_image_contracted']['text']) ? $_SESSION['theme']['menu_side_brand_image_contracted']['text'] : PROJECT_PATH."/themes/default/images/logo_side_contracted.png";
  1062. $menu_brand_image_expanded = !empty($_SESSION['theme']['menu_side_brand_image_expanded']['text']) ? $_SESSION['theme']['menu_side_brand_image_expanded']['text'] : PROJECT_PATH."/themes/default/images/logo_side_expanded.png";
  1063. $html .= "<a class='menu_brand_image' href='".PROJECT_PATH."/'>";
  1064. $html .= "<img id='menu_brand_image_contracted' style='".($_SESSION['theme']['menu_side_state']['text'] == 'expanded' ? "display: none;" : null)."' src='".escape($menu_brand_image_contracted)."' title=\"".escape($menu_brand_text)."\">";
  1065. $html .= "<img id='menu_brand_image_expanded' ".($_SESSION['theme']['menu_side_state']['text'] != 'expanded' ? "style='display: none;'" : null)." src='".escape($menu_brand_image_expanded)."' title=\"".escape($menu_brand_text)."\">";
  1066. $html .= "</a>\n";
  1067. break;
  1068. }
  1069. $html .= " </div>\n";
  1070. //main menu items
  1071. if (!empty($menu_array)) {
  1072. foreach ($menu_array as $menu_index_main => $menu_item_main) {
  1073. $menu_target = ($menu_item_main['menu_item_category'] == 'external') ? '_blank' : '';
  1074. $html .= " <a class='menu_side_item_main' ".(!empty($menu_item_main['menu_item_link']) ? "href='".$menu_item_main['menu_item_link']."' target='".$menu_target."'" : "onclick=\"menu_side_expand(); menu_side_item_toggle('".$menu_item_main['menu_item_uuid']."');\"")." title=\"".$menu_item_main['menu_language_title']."\">";
  1075. if (is_array($menu_item_main['menu_items']) && sizeof($menu_item_main['menu_items']) != 0 && $_SESSION['theme']['menu_side_item_main_sub_icons']['boolean'] == 'true') {
  1076. $html .= " <div class='menu_side_item_main_sub_icons' style='float: right; margin-right: -1px; ".($_SESSION['theme']['menu_side_state']['text'] != 'expanded' ? "display: none;" : null)."'><i id='sub_arrow_".$menu_item_main['menu_item_uuid']."' class='sub_arrows ".(!empty($_SESSION['theme']['menu_side_item_main_sub_icon_expand']['text']) ? $_SESSION['theme']['menu_side_item_main_sub_icon_expand']['text'] : 'fa-solid fa-chevron-down')." fa-xs'></i></div>\n";
  1077. }
  1078. if (!empty($menu_item_main['menu_item_icon']) && substr($menu_item_main['menu_item_icon'], 0, 3) == 'fa-') { // font awesome icon
  1079. $html .= "<i class='menu_side_item_icon ".$menu_item_main['menu_item_icon']." fa-fw' style='z-index: 99800; margin-right: 8px;'></i>";
  1080. }
  1081. $html .= "<span class='menu_side_item_title' ".($_SESSION['theme']['menu_side_state']['text'] != 'expanded' ? "style='display: none;'" : null).">".$menu_item_main['menu_language_title']."</span>";
  1082. $html .= "</a>\n";
  1083. //sub menu items
  1084. if (is_array($menu_item_main['menu_items']) && sizeof($menu_item_main['menu_items']) != 0) {
  1085. $html .= " <div id='sub_".$menu_item_main['menu_item_uuid']."' class='menu_side_sub' style='display: none;'>\n";
  1086. foreach ($menu_item_main['menu_items'] as $menu_index_sub => $menu_item_sub) {
  1087. $menu_sub_icon = null;
  1088. if ($_SESSION['theme']['menu_sub_icons']['boolean'] != 'false') {
  1089. if (!empty($menu_item_sub['menu_item_icon']) && substr($menu_item_sub['menu_item_icon'], 0, 3) == 'fa-') { // font awesome icon
  1090. $menu_sub_icon = "<span class='".escape($menu_item_sub['menu_item_icon']).(substr($menu_item_sub['menu_item_icon'], 0, 3) == 'fa-' ? ' fa-fw' : null)."'></span>";
  1091. }
  1092. else {
  1093. $menu_sub_icon = null;
  1094. }
  1095. }
  1096. $html .= " <a class='menu_side_item_sub' ".($menu_item_sub['menu_item_category'] == 'external' ? "target='_blank'" : null)." href='".$menu_item_sub['menu_item_link']."'>";
  1097. $html .= "<span class='menu_side_item_title' ".($_SESSION['theme']['menu_side_state']['text'] != 'expanded' ? "style='display: none;'" : null).">".$menu_item_sub['menu_language_title']."</span>";
  1098. $html .= $menu_sub_icon."</a>\n";
  1099. }
  1100. $html .= " </div>\n";
  1101. }
  1102. }
  1103. $html .= " <div style='height: 100px;'></div>\n";
  1104. }
  1105. $html .= "</div>\n";
  1106. if ($_SESSION['theme']['menu_side_state']['text'] != 'expanded' && $_SESSION['theme']['menu_side_state']['text'] != 'hidden') {
  1107. $content_container_onclick = "onclick=\"clearTimeout(menu_side_contract_timer); if ($(window).width() >= 576) { menu_side_contract(); }\"";
  1108. }
  1109. $html .= "<div id='content_container' ".$content_container_onclick.">\n";
  1110. //user menu on body header when side menu
  1111. //styles below are defined here to prevent caching (following a permission change, etc)
  1112. $html .= "<style>\n";
  1113. $html .= "div#body_header_user_menu {\n";
  1114. $html .= " right: ".(permission_exists('domain_select') ? '170px' : '30px')." !important;\n";
  1115. $html .= " }\n";
  1116. $html .= "@media (max-width: 575.98px) {\n";
  1117. $html .= " div#body_header_user_menu {\n";
  1118. $html .= " right: 10px !important;;\n";
  1119. $html .= " }\n";
  1120. $html .= " }\n";
  1121. $html .= "</style>\n";
  1122. $html .= "<div id='body_header_user_menu'>\n";
  1123. $html .= " <div class='row m-0'>\n";
  1124. if (!empty($_SESSION['user']['contact_image']) && is_uuid($_SESSION['user']['contact_image'])) {
  1125. $html .= " <div class='col-5 col-sm-6 p-0' style=\"min-width: 130px; background-image: url('".PROJECT_PATH."/app/contacts/contact_attachment.php?id=".$_SESSION['user']['contact_image']."&action=download&sid=".session_id()."'); background-repeat: no-repeat; background-size: cover; background-position: center;\"></div>\n";
  1126. }
  1127. else {
  1128. $html .= " <div class='col-5 col-sm-6 p-0 pt-1' style=\"min-width: 130px; cursor: help;\" title=\"".$text['label-primary-contact-attachment-image']."\"><i class='fa-solid fa-user-circle fa-8x' style='opacity: 0.1;'></i></div>\n";
  1129. }
  1130. // $html .= " <div class='".(!empty($_SESSION['user']['contact_image']) && is_uuid($_SESSION['user']['contact_image']) ? 'col-7 col-sm-6 pr-0' : 'col-12 p-0')." ' style='min-width: 130px; text-align: left;'>\n";
  1131. $html .= " <div class='col-7 col-sm-6 pr-0' style='min-width: 130px; text-align: left;'>\n";
  1132. if (!empty($_SESSION['user']['contact_name'])) {
  1133. $html .= " <div style='line-height: 95%;'><strong>".$_SESSION['user']['contact_name']."</strong></div>\n";
  1134. }
  1135. if (!empty($_SESSION['user']['contact_organization'])) {
  1136. $html .= " <div class='mt-2' style='font-size: 85%; line-height: 95%;'>".$_SESSION['user']['contact_organization']."</div>\n";
  1137. }
  1138. if (!empty($_SESSION['user']['extension'][0]['destination'])) {
  1139. $html .= " <div class='mt-2' style='font-size: 90%;'><i class='fa-solid fa-phone' style='margin-right: 5px; color: #00b043;'></i><strong>".$_SESSION['user']['extension'][0]['destination']."</strong></div>\n";
  1140. }
  1141. $html .= " <div class='pt-2 mt-3' style='border-top: 1px solid ".color_adjust($_SESSION['theme']['body_header_shadow_color']['text'], 0.05).";'>\n";
  1142. $html .= " <a href='".PROJECT_PATH."/core/users/user_edit.php?id=user'>".$text['title-account_settings']."</a><br>\n";
  1143. $html .= " <a href='".PROJECT_PATH."/logout.php'>".$text['title-logout']."</a>\n";
  1144. $html .= " </div>";
  1145. $html .= " </div>";
  1146. $html .= " </div>";
  1147. $html .= "</div>";
  1148. $html .= " <div id='body_header'>\n";
  1149. //header: left
  1150. $html .= "<div class='float-left'>\n";
  1151. // $html .= button::create(['type'=>'button','id'=>'menu_side_state_hidden_button','title'=>$this->text['theme-label-expand_menu'],'icon'=>'bars','class'=>'default '.($_SESSION['theme']['menu_side_state']['text'] != 'hidden' ? 'hide-sm-up ' : null).'float-left','onclick'=>'menu_side_expand();']);
  1152. $html .= "<a id='menu_side_state_hidden_button' class='".($_SESSION['theme']['menu_side_state']['text'] != 'hidden' ? 'hide-sm-up ' : null)."' href='show:menu' onclick=\"event.preventDefault(); menu_side_expand();\" title=\"".$this->text['theme-label-expand_menu']."\"><i class='fa-solid fa-bars fa-fw' style='margin: 7px 10px 5px 10px;'></i></a>";
  1153. $body_header_brand_text = !empty($_SESSION['theme']['body_header_brand_text']['text']) ? escape($_SESSION['theme']['body_header_brand_text']['text']) : "FusionPBX";
  1154. if ($_SESSION['theme']['body_header_brand_type']['text'] == 'image' || $_SESSION['theme']['body_header_brand_type']['text'] == 'image_text') {
  1155. $body_header_brand_image = !empty($_SESSION['theme']['body_header_brand_image']['text']) ? $_SESSION['theme']['body_header_brand_image']['text'] : PROJECT_PATH."/themes/default/images/logo_side_expanded.png";
  1156. $html .= "<div id='body_header_brand_image'>";
  1157. $html .= "<a href='".PROJECT_PATH."/'><img id='body_header_brand_image' src='".escape($body_header_brand_image)."' title=\"".escape($body_header_brand_text)."\"></a>";
  1158. $html .= "</div>";
  1159. }
  1160. if ($_SESSION['theme']['body_header_brand_type']['text'] == 'text' || $_SESSION['theme']['body_header_brand_type']['text'] == 'image_text') {
  1161. $html .= "<div id='body_header_brand_text'><a href='".PROJECT_PATH."/'>".$body_header_brand_text."</a></div>";
  1162. }
  1163. $html .= "</div>\n";
  1164. //header: right
  1165. $html .= "<div class='float-right' style='white-space: nowrap;'>";
  1166. //current user
  1167. //set (default) user graphic size and icon
  1168. $user_graphic_size = 18;
  1169. $user_graphic = "<i class='".(!empty($_SESSION['theme']['body_header_icon_user']['text']) ? $_SESSION['theme']['body_header_icon_user']['text'] : 'fa-solid fa-user-circle')." fa-lg fa-fw' style='margin-right: 5px;'></i>";
  1170. //overwrite user graphic with image from session, if exists
  1171. if ($_SESSION['theme']['body_header_user_image']['boolean'] == true && !empty($_SESSION['user']['contact_image']) && is_uuid($_SESSION['user']['contact_image'])) {
  1172. $user_graphic_size = str_replace(['px','%'], '', ($_SESSION['theme']['body_header_user_image_size']['numeric'] ?? 18));
  1173. $user_graphic = "<span style=\"display: inline-block; vertical-align: middle; width: ".$user_graphic_size."px; height: ".$user_graphic_size."px; border-radius: 50%; margin-right: 7px; margin-top: ".($user_graphic_size > 18 ? '-'.(ceil(($user_graphic_size - 18) / 2) - 4) : '-4')."px; background-image: url('".PROJECT_PATH."/app/contacts/contact_attachment.php?id=".$_SESSION['user']['contact_image']."&action=download&sid=".session_id()."'); background-repeat: no-repeat; background-size: cover; background-position: center;\"></span>";
  1174. }
  1175. $html .= "<span style='display: inline-block; padding-right: 20px; font-size: 90%;'>\n";
  1176. $html .= " <a href='show:usermenu' title=\"".$_SESSION['username']."\" onclick=\"event.preventDefault(); $('#body_header_user_menu').toggleFadeSlide();\">".($user_graphic ?? null)."<span class='d-none d-sm-inline'>".escape($_SESSION['username'])."</span></a>";
  1177. $html .= "</span>\n";
  1178. //domain name/selector (sm+)
  1179. if (!empty($_SESSION['username']) && permission_exists('domain_select') && count($_SESSION['domains']) > 1 && $_SESSION['theme']['domain_visible']['text'] == 'true') {
  1180. $html .= "<span style='display: inline-block; padding-right: 10px; font-size: 90%;'>\n";
  1181. $html .= " <a href='select:domain' onclick='event.preventDefault();' title='".$this->text['theme-label-open_selector']."' class='header_domain_selector_domain'><i class='".(!empty($_SESSION['theme']['body_header_icon_domain']['text']) ? $_SESSION['theme']['body_header_icon_domain']['text'] : 'fa-solid fa-earth-americas')." fa-fw' style='vertical-align: middle; font-size: ".($user_graphic_size - 1)."px; margin-top: ".($user_graphic_size > 18 ? '-'.(ceil(($user_graphic_size - 18) / 2) - 4) : '-3')."px; margin-right: 3px; line-height: 40%;'></i><span class='d-none d-sm-inline'>".escape($_SESSION['domain_name'])."</span></a>";
  1182. $html .= "</span>\n";
  1183. }
  1184. //logout icon
  1185. if (!empty($_SESSION['username']) && $_SESSION['theme']['logout_icon_visible']['text'] == "true") {
  1186. $html .= "<a id='header_logout_icon' href='#' title=\"".$this->text['theme-label-logout']."\" onclick=\"modal_open('modal-logout','btn_logout');\"><span class='fa-solid fa-right-from-bracket'></span></a>";
  1187. }
  1188. $html .= "</div>";
  1189. $html .= " </div>\n";
  1190. //modal for logout icon (above)
  1191. if (!empty($_SESSION['username']) && $_SESSION['theme']['logout_icon_visible']['text'] == "true") {
  1192. $html .= modal::create(['id'=>'modal-logout','type'=>'general','message'=>$this->text['theme-confirm-logout'],'actions'=>button::create(['type'=>'button','label'=>$this->text['theme-label-logout'],'icon'=>'fa-solid fa-right-from-bracket','id'=>'btn_logout','style'=>'float: right; margin-left: 15px;','collapse'=>'never','link'=>PROJECT_PATH.'/logout.php','onclick'=>"modal_close();"])]);
  1193. }
  1194. return $html;
  1195. unset($html);
  1196. }
  1197. }
  1198. }
  1199. ?>