Files
scripts/Perl OTRS/Kernel/Modules/AdminPackageManager.pm
2024-10-14 00:08:40 +02:00

2535 lines
88 KiB
Perl

# --
# 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} = '<pre class="contentbody">' . $Hash->{Content} . '</pre>';
}
$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} = '<pre class="contentbody">' . $Hash->{Content} . '</pre>';
}
$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} = '<pre class="contentbody">' . $Tag->{Content} . '</pre>';
}
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;