# -- # Copyright (C) 2001-2019 OTRS AG, https://otrs.com/ # -- # This software comes with ABSOLUTELY NO WARRANTY. For details, see # the enclosed file COPYING for license information (GPL). If you # did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt. # -- package Kernel::Output::HTML::Layout::Loader; use strict; use warnings; use File::stat; use Digest::MD5; use Kernel::Language qw(Translatable); our $ObjectManagerDisabled = 1; =head1 NAME Kernel::Output::HTML::Layout::Loader - CSS/JavaScript =head1 DESCRIPTION All valid functions. =head1 PUBLIC INTERFACE =head2 LoaderCreateAgentCSSCalls() Generate the minified CSS files and the tags referencing them, taking a list from the Loader::Agent::CommonCSS config item. $LayoutObject->LoaderCreateAgentCSSCalls( Skin => 'MySkin', # optional, if not provided skin is the configured by default ); =cut sub LoaderCreateAgentCSSCalls { my ( $Self, %Param ) = @_; # get config object my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); # get host based default skin configuration my $SkinSelectedHostBased; my $DefaultSkinHostBased = $ConfigObject->Get('Loader::Agent::DefaultSelectedSkin::HostBased'); if ( $DefaultSkinHostBased && $ENV{HTTP_HOST} ) { REGEXP: for my $RegExp ( sort keys %{$DefaultSkinHostBased} ) { # do not use empty regexp or skin directories next REGEXP if !$RegExp; next REGEXP if !$DefaultSkinHostBased->{$RegExp}; # check if regexp is matching if ( $ENV{HTTP_HOST} =~ /$RegExp/i ) { $SkinSelectedHostBased = $DefaultSkinHostBased->{$RegExp}; } } } # determine skin # 1. use UserSkin setting from Agent preferences, if available # 2. use HostBased skin setting, if available # 3. use default skin from configuration my $SkinSelected = $Self->{'UserSkin'}; # check if the skin is valid my $SkinValid = 0; if ($SkinSelected) { $SkinValid = $Self->SkinValidate( SkinType => 'Agent', Skin => $SkinSelected, ); } if ( !$SkinValid ) { $SkinSelected = $SkinSelectedHostBased || $ConfigObject->Get('Loader::Agent::DefaultSelectedSkin') || 'default'; } # save selected skin $Self->{SkinSelected} = $SkinSelected; my $SkinHome = $ConfigObject->Get('Home') . '/var/httpd/htdocs/skins'; my $DoMinify = $ConfigObject->Get('Loader::Enabled::CSS'); my $ToolbarModuleSettings = $ConfigObject->Get('Frontend::ToolBarModule'); my $CustomerUserItemSettings = $ConfigObject->Get('Frontend::CustomerUser::Item'); { my @FileList; # get global css my $CommonCSSList = $ConfigObject->Get('Loader::Agent::CommonCSS'); for my $Key ( sort keys %{$CommonCSSList} ) { push @FileList, @{ $CommonCSSList->{$Key} }; } # get toolbar module css for my $Key ( sort keys %{$ToolbarModuleSettings} ) { if ( $ToolbarModuleSettings->{$Key}->{CSS} ) { push @FileList, $ToolbarModuleSettings->{$Key}->{CSS}; } } # get customer user item css for my $Key ( sort keys %{$CustomerUserItemSettings} ) { if ( $CustomerUserItemSettings->{$Key}->{CSS} ) { push @FileList, $CustomerUserItemSettings->{$Key}->{CSS}; } } $Self->_HandleCSSList( List => \@FileList, DoMinify => $DoMinify, BlockName => 'CommonCSS', SkinHome => $SkinHome, SkinType => 'Agent', Skin => $SkinSelected, ); } # now handle module specific CSS my $LoaderAction = $Self->{Action} || 'Login'; $LoaderAction = 'Login' if ( $LoaderAction eq 'Logout' ); { my $Setting = $ConfigObject->Get("Loader::Module::$LoaderAction") || {}; my @FileList; MODULE: for my $Module ( sort keys %{$Setting} ) { next MODULE if ref $Setting->{$Module}->{CSS} ne 'ARRAY'; @FileList = ( @FileList, @{ $Setting->{$Module}->{CSS} || [] } ); } $Self->_HandleCSSList( List => \@FileList, DoMinify => $DoMinify, BlockName => 'ModuleCSS', SkinHome => $SkinHome, SkinType => 'Agent', Skin => $SkinSelected, ); } # handle the responsive CSS { my @FileList; my $ResponsiveCSSList = $ConfigObject->Get('Loader::Agent::ResponsiveCSS'); for my $Key ( sort keys %{$ResponsiveCSSList} ) { push @FileList, @{ $ResponsiveCSSList->{$Key} }; } $Self->_HandleCSSList( List => \@FileList, DoMinify => $DoMinify, BlockName => 'ResponsiveCSS', SkinHome => $SkinHome, SkinType => 'Agent', Skin => $SkinSelected, ); } #print STDERR "Time: " . Time::HiRes::tv_interval([$t0]); return 1; } =head2 LoaderCreateAgentJSCalls() Generate the minified JavaScript files and the tags referencing them, taking a list from the Loader::Agent::CommonJS config item. $LayoutObject->LoaderCreateAgentJSCalls(); =cut sub LoaderCreateAgentJSCalls { my ( $Self, %Param ) = @_; #use Time::HiRes; #my $t0 = Time::HiRes::gettimeofday(); # get config object my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); my $JSHome = $ConfigObject->Get('Home') . '/var/httpd/htdocs/js'; my $DoMinify = $ConfigObject->Get('Loader::Enabled::JS'); { my @FileList; # get global js my $CommonJSList = $ConfigObject->Get('Loader::Agent::CommonJS'); KEY: for my $Key ( sort keys %{$CommonJSList} ) { next KEY if $Key eq '100-CKEditor' && !$ConfigObject->Get('Frontend::RichText'); push @FileList, @{ $CommonJSList->{$Key} }; } # get toolbar module js my $ToolbarModuleSettings = $ConfigObject->Get('Frontend::ToolBarModule'); for my $Key ( sort keys %{$ToolbarModuleSettings} ) { if ( $ToolbarModuleSettings->{$Key}->{JavaScript} ) { push @FileList, $ToolbarModuleSettings->{$Key}->{JavaScript}; } } $Self->_HandleJSList( List => \@FileList, DoMinify => $DoMinify, BlockName => 'CommonJS', JSHome => $JSHome, ); } # now handle module specific JavaScript { my $LoaderAction = $Self->{Action} || 'Login'; $LoaderAction = 'Login' if ( $LoaderAction eq 'Logout' ); my $Setting = $ConfigObject->Get("Loader::Module::$LoaderAction") || {}; my @FileList; MODULE: for my $Module ( sort keys %{$Setting} ) { next MODULE if ref $Setting->{$Module}->{JavaScript} ne 'ARRAY'; @FileList = ( @FileList, @{ $Setting->{$Module}->{JavaScript} || [] } ); } $Self->_HandleJSList( List => \@FileList, DoMinify => $DoMinify, BlockName => 'ModuleJS', JSHome => $JSHome, ); } return 1; } =head2 LoaderCreateJavaScriptTemplateData() Generate a minified file for the template data that needs to be present on the client side for JavaScript based templates. $LayoutObject->LoaderCreateJavaScriptTemplateData(); =cut sub LoaderCreateJavaScriptTemplateData { my ( $Self, %Param ) = @_; # get config object my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); # load theme my $Theme = $Self->{UserTheme} || $ConfigObject->Get('DefaultTheme') || Translatable('Standard'); # force a theme based on host name my $DefaultThemeHostBased = $ConfigObject->Get('DefaultTheme::HostBased'); if ( $DefaultThemeHostBased && $ENV{HTTP_HOST} ) { THEME: for my $RegExp ( sort keys %{$DefaultThemeHostBased} ) { # do not use empty regexp or theme directories next THEME if !$RegExp; next THEME if $RegExp eq ''; next THEME if !$DefaultThemeHostBased->{$RegExp}; # check if regexp is matching if ( $ENV{HTTP_HOST} =~ /$RegExp/i ) { $Theme = $DefaultThemeHostBased->{$RegExp}; } } } # locate template files my $JSStandardTemplateDir = $ConfigObject->Get('TemplateDir') . '/JavaScript/Templates/' . 'Standard'; my $JSTemplateDir = $ConfigObject->Get('TemplateDir') . '/JavaScript/Templates/' . $Theme; # Check if 'Standard' fallback exists if ( !-e $JSStandardTemplateDir ) { $Self->FatalDie( Message => "No existing template directory found ('$JSTemplateDir')! Check your Home in Kernel/Config.pm." ); } if ( !-e $JSTemplateDir ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "No existing template directory found ('$JSTemplateDir')!. Default theme used instead.", ); # Set TemplateDir to 'Standard' as a fallback. $Theme = 'Standard'; $JSTemplateDir = $JSStandardTemplateDir; } my $JSCustomStandardTemplateDir = $ConfigObject->Get('CustomTemplateDir') . '/JavaScript/Templates/' . 'Standard'; my $JSCustomTemplateDir = $ConfigObject->Get('CustomTemplateDir') . '/JavaScript/Templates/' . $Theme; my @TemplateFolders = ( "$JSCustomTemplateDir", "$JSCustomStandardTemplateDir", "$JSTemplateDir", "$JSStandardTemplateDir", ); my $JSHome = $ConfigObject->Get('Home') . '/var/httpd/htdocs/js'; my $TargetFilenamePrefix = "TemplateJS"; my $TemplateChecksum; my $MainObject = $Kernel::OM->Get('Kernel::System::Main'); my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); my $CacheType = 'Loader'; my $CacheKey = "LoaderCreateJavaScriptTemplateData:${Theme}:" . $ConfigObject->ConfigChecksum(); # Even getting the list of files recursively from the directories is expensive, # so cache the checksum to avoid that. $TemplateChecksum = $CacheObject->Get( Type => $CacheType, Key => $CacheKey, ); if ( !$TemplateChecksum ) { my %ChecksumData; TEMPLATEFOLDER: for my $TemplateFolder (@TemplateFolders) { next TEMPLATEFOLDER if !-e $TemplateFolder; # get main object my @Templates = $MainObject->DirectoryRead( Directory => $TemplateFolder, Filter => '*.tmpl', Recursive => 1, ); TEMPLATE: for my $Template ( sort @Templates ) { next TEMPLATE if !-e $Template; my $Key = $Template; $Key =~ s/^$TemplateFolder\///xmsg; $Key =~ s/\.(\w+)\.tmpl$//xmsg; # check if a template with this name does already exist next TEMPLATE if $ChecksumData{$Key}; # get file metadata my $Stat = stat($Template); if ( !$Stat ) { print STDERR "Error: cannot stat file '$Template': $!"; next TEMPLATE; } $ChecksumData{$Key} = $Template . $Stat->mtime(); } } # generate a checksum only of the actual used files for my $Checksum ( sort keys %ChecksumData ) { $TemplateChecksum .= $ChecksumData{$Checksum}; } $TemplateChecksum = Digest::MD5::md5_hex($TemplateChecksum); $CacheObject->Set( Type => $CacheType, Key => $CacheKey, TTL => 60 * 60 * 24, Value => $TemplateChecksum, ); } # Check if cache already exists. if ( -e "$JSHome/js-cache/${TargetFilenamePrefix}_$TemplateChecksum.js" ) { $Self->Block( Name => 'CommonJS', Data => { JSDirectory => 'js-cache/', Filename => "${TargetFilenamePrefix}_$TemplateChecksum.js", }, ); return 1; } # if it doesnt exist, go through the folders and get the template content my %TemplateData; TEMPLATEFOLDER: for my $TemplateFolder (@TemplateFolders) { next TEMPLATEFOLDER if !-e $TemplateFolder; # get main object my @Templates = $MainObject->DirectoryRead( Directory => $TemplateFolder, Filter => '*.tmpl', Recursive => 1, ); TEMPLATE: for my $Template ( sort @Templates ) { next TEMPLATE if !-e $Template; my $Key = $Template; $Key =~ s/^$TemplateFolder\///xmsg; $Key =~ s/\.(\w+)\.tmpl$//xmsg; # check if a template with this name does already exist next TEMPLATE if $TemplateData{$Key}; my $TemplateContent = ${ $MainObject->FileRead( Location => $Template, Result => 'SCALAR', ) }; # Remove DTL-style comments (lines starting with #) $TemplateContent =~ s/^#.*\n//gm; $TemplateData{$Key} = $TemplateContent; } } my $TemplateDataJSON = $Kernel::OM->Get('Kernel::System::JSON')->Encode( Data => \%TemplateData, Pretty => 0, ); my $Content = <<"EOF"; // The content of this file is automatically generated, do not edit. Core.Template.Load($TemplateDataJSON); EOF my $MinifiedFile = $Kernel::OM->Get('Kernel::System::Loader')->MinifyFiles( Checksum => $TemplateChecksum, Content => $Content, Type => 'JavaScript', TargetDirectory => "$JSHome/js-cache/", TargetFilenamePrefix => $TargetFilenamePrefix, ); $Self->Block( Name => 'CommonJS', Data => { JSDirectory => 'js-cache/', Filename => $MinifiedFile, }, ); return 1; } =head2 LoaderCreateJavaScriptTranslationData() Generate a minified file for the translation data that needs to be present on the client side for JavaScript based translations. $LayoutObject->LoaderCreateJavaScriptTranslationData(); =cut sub LoaderCreateJavaScriptTranslationData { my ( $Self, %Param ) = @_; # get config object my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); my $JSHome = $ConfigObject->Get('Home') . '/var/httpd/htdocs/js'; my $UserLanguage = $Self->{UserLanguage}; my $LanguageObject = $Self->{LanguageObject}; my $LanguageChecksum = $LanguageObject->LanguageChecksum(); my $TargetFilenamePrefix = "TranslationJS_$UserLanguage"; # Check if cache already exists. if ( -e "$JSHome/js-cache/${TargetFilenamePrefix}_$LanguageChecksum.js" ) { $Self->Block( Name => 'CommonJS', Data => { JSDirectory => 'js-cache/', Filename => "${TargetFilenamePrefix}_$LanguageChecksum.js", }, ); return 1; } # Now create translation data for JavaScript. my %TranslationData; STRING: for my $String ( @{ $LanguageObject->{JavaScriptStrings} // [] } ) { next STRING if $TranslationData{$String}; $TranslationData{$String} = $LanguageObject->{Translation}->{$String}; } my %LanguageMetaData = ( LanguageCode => $UserLanguage, DateFormat => $LanguageObject->{DateFormat}, DateFormatLong => $LanguageObject->{DateFormatLong}, DateFormatShort => $LanguageObject->{DateFormatShort}, DateInputFormat => $LanguageObject->{DateInputFormat}, DateInputFormatLong => $LanguageObject->{DateInputFormatLong}, Completeness => $LanguageObject->{Completeness}, Separator => $LanguageObject->{Separator}, DecimalSeparator => $LanguageObject->{DecimalSeparator}, ); my $LanguageMetaDataJSON = $Kernel::OM->Get('Kernel::System::JSON')->Encode( Data => \%LanguageMetaData, SortKeys => 1, Pretty => 0, ); my $TranslationDataJSON = $Kernel::OM->Get('Kernel::System::JSON')->Encode( Data => \%TranslationData, SortKeys => 1, Pretty => 0, ); my $Content = <<"EOF"; // The content of this file is automatically generated, do not edit. Core.Language.Load($LanguageMetaDataJSON, $TranslationDataJSON); EOF my $MinifiedFile = $Kernel::OM->Get('Kernel::System::Loader')->MinifyFiles( Checksum => $LanguageChecksum, Content => $Content, Type => 'JavaScript', TargetDirectory => "$JSHome/js-cache/", TargetFilenamePrefix => $TargetFilenamePrefix, ); $Self->Block( Name => 'CommonJS', Data => { JSDirectory => 'js-cache/', Filename => $MinifiedFile, }, ); return 1; } =head2 LoaderCreateCustomerCSSCalls() Generate the minified CSS files and the tags referencing them, taking a list from the Loader::Customer::CommonCSS config item. $LayoutObject->LoaderCreateCustomerCSSCalls(); =cut sub LoaderCreateCustomerCSSCalls { my ( $Self, %Param ) = @_; # get config object my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); my $SkinSelected = $ConfigObject->Get('Loader::Customer::SelectedSkin') || 'default'; # force a skin based on host name my $DefaultSkinHostBased = $ConfigObject->Get('Loader::Customer::SelectedSkin::HostBased'); if ( $DefaultSkinHostBased && $ENV{HTTP_HOST} ) { REGEXP: for my $RegExp ( sort keys %{$DefaultSkinHostBased} ) { # do not use empty regexp or skin directories next REGEXP if !$RegExp; next REGEXP if !$DefaultSkinHostBased->{$RegExp}; # check if regexp is matching if ( $ENV{HTTP_HOST} =~ /$RegExp/i ) { $SkinSelected = $DefaultSkinHostBased->{$RegExp}; } } } my $SkinHome = $ConfigObject->Get('Home') . '/var/httpd/htdocs/skins'; my $DoMinify = $ConfigObject->Get('Loader::Enabled::CSS'); { my $CommonCSSList = $ConfigObject->Get('Loader::Customer::CommonCSS'); my @FileList; for my $Key ( sort keys %{$CommonCSSList} ) { push @FileList, @{ $CommonCSSList->{$Key} }; } $Self->_HandleCSSList( List => \@FileList, DoMinify => $DoMinify, BlockName => 'CommonCSS', SkinHome => $SkinHome, SkinType => 'Customer', Skin => $SkinSelected, ); } # now handle module specific CSS my $LoaderAction = $Self->{Action} || 'Login'; $LoaderAction = 'Login' if ( $LoaderAction eq 'Logout' ); { my $Setting = $ConfigObject->Get("Loader::Module::$LoaderAction") || {}; my @FileList; MODULE: for my $Module ( sort keys %{$Setting} ) { next MODULE if ref $Setting->{$Module}->{CSS} ne 'ARRAY'; @FileList = ( @FileList, @{ $Setting->{$Module}->{CSS} || [] } ); } $Self->_HandleCSSList( List => \@FileList, DoMinify => $DoMinify, BlockName => 'ModuleCSS', SkinHome => $SkinHome, SkinType => 'Customer', Skin => $SkinSelected, ); } # handle the responsive CSS { my @FileList; my $ResponsiveCSSList = $ConfigObject->Get('Loader::Customer::ResponsiveCSS'); for my $Key ( sort keys %{$ResponsiveCSSList} ) { push @FileList, @{ $ResponsiveCSSList->{$Key} }; } $Self->_HandleCSSList( List => \@FileList, DoMinify => $DoMinify, BlockName => 'ResponsiveCSS', SkinHome => $SkinHome, SkinType => 'Customer', Skin => $SkinSelected, ); } #print STDERR "Time: " . Time::HiRes::tv_interval([$t0]); return 1; } =head2 LoaderCreateCustomerJSCalls() Generate the minified JavaScript files and the tags referencing them, taking a list from the Loader::Customer::CommonJS config item. $LayoutObject->LoaderCreateCustomerJSCalls(); =cut sub LoaderCreateCustomerJSCalls { my ( $Self, %Param ) = @_; #use Time::HiRes; #my $t0 = Time::HiRes::gettimeofday(); # get config object my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); my $JSHome = $ConfigObject->Get('Home') . '/var/httpd/htdocs/js'; my $DoMinify = $ConfigObject->Get('Loader::Enabled::JS'); { my $CommonJSList = $ConfigObject->Get('Loader::Customer::CommonJS'); my @FileList; KEY: for my $Key ( sort keys %{$CommonJSList} ) { next KEY if $Key eq '100-CKEditor' && !$ConfigObject->Get('Frontend::RichText'); push @FileList, @{ $CommonJSList->{$Key} }; } $Self->_HandleJSList( List => \@FileList, DoMinify => $DoMinify, BlockName => 'CommonJS', JSHome => $JSHome, ); } # now handle module specific JS { my $LoaderAction = $Self->{Action} || 'CustomerLogin'; $LoaderAction = 'CustomerLogin' if ( $LoaderAction eq 'Logout' ); my $Setting = $ConfigObject->Get("Loader::Module::$LoaderAction") || {}; my @FileList; MODULE: for my $Module ( sort keys %{$Setting} ) { next MODULE if ref $Setting->{$Module}->{JavaScript} ne 'ARRAY'; @FileList = ( @FileList, @{ $Setting->{$Module}->{JavaScript} || [] } ); } $Self->_HandleJSList( List => \@FileList, DoMinify => $DoMinify, BlockName => 'ModuleJS', JSHome => $JSHome, ); } #print STDERR "Time: " . Time::HiRes::tv_interval([$t0]); return; } sub _HandleCSSList { my ( $Self, %Param ) = @_; my @Skins = ('default'); # validating selected custom skin, if any if ( $Param{Skin} && $Param{Skin} ne 'default' && $Self->SkinValidate(%Param) ) { push @Skins, $Param{Skin}; } #load default css files for my $Skin (@Skins) { my @FileList; CSSFILE: for my $CSSFile ( @{ $Param{List} } ) { my $SkinFile = "$Param{SkinHome}/$Param{SkinType}/$Skin/css/$CSSFile"; next CSSFILE if ( !-e $SkinFile ); if ( $Param{DoMinify} ) { push @FileList, $SkinFile; } else { $Self->Block( Name => $Param{BlockName}, Data => { Skin => $Skin, CSSDirectory => 'css', Filename => $CSSFile, }, ); } } if ( $Param{DoMinify} && @FileList ) { my $MinifiedFile = $Kernel::OM->Get('Kernel::System::Loader')->MinifyFiles( List => \@FileList, Type => 'CSS', TargetDirectory => "$Param{SkinHome}/$Param{SkinType}/$Skin/css-cache/", TargetFilenamePrefix => $Param{BlockName}, ); $Self->Block( Name => $Param{BlockName}, Data => { Skin => $Skin, CSSDirectory => 'css-cache', Filename => $MinifiedFile, }, ); } } return 1; } sub _HandleJSList { my ( $Self, %Param ) = @_; my $Content = $Param{Content}; return if !$Param{List} && !$Content; my %UsedFiles; my @FileList; JSFILE: for my $JSFile ( @{ $Param{List} // [] } ) { next JSFILE if $UsedFiles{$JSFile}; if ( $Param{DoMinify} ) { push @FileList, "$Param{JSHome}/$JSFile"; } else { $Self->Block( Name => $Param{BlockName}, Data => { JSDirectory => '', Filename => $JSFile, }, ); } # Save it for checking duplicates. $UsedFiles{$JSFile} = 1; } return 1 if $Param{List} && !@FileList; if ( $Param{DoMinify} ) { my $MinifiedFile; if (@FileList) { $MinifiedFile = $Kernel::OM->Get('Kernel::System::Loader')->MinifyFiles( List => \@FileList, Type => 'JavaScript', TargetDirectory => "$Param{JSHome}/js-cache/", TargetFilenamePrefix => $Param{FilenamePrefix} // $Param{BlockName}, ); } else { $MinifiedFile = $Kernel::OM->Get('Kernel::System::Loader')->MinifyFiles( Content => $Content, Type => 'JavaScript', TargetDirectory => "$Param{JSHome}/js-cache/", TargetFilenamePrefix => $Param{FilenamePrefix} // $Param{BlockName}, ); } $Self->Block( Name => $Param{BlockName}, Data => { JSDirectory => 'js-cache/', Filename => $MinifiedFile, }, ); } return 1; } =head2 SkinValidate() Returns 1 if skin is available for Agent or Customer frontends and 0 if not. my $SkinIsValid = $LayoutObject->SkinValidate( UserType => 'Agent' # Agent or Customer, Skin => 'ExampleSkin', ); =cut sub SkinValidate { my ( $Self, %Param ) = @_; for my $Needed ( 'SkinType', 'Skin' ) { if ( !$Param{$Needed} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Message => "Needed param: $Needed!", Priority => 'error', ); return; } } if ( exists $Self->{SkinValidateCache}->{ $Param{SkinType} . '::' . $Param{Skin} } ) { return $Self->{SkinValidateCache}->{ $Param{SkinType} . '::' . $Param{Skin} }; } # get config object my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); my $SkinType = $Param{SkinType}; my $PossibleSkins = $ConfigObject->Get("Loader::${SkinType}::Skin") || {}; my $Home = $ConfigObject->Get('Home'); my %ActiveSkins; # prepare the list of active skins for my $PossibleSkin ( values %{$PossibleSkins} ) { if ( $PossibleSkin->{InternalName} eq $Param{Skin} ) { my $SkinDir = $Home . "/var/httpd/htdocs/skins/$SkinType/" . $PossibleSkin->{InternalName}; if ( -d $SkinDir ) { $Self->{SkinValidateCache}->{ $Param{SkinType} . '::' . $Param{Skin} } = 1; return 1; } } } $Self->{SkinValidateCache}->{ $Param{SkinType} . '::' . $Param{Skin} } = undef; return; } 1; =head1 TERMS AND CONDITIONS This software is part of the OTRS project (L). This software comes with ABSOLUTELY NO WARRANTY. For details, see the enclosed file COPYING for license information (GPL). If you did not receive this file, see L. =cut