BxBaseFunctions.php 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup UnaBaseView UNA Base Representation Classes
  7. * @{
  8. */
  9. class BxBaseFunctions extends BxDolFactory implements iBxDolSingleton
  10. {
  11. protected $_oTemplate;
  12. protected $_sDesignBoxMenuTmplDefault;
  13. protected $_sDesignBoxMenuIcon;
  14. protected $_sDesignBoxMenuIconType;
  15. protected $_sDesignBoxMenuClick;
  16. protected function __construct($oTemplate)
  17. {
  18. parent::__construct();
  19. $this->_oTemplate = $oTemplate ? $oTemplate : BxDolTemplate::getInstance();
  20. $this->_sDesignBoxMenuTmplDefault = 'menu_block_submenu_ver.html';
  21. $this->_sDesignBoxMenuIcon = 'ellipsis-v';
  22. $this->_sDesignBoxMenuIconType = 'icon';
  23. $this->_sDesignBoxMenuClick = "bx_menu_popup_inline('#{design_box_menu}', this, " . json_encode(array(
  24. 'moveToDocRoot' => false
  25. )) . ")";
  26. }
  27. /**
  28. * Prevent cloning the instance
  29. */
  30. public function __clone()
  31. {
  32. if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
  33. trigger_error('Clone is not allowed for the class: ' . get_class($this), E_USER_ERROR);
  34. }
  35. public static function getInstanceWithTemplate($oTemplate)
  36. {
  37. $sClassName = 'BxTemplFunctions';
  38. if ($oTemplate){
  39. $sClassName .= get_class($oTemplate);
  40. }
  41. if(!isset($GLOBALS['bxDolClasses'][$sClassName]))
  42. $GLOBALS['bxDolClasses'][$sClassName] = new BxTemplFunctions($oTemplate);
  43. return $GLOBALS['bxDolClasses'][$sClassName];
  44. }
  45. public static function getInstance()
  46. {
  47. return self::getInstanceWithTemplate(null);
  48. }
  49. function TemplPageAddComponent($sKey)
  50. {
  51. $mixedResult = false; // if you have not such component, return false!
  52. switch($sKey) {
  53. case 'sys_header_width':
  54. $mixedResult = 'bx-def-page-width';
  55. break;
  56. case 'sys_toolbar_search':
  57. $oSearch = new BxTemplSearch();
  58. $oSearch->setLiveSearch(true);
  59. $mixedResult = $this->_oTemplate->parseHtmlByName('_page_toolbar_search.html', [
  60. 'sys_site_search' => $oSearch->getForm(BX_DB_PADDING_DEF, false, true) . $oSearch->getResultsContainer()
  61. ]);
  62. break;
  63. }
  64. return $mixedResult;
  65. }
  66. function msgBox($sText, $iTimer = 0, $sOnClose = "")
  67. {
  68. $iId = time() . mt_rand(1, 1000);
  69. if($iTimer > 0)
  70. BxDolTemplate::getInstance()->addJs(array('jquery.anim.js'));
  71. return $this->_oTemplate->parseHtmlByName('messageBox.html', array(
  72. 'id' => $iId,
  73. 'msgText' => $sText,
  74. 'bx_if:timer' => array(
  75. 'condition' => $iTimer > 0,
  76. 'content' => array(
  77. 'id' => $iId,
  78. 'time' => 1000 * $iTimer,
  79. 'on_close' => $sOnClose,
  80. )
  81. )
  82. ));
  83. }
  84. /**
  85. * Get standard popup box with title.
  86. *
  87. * @param string $sName - unique name
  88. * @param string $sTitle - translated title
  89. * @param string $sContent - content of the box
  90. * @return HTML string
  91. */
  92. function popupBox($sName, $sTitle, $sContent, $isHiddenByDefault = false)
  93. {
  94. $iId = !empty($sName) ? $sName : time();
  95. return $this->_oTemplate->parseHtmlByName('popup_box.html', array(
  96. 'id' => $iId,
  97. 'wrapper_style' => $isHiddenByDefault ? 'display:none;' : '',
  98. 'title' => $sTitle,
  99. 'content' => $sContent
  100. ));
  101. }
  102. /**
  103. * Get popup box without title.
  104. *
  105. * @param string $sName - unique name
  106. * @param string $sContent - content of the box
  107. * @return HTML string
  108. */
  109. function transBox($sName, $sContent, $isHiddenByDefault = false, $isPlaceInCenter = false)
  110. {
  111. return $this->simpleBox($sName, $sContent, $isHiddenByDefault, $isPlaceInCenter, 'popup_trans.html');
  112. }
  113. function slideBox($sName, $sContent, $isHiddenByDefault = false)
  114. {
  115. return $this->simpleBox($sName, $sContent, $isHiddenByDefault, false, 'popup_slide.html');
  116. }
  117. function inlineBox($sName, $sContent, $isHiddenByDefault = false)
  118. {
  119. return $this->simpleBox($sName, $sContent, $isHiddenByDefault, false, 'popup_inline.html');
  120. }
  121. protected function simpleBox($sName, $sContent, $isHiddenByDefault, $isPlaceInCenter, $sTemplate)
  122. {
  123. $iId = !empty($sName) ? $sName : time();
  124. if(!is_array($sContent))
  125. $sContent = array('content' => $sContent);
  126. $sContent = $this->_oTemplate->parseHtmlByName($sTemplate, array_merge(array(
  127. 'id' => $iId,
  128. 'wrapper_class' => '',
  129. 'wrapper_style' => $isHiddenByDefault ? 'display:none;' : '',
  130. 'wrapper_role' => 'dialog',
  131. 'wrapper_attrs' => '',
  132. 'content' => ''
  133. ), $sContent));
  134. if($isPlaceInCenter)
  135. $sContent = '<div class="login_ajax_wrap">' . $sContent . '</div>';
  136. return $sContent;
  137. }
  138. function simpleBoxContent($sContent, $bWithIndent = true)
  139. {
  140. if(!$bWithIndent)
  141. return $sContent;
  142. return $this->_oTemplate->parseHtmlByName('popup_content_indent.html', array(
  143. 'content' => $sContent
  144. ));
  145. }
  146. function getIcon($sCode, $aAttrs = array())
  147. {
  148. $sIconFont = false;
  149. $sIconA = false;
  150. $sIconUrl = false;
  151. $sIconHtml = false;
  152. $sIconFontWithHtml = false;
  153. $sClass = '';
  154. if(!empty($aAttrs['class'])) {
  155. $sClass = ' ' . $aAttrs['class'] .' ';
  156. unset($aAttrs['class']);
  157. }
  158. $sAttrs = '';
  159. foreach($aAttrs as $sKey => $sValue)
  160. $sAttrs .= ' ' . $sKey . '="' . bx_html_attribute($sValue) . '"';
  161. if (!empty($sCode)) {
  162. if (is_numeric($sCode) && (int)$sCode > 0) {
  163. $oStorage = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES);
  164. $sIconUrl = $oStorage ? $oStorage->getFileUrlById((int)$sCode) : false;
  165. }
  166. else {
  167. if (strpos($sCode, '&lt;img') !== false || strpos($sCode, '<img') !== false) {
  168. if(strpos($sCode, '&lt;img') !== false)
  169. $sIconHtml = htmlspecialchars_decode($sCode);
  170. else
  171. $sIconHtml = $sCode;
  172. //--- Process Inline SVG
  173. } else if (strpos($sCode, '&lt;svg') !== false || strpos($sCode, '<svg') !== false) {
  174. if(strpos($sCode, '&lt;svg') !== false)
  175. $sIconHtml = htmlspecialchars_decode($sCode);
  176. else
  177. $sIconHtml = $sCode;
  178. $sClass .= 'sys-icon sys-icon-svg ';
  179. $sIconHtmlClear = strip_tags($sIconHtml, '<svg>');
  180. if ($sClass != '' && strpos($sIconHtmlClear, 'class="') !== false)
  181. $sIconHtml = str_replace('class="', 'class="' . $sClass, $sIconHtml);
  182. else
  183. $sIconHtml = str_replace('<svg', '<svg class="' . $sClass . '" ', $sIconHtml);
  184. if ($sAttrs != '')
  185. $sIconHtml = str_replace('<svg', '<svg ' . $sAttrs . ' ', $sIconHtml);
  186. }
  187. else {
  188. $sEmojIsRegex =
  189. '/[\x{0080}-\x{02AF}'
  190. .'\x{0300}-\x{03FF}'
  191. .'\x{0600}-\x{06FF}'
  192. .'\x{0C00}-\x{0C7F}'
  193. .'\x{1DC0}-\x{1DFF}'
  194. .'\x{1E00}-\x{1EFF}'
  195. .'\x{2000}-\x{209F}'
  196. .'\x{20D0}-\x{214F}'
  197. .'\x{2190}-\x{23FF}'
  198. .'\x{2460}-\x{25FF}'
  199. .'\x{2600}-\x{27EF}'
  200. .'\x{2900}-\x{29FF}'
  201. .'\x{2B00}-\x{2BFF}'
  202. .'\x{2C60}-\x{2C7F}'
  203. .'\x{2E00}-\x{2E7F}'
  204. .'\x{3000}-\x{303F}'
  205. .'\x{A490}-\x{A4CF}'
  206. .'\x{E000}-\x{F8FF}'
  207. .'\x{FE00}-\x{FE0F}'
  208. .'\x{FE30}-\x{FE4F}'
  209. .'\x{1F000}-\x{1F02F}'
  210. .'\x{1F0A0}-\x{1F0FF}'
  211. .'\x{1F100}-\x{1F64F}'
  212. .'\x{1F680}-\x{1F6FF}'
  213. .'\x{1F910}-\x{1F96B}'
  214. .'\x{1F980}-\x{1F9E0}]/u';
  215. //--- Process Emoji
  216. if(preg_match($sEmojIsRegex, $sCode, $aTmp))
  217. $sIconHtml = $this->_oTemplate->parseHtmlByName('icon_emoji.html', array(
  218. 'icon' => $sCode,
  219. 'class' => $sClass,
  220. 'attrs' => $sAttrs
  221. ));
  222. else {
  223. if (strpos($sCode, '.') === false) {
  224. //--- Process animated icon
  225. if (strncmp($sCode, 'a:', 2) === 0)
  226. $sIconA = substr($sCode, 2);
  227. //--- Process font icons
  228. else {
  229. $sIconFont = $sCode;
  230. $sIconFontWithHtml = $this->getFontIconAsHtml($sIconFont, $sClass, $sAttrs);
  231. }
  232. }
  233. else {
  234. //--- Process common image
  235. if((!preg_match('/^https?:\/\//', $sCode)))
  236. $sIconUrl = $this->_oTemplate->getIconUrl($sCode);
  237. else
  238. $sIconUrl = $sCode;
  239. }
  240. }
  241. }
  242. }
  243. }
  244. return array ($sIconFont, $sIconUrl, $sIconA, $sIconHtml, $sIconFontWithHtml);
  245. }
  246. function getFontIconAsHtml($sIconFont, $sClass = '', $sAttrs = '')
  247. {
  248. return '<i class="sys-icon ' . $sIconFont .' ' . $sClass . '"' . $sAttrs . '></i>';
  249. }
  250. function getIconAsHtml($sCode, $aAttrs = array())
  251. {
  252. $aIcons = $this->getIcon($sCode, $aAttrs);
  253. return $aIcons[3] . $aIcons[4];
  254. }
  255. function getIconPreview($iId, $sIconImage = '', $sIcon = '')
  256. {
  257. $bIconImage = !empty($sIconImage);
  258. $aIcons = $this->getIcon($sIcon);
  259. $sIconHtml = $aIcons[2] . $aIcons[3] . $aIcons[4];
  260. $bIconHtml = !empty($sIconHtml) && !$bIconImage;
  261. return $this->_oTemplate->parseHtmlByName('item_icon_preview.html', [
  262. 'id' => $iId,
  263. 'bx_if:show_icon_empty' => [
  264. 'condition' => !$bIconImage && !$bIconHtml,
  265. 'content' => []
  266. ],
  267. 'bx_if:show_icon_image' => [
  268. 'condition' => $bIconImage,
  269. 'content' => [
  270. 'url' => $sIconImage,
  271. 'id' => $iId
  272. ]
  273. ],
  274. 'bx_if:show_icon_html' => [
  275. 'condition' => $bIconHtml,
  276. 'content' => [
  277. 'icon' => $sIconHtml
  278. ]
  279. ]
  280. ]);
  281. }
  282. function getTemplateIcon($sName)
  283. {
  284. $sUrl = $this->_oTemplate->getIconUrl($sName);
  285. return !empty($sUrl) ? $sUrl : $this->_oTemplate->getIconUrl('spacer.gif');
  286. }
  287. function getTemplateImage($sName)
  288. {
  289. $sUrl = $this->_oTemplate->getImageUrl($sName);
  290. return !empty($sUrl) ? $sUrl : $this->_oTemplate->getImageUrl('spacer.gif');
  291. }
  292. function sysIcon ($sIcon, $sName, $sUrl = '', $iWidth = 0)
  293. {
  294. return '<div class="sys_icon">' . ($sUrl ? '<a title="'.$sName.'" href="'.$sUrl.'">' : '') . '<img alt="'.$sName.'" src="'.$sIcon.'" '.($iWidth ? 'width='.$iWidth : '').' />' . ($sUrl ? '</a>' : '') . '</div>';
  295. }
  296. /**
  297. * functions for limiting maximal string length
  298. */
  299. function getStringWithLimitedLength($mixedString, $iWidth = 45, $isPopupOnOverflow = false, $bReturnString = true, $sPopupString = null)
  300. {
  301. if(is_array($mixedString))
  302. list($sStrPlane, $sStrOriginal) = $mixedString;
  303. else
  304. $sStrPlane = $sStrOriginal = $mixedString;
  305. if(empty($sStrPlane) || mb_strlen($sStrPlane, 'UTF-8') <= $iWidth)
  306. return $bReturnString ? $sStrPlane : [$sStrPlane];
  307. $sResult = '';
  308. $aWords = mb_split("[\s\r\n]", $sStrPlane);
  309. $iPosition = 0;
  310. $iWidthReal = $iWidth - 3;
  311. $iWidthMin = $iWidth - 15;
  312. foreach($aWords as $sWord) {
  313. $sWord = trim($sWord);
  314. $iWord = mb_strlen($sWord, 'UTF-8');
  315. if ($iPosition + $iWord > $iWidthReal)
  316. break;
  317. // add word and continue
  318. $sResult .= ' ' . $sWord;
  319. $iPosition += 1 + $iWord;
  320. }
  321. // last word is too long, cut it
  322. if(!$iPosition || $iPosition < $iWidthMin)
  323. $sResult .= ' ' . mb_substr($sWord, 0, $iWidthReal - $iPosition - $iWord, 'UTF-8');
  324. $sResult = trim($sResult);
  325. // add tripple dot
  326. if(!$isPopupOnOverflow) {
  327. $sResult .= '...';
  328. return $bReturnString ? $sResult : [$sResult];
  329. }
  330. // add button width popup
  331. $sId = 'bx-str-limit-' . rand(1, PHP_INT_MAX);
  332. $sTitle = bx_html_attribute(_t('_sys_str_limit_show'));
  333. $sPopup = '<button type="button" class="bx-str-limit pl-2" title="' . $sTitle . '" aria-label="' . $sTitle . '" onclick="$(\'#' . $sId . '\').dolPopup({pointer:{el:$(this), offset:\'10 1\'}})"><i class="sys-icon ellipsis-h"></i></button>';
  334. $sPopup .= BxTemplFunctions::getInstance()->transBox($sId, '<div class="bx-def-padding">' . ($sPopupString ?? $sStrOriginal) . '</div>', true);
  335. return $bReturnString ? $sResult . $sPopup : [$sResult, $sPopup];
  336. }
  337. /**
  338. * Display design box with specified title, template, content and menu.
  339. * @param $sTitle - design box title, please note that some templates don't use title.
  340. * @param $sContent - design box content.
  341. * @param $iTemplateNum - number of design box template, use predefined contants only, default is BX_DB_DEF.
  342. * @param $mixedMenu - design box menu, it can be:
  343. * - object: instance of BxTemplMenu class
  344. * - string: menu object identifier
  345. * - array: array of menu links to create menu from
  346. * @param $mixedButtons - design box menu representation, it can be:
  347. * - false: design box menu will be used as horizontal menu (tabs)
  348. * - array: array of menu links to create menu from. If empty array is used and 'design box menu' isn't empty, then 'design box menu' will be added as one of menu items automatically. If non-empty array is used and 'design box menu' isn't empty then it should be added as one of menu items. Use array('menu' => 1) to define menu item for 'design box menu'.
  349. * @return string
  350. *
  351. * @see BX_DB_CONTENT_ONLY
  352. * @see BX_DB_DEF
  353. * @see BX_DB_EMPTY
  354. * @see BX_DB_NO_CAPTION
  355. * @see BX_DB_PADDING_CONTENT_ONLY
  356. * @see BX_DB_PADDING_DEF
  357. * @see BX_DB_PADDING_NO_CAPTION
  358. */
  359. function designBoxContent ($sTitle, $sContent, $iTemplateNum = BX_DB_DEF, $mixedMenu = false, $mixedButtons = [])
  360. {
  361. $bNoTitle = in_array($iTemplateNum, [BX_DB_CONTENT_ONLY, BX_DB_PADDING_CONTENT_ONLY, BX_DB_NO_CAPTION, BX_DB_PADDING_NO_CAPTION]);
  362. $sMenu = $this->designBoxMenu($mixedMenu, $mixedButtons);
  363. $aTmplVarsDbMenu = [];
  364. if($bNoTitle && $sMenu)
  365. $aTmplVarsDbMenu = [
  366. 'caption_item' => $sMenu
  367. ];
  368. $sDescription = $sIcon = $sIconHtml = '';
  369. if(is_array($sTitle)) {
  370. list($sTitle, $sDescription, $sIcon) = $sTitle;
  371. if($sIcon) {
  372. list($sIcon, $sIconUrl, $sIconA, $sIconHtml) = $this->getIcon($sIcon);
  373. if($sIcon)
  374. $sIcon = BxDolIconset::getObjectInstance()->getIcon($sIcon);
  375. }
  376. }
  377. return $this->_oTemplate->parseHtmlByName('designbox_' . (int)$iTemplateNum . '.html', array_merge([
  378. 'bx_if:show_db_icon' => [
  379. 'condition' => !empty($sIcon),
  380. 'content' => [
  381. 'icon' => $sIcon,
  382. ]
  383. ],
  384. 'bx_if:show_db_icon_html' => [
  385. 'condition' => !empty($sIconHtml),
  386. 'content' => [
  387. 'icon_html' => $sIconHtml,
  388. ]
  389. ],
  390. 'title' => $sTitle,
  391. 'bx_if:show_db_description' => [
  392. 'condition' => !empty($sDescription),
  393. 'content' => [
  394. 'description' => $sDescription,
  395. ]
  396. ],
  397. 'designbox_content' => $sContent,
  398. ], ($bNoTitle ? [
  399. 'bx_if:show_db_menu' => [
  400. 'condition' => !empty($aTmplVarsDbMenu),
  401. 'content' => $aTmplVarsDbMenu
  402. ]
  403. ] : [
  404. 'caption_item' => $sMenu
  405. ]))
  406. );
  407. }
  408. function designBoxMenu ($mixedMenu, $mixedButtons = [])
  409. {
  410. $bUseTabs = is_bool($mixedButtons) && $mixedButtons === true;
  411. $sClass = $sMenu = '';
  412. if(!empty($mixedMenu)) {
  413. if(is_string($mixedMenu)) {
  414. if(($oMenu = BxTemplMenu::getObjectInstance($mixedMenu)) !== false) {
  415. $oMenu->setTemplateById($bUseTabs ? BX_DB_MENU_TEMPLATE_TABS : BX_DB_MENU_TEMPLATE_POPUP);
  416. $sMenu = $oMenu->getCode();
  417. }
  418. else
  419. $sMenu = $mixedMenu;
  420. }
  421. else if(is_array($mixedMenu)) {
  422. if(isset($mixedMenu['template']) && isset($mixedMenu['menu_items']))
  423. $aMenu = $mixedMenu;
  424. else
  425. $aMenu = array('template' => $this->_sDesignBoxMenuTmplDefault, 'menu_items' => $mixedMenu);
  426. if(($oMenu = new BxTemplMenu($aMenu, $this->_oTemplate)) !== false) {
  427. $oMenu->setTemplateById($bUseTabs ? BX_DB_MENU_TEMPLATE_TABS : BX_DB_MENU_TEMPLATE_POPUP);
  428. $sMenu = $oMenu->getCode();
  429. }
  430. else
  431. $sMenu = '';
  432. }
  433. else if(is_object($mixedMenu) && is_a($mixedMenu, 'BxTemplMenu')) {
  434. $mixedMenu->setTemplateById($bUseTabs ? BX_DB_MENU_TEMPLATE_TABS : BX_DB_MENU_TEMPLATE_POPUP);
  435. if(($mixedMenu instanceof BxBaseMenuMoreAuto) && $mixedMenu->isMoreAuto())
  436. $sClass = ' bx-db-menu-tab-more-auto';
  437. $sMenu = $mixedMenu->getCode();
  438. }
  439. }
  440. $bMenu = !empty($sMenu);
  441. $sResult = '';
  442. if($bUseTabs && $bMenu)
  443. $sResult = $sMenu;
  444. else if(is_array($mixedButtons)) {
  445. $sPopup = '';
  446. if($bMenu) {
  447. $aButton = array();
  448. if(empty($mixedButtons))
  449. list($aButton, $sPopup) = $this->_designBoxMenuButton($sMenu);
  450. //--- For backward compatibility
  451. else if(!empty($mixedButtons['menu']) && is_array($mixedButtons['menu'])) {
  452. list($aButton, $sPopup) = $this->_designBoxMenuButton($sMenu, $mixedButtons['menu']);
  453. unset($mixedButtons['menu']);
  454. }
  455. if(!empty($aButton))
  456. $mixedButtons[] = $aButton;
  457. }
  458. foreach($mixedButtons as $aButton) {
  459. if($bMenu && isset($aButton['menu'])) {
  460. if(is_numeric($aButton['menu']) && (int)$aButton['menu'] == 1)
  461. list($aButton, $sPopup) = $this->_designBoxMenuButton($sMenu);
  462. else if(is_array($aButton['menu']))
  463. list($aButton, $sPopup) = $this->_designBoxMenuButton($sMenu, $aButton['menu']);
  464. if(isset($aButton['menu']))
  465. continue;
  466. }
  467. $aAttrs = [];
  468. if(!empty($aButton['href']))
  469. $aAttrs['href'] = $aButton['href'];
  470. if(!empty($aButton['onclick'])) {
  471. $aAttrs['href'] = "javascript:void(0);";
  472. $aAttrs['onclick'] = $aButton['onclick'];
  473. }
  474. $aAttrs['class'] = 'bx-btn bx-btn-small';
  475. if(!empty($aButton['class']))
  476. $aAttrs['class'] .= ' ' . trim($aButton['class']);
  477. // an icon-only button renders no text, so it needs an accessible name of its own
  478. if(empty($aButton['title'])) {
  479. $sButtonLabel = _t(!empty($aButton['area_label']) ? $aButton['area_label'] : '_sys_menu_item_title_system_more');
  480. $aAttrs['aria-label'] = $sButtonLabel;
  481. }
  482. // the popup opened by bx_menu_popup* is role="dialog", bx_menu_popup_inline keeps aria-expanded in sync
  483. if(!empty($aAttrs['onclick']) && strpos($aAttrs['onclick'], 'bx_menu_popup') !== false) {
  484. $aAttrs['aria-haspopup'] = 'dialog';
  485. $aAttrs['aria-expanded'] = 'false';
  486. }
  487. $bTmplVarsButtonIcon = !empty($aButton['icon']);
  488. $aTmplVarsButtonIcon = !$bTmplVarsButtonIcon ? array() : array(
  489. 'icon' => $aButton['icon']
  490. );
  491. $bTmplVarsButtonIconA = !empty($aButton['icon-a']);
  492. $aTmplVarsButtonIconA = !$bTmplVarsButtonIconA ? array() : array(
  493. 'icon_a' => $aButton['icon-a']
  494. );
  495. $bTmplVarsButtonTitle = !empty($aButton['title']);
  496. $aTmplVarsButtonTitle = !$bTmplVarsButtonTitle ? array() : array(
  497. 'title' => $aButton['title']
  498. );
  499. $sResult .= $this->_oTemplate->parseHtmlByName('designbox_menu_button.html', array(
  500. 'attrs' => bx_convert_array2attrs($aAttrs),
  501. 'bx_if:show_icon' => array(
  502. 'condition' => $bTmplVarsButtonIcon,
  503. 'content' => $aTmplVarsButtonIcon
  504. ),
  505. 'bx_if:show_icon_a' => array(
  506. 'condition' => $bTmplVarsButtonIconA,
  507. 'content' => $aTmplVarsButtonIconA
  508. ),
  509. 'bx_if:show_title' => array(
  510. 'condition' => $bTmplVarsButtonTitle,
  511. 'content' => $aTmplVarsButtonTitle
  512. )
  513. ));
  514. }
  515. $sResult .= $sPopup;
  516. }
  517. if(!empty($sResult))
  518. $sResult = $this->_oTemplate->parseHtmlByName('designbox_menu.html', array(
  519. 'class' => $sClass,
  520. 'content' => $sResult
  521. ));
  522. return $sResult;
  523. }
  524. protected function _designBoxMenuId ()
  525. {
  526. return 'bx-menu-db-' . time() . rand(0, PHP_INT_MAX);
  527. }
  528. protected function _designBoxMenuButton ($sMenu, $aParams = array())
  529. {
  530. $sId = $this->_designBoxMenuId();
  531. $aButton = array($this->_sDesignBoxMenuIconType => $this->_sDesignBoxMenuIcon, 'onclick' => $this->_sDesignBoxMenuClick);
  532. if(!empty($aParams)) {
  533. if(!empty($aParams['id']))
  534. $sId = $aParams['id'];
  535. $aButton = array_merge($aButton, $aParams);
  536. }
  537. $aButton['onclick'] = bx_replace_markers($aButton['onclick'], array(
  538. 'design_box_menu' => $sId
  539. ));
  540. $sMenu = $this->_oTemplate->parseHtmlByName('designbox_menu_popup.html', array(
  541. 'content' => $sMenu
  542. ));
  543. return array($aButton, $this->transBox($sId, $sMenu, true));
  544. }
  545. /**
  546. * Get logo URL.
  547. * @return string
  548. */
  549. function getMainLogoUrl()
  550. {
  551. return BxDolDesigns::getInstance()->getSiteLogoUrl();
  552. }
  553. /**
  554. * Get mark URL.
  555. * @return string
  556. */
  557. function getMainMarkUrl()
  558. {
  559. return BxDolDesigns::getInstance()->getSiteMarkUrl();
  560. }
  561. /**
  562. * Get logo HTML.
  563. * @return string
  564. */
  565. function getMainLogo($aParams = array())
  566. {
  567. $oDesigns = BxDolDesigns::getInstance();
  568. $sAlt = $oDesigns->getSiteLogoAlt();
  569. if(empty($sAlt))
  570. $sAlt = getParam('site_title');
  571. $aLogoImages = [];
  572. if(($mixedFileUrl = $this->getMainLogoUrl()) && !empty($mixedFileUrl)) {
  573. if(!is_array($mixedFileUrl))
  574. $mixedFileUrl = ['light' => $mixedFileUrl];
  575. foreach($mixedFileUrl as $sName => $sFileUrl) {
  576. $iLogoWidth = (int)$oDesigns->getSiteLogoWidth();
  577. $sWidth = $iLogoWidth > 0 ? 'width:' . round($iLogoWidth/16, 3) . 'rem;' : '';
  578. $iLogoHeight = (int)$oDesigns->getSiteLogoHeight();
  579. $sHeight = $iLogoHeight > 0 ? 'height:' . round($iLogoHeight/16, 3) . 'rem;' : '';
  580. $aLogoImages[] = [
  581. 'id' => 'bx-logo',
  582. 'src' => $sFileUrl,
  583. 'alt' => bx_html_attribute($sAlt, BX_ESCAPE_STR_QUOTE),
  584. 'style' => $sWidth . ' ' . $sHeight
  585. ];
  586. }
  587. }
  588. $bLogoImages = !empty($aLogoImages) && is_array($aLogoImages);
  589. $aAttrs = array(
  590. 'class' => 'bx-def-font-contrasted',
  591. 'href' => BX_DOL_URL_ROOT,
  592. 'title' => bx_html_attribute($sAlt, BX_ESCAPE_STR_QUOTE)
  593. );
  594. if(!empty($aParams['attrs']) && is_array($aParams['attrs']))
  595. $aAttrs = array_merge($aAttrs, $aParams['attrs']);
  596. $sTmplName = 'logo_main.html';
  597. $aTmplVars = [
  598. 'attrs' => bx_convert_array2attrs($aAttrs),
  599. 'bx_if:show_title' => [
  600. 'condition' => !$bLogoImages,
  601. 'content' => [
  602. 'logo' => $sAlt,
  603. ]
  604. ],
  605. 'bx_if:show_logo' => [
  606. 'condition' => $bLogoImages,
  607. 'content' => [
  608. 'bx_repeat:images' => $aLogoImages,
  609. ]
  610. ],
  611. ];
  612. bx_alert('system', 'get_logo', 0, 0, [
  613. 'tmpl_name' => &$sTmplName,
  614. 'tmpl_vars' => &$aTmplVars,
  615. 'tmpl_name_ref' => &$sTmplName,
  616. 'tmpl_vars_ref' => &$aTmplVars,
  617. ]);
  618. return $this->_oTemplate->parseHtmlByName($sTmplName, $aTmplVars);
  619. }
  620. /**
  621. * Get HTML code for manifests.
  622. * @return HTML string to insert into HEAD section
  623. */
  624. function getManifests()
  625. {
  626. return '<link rel="manifest" href="' . BX_DOL_URL_ROOT . 'manifest.json.php" crossorigin="use-credentials" />';
  627. }
  628. /**
  629. * Get HTML code for meta icons.
  630. * @return HTML string to insert into HEAD section
  631. */
  632. function getMetaIcons()
  633. {
  634. // favicon icon
  635. $sImageUrlFav = '';
  636. if(($iId = (int)getParam('sys_site_icon')) != 0)
  637. $sImageUrlFav = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_FILES)->getFileUrlById($iId);
  638. // svg icon
  639. $sImageUrlSvg = '';
  640. if(($iId = (int)getParam('sys_site_icon_svg')) != 0)
  641. $sImageUrlSvg = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES)->getFileUrlById($iId);
  642. if(empty($sImageUrlFav) && empty($sImageUrlSvg))
  643. $sImageUrlFav = $sImageUrlSvg = $this->_oTemplate->getIconUrl('favicon.svg');
  644. // apple device icon
  645. $sImageUrlApl = '';
  646. if(($iId = (int)getParam('sys_site_icon_apple')) != 0)
  647. $sImageUrlApl = BxDolTranscoderImage::getObjectInstance(BX_DOL_TRANSCODER_OBJ_ICON_APPLE)->getFileUrl($iId);
  648. if(empty($sImageUrlApl))
  649. $sImageUrlApl = $this->_oTemplate->getIconUrl('apple-touch-icon.png');
  650. /*
  651. * TODO:
  652. * 1. Remove commented code later if it won't be used.
  653. * 2. Remove 'sys_icon_favicon' and 'sys_icon_facebook' transcoders and related transcoder filters.
  654. *
  655. // facebook icon
  656. $sImageUrlFcb = '';
  657. $oTranscoder = BxDolTranscoderImage::getObjectInstance(BX_DOL_TRANSCODER_OBJ_ICON_FACEBOOK);
  658. $sImageUrlFcb = $oTranscoder->getFileUrl($iId);
  659. if(empty($sImageUrlFcb))
  660. $sImageUrlFcb = $this->_oTemplate->getIconUrl('facebook-icon.png');
  661. */
  662. $sRet = '';
  663. if($sImageUrlFav)
  664. $sRet .= '<link rel="icon" href="' . $sImageUrlFav . '" sizes="any" />';
  665. if($sImageUrlSvg)
  666. $sRet .= '<link rel="icon" href="' . $sImageUrlSvg . '" type="image/svg+xml" />';
  667. $sRet .= '<link rel="apple-touch-icon" href="' . $sImageUrlApl . '" />';
  668. /*
  669. $sRet .= '<link rel="image_src" sizes="100x100" href="' . $sImageUrlFcb . '" />';
  670. */
  671. return $sRet;
  672. }
  673. function getInjectionHead()
  674. {
  675. return $this->getInjection('getInjHead');
  676. }
  677. function getInjectionHeader()
  678. {
  679. return $this->getInjection('getInjHeader');
  680. }
  681. function getInjectionFooter()
  682. {
  683. return $this->getInjection('getInjFooter');
  684. }
  685. public function getPopupAlert()
  686. {
  687. return $this->transBox('bx-popup-alert', $this->_oTemplate->parseHtmlByName('popup_trans_alert_cnt.html', array()), true);
  688. }
  689. public function getPopupConfirm()
  690. {
  691. return $this->transBox('bx-popup-confirm', $this->_oTemplate->parseHtmlByName('popup_trans_confirm_cnt.html', array()), true);
  692. }
  693. public function getPopupPrompt()
  694. {
  695. $sInputText = 'bx-popup-prompt-value';
  696. $aInputText = array(
  697. 'type' => 'text',
  698. 'name' => $sInputText,
  699. 'attrs' => array(
  700. 'id' => $sInputText,
  701. 'aria-labelledby' => 'bx-popup-prompt-text',
  702. ),
  703. 'value' => '',
  704. 'caption' => ''
  705. );
  706. $oForm = new BxTemplFormView(array(), $this->_oTemplate);
  707. return $this->transBox('bx-popup-prompt', $this->_oTemplate->parseHtmlByName('popup_trans_prompt_cnt.html', array(
  708. 'input' => $oForm->genRow($aInputText)
  709. )), true);
  710. }
  711. public function getPopupToast()
  712. {
  713. $sHtmlId = 'bx-popup-toast';
  714. return $this->_oTemplate->parseHtmlByName('popup_trans_toast_holder.html', [
  715. 'html_id' => $sHtmlId . '-holder',
  716. 'sample' => $this->transBox($sHtmlId, $this->_oTemplate->parseHtmlByName('popup_trans_toast_cnt.html', []), true)
  717. ]);
  718. }
  719. /**
  720. * Output time wrapped in <time> tag in HTML.
  721. * Then time is autoformatted using JS upon page load, this is aumatically converted to user's timezone and
  722. * updated in realtime in case of short periods of 'from now' time format.
  723. *
  724. * Short version of this function:
  725. * @see bx_time_js
  726. *
  727. * @param $iUnixTimestamp time as unixtimestamp
  728. * @param $sFormatIdentifier output format identifier
  729. * @see BX_FORMAT_DATE
  730. * @see BX_FORMAT_TIME
  731. * @see BX_FORMAT_DATE_TIME
  732. * @param $bForceFormat force provided format and don't use "from now" time autoformat.
  733. */
  734. function timeForJs ($iUnixTimestamp, $sFormatIdentifier = BX_FORMAT_DATE, $bForceFormat = false)
  735. {
  736. $sDateUTC = bx_time_utc ($iUnixTimestamp);
  737. return $this->timeForJsFullDate ($sDateUTC, $sFormatIdentifier, $bForceFormat);
  738. }
  739. /**
  740. * Same as @see timeForJs but instead of unxtimestamp full date format is used (ex: 2005-08-15T15:52:01) as passec date param
  741. */
  742. function timeForJsFullDate ($sDateUTC, $sFormatIdentifier = BX_FORMAT_DATE, $bForceFormat = false, $bUTC = false)
  743. {
  744. return '<time datetime="' . $sDateUTC . '" data-bx-format="' . getParam($sFormatIdentifier) . '" data-bx-autoformat="' . ($bForceFormat ? 0 : getParam('sys_format_timeago')) . '" data-bx-utc="' . ($bUTC ? 1 : 0) . '">' . $sDateUTC . '</time>';
  745. }
  746. function statusOnOff ($mixed, $isMsg = false)
  747. {
  748. if ((is_bool($mixed) && !$mixed) || (is_string($mixed) && 'fail' == $mixed))
  749. return '<i class="sys-icon circle col-red2"></i> ' . ($isMsg ? _t('_sys_off') : '');
  750. elseif (is_string($mixed) && 'warn' == $mixed)
  751. return '<i class="sys-icon circle col-red3"></i> ' . ($isMsg ? _t('_sys_warn') : '');
  752. elseif (is_string($mixed) && 'undef' == $mixed)
  753. return '<i class="sys-icon circle col-gray"></i> ' . ($isMsg ? _t('_undefined') : '');
  754. else
  755. return '<i class="sys-icon circle col-green1"></i> ' . ($isMsg ? _t('_sys_on') : '');
  756. }
  757. /**
  758. * Ouputs HTML5 video player.
  759. * @param $sUrlPoster video poster image
  760. * @param $sUrlMP4 .mp4 video
  761. * @param $sUrlMP4Hd .mp4 video in better quality
  762. * @param $aAttrs custom attributes, defaults are: controls="" preload="none" autobuffer=""
  763. * @param $sStyles custom styles, defaults are: width:100%; height:auto;
  764. */
  765. function videoPlayer ($sUrlPoster, $sUrlMP4, $sUrlMP4Hd = '', $aAttrs = false, $sStyles = false, $bDynamicMode = false)
  766. {
  767. $oPlayer = BxDolPlayer::getObjectInstance();
  768. if(!$oPlayer)
  769. return '';
  770. if($sStyles === false)
  771. $sStyles = 'width:100%; height:auto;';
  772. return $oPlayer->getCodeVideo (BX_PLAYER_STANDARD, array(
  773. 'poster' => $sUrlPoster,
  774. 'mp4' => array('sd' => $sUrlMP4, 'hd' => $sUrlMP4Hd),
  775. 'attrs' => $aAttrs,
  776. 'styles' => $sStyles,
  777. ), $bDynamicMode);
  778. }
  779. protected function getInjection($sPrefix)
  780. {
  781. $sContent = '';
  782. $aMethods = get_class_methods($this);
  783. foreach($aMethods as $sMethod)
  784. if(preg_match("/^(" . $sPrefix . ")[A-Z].+$/", $sMethod))
  785. $sContent .= $this->$sMethod();
  786. return $sContent;
  787. }
  788. /**
  789. * Interface sounds: Cuelume's cues (plugins_public/cuelume, MIT) with the settings of BxDolSounds. The module is
  790. * loaded and bound only where sounds are on for this platform and the visitor hasn't turned them off.
  791. * See bx_sound_init in inc/js/functions.js.
  792. */
  793. protected function getInjHeadSounds()
  794. {
  795. $oSounds = BxDolSounds::getInstance();
  796. $bOn = $oSounds->isOn($this->getSoundsPlatform());
  797. if(!$bOn && !$this->isSoundsPreview())
  798. return '';
  799. $aConfig = $oSounds->getConfig();
  800. unset($aConfig['platforms']);
  801. $aLabels = [];
  802. foreach(BxDolSounds::LEVELS as $sLevel)
  803. $aLabels[$sLevel] = _t('_sys_txt_sounds_level_' . $sLevel);
  804. return $this->_oTemplate->parseHtmlByName('sounds_init.html', [
  805. 'url' => bx_js_string(BX_DOL_URL_PLUGINS_PUBLIC . 'cuelume/cuelume.min.js', BX_ESCAPE_STR_APOS),
  806. 'config' => json_encode(array_merge($aConfig, ['on' => $bOn, 'labels' => $aLabels]), JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT)
  807. ]);
  808. }
  809. protected function getSoundsPlatform()
  810. {
  811. return 'site';
  812. }
  813. /**
  814. * Whether pages get the sounds setup while sounds are off here, to preview them (Studio's Settings > Sounds).
  815. */
  816. protected function isSoundsPreview()
  817. {
  818. return false;
  819. }
  820. protected function getInjHeadLiveUpdates()
  821. {
  822. $sContent = '';
  823. if(($oLiveUpdates = BxDolLiveUpdates::getInstance()) !== false)
  824. $sContent .= $oLiveUpdates->init();
  825. return $sContent;
  826. }
  827. protected function getInjHeaderPushNotifications()
  828. {
  829. $sResult = '';
  830. if(($oPush = BxDolPush::getObjectInstance()) !== false)
  831. $sResult = $oPush->getCode('page_header');
  832. return $sResult;
  833. }
  834. protected function getInjHeaderPopupLoading()
  835. {
  836. return $this->transBox('bx-popup-loading', $this->_oTemplate->parsePageByName('popup_loading.html', array()), true);
  837. }
  838. protected function getInjFooterMenuLoading()
  839. {
  840. return $this->_oTemplate->parsePageByName('menu_loading.html', array());
  841. }
  842. protected function getInjFooterPopupMenus()
  843. {
  844. $sContent = '';
  845. $oSearch = new BxTemplSearch();
  846. $oSearch->setLiveSearch(true);
  847. $sContent .= $this->_oTemplate->parsePageByName('search.html', array(
  848. 'search_form' => $oSearch->getForm(BX_DB_CONTENT_ONLY),
  849. 'results' => $oSearch->getResultsContainer(),
  850. ));
  851. $sContent .= $this->_oTemplate->getMenu ('sys_site');
  852. if(isLogged()) {
  853. $sContent .= $this->_oTemplate->getMenu ('sys_add_content');
  854. $sContent .= $this->_oTemplate->getMenu ('sys_account_popup');
  855. }
  856. return $sContent;
  857. }
  858. protected function getInjFooterPopupApps()
  859. {
  860. if($this->_oTemplate->getPageType() != BX_PAGE_TYPE_APPLICATION)
  861. return '';
  862. $oMenu = BxDolMenu::getObjectInstance('sys_homepage');
  863. if(!$oMenu)
  864. return '';
  865. $this->_oTemplate->addJs(['popper.js']);
  866. return $this->_oTemplate->parsePageByName('menu_apps.html', [
  867. 'name' => 'sys-menu-apps',
  868. 'title' => _t('_apps'),
  869. 'bx_repeat:menu_items' => $oMenu->getMenuItems(),
  870. ]);
  871. }
  872. protected function getInjFooterPopups()
  873. {
  874. $sContent = '';
  875. $sContent .= $this->getPopupAlert();
  876. $sContent .= $this->getPopupConfirm();
  877. $sContent .= $this->getPopupPrompt();
  878. $sContent .= $this->getPopupToast();
  879. return $sContent;
  880. }
  881. protected function getInjFooterEmbed()
  882. {
  883. // Load embed files
  884. $oEmbed = BxDolEmbed::getObjectInstance(false);
  885. if ($oEmbed)
  886. return $oEmbed->addJsCss() . $oEmbed->addProcessLinkMethod();
  887. return '';
  888. }
  889. }
  890. /** @} */