1# --
2# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/
3# --
4# This software comes with ABSOLUTELY NO WARRANTY. For details, see
5# the enclosed file COPYING for license information (GPL). If you
6# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
7# --
8
9package Kernel::Modules::AdminProcessManagementTransitionAction;
10
11use strict;
12use warnings;
13
14use Kernel::System::VariableCheck qw(:all);
15use Kernel::Language qw(Translatable);
16
17our $ObjectManagerDisabled = 1;
18
19sub new {
20    my ( $Type, %Param ) = @_;
21
22    # allocate new hash for object
23    my $Self = {%Param};
24    bless( $Self, $Type );
25
26    return $Self;
27}
28
29sub Run {
30    my ( $Self, %Param ) = @_;
31
32    my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
33
34    $Self->{Subaction} = $ParamObject->GetParam( Param => 'Subaction' ) || '';
35
36    my $TransitionActionID = $ParamObject->GetParam( Param => 'ID' )       || '';
37    my $EntityID           = $ParamObject->GetParam( Param => 'EntityID' ) || '';
38
39    my %SessionData = $Kernel::OM->Get('Kernel::System::AuthSession')->GetSessionIDData(
40        SessionID => $Self->{SessionID},
41    );
42
43    # convert JSON string to array
44    $Self->{ScreensPath} = $Kernel::OM->Get('Kernel::System::JSON')->Decode(
45        Data => $SessionData{ProcessManagementScreensPath}
46    );
47
48    # get needed objects
49    my $LayoutObject           = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
50    my $EntityObject           = $Kernel::OM->Get('Kernel::System::ProcessManagement::DB::Entity');
51    my $TransitionActionObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::DB::TransitionAction');
52
53    # ------------------------------------------------------------ #
54    # TransitionActionNew
55    # ------------------------------------------------------------ #
56    if ( $Self->{Subaction} eq 'TransitionActionNew' ) {
57
58        return $Self->_ShowEdit(
59            %Param,
60            Action => 'New',
61        );
62    }
63
64    # ------------------------------------------------------------ #
65    # TransitionActionNewAction
66    # ------------------------------------------------------------ #
67    elsif ( $Self->{Subaction} eq 'TransitionActionNewAction' ) {
68
69        # challenge token check for write action
70        $LayoutObject->ChallengeTokenCheck();
71
72        # get transition action data
73        my $TransitionActionData;
74
75        # get parameter from web browser
76        my $GetParam = $Self->_GetParams();
77
78        # set new configuration
79        $TransitionActionData->{Name}             = $GetParam->{Name};
80        $TransitionActionData->{Config}->{Module} = $GetParam->{Module};
81        $TransitionActionData->{Config}->{Config} = $GetParam->{Config};
82
83        # check required parameters
84        my %Error;
85        if ( !$GetParam->{Name} ) {
86
87            # add server error error class
88            $Error{NameServerError}        = 'ServerError';
89            $Error{NameServerErrorMessage} = Translatable('This field is required');
90        }
91
92        if ( !$GetParam->{Module} ) {
93
94            # add server error error class
95            $Error{ModuleServerError}        = 'ServerError';
96            $Error{ModuleServerErrorMessage} = Translatable('This field is required');
97        }
98
99        if ( !$GetParam->{Config} ) {
100            return $LayoutObject->ErrorScreen(
101                Message => Translatable('At least one valid config parameter is required.'),
102            );
103        }
104
105        # if there is an error return to edit screen
106        if ( IsHashRefWithData( \%Error ) ) {
107            return $Self->_ShowEdit(
108                %Error,
109                %Param,
110                TransitionActionData => $TransitionActionData,
111                Action               => 'New',
112            );
113        }
114
115        # generate entity ID
116        my $EntityID = $EntityObject->EntityIDGenerate(
117            EntityType => 'TransitionAction',
118            UserID     => $Self->{UserID},
119        );
120
121        # show error if can't generate a new EntityID
122        if ( !$EntityID ) {
123            return $LayoutObject->ErrorScreen(
124                Message => Translatable('There was an error generating a new EntityID for this TransitionAction'),
125            );
126        }
127
128        # otherwise save configuration and return process screen
129        my $TransitionActionID = $TransitionActionObject->TransitionActionAdd(
130            Name     => $TransitionActionData->{Name},
131            Module   => $TransitionActionData->{Module},
132            EntityID => $EntityID,
133            Config   => $TransitionActionData->{Config},
134            UserID   => $Self->{UserID},
135        );
136
137        # show error if can't create
138        if ( !$TransitionActionID ) {
139            return $LayoutObject->ErrorScreen(
140                Message => Translatable('There was an error creating the TransitionAction'),
141            );
142        }
143
144        # set entitty sync state
145        my $Success = $EntityObject->EntitySyncStateSet(
146            EntityType => 'TransitionAction',
147            EntityID   => $EntityID,
148            SyncState  => 'not_sync',
149            UserID     => $Self->{UserID},
150        );
151
152        # show error if can't set
153        if ( !$Success ) {
154            return $LayoutObject->ErrorScreen(
155                Message => $LayoutObject->{LanguageObject}->Translate(
156                    'There was an error setting the entity sync status for TransitionAction entity: %s',
157                    $EntityID
158                ),
159            );
160        }
161
162        # remove this screen from session screen path
163        $Self->_PopSessionScreen( OnlyCurrent => 1 );
164
165        my $Redirect = $ParamObject->GetParam( Param => 'PopupRedirect' ) || '';
166
167        # get latest config data to send it back to main window
168        my $TransitionActionConfig = $Self->_GetTransitionActionConfig(
169            EntityID => $EntityID,
170        );
171
172        # check if needed to open another window or if popup should go back
173        if ( $Redirect && $Redirect eq '1' ) {
174
175            $Self->_PushSessionScreen(
176                ID        => $TransitionActionID,
177                EntityID  => $EntityID,
178                Subaction => 'TransitionActionEdit'    # always use edit screen
179            );
180
181            my $RedirectAction    = $ParamObject->GetParam( Param => 'PopupRedirectAction' )    || '';
182            my $RedirectSubaction = $ParamObject->GetParam( Param => 'PopupRedirectSubaction' ) || '';
183            my $RedirectID        = $ParamObject->GetParam( Param => 'PopupRedirectID' )        || '';
184            my $RedirectEntityID  = $ParamObject->GetParam( Param => 'PopupRedirectEntityID' )  || '';
185
186            # redirect to another popup window
187            return $Self->_PopupResponse(
188                Redirect => 1,
189                Screen   => {
190                    Action    => $RedirectAction,
191                    Subaction => $RedirectSubaction,
192                    ID        => $RedirectID,
193                    EntityID  => $RedirectID,
194                },
195                ConfigJSON => $TransitionActionConfig,
196            );
197        }
198        else {
199
200            # remove last screen
201            my $LastScreen = $Self->_PopSessionScreen();
202
203            # check if needed to return to main screen or to be redirected to last screen
204            if ( $LastScreen->{Action} eq 'AdminProcessManagement' ) {
205
206                # close the popup
207                return $Self->_PopupResponse(
208                    ClosePopup => 1,
209                    ConfigJSON => $TransitionActionConfig,
210                );
211            }
212            else {
213
214                # redirect to last screen
215                return $Self->_PopupResponse(
216                    Redirect   => 1,
217                    Screen     => $LastScreen,
218                    ConfigJSON => $TransitionActionConfig,
219                );
220            }
221        }
222    }
223
224    # ------------------------------------------------------------ #
225    # TransitionActionEdit
226    # ------------------------------------------------------------ #
227    elsif ( $Self->{Subaction} eq 'TransitionActionEdit' ) {
228
229        # check for TransitionActionID
230        if ( !$TransitionActionID ) {
231            return $LayoutObject->ErrorScreen(
232                Message => Translatable('Need TransitionActionID!'),
233            );
234        }
235
236        # remove this screen from session screen path
237        $Self->_PopSessionScreen( OnlyCurrent => 1 );
238
239        # get TransitionAction data
240        my $TransitionActionData = $TransitionActionObject->TransitionActionGet(
241            ID     => $TransitionActionID,
242            UserID => $Self->{UserID},
243        );
244
245        # check for valid TransitionAction data
246        if ( !IsHashRefWithData($TransitionActionData) ) {
247            return $LayoutObject->ErrorScreen(
248                Message => $LayoutObject->{LanguageObject}->Translate(
249                    'Could not get data for TransitionActionID %s',
250                    $TransitionActionID
251                ),
252            );
253        }
254
255        return $Self->_ShowEdit(
256            %Param,
257            TransitionActionID   => $TransitionActionID,
258            TransitionActionData => $TransitionActionData,
259            Action               => 'Edit',
260        );
261    }
262
263    # ------------------------------------------------------------ #
264    # TransitionActionEditAction
265    # ------------------------------------------------------------ #
266    elsif ( $Self->{Subaction} eq 'TransitionActionEditAction' ) {
267
268        # challenge token check for write action
269        $LayoutObject->ChallengeTokenCheck();
270
271        # get transition action data
272        my $TransitionActionData;
273
274        # get parameter from web browser
275        my $GetParam = $Self->_GetParams();
276
277        # set new configuration
278        $TransitionActionData->{Name}             = $GetParam->{Name};
279        $TransitionActionData->{EntityID}         = $GetParam->{EntityID};
280        $TransitionActionData->{Config}->{Module} = $GetParam->{Module};
281        $TransitionActionData->{Config}->{Config} = $GetParam->{Config};
282
283        # check required parameters
284        my %Error;
285        if ( !$GetParam->{Name} ) {
286
287            # add server error error class
288            $Error{NameServerError}        = 'ServerError';
289            $Error{NameServerErrorMessage} = Translatable('This field is required');
290        }
291
292        if ( !$GetParam->{Module} ) {
293
294            # add server error error class
295            $Error{ModuleServerError}        = 'ServerError';
296            $Error{ModuleServerErrorMessage} = Translatable('This field is required');
297        }
298
299        if ( !$GetParam->{Config} ) {
300            return $LayoutObject->ErrorScreen(
301                Message => Translatable('At least one valid config parameter is required.'),
302            );
303        }
304
305        # if there is an error return to edit screen
306        if ( IsHashRefWithData( \%Error ) ) {
307            return $Self->_ShowEdit(
308                %Error,
309                %Param,
310                TransitionActionData => $TransitionActionData,
311                Action               => 'Edit',
312            );
313        }
314
315        # otherwise save configuration and return to overview screen
316        my $Success = $TransitionActionObject->TransitionActionUpdate(
317            ID       => $TransitionActionID,
318            EntityID => $TransitionActionData->{EntityID},
319            Name     => $TransitionActionData->{Name},
320            Module   => $TransitionActionData->{Module},
321            Config   => $TransitionActionData->{Config},
322            UserID   => $Self->{UserID},
323        );
324
325        # show error if can't update
326        if ( !$Success ) {
327            return $LayoutObject->ErrorScreen(
328                Message => Translatable('There was an error updating the TransitionAction'),
329            );
330        }
331
332        # set entitty sync state
333        $Success = $EntityObject->EntitySyncStateSet(
334            EntityType => 'TransitionAction',
335            EntityID   => $TransitionActionData->{EntityID},
336            SyncState  => 'not_sync',
337            UserID     => $Self->{UserID},
338        );
339
340        # show error if can't set
341        if ( !$Success ) {
342            return $LayoutObject->ErrorScreen(
343                Message => $LayoutObject->{LanguageObject}->Translate(
344                    'There was an error setting the entity sync status for TransitionAction entity: %s',
345                    $TransitionActionData->{EntityID}
346                ),
347            );
348        }
349
350        # remove this screen from session screen path
351        $Self->_PopSessionScreen( OnlyCurrent => 1 );
352
353        my $Redirect = $ParamObject->GetParam( Param => 'PopupRedirect' ) || '';
354
355        # get latest config data to send it back to main window
356        my $TransitionActionConfig = $Self->_GetTransitionActionConfig(
357            EntityID => $TransitionActionData->{EntityID},
358        );
359
360        # check if needed to open another window or if popup should go back
361        if ( $Redirect && $Redirect eq '1' ) {
362
363            $Self->_PushSessionScreen(
364                ID        => $TransitionActionID,
365                EntityID  => $TransitionActionData->{EntityID},
366                Subaction => 'TransitionActionEdit'               # always use edit screen
367            );
368
369            my $RedirectAction    = $ParamObject->GetParam( Param => 'PopupRedirectAction' )    || '';
370            my $RedirectSubaction = $ParamObject->GetParam( Param => 'PopupRedirectSubaction' ) || '';
371            my $RedirectID        = $ParamObject->GetParam( Param => 'PopupRedirectID' )        || '';
372            my $RedirectEntityID  = $ParamObject->GetParam( Param => 'PopupRedirectEntityID' )  || '';
373
374            # redirect to another popup window
375            return $Self->_PopupResponse(
376                Redirect => 1,
377                Screen   => {
378                    Action    => $RedirectAction,
379                    Subaction => $RedirectSubaction,
380                    ID        => $RedirectID,
381                    EntityID  => $RedirectID,
382                },
383                ConfigJSON => $TransitionActionConfig,
384            );
385        }
386        else {
387
388            # remove last screen
389            my $LastScreen = $Self->_PopSessionScreen();
390
391            # check if needed to return to main screen or to be redirected to last screen
392            if ( $LastScreen->{Action} eq 'AdminProcessManagement' ) {
393
394                # close the popup
395                return $Self->_PopupResponse(
396                    ClosePopup => 1,
397                    ConfigJSON => $TransitionActionConfig,
398                );
399            }
400            else {
401
402                # redirect to last screen
403                return $Self->_PopupResponse(
404                    Redirect   => 1,
405                    Screen     => $LastScreen,
406                    ConfigJSON => $TransitionActionConfig,
407                );
408            }
409        }
410    }
411
412    # ------------------------------------------------------------ #
413    # Close popup
414    # ------------------------------------------------------------ #
415    elsif ( $Self->{Subaction} eq 'ClosePopup' ) {
416
417        # close the popup
418        return $Self->_PopupResponse(
419            ClosePopup => 1,
420            ConfigJSON => '',
421        );
422    }
423
424    # ------------------------------------------------------------ #
425    # Error
426    # ------------------------------------------------------------ #
427    else {
428        return $LayoutObject->ErrorScreen(
429            Message => Translatable('This subaction is not valid'),
430        );
431    }
432}
433
434sub _GetTransitionActionConfig {
435    my ( $Self, %Param ) = @_;
436
437    # Get new Transition Config as JSON
438    my $ProcessDump = $Kernel::OM->Get('Kernel::System::ProcessManagement::DB::Process')->ProcessDump(
439        ResultType => 'HASH',
440        UserID     => $Self->{UserID},
441    );
442
443    my %TransitionActionConfig;
444    $TransitionActionConfig{TransitionAction} = ();
445    $TransitionActionConfig{TransitionAction}->{ $Param{EntityID} }
446        = $ProcessDump->{TransitionAction}->{ $Param{EntityID} };
447
448    return \%TransitionActionConfig;
449}
450
451sub _ShowEdit {
452    my ( $Self, %Param ) = @_;
453
454    # get TransitionAction information
455    my $TransitionActionData = $Param{TransitionActionData} || {};
456
457    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
458
459    # check if last screen action is main screen
460    if ( $Self->{ScreensPath}->[-1]->{Action} eq 'AdminProcessManagement' ) {
461
462        # show close popup link
463        $LayoutObject->Block(
464            Name => 'ClosePopup',
465            Data => {},
466        );
467    }
468    else {
469
470        # show go back link
471        $LayoutObject->Block(
472            Name => 'GoBack',
473            Data => {
474                Action          => $Self->{ScreensPath}->[-1]->{Action}          || '',
475                Subaction       => $Self->{ScreensPath}->[-1]->{Subaction}       || '',
476                ID              => $Self->{ScreensPath}->[-1]->{ID}              || '',
477                EntityID        => $Self->{ScreensPath}->[-1]->{EntityID}        || '',
478                StartActivityID => $Self->{ScreensPath}->[-1]->{StartActivityID} || '',
479            },
480        );
481    }
482
483    if ( defined $Param{Action} && $Param{Action} eq 'Edit' ) {
484        $Param{Title} = $LayoutObject->{LanguageObject}->Translate(
485            'Edit Transition Action "%s"',
486            $TransitionActionData->{Name}
487        );
488    }
489    else {
490        $Param{Title} = Translatable('Create New Transition Action');
491    }
492
493    my $Output = $LayoutObject->Header(
494        Value => $Param{Title},
495        Type  => 'Small',
496    );
497
498    if ( defined $Param{Action} && $Param{Action} eq 'Edit' ) {
499
500        my $Index       = 1;
501        my @ConfigItems = sort keys %{ $TransitionActionData->{Config}->{Config} };
502        for my $Key (@ConfigItems) {
503
504            $LayoutObject->Block(
505                Name => 'ConfigItemEditRow',
506                Data => {
507                    Key   => $Key,
508                    Value => $TransitionActionData->{Config}->{Config}->{$Key},
509                    Index => $Index,
510                },
511            );
512            $Index++;
513        }
514
515        # display other affected transitions by editing this activity (if applicable)
516        my $AffectedProcesses = $Self->_CheckTransitionActionUsage(
517            EntityID => $TransitionActionData->{EntityID},
518        );
519
520        if ( @{$AffectedProcesses} ) {
521
522            $LayoutObject->Block(
523                Name => 'EditWarning',
524                Data => {
525                    ProcessList => join( ', ', @{$AffectedProcesses} ),
526                }
527            );
528        }
529
530    }
531    else {
532        $LayoutObject->Block(
533            Name => 'ConfigItemInitRow',
534        );
535    }
536
537    # lookup existing Transition Actions on disk
538    my $Directory
539        = $Kernel::OM->Get('Kernel::Config')->Get('Home') . '/Kernel/System/ProcessManagement/TransitionAction';
540    my @List = $Kernel::OM->Get('Kernel::System::Main')->DirectoryRead(
541        Directory => $Directory,
542        Filter    => '*.pm',
543    );
544    my %TransitionAction;
545    ITEM:
546    for my $Item (@List) {
547
548        # remove .pm
549        $Item =~ s/^.*\/(.+?)\.pm$/$1/;
550        next ITEM if $Item eq 'Base';
551        $TransitionAction{ 'Kernel::System::ProcessManagement::TransitionAction::' . $Item } = $Item;
552    }
553
554    # build TransitionAction string
555    $Param{ModuleStrg} = $LayoutObject->BuildSelection(
556        Data         => \%TransitionAction,
557        Name         => 'Module',
558        PossibleNone => 1,
559        SelectedID   => $TransitionActionData->{Config}->{Module},
560        Class        => 'Modernize Validate_Required ' . ( $Param{Errors}->{'ModuleInvalid'} || '' ),
561    );
562
563    $Output .= $LayoutObject->Output(
564        TemplateFile => "AdminProcessManagementTransitionAction",
565        Data         => {
566            %Param,
567            %{$TransitionActionData},
568            Name => $TransitionActionData->{Name},
569        },
570    );
571
572    $Output .= $LayoutObject->Footer();
573
574    return $Output;
575}
576
577sub _GetParams {
578    my ( $Self, %Param ) = @_;
579
580    my $GetParam;
581    my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
582
583    # get parameters from web browser
584    for my $ParamName (
585        qw( Name Module EntityID )
586        )
587    {
588        $GetParam->{$ParamName} = $ParamObject->GetParam( Param => $ParamName ) || '';
589    }
590
591    # get config params
592    my @ParamNames = $ParamObject->GetParamNames();
593    my ( @ConfigParamKeys, @ConfigParamValues );
594
595    for my $ParamName (@ParamNames) {
596
597        # get keys
598        if ( $ParamName =~ m{ConfigKey\[(\d+)\]}xms ) {
599            push @ConfigParamKeys, $1;
600        }
601
602        # get values
603        if ( $ParamName =~ m{ConfigValue\[(\d+)\]}xms ) {
604            push @ConfigParamValues, $1;
605        }
606    }
607
608    if ( @ConfigParamKeys != @ConfigParamValues ) {
609        return $Kernel::OM->Get('Kernel::Output::HTML::Layout')->ErrorScreen(
610            Message => Translatable('Error: Not all keys seem to have values or vice versa.'),
611        );
612    }
613
614    my ( $KeyValue, $ValueValue );
615    for my $Key (@ConfigParamKeys) {
616        $KeyValue   = $ParamObject->GetParam( Param => "ConfigKey[$Key]" );
617        $ValueValue = $ParamObject->GetParam( Param => "ConfigValue[$Key]" );
618        $GetParam->{Config}->{$KeyValue} = $ValueValue;
619    }
620
621    return $GetParam;
622}
623
624sub _PopSessionScreen {
625    my ( $Self, %Param ) = @_;
626
627    my $LastScreen;
628
629    if ( defined $Param{OnlyCurrent} && $Param{OnlyCurrent} == 1 ) {
630
631        # check if last screen action is current screen action
632        if ( $Self->{ScreensPath}->[-1]->{Action} eq $Self->{Action} ) {
633
634            # remove last screen
635            $LastScreen = pop @{ $Self->{ScreensPath} };
636        }
637    }
638    else {
639
640        # remove last screen
641        $LastScreen = pop @{ $Self->{ScreensPath} };
642    }
643
644    # convert screens path to string (JSON)
645    my $JSONScreensPath = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->JSONEncode(
646        Data => $Self->{ScreensPath},
647    );
648
649    # update session
650    $Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID(
651        SessionID => $Self->{SessionID},
652        Key       => 'ProcessManagementScreensPath',
653        Value     => $JSONScreensPath,
654    );
655
656    return $LastScreen;
657}
658
659sub _PushSessionScreen {
660    my ( $Self, %Param ) = @_;
661
662    # add screen to the screen path
663    push @{ $Self->{ScreensPath} }, {
664        Action    => $Self->{Action} || '',
665        Subaction => $Param{Subaction},
666        ID        => $Param{ID},
667        EntityID  => $Param{EntityID},
668    };
669
670    # convert screens path to string (JSON)
671    my $JSONScreensPath = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->JSONEncode(
672        Data => $Self->{ScreensPath},
673    );
674
675    # update session
676    $Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID(
677        SessionID => $Self->{SessionID},
678        Key       => 'ProcessManagementScreensPath',
679        Value     => $JSONScreensPath,
680    );
681
682    return 1;
683}
684
685sub _PopupResponse {
686    my ( $Self, %Param ) = @_;
687
688    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
689
690    if ( $Param{Redirect} && $Param{Redirect} eq 1 ) {
691
692        # send data to JS
693        $LayoutObject->AddJSData(
694            Key   => 'Redirect',
695            Value => {
696                ConfigJSON => $Param{ConfigJSON},
697                %{ $Param{Screen} },
698            }
699        );
700    }
701    elsif ( $Param{ClosePopup} && $Param{ClosePopup} eq 1 ) {
702
703        # send data to JS
704        $LayoutObject->AddJSData(
705            Key   => 'ClosePopup',
706            Value => {
707                ConfigJSON => $Param{ConfigJSON},
708            }
709        );
710    }
711
712    my $Output = $LayoutObject->Header( Type => 'Small' );
713    $Output .= $LayoutObject->Output(
714        TemplateFile => "AdminProcessManagementPopupResponse",
715        Data         => {},
716    );
717    $Output .= $LayoutObject->Footer( Type => 'Small' );
718
719    return $Output;
720}
721
722sub _CheckTransitionActionUsage {
723    my ( $Self, %Param ) = @_;
724
725    # get a list of parents with all the details
726    my $List = $Kernel::OM->Get('Kernel::System::ProcessManagement::DB::Process')->ProcessListGet(
727        UserID => 1,
728    );
729
730    my @Usage;
731
732    # search entity id in all parents
733    PARENT:
734    for my $ParentData ( @{$List} ) {
735        next PARENT if !$ParentData;
736        next PARENT if !$ParentData->{TransitionActions};
737        ENTITY:
738        for my $EntityID ( @{ $ParentData->{TransitionActions} } ) {
739            if ( $EntityID eq $Param{EntityID} ) {
740                push @Usage, $ParentData->{Name};
741                last ENTITY;
742            }
743        }
744    }
745
746    return \@Usage;
747}
748
7491;
750