N°1632 Add a common webpage to be used for unauthenticated/token based actions, guarantees harmony between extensions style and a common API

This commit is contained in:
Stephen Abello
2021-08-19 11:36:13 +02:00
parent 121e39b738
commit 95b6dd0cc3
7 changed files with 404 additions and 1 deletions

47
css/unauthenticated.css Normal file
View File

@@ -0,0 +1,47 @@
/*!
* 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
*/
body {
display: block;
}
/* Landing page */
#uwp-main-container {
margin-top: 40px;
}
#uwp-main-container #uwp-logo, #uwp-main-container #uwp-title {
vertical-align: middle;
}
#uwp-main-container #uwp-title {
margin-left: 25px;
font-size: 20px;
font-weight: bold;
}
#uwp-main-container #uwp-description {
margin-bottom: 25px;
}
#uwp-main-container .uwp-text-hint--icon {
font-size: 12px;
margin-left: 5px;
color: #939191;
}
#uwp-main-container #uwp-bottom-buttons {
margin-top: 25px;
text-align: right;
}
#uwp-main-container #uwp-bottom-buttons .btn ~ .btn {
margin-left: 8px;
}

54
css/unauthenticated.scss Normal file
View File

@@ -0,0 +1,54 @@
/*!
* 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
*/
body{
display: block;
}
/* Landing page */
#uwp-main-container{
margin-top: 40px;
#uwp-logo,
#uwp-title{
vertical-align: middle;
}
#uwp-title{
margin-left: 25px;
font-size: 20px;
font-weight: bold;
}
#uwp-description{
margin-bottom: 25px;
}
.uwp-text-hint--icon{
font-size: 12px;
margin-left: 5px;
color: #939191;
}
#uwp-bottom-buttons {
margin-top: 25px;
text-align: right;
.btn ~ .btn{
margin-left: 8px;
}
}
}

View File

@@ -338,7 +338,7 @@ class ClassLoader
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
@@ -347,6 +347,8 @@ class ClassLoader
return true;
}
return null;
}
/**

View File

@@ -297,6 +297,7 @@ return array(
'Combodo\\iTop\\Controller\\Base\\Layout\\ActivityPanelController' => $baseDir . '/sources/Controller/Base/Layout/ActivityPanelController.php',
'Combodo\\iTop\\Controller\\PreferencesController' => $baseDir . '/sources/Controller/PreferencesController.php',
'Combodo\\iTop\\Core\\CMDBChange\\CMDBChangeOrigin' => $baseDir . '/sources/Core/CMDBChange/CMDBChangeOrigin.php',
'Combodo\\iTop\\Core\\MetaModel\\FriendlyNameType' => $baseDir . '/sources/Core/MetaModel/FriendlyNameType.php',
'Combodo\\iTop\\DesignDocument' => $baseDir . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\DesignElement' => $baseDir . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\Form\\Field\\BlobField' => $baseDir . '/sources/Form/Field/BlobField.php',
@@ -2318,6 +2319,7 @@ return array(
'URLButtonItem' => $baseDir . '/application/applicationextension.inc.php',
'URLPopupMenuItem' => $baseDir . '/application/applicationextension.inc.php',
'UnaryExpression' => $baseDir . '/core/oql/expression.class.inc.php',
'UnauthenticatedWebPage' => $baseDir . '/sources/application/WebPage/UnauthenticatedWebPage.php',
'UnknownClassOqlException' => $baseDir . '/core/oql/oqlinterpreter.class.inc.php',
'User' => $baseDir . '/core/userrights.class.inc.php',
'UserDashboard' => $baseDir . '/application/user.dashboard.class.inc.php',

View File

@@ -527,6 +527,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'Combodo\\iTop\\Controller\\Base\\Layout\\ActivityPanelController' => __DIR__ . '/../..' . '/sources/Controller/Base/Layout/ActivityPanelController.php',
'Combodo\\iTop\\Controller\\PreferencesController' => __DIR__ . '/../..' . '/sources/Controller/PreferencesController.php',
'Combodo\\iTop\\Core\\CMDBChange\\CMDBChangeOrigin' => __DIR__ . '/../..' . '/sources/Core/CMDBChange/CMDBChangeOrigin.php',
'Combodo\\iTop\\Core\\MetaModel\\FriendlyNameType' => __DIR__ . '/../..' . '/sources/Core/MetaModel/FriendlyNameType.php',
'Combodo\\iTop\\DesignDocument' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\DesignElement' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\Form\\Field\\BlobField' => __DIR__ . '/../..' . '/sources/Form/Field/BlobField.php',
@@ -2548,6 +2549,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'URLButtonItem' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'URLPopupMenuItem' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'UnaryExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php',
'UnauthenticatedWebPage' => __DIR__ . '/../..' . '/sources/application/WebPage/UnauthenticatedWebPage.php',
'UnknownClassOqlException' => __DIR__ . '/../..' . '/core/oql/oqlinterpreter.class.inc.php',
'User' => __DIR__ . '/../..' . '/core/userrights.class.inc.php',
'UserDashboard' => __DIR__ . '/../..' . '/application/user.dashboard.class.inc.php',

View File

@@ -0,0 +1,275 @@
<?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\TwigBase\Twig\TwigHelper;
use Combodo\iTop\Renderer\BlockRenderer;
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
$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/';
// Portal autoloader
require_once $sPortalBaseFolderAbsPath . 'vendor/autoload.php';
// 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;
/** @var array $aReadyScripts */
protected $aReadyScripts;
/**
* @inheritDoc
* @throws \Exception
*/
public function __construct($s_title, $bPrintable = false)
{
parent::__construct($s_title, $bPrintable);
$this->aReadyScripts = array();
$this->sContent = '';
$this->sPanelTitle = '';
$this->sPanelIcon = utils::GetAbsoluteUrlAppRoot().'images/itop-logo.png';
if (file_exists(MODULESROOT.'branding/main-logo.png'))
{
$this->sPanelIcon = utils::GetAbsoluteUrlModulesRoot().'branding/main-logo.png';
}
$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');
}
/**
* @inheritdoc
*/
public function add($sHtml)
{
$this->sContent .= $sHtml;
}
/**
* @inheritdoc
*/
public function add_ready_script($sScript)
{
$this->aReadyScripts[] = $sScript;
}
/**
* @inheritdoc
*/
public function output()
{
// 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());
echo $sHtml;
}
/**
* 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);
}
}
}

View File

@@ -0,0 +1,21 @@
{# @copyright Copyright (C) 2010-2021 Combodo SARL #}
{# @license http://opensource.org/licenses/AGPL-3.0 #}
{% extends "pages/backoffice/nicewebpage/layout.html.twig" %}
{% block iboPageBodyHtml %}
<div class="container-fluid">
<div class="row">
<div class="col-xs-12 col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2">
<div id="uwp-main-container">
<div id="uwp-container" class="panel panel-default">
<div id="uwp-header" class="panel-heading">
<h1 class="panel-title"><img id="uwp-logo" src="{{ aPage.sPanelIcon }}" /><span id="uwp-title">{{ aPage.sPanelTitle }}</span></h1>
</div>
<div id="uwp-content" class="panel-body">
{{ aPage.sContent|raw }}
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}