1# -- 2# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/ 3# -- 4# This software comes with ABSOLUTELY NO WARRANTY. For details, see 5# the enclosed file COPYING for license information (GPL). If you 6# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt. 7# -- 8 9package Kernel::System::Console::Command::Admin::Package::List; 10 11use strict; 12use utf8; 13use warnings; 14 15use parent qw(Kernel::System::Console::BaseCommand); 16 17our @ObjectDependencies = ( 18 'Kernel::Config', 19 'Kernel::System::Cache', 20 'Kernel::System::Main', 21 'Kernel::System::Package', 22); 23 24sub Configure { 25 my ( $Self, %Param ) = @_; 26 27 $Self->Description('List all installed OTRS packages.'); 28 29 $Self->AddOption( 30 Name => 'package-name', 31 Description => '(Part of) package name to filter for. Omit to show all installed packages.', 32 Required => 0, 33 HasValue => 1, 34 ValueRegex => qr/.*/, 35 ); 36 37 $Self->AddOption( 38 Name => 'show-deployment-info', 39 Description => 'Show package and files status (package deployment info).', 40 Required => 0, 41 HasValue => 0, 42 ); 43 44 $Self->AddOption( 45 Name => 'show-verification-info', 46 Description => 'Show package OTRS Verify™ status.', 47 Required => 0, 48 HasValue => 0, 49 ); 50 51 $Self->AddOption( 52 Name => 'delete-verification-cache', 53 Description => 'Delete OTRS Verify™ cache, so verification info is fetch again from OTRS group servers.', 54 Required => 0, 55 HasValue => 0, 56 ); 57 58 return; 59} 60 61sub PreRun { 62 my ( $Self, %Param ) = @_; 63 64 my $ShowVerificationInfoOption = $Self->GetOption('show-verification-info'); 65 my $DeleteVerificationCacheOption = $Self->GetOption('delete-verification-cache'); 66 67 if ( $DeleteVerificationCacheOption && !$ShowVerificationInfoOption ) { 68 die "--delete-verification-cache requires --show-verification-info"; 69 } 70 71 return; 72} 73 74sub Run { 75 my ( $Self, %Param ) = @_; 76 77 $Self->Print("<yellow>Listing all installed packages...</yellow>\n"); 78 79 my @Packages = $Kernel::OM->Get('Kernel::System::Package')->RepositoryList(); 80 81 if ( !@Packages ) { 82 $Self->Print("<green>There are no packages installed.</green>\n"); 83 return $Self->ExitCodeOk(); 84 } 85 86 my $PackageNameOption = $Self->GetOption('package-name'); 87 my $ShowDeploymentInfoOption = $Self->GetOption('show-deployment-info'); 88 my $ShowVerificationInfoOption = $Self->GetOption('show-verification-info'); 89 my $DeleteVerificationCacheOption = $Self->GetOption('delete-verification-cache'); 90 91 my $CloudServicesDisabled = $Kernel::OM->Get('Kernel::Config')->Get('CloudServices::Disabled') || 0; 92 93 # Do not show verification status is cloud services are disabled. 94 if ( $CloudServicesDisabled && $ShowVerificationInfoOption ) { 95 $ShowVerificationInfoOption = 0; 96 $Self->Print("<red>Cloud Services are disabled OTRS Verify information can not be retrieved</red>\n"); 97 } 98 99 # Get package object 100 my $PackageObject = $Kernel::OM->Get('Kernel::System::Package'); 101 102 my %VerificationInfo; 103 104 PACKAGE: 105 for my $Package (@Packages) { 106 107 # Just show if PackageIsVisible flag is enabled. 108 if ( 109 defined $Package->{PackageIsVisible} 110 && !$Package->{PackageIsVisible}->{Content} 111 ) 112 { 113 next PACKAGE; 114 } 115 116 if ( defined $PackageNameOption && length $PackageNameOption ) { 117 my $PackageString = $Package->{Name}->{Content} . '-' . $Package->{Version}->{Content}; 118 next PACKAGE if $PackageString !~ m{$PackageNameOption}i; 119 } 120 121 my %Data = $Self->_PackageMetadataGet( 122 Tag => $Package->{Description}, 123 StripHTML => 0, 124 ); 125 $Self->Print("+----------------------------------------------------------------------------+\n"); 126 $Self->Print("| <yellow>Name:</yellow> $Package->{Name}->{Content}\n"); 127 $Self->Print("| <yellow>Version:</yellow> $Package->{Version}->{Content}\n"); 128 $Self->Print("| <yellow>Vendor:</yellow> $Package->{Vendor}->{Content}\n"); 129 $Self->Print("| <yellow>URL:</yellow> $Package->{URL}->{Content}\n"); 130 $Self->Print("| <yellow>License:</yellow> $Package->{License}->{Content}\n"); 131 $Self->Print("| <yellow>Description:</yellow> $Data{Description}\n"); 132 133 if ($ShowDeploymentInfoOption) { 134 my $PackageDeploymentOK = $PackageObject->DeployCheck( 135 Name => $Package->{Name}->{Content}, 136 Version => $Package->{Version}->{Content}, 137 Log => 0, 138 ); 139 140 my %PackageDeploymentInfo = $PackageObject->DeployCheckInfo(); 141 if ( defined $PackageDeploymentInfo{File} && %{ $PackageDeploymentInfo{File} } ) { 142 $Self->Print( 143 '| <red>Deployment:</red> ' . ( $PackageDeploymentOK ? 'OK' : 'Not OK' ) . "\n" 144 ); 145 for my $File ( sort keys %{ $PackageDeploymentInfo{File} } ) { 146 my $FileMessage = $PackageDeploymentInfo{File}->{$File}; 147 $Self->Print("| <red>File Status:</red> $File => $FileMessage\n"); 148 } 149 } 150 else { 151 $Self->Print( 152 '| <yellow>Pck. Status:</yellow> ' . ( $PackageDeploymentOK ? 'OK' : 'Not OK' ) . "\n" 153 ); 154 } 155 } 156 157 if ($ShowVerificationInfoOption) { 158 159 if ( !%VerificationInfo ) { 160 161 # Clear the package verification cache to get fresh results. 162 if ($DeleteVerificationCacheOption) { 163 $Kernel::OM->Get('Kernel::System::Cache')->CleanUp( 164 Type => 'PackageVerification', 165 ); 166 } 167 168 # Get verification info for all packages (this will create the cache again). 169 %VerificationInfo = $PackageObject->PackageVerifyAll(); 170 } 171 172 if ( 173 !defined $VerificationInfo{ $Package->{Name}->{Content} } 174 || $VerificationInfo{ $Package->{Name}->{Content} } ne 'verified' 175 ) 176 { 177 $Self->Print("| <red>OTRS Verify:</red> Not Verified\n"); 178 } 179 else { 180 $Self->Print("| <yellow>OTRS Verify:</yellow> Verified\n"); 181 } 182 } 183 } 184 $Self->Print("+----------------------------------------------------------------------------+\n"); 185 186 $Self->Print("<green>Done.</green>\n"); 187 return $Self->ExitCodeOk(); 188} 189 190# =item _PackageMetadataGet() 191# 192# locates information in tags that are language specific. 193# First, 'en' is looked for, if that is not present, the first found language will be used. 194# 195# my %Data = $CommandObject->_PackageMetadataGet( 196# Tag => $Package->{Description}, 197# StripHTML => 1, # optional, perform HTML->ASCII conversion (default 1) 198# ); 199# 200# my %Data = $Self->_PackageMetadataGet( 201# Tag => $Structure{IntroInstallPost}, 202# AttributeFilterKey => 'Type', 203# AttributeFilterValue => 'pre', 204# ); 205# 206# Returns the content and the title of the tag in a hash: 207# 208# my %Result = ( 209# Description => '...', # tag content 210# Title => '...', # tag title 211# ); 212# 213# =cut 214 215sub _PackageMetadataGet { 216 my ( $Self, %Param ) = @_; 217 218 return if !ref $Param{Tag}; 219 220 my $AttributeFilterKey = $Param{AttributeFilterKey}; 221 my $AttributeFilterValue = $Param{AttributeFilterValue}; 222 223 my $Title = ''; 224 my $Description = ''; 225 226 TAG: 227 for my $Tag ( @{ $Param{Tag} } ) { 228 if ($AttributeFilterKey) { 229 if ( lc $Tag->{$AttributeFilterKey} ne lc $AttributeFilterValue ) { 230 next TAG; 231 } 232 } 233 if ( !$Description && $Tag->{Lang} eq 'en' ) { 234 $Description = $Tag->{Content} || ''; 235 $Title = $Tag->{Title} || ''; 236 } 237 } 238 if ( !$Description ) { 239 TAG: 240 for my $Tag ( @{ $Param{Tag} } ) { 241 if ($AttributeFilterKey) { 242 if ( lc $Tag->{$AttributeFilterKey} ne lc $AttributeFilterValue ) { 243 next TAG; 244 } 245 } 246 if ( !$Description ) { 247 $Description = $Tag->{Content} || ''; 248 $Title = $Tag->{Title} || ''; 249 } 250 } 251 } 252 253 if ( !defined $Param{StripHTML} || $Param{StripHTML} ) { 254 $Title =~ s/(.{4,78})(?:\s|\z)/| $1\n/gm; 255 $Description =~ s/^\s*//mg; 256 $Description =~ s/\n/ /gs; 257 $Description =~ s/\r/ /gs; 258 $Description =~ s/\<style.+?\>.*\<\/style\>//gsi; 259 $Description =~ s/\<br(\/|)\>/\n/gsi; 260 $Description =~ s/\<(hr|hr.+?)\>/\n\n/gsi; 261 $Description =~ s/\<(\/|)(pre|pre.+?|p|p.+?|table|table.+?|code|code.+?)\>/\n\n/gsi; 262 $Description =~ s/\<(tr|tr.+?|th|th.+?)\>/\n\n/gsi; 263 $Description =~ s/\.+?<\/(td|td.+?)\>/ /gsi; 264 $Description =~ s/\<.+?\>//gs; 265 $Description =~ s/ / /mg; 266 $Description =~ s/&/&/g; 267 $Description =~ s/</</g; 268 $Description =~ s/>/>/g; 269 $Description =~ s/"/"/g; 270 $Description =~ s/ / /g; 271 $Description =~ s/^\s*\n\s*\n/\n/mg; 272 $Description =~ s/(.{4,78})(?:\s|\z)/| $1\n/gm; 273 } 274 return ( 275 Description => $Description, 276 Title => $Title, 277 ); 278} 279 280sub _PackageContentGet { 281 my ( $Self, %Param ) = @_; 282 283 my $FileString; 284 285 if ( -e $Param{Location} ) { 286 my $ContentRef = $Kernel::OM->Get('Kernel::System::Main')->FileRead( 287 Location => $Param{Location}, 288 Mode => 'utf8', # optional - binmode|utf8 289 Result => 'SCALAR', # optional - SCALAR|ARRAY 290 ); 291 if ($ContentRef) { 292 $FileString = ${$ContentRef}; 293 } 294 else { 295 $Self->PrintError("Can't open: $Param{Location}: $!"); 296 return; 297 } 298 } 299 elsif ( $Param{Location} =~ /^(online|.*):(.+?)$/ ) { 300 my $URL = $1; 301 my $PackageName = $2; 302 if ( $URL eq 'online' ) { 303 my %List = %{ $Kernel::OM->Get('Kernel::Config')->Get('Package::RepositoryList') }; 304 %List = ( 305 %List, 306 $Kernel::OM->Get('Kernel::System::Package')->PackageOnlineRepositories() 307 ); 308 for ( sort keys %List ) { 309 if ( $List{$_} =~ /^\[-Master-\]/ ) { 310 $URL = $_; 311 } 312 } 313 } 314 if ( $PackageName !~ /^.+?.opm$/ ) { 315 my @Packages = $Kernel::OM->Get('Kernel::System::Package')->PackageOnlineList( 316 URL => $URL, 317 Lang => $Kernel::OM->Get('Kernel::Config')->Get('DefaultLanguage'), 318 ); 319 PACKAGE: 320 for my $Package (@Packages) { 321 if ( $Package->{Name} eq $PackageName ) { 322 $PackageName = $Package->{File}; 323 last PACKAGE; 324 } 325 } 326 } 327 $FileString = $Kernel::OM->Get('Kernel::System::Package')->PackageOnlineGet( 328 Source => $URL, 329 File => $PackageName, 330 ); 331 if ( !$FileString ) { 332 $Self->PrintError("No such file '$Param{Location}' in $URL!"); 333 return; 334 } 335 } 336 else { 337 if ( $Param{Location} =~ /^(.*)\-(\d{1,4}\.\d{1,4}\.\d{1,4})$/ ) { 338 $FileString = $Kernel::OM->Get('Kernel::System::Package')->RepositoryGet( 339 Name => $1, 340 Version => $2, 341 ); 342 } 343 else { 344 PACKAGE: 345 for my $Package ( $Kernel::OM->Get('Kernel::System::Package')->RepositoryList() ) { 346 if ( $Param{Location} eq $Package->{Name}->{Content} ) { 347 $FileString = $Kernel::OM->Get('Kernel::System::Package')->RepositoryGet( 348 Name => $Package->{Name}->{Content}, 349 Version => $Package->{Version}->{Content}, 350 ); 351 last PACKAGE; 352 } 353 } 354 } 355 if ( !$FileString ) { 356 $Self->PrintError("No such file '$Param{Location}' or invalid 'package-version'!"); 357 return; 358 } 359 } 360 361 return $FileString; 362} 363 3641; 365