Merge branch 'develop' into feature/faf_event_service

This commit is contained in:
Eric Espie
2022-03-15 10:52:45 +01:00
932 changed files with 14689 additions and 8581 deletions

View File

@@ -0,0 +1,340 @@
<?php
/*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Application\TwigBase\Twig\TwigHelper;
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
use Combodo\iTop\Application\UI\Base\iUIBlock;
use Combodo\iTop\Application\UI\Base\Layout\iUIContentBlock;
use Combodo\iTop\Renderer\BlockRenderer;
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
use Combodo\iTop\Service\EventName;
use Combodo\iTop\Service\EventService;
class AjaxPage extends WebPage implements iTabbedPage
{
/**
* Jquery style ready script
*
* @var array
*/
protected $m_oTabs;
private $m_sMenu; // If set, then the menu will be updated
const DEFAULT_PAGE_TEMPLATE_REL_PATH = 'pages/backoffice/ajaxpage/layout';
/** @var string */
private $sPromiseId;
/**
* @var bool if false will also output extra JS & CSS
* @since 3.0.1 3.1.0 N°4836 Introduce this new option to AjaxPage, as sometimes we only need to return a simple string
*/
protected $bOutputDataOnly = false;
/**
* constructor for the web page
*
* @param string $s_title Not used
* @param bool $bOutputExtraResources if true will output also JS & CSS resources
*/
function __construct($s_title)
{
$oKpi = new ExecutionKPI();
$sPrintable = utils::ReadParam('printable', '0');
$bPrintable = ($sPrintable == '1');
parent::__construct($s_title, $bPrintable);
//$this->add_header("Content-type: text/html; charset=utf-8");
$this->no_cache();
$this->add_xframe_options();
$this->m_oTabs = new TabManager();
$this->sContentType = 'text/html';
$this->sContentDisposition = 'inline';
$this->m_sMenu = "";
$this->sPromiseId = utils::ReadParam('ajax_promise_id', uniqid('ajax_', true));
utils::InitArchiveMode();
$oKpi->ComputeStats(get_class($this).' creation', 'AjaxPage');
}
/**
* @see static::$bOutputDataOnly
* @param bool $bFlag
*
* @return $this
*
* @since 3.0.1 3.1.0 N°4836 Method creation : sometimes we only want to output a simple string
*/
public function SetOutputDataOnly(bool $bFlag)
{
$this->bOutputDataOnly = $bFlag;
return $this;
}
/**
* @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
*/
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...
*/
public function SelectTab($sTabContainer, $sTabCode)
{
$this->add_ready_script($this->m_oTabs->SelectTab($sTabContainer, $sTabCode));
}
/**
* @param string $sHtml
*
* @deprecated Will be removed in 3.0.0
*/
public function AddToMenu($sHtml)
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
$this->m_sMenu .= $sHtml;
}
/**
* @inheritDoc
*/
public function output()
{
$oKpi = new ExecutionKPI();
$s_captured_output = $this->ob_get_clean_safe();
if (!empty($this->sContentType)) {
$this->add_header('Content-type: '.$this->sContentType);
}
if (!empty($this->sContentDisposition)) {
$this->add_header('Content-Disposition: '.$this->sContentDisposition.'; filename="'.$this->sContentFileName.'"');
}
foreach ($this->a_headers as $s_header) {
header($s_header);
}
if (false === $this->bOutputDataOnly) {
// Prepare internal parts (js files, css files, js snippets, css snippets, ...)
// - Generate necessary dict. files
if ($this->bAddJSDict) {
$this->output_dict_entries();
}
ConsoleBlockRenderer::AddCssJsToPage($this, $this->oContentLayout);
$this->outputCollapsibleSectionInit();
}
// Render the blocks
$aData = [];
$aData['oLayout'] = $this->oContentLayout;
$aData['aDeferredBlocks'] = $this->GetDeferredBlocks($this->oContentLayout);
$aData['aPage'] = [
'sAbsoluteUrlAppRoot' => addslashes(utils::GetAbsoluteUrlAppRoot()),
'sTitle' => $this->s_title,
'aMetadata' => [
'sCharset' => static::PAGES_CHARSET,
'sLang' => $this->GetLanguageForMetadata(),
],
'aCssFiles' => $this->a_linked_stylesheets,
'aCssInline' => $this->a_styles,
'aJsFiles' => $this->a_linked_scripts,
'aJsInlineLive' => $this->a_scripts,
'aJsInlineOnDomReady' => $this->GetReadyScripts(),
'aJsInlineOnInit' => $this->a_init_scripts,
'bEscapeContent' => ($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'),
// TODO 3.0.0: TEMP, used while developping, remove it.
'sSanitizedContent' => utils::FilterXSS($this->s_content),
'sDeferredContent' => utils::FilterXSS(addslashes(str_replace("\n", '', $this->s_deferred_content))),
'sCapturedOutput' => utils::FilterXSS($s_captured_output),
'sPromiseId' => $this->sPromiseId,
];
$aData['aBlockParams'] = $this->GetBlockParams();
$oTwigEnv = TwigHelper::GetTwigEnvironment(BlockRenderer::TWIG_BASE_PATH, BlockRenderer::TWIG_ADDITIONAL_PATHS);
// Render final TWIG into global HTML
$sHtml = TwigHelper::RenderTemplate($oTwigEnv, $aData, $this->GetTemplateRelPath());
$oKpi->ComputeAndReport(get_class($this).' output');
// Echo global HTML
echo $sHtml;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($sHtml) / 1024).' Kb)');
$this->FireAfterDisplayEvent();
ExecutionKPI::ReportStats();
}
/**
* Adds a paragraph with a smaller font into the page
* NOT implemented (i.e does nothing)
*
* @param string $sText Content of the (small) paragraph
*
* @return void
*/
public function small_p($sText)
{
}
/**
* @inheritDoc
* @throws \Exception
*/
public function add($sHtml)
{
if (($this->m_oTabs->GetCurrentTabContainer() != '') && ($this->m_oTabs->GetCurrentTab() != '')) {
$this->m_oTabs->AddToTab($this->m_oTabs->GetCurrentTabContainer(), $this->m_oTabs->GetCurrentTab(), $sHtml);
} else {
parent::add($sHtml);
}
}
/**
* @inheritDoc
*/
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;
}
/**
* @inheritDoc
*/
public function add_at_the_end($s_html, $sId = null)
{
if ($sId != '') {
$this->add_script("$('#{$sId}').remove();"); // Remove any previous instance of the same Id
}
$this->s_deferred_content .= $s_html;
}
/**
* @inheritDoc
*/
public function GetUniqueId()
{
assert(false);
}
/**
* @inheritDoc
*/
public static function FilterXSS($sHTML)
{
return str_ireplace(array('<script', '</script>'), array('<!-- <removed-script', '</removed-script> -->'), $sHTML);
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class CLILikeWebPage extends WebPage
{
public function add_comment($sText)
{
$this->add('#'.$sText."<br/>\n");
}
}

View File

@@ -0,0 +1,105 @@
<?php
// Copyright (C) 2010-2021 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Service\EventName;
use Combodo\iTop\Service\EventService;
/**
* CLI page
* The page adds the content-type text/XML and the encoding into the headers
*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class CLIPage implements Page
{
/** @var string */
public $s_title;
function __construct($s_title)
{
$this->s_title = $s_title;
}
public function output()
{
$this->FireAfterDisplayEvent();
if (class_exists('DBSearch')) {
DBSearch::RecordQueryTrace();
}
if (class_exists('ExecutionKPI')) {
ExecutionKPI::ReportStats();
}
}
protected function FireAfterDisplayEvent()
{
$aData['debug_info'] = 'from: '.get_class($this).":[$this->s_title]";
$aData['object'] = $this;
EventService::FireEvent(EventName::AFTER_DISPLAY_PAGE, get_class($this), $aData);
}
public function add($sText)
{
echo $sText;
}
public function p($sText)
{
echo $sText."\n";
}
public function pre($sText)
{
echo $sText."\n";
}
public function add_comment($sText)
{
echo "#".$sText."\n";
}
public function table($aConfig, $aData, $aParams = array())
{
$aCells = array();
foreach($aConfig as $sName=>$aDef)
{
if (strlen($aDef['description']) > 0)
{
$aCells[] = $aDef['label'].' ('.$aDef['description'].')';
}
else
{
$aCells[] = $aDef['label'];
}
}
echo implode(';', $aCells)."\n";
foreach($aData as $aRow)
{
$aCells = array();
foreach($aConfig as $sName=>$aAttribs)
{
$sValue = $aRow["$sName"];
$aCells[] = $sValue;
}
echo implode(';', $aCells)."\n";
}
}
}

View File

@@ -0,0 +1,112 @@
<?php
// Copyright (C) 2010-2021 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Service\EventName;
use Combodo\iTop\Service\EventService;
/**
* Simple web page with no includes or fancy formatting, useful to generateXML documents
* The page adds the content-type text/XML and the encoding into the headers
*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class CSVPage extends WebPage
{
function __construct($s_title)
{
$oKpi = new ExecutionKPI();
parent::__construct($s_title);
$this->add_header("Content-type: text/plain; charset=".self::PAGES_CHARSET);
$this->no_cache();
$this->add_xframe_options();
//$this->add_header("Content-Transfer-Encoding: binary");
$oKpi->ComputeStats(get_class($this).' creation', 'CSVPage');
}
public function output()
{
$this->add_header("Content-Length: ".strlen(trim($this->s_content)));
// Get the unexpected output but do nothing with it
$sTrash = $this->ob_get_clean_safe();
$oKpi = new ExecutionKPI();
foreach ($this->a_headers as $s_header) {
header($s_header);
}
echo trim($this->s_content);
echo "\n";
$oKpi->ComputeAndReport('Echoing ('.round(strlen($this->s_content) / 1024).' Kb)');
$this->FireAfterDisplayEvent();
if (class_exists('DBSearch')) {
DBSearch::RecordQueryTrace();
}
ExecutionKPI::ReportStats();
}
public function small_p($sText)
{
}
public function add($sText)
{
$this->s_content .= $sText;
}
public function p($sText)
{
$this->s_content .= $sText."\n";
}
public function add_comment($sText)
{
$this->s_content .= "#".$sText."\n";
}
public function table($aConfig, $aData, $aParams = array())
{
$aCells = array();
foreach($aConfig as $sName=>$aDef)
{
if (strlen($aDef['description']) > 0)
{
$aCells[] = $aDef['label'].' ('.$aDef['description'].')';
}
else
{
$aCells[] = $aDef['label'];
}
}
$this->s_content .= implode(';', $aCells)."\n";
foreach($aData as $aRow)
{
$aCells = array();
foreach($aConfig as $sName=>$aAttribs)
{
$sValue = $aRow["$sName"];
$aCells[] = $sValue;
}
$this->s_content .= implode(';', $aCells)."\n";
}
}
}

View File

@@ -0,0 +1,91 @@
<?php
// Copyright (C) 2021 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Application\UI\Base\iUIBlock;
use Combodo\iTop\Renderer\BlockRenderer;
/**
* Adapter class: when an API requires WebPage and you want to produce something else
*
* @copyright Copyright (C) 2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class CaptureWebPage extends WebPage
{
function __construct()
{
$oKpi = new ExecutionKPI();
parent::__construct('capture web page');
$oKpi->ComputeStats(get_class($this).' creation', 'CaptureWebPage');
}
public function GetHtml()
{
$trash = $this->ob_get_clean_safe();
$oBlockRenderer = new BlockRenderer($this->oContentLayout);
return $oBlockRenderer->RenderHtml();
}
public function GetJS()
{
$sRet = implode("\n", $this->a_scripts);
if (!empty($this->s_deferred_content))
{
$sRet .= "\n\$('body').append('".addslashes(str_replace("\n", '', $this->s_deferred_content))."');";
}
$oBlockRenderer = new BlockRenderer($this->oContentLayout);
$sRet .= $oBlockRenderer->RenderJsInline(iUIBlock::ENUM_JS_TYPE_LIVE);
$sRet .= $oBlockRenderer->RenderJsInline(iUIBlock::ENUM_JS_TYPE_ON_INIT);
return $sRet;
}
public function GetReadyJS()
{
$sRet = "\$(document).ready(function() {\n".implode("\n", $this->a_init_scripts).implode("\n", $this->a_ready_scripts)."\n});";
$oBlockRenderer = new BlockRenderer($this->oContentLayout);
$sRet .= $oBlockRenderer->RenderJsInline(iUIBlock::ENUM_JS_TYPE_ON_READY);
return $sRet;
}
public function GetCSS()
{
return $this->a_styles;
}
public function GetJSFiles()
{
return $this->a_linked_scripts;
}
public function GetCSSFiles()
{
return $this->a_linked_stylesheets;
}
public function output()
{
throw new Exception(__method__.' should not be called');
}
}

View File

@@ -0,0 +1,61 @@
<?php
/*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Service\EventName;
use Combodo\iTop\Service\EventService;
/**
* Class DownloadPage
*
* Use it to download a file raw content (no extra / meta data from iTop)
*
* @api
* @author Eric Espie <eric.espie@combodo.com>
* @since 3.0.0
*/
class DownloadPage extends AjaxPage
{
/** @var string */
protected $sContent;
/**
* @inheritDoc
*/
public function add($sContent)
{
$this->sContent .= $sContent;
}
/**
* @inheritDoc
*/
public function output()
{
$oKpi = new ExecutionKPI();
if (!empty($this->sContentType)) {
$this->add_header('Content-type: '.$this->sContentType);
}
if (!empty($this->sContentDisposition)) {
$this->add_header('Content-Disposition: '.$this->sContentDisposition.'; filename="'.$this->sContentFileName.'"');
}
foreach ($this->a_headers as $s_header) {
header($s_header);
}
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline')) {
// inline content != attachment && html => filter all scripts for malicious XSS scripts
$sContent = self::FilterXSS($this->sContent);
} else {
$sContent = $this->sContent;
}
$oKpi->ComputeAndReport(get_class($this).' output');
echo $sContent;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($sContent) / 1024).' Kb)');
$this->FireAfterDisplayEvent();
ExecutionKPI::ReportStats();
}
}

View File

@@ -0,0 +1,102 @@
<?php
use Combodo\iTop\Application\Branding;
use Combodo\iTop\Application\UI\Base\Component\Title\Title;
use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
/**
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* @since 2.7.1 N°2641 class creation
*/
class ErrorPage extends NiceWebPage
{
public function __construct($sTitle)
{
$oKpi = new ExecutionKPI();
parent::__construct($sTitle);
$this->add_linked_script("../js/jquery.blockUI.js");
$this->add_linked_script("../setup/setup.js");
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-awesome/css/all.min.css');
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-combodo/font-combodo.css');
$this->add_saas("css/setup.scss");
$oKpi->ComputeStats(get_class($this).' creation', 'ErrorPage');
}
public function info($sText)
{
$this->add("<p class=\"info\">$sText</p>\n");
$this->log_info($sText);
}
public function ok($sText)
{
$this->add("<div class=\"message message-valid\"><span class=\"message-title\">Success:</span>$sText</div>");
$this->log_ok($sText);
}
public function warning($sText)
{
$this->add("<div class=\"message message-warning\"><span class=\"message-title\">Warning:</span>$sText</div>");
$this->log_warning($sText);
}
public function error($sText)
{
$this->add("<div class=\"message message-error\">$sText</div>");
if(utils::IsEasterEggAllowed())
{
$this->add('<div class="message message-valid">'.Dict::S('UI:ErrorPage:UnstableVersion').'</div>');
$this->add('<img src="../images/alpha-fatal-error.gif">');
$this->add('<div class="message message-valid">'.nl2br(Dict::S('UI:ErrorPage:KittyDisclaimer')).'</div>');
}
$this->log_error($sText);
}
public function output()
{
$sLogo = Branding::GetFullMainLogoAbsoluteUrl();
$oSetupPage = UIContentBlockUIBlockFactory::MakeStandard('ibo_setup_container', ['ibo-setup']);
$oHeader = UIContentBlockUIBlockFactory::MakeStandard('header', ['ibo-setup--header']);
$oSetupPage->AddSubBlock($oHeader);
$oTitle = TitleUIBlockFactory::MakeForPageWithIcon($this->s_title, $sLogo, Title::DEFAULT_ICON_COVER_METHOD, false);
$oHeader->AddSubBlock($oTitle);
$oSetup = UIContentBlockUIBlockFactory::MakeStandard('setup', ['ibo-setup--body']);
$oSetupPage->AddSubBlock($oSetup);
$oSetup->AddSubBlock($this->oContentLayout);
$this->oContentLayout = $oSetupPage;
return parent::output();
}
public static function log_error($sText)
{
IssueLog::Error($sText);
}
public static function log_warning($sText)
{
IssueLog::Warning($sText);
}
public static function log_info($sText)
{
IssueLog::Info($sText);
}
public static function log_ok($sText)
{
IssueLog::Ok($sText);
}
public static function log($sText)
{
IssueLog::Ok($sText);
}
}

View File

@@ -0,0 +1,109 @@
<?php
/*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Service\EventName;
use Combodo\iTop\Service\EventService;
/**
* Class JsonPage
*
* @author Anne-Catherine Cognet <anne-catherine.cognet@combodo.com>
* @author Eric Espie <eric.espie@combodo.com>
* @since 3.0.0
*/
class JsonPage extends WebPage
{
/** @var array Bags of data to include in the response */
protected $aData = [];
/**
* @var bool If true, only static::$aData will be output; otherwise data and scripts will be output in a structured object.
* This can be useful when feeding response to a third party lib that doesn't understand the structured format.
*/
protected $bOutputDataOnly = false;
/**
* JsonPage constructor.
*/
public function __construct()
{
$oKpi = new ExecutionKPI();
parent::__construct('');
$oKpi->ComputeStats(get_class($this).' creation', 'JsonPage');
}
/**
* @return array
*/
public function GetData(): array
{
return $this->aData;
}
/**
* @param array $aData
*
* @return $this
*/
public function SetData(array $aData)
{
$this->aData = $aData;
return $this;
}
/**
* @param array $aDataRow
*
* @return $this
*/
public function AddData(array $aDataRow)
{
$this->aData[] = $aDataRow;
return $this;
}
/**
* @see static::$bOutputDataOnly
* @param bool $bFlag
*
* @return $this
*/
public function SetOutputDataOnly(bool $bFlag)
{
$this->bOutputDataOnly = $bFlag;
return $this;
}
/**
* @inheritDoc
*/
public function output()
{
$oKpi = new ExecutionKPI();
$this->add_header('Content-type: application/json');
foreach ($this->a_headers as $s_header) {
header($s_header);
}
$aScripts = array_merge($this->a_init_scripts, $this->a_scripts, $this->a_ready_scripts);
$aJson = $this->bOutputDataOnly ? $this->aData : [
'data' => $this->aData,
'scripts' => $aScripts,
];
$sJSON = json_encode($aJson);
$oKpi->ComputeAndReport(get_class($this).' output');
echo $sJSON;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($sJSON) / 1024).' Kb)');
$this->FireAfterDisplayEvent();
ExecutionKPI::ReportStats();
}
}

View File

@@ -0,0 +1,268 @@
<?php
/**
* Copyright (C) 2013-2021 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
/**
* Web page with some associated CSS and scripts (jquery) for a fancier display
*/
class NiceWebPage extends WebPage
{
/** @inheritDoc */
protected const COMPATIBILITY_MOVED_LINKED_SCRIPTS_REL_PATH = [
// - DisplayableGraph, impact analysis
'js/jquery.positionBy.js',
'js/jquery.popupmenu.js',
// - SearchForm
'js/search/search_form_handler.js',
'js/search/search_form_handler_history.js',
'js/search/search_form_criteria.js',
'js/search/search_form_criteria_raw.js',
'js/search/search_form_criteria_string.js',
'js/search/search_form_criteria_external_field.js',
'js/search/search_form_criteria_numeric.js',
'js/search/search_form_criteria_enum.js',
'js/search/search_form_criteria_tag_set.js',
'js/search/search_form_criteria_external_key.js',
'js/search/search_form_criteria_hierarchical_key.js',
'js/search/search_form_criteria_date_abstract.js',
'js/search/search_form_criteria_date.js',
'js/search/search_form_criteria_date_time.js',
// - DataTable UIBlock
'js/field_sorter.js',
'js/table-selectable-lines.js',
// - Not used internally or by extensions yet
'js/clipboard.min.js',
'js/clipboardwidget.js',
// - SearchForm
'js/searchformforeignkeys.js',
];
/** @inheritDoc */
protected const COMPATIBILITY_DEPRECATED_LINKED_SCRIPTS_REL_PATH = [
/** @deprecated 3.0.0 Not used in the backoffice since the introduction of the new tooltip lib. */
'js/hovertip.js',
/** @deprecated 3.0.0 N°2737 - Migrate table to DataTables plugin to be iso with the end-users portal, will be removed in 3.x */
'js/datatable.js',
'js/jquery.tablesorter.js',
'js/jquery.tablesorter.pager.js',
'js/jquery.tablehover.js',
];
const DEFAULT_PAGE_TEMPLATE_REL_PATH = 'pages/backoffice/nicewebpage/layout';
var $m_sRootUrl;
public function __construct($s_title, $bPrintable = false)
{
$oKpi = new ExecutionKPI();
$this->m_sRootUrl = $this->GetAbsoluteUrlAppRoot();
parent::__construct($s_title, $bPrintable);
$this->LoadTheme();
$oKpi->ComputeStats(get_class($this).' creation', 'NiceWebPage');
}
/**
* @inheritDoc
* @since 3.0.0
*/
protected function InitializeScripts(): void
{
parent::InitializeScripts();
$sAbsURLAppRoot = addslashes($this->m_sRootUrl);
$sAbsURLModulesRoot = addslashes($this->GetAbsoluteUrlModulesRoot());
$sEnvironment = addslashes(utils::GetCurrentEnvironment());
$sAppContext = addslashes($this->GetApplicationContext());
$this->add_script(
<<<EOF
function GetAbsoluteUrlAppRoot()
{
return '$sAbsURLAppRoot';
}
function GetAbsoluteUrlModulesRoot()
{
return '$sAbsURLModulesRoot';
}
function GetAbsoluteUrlModulePage(sModule, sPage, aArguments)
{
// aArguments is optional, it default to an empty hash
aArguments = typeof aArguments !== 'undefined' ? aArguments : {};
var sUrl = '$sAbsURLAppRoot'+'pages/exec.php?exec_module='+sModule+'&exec_page='+sPage+'&exec_env='+'$sEnvironment';
for (var sArgName in aArguments)
{
if (aArguments.hasOwnProperty(sArgName))
{
sUrl = sUrl + '&'+sArgName+'='+aArguments[sArgname];
}
}
return sUrl;
}
function AddAppContext(sURL)
{
var sContext = '$sAppContext';
if (sContext.length > 0)
{
if (sURL.indexOf('?') == -1)
{
return sURL+'?'+sContext;
}
return sURL+'&'+sContext;
}
return sURL;
}
EOF
);
}
/**
* @inheritDoc
* @since 3.0.0
*/
protected function InitializeLinkedScripts(): void
{
parent::InitializeLinkedScripts();
// Used throughout the app.
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.min.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.blockUI.js');
if (utils::IsDevelopmentEnvironment()) // Needed since many other plugins still rely on oldies like $.browser
{
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate.dev-params.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate.dev.js');
} else {
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate.prod.min.js');
}
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-ui.custom.min.js');
// Used throughout the app.
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/utils.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/latinise/latinise.min.js');
}
/**
* @inheritDoc
* @since 3.0.0
*/
protected function InitializeDictEntries(): void
{
parent::InitializeDictEntries();
$this->add_dict_entries('UI:Combo');
}
public function SetRootUrl($sRootUrl)
{
$this->m_sRootUrl = $sRootUrl;
}
public function small_p($sText)
{
$this->add("<p style=\"font-size:smaller\">$sText</p>\n");
}
public function GetAbsoluteUrlAppRoot()
{
return utils::GetAbsoluteUrlAppRoot();
}
public function GetAbsoluteUrlModulesRoot()
{
return utils::GetAbsoluteUrlModulesRoot();
}
function GetApplicationContext()
{
$oAppContext = new ApplicationContext();
return $oAppContext->GetForLink();
}
// By Rom, used by CSVImport and Advanced search
public function MakeClassesSelect($sName, $sDefaultValue, $iWidthPx, $iActionCode = null)
{
// $aTopLevelClasses = array('bizService', 'bizContact', 'logInfra', 'bizDocument');
// These are classes wich root class is cmdbAbstractObject !
$this->add("<select id=\"select_$sName\" name=\"$sName\">");
$aValidClasses = array();
foreach(MetaModel::GetClasses('bizmodel') as $sClassName)
{
if (is_null($iActionCode) || UserRights::IsActionAllowed($sClassName, $iActionCode))
{
$sSelected = ($sClassName == $sDefaultValue) ? " SELECTED" : "";
$sDescription = MetaModel::GetClassDescription($sClassName);
$sDisplayName = MetaModel::GetName($sClassName);
$aValidClasses[$sDisplayName] = "<option style=\"width: ".$iWidthPx." px;\" title=\"$sDescription\" value=\"$sClassName\"$sSelected>$sDisplayName</option>";
}
}
ksort($aValidClasses);
$this->add(implode("\n", $aValidClasses));
$this->add("</select>");
}
// By Rom, used by Advanced search
public function add_select($aChoices, $sName, $sDefaultValue, $iWidthPx)
{
$this->add("<select id=\"select_$sName\" name=\"$sName\">");
foreach($aChoices as $sKey => $sValue)
{
$sSelected = ($sKey == $sDefaultValue) ? " SELECTED" : "";
$this->add("<option style=\"width: ".$iWidthPx." px;\" value=\"".htmlspecialchars($sKey)."\"$sSelected>".htmlentities($sValue,
ENT_QUOTES, self::PAGES_CHARSET)."</option>");
}
$this->add("</select>");
}
/**
* @inheritDoc
* @throws \Exception
*/
protected function LoadTheme()
{
// TODO 3.0.0: Remove light-grey when development of Full Moon is done.
// TODO 3.0.0: Reuse theming mechanism for Full Moon
$sCssThemeUrl = ThemeHandler::GetCurrentThemeUrl();
$this->add_linked_stylesheet($sCssThemeUrl);
$sCssRelPath = utils::GetCSSFromSASS(
'css/backoffice/main.scss',
array(
APPROOT.'css/backoffice/',
)
);
}
protected function GetReadyScriptsStartedTrigger(): ?string
{
return <<<JS
$("body").attr("data-ready-scripts", "start");
JS;
}
protected function GetReadyScriptsFinishedTrigger(): ?string
{
return <<<JS
$("body").attr("data-ready-scripts", "done");
JS;
}
}

View File

@@ -0,0 +1,171 @@
<?php
/**
* Copyright (C) 2013-2021 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
use Combodo\iTop\Renderer\BlockRenderer;
use Combodo\iTop\Service\EventName;
use Combodo\iTop\Service\EventService;
/**
* Special class of WebPage for printing into a PDF document
*/
class PDFPage extends WebPage
{
/** @var \iTopPDF Instance of the TCPDF object for creating the PDF */
protected $oPdf;
public function __construct($s_title, $sPageFormat = 'A4', $sPageOrientation = 'L')
{
$oKpi = new ExecutionKPI();
parent::__construct($s_title);
define('K_PATH_FONTS', APPROOT.'lib/combodo/tcpdf/fonts/');
$this->oPdf = new iTopPDF($sPageOrientation, 'mm', $sPageFormat, true, self::PAGES_CHARSET, false);
// set document information
$this->oPdf->SetCreator(PDF_CREATOR);
$this->oPdf->SetAuthor('iTop');
$this->oPdf->SetTitle($s_title);
$this->oPdf->SetDocumentTitle($s_title);
$this->oPdf->setFontSubsetting(true);
// dejavusans is a UTF-8 Unicode font. Standard PDF fonts like helvetica or times new roman are NOT UTF-8
$this->oPdf->SetFontParams('', 10, '', true);
// set auto page breaks
$this->oPdf->SetAutoPageBreak(true, 15); // 15 mm break margin at the bottom
$this->oPdf->SetTopMargin(15);
// Add a page, we're ready to start
$this->oPdf->AddPage();
$this->SetContentDisposition('inline', $s_title.'.pdf');
$this->SetDefaultStyle();
$oKpi->ComputeStats(get_class($this).' creation', 'PDFPage');
}
/**
* Sets a default style (suitable for printing) to be included each time $this->oPdf->writeHTML() is called
*/
protected function SetDefaultStyle()
{
$this->add_style(
<<<EOF
table {
padding: 2pt;
}
table.ibo-datatable td, table.listResults td {
border: 0.5pt solid #000 ;
}
table.ibo-datatable th, table.listResults th {
background-color: #eee;
border: 0.5pt solid #000 ;
}
a {
text-decoration: none;
color: #000;
}
table.section td {
vertical-align: middle;
font-size: 10pt;
background-color:#eee;
}
td.icon {
width: 30px;
}
h2{
font-size: 10pt;
vertical-align: middle;
background-color:#eee;
padding: 2pt;
margin:2pt;
}
EOF
);
}
/**
* Get access to the underlying TCPDF object
*
* @return \iTopPDF
*/
public function get_tcpdf()
{
$this->flush();
return $this->oPdf;
}
/**
* Writes the currently buffered HTML content into the PDF. This can be useful:
* - to sync the flow in case you want to access the underlying TCPDF object for some specific/graphic output
* - to process the HTML by smaller chunks instead of processing the whole page at once for performance reasons
*/
public function flush()
{
$sHtml = '';
if (count($this->a_styles) > 0) {
$sHtml .= "<style>\n".implode("\n", $this->a_styles)."\n</style>\n";
}
if (strlen($this->s_content) > 0) {
$sHtml .= $this->s_content;
$this->s_content = '';
}
$sHtml .= BlockRenderer::RenderBlockTemplates($this->oContentLayout);
$this->oPdf->writeHTML($sHtml); // The style(s) must be supplied each time we call writeHtml
}
/**
* Whether or not the page is a PDF page
*
* @return boolean
*/
public function is_pdf()
{
return true;
}
/**
* Generates the PDF document and returns the PDF content as a string
*
* @see WebPage::output()
*/
public function output()
{
$this->add_header('Content-type: application/x-pdf');
if (!empty($this->sContentDisposition))
{
$this->add_header('Content-Disposition: '.$this->sContentDisposition.'; filename="'.$this->sContentFileName.'"');
}
foreach ($this->a_headers as $s_header)
{
header($s_header);
}
$this->flush();
echo $this->oPdf->Output($this->s_title.'.pdf', 'S');
$this->FireAfterDisplayEvent();
}
public function get_pdf()
{
$this->flush();
return $this->oPdf->Output($this->s_title.'.pdf', 'S');
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* Generic interface common to CLI and Web pages
*/
interface Page
{
/**
* Outputs (via some echo) the complete HTML page by assembling all its elements
*
* @return mixed
*/
public function output();
/**
* Add any text or HTML fragment to the body of the page
*
* @param string $sText
*
* @return void
*/
public function add($sText);
/**
* Add a paragraph to the body of the page
*
* @param string $sText
*
* @return void
*/
public function p($sText);
/**
* Add a pre-formatted text to the body of the page
*
* @param string $sText
*
* @return void
*/
public function pre($sText);
/**
* Add a comment
*
* @param string $sText
*
* @return void
*/
public function add_comment($sText);
/**
* Adds a tabular content to the web page
*
* @param string[] $aConfig Configuration of the table: hash array of 'column_id' => 'Column Label'
* @param string[] $aData Hash array. Data to display in the table: each row is made of 'column_id' => Data. A
* column 'pkey' is expected for each row
* @param array $aParams Hash array. Extra parameters for the table.
*
* @return void
*/
public function table($aConfig, $aData, $aParams = array());
}

View File

@@ -0,0 +1,357 @@
<?php
/**
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Application\UI\Base\iUIBlock;
use Combodo\iTop\Application\UI\Base\Layout\TabContainer\Tab\Tab;
use Combodo\iTop\Application\UI\Base\Layout\TabContainer\TabContainer;
/**
* Helper class to implement JQueryUI tabs inside a page
*/
class TabManager
{
const ENUM_TAB_TYPE_HTML = 'html';
const ENUM_TAB_TYPE_AJAX = 'ajax';
const DEFAULT_TAB_TYPE = self::ENUM_TAB_TYPE_HTML;
/**
* @var TabContainer[]
*/
protected $m_aTabs;
protected $m_sCurrentTabContainer;
protected $m_sCurrentTab;
public function __construct()
{
$this->m_aTabs = [];
$this->m_sCurrentTabContainer = '';
$this->m_sCurrentTab = '';
}
/**
* @param string $sTabContainer
* @param string $sPrefix
*
* @return \Combodo\iTop\Application\UI\Base\iUIBlock
*/
public function AddTabContainer(string $sTabContainer, $sPrefix = ''): TabContainer
{
$oTabContainer = new TabContainer($sTabContainer, $sPrefix);
$this->m_aTabs[$sTabContainer] = $oTabContainer;
return $oTabContainer;
}
/**
* @param string $sHtml
*
* @throws \Exception
*/
public function AddToCurrentTab(string $sHtml): void
{
$this->AddToTab($this->m_sCurrentTabContainer, $this->m_sCurrentTab, $sHtml);
}
public function AddUIBlockToCurrentTab(iUIBlock $oBlock): iUIBlock
{
$this->AddUIBlockToTab($this->m_sCurrentTabContainer, $this->m_sCurrentTab, $oBlock);
return $oBlock;
}
public function AddUIBlockToTab(string $sTabContainer, string $sTabCode, iUIBlock $oBlock, $sTabTitle = null): void
{
if (!$this->TabExists($sTabContainer, $sTabCode)) {
$this->InitTab($sTabContainer, $sTabCode, static::ENUM_TAB_TYPE_HTML, $sTabTitle);
}
$oTab = $this->GetTab($sTabContainer, $sTabCode);
// Append to the content of the tab
$oTab->AddSubBlock($oBlock);
}
/**
* @return int
* @deprecated 3.0.0
*/
public function GetCurrentTabLength()
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
return 0;
}
/**
* Truncates the given tab to the specifed length and returns the truncated part
*
* @param string $sTabContainer The tab container in which to truncate the tab
* @param string $sTab The name/identifier of the tab to truncate
* @param integer $iLength The length/offset at which to truncate the tab
*
* @return string The truncated part
* @deprecated 3.0.0
*/
public function TruncateTab(string $sTabContainer, string $sTab, int $iLength)
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
return '';
}
/**
* @param string $sTabContainer
* @param string $sTab
*
* @return bool
*/
public function TabExists(string $sTabContainer, string $sTab)
{
return isset($this->m_aTabs[$sTabContainer]) ? $this->m_aTabs[$sTabContainer]->TabExists($sTab) : false;
}
/**
* @return int
*/
public function TabsContainerCount()
{
return count($this->m_aTabs);
}
private function GetTab(string $sTabContainer, string $sTab): ?Tab
{
if ($this->TabExists($sTabContainer, $sTab)) {
return $this->m_aTabs[$sTabContainer]->GetTab($sTab);
}
return null;
}
/**
* @param string $sTabContainer
* @param string $sTabCode
* @param string $sHtml
* @param string|null $sTabTitle
*
* @return string
* @throws \Exception
*/
public function AddToTab(string $sTabContainer, string $sTabCode, string $sHtml, $sTabTitle = null): string
{
if (!$this->TabExists($sTabContainer, $sTabCode)) {
$this->InitTab($sTabContainer, $sTabCode, static::ENUM_TAB_TYPE_HTML, $sTabTitle);
}
$oTab = $this->GetTab($sTabContainer, $sTabCode);
// Append to the content of the tab
$oTab->AddHtml($sHtml);
return ''; // Nothing to add to the page for now
}
/**
* @param string $sTabContainer
*
* @return string
*/
public function SetCurrentTabContainer($sTabContainer = '')
{
$sPreviousTabContainer = $this->m_sCurrentTabContainer;
$this->m_sCurrentTabContainer = $sTabContainer;
return $sPreviousTabContainer;
}
/**
* @param string $sTabCode
*
* @param string|null $sTabTitle
*
* @return string
* @throws \Combodo\iTop\Application\UI\Base\UIException
*/
public function SetCurrentTab(string $sTabCode = '', string $sTabTitle = null): ?string
{
$sPreviousTabCode = $this->m_sCurrentTab;
$this->m_sCurrentTab = $sTabCode;
if ($sTabCode != '') {
// Init tab to HTML tab if not existing
if (!$this->TabExists($this->GetCurrentTabContainer(), $sTabCode)) {
$this->InitTab($this->GetCurrentTabContainer(), $sTabCode, static::ENUM_TAB_TYPE_HTML, $sTabTitle);
}
}
return $sPreviousTabCode;
}
/**
* Add a tab which content will be loaded asynchronously via the supplied URL
*
* Limitations:
* Cross site scripting is not not allowed for security reasons. Use a normal tab with an IFRAME if you want to
* pull content from another server. Static content cannot be added inside such tabs.
*
* @param string $sTabCode The (localised) label of the tab
* @param string $sUrl The URL to load (on the same server)
* @param boolean $bCache Whether or not to cache the content of the tab once it has been loaded. false will cause
* the tab to be reloaded upon each activation.
*
* @param string|null $sTabTitle
* @param string $sPlaceholder
*
* @return string
*
* @throws \Combodo\iTop\Application\UI\Base\UIException
* @since 2.0.3
*/
public function AddAjaxTab(string $sTabCode, string $sUrl, bool $bCache = true, string $sTabTitle = null, string $sPlaceholder = null): string
{
// Set the content of the tab
/** @var \Combodo\iTop\Application\UI\Base\Layout\TabContainer\Tab\AjaxTab $oTab */
$oTab = $this->InitTab($this->m_sCurrentTabContainer, $sTabCode, static::ENUM_TAB_TYPE_AJAX, $sTabTitle, $sPlaceholder);
$oTab->SetUrl($sUrl)
->SetCache($bCache);
return ''; // Nothing to add to the page for now
}
/**
* @return string
*/
public function GetCurrentTabContainer()
{
return $this->m_sCurrentTabContainer;
}
/**
* @return string
*/
public function GetCurrentTab()
{
return $this->m_sCurrentTab;
}
/**
* @param string $sTabCode
* @param string|null $sTabContainer
*/
public function RemoveTab(string $sTabCode, string $sTabContainer = null)
{
if ($sTabContainer == null) {
$sTabContainer = $this->m_sCurrentTabContainer;
}
if (isset($this->m_aTabs[$sTabContainer]) && $this->m_aTabs[$sTabContainer]->TabExists($sTabCode)) {
// Delete the content of the tab
$this->m_aTabs[$sTabContainer]->RemoveTab($sTabCode);
// If we just removed the active tab, let's reset the active tab
if (($this->m_sCurrentTabContainer == $sTabContainer) && ($this->m_sCurrentTab == $sTabCode)) {
$this->m_sCurrentTab = '';
}
}
}
/**
* Finds the tab whose title matches a given pattern
*
* @param string $sPattern
* @param string|null $sTabContainer
*
* @return mixed The actual name of the tab (as a string) or false if not found
*/
public function FindTab(string $sPattern, string $sTabContainer = null)
{
$result = false;
if ($sTabContainer == null) {
$sTabContainer = $this->m_sCurrentTabContainer;
}
if (isset($this->m_aTabs[$sTabContainer])) {
foreach ($this->m_aTabs[$sTabContainer]->GetSubBlocks() as $sTabCode => $void) {
if (preg_match($sPattern, $sTabCode)) {
$result = $sTabCode;
break;
}
}
}
return $result;
}
/**
* 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
*
* @return string
* @deprecated 3.0.0
*/
public function SelectTab(string $sTabContainer, string $sTabCode)
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
return '';
}
/**
* @param string $sContent
* @param \WebPage $oPage
*
* @return mixed
* @deprecated 3.0.0
*/
public function RenderIntoContent(string $sContent, WebPage $oPage)
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
return '';
}
/**
* @param string $sTabContainer
* @param string $sTabCode
* @param string $sTabType
* @param string|null $sTabTitle
* @param string|null $sPlaceholder
*
* @return \Combodo\iTop\Application\UI\Base\Layout\TabContainer\Tab\Tab
* @throws \Combodo\iTop\Application\UI\Base\UIException
* @since 2.7.0
*/
protected function InitTab(string $sTabContainer, string $sTabCode, string $sTabType = self::DEFAULT_TAB_TYPE, string $sTabTitle = null, string $sPlaceholder = null): Tab
{
$oTab = null;
if (!$this->TabExists($sTabContainer, $sTabCode)) {
if (!isset($this->m_aTabs[$sTabContainer])) {
$oTabContainer = $this->AddTabContainer($sTabContainer);
} else {
$oTabContainer = $this->m_aTabs[$sTabContainer];
}
$sTitle = ($sTabTitle !== null) ? Dict::S($sTabTitle) : Dict::S($sTabCode);
switch ($sTabType) {
case static::ENUM_TAB_TYPE_AJAX:
$oTab = $oTabContainer->AddAjaxTab($sTabCode, $sTitle, $sPlaceholder);
break;
case static::ENUM_TAB_TYPE_HTML:
default:
$oTab = $oTabContainer->AddTab($sTabCode, $sTitle);
break;
}
} else {
$oTab = $this->GetTab($sTabContainer, $sTabCode);
}
return $oTab;
}
}

View File

@@ -0,0 +1,265 @@
<?php
/**
* Copyright (C) 2013-2020 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
use Combodo\iTop\Application\Branding;
use Combodo\iTop\Application\TwigBase\Twig\TwigHelper;
use Combodo\iTop\Renderer\BlockRenderer;
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
use Combodo\iTop\Service\EventName;
use Combodo\iTop\Service\EventService;
$sPortalBaseFolderRelPath = 'env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/';
$sPortalSourcesFolderRelPath = $sPortalBaseFolderRelPath . 'src/';
$sPortalPublicFolderRelPath = $sPortalBaseFolderRelPath . 'public/';
$sPortalBaseFolderAbsPath = APPROOT . $sPortalBaseFolderRelPath;
$sPortalSourcesFolderAbsPath = APPROOT . $sPortalSourcesFolderRelPath;
$sPortalPublicFolderAbsPath = APPROOT . $sPortalPublicFolderRelPath;
/** @noinspection PhpUnhandledExceptionInspection */
$sPortalPublicFolderAbsUrl = utils::GetAbsoluteUrlModulesRoot().'/itop-portal-base/portal/public/';
// Constants to be used in the UnauthenticatedWebPage
if(!defined('UAWP_PORTAL_PUBLIC_FOLDER_ABSOLUTE_URL'))
{
define('UAWP_PORTAL_PUBLIC_FOLDER_ABSOLUTE_URL', $sPortalPublicFolderAbsUrl);
}
if(!defined('UAWP_PORTAL_PUBLIC_FOLDER_RELATIVE_PATH'))
{
define('UAWP_PORTAL_PUBLIC_FOLDER_RELATIVE_PATH', $sPortalPublicFolderRelPath);
}
/**
* Class UnauthenticatedWebPage
*
* @author Stephen Abello <stephen.abello@combodo.com>
*
* @since 3.0.0
*/
class UnauthenticatedWebPage extends NiceWebPage
{
const DEFAULT_PAGE_TEMPLATE_REL_PATH = 'pages/backoffice/unauthenticatedwebpage/layout';
private $sContent;
private $sPanelTitle;
private $sPanelIcon;
// TODO 3.0 Find a clever way to allow theme customization for unauthenticated webpages
private $sCustomThemeUrl;
/**
* @inheritDoc
* @throws \Exception
*/
public function __construct($s_title, $bPrintable = false)
{
$oKpi = new ExecutionKPI();
parent::__construct($s_title, $bPrintable);
$this->sContent = '';
$this->sPanelTitle = '';
$this->sPanelIcon = Branding::GetFullMainLogoAbsoluteUrl();
$this->SetContentType('text/html');
// - bootstrap
$this->add_linked_script(UAWP_PORTAL_PUBLIC_FOLDER_ABSOLUTE_URL . 'lib/bootstrap/js/bootstrap.min.js');
// Note: Since 2.6.0 moment was moved from portal to iTop core
$sMomentURL = utils::GetAbsoluteUrlAppRoot().'/js/moment-with-locales.min.js';
$this->add_linked_script($sMomentURL);
$this->add_linked_script(UAWP_PORTAL_PUBLIC_FOLDER_ABSOLUTE_URL . 'lib/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js');
// CSS files
$this->add_linked_stylesheet(UAWP_PORTAL_PUBLIC_FOLDER_ABSOLUTE_URL . 'lib/bootstrap/css/bootstrap.min.css');
$this->add_saas(UAWP_PORTAL_PUBLIC_FOLDER_RELATIVE_PATH . 'css/bootstrap-theme-combodo.scss');
$this->add_linked_stylesheet(UAWP_PORTAL_PUBLIC_FOLDER_ABSOLUTE_URL . 'lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker.css');
// Default theme
$this->add_saas('css/unauthenticated.scss');
$oKpi->ComputeStats(get_class($this).' creation', $s_title);
}
/**
* @inheritdoc
*/
public function add($sHtml)
{
$this->sContent .= $sHtml;
}
/**
* @inheritdoc
*/
public function output()
{
$oKpi = new ExecutionKPI();
// Send headers
foreach ($this->a_headers as $sHeader) {
header($sHeader);
}
$s_captured_output = $this->ob_get_clean_safe();
$aData = [];
// Prepare internal parts (js files, css files, js snippets, css snippets, ...)
// - Generate necessary dict. files
if ($this->bAddJSDict) {
$this->output_dict_entries();
}
$aData['oLayout'] = $this->oContentLayout;
$aData['aDeferredBlocks'] = $this->GetDeferredBlocks($this->oContentLayout);
ConsoleBlockRenderer::AddCssJsToPage($this, $this->oContentLayout);
// Base structure of data to pass to the TWIG template
$aData['aPage'] = [
'sAbsoluteUrlAppRoot' => addslashes(utils::GetAbsoluteUrlAppRoot()),
'sTitle' => $this->s_title,
'aMetadata' => [
'sCharset' => static::PAGES_CHARSET,
'sLang' => $this->GetLanguageForMetadata(),
],
'aCssFiles' => $this->a_linked_stylesheets,
'aCssInline' => $this->a_styles,
'aJsInlineEarly' => $this->a_early_scripts,
'aJsFiles' => $this->a_linked_scripts,
'aJsInlineLive' => $this->a_scripts,
'aJsInlineOnDomReady' => $this->GetReadyScripts(),
'aJsInlineOnInit' => $this->a_init_scripts,
// TODO 3.0.0: TEMP, used while developing, remove it.
'sCapturedOutput' => utils::FilterXSS($s_captured_output),
'sDeferredContent' => utils::FilterXSS($this->s_deferred_content),
'sContent' => $this->sContent,
'sPanelIcon' => $this->sPanelIcon,
'sPanelTitle' => $this->sPanelTitle
];
$aData['aBlockParams'] = $this->GetBlockParams();
if ($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'];
}
// Favicon
$aData['aPage']['sFaviconUrl'] = $this->GetFaviconAbsoluteUrl();
$oTwigEnv = TwigHelper::GetTwigEnvironment(BlockRenderer::TWIG_BASE_PATH, BlockRenderer::TWIG_ADDITIONAL_PATHS);
// Render final TWIG into global HTML
$sHtml = TwigHelper::RenderTemplate($oTwigEnv, $aData, $this->GetTemplateRelPath());
$oKpi->ComputeAndReport(get_class($this).' output');
echo $sHtml;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($sHtml) / 1024).' Kb)');
$this->FireAfterDisplayEvent();
ExecutionKPI::ReportStats();
}
/**
* Displays a success message.
*
* @param string $sMessage
*
* @throws \Exception
*/
public function DisplaySuccessMessage($sMessage)
{
$this->Display('<div class="alert alert-success">'.$sMessage.'</div>');
}
/**
* Displays an error message.
*
* @param string $sMessage
*
* @throws \Exception
*/
public function DisplayErrorMessage($sMessage)
{
$sFormTitle = Dict::S('UnauthenticatedForms:Form:DefaultLabel:Form:Title');
$sHtml = '<div class="alert alert-danger">'.$sMessage.'</div>';
$this->set_title($sMessage);
$this->Display($sHtml, $sFormTitle);
}
/**
* @return string
*/
public function GetPanelTitle(): string
{
return $this->sPanelTitle;
}
/**
* @param string $sPanelTitle
*
* @return \UnauthenticatedWebPage
*/
public function SetPanelTitle(string $sPanelTitle): UnauthenticatedWebPage
{
$this->sPanelTitle = $sPanelTitle;
return $this;
}
/**
* @return string
*/
public function GetPanelIcon(): string
{
return $this->sPanelIcon;
}
/**
* @param string $sPanelIcon
*
* @return \UnauthenticatedWebPage
*/
public function SetPanelIcon(string $sPanelIcon): UnauthenticatedWebPage
{
$this->sPanelIcon = $sPanelIcon;
return $this;
}
/**
* @inheritDoc
* @throws \Exception
*/
protected function LoadTheme()
{
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-awesome/css/all.min.css');
// Default theme
$this->add_saas('css/unauthenticated.scss');
// Custom theme to allow admin to override the default one.
if(!empty($this->sCustomThemeUrl))
{
$this->add_linked_stylesheet($this->sCustomThemeUrl);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,113 @@
<?php
// Copyright (C) 2010-2021 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class XMLPage
*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Service\EventName;
use Combodo\iTop\Service\EventService;
/**
* Simple web page with no includes or fancy formatting, useful to generateXML documents
* The page adds the content-type text/XML and the encoding into the headers
*/
class XMLPage extends WebPage
{
/**
* For big XML files, it's better NOT to store everything in memory and output the XML piece by piece
*/
var $m_bPassThrough;
var $m_bHeaderSent;
function __construct($s_title, $bPassThrough = false)
{
$oKpi = new ExecutionKPI();
parent::__construct($s_title);
$this->m_bPassThrough = $bPassThrough;
$this->m_bHeaderSent = false;
$this->add_header("Content-type: text/xml; charset=".self::PAGES_CHARSET);
$this->no_cache();
$this->add_xframe_options();
$this->add_header("Content-location: export.xml");
$oKpi->ComputeStats(get_class($this).' creation', 'XMLPage');
}
public function output()
{
if (!$this->m_bPassThrough)
{
$oKpi = new ExecutionKPI();
// Get the unexpected output but do nothing with it
$sTrash = $this->ob_get_clean_safe();
$sCharset = self::PAGES_CHARSET;
$this->s_content = "<?xml version=\"1.0\" encoding=\"$sCharset\"?".">\n".trim($this->s_content);
$this->add_header("Content-Length: ".strlen($this->s_content));
foreach($this->a_headers as $s_header)
{
header($s_header);
}
$oKpi->ComputeAndReport(get_class($this).' output');
echo $this->s_content;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($this->s_content) / 1024).' Kb)');
}
if (class_exists('DBSearch')) {
DBSearch::RecordQueryTrace();
}
$this->FireAfterDisplayEvent();
ExecutionKPI::ReportStats();
}
public function add($sText)
{
if (!$this->m_bPassThrough) {
parent::add($sText);
} else {
if ($this->m_bHeaderSent) {
echo $sText;
}
else
{
$s_captured_output = $this->ob_get_clean_safe();
foreach($this->a_headers as $s_header)
{
header($s_header);
}
$sCharset = self::PAGES_CHARSET;
echo "<?xml version=\"1.0\" encoding=\"$sCharset\"?".">\n";
echo trim($s_captured_output);
echo trim($this->s_content);
echo $sText;
$this->m_bHeaderSent = true;
}
}
}
public function small_p($sText)
{
}
public function table($aConfig, $aData, $aParams = array())
{
}
}

View File

@@ -0,0 +1,80 @@
<?php
use Combodo\iTop\Application\UI\Base\Layout\iUIContentBlock;
/**
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
interface iTabbedPage
{
/**
* @param string $sTabContainer
* @param string $sPrefix
*
* @param \Combodo\iTop\Application\UI\Base\Layout\iUIContentBlock|null $oParentBlock
*
* @return mixed
*/
public function AddTabContainer($sTabContainer, $sPrefix = '', iUIContentBlock $oParentBlock = null);
/**
* @param string $sTabContainer
* @param string $sTabCode
* @param string $sHtml
*
* @return mixed
*/
public function AddToTab($sTabContainer, $sTabCode, $sHtml);
/**
* @param string $sTabContainer
*
* @return mixed
*/
public function SetCurrentTabContainer($sTabContainer = '');
/**
* @param string $sTabCode
*
* @return mixed
*/
public function SetCurrentTab($sTabCode = '');
/**
* Add a tab which content will be loaded asynchronously via the supplied URL
*
* Limitations:
* Cross site scripting is not not allowed for security reasons. Use a normal tab with an IFRAME if you want to
* pull content from another server. Static content cannot be added inside such tabs.
*
* @param string $sTabCode The (localised) label of the tab
* @param string $sUrl The URL to load (on the same server)
* @param boolean $bCache Whether or not to cache the content of the tab once it has been loaded. flase will cause
* the tab to be reloaded upon each activation.
* @param string|null $sTabTitle
*
* @since 2.0.3
*/
public function AddAjaxTab($sTabCode, $sUrl, $bCache = true, $sTabTitle = null);
public function GetCurrentTab();
/**
* @param string $sTabCode
* @param string|null $sTabContainer
*
* @return mixed
*/
public function RemoveTab($sTabCode, $sTabContainer = null);
/**
* Finds the tab whose title matches a given pattern
*
* @param string $sPattern
* @param string|null $sTabContainer
*
* @return mixed The name of the tab as a string or false if not found
*/
public function FindTab($sPattern, $sTabContainer = null);
}

View File

@@ -0,0 +1,119 @@
<?php
/**
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Application\Branding;
/**
* Custom class derived from TCPDF for providing custom headers and footers
*
* @author denis
*
*/
class iTopPDF extends TCPDF
{
protected $sDocumentTitle;
/**
* Shortcut for {@link TCPDF::SetFont}, to use the font configured
*
* @param string $style
* @param int $size
* @param string $fontfile
* @param string $subset
* @param bool $out
*
* @uses \TCPDF::SetFont()
* @uses \iTopPDF::GetPdfFont()
* @since 2.7.0
*/
public function SetFontParams($style, $size, $fontfile = '', $subset = 'default', $out = true)
{
$siTopFont = self::GetPdfFont();
$this->SetFont($siTopFont, $style, $size, $fontfile, $subset, $out);
}
public function SetDocumentTitle($sDocumentTitle)
{
$this->sDocumentTitle = $sDocumentTitle;
}
/**
* Add image
*
* @param string $sImagePath Name of the SVG file or a '@' character followed by the SVG data string.
* @param $x (float) Abscissa of the upper-left corner.
* @param $y (float) Ordinate of the upper-left corner.
* @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
* @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
*/
public function AddImage($sImagePath, $x = '', $y = '', $w = 0, $h = 0)
{
/*if (endsWith(strtolower($sImagePath), ".svg")) {
$this->ImageSVG($sImagePath, $x, $y, $w, $h);
} else {
$this->Image($sImagePath, $x, $y, $w, $h);
}*/
$imgtype = TCPDF_IMAGES::getImageFileType($sImagePath);
if (($imgtype == 'eps') or ($imgtype == 'ai')) {
$this->ImageEps($sImagePath, $x, $y, $w, $h);;
} elseif ($imgtype == 'svg') {
$this->ImageSVG($sImagePath, $x, $y, $w, $h);;
} else {
$this->Image($sImagePath, $x, $y, $w, $h);;
}
}
/**
* Builds the custom header. Called for each new page.
*
* @see TCPDF::Header()
*/
public function Header()
{
// Title
// Set font
$this->SetFontParams('B', 10);
$iPageNumberWidth = 25;
$aMargins = $this->getMargins();
// Display the title (centered)
$this->SetXY($aMargins['left'] + $iPageNumberWidth, 0);
$this->MultiCell($this->getPageWidth() - $aMargins['left'] - $aMargins['right'] - 2 * $iPageNumberWidth, 15, $this->sDocumentTitle,
0, 'C', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
$this->SetFontParams('', 10);
// Display the page number (right aligned)
// Warning: the 'R'ight alignment does not work when using placeholders like $this->getAliasNumPage() or $this->getAliasNbPages()
$this->MultiCell($iPageNumberWidth, 15, Dict::Format('Core:BulkExport:PDF:PageNumber', $this->page), 0, 'R', false, 0 /* $ln */, '',
'', true, 0, false, true, 15, 'M' /* $valign */);
// Branding logo
$sBrandingIcon = Branding::GetLogoRelativePath(Branding::ENUM_LOGO_TYPE_MAIN_LOGO_FULL);
$this->AddImage($sBrandingIcon, $aMargins['left'], 5, 0, 10);
}
// Page footer
public function Footer()
{
// No footer
}
/**
* dejavusans is a UTF-8 Unicode font. Standard PDF fonts like helvetica or times new roman are NOT UTF-8
*
* @return string font in the config file (export_pdf_font)
*/
public static function GetPdfFont()
{
$oConfig = utils::GetConfig();
$sPdfFont = $oConfig->Get('export_pdf_font');
return $sPdfFont;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,57 @@
<?php
// Copyright (C) 2010-2021 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class iTopWizardWebPage
*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* Web page to display a wizard in the iTop framework
*/
class iTopWizardWebPage extends iTopWebPage
{
var $m_iCurrentStep;
var $m_aSteps;
public function __construct($sTitle, $currentOrganization, $iCurrentStep, $aSteps)
{
$oKpi = new ExecutionKPI();
parent::__construct($sTitle." - step $iCurrentStep of ".count($aSteps)." - ".$aSteps[$iCurrentStep - 1], $currentOrganization);
$this->m_iCurrentStep = $iCurrentStep;
$this->m_aSteps = $aSteps;
$oKpi->ComputeStats(get_class($this).' creation', 'iTopWizardWebPage');
}
public function output()
{
$aSteps = array();
$iIndex = 0;
foreach($this->m_aSteps as $sStepTitle)
{
$iIndex++;
$sStyle = ($iIndex == $this->m_iCurrentStep) ? 'wizActiveStep' : 'wizStep';
$aSteps[] = "<div class=\"$sStyle\"><span>$sStepTitle</span></div>";
}
$sWizardHeader = "<div class=\"wizHeader\"><h1>".htmlentities($this->s_title, ENT_QUOTES, 'UTF-8')."</h1>\n".implode("<div class=\"wizSeparator\"><img align=\"bottom\" src=\"../images/wizArrow.gif\"></div>", $aSteps)."<br style=\"clear:both;\"/></div>\n";
$this->s_content = "$sWizardHeader<div class=\"wizContainer\">".$this->s_content."</div>";
parent::output();
}
}