# -- # 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::Modules::AdminPackageManager; ## nofilter(TidyAll::Plugin::OTRS::Perl::DBObject) use strict; use warnings; use Kernel::System::VariableCheck qw(:all); use Kernel::Language qw(Translatable); use parent('Kernel::System::AsynchronousExecutor'); our $ObjectManagerDisabled = 1; sub new { my ( $Type, %Param ) = @_; # allocate new hash for object my $Self = {%Param}; bless( $Self, $Type ); # check if cloud services are disabled $Self->{CloudServicesDisabled} = $Kernel::OM->Get('Kernel::Config')->Get('CloudServices::Disabled') || 0; return $Self; } sub Run { my ( $Self, %Param ) = @_; my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); my $Source = $Self->{UserRepository} || ''; my %Errors; # ------------------------------------------------------------ # # check mod perl version and Apache::Reload # ------------------------------------------------------------ # if ( exists $ENV{MOD_PERL} ) { if ( defined $mod_perl::VERSION ) { ## no critic if ( $mod_perl::VERSION >= 1.99 ) { ## no critic # check if Apache::Reload is loaded my $ApacheReload = 0; for my $Module ( sort keys %INC ) { $Module =~ s/\//::/g; $Module =~ s/\.pm$//g; if ( $Module eq 'Apache::Reload' || $Module eq 'Apache2::Reload' ) { $ApacheReload = 1; } } if ( !$ApacheReload ) { return $LayoutObject->ErrorScreen( Message => Translatable( 'Sorry, Apache::Reload is needed as PerlModule and PerlInitHandler in Apache config file. See also scripts/apache2-httpd.include.conf. Alternatively, you can use the command line tool bin/otrs.Console.pl to install packages!' ), ); } } } } my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); # secure mode message (don't allow this action until secure mode is enabled) if ( !$ConfigObject->Get('SecureMode') ) { return $LayoutObject->SecureMode(); } my $PackageObject = $Kernel::OM->Get('Kernel::System::Package'); my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); my $MainObject = $Kernel::OM->Get('Kernel::System::Main'); # ------------------------------------------------------------ # # view diff file # ------------------------------------------------------------ # if ( $Self->{Subaction} eq 'ViewDiff' ) { my $Name = $ParamObject->GetParam( Param => 'Name' ) || ''; my $Version = $ParamObject->GetParam( Param => 'Version' ) || ''; my $Location = $ParamObject->GetParam( Param => 'Location' ); # get package my $Package = $PackageObject->RepositoryGet( Name => $Name, Version => $Version, Result => 'SCALAR', ); if ( !$Package ) { return $LayoutObject->ErrorScreen( Message => Translatable('No such package!'), ); } my %Structure = $PackageObject->PackageParse( String => $Package ); my $File = ''; if ( ref $Structure{Filelist} eq 'ARRAY' ) { for my $Hash ( @{ $Structure{Filelist} } ) { if ( $Hash->{Location} eq $Location ) { $File = $Hash->{Content}; } } } my $LocalFile = $ConfigObject->Get('Home') . "/$Location"; # do not allow to read file with including .. path (security related) $LocalFile =~ s/\.\.//g; if ( !$File ) { $LayoutObject->Block( Name => 'FileDiff', Data => { Location => $Location, Name => $Name, Version => $Version, Diff => $LayoutObject->{LanguageObject}->Translate( 'No such file %s in package!', $LocalFile ), }, ); } elsif ( !-e $LocalFile ) { $LayoutObject->Block( Name => 'FileDiff', Data => { Location => $Location, Name => $Name, Version => $Version, Diff => $LayoutObject->{LanguageObject} ->Translate( 'No such file %s in local file system!', $LocalFile ), }, ); } elsif ( -e $LocalFile ) { my $Content = $MainObject->FileRead( Location => $LocalFile, Mode => 'binmode', ); if ($Content) { $MainObject->Require('Text::Diff'); my $Diff = Text::Diff::diff( \$File, $Content, { STYLE => 'OldStyle' } ); $LayoutObject->Block( Name => "FileDiff", Data => { Location => $Location, Name => $Name, Version => $Version, Diff => $Diff, }, ); } else { $LayoutObject->Block( Name => "FileDiff", Data => { Location => $Location, Name => $Name, Version => $Version, Diff => $LayoutObject->{LanguageObject}->Translate( 'Can\'t read %s!', $LocalFile ), }, ); } } my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPackageManager', ); $Output .= $LayoutObject->Footer(); return $Output; } my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); # ------------------------------------------------------------ # # view package # ------------------------------------------------------------ # if ( $Self->{Subaction} eq 'View' ) { my $Name = $ParamObject->GetParam( Param => 'Name' ) || ''; my $Version = $ParamObject->GetParam( Param => 'Version' ) || ''; my $Location = $ParamObject->GetParam( Param => 'Location' ); my %Frontend; # get package my $Package = $PackageObject->RepositoryGet( Name => $Name, Version => $Version, Result => 'SCALAR', ); if ( !$Package ) { return $LayoutObject->ErrorScreen( Message => Translatable('No such package!'), ); } # parse package my %Structure = $PackageObject->PackageParse( String => $Package ); # online verification my $Verified = $PackageObject->PackageVerify( Package => $Package, Structure => \%Structure, ) || 'unknown'; my %VerifyInfo = $PackageObject->PackageVerifyInfo(); # translate description if ( $LayoutObject->{LanguageObject} ) { $VerifyInfo{Description} = $LayoutObject->{LanguageObject}->Translate( $VerifyInfo{Description} ); } # deploy check my $Deployed = $PackageObject->DeployCheck( Name => $Name, Version => $Version, ); my %DeployInfo = $PackageObject->DeployCheckInfo(); $LayoutObject->Block( Name => 'Package', Data => { %Param, %Frontend, Name => $Name, Version => $Version, }, ); my @RepositoryList = $PackageObject->RepositoryList( Result => 'short', ); # if visible property is not enable, return error screen if ( defined $Structure{PackageIsVisible} && exists $Structure{PackageIsVisible}->{Content} && !$Structure{PackageIsVisible}->{Content} ) { return $LayoutObject->ErrorScreen( Message => Translatable('No such package!'), ); } PACKAGEACTION: for my $PackageAction (qw(DownloadLocal Rebuild Reinstall)) { if ( $PackageAction eq 'DownloadLocal' && ( defined $Structure{PackageIsDownloadable} && exists $Structure{PackageIsDownloadable}->{Content} && !$Structure{PackageIsDownloadable}->{Content} ) ) { next PACKAGEACTION; } $LayoutObject->Block( Name => 'Package' . $PackageAction, Data => { %Param, %Frontend, Name => $Name, Version => $Version, }, ); } # check if file is requested if ($Location) { if ( ref $Structure{Filelist} eq 'ARRAY' ) { for my $Hash ( @{ $Structure{Filelist} } ) { if ( $Hash->{Location} eq $Location ) { return $LayoutObject->Attachment( Filename => $Location, ContentType => 'application/octet-stream', Content => $Hash->{Content}, ); } } } } my @DatabaseBuffer; # correct any 'dos-style' line endings - http://bugs.otrs.org/show_bug.cgi?id=9838 ${$Package} =~ s{\r\n}{\n}xmsg; # create MD5 sum and add it into existing package structure my $MD5sum = $MainObject->MD5sum( String => $Package ); $Structure{MD5sum} = { Tag => 'MD5sum', Content => $MD5sum, }; for my $Key ( sort keys %Structure ) { if ( ref $Structure{$Key} eq 'HASH' ) { if ( $Key =~ /^(Description|Filelist)$/ ) { $LayoutObject->Block( Name => "PackageItem$Key", Data => { Tag => $Key, %{ $Structure{$Key} } }, ); } elsif ( $Key =~ /^Database(Install|Reinstall|Upgrade|Uninstall)$/ ) { for my $Type (qw(pre post)) { for my $Hash ( @{ $Structure{$Key}->{$Type} } ) { if ( $Hash->{TagType} eq 'Start' ) { if ( $Hash->{Tag} =~ /^Table/ ) { $LayoutObject->Block( Name => "PackageItemDatabase", Data => { %{$Hash}, TagName => $Key, Type => $Type }, ); push @DatabaseBuffer, $Hash; } else { $LayoutObject->Block( Name => "PackageItemDatabaseSub", Data => { %{$Hash}, TagName => $Key, }, ); push @DatabaseBuffer, $Hash; } } if ( $Hash->{Tag} =~ /^Table/ && $Hash->{TagType} eq 'End' ) { push @DatabaseBuffer, $Hash; my @SQL = $DBObject->SQLProcessor( Database => \@DatabaseBuffer ); my @SQLPost = $DBObject->SQLProcessorPost(); push @SQL, @SQLPost; for my $SQL (@SQL) { $LayoutObject->Block( Name => "PackageItemDatabaseSQL", Data => { TagName => $Key, SQL => $SQL, }, ); } @DatabaseBuffer = (); } } } } else { $LayoutObject->Block( Name => 'PackageItemGeneric', Data => { Tag => $Key, %{ $Structure{$Key} } }, ); } } elsif ( ref $Structure{$Key} eq 'ARRAY' ) { for my $Hash ( @{ $Structure{$Key} } ) { if ( $Key =~ /^(Description|ChangeLog)$/ ) { $LayoutObject->Block( Name => "PackageItem$Key", Data => { %{$Hash}, Tag => $Key, }, ); } elsif ( $Key =~ /^Code/ ) { $Hash->{Content} = $LayoutObject->Ascii2Html( Text => $Hash->{Content}, HTMLResultMode => 1, NewLine => 72, ); $LayoutObject->Block( Name => "PackageItemCode", Data => { Tag => $Key, %{$Hash} }, ); } elsif ( $Key =~ /^(Intro)/ ) { if ( $Hash->{Format} && $Hash->{Format} =~ /plain/i ) { $Hash->{Content} = '
' . $Hash->{Content} . '
'; } $LayoutObject->Block( Name => "PackageItemIntro", Data => { %{$Hash}, Tag => $Key, }, ); } elsif ( $Hash->{Tag} =~ /^(File)$/ ) { # add human readable file size if ( defined $Hash->{Size} ) { $Hash->{Size} = $LayoutObject->HumanReadableDataSize( Size => $Hash->{Size}, ); } $LayoutObject->Block( Name => "PackageItemFilelistFile", Data => { Name => $Name, Version => $Version, %{$Hash}, }, ); # check if is possible to download files if ( !defined $Structure{PackageIsDownloadable} || ( defined $Structure{PackageIsDownloadable}->{Content} && $Structure{PackageIsDownloadable}->{Content} eq '1' ) ) { $LayoutObject->Block( Name => "PackageItemFilelistFileLink", Data => { Name => $Name, Version => $Version, %{$Hash}, }, ); } if ( $DeployInfo{File}->{ $Hash->{Location} } ) { if ( $DeployInfo{File}->{ $Hash->{Location} } =~ /different/ ) { $LayoutObject->Block( Name => "PackageItemFilelistFileNoteDiff", Data => { Name => $Name, Version => $Version, %{$Hash}, Message => $DeployInfo{File}->{ $Hash->{Location} }, Icon => 'IconNotReady', }, ); } else { $LayoutObject->Block( Name => "PackageItemFilelistFileNote", Data => { Name => $Name, Version => $Version, %{$Hash}, Message => $DeployInfo{File}->{ $Hash->{Location} }, Icon => 'IconNotReadyGrey', }, ); } } else { $LayoutObject->Block( Name => "PackageItemFilelistFileNote", Data => { Name => $Name, Version => $Version, %{$Hash}, Message => Translatable('File is OK'), Icon => 'IconReady', }, ); } } else { $LayoutObject->Block( Name => "PackageItemGeneric", Data => { %{$Hash}, Tag => $Key, }, ); } } } } my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); if ( !$Deployed ) { my $Priority = 'Error'; my $Message = $LayoutObject->{LanguageObject} ->Translate("Package not correctly deployed! Please reinstall the package."); if ( $Kernel::OM->Get('Kernel::Config')->Get('Package::AllowLocalModifications') ) { $Priority = 'Notice'; $Message = $LayoutObject->{LanguageObject}->Translate("Package has locally modified files."); } $Output .= $LayoutObject->Notify( Priority => $Priority, Data => "$Name $Version - $Message", Link => $LayoutObject->{Baselink} . 'Action=AdminPackageManager;Subaction=View;Name=' . $Name . ';Version=' . $Version, ); } if ( $Verified ne 'verified' ) { $Output .= $LayoutObject->Notify( Priority => 'Error', Data => "$Name $Version - " . $LayoutObject->{LanguageObject}->Translate( "Package not verified by the OTRS Group! It is recommended not to use this package." ), ); } $Output .= $LayoutObject->Output( TemplateFile => 'AdminPackageManager', ); $Output .= $LayoutObject->Footer(); return $Output; } # ------------------------------------------------------------ # # view remote package # ------------------------------------------------------------ # if ( $Self->{Subaction} eq 'ViewRemote' ) { my $File = $ParamObject->GetParam( Param => 'File' ) || ''; my $Location = $ParamObject->GetParam( Param => 'Location' ); my %Frontend; # download package my $Package = $PackageObject->PackageOnlineGet( Source => $Source, File => $File, ); if ( !$Package ) { return $LayoutObject->ErrorScreen( Message => Translatable('No such package!'), ); } elsif ( substr( $Package, 0, length('ErrorMessage:') ) eq 'ErrorMessage:' ) { # an error from the Package::CloudFileGet function return $LayoutObject->ErrorScreen( Message => $Package ); } my %Structure = $PackageObject->PackageParse( String => $Package ); $Frontend{Name} = $Structure{Name}->{Content}; $LayoutObject->Block( Name => 'Package', Data => { %Param, %Frontend, }, ); # allow to download only if package is allow to do it if ( !defined $Structure{PackageIsDownloadable} || ( defined $Structure{PackageIsDownloadable}->{Content} && $Structure{PackageIsDownloadable}->{Content} eq '1' ) ) { $LayoutObject->Block( Name => 'PackageDownloadRemote', Data => { %Param, %Frontend, File => $File, }, ); } # check if file is requested if ($Location) { if ( ref $Structure{Filelist} eq 'ARRAY' ) { for my $Hash ( @{ $Structure{Filelist} } ) { if ( $Hash->{Location} eq $Location ) { return $LayoutObject->Attachment( Filename => $Location, ContentType => 'application/octet-stream', Content => $Hash->{Content}, ); } } } } my @DatabaseBuffer; for my $Key ( sort keys %Structure ) { if ( ref $Structure{$Key} eq 'HASH' ) { if ( $Key =~ /^(Description|Filelist)$/ ) { $LayoutObject->Block( Name => "PackageItem$Key", Data => { Tag => $Key, %{ $Structure{$Key} } }, ); } elsif ( $Key =~ /^Database(Install|Reinstall|Upgrade|Uninstall)$/ ) { for my $Type (qw(pre post)) { for my $Hash ( @{ $Structure{$Key}->{$Type} } ) { if ( $Hash->{TagType} eq 'Start' ) { if ( $Hash->{Tag} =~ /^Table/ ) { $LayoutObject->Block( Name => "PackageItemDatabase", Data => { %{$Hash}, TagName => $Key, Type => $Type }, ); push @DatabaseBuffer, $Hash; } else { $LayoutObject->Block( Name => "PackageItemDatabaseSub", Data => { %{$Hash}, TagName => $Key, }, ); push @DatabaseBuffer, $Hash; } } if ( $Hash->{Tag} =~ /^Table/ && $Hash->{TagType} eq 'End' ) { push @DatabaseBuffer, $Hash; my @SQL = $DBObject->SQLProcessor( Database => \@DatabaseBuffer ); my @SQLPost = $DBObject->SQLProcessorPost(); push @SQL, @SQLPost; for my $SQL (@SQL) { $LayoutObject->Block( Name => "PackageItemDatabaseSQL", Data => { TagName => $Key, SQL => $SQL, }, ); } @DatabaseBuffer = (); } } } } else { $LayoutObject->Block( Name => 'PackageItemGeneric', Data => { Tag => $Key, %{ $Structure{$Key} } }, ); } } elsif ( ref $Structure{$Key} eq 'ARRAY' ) { for my $Hash ( @{ $Structure{$Key} } ) { if ( $Key =~ /^(Description|ChangeLog)$/ ) { $LayoutObject->Block( Name => "PackageItem$Key", Data => { %{$Hash}, Tag => $Key, }, ); } elsif ( $Key =~ /^Code/ ) { $Hash->{Content} = $LayoutObject->Ascii2Html( Text => $Hash->{Content}, HTMLResultMode => 1, NewLine => 72, ); $LayoutObject->Block( Name => "PackageItemCode", Data => { Tag => $Key, %{$Hash} }, ); } elsif ( $Key =~ /^(Intro)/ ) { if ( $Hash->{Format} && $Hash->{Format} =~ /plain/i ) { $Hash->{Content} = '
' . $Hash->{Content} . '
'; } $LayoutObject->Block( Name => "PackageItemIntro", Data => { %{$Hash}, Tag => $Key, }, ); } elsif ( $Hash->{Tag} =~ /^(File)$/ ) { # add human readable file size if ( defined $Hash->{Size} ) { $Hash->{Size} = $LayoutObject->HumanReadableDataSize( Size => $Hash->{Size}, ); } $LayoutObject->Block( Name => 'PackageItemFilelistFile', Data => { Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, File => $File, %{$Hash}, }, ); # check if is possible to download files if ( !defined $Structure{PackageIsDownloadable} || ( defined $Structure{PackageIsDownloadable}->{Content} && $Structure{PackageIsDownloadable}->{Content} eq '1' ) ) { $LayoutObject->Block( Name => "PackageItemFilelistFileLink", Data => { Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, File => $File, %{$Hash}, }, ); } } else { $LayoutObject->Block( Name => 'PackageItemGeneric', Data => { %{$Hash}, Tag => $Key, }, ); } } } } my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPackageManager', ); $Output .= $LayoutObject->Footer(); return $Output; } # ------------------------------------------------------------ # # download package # ------------------------------------------------------------ # elsif ( $Self->{Subaction} eq 'Download' ) { my $Name = $ParamObject->GetParam( Param => 'Name' ) || ''; my $Version = $ParamObject->GetParam( Param => 'Version' ) || ''; # get package my $Package = $PackageObject->RepositoryGet( Name => $Name, Version => $Version, ); if ( !$Package ) { return $LayoutObject->ErrorScreen( Message => Translatable('No such package!'), ); } return $LayoutObject->Attachment( Content => $Package, ContentType => 'application/octet-stream', Filename => "$Name-$Version.opm", Type => 'attachment', ); } # ------------------------------------------------------------ # # download remote package # ------------------------------------------------------------ # elsif ( $Self->{Subaction} eq 'DownloadRemote' ) { my $File = $ParamObject->GetParam( Param => 'File' ) || ''; # download package my $Package = $PackageObject->PackageOnlineGet( Source => $Source, File => $File, ); # check if ( !$Package ) { return $LayoutObject->ErrorScreen( Message => Translatable('No such package!'), ); } return $LayoutObject->Attachment( Content => $Package, ContentType => 'application/octet-stream', Filename => $File, Type => 'attachment', ); } # ------------------------------------------------------------ # # change repository # ------------------------------------------------------------ # elsif ( $Self->{Subaction} eq 'ChangeRepository' ) { # challenge token check for write action $LayoutObject->ChallengeTokenCheck(); my $Source = $ParamObject->GetParam( Param => 'Source' ) || ''; $Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID( SessionID => $Self->{SessionID}, Key => 'UserRepository', Value => $Source, ); return $LayoutObject->Redirect( OP => "Action=$Self->{Action}" ); } # ------------------------------------------------------------ # # install package # ------------------------------------------------------------ # elsif ( $Self->{Subaction} eq 'Install' ) { # challenge token check for write action $LayoutObject->ChallengeTokenCheck(); my $Name = $ParamObject->GetParam( Param => 'Name' ) || ''; my $Version = $ParamObject->GetParam( Param => 'Version' ) || ''; # get package my $Package = $PackageObject->RepositoryGet( Name => $Name, Version => $Version, Result => 'SCALAR', ); return $Self->_InstallHandling( Package => $Package ); } # ------------------------------------------------------------ # # install remote package # ------------------------------------------------------------ # elsif ( $Self->{Subaction} eq 'InstallRemote' ) { # challenge token check for write action $LayoutObject->ChallengeTokenCheck(); my $File = $ParamObject->GetParam( Param => 'File' ) || ''; # download package my $Package = $PackageObject->PackageOnlineGet( Source => $Source, File => $File, ); return $Self->_InstallHandling( Package => $Package, Source => $Source, File => $File, ); } # ------------------------------------------------------------ # # upgrade remote package # ------------------------------------------------------------ # elsif ( $Self->{Subaction} eq 'UpgradeRemote' ) { # challenge token check for write action $LayoutObject->ChallengeTokenCheck(); my $File = $ParamObject->GetParam( Param => 'File' ) || ''; # download package my $Package = $PackageObject->PackageOnlineGet( File => $File, Source => $Source, ); return $Self->_UpgradeHandling( Package => $Package, File => $File, Source => $Source, ); } # ------------------------------------------------------------ # # reinstall package # ------------------------------------------------------------ # elsif ( $Self->{Subaction} eq 'Reinstall' ) { # challenge token check for write action $LayoutObject->ChallengeTokenCheck(); my $Name = $ParamObject->GetParam( Param => 'Name' ) || ''; my $Version = $ParamObject->GetParam( Param => 'Version' ) || ''; my $IntroReinstallPre = $ParamObject->GetParam( Param => 'IntroReinstallPre' ) || ''; # get package my $Package = $PackageObject->RepositoryGet( Name => $Name, Version => $Version, Result => 'SCALAR', ); if ( !$Package ) { return $LayoutObject->ErrorScreen( Message => Translatable('No such package!'), ); } # check if we have to show reinstall intro pre my %Structure = $PackageObject->PackageParse( String => $Package, ); # intro screen my %Data; if ( $Structure{IntroReinstall} ) { %Data = $Self->_MessageGet( Info => $Structure{IntroReinstall}, Type => 'pre' ); } if ( %Data && !$IntroReinstallPre ) { $LayoutObject->Block( Name => 'Intro', Data => { %Param, %Data, Subaction => $Self->{Subaction}, Type => 'IntroReinstallPre', Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, }, ); $LayoutObject->Block( Name => 'IntroForm', Data => { %Param, %Data, Subaction => $Self->{Subaction}, Type => 'IntroReinstallPre', Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, }, ); $LayoutObject->Block( Name => 'IntroCancel', ); my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPackageManager', Data => \%Param, ); $Output .= $LayoutObject->Footer(); return $Output; } # reinstall screen else { $LayoutObject->Block( Name => 'Reinstall', Data => { %Param, Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, }, ); my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPackageManager', ); $Output .= $LayoutObject->Footer(); return $Output; } } # ------------------------------------------------------------ # # reinstall action package # ------------------------------------------------------------ # elsif ( $Self->{Subaction} eq 'ReinstallAction' ) { # challenge token check for write action $LayoutObject->ChallengeTokenCheck(); my $Name = $ParamObject->GetParam( Param => 'Name' ) || ''; my $Version = $ParamObject->GetParam( Param => 'Version' ) || ''; my $IntroReinstallPost = $ParamObject->GetParam( Param => 'IntroReinstallPost' ) || ''; # get package my $Package = $PackageObject->RepositoryGet( Name => $Name, Version => $Version, ); if ( !$Package ) { return $LayoutObject->ErrorScreen( Message => Translatable('No such package!'), ); } # check if we have to show reinstall intro pre my %Structure = $PackageObject->PackageParse( String => $Package, ); # intro screen if ( !$PackageObject->PackageReinstall( String => $Package ) ) { return $LayoutObject->ErrorScreen(); } my %Data; if ( $Structure{IntroReinstall} ) { %Data = $Self->_MessageGet( Info => $Structure{IntroReinstall}, Type => 'post' ); } if ( %Data && !$IntroReinstallPost ) { $LayoutObject->Block( Name => 'Intro', Data => { %Param, %Data, Subaction => '', Type => 'IntroReinstallPost', Name => $Name, Version => $Version, }, ); $LayoutObject->Block( Name => 'IntroForm', Data => { %Param, %Data, Subaction => '', Type => 'IntroReinstallPost', Name => $Name, Version => $Version, }, ); my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPackageManager', ); $Output .= $LayoutObject->Footer(); return $Output; } # redirect return $LayoutObject->Redirect( OP => "Action=$Self->{Action}" ); } # ------------------------------------------------------------ # # uninstall package # ------------------------------------------------------------ # elsif ( $Self->{Subaction} eq 'Uninstall' ) { # challenge token check for write action $LayoutObject->ChallengeTokenCheck(); my $Name = $ParamObject->GetParam( Param => 'Name' ) || ''; my $Version = $ParamObject->GetParam( Param => 'Version' ) || ''; my $IntroUninstallPre = $ParamObject->GetParam( Param => 'IntroUninstallPre' ) || ''; # get package my $Package = $PackageObject->RepositoryGet( Name => $Name, Version => $Version, Result => 'SCALAR', ); if ( !$Package ) { return $LayoutObject->ErrorScreen( Message => Translatable('No such package!'), ); } # check if we have to show uninstall intro pre my %Structure = $PackageObject->PackageParse( String => $Package, ); # intro screen my %Data; if ( $Structure{IntroUninstall} ) { %Data = $Self->_MessageGet( Info => $Structure{IntroUninstall}, Type => 'pre' ); } if ( %Data && !$IntroUninstallPre ) { $LayoutObject->Block( Name => 'Intro', Data => { %Param, %Data, Subaction => $Self->{Subaction}, Type => 'IntroUninstallPre', Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, }, ); $LayoutObject->Block( Name => 'IntroForm', Data => { %Param, %Data, Subaction => $Self->{Subaction}, Type => 'IntroUninstallPre', Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, }, ); $LayoutObject->Block( Name => 'IntroCancel', ); my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPackageManager', ); $Output .= $LayoutObject->Footer(); return $Output; } # uninstall screen else { $LayoutObject->Block( Name => 'Uninstall', Data => { %Param, Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, }, ); my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPackageManager', ); $Output .= $LayoutObject->Footer(); return $Output; } } # ------------------------------------------------------------ # # uninstall action package # ------------------------------------------------------------ # elsif ( $Self->{Subaction} eq 'UninstallAction' ) { # challenge token check for write action $LayoutObject->ChallengeTokenCheck(); my $Name = $ParamObject->GetParam( Param => 'Name' ) || ''; my $Version = $ParamObject->GetParam( Param => 'Version' ) || ''; my $IntroUninstallPost = $ParamObject->GetParam( Param => 'IntroUninstallPost' ) || ''; # get package my $Package = $PackageObject->RepositoryGet( Name => $Name, Version => $Version, ); if ( !$Package ) { return $LayoutObject->ErrorScreen( Message => Translatable('No such package!'), ); } # parse package my %Structure = $PackageObject->PackageParse( String => $Package, ); # unsinstall the package if ( !$PackageObject->PackageUninstall( String => $Package ) ) { return $LayoutObject->ErrorScreen(); } # intro screen my %Data; if ( $Structure{IntroUninstall} ) { %Data = $Self->_MessageGet( Info => $Structure{IntroUninstall}, Type => 'post' ); } if ( %Data && !$IntroUninstallPost ) { my %Data = $Self->_MessageGet( Info => $Structure{IntroUninstallPost} ); $LayoutObject->Block( Name => 'Intro', Data => { %Param, %Data, Subaction => '', Type => 'IntroUninstallPost', Name => $Name, Version => $Version, }, ); $LayoutObject->Block( Name => 'IntroForm', Data => { %Param, %Data, Subaction => '', Type => 'IntroUninstallPost', Name => $Name, Version => $Version, }, ); my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPackageManager', ); $Output .= $LayoutObject->Footer(); return $Output; } # redirect return $LayoutObject->Redirect( OP => "Action=$Self->{Action}" ); } # ------------------------------------------------------------ # # install package # ------------------------------------------------------------ # elsif ( $Self->{Subaction} eq 'InstallUpload' ) { # challenge token check for write action $LayoutObject->ChallengeTokenCheck(); my $FormID = $ParamObject->GetParam( Param => 'FormID' ) || ''; my %UploadStuff = $ParamObject->GetUploadAll( Param => 'FileUpload', ); my $UploadCacheObject = $Kernel::OM->Get('Kernel::System::Web::UploadCache'); # save package in upload cache if (%UploadStuff) { my $Added = $UploadCacheObject->FormIDAddFile( FormID => $FormID, %UploadStuff, ); # if file got not added to storage # (e. g. because of 1 MB max_allowed_packet MySQL problem) if ( !$Added ) { $LayoutObject->FatalError(); } } # get package from upload cache else { my @AttachmentData = $UploadCacheObject->FormIDGetAllFilesData( FormID => $FormID, ); if ( !@AttachmentData || ( $AttachmentData[0] && !%{ $AttachmentData[0] } ) ) { $Errors{FileUploadInvalid} = 'ServerError'; } else { %UploadStuff = %{ $AttachmentData[0] }; } } if ( !%Errors ) { my $Feedback = $PackageObject->PackageIsInstalled( String => $UploadStuff{Content} ); if ($Feedback) { return $Self->_UpgradeHandling( Package => $UploadStuff{Content}, FormID => $FormID, ); } return $Self->_InstallHandling( Package => $UploadStuff{Content}, FormID => $FormID, ); } } # ------------------------------------------------------------ # # rebuild package # ------------------------------------------------------------ # elsif ( $Self->{Subaction} eq 'RebuildPackage' ) { # challenge token check for write action $LayoutObject->ChallengeTokenCheck(); my $Name = $ParamObject->GetParam( Param => 'Name' ) || ''; my $Version = $ParamObject->GetParam( Param => 'Version' ) || ''; # get package my $Package = $PackageObject->RepositoryGet( Name => $Name, Version => $Version, Result => 'SCALAR', ); if ( !$Package ) { return $LayoutObject->ErrorScreen( Message => Translatable('No such package!'), ); } my %Structure = $PackageObject->PackageParse( String => $Package, ); my $File = $PackageObject->PackageBuild(%Structure); return $LayoutObject->Attachment( Content => $File, ContentType => 'application/octet-stream', Filename => "$Name-$Version-rebuild.opm", Type => 'attachment', ); } # ------------------------------------------------------------ # # Create a PackageUpgradeAll task for daemon # ------------------------------------------------------------ # elsif ( $Self->{Subaction} eq 'AJAXPackageUpgradeAll' ) { my $Success = $Self->AsyncCall( ObjectName => 'Kernel::System::Package', FunctionName => 'PackageUpgradeAll', FunctionParams => [], Attempts => 3, MaximumParallelInstances => 1, ); my $JSON = $LayoutObject->JSONEncode( Data => { Success => $Success, }, ); return $LayoutObject->Attachment( ContentType => 'application/json; charset=' . $LayoutObject->{Charset}, Content => $JSON || '', Type => 'inline', NoCache => 1, ); } # ------------------------------------------------------------ # # Check if is safe to start a new Package Upgrade all process # ------------------------------------------------------------ # elsif ( $Self->{Subaction} eq 'AJAXGetPackageUpgradeRunStatus' ) { my %Result = $PackageObject->PackageUpgradeAllIsRunning(); my $JSON = $LayoutObject->JSONEncode( Data => { Success => 1, %Result, }, ); return $LayoutObject->Attachment( ContentType => 'application/json; charset=' . $LayoutObject->{Charset}, Content => $JSON, Type => 'inline', NoCache => 1, ); } # ------------------------------------------------------------ # # Check current Package Upgrade all results (partial or full) # ------------------------------------------------------------ # elsif ( $Self->{Subaction} eq 'AJAXGetPackageUpgradeResult' ) { my $SystemDataObject = $Kernel::OM->Get('Kernel::System::SystemData'); my %SystemData = $SystemDataObject->SystemDataGroupGet( Group => 'Package_UpgradeAll', ); my $JSONObject = $Kernel::OM->Get('Kernel::System::JSON'); my $InstalledPackages = $JSONObject->Decode( Data => $SystemData{InstalledPackages} || {}, ); my $UpgradeResult = $JSONObject->Decode( Data => $SystemData{UpgradeResult} || {}, ); my %PackageList; if ( IsArrayRefWithData($InstalledPackages) ) { my $DefaultStatus = Translatable('Not Started'); my $DefaultStatusDisplay = $LayoutObject->{LanguageObject}->Translate($DefaultStatus); for my $Package ( @{$InstalledPackages} ) { $PackageList{ $Package->{Name} } = { Name => $Package->{Name}, Status => $DefaultStatus, StatusDisplay => $DefaultStatusDisplay, }; } my %StatusStings = ( Updated => $LayoutObject->{LanguageObject}->Translate('Updated'), AlreadyUpdated => $LayoutObject->{LanguageObject}->Translate('Already up-to-date'), Installed => $LayoutObject->{LanguageObject}->Translate('Installed'), Undeployed => $LayoutObject->{LanguageObject}->Translate('Not correctly deployed'), Failed => $LayoutObject->{LanguageObject}->Translate('Failed'), ); my %StatusMessages = ( Updated => $LayoutObject->{LanguageObject}->Translate('Package updated correctly'), AlreadyUpdated => $LayoutObject->{LanguageObject}->Translate('Package was already updated'), Installed => $LayoutObject->{LanguageObject}->Translate('Dependency installed correctly'), Undeployed => $LayoutObject->{LanguageObject}->Translate('The package needs to be reinstalled'), Cyclic => $LayoutObject->{LanguageObject}->Translate('The package contains cyclic dependencies'), NotFound => $LayoutObject->{LanguageObject}->Translate('Not found in on-line repositories'), WrongVersion => $LayoutObject->{LanguageObject}->Translate('Required version is higher than available'), DependencyFail => $LayoutObject->{LanguageObject}->Translate('Dependencies fail to upgrade or install'), InstallError => $LayoutObject->{LanguageObject}->Translate('Package could not be installed'), UpdateError => $LayoutObject->{LanguageObject}->Translate('Package could not be upgraded'), ); if ( IsHashRefWithData($UpgradeResult) ) { for my $StatusType (qw(Updated Installed AlreadyUpdated Undeployed)) { for my $PackageName ( sort keys %{ $UpgradeResult->{$StatusType} } ) { my $Class = 'Success'; if ( $StatusType eq 'Installed' || $StatusType eq 'Undeployed' ) { $Class = 'Warning'; } $PackageList{$PackageName} = { Name => $PackageName, Status => $StatusType, StatusDisplay => $StatusStings{$StatusType}, StatusMessage => $StatusMessages{$StatusType}, Class => $Class, }; } } for my $FailType ( sort keys %{ $UpgradeResult->{Failed} } ) { for my $PackageName ( sort keys %{ $UpgradeResult->{Failed}->{$FailType} } ) { $PackageList{$PackageName} = { Name => $PackageName, Status => 'Failed', StatusDisplay => $StatusStings{Failed}, StatusMessage => $StatusMessages{$FailType}, Class => 'Fail', }; } } } } # Convert it into an array for easy and persistent sorting. my @PackageList = map { $PackageList{$_} } sort keys %PackageList; my $JSON = $LayoutObject->JSONEncode( Data => { Success => 1, UpgradeStatus => $SystemData{Status} || '', UpgradeSuccess => $SystemData{Success} || '', PackageList => \@PackageList, }, ); return $LayoutObject->Attachment( ContentType => 'application/json; charset=' . $LayoutObject->{Charset}, Content => $JSON || '', Type => 'inline', NoCache => 1, ); } # ------------------------------------------------------------ # # Removes any Package Upgrade data from the database # ------------------------------------------------------------ # elsif ( $Self->{Subaction} eq 'AJAXDeletePackageUpgradeData' ) { my $Success = $Kernel::OM->Get('Kernel::System::Package')->PackageUpgradeAllDataDelete(); my $JSON = $LayoutObject->JSONEncode( Data => { Success => $Success, }, ); return $LayoutObject->Attachment( ContentType => 'application/json; charset=' . $LayoutObject->{Charset}, Content => $JSON || '', Type => 'inline', NoCache => 1, ); } # ------------------------------------------------------------ # # overview # ------------------------------------------------------------ # my %Frontend; my %NeedReinstall; my %List; my $OutputNotify = ''; if ( $ConfigObject->Get('Package::RepositoryList') ) { %List = %{ $ConfigObject->Get('Package::RepositoryList') }; } my %RepositoryRoot; if ( $ConfigObject->Get('Package::RepositoryRoot') ) { %RepositoryRoot = $PackageObject->PackageOnlineRepositories(); } # show cloud repo if system is registered my $RepositoryCloudList; my $RegistrationState = $Kernel::OM->Get('Kernel::System::SystemData')->SystemDataGet( Key => 'Registration::State', ) || ''; if ( $RegistrationState eq 'registered' && !$Self->{CloudServicesDisabled} ) { $RepositoryCloudList = $PackageObject->RepositoryCloudList( NoCache => 1 ); } # In case Source is present on repository cloud list # the call for retrieving data about it, should be performed # using the CloudService backend. my $FromCloud = ( $RepositoryCloudList->{$Source} ? 1 : 0 ); # Get the list of the installed packages early to be able to show or not the Upgrade All button # in the layout block. my @RepositoryList = $PackageObject->RepositoryList(); $Frontend{SourceList} = $LayoutObject->BuildSelection( Data => { %List, %RepositoryRoot, %{$RepositoryCloudList}, }, Name => 'Source', Title => Translatable('Repository List'), Max => 40, Translation => 0, SelectedID => $Source, Class => "Modernize W100pc", ); $LayoutObject->Block( Name => 'Overview', Data => { %Param, %Frontend, InstalledPackages => @RepositoryList ? 1 : 0, }, ); if ($Source) { my @List = $PackageObject->PackageOnlineList( URL => $Source, Lang => $LayoutObject->{UserLanguage}, FromCloud => $FromCloud, ); if ( !@List ) { $OutputNotify .= $LayoutObject->Notify( Priority => 'Warning', Info => Translatable('No packages found in selected repository. Please check log for more info!'), Link => $LayoutObject->{Baselink} . 'Action=AdminLog', ); $LayoutObject->Block( Name => 'NoDataFoundMsg', Data => {}, ); } for my $Data (@List) { $LayoutObject->Block( Name => 'ShowRemotePackage', Data => { %{$Data}, Source => $Source, }, ); # show documentation link my %DocFile = $Self->_DocumentationGet( Filelist => $Data->{Filelist} ); if (%DocFile) { $LayoutObject->Block( Name => 'ShowRemotePackageDocumentation', Data => { %{$Data}, Source => $Source, %DocFile, }, ); } if ( $Data->{Upgrade} ) { $LayoutObject->Block( Name => 'ShowRemotePackageUpgrade', Data => { %{$Data}, Source => $Source, }, ); } elsif ( !$Data->{Installed} ) { $LayoutObject->Block( Name => 'ShowRemotePackageInstall', Data => { %{$Data}, Source => $Source, }, ); } } } # if there are no remote packages to show, a msg is displayed else { $LayoutObject->Block( Name => 'NoDataFoundMsg', Data => {}, ); } # remove not visible packages @RepositoryList = map { ( !defined $_->{PackageIsVisible} || ( $_->{PackageIsVisible}->{Content} && $_->{PackageIsVisible}->{Content} eq '1' ) ) ? $_ : () } @RepositoryList; # if there are no local packages to show, a msg is displayed if ( !@RepositoryList ) { $LayoutObject->Block( Name => 'NoDataFoundMsg2', ); } # verify packages if we have some my %VerificationData; if (@RepositoryList) { %VerificationData = $PackageObject->PackageVerifyAll(); } my %NotVerifiedPackages; my %UnknownVerficationPackages; for my $Package (@RepositoryList) { my %Data = $Self->_MessageGet( Info => $Package->{Description} ); $LayoutObject->Block( Name => 'ShowLocalPackage', Data => { %{$Package}, %Data, Name => $Package->{Name}->{Content}, Version => $Package->{Version}->{Content}, Vendor => $Package->{Vendor}->{Content}, URL => $Package->{URL}->{Content}, }, ); if ( $VerificationData{ $Package->{Name}->{Content} } && $VerificationData{ $Package->{Name}->{Content} } eq 'verified' && !$Self->{CloudServicesDisabled} ) { $LayoutObject->Block( Name => 'ShowLocalPackageVerifyLogo', ); } # show documentation link my %DocFile = $Self->_DocumentationGet( Filelist => $Package->{Filelist} ); if (%DocFile) { $LayoutObject->Block( Name => 'ShowLocalPackageDocumentation', Data => { Name => $Package->{Name}->{Content}, Version => $Package->{Version}->{Content}, %DocFile, }, ); } if ( $Package->{Status} eq 'installed' ) { if ( !defined $Package->{PackageIsRemovable} || ( defined $Package->{PackageIsRemovable}->{Content} && $Package->{PackageIsRemovable}->{Content} eq '1' ) ) { $LayoutObject->Block( Name => 'ShowLocalPackageUninstall', Data => { %{$Package}, Name => $Package->{Name}->{Content}, Version => $Package->{Version}->{Content}, Vendor => $Package->{Vendor}->{Content}, URL => $Package->{URL}->{Content}, }, ); } if ( !$PackageObject->DeployCheck( Name => $Package->{Name}->{Content}, Version => $Package->{Version}->{Content} ) ) { $NeedReinstall{ $Package->{Name}->{Content} } = $Package->{Version}->{Content}; $LayoutObject->Block( Name => 'ShowLocalPackageReinstall', Data => { %{$Package}, Name => $Package->{Name}->{Content}, Version => $Package->{Version}->{Content}, Vendor => $Package->{Vendor}->{Content}, URL => $Package->{URL}->{Content}, }, ); } } else { $LayoutObject->Block( Name => 'ShowLocalPackageInstall', Data => { %{$Package}, Name => $Package->{Name}->{Content}, Version => $Package->{Version}->{Content}, Vendor => $Package->{Vendor}->{Content}, URL => $Package->{URL}->{Content}, }, ); } if ( $VerificationData{ $Package->{Name}->{Content} } && $VerificationData{ $Package->{Name}->{Content} } eq 'not_verified' ) { $NotVerifiedPackages{ $Package->{Name}->{Content} } = $Package->{Version}->{Content}; } elsif ( $VerificationData{ $Package->{Name}->{Content} } && $VerificationData{ $Package->{Name}->{Content} } eq 'unknown' ) { $UnknownVerficationPackages{ $Package->{Name}->{Content} } = $Package->{Version}->{Content}; } } # show file upload if ( $ConfigObject->Get('Package::FileUpload') ) { $LayoutObject->Block( Name => 'OverviewFileUpload', Data => { FormID => $Kernel::OM->Get('Kernel::System::Web::UploadCache')->FormIDCreate(), %Errors, }, ); # check if we're on MySQL and show a max_allowed_packet notice # if the actual value for this setting is too low if ( $DBObject->{'DB::Type'} eq 'mysql' ) { # check the actual setting $DBObject->Prepare( SQL => "SHOW variables WHERE Variable_name = 'max_allowed_packet'", ); my $MaxAllowedPacket = 0; my $MaxAllowedPacketRecommended = 64; while ( my @Data = $DBObject->FetchrowArray() ) { if ( $Data[1] ) { $MaxAllowedPacket = $Data[1] / 1024 / 1024; } } if ( $MaxAllowedPacket < $MaxAllowedPacketRecommended ) { $LayoutObject->Block( Name => 'DatabasePackageSizeWarning', Data => { MaxAllowedPacket => $MaxAllowedPacket, MaxAllowedPacketRecommended => $MaxAllowedPacketRecommended, }, ); } } } # FeatureAddons if ( $ConfigObject->Get('Package::ShowFeatureAddons') ) { my $FeatureAddonData; if ( !$Self->{CloudServicesDisabled} ) { $FeatureAddonData = $Self->_GetFeatureAddonData(); } if ( ref $FeatureAddonData eq 'ARRAY' && scalar @{$FeatureAddonData} > 0 ) { $LayoutObject->Block( Name => 'FeatureAddonList', ); for my $Item ( @{$FeatureAddonData} ) { $LayoutObject->Block( Name => 'FeatureAddonData', Data => $Item, ); } } } if ( $Self->{CloudServicesDisabled} ) { $LayoutObject->Block( Name => 'CloudServicesWarning', ); } # Check if OTRS Daemon is running in the background. # Get daemon state from the cache. my $DaemonRunning = $Kernel::OM->Get('Kernel::System::Cache')->Get( Type => 'DaemonRunning', Key => $ConfigObject->Get('NodeID') || 1, ); $LayoutObject->AddJSData( Key => 'DaemonCheckNotRunning', Value => !$DaemonRunning, ); # Remove old package upgrade all data. my $SystemDataObject = $Kernel::OM->Get('Kernel::System::SystemData'); my %SystemData = $SystemDataObject->SystemDataGroupGet( Group => 'Package_UpgradeAll', ); if ( %SystemData && $SystemData{UpdateTime} ) { my $CurrentDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); my $TargetDateTimeObject = $Kernel::OM->Create( 'Kernel::System::DateTime', ObjectParams => { String => $SystemData{UpdateTime}, } ); $TargetDateTimeObject->Add( Days => 1 ); if ( $CurrentDateTimeObject > $TargetDateTimeObject ) { $PackageObject->PackageUpgradeAllDataDelete(); } } my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $OutputNotify; for my $ReinstallKey ( sort keys %NeedReinstall ) { my $Priority = 'Error'; my $Message = $LayoutObject->{LanguageObject} ->Translate("Package not correctly deployed! Please reinstall the package."); if ( $Kernel::OM->Get('Kernel::Config')->Get('Package::AllowLocalModifications') ) { $Priority = 'Notice'; $Message = $LayoutObject->{LanguageObject}->Translate("Package has locally modified files."); } $Output .= $LayoutObject->Notify( Priority => $Priority, Data => "$ReinstallKey $NeedReinstall{$ReinstallKey} - $Message", Link => $LayoutObject->{Baselink} . 'Action=AdminPackageManager;Subaction=View;Name=' . $ReinstallKey . ';Version=' . $NeedReinstall{$ReinstallKey}, ); } VERIFICATION: for my $Package ( sort keys %NotVerifiedPackages ) { next VERIFICATION if !$Package; next VERIFICATION if !$NotVerifiedPackages{$Package}; $Output .= $LayoutObject->Notify( Priority => 'Error', Data => "$Package $NotVerifiedPackages{$Package} - " . $LayoutObject->{LanguageObject}->Translate( "Package not verified by the OTRS Group! It is recommended not to use this package." ), ); } if ( !$Self->{CloudServicesDisabled} ) { VERIFICATION: for my $Package ( sort keys %UnknownVerficationPackages ) { next VERIFICATION if !$Package; next VERIFICATION if !$UnknownVerficationPackages{$Package}; $Output .= $LayoutObject->Notify( Priority => 'Error', Data => "$Package $UnknownVerficationPackages{$Package} - " . $LayoutObject->{LanguageObject}->Translate( "Package not verified due a communication issue with verification server!" ), ); } } $Output .= $LayoutObject->Output( TemplateFile => 'AdminPackageManager', ); $Output .= $LayoutObject->Footer(); return $Output; } sub _MessageGet { my ( $Self, %Param ) = @_; my $Title = ''; my $Description = ''; my $Use = 0; my $Language = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{UserLanguage} || $Kernel::OM->Get('Kernel::Config')->Get('DefaultLanguage'); if ( $Param{Info} ) { TAG: for my $Tag ( @{ $Param{Info} } ) { if ( $Param{Type} ) { next TAG if $Tag->{Type} !~ /^$Param{Type}/i; } $Use = 1; if ( $Tag->{Format} && $Tag->{Format} =~ /plain/i ) { $Tag->{Content} = '
' . $Tag->{Content} . '
'; } if ( !$Description && $Tag->{Lang} eq 'en' ) { $Description = $Tag->{Content}; $Title = $Tag->{Title}; } if ( $Tag->{Lang} eq $Language ) { $Description = $Tag->{Content}; $Title = $Tag->{Title}; } } if ( !$Description && $Use ) { for my $Tag ( @{ $Param{Info} } ) { if ( !$Description ) { $Description = $Tag->{Content}; $Title = $Tag->{Title}; } } } } return if !$Description && !$Title; return ( Description => $Description, Title => $Title, ); } sub _DocumentationGet { my ( $Self, %Param ) = @_; return if !$Param{Filelist}; return if ref $Param{Filelist} ne 'ARRAY'; # find the correct user language documentation file my $DocumentationFileUserLanguage; # find the correct default language documentation file my $DocumentationFileDefaultLanguage; # find the correct en documentation file my $DocumentationFile; # remember fallback file my $DocumentationFileFallback; # get default language my $DefaultLanguage = $Kernel::OM->Get('Kernel::Config')->Get('DefaultLanguage'); # find documentation files FILE: for my $File ( @{ $Param{Filelist} } ) { next FILE if !$File; next FILE if !$File->{Location}; my ( $Dir, $Filename ) = $File->{Location} =~ m{ \A doc/ ( .+ ) / ( .+? .pdf ) }xmsi; next FILE if !$Dir; next FILE if !$Filename; # take user language first if ( $Dir eq $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{UserLanguage} ) { $DocumentationFileUserLanguage = $File->{Location}; } # take default language next elsif ( $Dir eq $DefaultLanguage ) { $DocumentationFileDefaultLanguage = $File->{Location}; } # take en language next elsif ( $Dir eq 'en' && !$DocumentationFile ) { $DocumentationFile = $File->{Location}; } # remember fallback file $DocumentationFileFallback = $File->{Location}; } # set fallback file (if exists) as documentation file my %Doc; if ($DocumentationFileUserLanguage) { $Doc{Location} = $DocumentationFileUserLanguage; } elsif ($DocumentationFileDefaultLanguage) { $Doc{Location} = $DocumentationFileDefaultLanguage; } elsif ($DocumentationFile) { $Doc{Location} = $DocumentationFile; } elsif ($DocumentationFileFallback) { $Doc{Location} = $DocumentationFileFallback; } return %Doc; } sub _InstallHandling { my ( $Self, %Param ) = @_; my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # check needed params if ( !$Param{Package} ) { return $LayoutObject->ErrorScreen( Message => Translatable('No such package!'), ); } my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); # redirect after finishing installation if ( $ParamObject->GetParam( Param => 'IntroInstallPost' ) ) { return $LayoutObject->Redirect( OP => "Action=$Self->{Action}" ); } my $IntroInstallPre = $ParamObject->GetParam( Param => 'IntroInstallPre' ) || ''; my $IntroInstallVendor = $ParamObject->GetParam( Param => 'IntroInstallVendor' ) || ''; my $PackageObject = $Kernel::OM->Get('Kernel::System::Package'); # parse package my %Structure = $PackageObject->PackageParse( String => $Param{Package} ); # online verification my $Verified = $PackageObject->PackageVerify( Package => $Param{Package}, Structure => \%Structure, ) || 'verified'; my %VerifyInfo = $PackageObject->PackageVerifyInfo(); # translate description if ( $LayoutObject->{LanguageObject} ) { $VerifyInfo{Description} = $LayoutObject->{LanguageObject}->Translate( $VerifyInfo{Description}, $VerifyInfo{PackageInstallPossible} ? '' : $LayoutObject->{Baselink}, ); } # vendor screen if ( !$IntroInstallVendor && !$IntroInstallPre && $Verified ne 'verified' ) { $LayoutObject->Block( Name => 'Intro', Data => { %Param, %VerifyInfo, Subaction => $Self->{Subaction}, Type => 'IntroInstallVendor', Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, }, ); if ( $VerifyInfo{PackageInstallPossible} ) { $LayoutObject->Block( Name => 'IntroForm', Data => { %Param, %VerifyInfo, Subaction => $Self->{Subaction}, Type => 'IntroInstallVendor', Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, }, ); $LayoutObject->Block( Name => 'IntroCancel', ); } my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPackageManager', ); $Output .= $LayoutObject->Footer(); return $Output; } # intro screen my %Data; if ( $Structure{IntroInstall} ) { %Data = $Self->_MessageGet( Info => $Structure{IntroInstall}, Type => 'pre' ); } # get cloud repositories my $RepositoryCloudList; if ( !$Self->{CloudServicesDisabled} ) { $RepositoryCloudList = $PackageObject->RepositoryCloudList(); } # in case Source is present on repository cloud list # the package should be retrieved using the CloudService backend my $FromCloud = 0; if ( $Param{Source} && $RepositoryCloudList->{ $Param{Source} } ) { $FromCloud = 1; } my %Response = $PackageObject->AnalyzePackageFrameworkRequirements( Framework => $Structure{Framework}, NoLog => 1, ); if ( !$Response{Success} ) { $LayoutObject->Block( Name => 'IncompatibleInfo', Data => { %Param, %Data, Subaction => $Self->{Subaction}, Type => 'InstallIncompatible', Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, RequiredMinimumVersion => $Response{RequiredFrameworkMinimum}, RequiredMaximumVersion => $Response{RequiredFrameworkMaximum}, RequiredFramework => $Response{RequiredFramework}, }, ); my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPackageManager', ); $Output .= $LayoutObject->Footer(); return $Output; } # intro before installation if ( %Data && !$IntroInstallPre ) { $LayoutObject->Block( Name => 'Intro', Data => { %Param, %Data, Subaction => $Self->{Subaction}, Type => 'IntroInstallPre', Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, }, ); if ( $Verified eq 'verified' && !$Self->{CloudServicesDisabled} ) { $LayoutObject->Block( Name => 'OTRSVerifyLogo', ); } $LayoutObject->Block( Name => 'IntroForm', Data => { %Param, %Data, Subaction => $Self->{Subaction}, Type => 'IntroInstallPre', Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, }, ); $LayoutObject->Block( Name => 'IntroCancel', ); my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPackageManager', ); $Output .= $LayoutObject->Footer(); return $Output; } # install package elsif ( $PackageObject->PackageInstall( String => $Param{Package}, FromCloud => $FromCloud ) ) { # intro screen my %Data; if ( $Structure{IntroInstall} ) { %Data = $Self->_MessageGet( Info => $Structure{IntroInstall}, Type => 'post' ); } if (%Data) { $LayoutObject->Block( Name => 'Intro', Data => { %Param, %Data, Subaction => 'Install', Type => 'IntroInstallPost', Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, }, ); $LayoutObject->Block( Name => 'IntroForm', Data => { %Param, %Data, Subaction => 'Install', Type => 'IntroInstallPost', Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, }, ); if ( $Verified eq 'verified' ) { $LayoutObject->Block( Name => 'OTRSVerifyLogo', ); } my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPackageManager', ); $Output .= $LayoutObject->Footer(); return $Output; } # redirect return $LayoutObject->Redirect( OP => "Action=$Self->{Action}" ); } return $LayoutObject->ErrorScreen(); } sub _UpgradeHandling { my ( $Self, %Param ) = @_; my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # check needed params if ( !$Param{Package} ) { return $LayoutObject->ErrorScreen( Message => Translatable('No such package!'), ); } my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); # redirect after finishing upgrade if ( $ParamObject->GetParam( Param => 'IntroUpgradePost' ) ) { return $LayoutObject->Redirect( OP => "Action=$Self->{Action}" ); } my $IntroUpgradePre = $ParamObject->GetParam( Param => 'IntroUpgradePre' ) || ''; my $PackageObject = $Kernel::OM->Get('Kernel::System::Package'); # check if we have to show upgrade intro pre my %Structure = $PackageObject->PackageParse( String => $Param{Package}, ); # intro screen my %Data; if ( $Structure{IntroUpgrade} ) { %Data = $Self->_MessageGet( Info => $Structure{IntroUpgrade}, Type => 'pre' ); } my %Response = $PackageObject->AnalyzePackageFrameworkRequirements( Framework => $Structure{Framework}, NoLog => 1, ); if ( !$Response{Success} ) { $LayoutObject->Block( Name => 'IncompatibleInfo', Data => { %Param, %Data, Subaction => $Self->{Subaction}, Type => 'UpgradeIncompatible', Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, RequiredMinimumVersion => $Response{RequiredFrameworkMinimum}, RequiredMaximumVersion => $Response{RequiredFrameworkMaximum}, RequiredFramework => $Response{RequiredFramework}, }, ); my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPackageManager', ); $Output .= $LayoutObject->Footer(); return $Output; } if ( %Data && !$IntroUpgradePre ) { $LayoutObject->Block( Name => 'Intro', Data => { %Param, %Data, Subaction => $Self->{Subaction}, Type => 'IntroUpgradePre', Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, }, ); $LayoutObject->Block( Name => 'IntroForm', Data => { %Param, %Data, Subaction => $Self->{Subaction}, Type => 'IntroUpgradePre', Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, }, ); $LayoutObject->Block( Name => 'IntroCancel', ); my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPackageManager', ); $Output .= $LayoutObject->Footer(); return $Output; } # upgrade elsif ( $PackageObject->PackageUpgrade( String => $Param{Package} ) ) { # intro screen my %Data; if ( $Structure{IntroUpgrade} ) { %Data = $Self->_MessageGet( Info => $Structure{IntroUpgrade}, Type => 'post' ); } if (%Data) { $LayoutObject->Block( Name => 'Intro', Data => { %Param, %Data, Subaction => '', Type => 'IntroUpgradePost', Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, }, ); $LayoutObject->Block( Name => 'IntroForm', Data => { %Param, %Data, Subaction => '', Type => 'IntroUpgradePost', Name => $Structure{Name}->{Content}, Version => $Structure{Version}->{Content}, }, ); my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminPackageManager', ); $Output .= $LayoutObject->Footer(); return $Output; } # redirect return $LayoutObject->Redirect( OP => "Action=$Self->{Action}" ); } return $LayoutObject->ErrorScreen(); } sub _GetFeatureAddonData { my ( $Self, %Param ) = @_; my $Language = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{UserLanguage}; # cleanup main language for languages like es_MX (es in this case) $Language = substr $Language, 0, 2; my $CacheKey = "FeatureAddonData::$Language"; my $CacheTTL = 60 * 60 * 24; # 1 day my $CacheType = 'PackageManager'; my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); my $CacheResult = $CacheObject->Get( Type => $CacheType, Key => $CacheKey, ); return $CacheResult if ref $CacheResult eq 'ARRAY'; my $CloudService = 'PublicFeeds'; my $Operation = 'FAOFeed'; # prepare cloud service request my %RequestParams = ( RequestData => { $CloudService => [ { Operation => $Operation, Data => { Language => $Language, }, }, ], }, ); my $CloudServiceObject = $Kernel::OM->Get('Kernel::System::CloudService::Backend::Run'); # dispatch the cloud service request my $RequestResult = $CloudServiceObject->Request(%RequestParams); # as this is the only operation an unsuccessful request means that the operation was also # unsuccessful if ( !IsHashRefWithData($RequestResult) ) { return Translatable('Can\'t connect to OTRS Feature Add-on list server!'); } my $OperationResult = $CloudServiceObject->OperationResultGet( RequestResult => $RequestResult, CloudService => $CloudService, Operation => $Operation, ); if ( !IsHashRefWithData($OperationResult) ) { return Translatable('Can\'t get OTRS Feature Add-on list from server!'); } elsif ( !$OperationResult->{Success} ) { return $OperationResult->{ErrorMessage} || Translatable('Can\'t get OTRS Feature Add-on from server!'); } my $FAOFeed = $OperationResult->{Data}->{FAOs}; return if !IsArrayRefWithData($FAOFeed); # set cache $CacheObject->Set( Type => $CacheType, Key => $CacheKey, Value => $FAOFeed, TTL => $CacheTTL, ); return $FAOFeed; } 1;