# -- # 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::AdminGenericAgent; use strict; use warnings; 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 ); # get the dynamic fields for ticket object $Self->{DynamicField} = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( Valid => 1, ObjectType => ['Ticket'], ); return $Self; } sub Run { my ( $Self, %Param ) = @_; # get layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # secure mode message (don't allow this action till secure mode is enabled) if ( !$Kernel::OM->Get('Kernel::Config')->Get('SecureMode') ) { return $LayoutObject->SecureMode(); } # get param object my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); # get config data $Self->{Profile} = $ParamObject->GetParam( Param => 'Profile' ) || ''; $Self->{OldProfile} = $ParamObject->GetParam( Param => 'OldProfile' ) || ''; $Self->{Subaction} = $ParamObject->GetParam( Param => 'Subaction' ) || ''; # get needed objects my $CheckItemObject = $Kernel::OM->Get('Kernel::System::CheckItem'); my $GenericAgentObject = $Kernel::OM->Get('Kernel::System::GenericAgent'); # ---------------------------------------------------------- # # run a generic agent job -> "run now" # ---------------------------------------------------------- # if ( $Self->{Subaction} eq 'RunNow' ) { # challenge token check for write action $LayoutObject->ChallengeTokenCheck(); my $Run = $GenericAgentObject->JobRun( Job => $Self->{Profile}, UserID => 1, ); # redirect if ($Run) { return $LayoutObject->Redirect( OP => "Action=$Self->{Action}", ); } # redirect return $LayoutObject->ErrorScreen(); } if ( $Self->{Subaction} eq 'Run' ) { return $Self->_MaskRun(); } # --------------------------------------------------------------- # # save generic agent job and show a view of all affected tickets # --------------------------------------------------------------- # # show result site if ( $Self->{Subaction} eq 'UpdateAction' ) { # challenge token check for write action $LayoutObject->ChallengeTokenCheck(); my ( %GetParam, %Errors ); # challenge token check for write action $LayoutObject->ChallengeTokenCheck(); # get single params for my $Parameter ( qw(TicketNumber Title MIMEBase_From MIMEBase_To MIMEBase_Cc MIMEBase_Subject MIMEBase_Body CustomerID CustomerUserLogin Agent SearchInArchive NewTitle NewCustomerID NewPendingTime NewPendingTimeType NewCustomerUserLogin NewStateID NewQueueID NewPriorityID NewOwnerID NewResponsibleID NewTypeID NewServiceID NewSLAID NewNoteFrom NewNoteSubject NewNoteBody NewNoteIsVisibleForCustomer NewNoteTimeUnits NewModule NewParamKey1 NewParamKey2 NewParamKey3 NewParamKey4 NewParamValue1 NewParamValue2 NewParamValue3 NewParamValue4 NewParamKey5 NewParamKey6 NewParamKey7 NewParamKey8 NewParamValue5 NewParamValue6 NewParamValue7 NewParamValue8 NewLockID NewDelete NewCMD NewSendNoNotification NewArchiveFlag ScheduleLastRun Valid ) ) { $GetParam{$Parameter} = $ParamObject->GetParam( Param => $Parameter ); # remove leading and trailing blank spaces if ( $GetParam{$Parameter} ) { $CheckItemObject->StringClean( StringRef => \$GetParam{$Parameter}, ); } } for my $Type ( qw(Time ChangeTime CloseTime LastChangeTime TimePending EscalationTime EscalationResponseTime EscalationUpdateTime EscalationSolutionTime) ) { my $Key = $Type . 'SearchType'; $GetParam{$Key} = $ParamObject->GetParam( Param => $Key ); } for my $Type ( qw( TicketCreate TicketChange TicketClose TicketLastChange TicketPending TicketEscalation TicketEscalationResponse TicketEscalationUpdate TicketEscalationSolution ) ) { for my $Attribute ( qw( TimePoint TimePointFormat TimePointStart TimeStart TimeStartDay TimeStartMonth TimeStopMonth TimeStop TimeStopDay TimeStopYear TimeStartYear ) ) { my $Key = $Type . $Attribute; $GetParam{$Key} = $ParamObject->GetParam( Param => $Key ); } # validate data for my $Attribute ( qw(TimeStartDay TimeStartMonth TimeStopMonth TimeStopDay) ) { my $Key = $Type . $Attribute; if ( $GetParam{$Key} ) { $GetParam{$Key} = sprintf( '%02d', $GetParam{$Key} ); } } } # get dynamic fields to set from web request # to store dynamic fields profile data my %DynamicFieldValues; # get dynamic field backend object my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); # cycle trough the activated Dynamic Fields for this screen DYNAMICFIELD: for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); # extract the dynamic field value from the web request my $DynamicFieldValue = $DynamicFieldBackendObject->EditFieldValueGet( DynamicFieldConfig => $DynamicFieldConfig, ParamObject => $ParamObject, LayoutObject => $LayoutObject, ReturnTemplateStructure => 1, ); # set the complete value structure in GetParam to store it later in the Generic Agent Job if ( IsHashRefWithData($DynamicFieldValue) ) { %DynamicFieldValues = ( %DynamicFieldValues, %{$DynamicFieldValue} ); } } # get array params for my $Parameter ( qw(LockIDs StateIDs StateTypeIDs QueueIDs PriorityIDs OwnerIDs ResponsibleIDs TypeIDs ServiceIDs SLAIDs ScheduleDays ScheduleMinutes ScheduleHours EventValues ) ) { # get search array params (get submitted params) if ( $ParamObject->GetArray( Param => $Parameter ) ) { @{ $GetParam{$Parameter} } = $ParamObject->GetArray( Param => $Parameter ); } } # get Dynamic fields for search from web request # cycle trough the activated Dynamic Fields for this screen DYNAMICFIELD: for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); # get search field preferences my $SearchFieldPreferences = $DynamicFieldBackendObject->SearchFieldPreferences( DynamicFieldConfig => $DynamicFieldConfig, ); next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); PREFERENCE: for my $Preference ( @{$SearchFieldPreferences} ) { # extract the dynamic field value from the web request my $DynamicFieldValue = $DynamicFieldBackendObject->SearchFieldValueGet( DynamicFieldConfig => $DynamicFieldConfig, ParamObject => $ParamObject, ReturnProfileStructure => 1, LayoutObject => $LayoutObject, Type => $Preference->{Type}, ); # set the complete value structure in %DynamicFieldValues to store it later in the # Generic Agent Job if ( IsHashRefWithData($DynamicFieldValue) ) { %DynamicFieldValues = ( %DynamicFieldValues, %{$DynamicFieldValue} ); } } } # check needed data if ( !$Self->{Profile} ) { $Errors{ProfileInvalid} = 'ServerError'; } # Check length of fields from Add Note section. if ( length $GetParam{NewNoteFrom} > 200 ) { $Errors{NewNoteFromServerError} = 'ServerError'; } if ( length $GetParam{NewNoteSubject} > 200 ) { $Errors{NewNoteSubjectServerError} = 'ServerError'; } if ( length $GetParam{NewNoteBody} > 200 ) { $Errors{NewNoteBodyServerError} = 'ServerError'; } # Check if ticket selection contains stop words my %StopWordsServerErrors = $Self->_StopWordsServerErrorsGet( MIMEBase_From => $GetParam{MIMEBase_From}, MIMEBase_To => $GetParam{MIMEBase_To}, MIMEBase_Cc => $GetParam{MIMEBase_Cc}, MIMEBase_Subject => $GetParam{MIMEBase_Subject}, MIMEBase_Body => $GetParam{MIMEBase_Body}, ); %Errors = ( %Errors, %StopWordsServerErrors ); # if no errors occurred if ( !%Errors ) { if ( $Self->{OldProfile} ) { # remove/clean up old profile stuff $GenericAgentObject->JobDelete( Name => $Self->{OldProfile}, UserID => $Self->{UserID}, ); } # insert new profile params my $JobAddResult = $GenericAgentObject->JobAdd( Name => $Self->{Profile}, Data => { %GetParam, %DynamicFieldValues, }, UserID => $Self->{UserID}, ); if ($JobAddResult) { # if the user would like to continue editing the generic agent job, just redirect to the edit screen if ( defined $ParamObject->GetParam( Param => 'ContinueAfterSave' ) && ( $ParamObject->GetParam( Param => 'ContinueAfterSave' ) eq '1' ) ) { my $Profile = $Self->{Profile} || ''; return $LayoutObject->Redirect( OP => "Action=$Self->{Action};Subaction=Update;Profile=$Profile" ); } else { # otherwise return to overview return $LayoutObject->Redirect( OP => "Action=$Self->{Action}" ); } } else { $Errors{ProfileInvalid} = 'ServerError'; $Errors{ProfileInvalidMsg} = 'AddError'; } } # something went wrong my $JobDataReference; $JobDataReference = $Self->_MaskUpdate( %Param, %GetParam, %DynamicFieldValues, %Errors, StopWordsAlreadyChecked => 1, ); # generate search mask my $Output = $LayoutObject->Header( Title => Translatable('Edit'), ); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminGenericAgent', Data => $JobDataReference, ); $Output .= $LayoutObject->Footer(); return $Output; } # ---------------------------------------------------------- # # edit generic agent job # ---------------------------------------------------------- # if ( $Self->{Subaction} eq 'Update' ) { my $JobDataReference; $JobDataReference = $Self->_MaskUpdate(%Param); # generate search mask my $Output = $LayoutObject->Header( Title => Translatable('Edit'), ); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminGenericAgent', Data => $JobDataReference, ); $Output .= $LayoutObject->Footer(); return $Output; } # ---------------------------------------------------------- # # delete an generic agent job # ---------------------------------------------------------- # if ( $Self->{Subaction} eq 'Delete' && $Self->{Profile} ) { # challenge token check for write action $LayoutObject->ChallengeTokenCheck(); if ( $Self->{Profile} ) { $GenericAgentObject->JobDelete( Name => $Self->{Profile}, UserID => $Self->{UserID}, ); } } # ---------------------------------------------------------- # # overview of all generic agent jobs # ---------------------------------------------------------- # $LayoutObject->Block( Name => 'ActionList', ); $LayoutObject->Block( Name => 'ActionAdd', ); $LayoutObject->Block( Name => 'Filter', ); $LayoutObject->Block( Name => 'Overview', ); my %Jobs = $GenericAgentObject->JobList(); # if there are any data, it is shown if (%Jobs) { my $Counter = 1; for my $JobKey ( sort keys %Jobs ) { my %JobData = $GenericAgentObject->JobGet( Name => $JobKey ); # css setting and text for valid or invalid jobs $JobData{ShownValid} = $JobData{Valid} ? 'valid' : 'invalid'; # separate each search result line by using several css $LayoutObject->Block( Name => 'Row', Data => {%JobData}, ); } } # otherwise a no data found message is displayed else { $LayoutObject->Block( Name => 'NoDataFoundMsg', Data => {}, ); } # generate search mask my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminGenericAgent', Data => \%Param, ); $Output .= $LayoutObject->Footer(); return $Output; } sub _MaskUpdate { my ( $Self, %Param ) = @_; my %JobData; if ( $Self->{Profile} ) { # get db job data %JobData = $Kernel::OM->Get('Kernel::System::GenericAgent')->JobGet( Name => $Self->{Profile}, ); } $JobData{Profile} = $Self->{Profile}; $JobData{Subaction} = $Self->{Subaction}; # get config object my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); # get list type my $TreeView = 0; if ( $ConfigObject->Get('Ticket::Frontend::ListType') eq 'tree' ) { $TreeView = 1; } my %ShownUsers = $Kernel::OM->Get('Kernel::System::User')->UserList( Type => 'Long', Valid => 1, ); # get layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); $JobData{OwnerStrg} = $LayoutObject->BuildSelection( Data => \%ShownUsers, Name => 'OwnerIDs', Multiple => 1, Size => 5, Translation => 0, SelectedID => $JobData{OwnerIDs}, Class => 'Modernize', ); $JobData{NewOwnerStrg} = $LayoutObject->BuildSelection( Data => \%ShownUsers, Name => 'NewOwnerID', Size => 5, Multiple => 0, Translation => 0, SelectedID => $JobData{NewOwnerID}, Class => 'Modernize', ); my %Hours; for my $Number ( 0 .. 23 ) { $Hours{$Number} = sprintf( "%02d", $Number ); } $JobData{ScheduleHoursList} = $LayoutObject->BuildSelection( Data => \%Hours, Name => 'ScheduleHours', Size => 6, Multiple => 1, Translation => 0, SelectedID => $JobData{ScheduleHours}, Class => 'Modernize', ); my %Minutes; for my $Number ( 0 .. 59 ) { $Minutes{$Number} = sprintf( "%02d", $Number ); } $JobData{ScheduleMinutesList} = $LayoutObject->BuildSelection( Data => \%Minutes, Name => 'ScheduleMinutes', Size => 6, Multiple => 1, Translation => 0, SelectedID => $JobData{ScheduleMinutes}, Class => 'Modernize', ); $JobData{ScheduleDaysList} = $LayoutObject->BuildSelection( Data => { 1 => Translatable('Mon'), 2 => Translatable('Tue'), 3 => Translatable('Wed'), 4 => Translatable('Thu'), 5 => Translatable('Fri'), 6 => Translatable('Sat'), 0 => Translatable('Sun'), }, Sort => 'NumericKey', Name => 'ScheduleDays', Size => 7, Multiple => 1, SelectedID => $JobData{ScheduleDays}, Class => 'Modernize', ); # get state object my $StateObject = $Kernel::OM->Get('Kernel::System::State'); $JobData{StatesStrg} = $LayoutObject->BuildSelection( Data => { $StateObject->StateList( UserID => 1, Action => $Self->{Action}, ), }, Name => 'StateIDs', Multiple => 1, Size => 5, SelectedID => $JobData{StateIDs}, Class => 'Modernize', ); $JobData{NewStatesStrg} = $LayoutObject->BuildSelection( Data => { $StateObject->StateList( UserID => 1, Action => $Self->{Action}, ), }, Name => 'NewStateID', Size => 5, Multiple => 0, SelectedID => $JobData{NewStateID}, Class => 'Modernize', ); $JobData{NewPendingTimeTypeStrg} = $LayoutObject->BuildSelection( Data => [ { Key => 60, Value => Translatable('minute(s)'), }, { Key => 3600, Value => Translatable('hour(s)'), }, { Key => 86400, Value => Translatable('day(s)'), }, { Key => 2592000, Value => Translatable('month(s)'), }, { Key => 31536000, Value => Translatable('year(s)'), }, ], Name => 'NewPendingTimeType', Size => 1, Multiple => 0, SelectedID => $JobData{NewPendingTimeType}, Translation => 1, Title => $LayoutObject->{LanguageObject}->Translate('Time unit'), Class => 'Modernize', ); # get queue object my $QueueObject = $Kernel::OM->Get('Kernel::System::Queue'); $JobData{QueuesStrg} = $LayoutObject->AgentQueueListOption( Data => { $QueueObject->GetAllQueues(), }, Size => 5, Multiple => 1, Name => 'QueueIDs', SelectedIDRefArray => $JobData{QueueIDs}, TreeView => $TreeView, OnChangeSubmit => 0, Class => 'Modernize', ); $JobData{NewQueuesStrg} = $LayoutObject->AgentQueueListOption( Data => { $QueueObject->GetAllQueues(), }, Size => 5, Multiple => 0, Name => 'NewQueueID', SelectedID => $JobData{NewQueueID}, TreeView => $TreeView, OnChangeSubmit => 0, Class => 'Modernize', ); # get priority object my $PriorityObject = $Kernel::OM->Get('Kernel::System::Priority'); $JobData{PrioritiesStrg} = $LayoutObject->BuildSelection( Data => { $PriorityObject->PriorityList( UserID => 1, Action => $Self->{Action}, ), }, Name => 'PriorityIDs', Size => 5, Multiple => 1, SelectedID => $JobData{PriorityIDs}, Class => 'Modernize', ); $JobData{NewPrioritiesStrg} = $LayoutObject->BuildSelection( Data => { $PriorityObject->PriorityList( UserID => 1, Action => $Self->{Action}, ), }, Name => 'NewPriorityID', Size => 5, Multiple => 0, SelectedID => $JobData{NewPriorityID}, Class => 'Modernize', ); # get time option my %Map = ( TicketCreate => 'Time', TicketChange => 'ChangeTime', TicketClose => 'CloseTime', TicketLastChange => 'LastChangeTime', TicketPending => 'TimePending', TicketEscalation => 'EscalationTime', TicketEscalationResponse => 'EscalationResponseTime', TicketEscalationUpdate => 'EscalationUpdateTime', TicketEscalationSolution => 'EscalationSolutionTime', ); for my $Type ( qw( TicketCreate TicketClose TicketChange TicketLastChange TicketPending TicketEscalation TicketEscalationResponse TicketEscalationUpdate TicketEscalationSolution ) ) { my $SearchType = $Map{$Type} . 'SearchType'; if ( !$JobData{$SearchType} ) { $JobData{ $SearchType . '::None' } = 'checked="checked"'; } elsif ( $JobData{$SearchType} eq 'TimePoint' ) { $JobData{ $SearchType . '::TimePoint' } = 'checked="checked"'; } elsif ( $JobData{$SearchType} eq 'TimeSlot' ) { $JobData{ $SearchType . '::TimeSlot' } = 'checked="checked"'; } my %Counter; for my $Number ( 1 .. 60 ) { $Counter{$Number} = sprintf( "%02d", $Number ); } # time $JobData{ $Type . 'TimePoint' } = $LayoutObject->BuildSelection( Data => \%Counter, Name => $Type . 'TimePoint', SelectedID => $JobData{ $Type . 'TimePoint' }, Translation => 0, ); $JobData{ $Type . 'TimePointStart' } = $LayoutObject->BuildSelection( Data => { Last => Translatable('within the last ...'), Next => Translatable('within the next ...'), Before => Translatable('more than ... ago'), }, Name => $Type . 'TimePointStart', SelectedID => $JobData{ $Type . 'TimePointStart' } || 'Last', ); $JobData{ $Type . 'TimePointFormat' } = $LayoutObject->BuildSelection( Data => { minute => Translatable('minute(s)'), hour => Translatable('hour(s)'), day => Translatable('day(s)'), week => Translatable('week(s)'), month => Translatable('month(s)'), year => Translatable('year(s)'), }, Name => $Type . 'TimePointFormat', SelectedID => $JobData{ $Type . 'TimePointFormat' }, ); $JobData{ $Type . 'TimeStart' } = $LayoutObject->BuildDateSelection( %JobData, Prefix => $Type . 'TimeStart', Format => 'DateInputFormat', DiffTime => -( 60 * 60 * 24 ) * 30, Validate => 1, ); $JobData{ $Type . 'TimeStop' } = $LayoutObject->BuildDateSelection( %JobData, Prefix => $Type . 'TimeStop', Format => 'DateInputFormat', Validate => 1, ); } $JobData{DeleteOption} = $LayoutObject->BuildSelection( Data => $ConfigObject->Get('YesNoOptions'), Name => 'NewDelete', SelectedID => $JobData{NewDelete} || 0, Class => 'Modernize', ); $JobData{ValidOption} = $LayoutObject->BuildSelection( Data => $ConfigObject->Get('YesNoOptions'), Name => 'Valid', SelectedID => defined( $JobData{Valid} ) ? $JobData{Valid} : 1, Class => 'Modernize', ); # get lock object my $LockObject = $Kernel::OM->Get('Kernel::System::Lock'); $JobData{LockOption} = $LayoutObject->BuildSelection( Data => { $LockObject->LockList( UserID => 1, Action => $Self->{Action}, ), }, Name => 'LockIDs', Multiple => 1, Size => 3, SelectedID => $JobData{LockIDs}, Class => 'Modernize', ); $JobData{NewLockOption} = $LayoutObject->BuildSelection( Data => { $LockObject->LockList( UserID => 1, Action => $Self->{Action}, ), }, Name => 'NewLockID', Size => 3, Multiple => 0, SelectedID => $JobData{NewLockID}, Class => 'Modernize', ); # Show server errors if ticket selection contains stop words my %StopWordsServerErrors; if ( !$Param{StopWordsAlreadyChecked} ) { %StopWordsServerErrors = $Self->_StopWordsServerErrorsGet( MIMEBase_From => $JobData{MIMEBase_From}, MIMEBase_To => $JobData{MIMEBase_To}, MIMEBase_Cc => $JobData{MIMEBase_Cc}, MIMEBase_Subject => $JobData{MIMEBase_Subject}, MIMEBase_Body => $JobData{MIMEBase_Body}, ); } # REMARK: we changed the wording "Send no notifications" to # "Send agent/customer notifications on changes" in frontend. # But the backend code is still the same (compatibility). # Because of this case we changed 1=>'Yes' to 1=>'No' $JobData{SendNoNotificationOption} = $LayoutObject->BuildSelection( Data => { '1' => Translatable('No'), '0' => Translatable('Yes'), }, Name => 'NewSendNoNotification', SelectedID => $JobData{NewSendNoNotification} || 0, Class => 'Modernize', ); $JobData{AllowCustomScriptExecution} = $ConfigObject->Get('Ticket::GenericAgentAllowCustomScriptExecution') || 0; $JobData{AllowCustomModuleExecution} = $ConfigObject->Get('Ticket::GenericAgentAllowCustomModuleExecution') || 0; $LayoutObject->Block( Name => 'ActionList', ); $LayoutObject->Block( Name => 'ActionOverview', ); $LayoutObject->Block( Name => 'Edit', Data => { %JobData, %Param, %StopWordsServerErrors, }, ); # check for profile errors if ( defined $Param{ProfileInvalid} ) { $Param{ProfileInvalidMsg} //= ''; $LayoutObject->Block( Name => 'ProfileInvalidMsg' . $Param{ProfileInvalidMsg}, ); } # check if the schedule options are selected if ( !defined $JobData{ScheduleDays}->[0] || !defined $JobData{ScheduleHours}->[0] || !defined $JobData{ScheduleMinutes}->[0] ) { $LayoutObject->Block( Name => 'JobScheduleWarning', ); } # build type string if ( $ConfigObject->Get('Ticket::Type') ) { my %Type = $Kernel::OM->Get('Kernel::System::Type')->TypeList( UserID => $Self->{UserID}, ); $JobData{TypesStrg} = $LayoutObject->BuildSelection( Data => \%Type, Name => 'TypeIDs', SelectedID => $JobData{TypeIDs}, Sort => 'AlphanumericValue', Size => 3, Multiple => 1, Translation => 0, Class => 'Modernize', ); $LayoutObject->Block( Name => 'TicketType', Data => \%JobData, ); $JobData{NewTypesStrg} = $LayoutObject->BuildSelection( Data => \%Type, Name => 'NewTypeID', SelectedID => $JobData{NewTypeID}, Sort => 'AlphanumericValue', Size => 3, Multiple => 0, Translation => 0, Class => 'Modernize', ); $LayoutObject->Block( Name => 'NewTicketType', Data => \%JobData, ); } # build service string if ( $ConfigObject->Get('Ticket::Service') ) { # get list type my %Service = $Kernel::OM->Get('Kernel::System::Service')->ServiceList( Valid => 1, KeepChildren => $ConfigObject->Get('Ticket::Service::KeepChildren') // 0, UserID => $Self->{UserID}, ); my %NewService = %Service; $JobData{ServicesStrg} = $LayoutObject->BuildSelection( Data => \%Service, Name => 'ServiceIDs', SelectedID => $JobData{ServiceIDs}, Size => 5, Multiple => 1, TreeView => $TreeView, Translation => 0, Max => 200, Class => 'Modernize', ); $JobData{NewServicesStrg} = $LayoutObject->BuildSelection( Data => \%NewService, Name => 'NewServiceID', SelectedID => $JobData{NewServiceID}, Size => 5, Multiple => 0, TreeView => $TreeView, Translation => 0, Max => 200, Class => 'Modernize', ); my %SLA = $Kernel::OM->Get('Kernel::System::SLA')->SLAList( UserID => $Self->{UserID}, ); $JobData{SLAsStrg} = $LayoutObject->BuildSelection( Data => \%SLA, Name => 'SLAIDs', SelectedID => $JobData{SLAIDs}, Sort => 'AlphanumericValue', Size => 5, Multiple => 1, Translation => 0, Max => 200, Class => 'Modernize', ); $JobData{NewSLAsStrg} = $LayoutObject->BuildSelection( Data => \%SLA, Name => 'NewSLAID', SelectedID => $JobData{NewSLAID}, Sort => 'AlphanumericValue', Size => 5, Multiple => 0, Translation => 0, Max => 200, Class => 'Modernize', ); $LayoutObject->Block( Name => 'TicketService', Data => {%JobData}, ); $LayoutObject->Block( Name => 'NewTicketService', Data => {%JobData}, ); } # ticket responsible string if ( $ConfigObject->Get('Ticket::Responsible') ) { $JobData{ResponsibleStrg} = $LayoutObject->BuildSelection( Data => \%ShownUsers, Name => 'ResponsibleIDs', Size => 5, Multiple => 1, Translation => 0, SelectedID => $JobData{ResponsibleIDs}, Class => 'Modernize', ); $JobData{NewResponsibleStrg} = $LayoutObject->BuildSelection( Data => \%ShownUsers, Name => 'NewResponsibleID', Size => 5, Multiple => 0, Translation => 0, SelectedID => $JobData{NewResponsibleID}, Class => 'Modernize', ); $LayoutObject->Block( Name => 'TicketResponsible', Data => {%JobData}, ); $LayoutObject->Block( Name => 'NewTicketResponsible', Data => {%JobData}, ); } # prepare archive if ( $ConfigObject->Get('Ticket::ArchiveSystem') ) { $JobData{'SearchInArchiveStrg'} = $LayoutObject->BuildSelection( Data => { ArchivedTickets => Translatable('Archived tickets'), NotArchivedTickets => Translatable('Unarchived tickets'), AllTickets => Translatable('All tickets'), }, Name => 'SearchInArchive', SelectedID => $JobData{SearchInArchive} || 'AllTickets', Class => 'Modernize', ); $LayoutObject->Block( Name => 'SearchInArchive', Data => {%JobData}, ); $JobData{'NewArchiveFlagStrg'} = $LayoutObject->BuildSelection( Data => { 'y' => Translatable('archive tickets'), 'n' => Translatable('restore tickets from archive'), }, Name => 'NewArchiveFlag', PossibleNone => 1, SelectedID => $JobData{NewArchiveFlag} || '', Class => 'Modernize', ); $LayoutObject->Block( Name => 'NewArchiveFlag', Data => {%JobData}, ); } # create dynamic field HTML for set with historical data options my $PrintDynamicFieldsSearchHeader = 1; # get dynamic field backend object my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); # cycle trough the activated Dynamic Fields for this screen DYNAMICFIELD: for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); # get search field preferences my $SearchFieldPreferences = $DynamicFieldBackendObject->SearchFieldPreferences( DynamicFieldConfig => $DynamicFieldConfig, ); next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); PREFERENCE: for my $Preference ( @{$SearchFieldPreferences} ) { # get field HTML my $DynamicFieldHTML = $DynamicFieldBackendObject->SearchFieldRender( DynamicFieldConfig => $DynamicFieldConfig, Profile => \%JobData, DefaultValue => $Self->{Config}->{Defaults}->{DynamicField}->{ $DynamicFieldConfig->{Name} }, LayoutObject => $LayoutObject, ConfirmationCheckboxes => 1, Type => $Preference->{Type}, ); next PREFERENCE if !IsHashRefWithData($DynamicFieldHTML); if ($PrintDynamicFieldsSearchHeader) { $LayoutObject->Block( Name => 'DynamicField', ); $PrintDynamicFieldsSearchHeader = 0; } # output dynamic field $LayoutObject->Block( Name => 'DynamicFieldElement', Data => { Label => $DynamicFieldHTML->{Label}, Field => $DynamicFieldHTML->{Field}, }, ); } } # create dynamic field HTML for set with historical data options my $PrintDynamicFieldsEditHeader = 1; # get param object my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); # cycle trough the activated Dynamic Fields for this screen DYNAMICFIELD: for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); # Check if field is Attachment type ( from OTRSDynamicFieldAttachment ) # this field is not updatable by Generic Agent my $IsAttachement = $DynamicFieldBackendObject->HasBehavior( DynamicFieldConfig => $DynamicFieldConfig, Behavior => 'IsAttachement', ); next DYNAMICFIELD if $IsAttachement; my $PossibleValuesFilter; my $IsACLReducible = $DynamicFieldBackendObject->HasBehavior( DynamicFieldConfig => $DynamicFieldConfig, Behavior => 'IsACLReducible', ); if ($IsACLReducible) { # get PossibleValues my $PossibleValues = $DynamicFieldBackendObject->PossibleValuesGet( DynamicFieldConfig => $DynamicFieldConfig, ); # check if field has PossibleValues property in its configuration if ( IsHashRefWithData($PossibleValues) ) { # convert possible values key => value to key => key for ACLs using a Hash slice my %AclData = %{$PossibleValues}; @AclData{ keys %AclData } = keys %AclData; # get ticket object my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); # set possible values filter from ACLs my $ACL = $TicketObject->TicketAcl( Action => $Self->{Action}, Type => 'DynamicField_' . $DynamicFieldConfig->{Name}, ReturnType => 'Ticket', ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name}, Data => \%AclData, UserID => $Self->{UserID}, ); if ($ACL) { my %Filter = $TicketObject->TicketAclData(); # convert Filer key => key back to key => value using map %{$PossibleValuesFilter} = map { $_ => $PossibleValues->{$_} } keys %Filter; } } } # get field HTML my $DynamicFieldHTML = $DynamicFieldBackendObject->EditFieldRender( DynamicFieldConfig => $DynamicFieldConfig, PossibleValuesFilter => $PossibleValuesFilter, LayoutObject => $LayoutObject, ParamObject => $ParamObject, UseDefaultValue => 0, OverridePossibleNone => 1, ConfirmationNeeded => 1, Template => \%JobData, MaxLength => 200, ); next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldHTML); if ($PrintDynamicFieldsEditHeader) { $LayoutObject->Block( Name => 'NewDynamicField', ); $PrintDynamicFieldsEditHeader = 0; } # output dynamic field $LayoutObject->Block( Name => 'NewDynamicFieldElement', Data => { Label => $DynamicFieldHTML->{Label}, Field => $DynamicFieldHTML->{Field}, }, ); } # get event object my $EventObject = $Kernel::OM->Get('Kernel::System::Event'); # get registered event triggers from the config my %RegisteredEvents = $EventObject->EventList( ObjectTypes => [ 'Ticket', 'Article' ], ); # create the event triggers table for my $Event ( @{ $JobData{EventValues} || [] } ) { # set the event type ( event object like Article or Ticket) my $EventType; EVENTTYPE: for my $Type ( sort keys %RegisteredEvents ) { if ( grep { $_ eq $Event } @{ $RegisteredEvents{$Type} } ) { $EventType = $Type; last EVENTTYPE; } } # paint each event row in event triggers table $LayoutObject->Block( Name => 'EventRow', Data => { Event => $Event, EventType => $EventType || '-', }, ); } my @EventTypeList; my $SelectedEventType = $ParamObject->GetParam( Param => 'EventType' ) || 'Ticket'; # create event trigger selectors (one for each type) TYPE: for my $Type ( sort keys %RegisteredEvents ) { # refresh event list for each event type # paint each selector my $EventStrg = $LayoutObject->BuildSelection( PossibleNone => 0, Data => $RegisteredEvents{$Type} || [], Name => $Type . 'Event', Sort => 'AlphanumericValue', PossibleNone => 0, Class => 'Modernize EventList GenericInterfaceSpacing', Title => $LayoutObject->{LanguageObject}->Translate('Event'), ); $LayoutObject->Block( Name => 'EventAdd', Data => { EventStrg => $EventStrg, }, ); push @EventTypeList, $Type; } # create event type selector my $EventTypeStrg = $LayoutObject->BuildSelection( Data => \@EventTypeList, Name => 'EventType', Sort => 'AlphanumericValue', SelectedValue => $SelectedEventType, PossibleNone => 0, Class => 'Modernize', Title => $LayoutObject->{LanguageObject}->Translate('Type'), ); $LayoutObject->Block( Name => 'EventTypeStrg', Data => { EventTypeStrg => $EventTypeStrg, }, ); return \%JobData; } sub _MaskRun { my ( $Self, %Param ) = @_; # get layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); my %JobData; if ( $Self->{Profile} ) { %JobData = $Kernel::OM->Get('Kernel::System::GenericAgent')->JobGet( Name => $Self->{Profile} ); if ( exists $JobData{SearchInArchive} && $JobData{SearchInArchive} eq 'ArchivedTickets' ) { $JobData{ArchiveFlags} = ['y']; } if ( exists $JobData{SearchInArchive} && $JobData{SearchInArchive} eq 'AllTickets' ) { $JobData{ArchiveFlags} = [ 'y', 'n' ]; } } else { $LayoutObject->FatalError( Message => Translatable('Need Profile!'), ); } $JobData{Profile} = $Self->{Profile}; $Param{Subaction} = $Self->{Subaction}; $Param{Profile} = $Self->{Profile}; # dynamic fields search parameters for ticket search my %DynamicFieldSearchParameters; # get dynamic field backend object my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); # cycle trough the activated Dynamic Fields for this screen DYNAMICFIELD: for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); # get search field preferences my $SearchFieldPreferences = $DynamicFieldBackendObject->SearchFieldPreferences( DynamicFieldConfig => $DynamicFieldConfig, ); next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); PREFERENCE: for my $Preference ( @{$SearchFieldPreferences} ) { if ( !$JobData{ 'Search_DynamicField_' . $DynamicFieldConfig->{Name} . $Preference->{Type} } ) { next PREFERENCE; } # extract the dynamic field value from the profile my $SearchParameter = $DynamicFieldBackendObject->SearchFieldParameterBuild( DynamicFieldConfig => $DynamicFieldConfig, Profile => \%JobData, LayoutObject => $LayoutObject, Type => $Preference->{Type}, ); # set search parameter if ( defined $SearchParameter ) { $DynamicFieldSearchParameters{ 'DynamicField_' . $DynamicFieldConfig->{Name} } = $SearchParameter->{Parameter}; } } } # remove residual dynamic field data from job definition # they are passed through dedicated variable anyway PARAM_NAME: for my $ParamName ( sort keys %JobData ) { next PARAM_NAME if !( $ParamName =~ /^DynamicField_/ ); delete $JobData{$ParamName}; } # get needed objects my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article'); my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); # perform ticket search my $GenericAgentTicketSearch = $ConfigObject->Get("Ticket::GenericAgentTicketSearch") || {}; my $Counter = $TicketObject->TicketSearch( Result => 'COUNT', SortBy => 'Age', OrderBy => 'Down', UserID => 1, Limit => 60_000, ConditionInline => $GenericAgentTicketSearch->{ExtendedSearchCondition}, %JobData, %DynamicFieldSearchParameters, ) || 0; my @TicketIDs = $TicketObject->TicketSearch( Result => 'ARRAY', SortBy => 'Age', OrderBy => 'Down', UserID => 1, Limit => 30, ConditionInline => $GenericAgentTicketSearch->{ExtendedSearchCondition}, %JobData, %DynamicFieldSearchParameters, ); $LayoutObject->Block( Name => 'ActionList', ); $LayoutObject->Block( Name => 'ActionOverview', ); $LayoutObject->Block( Name => 'Result', Data => { %Param, Name => $Self->{Profile}, AffectedIDs => $Counter, }, ); my $RunLimit = $ConfigObject->Get('Ticket::GenericAgentRunLimit'); if ( $Counter > $RunLimit ) { $LayoutObject->Block( Name => 'RunLimit', Data => { Counter => $Counter, RunLimit => $RunLimit, }, ); } if (@TicketIDs) { $LayoutObject->Block( Name => 'ResultBlock', ); for my $TicketID (@TicketIDs) { # Get ticket data. my %Ticket = $TicketObject->TicketGet( TicketID => $TicketID, DynamicFields => 0, ); # Get article data. my @Articles = $ArticleObject->ArticleList( TicketID => $TicketID, OnlyFirst => 1, ); my %Article; for my $Article (@Articles) { %Article = $ArticleObject->BackendForArticle( %{$Article} )->ArticleGet( %{$Article} ); } my %Data = ( %Ticket, %Article ); # Set missing information for tickets without articles. if ( !%Article ) { $Data{Subject} = $Data{Title}; } $Data{Age} = $LayoutObject->CustomerAge( Age => $Data{Age}, Space => ' ', ); $Data{css} = "PriorityID-$Data{PriorityID}"; # user info my %UserInfo = $Kernel::OM->Get('Kernel::System::User')->GetUserData( User => $Data{Owner}, ); $Data{UserLastname} = $UserInfo{UserLastname}; $Data{UserFirstname} = $UserInfo{UserFirstname}; $Data{UserFullname} = $UserInfo{UserFullname}; $LayoutObject->Block( Name => 'Ticket', Data => \%Data, ); } if ( $JobData{NewDelete} ) { $LayoutObject->Block( Name => 'DeleteWarning', ); } } # HTML search mask output my $Output = $LayoutObject->Header( Title => Translatable('Affected Tickets'), ); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( TemplateFile => 'AdminGenericAgent', Data => \%Param, ); # build footer $Output .= $LayoutObject->Footer(); return $Output; } sub _StopWordsServerErrorsGet { my ( $Self, %Param ) = @_; if ( !%Param ) { $Kernel::OM->Get('Kernel::Output::HTML::Layout')->FatalError( Message => Translatable('Got no values to check.'), ); } my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article'); my %StopWordsServerErrors; if ( !$ArticleObject->SearchStringStopWordsUsageWarningActive() ) { return %StopWordsServerErrors; } my %SearchStrings; FIELD: for my $Field ( sort keys %Param ) { next FIELD if !defined $Param{$Field}; next FIELD if !length $Param{$Field}; $SearchStrings{$Field} = $Param{$Field}; } if (%SearchStrings) { my $StopWords = $ArticleObject->SearchStringStopWordsFind( SearchStrings => \%SearchStrings, ); FIELD: for my $Field ( sort keys %{$StopWords} ) { next FIELD if !defined $StopWords->{$Field}; next FIELD if ref $StopWords->{$Field} ne 'ARRAY'; next FIELD if !@{ $StopWords->{$Field} }; $StopWordsServerErrors{ $Field . 'Invalid' } = 'ServerError'; $StopWordsServerErrors{ $Field . 'InvalidTooltip' } = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{LanguageObject} ->Translate('Please remove the following words because they cannot be used for the ticket selection:') . ' ' . join( ',', sort @{ $StopWords->{$Field} } ); } } return %StopWordsServerErrors; } 1;