");
// Hack for the date and time picker addon issue on Chrome (see #1305)
// The workaround is to instantiate the widget on demand
// It relies on the same markup, thus reverting to the original implementation should be straightforward
$(".datetime-pick:not(.is-widget-ready)").each(function(){
var oInput = this;
$(oInput).addClass('is-widget-ready');
$('
HTML;
}
// Note: Message icon has been ignored during 3.0 migration. If we want them back, we should find a proper way to integrate them, not just putting an
tag
$oAppMessageAlert = AlertUIBlockFactory::MakeForInformation('', $sMessageForHtml);
$oHeader->AddSubBlock($oAppMessageAlert);
}
// Call the extensions to add content to the page, warning they can also add styles or scripts through as they have access to the \iTopWebPage
/** @var \iPageUIBlockExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iPageUIBlockExtension') as $oExtensionInstance)
{
$oBlock = $oExtensionInstance->GetHeaderBlock();
if ($oBlock) {
$oHeader->AddSubBlock($oBlock);
}
}
return $oHeader;
}
/**
* Render the footer HTML which can come from both iTop itself and from extensions
*
* @see \iPageUIExtension::GetSouthPaneHtml()
* @internal
*
* @return string
* @since 3.0.0
*/
protected function RenderFooterHtml()
{
$sFooterHtml = '';
// Call the extensions to add content to the page, warning they can also add styles or scripts through as they have access to the \iTopWebPage
/** @var \iPageUIExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iPageUIExtension') as $oExtensionInstance) {
$sFooterHtml .= $oExtensionInstance->GetSouthPaneHtml($this);
}
return $sFooterHtml;
}
/**
* Render the footer UIBlock which can come from both iTop itself and from extensions
*
* @see \iPageUIExtension::GetSouthPaneHtml()
* @internal
*
* @return iUIBlock
* @since 3.0.0
*/
protected function RenderFooterBlock()
{
$oFooter = new UIContentBlock();
// Call the extensions to add content to the page, warning they can also add styles or scripts through as they have access to the \iTopWebPage
/** @var \iPageUIBlockExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iPageUIBlockExtension') as $oExtensionInstance) {
$oBlock = $oExtensionInstance->GetFooterBlock();
if ($oBlock) {
$oFooter->AddSubBlock($oBlock);
}
}
return $oFooter;
}
/**
* @inheritDoc
* @throws \Exception
*/
public function output()
{
// Data to be passed to the view
$aData = [];
$s_captured_output = $this->ob_get_clean_safe();
// Prepare page metadata
$sAbsoluteUrlAppRoot = addslashes($this->m_sRootUrl);
$sFaviconUrl = $this->GetFaviconAbsoluteUrl();
$sMetadataLanguage = $this->GetLanguageForMetadata();
$oPrintHeader = null;
// Prepare internal parts (js files, css files, js snippets, css snippets, ...)
// - Generate necessary dict. files
$this->output_dict_entries();
// TODO 3.0.0 not displayed ?
$this->GetContentLayout()->SetExtraHtmlContent(utils::FilterXSS($this->s_content));
// TODO 3.0.0 : to be removed
$this->outputCollapsibleSectionInit();
// Base structure of data to pass to the TWIG template
$aData['aPage'] = [
'sAbsoluteUrlAppRoot' => $sAbsoluteUrlAppRoot,
'sTitle' => $this->s_title,
'sFaviconUrl' => $sFaviconUrl,
'aMetadata' => [
'sCharset' => static::PAGES_CHARSET,
'sLang' => $sMetadataLanguage,
],
'oPrintHeader' => $oPrintHeader,
'isPrintable' => $this->IsPrintableVersion(),
];
// Base tag
// Note: We might consider to put the app_root_url parameter here, but that would need a BIG rework on iTop AND the extensions to replace all the "../images|js|css/xxx.yyy"...
if (!empty($this->a_base['href'])) {
$aData['aPage']['aMetadata']['sBaseUrl'] = $this->a_base['href'];
}
if ($this->a_base['target'] != '') {
$aData['aPage']['aMetadata']['sBaseTarget'] = $this->a_base['target'];
}
// Layouts
$aData['aLayouts'] = [
'sBanner' => $this->RenderBannerHtml(),
'oBanner' => $this->RenderBannerBlock(),
'sHeader' => $this->RenderHeaderHtml(),
'oHeader' => $this->RenderHeaderBlock(),
'sFooter' => $this->RenderFooterHtml(),
'oFooter' => $this->RenderFooterBlock(),
];
// - Prepare navigation menu
$aData['aLayouts']['oNavigationMenu'] = $this->GetNavigationMenuLayout();
$aData['aDeferredBlocks']['oNavigationMenu'] = $this->GetDeferredBlocks($this->GetNavigationMenuLayout());
// - Prepare top bar
$aData['aLayouts']['oTopBar'] = $this->GetTopBarLayout();
$aData['aDeferredBlocks']['oTopBar'] = $this->GetDeferredBlocks($this->GetTopBarLayout());
// - Prepare content
$aData['aLayouts']['oPageContent'] = $this->GetContentLayout();
$aData['aDeferredBlocks']['oPageContent'] = $this->GetDeferredBlocks($this->GetContentLayout());
// - Retrieve layouts linked files
// Note: Adding them now instead of in the template allow us to remove duplicates and lower the browser parsing time
/** @var \Combodo\iTop\Application\UI\Base\UIBlock|string $oLayout */
foreach ($aData['aLayouts'] as $oLayout) {
if (!$oLayout instanceof UIBlock) {
continue;
}
ConsoleBlockRenderer::AddCssJsToPage($this, $oLayout, $aData);
}
// Components
// Note: For now all components are either included in the layouts above or put in page through the AddUiBlock() API, so there is no need to do anything more.
$this->InitializeKeyboardShortcuts();
// Variable content of the page
$aData['aPage'] = array_merge(
$aData['aPage'],
[
'aCssFiles' => $this->a_linked_stylesheets,
'aCssInline' => $this->a_styles,
'aJsFiles' => $this->a_linked_scripts,
'aJsInlineOnInit' => $this->a_init_scripts,
'aJsInlineOnDomReady' => $this->GetReadyScripts(),
'aJsInlineLive' => $this->a_scripts,
// TODO 3.0.0: TEMP, used while developping, remove it.
'sSanitizedContent' => utils::FilterXSS($this->s_content),
'sDeferredContent' => utils::FilterXSS($this->s_deferred_content),
'sCapturedOutput' => utils::FilterXSS($s_captured_output),
]
);
$oTwigEnv = TwigHelper::GetTwigEnvironment(BlockRenderer::TWIG_BASE_PATH, BlockRenderer::TWIG_ADDITIONAL_PATHS);
// Send headers
if ($this->GetOutputFormat() === 'html') {
foreach ($this->a_headers as $sHeader) {
header($sHeader);
}
}
// Render final TWIG into global HTML
$oKpi = new ExecutionKPI();
$sHtml = TwigHelper::RenderTemplate($oTwigEnv, $aData, $this->GetTemplateRelPath());
$oKpi->ComputeAndReport('TWIG rendering');
// Echo global HTML
$oKpi = new ExecutionKPI();
echo $sHtml;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($sHtml) / 1024).' Kb)');
DBSearch::RecordQueryTrace();
ExecutionKPI::ReportStats();
return;
/////////////////////////////////////////////////////////
////////////////// ☢ DANGER ZONE ☢ /////////////////////
/////////////////////////////////////////////////////////
// Render the tabs in the page (if any)
// $this->s_content = $this->m_oTabs->RenderIntoContent($this->s_content, $this);
// Put here the 'ready scripts' that must be executed after all others
$aMultiselectOptions = array(
'header' => true,
'checkAllText' => Dict::S('UI:SearchValue:CheckAll'),
'uncheckAllText' => Dict::S('UI:SearchValue:UncheckAll'),
'noneSelectedText' => Dict::S('UI:SearchValue:Any'),
'selectedText' => Dict::S('UI:SearchValue:NbSelected'),
'selectedList' => 1,
);
$sJSMultiselectOptions = json_encode($aMultiselectOptions);
$this->add_ready_script(
<<outputCollapsibleSectionInit();
// TODO 3.0.0: Is this for the "Debug" popup? We should do a helper to display a popup in various cases (welcome message for example)
$s_captured_output = $this->ob_get_clean_safe();
}
/**
* @inheritDoc
* @throws \Exception
*/
public function AddTabContainer($sTabContainer, $sPrefix = '', iUIContentBlock $oParentBlock = null)
{
if(is_null($oParentBlock)) {
$oParentBlock = PanelUIBlockFactory::MakeNeutral('');
$this->AddUiBlock($oParentBlock);
}
$oParentBlock->AddSubBlock($this->m_oTabs->AddTabContainer($sTabContainer, $sPrefix));
}
/**
* @inheritDoc
* @throws \Exception
*/
public function AddToTab($sTabContainer, $sTabCode, $sHtml)
{
$this->add($this->m_oTabs->AddToTab($sTabContainer, $sTabCode, $sHtml));
}
/**
* @inheritDoc
*/
public function SetCurrentTabContainer($sTabContainer = '')
{
return $this->m_oTabs->SetCurrentTabContainer($sTabContainer);
}
/**
* @inheritDoc
*/
public function SetCurrentTab($sTabCode = '', $sTabTitle = null)
{
return $this->m_oTabs->SetCurrentTab($sTabCode, $sTabTitle);
}
/**
* @inheritDoc
* @throws \Exception
* @since 2.0.3
*/
public function AddAjaxTab($sTabCode, $sUrl, $bCache = true, $sTabTitle = null, $sPlaceholder = null)
{
$this->add($this->m_oTabs->AddAjaxTab($sTabCode, $sUrl, $bCache, $sTabTitle, $sPlaceholder));
}
/**
* @inheritDoc
*/
public function GetCurrentTab()
{
return $this->m_oTabs->GetCurrentTab();
}
/**
* @inheritDoc
*/
public function RemoveTab($sTabCode, $sTabContainer = null)
{
$this->m_oTabs->RemoveTab($sTabCode, $sTabContainer);
}
/**
* @inheritDoc
*/
public function FindTab($sPattern, $sTabContainer = null)
{
return $this->m_oTabs->FindTab($sPattern, $sTabContainer);
}
/**
* Make the given tab the active one, as if it were clicked
* DOES NOT WORK: apparently in the *old* version of jquery
* that we are using this is not supported... TO DO upgrade
* the whole jquery bundle...
*
* @param string $sTabContainer
* @param string $sTabCode
*
* @deprecated 3.0.0
*/
public function SelectTab($sTabContainer, $sTabCode)
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
$this->add_ready_script($this->m_oTabs->SelectTab($sTabContainer, $sTabCode));
}
/**
* @inheritDoc
* @throws \Exception
*/
public function add($sHtml)
{
if (($this->m_oTabs->GetCurrentTabContainer() != '') && ($this->m_oTabs->GetCurrentTab() != '')) {
$this->m_oTabs->AddToCurrentTab($sHtml);
} else {
parent::add($sHtml);
}
}
public function AddUiBlock(?iUIBlock $oBlock): ?iUIBlock
{
if (is_null($oBlock)) {
return null;
}
if (($this->m_oTabs->GetCurrentTabContainer() != '') && ($this->m_oTabs->GetCurrentTab() != '')) {
return $this->m_oTabs->AddUIBlockToCurrentTab($oBlock);
}
return parent::AddUiBlock($oBlock);
}
/**
* @inheritDoc
*/
public function start_capture()
{
$sCurrentTabContainer = $this->m_oTabs->GetCurrentTabContainer();
$sCurrentTab = $this->m_oTabs->GetCurrentTab();
if (!empty($sCurrentTabContainer) && !empty($sCurrentTab))
{
$iOffset = $this->m_oTabs->GetCurrentTabLength();
return array('tc' => $sCurrentTabContainer, 'tab' => $sCurrentTab, 'offset' => $iOffset);
}
else
{
return parent::start_capture();
}
}
/**
* @inheritDoc
*/
public function end_capture($offset)
{
if (is_array($offset))
{
if ($this->m_oTabs->TabExists($offset['tc'], $offset['tab']))
{
$sCaptured = $this->m_oTabs->TruncateTab($offset['tc'], $offset['tab'], $offset['offset']);
}
else
{
$sCaptured = '';
}
}
else
{
$sCaptured = parent::end_capture($offset);
}
return $sCaptured;
}
/**
* Set the message to be displayed in the 'app-banner' section at the top of the page
*
* @param string $sHtmlMessage
*/
public function SetMessage($sHtmlMessage)
{
$sHtmlIcon = '';
$this->AddApplicationMessage($sHtmlMessage, $sHtmlIcon);
}
/**
* Add message to be displayed in the 'app-banner' section at the top of the page
*
* @param string $sHtmlMessage
* @param string|null $sHtmlIcon
* @param string|null $sTip
*/
public function AddApplicationMessage($sHtmlMessage, $sHtmlIcon = null, $sTip = null)
{
if (strlen($sHtmlMessage))
{
$this->m_aMessages[] = array(
'icon' => $sHtmlIcon,
'message' => $sHtmlMessage,
'tip' => $sTip,
);
}
}
/**
* Adds in the page a container with the header_message CSS class
*
* @param string $sContent
* @param string $sCssClasses CSS classes to add to the container
*
* @since 2.6.0
*/
public function AddHeaderMessage(string $sContent, string $sCssClasses = 'message_info')
{
switch ($sCssClasses) {
case 'message_ok':
$oAlert = AlertUIBlockFactory::MakeForSuccess('', $sContent);
break;
case 'message_warning':
$oAlert = AlertUIBlockFactory::MakeForWarning('', $sContent);
break;
case 'message_error':
$oAlert = AlertUIBlockFactory::MakeForDanger('', $sContent);
break;
case 'message_info':
default:
$oAlert = AlertUIBlockFactory::MakeForInformation('', $sContent);
break;
}
$oAlert->AddCSSClass($sCssClasses);
$this->AddUiBlock($oAlert);
}
/**
* @return TopBar
*/
public function GetTopBarLayout(): TopBar
{
return $this->oTopBarLayout;
}
/**
* @param TopBar $oTopBarLayout
*
* @return iTopWebPage
*/
public function SetTopBarLayout(TopBar $oTopBarLayout): iTopWebPage
{
$this->oTopBarLayout = $oTopBarLayout;
return $this;
}
/**
*
* @return BlockPrintHeader
*/
protected function OutputPrintable(): BlockPrintHeader
{
$oBlock = new BlockPrintHeader();
return $oBlock;
}
}