N°3936 - Add user preference to choose backoffice theme

This commit is contained in:
Molkobain
2021-04-08 17:56:11 +02:00
parent 80d974f2b4
commit 8ccada40d1
6 changed files with 159 additions and 29 deletions

View File

@@ -58,22 +58,29 @@ class ThemeHandler
/**
* Return the ID of the theme currently defined in the config. file
*
* @deprecated 3.0.0, will be removed in 3.1, see N°3898
* @return string
*/
public static function GetCurrentThemeId()
{
try
{
if (is_null(MetaModel::GetConfig()))
{
static::GetCurrentUserThemeId();
}
/**
* @return string ID of the theme currently defined in the config. file, which applies to all users by default. If non defined, fallback on the default one.
* @since 3.0.0
*/
public static function GetApplicationThemeId(): string
{
try {
if (is_null(MetaModel::GetConfig())) {
throw new CoreException('no config');
}
$sThemeId = MetaModel::GetConfig()->Get('backoffice_default_theme');
}
catch(CoreException $oCompileException)
{
catch (CoreException $oCompileException) {
// Fallback on our default theme in case the config. is not available yet
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
$sThemeId = $aDefaultTheme['name'];
}
@@ -81,44 +88,115 @@ class ThemeHandler
}
/**
* Return the absolute path of the compiled theme folder.
*
* @return string ID of the theme to use for the current user as per they preferences. If non defined, fallback on the app. theme ID.
* @since 3.0.0
*/
public static function GetCurrentUserThemeId(): string
{
try {
$sThemeId = appUserPreferences::GetPref('backoffice_theme', null);
}
catch (Exception $oException) {
$sThemeId = null;
}
// Fallback on the app. theme
if (is_null($sThemeId)) {
$sThemeId = static::GetApplicationThemeId();
}
return $sThemeId;
}
/**
* @param string $sThemeId
*
* @return string
* @return string Label of the theme which is either a dict entry ('theme:<THEME_ID>') or the ID if no localized dict. entry found.
* @since 3.0.0
*/
public static function GetCompiledThemeFolderAbsolutePath($sThemeId)
public static function GetThemeLabel(string $sThemeId): string
{
return APPROOT.'env-'.utils::GetCurrentEnvironment().'/branding/themes/'.$sThemeId.'/';
$sDictEntryCode = 'theme:'.$sThemeId;
$sDictEntryValue = Dict::S('theme:'.$sThemeId);
return ($sDictEntryCode === $sDictEntryValue) ? $sThemeId : $sDictEntryValue;
}
/**
* @return array Associative array of <THEME_ID> => <THEME_LABEL>, ordered by labels
* @since 3.0.0
*/
public static function GetAvailableThemes(): array
{
$aThemes = [];
foreach (glob(static::GetCompiledThemesFolderAbsolutePath().'/*') as $sPath) {
if (is_dir($sPath)) {
$sThemeId = basename($sPath);
$sThemeLabel = static::GetThemeLabel($sThemeId);
$aThemes[$sThemeId] = $sThemeLabel;
}
}
asort($aThemes);
return $aThemes;
}
/**
* @param string $sThemeId
*
* @return bool True if $sThemeId is a valid theme that can be used.
* @since 3.0.0
*/
public static function IsValidTheme(string $sThemeId): bool
{
return array_key_exists($sThemeId, static::GetAvailableThemes());
}
/**
* @return string Absolute path to the folder containing all the compiled themes
* @since 3.0.0
*/
public static function GetCompiledThemesFolderAbsolutePath(): string
{
return APPROOT.'env-'.utils::GetCurrentEnvironment().'/branding/themes/';
}
/**
* @param string $sThemeId
*
* @return string Absolute path to the folder containing the $sThemeId theme
*/
public static function GetCompiledThemeFolderAbsolutePath(string $sThemeId): string
{
return static::GetCompiledThemesFolderAbsolutePath().$sThemeId.'/';
}
/**
* Return the absolute URL for the current theme CSS file
*
* @return string
* @throws \Exception
*/
public static function GetCurrentThemeUrl()
public static function GetCurrentThemeUrl(): string
{
try
{
try {
// Try to compile theme defined in the configuration
$sThemeId = static::GetCurrentThemeId();
$sThemeId = static::GetCurrentUserThemeId();
static::CompileTheme($sThemeId);
}
catch(CoreException $oCompileException)
{
catch (CoreException $oCompileException) {
// Fallback on our default theme (should always be compilable) in case the previous theme doesn't exists
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
$sThemeId = $aDefaultTheme['name'];
$sDefaultThemeDirPath = static::GetCompiledThemeFolderAbsolutePath($sThemeId);
// Create our theme dir if it doesn't exist (XML theme node removed, renamed etc..)
if(!is_dir($sDefaultThemeDirPath))
{
if (!is_dir($sDefaultThemeDirPath)) {
SetupUtils::builddir($sDefaultThemeDirPath);
}
static::CompileTheme($sThemeId, false, "", $aDefaultTheme['parameters']);
}

View File

@@ -371,3 +371,9 @@ Dict::Add('EN US', 'English', 'English', array(
'Person:personal_info' => 'Personal information',
'Person:notifiy' => 'Notification',
));
// Themes
Dict::Add('EN US', 'English', 'English', array(
'theme:fullmoon' => 'Full moon 🌕',
'theme:test-red' => 'Test instance (Red)',
));

View File

@@ -381,3 +381,9 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Person:personal_info' => 'Informations personnelles',
'Person:notifiy' => 'Notification',
));
// Themes
Dict::Add('EN US', 'English', 'English', array(
'theme:fullmoon' => 'Full moon 🌕',
'theme:test-red' => 'Instance de test (Rouge)',
));

View File

@@ -21,6 +21,9 @@
Dict::Add('EN US', 'English', 'English', array(
'UI:Preferences:Title' => 'Preferences',
'UI:Preferences:UserInterface:Title' => 'User interface',
'UI:Preferences:General:Title' => 'General',
'UI:Preferences:General:Theme' => 'Theme',
'UI:Preferences:General:Theme:DefaultThemeLabel' => '%1$s (default)',
'UI:Preferences:Lists:Title' => 'Lists',
'UI:Preferences:RichText:Title' => 'Rich text editor',
'UI:Preferences:RichText:ToolbarState' => 'Toolbar default state',

View File

@@ -21,6 +21,9 @@
Dict::Add('FR FR', 'French', 'Français', array(
'UI:Preferences:Title' => 'Préférences',
'UI:Preferences:UserInterface:Title' => 'Interface utilisateur',
'UI:Preferences:General:Title' => 'Général',
'UI:Preferences:General:Theme' => 'Thême',
'UI:Preferences:General:Theme:DefaultThemeLabel' => '%1$s (défaut)',
'UI:Preferences:Lists:Title' => 'Listes',
'UI:Preferences:RichText:Title' => 'Éditeur texte riche',
'UI:Preferences:RichText:ToolbarState' => 'Affichage par défaut de la barre d\'outil',

View File

@@ -76,10 +76,11 @@ function DisplayPreferences($oP)
$oUISubmitButton = ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('UI:Button:Apply'), 'operation', 'apply_user_interface', true);
$oUIToolbar->AddSubBlock($oUISubmitButton);
// Language
$oLanguageFieldset = FieldSetUIBlockFactory::MakeStandard(Dict::S('UI:FavoriteLanguage'), 'ibo-fieldset-for-language-preferences');
$oLanguageFieldset->AddSubBlock(GetLanguageFieldBlock());
$oFirstColumn->AddSubBlock($oLanguageFieldset);
// General
$oGeneralFieldset = FieldSetUIBlockFactory::MakeStandard(Dict::S('UI:Preferences:General:Title'), 'ibo-fieldset-for-language-preferences');
$oGeneralFieldset->AddSubBlock(GetLanguageFieldBlock());
$oGeneralFieldset->AddSubBlock(GetThemeFieldBlock());
$oFirstColumn->AddSubBlock($oGeneralFieldset);
// Lists
$oListsFieldset = FieldSetUIBlockFactory::MakeStandard(Dict::S('UI:Preferences:Lists:Title'), 'ibo-fieldset-for-lists-preferences');
@@ -467,7 +468,7 @@ function GetLanguageFieldBlock(): iUIBlock
}
ksort($aSortedLanguages);
$oSelect = SelectUIBlockFactory::MakeForSelectWithLabel('language', Dict::S('UI:Favorites:SelectYourLanguage'));
$oSelect = SelectUIBlockFactory::MakeForSelectWithLabel('language', Dict::S('UI:FavoriteLanguage'));
/** @var \Combodo\iTop\Application\UI\Base\Component\Input\Select $oSelectInput */
$oSelectInput = $oSelect->GetInput();
foreach ($aSortedLanguages as $sCode) {
@@ -478,6 +479,33 @@ function GetLanguageFieldBlock(): iUIBlock
return $oSelect;
}
/**
* @return \Combodo\iTop\Application\UI\Base\iUIBlock
* @since 3.0.0
*/
function GetThemeFieldBlock(): iUIBlock
{
$aAvailableThemes = ThemeHandler::GetAvailableThemes();
$oSelect = SelectUIBlockFactory::MakeForSelectWithLabel('theme', Dict::S('UI:Preferences:General:Theme'));
/** @var \Combodo\iTop\Application\UI\Base\Component\Input\Select $oSelectInput */
$oSelectInput = $oSelect->GetInput();
foreach ($aAvailableThemes as $sCode => $sLabel) {
if (MetaModel::GetConfig()->Get('demo_mode') && ($sCode !== ThemeHandler::GetApplicationThemeId())) {
// Demo mode: only the current app. theme is listed in the available choices
continue;
}
$bSelected = ($sCode === ThemeHandler::GetCurrentUserThemeId());
if (true === $bSelected) {
$sLabel = Dict::Format('UI:Preferences:General:Theme:DefaultThemeLabel', $sLabel);
}
$oSelectInput->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sCode, $sLabel, $bSelected));
}
return $oSelect;
}
/**
* @return \Combodo\iTop\Application\UI\Base\iUIBlock
* @throws \CoreException
@@ -689,6 +717,12 @@ try {
$oUser->DBUpdate();
utils::PopArchiveMode();
// Theme
$sThemeId = utils::ReadParam('theme', '');
if (!empty($sThemeId) && ThemeHandler::IsValidTheme($sThemeId)) {
appUserPreferences::SetPref('backoffice_theme', $sThemeId);
}
// List
$iDefaultPageSize = (int)utils::ReadParam('default_page_size', -1);
if ($iDefaultPageSize > 0) {