# -- # 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::AgentITSMConfigItemEdit; use strict; use warnings; ## nofilter(TidyAll::Plugin::OTRS::Migrations::OTRS6::SysConfig) use Kernel::System::VariableCheck qw(:all); use Kernel::Language qw(Translatable); 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 param object my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); # get configitem id and class id my $ConfigItem = {}; $ConfigItem->{ConfigItemID} = $ParamObject->GetParam( Param => 'ConfigItemID' ) || 0; $ConfigItem->{ClassID} = $ParamObject->GetParam( Param => 'ClassID' ) || 0; my $DuplicateID = $ParamObject->GetParam( Param => 'DuplicateID' ) || 0; my $HasAccess; # get needed objects my $ConfigItemObject = $Kernel::OM->Get('Kernel::System::ITSMConfigItem'); my $GeneralCatalogObject = $Kernel::OM->Get('Kernel::System::GeneralCatalog'); my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # get config of frontend module $Self->{Config} = $ConfigObject->Get("ITSMConfigItem::Frontend::$Self->{Action}"); # get needed data if ( $ConfigItem->{ConfigItemID} && $ConfigItem->{ConfigItemID} ne 'NEW' ) { # check access for config item $HasAccess = $ConfigItemObject->Permission( Scope => 'Item', ItemID => $ConfigItem->{ConfigItemID}, UserID => $Self->{UserID}, Type => $Self->{Config}->{Permission}, ); # get config item $ConfigItem = $ConfigItemObject->ConfigItemGet( ConfigItemID => $ConfigItem->{ConfigItemID}, ); } elsif ($DuplicateID) { # get config item to duplicate $ConfigItem = $ConfigItemObject->ConfigItemGet( ConfigItemID => $DuplicateID, ); # check access for config item $HasAccess = $ConfigItemObject->Permission( Scope => 'Item', ItemID => $ConfigItem->{ConfigItemID}, UserID => $Self->{UserID}, Type => $Self->{Config}->{Permission}, ); # set config item id and number $ConfigItem->{ConfigItemID} = 'NEW'; $ConfigItem->{Number} = Translatable('New'); } elsif ( $ConfigItem->{ClassID} ) { # set config item id and number $ConfigItem->{ConfigItemID} = 'NEW'; $ConfigItem->{Number} = Translatable('New'); # check access for config item $HasAccess = $ConfigItemObject->Permission( Scope => 'Class', ClassID => $ConfigItem->{ClassID}, UserID => $Self->{UserID}, Type => $Self->{Config}->{Permission}, ); # get class list my $ClassList = $GeneralCatalogObject->ItemList( Class => 'ITSM::ConfigItem::Class', ); $ConfigItem->{Class} = $ClassList->{ $ConfigItem->{ClassID} }; } else { return $LayoutObject->ErrorScreen( Message => Translatable('No ConfigItemID, DuplicateID or ClassID is given!'), Comment => Translatable('Please contact the administrator.'), ); } # if user has no access rights show error page if ( !$HasAccess ) { return $LayoutObject->ErrorScreen( Message => Translatable('No access is given!'), Comment => Translatable('Please contact the administrator.'), ); } # get definition my $XMLDefinition = $ConfigItemObject->DefinitionGet( ClassID => $ConfigItem->{ClassID}, ); # abort, if no definition is defined if ( !$XMLDefinition->{DefinitionID} ) { return $LayoutObject->ErrorScreen( Message => $LayoutObject->{LanguageObject} ->Translate( 'No definition was defined for class %s!', $ConfigItem->{Class} ), Comment => Translatable('Please contact the administrator.'), ); } # get upload cache object my $UploadCacheObject = $Kernel::OM->Get('Kernel::System::Web::UploadCache'); # get form id $Self->{FormID} = $ParamObject->GetParam( Param => 'FormID' ); # create form id if ( !$Self->{FormID} ) { $Self->{FormID} = $UploadCacheObject->FormIDCreate(); } # when there's no ClassID it means, an existing config item is edited as the ClassID is only # provided as GET param when creating a new config item if ( !$ParamObject->GetParam( Param => 'ClassID' ) ) { # get all attachments meta data my @ExistingAttachments = $ConfigItemObject->ConfigItemAttachmentList( ConfigItemID => $ConfigItem->{ConfigItemID}, ); # copy all existing attachments to upload cache FILENAME: for my $Filename (@ExistingAttachments) { # get the existing attachment data my $AttachmentData = $ConfigItemObject->ConfigItemAttachmentGet( ConfigItemID => $ConfigItem->{ConfigItemID}, Filename => $Filename, UserID => $Self->{UserID}, ); # add attachment to the upload cache $UploadCacheObject->FormIDAddFile( FormID => $Self->{FormID}, Filename => $AttachmentData->{Filename}, Content => $AttachmentData->{Content}, ContentType => $AttachmentData->{ContentType}, ); } } # get submit save my $SubmitSave = $ParamObject->GetParam( Param => 'SubmitSave' ); # get log object my $LogObject = $Kernel::OM->Get('Kernel::System::Log'); # get xml data my $Version = {}; my $NameDuplicates; my $CINameRegexErrorMessage; if ( $Self->{Subaction} eq 'VersionSave' ) { # get the uploaded attachment my %UploadStuff = $ParamObject->GetUploadAll( Param => 'FileUpload', Source => 'string', ); if (%UploadStuff) { # add attachment to the upload cache $UploadCacheObject->FormIDAddFile( FormID => $Self->{FormID}, %UploadStuff, ); } my $AllRequired = 1; # get general form data for my $FormParam (qw(Name DeplStateID InciStateID)) { $Version->{$FormParam} = $ParamObject->GetParam( Param => $FormParam ); if ( !$Version->{$FormParam} ) { $AllRequired = 0; } } # get xml form data $Version->{XMLData}->[1]->{Version}->[1] = $Self->_XMLFormGet( XMLDefinition => $XMLDefinition->{DefinitionRef}, AllRequired => \$AllRequired, ConfigItemID => $ConfigItem->{ConfigItemID}, ); # check, whether the feature to check for a unique name is enabled if ( IsStringWithData( $Version->{Name} ) && $ConfigObject->Get('UniqueCIName::EnableUniquenessCheck') ) { if ( $ConfigObject->{Debug} > 0 ) { $LogObject->Log( Priority => 'debug', Message => "Checking for duplicate names (ClassID: $ConfigItem->{ClassID}, " . "Name: $Version->{Name}, ConfigItemID: $ConfigItem->{ConfigItemID})", ); } $NameDuplicates = $ConfigItemObject->UniqueNameCheck( ConfigItemID => $ConfigItem->{ConfigItemID}, ClassID => $ConfigItem->{ClassID}, Name => $Version->{Name}, ); # stop processing if the name is not unique if ( IsArrayRefWithData($NameDuplicates) ) { $AllRequired = 0; # build a string of all duplicate IDs my $NameDuplicatesString = join ', ', @{$NameDuplicates}; $LogObject->Log( Priority => 'error', Message => "The name $Version->{Name} is already in use by the ConfigItemID(s): " . $NameDuplicatesString, ); } } # get the config option for the name regex checks my $CINameRegexConfig = $ConfigObject->Get("ITSMConfigItem::CINameRegex"); # check if the CI name is given and should be checked with a regular expression if ( IsStringWithData( $Version->{Name} ) && $CINameRegexConfig ) { # get class list my $ClassList = $GeneralCatalogObject->ItemList( Class => 'ITSM::ConfigItem::Class', ); # get the class name my $ClassName = $ClassList->{ $ConfigItem->{ClassID} } || ''; # get the regex for this class my $CINameRegex = $CINameRegexConfig->{ $ClassName . '::' . 'CINameRegex' } || ''; # if a regex is defined and the CI name does not match the regular expression if ( $CINameRegex && $Version->{Name} !~ m{ $CINameRegex }xms ) { $AllRequired = 0; # get the error message for this class $CINameRegexErrorMessage = $CINameRegexConfig->{ $ClassName . '::' . 'CINameRegexErrorMessage' } || ''; } } # save version to database if ( $SubmitSave && $AllRequired ) { if ( $ConfigItem->{ConfigItemID} eq 'NEW' ) { $ConfigItem->{ConfigItemID} = $ConfigItemObject->ConfigItemAdd( ClassID => $ConfigItem->{ClassID}, UserID => $Self->{UserID}, ); } # get all attachments from upload cache my @Attachments = $UploadCacheObject->FormIDGetAllFilesData( FormID => $Self->{FormID}, ); # build a lookup lookup hash of the new attachments my %NewAttachment; for my $Attachment (@Attachments) { # the key is the filename + filesize + content type my $Key = $Attachment->{Filename} . $Attachment->{Filesize} . $Attachment->{ContentType}; # store all of the new attachment data $NewAttachment{$Key} = $Attachment; } # get all attachments meta data my @ExistingAttachments = $ConfigItemObject->ConfigItemAttachmentList( ConfigItemID => $ConfigItem->{ConfigItemID}, ); # check the existing attachments FILENAME: for my $Filename (@ExistingAttachments) { # get the existing attachment data my $AttachmentData = $ConfigItemObject->ConfigItemAttachmentGet( ConfigItemID => $ConfigItem->{ConfigItemID}, Filename => $Filename, UserID => $Self->{UserID}, ); # the key is the filename + filesize + content type # (no content id, as existing attachments don't have it) my $Key = $AttachmentData->{Filename} . $AttachmentData->{Filesize} . $AttachmentData->{ContentType}; # attachment is already existing, we can delete it from the new attachment hash if ( $NewAttachment{$Key} ) { delete $NewAttachment{$Key}; } # existing attachment is no longer in new attachments hash else { # delete the existing attachment my $DeleteSuccessful = $ConfigItemObject->ConfigItemAttachmentDelete( ConfigItemID => $ConfigItem->{ConfigItemID}, Filename => $Filename, UserID => $Self->{UserID}, ); # check error if ( !$DeleteSuccessful ) { return $LayoutObject->FatalError(); } } } # write the new attachments ATTACHMENT: for my $Attachment ( values %NewAttachment ) { # add attachment my $Success = $ConfigItemObject->ConfigItemAttachmentAdd( %{$Attachment}, ConfigItemID => $ConfigItem->{ConfigItemID}, UserID => $Self->{UserID}, ); # check error if ( !$Success ) { return $LayoutObject->FatalError(); } } # add version $ConfigItemObject->VersionAdd( %{$Version}, ConfigItemID => $ConfigItem->{ConfigItemID}, DefinitionID => $XMLDefinition->{DefinitionID}, UserID => $Self->{UserID}, ); # redirect to zoom mask my $ScreenType = $ParamObject->GetParam( Param => 'ScreenType' ) || 0; if ($ScreenType) { my $URL = "Action=AgentITSMConfigItemZoom;ConfigItemID=$ConfigItem->{ConfigItemID}"; # return to overview or search results instead if called Duplicate from row action if ( $Self->{LastScreenView} eq 'Action=AgentITSMConfigItem' || $Self->{LastScreenView} =~ m{\A Action=AgentITSMConfigItem(?: Search)?;}msx ) { $URL = $Self->{LastScreenView}; } return $LayoutObject->PopupClose( URL => $URL, ); } else { return $LayoutObject->Redirect( OP => "Action=AgentITSMConfigItemZoom;ConfigItemID=$ConfigItem->{ConfigItemID}", ); } } } elsif ($DuplicateID) { my $VersionID = $ParamObject->GetParam( Param => 'VersionID' ); if ($VersionID) { # get version data to duplicate config item $Version = $ConfigItemObject->VersionGet( VersionID => $VersionID, ); } else { # get last version data to duplicate config item $Version = $ConfigItemObject->VersionGet( ConfigItemID => $DuplicateID, ); } } elsif ( $ConfigItem->{ConfigItemID} ne 'NEW' ) { # get last version data $Version = $ConfigItemObject->VersionGet( ConfigItemID => $ConfigItem->{ConfigItemID}, ); } my %XMLFormOutputParam; if ( $Version->{XMLData}->[1]->{Version}->[1] ) { $XMLFormOutputParam{XMLData} = $Version->{XMLData}->[1]->{Version}->[1]; } # output name invalid block my $RowNameInvalid = ''; if ( !$Version->{Name} && $Self->{Subaction} eq 'VersionSave' && $SubmitSave ) { $RowNameInvalid = 'ServerError'; } # check for name duplicates if ( IsArrayRefWithData($NameDuplicates) ) { $RowNameInvalid = 'ServerError'; } # check for not matched name regex if ($CINameRegexErrorMessage) { $RowNameInvalid = 'ServerError'; } # output name block $LayoutObject->Block( Name => 'RowName', Data => { %{$Version}, RowNameInvalid => $RowNameInvalid, }, ); if ( IsStringWithData($RowNameInvalid) && !IsArrayRefWithData($NameDuplicates) && !$CINameRegexErrorMessage ) { if ( $ConfigObject->{Debug} > 0 ) { $LogObject->Log( Priority => 'debug', Message => "Rendering default error block", ); } $LayoutObject->Block( Name => 'RowNameErrorDefault', ); } elsif ( IsArrayRefWithData($NameDuplicates) ) { # build array with CI-Numbers my @NameDuplicatesByCINumber; for my $ConfigItemID ( @{$NameDuplicates} ) { # lookup the CI number my $CINumber = $ConfigItemObject->ConfigItemLookup( ConfigItemID => $ConfigItemID, ); push @NameDuplicatesByCINumber, $CINumber; } my $DuplicateString = join ', ', @NameDuplicatesByCINumber; if ( $ConfigObject->{Debug} > 0 ) { $LogObject->Log( Priority => 'debug', Message => "Rendering block for duplicates (CI-Numbers: $DuplicateString) error message", ); } $LayoutObject->Block( Name => 'RowNameErrorDuplicates', Data => { Duplicates => $DuplicateString, }, ); } elsif ($CINameRegexErrorMessage) { $LayoutObject->Block( Name => 'RowNameErrorRegEx', Data => { RegExErrorMessage => $CINameRegexErrorMessage, }, ); } # get deployment state list my $DeplStateList = $GeneralCatalogObject->ItemList( Class => 'ITSM::ConfigItem::DeploymentState', ); # output deployment state invalid block my $RowDeplStateInvalid = ''; if ( !$Version->{DeplStateID} && $Self->{Subaction} eq 'VersionSave' && $SubmitSave ) { $RowDeplStateInvalid = ' ServerError'; } # generate DeplStateOptionStrg my $DeplStateOptionStrg = $LayoutObject->BuildSelection( Data => $DeplStateList, Name => 'DeplStateID', PossibleNone => 1, Class => 'Validate_Required Modernize' . $RowDeplStateInvalid, SelectedID => $Version->{DeplStateID}, ); # output deployment state block $LayoutObject->Block( Name => 'RowDeplState', Data => { DeplStateOptionStrg => $DeplStateOptionStrg, }, ); # get incident state list my $InciStateList = $GeneralCatalogObject->ItemList( Class => 'ITSM::Core::IncidentState', Preferences => { Functionality => [ 'operational', 'incident' ], }, ); # output incident state invalid block my $RowInciStateInvalid = ''; if ( !$Version->{InciStateID} && $Self->{Subaction} eq 'VersionSave' && $SubmitSave ) { $RowInciStateInvalid = ' ServerError'; } # generate InciStateOptionStrg my $InciStateOptionStrg = $LayoutObject->BuildSelection( Data => $InciStateList, Name => 'InciStateID', PossibleNone => 1, Class => 'Validate_Required Modernize' . $RowInciStateInvalid, SelectedID => $Version->{InciStateID}, ); # output incident state block $LayoutObject->Block( Name => 'RowInciState', Data => { InciStateOptionStrg => $InciStateOptionStrg, }, ); # output xml form if ( $XMLDefinition->{Definition} ) { $Self->{CustomerSearchItemIDs} = []; $Self->_XMLFormOutput( XMLDefinition => $XMLDefinition->{DefinitionRef}, %XMLFormOutputParam, ); } # get all attachments meta data $Param{AttachmentList} = [ $UploadCacheObject->FormIDGetAllFilesMeta( FormID => $Self->{FormID}, ) ]; my $Output = ''; if ( ( $ConfigItem->{ConfigItemID} && $ConfigItem->{ConfigItemID} ne 'NEW' ) || $DuplicateID ) { # output block $LayoutObject->Block( Name => 'StartSmall', Data => { %Param, %{$ConfigItem}, }, ); $LayoutObject->Block( Name => 'EndSmall' ); # output header $Output .= $LayoutObject->Header( Title => Translatable('Edit'), Type => 'Small', ); # start template output $Output .= $LayoutObject->Output( TemplateFile => 'AgentITSMConfigItemEdit', Data => { %Param, %{$ConfigItem}, DuplicateID => $DuplicateID, FormID => $Self->{FormID}, }, ); $Output .= $LayoutObject->Footer( Type => 'Small' ); } else { # Necessary stuff for Add New # get class list my $ClassList = $GeneralCatalogObject->ItemList( Class => 'ITSM::ConfigItem::Class', ); # check for access rights 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; } # generate ClassOptionStrg my $ClassOptionStrg = $LayoutObject->BuildSelection( Data => $ClassList, Name => 'ClassID', PossibleNone => 1, Translation => 0, Class => 'W100pc', SelectedID => $ConfigItem->{ClassID}, ); # End Necessary stuff for Add New # output block $LayoutObject->Block( Name => 'StartNormal', Data => { ClassOptionStrg => $ClassOptionStrg, %Param, %{$ConfigItem}, }, ); $LayoutObject->Block( Name => 'EndNormal' ); # output header $Output .= $LayoutObject->Header( Title => Translatable('Edit'), ); $Output .= $LayoutObject->NavigationBar(); # start template output $Output .= $LayoutObject->Output( TemplateFile => 'AgentITSMConfigItemEdit', Data => { %Param, %{$ConfigItem}, DuplicateID => $DuplicateID, FormID => $Self->{FormID}, }, ); $Output .= $LayoutObject->Footer(); } return $Output; } sub _XMLFormGet { my ( $Self, %Param ) = @_; # check needed stuff return if !$Param{XMLDefinition}; return if !$Param{AllRequired}; return if ref $Param{XMLDefinition} ne 'ARRAY'; return if ref $Param{AllRequired} ne 'SCALAR'; return if !$Param{ConfigItemID}; my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); my $FormData = {}; ITEM: for my $Item ( @{ $Param{XMLDefinition} } ) { my $CounterInsert = 1; COUNTER: for my $Counter ( 1 .. $Item->{CountMax} ) { # create inputkey and addkey my $InputKey = $Item->{Key} . '::' . $Counter; my $AddKey = $Item->{Key} . '::Add'; if ( $Param{Prefix} ) { $InputKey = $Param{Prefix} . '::' . $InputKey; $AddKey = $Param{Prefix} . '::' . $AddKey; } # get param my $FormValues = $LayoutObject->ITSMConfigItemFormDataGet( Key => $InputKey, Item => $Item, ConfigItemID => $Param{ConfigItemID}, ); if ( defined $FormValues->{Value} ) { # check required value if ( $FormValues->{Invalid} ) { ${ $Param{AllRequired} } = 0; } # check delete button next COUNTER if $ParamObject->GetParam( Param => $InputKey . '::Delete' ); # start recursion, if "Sub" was found if ( $Item->{Sub} ) { my $SubFormData = $Self->_XMLFormGet( XMLDefinition => $Item->{Sub}, Prefix => $InputKey, AllRequired => $Param{AllRequired}, ConfigItemID => $Param{ConfigItemID}, ); $FormData->{ $Item->{Key} }->[$CounterInsert] = $SubFormData; } $FormData->{ $Item->{Key} }->[$CounterInsert]->{Content} = $FormValues->{Value}; $CounterInsert++; } else { # check add button if ( $ParamObject->GetParam( Param => $AddKey ) ) { if ( $Item->{Sub} ) { $FormData->{ $Item->{Key} }->[$CounterInsert] = $Self->_XMLDefaultSet( XMLDefinition => $Item->{Sub}, ); } $FormData->{ $Item->{Key} }->[$CounterInsert]->{Content} = ''; } last COUNTER; } } } return $FormData; } sub _XMLDefaultSet { my ( $Self, %Param ) = @_; # check needed stuff return if !$Param{XMLDefinition}; return if ref $Param{XMLDefinition} ne 'ARRAY'; my $DefaultData = {}; for my $Item ( @{ $Param{XMLDefinition} } ) { for my $Counter ( 1 .. $Item->{CountDefault} ) { # start recursion, if "Sub" was found if ( $Item->{Sub} ) { $DefaultData->{ $Item->{Key} }->[$Counter] = $Self->_XMLDefaultSet( XMLDefinition => $Item->{Sub}, ); } $DefaultData->{ $Item->{Key} }->[$Counter]->{Content} = ''; } } return $DefaultData; } sub _XMLFormOutput { my ( $Self, %Param ) = @_; # check needed stuff return if !$Param{XMLDefinition}; return if ref $Param{XMLDefinition} ne 'ARRAY'; $Param{Level} ||= 0; $Param{Prefix} ||= ''; # get submit save my $SubmitSave = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => 'SubmitSave' ); # set data present mode my $DataPresentMode = 0; if ( $Param{XMLData} && ref $Param{XMLData} eq 'HASH' ) { $DataPresentMode = 1; } my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); my $ItemCounter = 1; ITEM: for my $Item ( @{ $Param{XMLDefinition} } ) { # set loop my $Loop = $Item->{CountDefault}; if ($DataPresentMode) { $Loop = 0; # search the last content COUNTER: for my $Counter ( 1 .. $Item->{CountMax} ) { last COUNTER if !defined $Param{XMLData}->{ $Item->{Key} }->[$Counter]->{Content}; $Loop = $Counter; } # set absolut minimum if ( $Loop < $Item->{CountMin} ) { $Loop = $Item->{CountMin}; } } # set delete my $Delete = 0; if ( $Loop > $Item->{CountMin} ) { $Delete = 1; } # output content rows for my $Counter ( 1 .. $Loop ) { # output row block $LayoutObject->Block( Name => 'XMLRow' ); if ( !$Param{Level} ) { $LayoutObject->Block( Name => 'XMLRowFieldsetStart' ); } # create inputkey and addkey my $InputKey = $Item->{Key} . '::' . $Counter; if ( $Param{Prefix} ) { $InputKey = $Param{Prefix} . '::' . $InputKey; } # output blue required star my $XMLRowValueContentRequired = 0; my $LabelClass = ''; if ( $Item->{Input}->{Required} ) { $XMLRowValueContentRequired = 1; $LabelClass = 'Mandatory'; } # output red invalid star my $XMLRowValueContentInvalid = 0; if ( $Item->{Form}->{$InputKey}->{Invalid} && $SubmitSave ) { $XMLRowValueContentInvalid = 1; } my $ItemID = 'Item' . $ItemCounter++ . $Param{Prefix} . $Param{Level}; if ( $Item->{Input}->{Type} eq 'Customer' ) { push @{ $Self->{CustomerSearchItemIDs} }, $ItemID; } # create input element my $InputString = $LayoutObject->ITSMConfigItemInputCreate( Key => $InputKey, Item => $Item, Value => $Param{XMLData}->{ $Item->{Key} }->[$Counter]->{Content}, ItemId => $ItemID, Required => $XMLRowValueContentRequired, Invalid => $XMLRowValueContentInvalid, OverrideTimeZone => 1, ); # ID? my $LabelFor = $ItemID; if ( $Item->{Input}->{Type} eq 'Date' || $Item->{Input}->{Type} eq 'DateTime' ) { $LabelFor = ''; } # id needed? if ($LabelFor) { $LabelFor = 'for="' . $LabelFor . '"'; } # is this a sub field? my $Class = ''; if ( $Param{Level} ) { $Class = 'SubElement'; } # class needed? if ($LabelClass) { $LabelClass = 'class="' . "$Class $LabelClass" . '"'; } else { $LabelClass = 'class="' . $Class . '"'; } # output row value content block $LayoutObject->Block( Name => 'XMLRowValue', Data => { Name => $Item->{Name}, ItemID => $ItemID, LabelFor => $LabelFor || '', Description => $Item->{Description} || $Item->{Name}, InputString => $InputString, LabelClass => $LabelClass || '', Class => $Class || '', }, ); if ( $Item->{Input}->{Required} ) { $LayoutObject->Block( Name => 'XMLRowValueContentRequired' ); } # output delete button if ($Delete) { $LayoutObject->Block( Name => 'XMLRowValueContentDelete', Data => { InputKey => $InputKey, }, ); } # the content is invalid if ($XMLRowValueContentInvalid) { # show regex error message block if ( $Item->{Form}->{$InputKey}->{RegExErrorMessage} ) { $LayoutObject->Block( Name => 'XMLRowValueRegExError', Data => { ItemID => $ItemID, RegExErrorMessage => $Item->{Form}->{$InputKey}->{RegExErrorMessage}, }, ); } # otherwise show normal server error block else { $LayoutObject->Block( Name => 'XMLRowValueServerError', Data => { ItemID => $ItemID, }, ); } } # start recursion, if "Sub" was found if ( $Item->{Sub} ) { my %XMLFormOutputParam; if ( $DataPresentMode && defined $Param{XMLData}->{ $Item->{Key} }->[$Counter]->{Content} ) { $XMLFormOutputParam{XMLData} = $Param{XMLData}->{ $Item->{Key} }->[$Counter]; } $Self->_XMLFormOutput( XMLDefinition => $Item->{Sub}, %XMLFormOutputParam, Level => $Param{Level} + 1, Prefix => $InputKey, ); } if ( !$Param{Level} ) { $LayoutObject->Block( Name => 'XMLRowFieldsetEnd' ); } # output row to sort rows correctly $LayoutObject->Block( Name => 'XMLRow' ); } # output add button if ( $Loop < $Item->{CountMax} ) { # if no item should be shown we need to show the add button # and therefore we need to show the XMLRow block if ( !$Loop ) { $LayoutObject->Block( Name => 'XMLRow' ); } my $Class = ''; if ( $Param{Level} ) { $Class = 'class="SubElement"'; } else { $LayoutObject->Block( Name => 'XMLRowFieldsetEnd' ); $LayoutObject->Block( Name => 'XMLRowFieldsetStart' ); } # set prefix my $InputKey = $Item->{Key}; if ( $Param{Prefix} ) { $InputKey = $Param{Prefix} . '::' . $InputKey; } # output row add content block $LayoutObject->Block( Name => 'XMLRowAddContent', Data => { ItemID => $InputKey . 'Add', Name => $Item->{Name}, Description => $Item->{Description} || $Item->{Name}, InputKey => $InputKey, Class => $Class, }, ); } } if ( IsArrayRefWithData( $Self->{CustomerSearchItemIDs} ) ) { $LayoutObject->AddJSData( Key => 'CustomerSearchItemIDs', Value => $Self->{CustomerSearchItemIDs}, ); } return 1; } 1;