From 1394bc221d3681da578ef6884139c8c63e22b2db Mon Sep 17 00:00:00 2001 From: Anne-Catherine <57360138+accognet@users.noreply.github.com> Date: Wed, 28 Feb 2024 09:59:11 +0100 Subject: [PATCH] =?UTF-8?q?N=C2=B03363=20-=20Add=20favicon=20in=20branding?= =?UTF-8?q?=20(#522)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/loginwebpage.class.inc.php | 11 ++- .../PortalXmlConfiguration/Basic.php | 38 +++++++--- .../portal/templates/layout.html.twig | 2 +- setup/compiler.class.inc.php | 17 +++-- setup/itopdesignformat.class.inc.php | 9 ++- sources/Application/Branding.php | 69 +++++++++++++++++-- .../WebPage/UnauthenticatedWebPage.php | 11 ++- sources/Application/WebPage/WebPage.php | 13 ++-- .../Convert-samples/3.1_to_3.2.expected.xml | 3 + .../Convert-samples/3.1_to_3.2.input.xml | 3 + .../Convert-samples/3.2_to_3.1.expected.xml | 4 ++ .../Convert-samples/3.2_to_3.1.input.xml | 14 ++++ .../iTopDesignFormat/iTopDesignFormatTest.php | 2 + 13 files changed, 161 insertions(+), 35 deletions(-) create mode 100644 tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.1_to_3.2.expected.xml create mode 100644 tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.1_to_3.2.input.xml create mode 100644 tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.2_to_3.1.expected.xml create mode 100644 tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.2_to_3.1.input.xml diff --git a/application/loginwebpage.class.inc.php b/application/loginwebpage.class.inc.php index a7ecd12fb..5c5cb6eaf 100644 --- a/application/loginwebpage.class.inc.php +++ b/application/loginwebpage.class.inc.php @@ -82,7 +82,7 @@ class LoginWebPage extends NiceWebPage } protected static $m_sLoginFailedMessage = ''; - + public function __construct($sTitle = null) { if ($sTitle === null) { @@ -101,6 +101,15 @@ class LoginWebPage extends NiceWebPage $this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-awesome/css/all.min.css'); } + /** + * @inheritDoc + * @since 3.2.0 + */ + protected function GetFaviconAbsoluteUrl() + { + return Branding::GetLoginFavIconAbsoluteUrl(); + } + public static function SetLoginFailedMessage($sMessage) { self::$m_sLoginFailedMessage = $sMessage; diff --git a/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php index 5a6d12527..fc9b27604 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php +++ b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php @@ -52,6 +52,8 @@ class Basic extends AbstractConfiguration $aPortalConf = $this->ParseGlobalProperties($aPortalConf); // - Rectifying portal logo url $aPortalConf = $this->AppendLogoUri($aPortalConf); + // - Rectifying portal favicon url + $aPortalConf = $this->AppendFavIconUri($aPortalConf); } catch (Exception $oException) { @@ -71,10 +73,11 @@ class Basic extends AbstractConfiguration { $aPortalConf = array( 'properties' => array( - 'id' => $_ENV['PORTAL_ID'], - 'name' => 'Page:DefaultTitle', - 'logo' => Branding::GetPortalLogoAbsoluteUrl(), - 'themes' => array( + 'id' => $_ENV['PORTAL_ID'], + 'name' => 'Page:DefaultTitle', + 'logo' => Branding::GetPortalLogoAbsoluteUrl(), + 'favicon' => Branding::GetPortalFavIconAbsoluteUrl(), + 'themes' => array( 'bootstrap' => 'itop-portal-base/portal/public/css/bootstrap-theme-combodo.scss', 'portal' => 'itop-portal-base/portal/public/css/portal.scss', 'others' => array(), @@ -116,11 +119,8 @@ class Basic extends AbstractConfiguration case 'name': case 'urlmaker_class': case 'triggers_query': - $aPortalConf['properties'][$oPropertyNode->nodeName] = $oPropertyNode->GetText( - $aPortalConf['properties'][$oPropertyNode->nodeName] - ); - break; case 'logo': + case 'favicon': $aPortalConf['properties'][$oPropertyNode->nodeName] = $oPropertyNode->GetText( $aPortalConf['properties'][$oPropertyNode->nodeName] ); @@ -263,8 +263,7 @@ class Basic extends AbstractConfiguration private function AppendLogoUri(array $aPortalConf) { $sLogoUri = $aPortalConf['properties']['logo']; - if (!preg_match('/^http/', $sLogoUri)) - { + if (!preg_match('/^http/', $sLogoUri)) { // We prefix it with the server base url $sLogoUri = utils::GetAbsoluteUrlAppRoot().'env-'.utils::GetCurrentEnvironment().'/'.$sLogoUri; } @@ -272,4 +271,23 @@ class Basic extends AbstractConfiguration return $aPortalConf; } + + /** + * @param array $aPortalConf + * + * @return array + * @throws \Exception + * @since 3.2.0 N°3363 + */ + private function AppendFaviconUri(array $aPortalConf) + { + $sFaviconUri = $aPortalConf['properties']['favicon']; + if (!preg_match('/^http/', $sFaviconUri)) { + // We prefix it with the server base url + $sFaviconUri = utils::GetAbsoluteUrlAppRoot().'env-'.utils::GetCurrentEnvironment().'/'.$sFaviconUri; + } + $aPortalConf['properties']['favicon'] = $sFaviconUri; + + return $aPortalConf; + } } \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig index afb837ba7..62260ccf8 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig @@ -23,7 +23,7 @@ {% block pPageExtraMetas %} {% endblock %} {% block pPageTitle %}{% if sPageTitle is defined and sPageTitle is not null %}{{ sPageTitle }} - {{ constant('ITOP_APPLICATION_SHORT') }}{% else %}{{ 'Page:DefaultTitle'|dict_format(constant('ITOP_APPLICATION_SHORT')) }}{% endif %}{% endblock %} - + {% block pPageStylesheets %} {# First bootstrap core, lib themes, then bootstrap theme, portal adjustements #} diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php index 1ae74c20f..8d95d519a 100644 --- a/setup/compiler.class.inc.php +++ b/setup/compiler.class.inc.php @@ -3652,12 +3652,15 @@ EOF; $this->CompileFiles($oBrandingNode, $sTempTargetDir.'/branding', $sFinalTargetDir.'/branding', 'branding'); $aDataBranding = []; - $aLogosToCompile = [ - ['sNodeName' => 'login_logo', 'sTargetFile' => 'login-logo', 'sType' => Branding::ENUM_LOGO_TYPE_LOGIN_LOGO], - ['sNodeName' => 'main_logo', 'sTargetFile' => 'main-logo-full', 'sType' => Branding::ENUM_LOGO_TYPE_MAIN_LOGO_FULL], - ['sNodeName' => 'main_logo_compact', 'sTargetFile' => 'main-logo-compact', 'sType' => Branding::ENUM_LOGO_TYPE_MAIN_LOGO_COMPACT], - ['sNodeName' => 'portal_logo', 'sTargetFile' =>'portal-logo', 'sType' => Branding::ENUM_LOGO_TYPE_PORTAL_LOGO], - ]; + $aLogosToCompile = [ + ['sNodeName' => 'login_logo', 'sTargetFile' => 'login-logo', 'sType' => Branding::ENUM_LOGO_TYPE_LOGIN_LOGO], + ['sNodeName' => 'main_logo', 'sTargetFile' => 'main-logo-full', 'sType' => Branding::ENUM_LOGO_TYPE_MAIN_LOGO_FULL], + ['sNodeName' => 'main_logo_compact', 'sTargetFile' => 'main-logo-compact', 'sType' => Branding::ENUM_LOGO_TYPE_MAIN_LOGO_COMPACT], + ['sNodeName' => 'portal_logo', 'sTargetFile' => 'portal-logo', 'sType' => Branding::ENUM_LOGO_TYPE_PORTAL_LOGO], + ['sNodeName' => 'login_favicon', 'sTargetFile' => 'login_favicon', 'sType' => Branding::ENUM_LOGO_TYPE_LOGIN_FAVICON], + ['sNodeName' => 'main_favicon', 'sTargetFile' => 'main_favicon', 'sType' => Branding::ENUM_LOGO_TYPE_MAIN_FAVICON], + ['sNodeName' => 'portal_favicon', 'sTargetFile' => 'portal_favicon', 'sType' => Branding::ENUM_LOGO_TYPE_PORTAL_FAVICON], + ]; foreach ($aLogosToCompile as $aLogo) { $sLogo = $this->CompileLogo($oBrandingNode, $sTempTargetDir, $sFinalTargetDir, $aLogo['sNodeName'], $aLogo['sTargetFile']); if ($sLogo != null) { @@ -3677,7 +3680,7 @@ EOF; { SetupUtils::rrmdir($sTempTargetDir.'/branding/images'); } - + // Compile themes $this->CompileThemes($oBrandingNode, $sTempTargetDir); } diff --git a/setup/itopdesignformat.class.inc.php b/setup/itopdesignformat.class.inc.php index 5f38ea7dd..1614a6a1c 100644 --- a/setup/itopdesignformat.class.inc.php +++ b/setup/itopdesignformat.class.inc.php @@ -910,8 +910,8 @@ class iTopDesignFormat $oNodeList = $oXPath->query('/itop_design/branding/themes/theme[@id="test-red"]/variables/variable[@id="backoffice-environment-banner-background-color"]'); foreach ($oNodeList as $oNode) { $oNode->setAttribute('id', 'ibo-page-banner--background-color'); - } - + } + $oNodeList = $oXPath->query( '/itop_design/branding/themes/theme[@id="test-red"]/variables/variable[@id="backoffice-environment-banner-text-color"]'); foreach ($oNodeList as $oNode) { $oNode->setAttribute('id', 'ibo-page-banner--text-color'); @@ -1108,7 +1108,10 @@ class iTopDesignFormat */ protected function From32To31($oFactory) { - // Nothing for now... + // N°3363 - Add favicon in branding + $this->RemoveNodeFromXPath('/itop_design/branding/main_favicon'); + $this->RemoveNodeFromXPath('/itop_design/branding/portal_favicon'); + $this->RemoveNodeFromXPath('/itop_design/branding/login_favicon'); } /** diff --git a/sources/Application/Branding.php b/sources/Application/Branding.php index 609a873c3..b9c9cf061 100644 --- a/sources/Application/Branding.php +++ b/sources/Application/Branding.php @@ -19,6 +19,7 @@ namespace Combodo\iTop\Application; +use MetaModel; use utils; /** @@ -38,27 +39,52 @@ class Branding public const ENUM_LOGO_TYPE_PORTAL_LOGO = 'portal_logo'; /** @var string Logo used in the login pages */ public const ENUM_LOGO_TYPE_LOGIN_LOGO = 'login_logo'; + /** + * @var string Logo used in most pages (backoffice, ...) + * @since 3.2.0 N°3363 + */ + public const ENUM_LOGO_TYPE_MAIN_FAVICON = 'main_favicon'; + /** + * @var string Logo used in the end-users portals + * @since 3.2.0 N°3363 + */ + public const ENUM_LOGO_TYPE_PORTAL_FAVICON = 'portal_favicon'; + /** + * @var string Logo used in the login page + * @since 3.2.0 N°3363 + */ + public const ENUM_LOGO_TYPE_LOGIN_FAVICON = 'login_favicon'; /** @var string Default logo */ public const DEFAULT_LOGO_TYPE = self::ENUM_LOGO_TYPE_MAIN_LOGO_FULL; /** @var \string[][] Relative paths to the logos from the current environment folder */ public static $aLogoPaths = [ - self::ENUM_LOGO_TYPE_MAIN_LOGO_FULL => [ + self::ENUM_LOGO_TYPE_MAIN_LOGO_FULL => [ 'default' => 'images/itop-logo.png', ], self::ENUM_LOGO_TYPE_MAIN_LOGO_COMPACT => [ 'default' => 'images/itop-logo-square.png', ], - self::ENUM_LOGO_TYPE_PORTAL_LOGO => [ + self::ENUM_LOGO_TYPE_PORTAL_LOGO => [ 'default' => 'images/logo-itop-dark-bg.svg', ], - self::ENUM_LOGO_TYPE_LOGIN_LOGO => [ + self::ENUM_LOGO_TYPE_LOGIN_LOGO => [ 'default' => 'images/itop-logo-external.png', ], + self::ENUM_LOGO_TYPE_MAIN_FAVICON => [ + 'default' => 'images/favicon.ico', + ], + self::ENUM_LOGO_TYPE_PORTAL_FAVICON => [ + 'default' => 'images/favicon.ico', + ], + self::ENUM_LOGO_TYPE_LOGIN_FAVICON => [ + 'default' => 'images/favicon.ico', + ], ]; /** * Return url or path of logo defined by $sType + * * @param string $sType * @param string $sAppPath * @param ?string $sModulePath @@ -73,7 +99,7 @@ class Branding if (isset($aThemeParameters[$sType])) { $sCustomLogoPath = $aThemeParameters[$sType]; if (file_exists($sWorkingPath.$sCustomLogoPath)) { - return ($sModulePath??$sAppPath).$sCustomLogoPath; + return ($sModulePath ?? $sAppPath).$sCustomLogoPath; } } @@ -154,4 +180,39 @@ class Branding return static::GetLogoAbsoluteUrl(static::ENUM_LOGO_TYPE_LOGIN_LOGO); } + /** + * Return the absolute URL for thefavicon + * + * @return string + * @throws \Exception + * @since 3.2.0 N°3363 + */ + public static function GetMainFavIconAbsoluteUrl(): string + { + return static::GetLogoAbsoluteUrl(static::ENUM_LOGO_TYPE_MAIN_FAVICON); + } + + /** + * Return the absolute URL for thefavicon + * + * @return string + * @throws \Exception + * @since 3.2.0 N°3363 + */ + public static function GetPortalFavIconAbsoluteUrl(): string + { + return static::GetLogoAbsoluteUrl(static::ENUM_LOGO_TYPE_PORTAL_FAVICON); + } + + /** + * Return the absolute URL for thefavicon + * + * @return string + * @throws \Exception + * @since 3.2.0 N°3363 + */ + public static function GetLoginFavIconAbsoluteUrl(): string + { + return static::GetLogoAbsoluteUrl(static::ENUM_LOGO_TYPE_LOGIN_FAVICON); + } } \ No newline at end of file diff --git a/sources/Application/WebPage/UnauthenticatedWebPage.php b/sources/Application/WebPage/UnauthenticatedWebPage.php index 7f101a1a2..d81b0df7a 100644 --- a/sources/Application/WebPage/UnauthenticatedWebPage.php +++ b/sources/Application/WebPage/UnauthenticatedWebPage.php @@ -41,7 +41,7 @@ class UnauthenticatedWebPage extends NiceWebPage private $sContent; private $sPanelTitle; private $sPanelIcon; - + // TODO 3.0 Find a clever way to allow theme customization for unauthenticated webpages private $sCustomThemeUrl; @@ -288,4 +288,13 @@ class UnauthenticatedWebPage extends NiceWebPage $this->add_linked_stylesheet($this->sCustomThemeUrl); } } + + /** + * @inheritDoc + * @since 3.2.0 + */ + protected function GetFaviconAbsoluteUrl() + { + return Branding::GetLoginFavIconAbsoluteUrl(); + } } \ No newline at end of file diff --git a/sources/Application/WebPage/WebPage.php b/sources/Application/WebPage/WebPage.php index 281f23ac0..ee9ae8e82 100644 --- a/sources/Application/WebPage/WebPage.php +++ b/sources/Application/WebPage/WebPage.php @@ -6,6 +6,7 @@ namespace Combodo\iTop\Application\WebPage; +use Combodo\iTop\Application\Branding; use Combodo\iTop\Application\Helper\Session; use Combodo\iTop\Application\Helper\WebResourcesHelper; use Combodo\iTop\Application\TwigBase\Twig\TwigHelper; @@ -1761,17 +1762,13 @@ EOD } /** - * Return the absolute URL for the favicon - * - * @return string + * @return string Absolute URL for the favicon * @throws \Exception * @since 3.0.0 */ protected function GetFaviconAbsoluteUrl() { - // TODO 3.0.0: Make it a property so it can be changed programmatically - // TODO 3.0.0: How to set both dark/light mode favicons - return utils::GetAbsoluteUrlAppRoot().'images/favicon.ico'; + return Branding::GetMainFavIconAbsoluteUrl(); } /** @@ -1838,7 +1835,7 @@ EOD /** * @return bool - * + * * @since 3.2.0 */ public function GetAddJSDict(): bool @@ -1850,7 +1847,7 @@ EOD * @param bool $bAddJSDict * * @return $this - * + * * @since 3.2.0 */ public function SetAddJSDict(bool $bAddJSDict) diff --git a/tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.1_to_3.2.expected.xml b/tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.1_to_3.2.expected.xml new file mode 100644 index 000000000..da9755d33 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.1_to_3.2.expected.xml @@ -0,0 +1,3 @@ + + + diff --git a/tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.1_to_3.2.input.xml b/tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.1_to_3.2.input.xml new file mode 100644 index 000000000..bcd33d37d --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.1_to_3.2.input.xml @@ -0,0 +1,3 @@ + + + diff --git a/tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.2_to_3.1.expected.xml b/tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.2_to_3.1.expected.xml new file mode 100644 index 000000000..24015d8fa --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.2_to_3.1.expected.xml @@ -0,0 +1,4 @@ + + + + diff --git a/tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.2_to_3.1.input.xml b/tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.2_to_3.1.input.xml new file mode 100644 index 000000000..f36793519 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.2_to_3.1.input.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/iTopDesignFormatTest.php b/tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/iTopDesignFormatTest.php index a68ab04ea..7f8fe2bbe 100644 --- a/tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/iTopDesignFormatTest.php +++ b/tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/iTopDesignFormatTest.php @@ -143,6 +143,8 @@ class iTopDesignFormatTest extends ItopTestCase '3.0 to 1.7' => ['sXmlFileName' => '3.0_to_1.7'], '3.0 to 3.1' => ['sXmlFileName' => '3.0_to_3.1'], '3.1 to 3.0' => ['sXmlFileName' => '3.1_to_3.0'], + '3.1 to 3.2' => ['sXmlFileName' => '3.1_to_3.2'], + '3.2 to 3.1' => ['sXmlFileName' => '3.2_to_3.1'], 'Bug_4569' => ['sXmlFileName' => 'Bug_4569'], ]; }