# -- # 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;