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

1354 lines
49 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::AgentITSMConfigItemSearch;
use strict;
use warnings;
use Kernel::Language qw(Translatable);
use Kernel::System::VariableCheck qw(:all);
our $ObjectManagerDisabled = 1;
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {%Param};
bless( $Self, $Type );
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
my $Output;
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# get config of frontend module
$Self->{Config} = $ConfigObject->Get("ITSMConfigItem::Frontend::$Self->{Action}");
# get param object
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
# get config data
$Self->{StartHit} = int( $ParamObject->GetParam( Param => 'StartHit' ) || 1 );
$Self->{SearchLimit} = $Self->{Config}->{SearchLimit} || 10000;
$Self->{SortBy} = $ParamObject->GetParam( Param => 'SortBy' )
|| $Self->{Config}->{'SortBy::Default'}
|| 'Number';
$Self->{OrderBy} = $ParamObject->GetParam( Param => 'OrderBy' )
|| $Self->{Config}->{'Order::Default'}
|| 'Down';
$Self->{Profile} = $ParamObject->GetParam( Param => 'Profile' ) || '';
$Self->{SaveProfile} = $ParamObject->GetParam( Param => 'SaveProfile' ) || '';
$Self->{TakeLastSearch} = $ParamObject->GetParam( Param => 'TakeLastSearch' );
# get general catalog object
my $GeneralCatalogObject = $Kernel::OM->Get('Kernel::System::GeneralCatalog');
# get class list
my $ClassList = $GeneralCatalogObject->ItemList(
Class => 'ITSM::ConfigItem::Class',
);
# get config item object
my $ConfigItemObject = $Kernel::OM->Get('Kernel::System::ITSMConfigItem');
# check for access rights on the classes
for my $ClassID ( sort keys %{$ClassList} ) {
my $HasAccess = $ConfigItemObject->Permission(
Type => $Self->{Config}->{Permission},
Scope => 'Class',
ClassID => $ClassID,
UserID => $Self->{UserID},
);
delete $ClassList->{$ClassID} if !$HasAccess;
}
# get class id
my $ClassID = $ParamObject->GetParam( Param => 'ClassID' );
# get layout object
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
# check if class id is valid
if ( $ClassID && !$ClassList->{$ClassID} ) {
return $LayoutObject->ErrorScreen(
Message => Translatable('Invalid ClassID!'),
Comment => Translatable('Please contact the administrator.'),
);
}
# get single params
my %GetParam;
# get search profile object
my $SearchProfileObject = $Kernel::OM->Get('Kernel::System::SearchProfile');
# load profiles string params
if ( ( $ClassID && $Self->{Profile} ) && $Self->{TakeLastSearch} ) {
%GetParam = $SearchProfileObject->SearchProfileGet(
Base => 'ConfigItemSearch' . $ClassID,
Name => $Self->{Profile},
UserLogin => $Self->{UserLogin},
);
}
# ------------------------------------------------------------ #
# delete search profiles
# ------------------------------------------------------------ #
if ( $Self->{Subaction} eq 'AJAXProfileDelete' ) {
# remove old profile stuff
$SearchProfileObject->SearchProfileDelete(
Base => 'ConfigItemSearch' . $ClassID,
Name => $Self->{Profile},
UserLogin => $Self->{UserLogin},
);
my $Output = $LayoutObject->JSONEncode(
Data => 1,
);
return $LayoutObject->Attachment(
NoCache => 1,
ContentType => 'text/html',
Content => $Output,
Type => 'inline',
);
}
# ------------------------------------------------------------ #
# init search dialog (select class)
# ------------------------------------------------------------ #
elsif ( $Self->{Subaction} eq 'AJAX' ) {
# generate dropdown for selecting the class
# automatically show search mask after selecting a class via AJAX
my $ClassOptionStrg = $LayoutObject->BuildSelection(
Data => $ClassList,
Name => 'SearchClassID',
PossibleNone => 1,
SelectedID => $ClassID || '',
Translation => 1,
Class => 'Modernize',
);
# html search mask output
$LayoutObject->Block(
Name => 'SearchAJAX',
Data => {
ClassOptionStrg => $ClassOptionStrg,
Profile => $Self->{Profile},
ClassID => $ClassID,
},
);
# output template
$Output = $LayoutObject->Output(
TemplateFile => 'AgentITSMConfigItemSearch',
);
return $LayoutObject->Attachment(
NoCache => 1,
ContentType => 'text/html',
Content => $Output,
Type => 'inline',
);
}
# ------------------------------------------------------------ #
# set search fields for selected class
# ------------------------------------------------------------ #
elsif ( $Self->{Subaction} eq 'AJAXUpdate' ) {
# ClassID is required for the search mask and for actual searching
if ( !$ClassID ) {
return $LayoutObject->ErrorScreen(
Message => Translatable('No ClassID is given!'),
Comment => Translatable('Please contact the administrator.'),
);
}
# check if user is allowed to search class
my $HasAccess = $ConfigItemObject->Permission(
Type => $Self->{Config}->{Permission},
Scope => 'Class',
ClassID => $ClassID,
UserID => $Self->{UserID},
);
# show error screen
if ( !$HasAccess ) {
return $LayoutObject->ErrorScreen(
Message => Translatable('No access rights for this class given!'),
Comment => Translatable('Please contact the administrator.'),
);
}
# get current definition
my $XMLDefinition = $ConfigItemObject->DefinitionGet(
ClassID => $ClassID,
);
# abort, if no definition is defined
if ( !$XMLDefinition->{DefinitionID} ) {
return $LayoutObject->ErrorScreen(
Message =>
$LayoutObject->{LanguageObject}->Translate( 'No definition was defined for class %s!', $ClassID ),
Comment => Translatable('Please contact the administrator.'),
);
}
my @XMLAttributes = (
{
Key => 'Number',
Value => Translatable('Number'),
},
{
Key => 'Name',
Value => Translatable('Name'),
},
{
Key => 'DeplStateIDs',
Value => Translatable('Deployment State'),
},
{
Key => 'InciStateIDs',
Value => Translatable('Incident State'),
},
);
my %GetParam = $SearchProfileObject->SearchProfileGet(
Base => 'ConfigItemSearch' . $ClassID,
Name => $Self->{Profile},
UserLogin => $Self->{UserLogin},
);
# get attributes to include in attributes string
if ( $XMLDefinition->{Definition} ) {
$Self->_XMLSearchAttributesGet(
XMLDefinition => $XMLDefinition->{DefinitionRef},
XMLAttributes => \@XMLAttributes,
);
}
# build attributes string for attributes list
$Param{AttributesStrg} = $LayoutObject->BuildSelection(
PossibleNone => 1,
Data => \@XMLAttributes,
Name => 'Attribute',
Multiple => 0,
Class => 'Modernize',
);
# build attributes string for recovery on add or subtract search fields
$Param{AttributesOrigStrg} = $LayoutObject->BuildSelection(
PossibleNone => 1,
Data => \@XMLAttributes,
Name => 'AttributeOrig',
Multiple => 0,
Class => 'Modernize',
);
my %Profiles = $SearchProfileObject->SearchProfileList(
Base => 'ConfigItemSearch' . $ClassID,
UserLogin => $Self->{UserLogin},
);
$Param{ProfilesStrg} = $LayoutObject->BuildSelection(
Data => \%Profiles,
Name => 'Profile',
ID => 'SearchProfile',
SelectedID => $Self->{Profile},
Class => 'Modernize',
PossibleNone => 1,
);
# get deployment state list
my $DeplStateList = $GeneralCatalogObject->ItemList(
Class => 'ITSM::ConfigItem::DeploymentState',
);
# generate dropdown for selecting the wanted deployment states
my $CurDeplStateOptionStrg = $LayoutObject->BuildSelection(
Data => $DeplStateList,
Name => 'DeplStateIDs',
SelectedID => $GetParam{DeplStateIDs} || [],
Size => 5,
Multiple => 1,
Class => 'Modernize',
);
# get incident state list
my $InciStateList = $GeneralCatalogObject->ItemList(
Class => 'ITSM::Core::IncidentState',
);
# generate dropdown for selecting the wanted incident states
my $CurInciStateOptionStrg = $LayoutObject->BuildSelection(
Data => $InciStateList,
Name => 'InciStateIDs',
SelectedID => $GetParam{InciStateIDs} || [],
Size => 5,
Multiple => 1,
Class => 'Modernize',
);
# generate PreviousVersionOptionStrg
my $PreviousVersionOptionStrg = $LayoutObject->BuildSelection(
Name => 'PreviousVersionSearch',
Data => {
0 => Translatable('No'),
1 => Translatable('Yes'),
},
SelectedID => $GetParam{PreviousVersionSearch} || '0',
Class => 'Modernize',
);
# build output format string
$Param{ResultFormStrg} = $LayoutObject->BuildSelection(
Data => {
Normal => Translatable('Normal'),
Print => Translatable('Print'),
CSV => Translatable('CSV'),
Excel => Translatable('Excel'),
},
Name => 'ResultForm',
SelectedID => $GetParam{ResultForm} || 'Normal',
Class => 'Modernize',
);
$LayoutObject->Block(
Name => 'AJAXContent',
Data => {
ClassID => $ClassID,
CurDeplStateOptionStrg => $CurDeplStateOptionStrg,
CurInciStateOptionStrg => $CurInciStateOptionStrg,
PreviousVersionOptionStrg => $PreviousVersionOptionStrg,
AttributesStrg => $Param{AttributesStrg},
AttributesOrigStrg => $Param{AttributesOrigStrg},
ResultFormStrg => $Param{ResultFormStrg},
ProfilesStrg => $Param{ProfilesStrg},
Number => $GetParam{Number} || '',
Name => $GetParam{Name} || '',
},
);
# output xml search form
if ( $XMLDefinition->{Definition} ) {
$Self->_XMLSearchFormOutput(
XMLDefinition => $XMLDefinition->{DefinitionRef},
XMLAttributes => \@XMLAttributes,
GetParam => \%GetParam,
);
}
my @ProfileAttributes;
# show attributes
my $AttributeIsUsed = 0;
KEY:
for my $Key ( sort keys %GetParam ) {
next KEY if !$Key;
next KEY if !defined $GetParam{$Key};
next KEY if $GetParam{$Key} eq '';
$AttributeIsUsed = 1;
push @ProfileAttributes, $Key;
}
# if no attribute is shown, show configitem number
if ( !$Self->{Profile} ) {
push @ProfileAttributes, 'Number';
}
$LayoutObject->AddJSData(
Key => 'ITSMSearchProfileAttributes',
Value => \@ProfileAttributes,
);
# output template
$Output = $LayoutObject->Output(
TemplateFile => 'AgentITSMConfigItemSearch',
AJAX => 1,
);
return $LayoutObject->Attachment(
NoCache => 1,
ContentType => 'text/html',
Content => $Output,
Type => 'inline',
);
}
# ------------------------------------------------------------ #
# perform search
# ------------------------------------------------------------ #
elsif ( $Self->{Subaction} eq 'Search' ) {
my $SearchDialog = $ParamObject->GetParam( Param => 'SearchDialog' );
# fill up profile name (e.g. with last-search)
if ( !$Self->{Profile} || !$Self->{SaveProfile} ) {
$Self->{Profile} = 'last-search';
}
# store last overview screen
my $URL = "Action=AgentITSMConfigItemSearch;Profile=$Self->{Profile};"
. "TakeLastSearch=1;StartHit=$Self->{StartHit};Subaction=Search;"
. "OrderBy=$Self->{OrderBy};SortBy=$Self->{SortBy}";
if ($ClassID) {
$URL .= ";ClassID=$ClassID";
}
# get session object
my $SessionObject = $Kernel::OM->Get('Kernel::System::AuthSession');
$SessionObject->UpdateSessionID(
SessionID => $Self->{SessionID},
Key => 'LastScreenOverview',
Value => $URL,
);
$SessionObject->UpdateSessionID(
SessionID => $Self->{SessionID},
Key => 'LastScreenView',
Value => $URL,
);
# ClassID is required for the search mask and for actual searching
if ( !$ClassID ) {
return $LayoutObject->ErrorScreen(
Message => Translatable('No ClassID is given!'),
Comment => Translatable('Please contact the administrator.'),
);
}
# check if user is allowed to search class
my $HasAccess = $ConfigItemObject->Permission(
Type => $Self->{Config}->{Permission},
Scope => 'Class',
ClassID => $ClassID,
UserID => $Self->{UserID},
);
# show error screen
if ( !$HasAccess ) {
return $LayoutObject->ErrorScreen(
Message => Translatable('No access rights for this class given!'),
Comment => Translatable('Please contact the administrator.'),
);
}
# get current definition
my $XMLDefinition = $ConfigItemObject->DefinitionGet(
ClassID => $ClassID,
);
# abort, if no definition is defined
if ( !$XMLDefinition->{DefinitionID} ) {
return $LayoutObject->ErrorScreen(
Message =>
$LayoutObject->{LanguageObject}->Translate( 'No definition was defined for class %s!', $ClassID ),
Comment => Translatable('Please contact the administrator.'),
);
}
# get scalar search attributes (special handling for Number and Name)
FORMVALUE:
for my $FormValue (qw(Number Name)) {
my $Value = $ParamObject->GetParam( Param => $FormValue );
# must be defined and not be an empty string
# BUT the number 0 is an allowed value
next FORMVALUE if !defined $Value;
next FORMVALUE if $Value eq '';
$GetParam{$FormValue} = $Value;
}
# get ther scalar search attributes
FORMVALUE:
for my $FormValue (qw(PreviousVersionSearch ResultForm)) {
my $Value = $ParamObject->GetParam( Param => $FormValue );
next FORMVALUE if !$Value;
$GetParam{$FormValue} = $Value;
}
# get array search attributes
FORMARRAY:
for my $FormArray (qw(DeplStateIDs InciStateIDs)) {
my @Array = $ParamObject->GetArray( Param => $FormArray );
next FORMARRAY if !@Array;
$GetParam{$FormArray} = \@Array;
}
# get xml search form
my $XMLFormData = [];
my $XMLGetParam = [];
$Self->_XMLSearchFormGet(
XMLDefinition => $XMLDefinition->{DefinitionRef},
XMLFormData => $XMLFormData,
XMLGetParam => $XMLGetParam,
%GetParam,
);
if ( @{$XMLFormData} ) {
$GetParam{What} = $XMLFormData;
}
# save search profile (under last-search or real profile name)
$Self->{SaveProfile} = 1;
# remember last search values only if search is called from a search dialog
# not from results page
if ( $Self->{SaveProfile} && $Self->{Profile} && $SearchDialog ) {
# remove old profile stuff
$SearchProfileObject->SearchProfileDelete(
Base => 'ConfigItemSearch' . $ClassID,
Name => $Self->{Profile},
UserLogin => $Self->{UserLogin},
);
# insert new profile params
for my $Key ( sort keys %GetParam ) {
if ( $GetParam{$Key} && $Key ne 'What' ) {
$SearchProfileObject->SearchProfileAdd(
Base => 'ConfigItemSearch' . $ClassID,
Name => $Self->{Profile},
Key => $Key,
Value => $GetParam{$Key},
UserLogin => $Self->{UserLogin},
);
}
}
# insert new profile params also from XMLform
if ( @{$XMLGetParam} ) {
for my $Parameter ( @{$XMLGetParam} ) {
for my $Key ( sort keys %{$Parameter} ) {
if ( $Parameter->{$Key} ) {
$SearchProfileObject->SearchProfileAdd(
Base => 'ConfigItemSearch' . $ClassID,
Name => $Self->{Profile},
Key => $Key,
Value => $Parameter->{$Key},
UserLogin => $Self->{UserLogin},
);
}
}
}
}
}
my $SearchResultList = [];
# start search if called from a search dialog or from a results page
if ( $SearchDialog || $Self->{TakeLastSearch} ) {
# start search
$SearchResultList = $ConfigItemObject->ConfigItemSearchExtended(
%GetParam,
OrderBy => [ $Self->{SortBy} ],
OrderByDirection => [ $Self->{OrderBy} ],
Limit => $Self->{SearchLimit},
ClassIDs => [$ClassID],
);
}
# get param only if called from a search dialog, otherwise it must be already in %GetParam
# from a loaded profile
if ($SearchDialog) {
$GetParam{ResultForm} = $ParamObject->GetParam( Param => 'ResultForm' );
}
# CSV output
if (
$GetParam{ResultForm} eq 'CSV'
||
$GetParam{ResultForm} eq 'Excel'
)
{
my @CSVData;
my @CSVHead;
# mapping between header name and data field
my %Header2Data = (
'Class' => 'Class',
'Incident State' => 'InciState',
'Name' => 'Name',
'ConfigItemNumber' => 'Number',
'Deployment State' => 'DeplState',
'Version' => 'VersionID',
'Create Time' => 'CreateTime',
);
CONFIGITEMID:
for my $ConfigItemID ( @{$SearchResultList} ) {
# check for access rights
my $HasAccess = $ConfigItemObject->Permission(
Scope => 'Item',
ItemID => $ConfigItemID,
UserID => $Self->{UserID},
Type => $Self->{Config}->{Permission},
);
next CONFIGITEMID if !$HasAccess;
# get version
my $LastVersion = $ConfigItemObject->VersionGet(
ConfigItemID => $ConfigItemID,
XMLDataGet => 0,
);
# csv quote
if ( !@CSVHead ) {
@CSVHead = @{ $Self->{Config}->{SearchCSVData} };
}
# store data
my @Data;
for my $Header (@CSVHead) {
push @Data, $LastVersion->{ $Header2Data{$Header} };
}
push @CSVData, \@Data;
}
# csv quote
# translate non existing header may result in a garbage file
if ( !@CSVHead ) {
@CSVHead = @{ $Self->{Config}->{SearchCSVData} };
}
# translate headers
for my $Header (@CSVHead) {
# replace ConfigItemNumber header with the current ConfigItemNumber hook from sysconfig
if ( $Header eq 'ConfigItemNumber' ) {
$Header = $ConfigObject->Get('ITSMConfigItem::Hook');
}
else {
$Header = $LayoutObject->{LanguageObject}->Translate($Header);
}
}
my $CSVObject = $Kernel::OM->Get('Kernel::System::CSV');
my $CurSysDTObject = $Kernel::OM->Create('Kernel::System::DateTime');
if ( $GetParam{ResultForm} eq 'CSV' ) {
# Assemble CSV data.
my $CSV = $CSVObject->Array2CSV(
Head => \@CSVHead,
Data => \@CSVData,
Separator => $Self->{UserCSVSeparator},
);
# Return CSV to download.
return $LayoutObject->Attachment(
Filename => sprintf(
'change_search_%s.csv',
$CurSysDTObject->Format(
Format => '%F_%H-%M',
),
),
ContentType => "text/csv; charset=" . $LayoutObject->{UserCharset},
Content => $CSV,
);
}
elsif ( $GetParam{ResultForm} eq 'Excel' ) {
# Assemble Excel data.
my $Excel = $CSVObject->Array2CSV(
Head => \@CSVHead,
Data => \@CSVData,
Format => 'Excel',
);
# Return Excel to download.
return $LayoutObject->Attachment(
Filename => sprintf(
'change_search_%s.xlsx',
$CurSysDTObject->Format(
Format => '%F_%H-%M',
),
),
ContentType => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset="
. $LayoutObject->{UserCharset},
Content => $Excel,
);
}
}
# print PDF output
elsif ( $GetParam{ResultForm} eq 'Print' ) {
my @PDFData;
# get pdf object
my $PDFObject = $Kernel::OM->Get('Kernel::System::PDF');
CONFIGITEMID:
for my $ConfigItemID ( @{$SearchResultList} ) {
# check for access rights
my $HasAccess = $ConfigItemObject->Permission(
Scope => 'Item',
ItemID => $ConfigItemID,
UserID => $Self->{UserID},
Type => $Self->{Config}->{Permission},
);
next CONFIGITEMID if !$HasAccess;
# get version
my $LastVersion = $ConfigItemObject->VersionGet(
ConfigItemID => $ConfigItemID,
XMLDataGet => 0,
);
# set pdf rows
my @PDFRow;
for my $StoreData (qw(Class InciState Name Number DeplState VersionID CreateTime)) {
push @PDFRow, $LastVersion->{$StoreData};
}
push @PDFData, \@PDFRow;
}
# PDF Output
my $Title = $LayoutObject->{LanguageObject}->Translate('Configuration Item') . ' '
. $LayoutObject->{LanguageObject}->Translate('Search');
my $PrintedBy = $LayoutObject->{LanguageObject}->Translate('printed by');
my $Page = $LayoutObject->{LanguageObject}->Translate('Page');
my $Time = $LayoutObject->{Time};
# get maximum number of pages
my $MaxPages = $ConfigObject->Get('PDF::MaxPages');
if ( !$MaxPages || $MaxPages < 1 || $MaxPages > 1000 ) {
$MaxPages = 100;
}
# create the header
my $CellData;
# output 'No Result', if no content was given
if (@PDFData) {
$CellData->[0]->[0]->{Content} = $LayoutObject->{LanguageObject}->Translate('Class');
$CellData->[0]->[0]->{Font} = 'ProportionalBold';
$CellData->[0]->[1]->{Content} = $LayoutObject->{LanguageObject}->Translate('Incident State');
$CellData->[0]->[1]->{Font} = 'ProportionalBold';
$CellData->[0]->[2]->{Content} = $LayoutObject->{LanguageObject}->Translate('Name');
$CellData->[0]->[2]->{Font} = 'ProportionalBold';
$CellData->[0]->[3]->{Content} = $LayoutObject->{LanguageObject}->Translate('Number');
$CellData->[0]->[3]->{Font} = 'ProportionalBold';
$CellData->[0]->[4]->{Content} = $LayoutObject->{LanguageObject}->Translate('Deployment State');
$CellData->[0]->[4]->{Font} = 'ProportionalBold';
$CellData->[0]->[5]->{Content} = $LayoutObject->{LanguageObject}->Translate('Version');
$CellData->[0]->[5]->{Font} = 'ProportionalBold';
$CellData->[0]->[6]->{Content} = $LayoutObject->{LanguageObject}->Translate('Create Time');
$CellData->[0]->[6]->{Font} = 'ProportionalBold';
# create the content array
my $CounterRow = 1;
for my $Row (@PDFData) {
my $CounterColumn = 0;
for my $Content ( @{$Row} ) {
$CellData->[$CounterRow]->[$CounterColumn]->{Content} = $Content;
$CounterColumn++;
}
$CounterRow++;
}
}
else {
$CellData->[0]->[0]->{Content} = $LayoutObject->{LanguageObject}->Translate('No Result!');
}
# page params
my %PageParam;
$PageParam{PageOrientation} = 'landscape';
$PageParam{MarginTop} = 30;
$PageParam{MarginRight} = 40;
$PageParam{MarginBottom} = 40;
$PageParam{MarginLeft} = 40;
$PageParam{HeaderRight} = $Title;
# table params
my %TableParam;
$TableParam{CellData} = $CellData;
$TableParam{Type} = 'Cut';
$TableParam{FontSize} = 6;
$TableParam{Border} = 0;
$TableParam{BackgroundColorEven} = '#DDDDDD';
$TableParam{Padding} = 1;
$TableParam{PaddingTop} = 3;
$TableParam{PaddingBottom} = 3;
# create new pdf document
$PDFObject->DocumentNew(
Title => $ConfigObject->Get('Product') . ': ' . $Title,
Encode => $LayoutObject->{UserCharset},
);
# start table output
$PDFObject->PageNew(
%PageParam,
FooterRight => $Page . ' 1',
);
$PDFObject->PositionSet(
Move => 'relativ',
Y => -6,
);
# output title
$PDFObject->Text(
Text => $Title,
FontSize => 13,
);
$PDFObject->PositionSet(
Move => 'relativ',
Y => -6,
);
# output "printed by"
$PDFObject->Text(
Text => $PrintedBy . ' '
. $Self->{UserFullname} . ' '
. $Time,
FontSize => 9,
);
$PDFObject->PositionSet(
Move => 'relativ',
Y => -14,
);
PAGE:
for my $Count ( 2 .. $MaxPages ) {
# output table (or a fragment of it)
%TableParam = $PDFObject->Table(%TableParam);
# stop output or another page
if ( $TableParam{State} ) {
last PAGE;
}
else {
$PDFObject->PageNew(
%PageParam,
FooterRight => $Page . ' ' . $Count,
);
}
}
# return the pdf document
my $CurrentSystemDTObj = $Kernel::OM->Create('Kernel::System::DateTime');
my $PDFString = $PDFObject->DocumentOutput();
return $LayoutObject->Attachment(
Filename => sprintf(
'configitem_search_%s_%s.pdf',
$CurrentSystemDTObj->Format( Format => '%F_%H-%M' ),
),
ContentType => "application/pdf",
Content => $PDFString,
Type => 'inline',
);
}
# normal HTML output
else {
# start html page
my $Output = $LayoutObject->Header();
$Output .= $LayoutObject->NavigationBar();
$LayoutObject->Print( Output => \$Output );
$Output = '';
$Self->{Filter} = $ParamObject->GetParam( Param => 'Filter' ) || '';
$Self->{View} = $ParamObject->GetParam( Param => 'View' ) || '';
# show config items
my $LinkPage = 'Filter='
. $LayoutObject->Ascii2Html( Text => $Self->{Filter} )
. ';View=' . $LayoutObject->Ascii2Html( Text => $Self->{View} )
. ';SortBy=' . $LayoutObject->Ascii2Html( Text => $Self->{SortBy} )
. ';OrderBy='
. $LayoutObject->Ascii2Html( Text => $Self->{OrderBy} )
. ';Profile=' . $Self->{Profile} . ';TakeLastSearch=1;Subaction=Search'
. ';ClassID=' . $ClassID
. ';';
my $LinkSort = 'Filter='
. $LayoutObject->Ascii2Html( Text => $Self->{Filter} )
. ';View=' . $LayoutObject->Ascii2Html( Text => $Self->{View} )
. ';Profile=' . $Self->{Profile} . ';TakeLastSearch=1;Subaction=Search'
. ';ClassID=' . $ClassID
. ';';
my $LinkFilter = 'TakeLastSearch=1;Subaction=Search;Profile='
. $LayoutObject->Ascii2Html( Text => $Self->{Profile} )
. ';ClassID='
. $LayoutObject->Ascii2Html( Text => $ClassID )
. ';';
my $LinkBack = 'Subaction=LoadProfile;Profile='
. $LayoutObject->Ascii2Html( Text => $Self->{Profile} )
. ';ClassID='
. $LayoutObject->Ascii2Html( Text => $ClassID )
. ';TakeLastSearch=1;';
# find out which columns should be shown
my @ShowColumns;
if ( $Self->{Config}->{ShowColumns} ) {
# get all possible columns from config
my %PossibleColumn = %{ $Self->{Config}->{ShowColumns} };
# get the column names that should be shown
COLUMNNAME:
for my $Name ( sort keys %PossibleColumn ) {
next COLUMNNAME if !$PossibleColumn{$Name};
push @ShowColumns, $Name;
}
}
# get the configured columns and reorganize them by class name
if (
IsArrayRefWithData( $Self->{Config}->{ShowColumnsByClass} )
&& $ClassID
)
{
my %ColumnByClass;
NAME:
for my $Name ( @{ $Self->{Config}->{ShowColumnsByClass} } ) {
my ( $Class, $Column ) = split /::/, $Name, 2;
next NAME if !$Column;
push @{ $ColumnByClass{$Class} }, $Column;
}
# check if there is a specific column config for the selected class
my $SelectedClass = $ClassList->{$ClassID};
if ( $ColumnByClass{$SelectedClass} ) {
@ShowColumns = @{ $ColumnByClass{$SelectedClass} };
}
}
my $ClassName = $ClassList->{$ClassID};
my $Title = $LayoutObject->{LanguageObject}->Translate('Config Item Search Results')
. ' '
. $LayoutObject->{LanguageObject}->Translate('Class')
. ' '
. $LayoutObject->{LanguageObject}->Translate($ClassName);
$Output .= $LayoutObject->ITSMConfigItemListShow(
ConfigItemIDs => $SearchResultList,
Total => scalar @{$SearchResultList},
View => $Self->{View},
Filter => $ClassID,
Env => $Self,
LinkPage => $LinkPage,
LinkSort => $LinkSort,
LinkFilter => $LinkFilter,
LinkBack => $LinkBack,
Profile => $Self->{Profile},
TitleName => $Title,
ShowColumns => \@ShowColumns,
SortBy => $LayoutObject->Ascii2Html( Text => $Self->{SortBy} ),
OrderBy => $LayoutObject->Ascii2Html( Text => $Self->{OrderBy} ),
ClassID => $ClassID,
RequestURL => $Self->{RequestedURL},
);
$LayoutObject->AddJSData(
Key => 'ITSMConfigItemSearch',
Value => {
Profile => $Self->{Profile},
ClassID => $ClassID,
Action => $Self->{Action},
},
);
# build footer
$Output .= $LayoutObject->Footer();
return $Output;
}
}
# ------------------------------------------------------------ #
# call search dialog from search empty screen
# ------------------------------------------------------------ #
else {
# show default search screen
$Output = $LayoutObject->Header();
$Output .= $LayoutObject->NavigationBar();
$LayoutObject->AddJSData(
Key => 'ITSMConfigItemOpenSearchDialog',
Value => {
Profile => $Self->{Profile},
ClassID => $ClassID,
Action => $Self->{Action},
},
);
# output template
$Output .= $LayoutObject->Output(
TemplateFile => 'AgentITSMConfigItemSearch',
);
# output footer
$Output .= $LayoutObject->Footer();
return $Output;
}
}
sub _XMLSearchFormOutput {
my ( $Self, %Param ) = @_;
my %GetParam = %{ $Param{GetParam} };
# check needed stuff
return if !$Param{XMLDefinition};
return if ref $Param{XMLDefinition} ne 'ARRAY';
return if ref $Param{XMLAttributes} ne 'ARRAY';
$Param{Level} ||= 0;
ITEM:
for my $Item ( @{ $Param{XMLDefinition} } ) {
# set prefix
my $InputKey = $Item->{Key};
my $Name = $Item->{Name};
if ( $Param{Prefix} ) {
$InputKey = $Param{Prefix} . '::' . $InputKey;
$Name = $Param{PrefixName} . '::' . $Name;
}
# output attribute, if marked as searchable
if ( $Item->{Searchable} ) {
my $Value;
# date type fields must to get all date parameters
if ( $Item->{Input}->{Type} eq 'Date' ) {
$Value =
{
$InputKey => $GetParam{$InputKey},
$InputKey . '::TimeStart::Day' => $GetParam{ $InputKey . '::TimeStart::Day' },
$InputKey
. '::TimeStart::Month' => $GetParam{ $InputKey . '::TimeStart::Month' },
$InputKey . '::TimeStart::Year' => $GetParam{ $InputKey . '::TimeStart::Year' },
$InputKey . '::TimeStop::Day' => $GetParam{ $InputKey . '::TimeStop::Day' },
$InputKey . '::TimeStop::Month' => $GetParam{ $InputKey . '::TimeStop::Month' },
$InputKey . '::TimeStop::Year' => $GetParam{ $InputKey . '::TimeStop::Year' },
} || '';
}
# date-time type fields must get all date and time parameters
elsif ( $Item->{Input}->{Type} eq 'DateTime' ) {
$Value =
{
$InputKey => $GetParam{$InputKey},
$InputKey
. '::TimeStart::Minute' => $GetParam{ $InputKey . '::TimeStart::Minute' },
$InputKey . '::TimeStart::Hour' => $GetParam{ $InputKey . '::TimeStart::Hour' },
$InputKey . '::TimeStart::Day' => $GetParam{ $InputKey . '::TimeStart::Day' },
$InputKey
. '::TimeStart::Month' => $GetParam{ $InputKey . '::TimeStart::Month' },
$InputKey . '::TimeStart::Year' => $GetParam{ $InputKey . '::TimeStart::Year' },
$InputKey
. '::TimeStop::Minute' => $GetParam{ $InputKey . '::TimeStop::Minute' },
$InputKey . '::TimeStop::Hour' => $GetParam{ $InputKey . '::TimeStop::Hour' },
$InputKey . '::TimeStop::Day' => $GetParam{ $InputKey . '::TimeStop::Day' },
$InputKey . '::TimeStop::Month' => $GetParam{ $InputKey . '::TimeStop::Month' },
$InputKey . '::TimeStop::Year' => $GetParam{ $InputKey . '::TimeStop::Year' },
} || '';
}
# other kinds of fields can get its value directly
else {
$Value = $GetParam{$InputKey} || '';
}
# get layout object
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
# create search input element
my $InputString = $LayoutObject->ITSMConfigItemSearchInputCreate(
Key => $InputKey,
Item => $Item,
Value => $Value,
);
# output attribute row
$LayoutObject->Block(
Name => 'AttributeRow',
Data => {
Key => $InputKey,
Name => $Name,
Description => $Item->{Description} || $Item->{Name},
InputString => $InputString,
},
);
push @{ $Param{XMLAttributes} }, {
Key => $InputKey,
Value => $Name,
};
}
next ITEM if !$Item->{Sub};
# start recursion, if "Sub" was found
$Self->_XMLSearchFormOutput(
XMLDefinition => $Item->{Sub},
XMLAttributes => $Param{XMLAttributes},
Level => $Param{Level} + 1,
Prefix => $InputKey,
PrefixName => $Name,
GetParam => \%GetParam,
);
}
return 1;
}
sub _XMLSearchFormGet {
my ( $Self, %Param ) = @_;
# check needed stuff
return if !$Param{XMLDefinition};
return if !$Param{XMLFormData};
return if !$Param{XMLGetParam};
return if ref $Param{XMLDefinition} ne 'ARRAY';
return if ref $Param{XMLFormData} ne 'ARRAY';
return if ref $Param{XMLGetParam} ne 'ARRAY';
$Param{Level} ||= 0;
ITEM:
for my $Item ( @{ $Param{XMLDefinition} } ) {
# create inputkey
my $InputKey = $Item->{Key};
if ( $Param{Prefix} ) {
$InputKey = $Param{Prefix} . '::' . $InputKey;
}
# Date type fields must to get all date parameters.
if ( $Item->{Input}->{Type} eq 'Date' && $Param{$InputKey} ) {
$Param{$InputKey} =
{
$InputKey => $Param{$InputKey},
$InputKey . '::TimeStart::Day' => $Param{ $InputKey . '::TimeStart::Day' },
$InputKey
. '::TimeStart::Month' => $Param{ $InputKey . '::TimeStart::Month' },
$InputKey . '::TimeStart::Year' => $Param{ $InputKey . '::TimeStart::Year' },
$InputKey . '::TimeStop::Day' => $Param{ $InputKey . '::TimeStop::Day' },
$InputKey . '::TimeStop::Month' => $Param{ $InputKey . '::TimeStop::Month' },
$InputKey . '::TimeStop::Year' => $Param{ $InputKey . '::TimeStop::Year' },
} || '';
}
# Date-time type fields must get all date and time parameters.
elsif ( $Item->{Input}->{Type} eq 'DateTime' && $Param{$InputKey} ) {
$Param{$InputKey} =
{
$InputKey => $Param{$InputKey},
$InputKey
. '::TimeStart::Minute' => $Param{ $InputKey . '::TimeStart::Minute' },
$InputKey . '::TimeStart::Hour' => $Param{ $InputKey . '::TimeStart::Hour' },
$InputKey . '::TimeStart::Day' => $Param{ $InputKey . '::TimeStart::Day' },
$InputKey
. '::TimeStart::Month' => $Param{ $InputKey . '::TimeStart::Month' },
$InputKey . '::TimeStart::Year' => $Param{ $InputKey . '::TimeStart::Year' },
$InputKey
. '::TimeStop::Minute' => $Param{ $InputKey . '::TimeStop::Minute' },
$InputKey . '::TimeStop::Hour' => $Param{ $InputKey . '::TimeStop::Hour' },
$InputKey . '::TimeStop::Day' => $Param{ $InputKey . '::TimeStop::Day' },
$InputKey . '::TimeStop::Month' => $Param{ $InputKey . '::TimeStop::Month' },
$InputKey . '::TimeStop::Year' => $Param{ $InputKey . '::TimeStop::Year' },
} || '';
}
# get search form data
my $Values = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->ITSMConfigItemSearchFormDataGet(
Key => $InputKey,
Item => $Item,
Value => $Param{$InputKey},
);
# create search key
my $SearchKey = $InputKey;
$SearchKey =~ s{ :: }{\'\}[%]\{\'}xmsg;
$SearchKey = "[1]{'Version'}[1]{'$SearchKey'}[%]{'Content'}";
# ITSMConfigItemSearchFormDataGet() can return string, arrayref or hashref
if ( ref $Values eq 'ARRAY' ) {
# filter empty elements
my @SearchValues = grep {$_} @{$Values};
if (@SearchValues) {
push @{ $Param{XMLFormData} }, {
$SearchKey => \@SearchValues,
};
push @{ $Param{XMLGetParam} }, {
$InputKey => \@SearchValues,
};
}
}
elsif ($Values) {
# e.g. for Date between searches
push @{ $Param{XMLFormData} }, {
$SearchKey => $Values,
};
if ( ref $Values eq 'HASH' ) {
if ( $Item->{Input}->{Type} eq 'Date' ) {
if ( $Values->{'-between'} ) {
# get time elemet values
my ( $StartDate, $StopDate ) = @{ $Values->{'-between'} };
my ( $StartYear, $StartMonth, $StartDay ) = split( /-/, $StartDate );
my ( $StopYear, $StopMonth, $StopDay ) = split( /-/, $StopDate );
# store time elment values
push @{ $Param{XMLGetParam} }, {
$InputKey => 1,
$InputKey . '::TimeStart::Day' => $StartDay,
$InputKey . '::TimeStart::Month' => $StartMonth,
$InputKey . '::TimeStart::Year' => $StartYear,
$InputKey . '::TimeStop::Day' => $StopDay,
$InputKey . '::TimeStop::Month' => $StopMonth,
$InputKey . '::TimeStop::Year' => $StopYear,
};
}
}
elsif ( $Item->{Input}->{Type} eq 'DateTime' ) {
if ( $Values->{'-between'} ) {
# get time elemet values
my ( $StartDateTime, $StopDateTime ) = @{ $Values->{'-between'} };
my ( $StartDate, $StartTime ) = split( /\s/, $StartDateTime );
my ( $StartYear, $StartMonth, $StartDay ) = split( /-/, $StartDate );
my ( $StartHour, $StartMinute, $StartSecond ) = split( /\:/, $StartTime );
my ( $StopDate, $StopTime ) = split( /\s/, $StopDateTime );
my ( $StopYear, $StopMonth, $StopDay ) = split( /-/, $StopDate );
my ( $StopHour, $StopMinute, $StopSecond ) = split( /\:/, $StopTime );
# store time elment values
push @{ $Param{XMLGetParam} }, {
$InputKey => 1,
$InputKey . '::TimeStart::Minute' => $StartMinute,
$InputKey . '::TimeStart::Hour' => $StartHour,
$InputKey . '::TimeStart::Day' => $StartDay,
$InputKey . '::TimeStart::Month' => $StartMonth,
$InputKey . '::TimeStart::Year' => $StartYear,
$InputKey . '::TimeStop::Minute' => $StopMinute,
$InputKey . '::TimeStop::Hour' => $StopHour,
$InputKey . '::TimeStop::Day' => $StopDay,
$InputKey . '::TimeStop::Month' => $StopMonth,
$InputKey . '::TimeStop::Year' => $StopYear,
};
}
}
}
else {
push @{ $Param{XMLGetParam} }, {
$InputKey => $Values,
};
}
}
next ITEM if !$Item->{Sub};
# start recursion, if "Sub" was found
$Self->_XMLSearchFormGet(
%Param,
XMLDefinition => $Item->{Sub},
XMLFormData => $Param{XMLFormData},
XMLGetParam => $Param{XMLGetParam},
Level => $Param{Level} + 1,
Prefix => $InputKey,
);
}
return 1;
}
sub _XMLSearchAttributesGet {
my ( $Self, %Param ) = @_;
# check needed stuff
return if !$Param{XMLDefinition};
return if ref $Param{XMLDefinition} ne 'ARRAY';
return if ref $Param{XMLAttributes} ne 'ARRAY';
$Param{Level} ||= 0;
ITEM:
for my $Item ( @{ $Param{XMLDefinition} } ) {
# set prefix
my $InputKey = $Item->{Key};
my $Name = $Item->{Name};
if ( $Param{Prefix} ) {
$InputKey = $Param{Prefix} . '::' . $InputKey;
$Name = $Param{PrefixName} . '::' . $Name;
}
# store attribute, if marked as searchable
if ( $Item->{Searchable} ) {
push @{ $Param{XMLAttributes} }, {
Key => $InputKey,
Value => $Name,
};
}
next ITEM if !$Item->{Sub};
# start recursion, if "Sub" was found
$Self->_XMLSearchAttributesGet(
XMLDefinition => $Item->{Sub},
XMLAttributes => $Param{XMLAttributes},
Level => $Param{Level} + 1,
Prefix => $InputKey,
PrefixName => $Name,
);
}
return 1;
}
1;