1# --
2# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/
3# --
4# This software comes with ABSOLUTELY NO WARRANTY. For details, see
5# the enclosed file COPYING for license information (GPL). If you
6# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
7# --
8
9package Kernel::System::SysConfig::DB;
10
11use strict;
12use warnings;
13
14use MIME::Base64;
15use Time::HiRes();
16use utf8;
17
18use Kernel::System::VariableCheck qw( :all );
19
20our @ObjectDependencies = (
21    'Kernel::Config',
22    'Kernel::System::Cache',
23    'Kernel::System::DB',
24    'Kernel::System::Encode',
25    'Kernel::System::Log',
26    'Kernel::System::Main',
27    'Kernel::System::YAML',
28);
29
30=head1 NAME
31
32Kernel::System::SysConfig::DB - Functions to manage system configuration settings interactions with the database.
33
34=head1 PUBLIC INTERFACE
35
36=head2 new()
37
38Create an object. Do not use it directly, instead use:
39
40    use Kernel::System::ObjectManager;
41    local $Kernel::OM = Kernel::System::ObjectManager->new();
42    my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB');
43
44=cut
45
46sub new {
47    my ( $Type, %Param ) = @_;
48
49    # allocate new hash for object
50    my $Self = {};
51    bless( $Self, $Type );
52
53    $Self->{CacheTTL} = 24 * 3600 * 30;    # 1 month
54
55    return $Self;
56}
57
58=head2 DefaultSettingAdd()
59
60Add a new SysConfig default entry.
61
62    my $DefaultID = $SysConfigDBObject->DefaultSettingAdd(
63        Name                     => "ProductName",                 # (required)
64        Description              => "Setting description",         # (required)
65        Navigation               => "ASimple::Path::Structure",    # (required)
66        IsInvisible              => 1,                             # (optional) 1 or 0, default 0
67        IsReadonly               => 0,                             # (optional) 1 or 0, default 0
68        IsRequired               => 1,                             # (optional) 1 or 0, default 0
69        IsValid                  => 1,                             # (optional) 1 or 0, default 0
70        HasConfigLevel           => 200,                           # (optional) default 0
71        UserModificationPossible => 0,                             # (optional) 1 or 0, default 0
72        UserModificationActive   => 0,                             # (optional) 1 or 0, default 0
73        UserPreferencesGroup     => 'Some Group'                   # (optional)
74        XMLContentRaw            => $XMLString,                    # (required) the setting XML structure as it is on the config file
75        XMLContentParsed         => $XMLParsedToPerl,              # (required) the setting XML structure converted into a Perl structure
76        XMLFilename              => 'Framework.xml'                # (required) the name of the XML file
77        EffectiveValue           => $SettingEffectiveValue,        # (required) the value as will be stored in the Perl configuration file
78        ExclusiveLockExpiryTime  => '2017-02-01 12:23:13',         # (optional) If not provided, method will calculate it.
79        UserID                   => 123,
80        NoCleanup                => 0,                             # (optional) Default 0. If enabled, system WILL NOT DELETE CACHE. In this case, it must be done manually.
81                                                                   #    USE IT CAREFULLY.
82    );
83
84Returns:
85
86    my $DefaultID = 123;  # false in case of an error
87
88=cut
89
90sub DefaultSettingAdd {
91    my ( $Self, %Param ) = @_;
92
93    # Store params for further usage.
94    my %DefaultVersionParams = %Param;
95
96    for my $Key (
97        qw(Name Description Navigation XMLContentRaw XMLContentParsed XMLFilename EffectiveValue UserID)
98        )
99    {
100        if ( !defined $Param{$Key} ) {
101            $Kernel::OM->Get('Kernel::System::Log')->Log(
102                Priority => 'error',
103                Message  => "Need $Key!",
104            );
105
106            return;
107        }
108    }
109
110    my @DefaultSettings = $Self->DefaultSettingList(
111        IncludeInvisible => 1,
112    );
113
114    # Check duplicate name
115    my ($SettingData) = grep { $_->{Name} eq $Param{Name} } @DefaultSettings;
116    return if IsHashRefWithData($SettingData);
117
118    # Check config level.
119    $Param{HasConfigLevel} //= 0;
120
121    # The value should be a positive integer if defined.
122    if ( !IsPositiveInteger( $Param{HasConfigLevel} ) && $Param{HasConfigLevel} ne '0' ) {
123        $Kernel::OM->Get('Kernel::System::Log')->Log(
124            Priority => 'error',
125            Message  => "HasConfigLevel must be an integer!",
126        );
127        return;
128    }
129
130    # Check boolean parameters (0 as default value).
131    for my $Key (qw(IsInvisible IsReadonly IsRequired IsValid UserModificationPossible UserModificationActive)) {
132        $Param{$Key} = ( defined $Param{$Key} && $Param{$Key} ? 1 : 0 );
133    }
134
135    # Serialize data as string.
136    for my $Key (qw(XMLContentParsed EffectiveValue)) {
137        $Param{$Key} = $Kernel::OM->Get('Kernel::System::YAML')->Dump(
138            Data => $Param{$Key},
139        );
140        $DefaultVersionParams{$Key} = $Param{$Key};
141    }
142
143    # Set GuID.
144    $Param{ExclusiveLockGUID} //= 1;
145
146    # Set is dirty as enabled due it is a new setting.
147    $Param{IsDirty} = 1;
148
149    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
150
151    # Insert the default.
152    return if !$DBObject->Do(
153        SQL => '
154            INSERT INTO sysconfig_default
155                (name, description, navigation, is_invisible, is_readonly, is_required, is_valid, has_configlevel,
156                user_modification_possible, user_modification_active, user_preferences_group, xml_content_raw, xml_content_parsed, xml_filename, effective_value,
157                is_dirty, exclusive_lock_guid, create_time, create_by, change_time, change_by)
158            VALUES
159                (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, current_timestamp, ?, current_timestamp, ?)',
160        Bind => [
161            \$Param{Name},       \$Param{Description}, \$Param{Navigation}, \$Param{IsInvisible},
162            \$Param{IsReadonly}, \$Param{IsRequired},  \$Param{IsValid},    \$Param{HasConfigLevel},
163            \$Param{UserModificationPossible}, \$Param{UserModificationActive}, \$Param{UserPreferencesGroup},
164            \$Param{XMLContentRaw}, \$Param{XMLContentParsed}, \$Param{XMLFilename}, \$Param{EffectiveValue},
165            \$Param{IsDirty}, \$Param{ExclusiveLockGUID}, \$Param{UserID}, \$Param{UserID},
166        ],
167    );
168
169    # Get default ID.
170    $DBObject->Prepare(
171        SQL   => 'SELECT id FROM sysconfig_default WHERE name = ?',
172        Bind  => [ \$Param{Name} ],
173        Limit => 1,
174    );
175
176    my $DefaultID;
177
178    # Fetch the default setting ID.
179    ROW:
180    while ( my @Row = $DBObject->FetchrowArray() ) {
181        $DefaultID = $Row[0];
182        last ROW;
183    }
184
185    # Add default setting version.
186    my $DefaultVersionID = $Self->DefaultSettingVersionAdd(
187        DefaultID => $DefaultID,
188        %DefaultVersionParams,
189        NoVersionID => 1,
190    );
191
192    if ( !$Param{NoCleanup} ) {
193        my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
194        $CacheObject->Delete(
195            Type => 'SysConfigDefault',
196            Key  => 'DefaultSettingGet::' . $Param{Name},
197        );
198        $CacheObject->CleanUp(
199            Type => 'SysConfigDefaultListGet',
200        );
201        $CacheObject->Delete(
202            Type => 'SysConfigDefaultList',
203            Key  => 'DefaultSettingList',
204        );
205        $CacheObject->CleanUp(
206            Type => 'SysConfigNavigation',
207        );
208        $CacheObject->CleanUp(
209            Type => 'SysConfigEntities',
210        );
211        $CacheObject->CleanUp(
212            Type => 'SysConfigIsDirty',
213        );
214    }
215
216    # Return if not version inserted.
217    return if !$DefaultVersionID;
218
219    return $DefaultID;
220}
221
222=head2 DefaultSettingBulkAdd()
223
224Add new SysConfig default entries.
225
226    my $Success = $SysConfigDBObject->DefaultSettingBulkAdd(
227        Settings => {                                                 # (required) Hash of settings
228            "ACL::CacheTTL" => {
229                "EffectiveValue" => "--- '3600'\n",
230                "XMLContentParsed" => {
231                    "Description" => [
232                        {
233                            "Content" => "Cache time in ...",
234                            "Translatable" => 1
235                        },
236                    ],
237                    "Name" => "ACL::CacheTTL",
238                    "Navigation" => [
239                        {
240                            "Content" => "Core::Ticket::ACL"
241                        },
242                    ],
243                    "Required" => 1,
244                    "Valid" => 1,
245                    "Value" => [
246                        {
247                            "Item" => [
248                                {
249                                    "Content" => 3600,
250                                    "ValueRegex" => "^\\d+\$",
251                                    "ValueType" => "String"
252                                },
253                            ],
254                        },
255                    ],
256                },
257                "XMLContentParsedYAML" => "---\nDescription:\n- Content: Cache...",
258                "XMLContentRaw" => "<Setting Name=\"ACL::CacheTTL\" Required=\"1\" ...",
259                "XMLFilename" => "Ticket.xml"
260            },
261            ...
262        },
263        SettingList => [                                                # list of current settings in DB
264            {
265                DefaultID         => '123',
266                Name              => 'SettingName1',
267                IsDirty           => 1,
268                ExclusiveLockGUID => 0,
269            },
270            # ...
271        ],
272        UserID => 1,                                                    # (required) UserID
273    );
274
275=cut
276
277sub DefaultSettingBulkAdd {
278    my ( $Self, %Param ) = @_;
279
280    for my $Needed (qw(Settings UserID)) {
281        if ( !$Param{$Needed} ) {
282            $Kernel::OM->Get('Kernel::System::Log')->Log(
283                Priority => 'error',
284                Message  => "Need $Needed!",
285            );
286            return;
287        }
288    }
289
290    if ( ref $Param{Settings} ne 'HASH' ) {
291        $Kernel::OM->Get('Kernel::System::Log')->Log(
292            Priority => 'error',
293            Message  => "Settings must be a hash!"
294        );
295        return;
296    }
297
298    $Param{SettingList} //= [];
299    if ( ref $Param{SettingList} ne 'ARRAY' ) {
300        $Kernel::OM->Get('Kernel::System::Log')->Log(
301            Priority => 'error',
302            Message  => "SettingList must be an array",
303        );
304    }
305
306    my @Data;
307
308    SETTINGNAME:
309    for my $SettingName ( sort keys %{ $Param{Settings} } ) {
310
311        my ($BasicData) = grep { $_->{Name} eq $SettingName } @{ $Param{SettingList} };
312
313        if ($BasicData) {
314            $Kernel::OM->Get('Kernel::System::Log')->Log(
315                Priority => 'error',
316                Message  => "$SettingName should not exist!"
317            );
318            next SETTINGNAME;
319        }
320
321        # Gather data for all records.
322        push @Data, [
323            $SettingName,
324            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{Description}->[0]->{Content} || '',
325            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{Navigation}->[0]->{Content}  || '',
326            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{Invisible}                   || 0,
327            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{ReadOnly}                    || 0,
328            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{Required}                    || 0,
329            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{Valid}                       || 0,
330            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{ConfigLevel}                 || 100,
331            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{UserModificationPossible}    || 0,
332            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{UserModificationActive}      || 0,
333            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{UserPreferencesGroup},
334            $Param{Settings}->{$SettingName}->{XMLContentRaw},
335            $Param{Settings}->{$SettingName}->{XMLContentParsedYAML},
336            $Param{Settings}->{$SettingName}->{XMLFilename},
337            $Param{Settings}->{$SettingName}->{EffectiveValue},
338            1,
339            1,
340            'current_timestamp',
341            $Param{UserID},
342            'current_timestamp',
343            $Param{UserID},
344        ];
345    }
346
347    return if !$Self->_BulkInsert(
348        Table   => 'sysconfig_default',
349        Columns => [
350            'name',
351            'description',
352            'navigation',
353            'is_invisible',
354            'is_readonly',
355            'is_required',
356            'is_valid',
357            'has_configlevel',
358            'user_modification_possible',
359            'user_modification_active',
360            'user_preferences_group',
361            'xml_content_raw',
362            'xml_content_parsed',
363            'xml_filename',
364            'effective_value',
365            'is_dirty',
366            'exclusive_lock_guid',
367            'create_time',
368            'create_by',
369            'change_time',
370            'change_by'
371        ],
372        Data => \@Data,
373    );
374
375    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
376
377    $CacheObject->CleanUp(
378        Type => 'SysConfigDefaultListGet',
379    );
380    $CacheObject->Delete(
381        Type => 'SysConfigDefaultList',
382        Key  => 'DefaultSettingList',
383    );
384    $CacheObject->CleanUp(
385        Type => 'SysConfigNavigation',
386    );
387    $CacheObject->CleanUp(
388        Type => 'SysConfigEntities',
389    );
390    $CacheObject->CleanUp(
391        Type => 'SysConfigIsDirty',
392    );
393
394    return 1;
395}
396
397=head2 DefaultSettingVersionBulkAdd()
398
399    my $Success = $SysConfigDBObject->DefaultSettingVersionBulkAdd(
400        Settings => {                                                 # (required) Hash of settings
401            "ACL::CacheTTL" => {
402                "EffectiveValue" => "--- '3600'\n",
403                "XMLContentParsed" => {
404                    "Description" => [
405                        {
406                            "Content" => "Cache time in ...",
407                            "Translatable" => 1
408                        },
409                    ],
410                    "Name" => "ACL::CacheTTL",
411                    "Navigation" => [
412                        {
413                            "Content" => "Core::Ticket::ACL"
414                        },
415                    ],
416                    "Required" => 1,
417                    "Valid" => 1,
418                    "Value" => [
419                        {
420                            "Item" => [
421                                {
422                                    "Content" => 3600,
423                                    "ValueRegex" => "^\\d+\$",
424                                    "ValueType" => "String"
425                                },
426                            ],
427                        },
428                    ],
429                },
430                "XMLContentParsedYAML" => "---\nDescription:\n- Content: Cache...",
431                "XMLContentRaw" => "<Setting Name=\"ACL::CacheTTL\" Required=\"1\" ...",
432                "XMLFilename" => "Ticket.xml"
433            },
434            ...
435        },
436        SettingList => [                                                # list of current settings in DB
437            {
438                DefaultID         => '123',
439                Name              => 'SettingName1',
440                IsDirty           => 1,
441                ExclusiveLockGUID => 0,
442            },
443            # ...
444        ],
445        UserID => 1,                                                    # (required) UserID
446    );
447
448=cut
449
450sub DefaultSettingVersionBulkAdd {
451    my ( $Self, %Param ) = @_;
452
453    # Check needed stuff.
454    for my $Needed (qw(Settings SettingList UserID)) {
455        if ( !$Param{$Needed} ) {
456            $Kernel::OM->Get('Kernel::System::Log')->Log(
457                Priority => 'error',
458                Message  => "Need $Needed!",
459            );
460            return;
461        }
462    }
463
464    if ( ref $Param{Settings} ne 'HASH' ) {
465        $Kernel::OM->Get('Kernel::System::Log')->Log(
466            Priority => 'error',
467            Message  => "Settings must be a hash!"
468        );
469        return;
470    }
471
472    my @Data;
473
474    SETTINGNAME:
475    for my $SettingName ( sort keys %{ $Param{Settings} } ) {
476
477        my ($BasicData) = grep { $_->{Name} eq $SettingName } @{ $Param{SettingList} };
478
479        if ( !$BasicData || !$BasicData->{DefaultID} ) {
480            $Kernel::OM->Get('Kernel::System::Log')->Log(
481                Priority => 'error',
482                Message  => "DefaultID for $SettingName couldn't be determined, skipped!"
483            );
484            next SETTINGNAME;
485        }
486
487        # Gather data for all records.
488        push @Data, [
489            $BasicData->{DefaultID},
490            $SettingName,
491            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{Description}->[0]->{Content} || '',
492            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{Navigation}->[0]->{Content}  || '',
493            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{Invisible}                   || 0,
494            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{ReadOnly}                    || 0,
495            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{Required}                    || 0,
496            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{Valid}                       || 0,
497            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{ConfigLevel}                 || 100,
498            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{UserModificationPossible}    || 0,
499            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{UserModificationActive}      || 0,
500            $Param{Settings}->{$SettingName}->{XMLContentParsed}->{UserPreferencesGroup},
501            $Param{Settings}->{$SettingName}->{XMLContentRaw},
502            $Param{Settings}->{$SettingName}->{XMLContentParsedYAML},
503            $Param{Settings}->{$SettingName}->{XMLFilename},
504            $Param{Settings}->{$SettingName}->{EffectiveValue},
505            'current_timestamp',
506            $Param{UserID},
507            'current_timestamp',
508            $Param{UserID},
509        ];
510    }
511
512    return if !$Self->_BulkInsert(
513        Table   => 'sysconfig_default_version',
514        Columns => [
515            'sysconfig_default_id',
516            'name',
517            'description',
518            'navigation',
519            'is_invisible',
520            'is_readonly',
521            'is_required',
522            'is_valid',
523            'has_configlevel',
524            'user_modification_possible',
525            'user_modification_active',
526            'user_preferences_group',
527            'xml_content_raw',
528            'xml_content_parsed',
529            'xml_filename',
530            'effective_value',
531            'create_time',
532            'create_by',
533            'change_time',
534            'change_by',
535        ],
536        Data => \@Data,
537    );
538
539    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
540
541    $CacheObject->CleanUp(
542        Type => 'SysConfigDefaultVersion',
543    );
544    $CacheObject->CleanUp(
545        Type => 'SysConfigDefaultVersionList',
546    );
547
548    return 1;
549}
550
551=head2 DefaultSettingGet()
552
553Get SysConfig default entry.
554
555    my %DefaultSetting = $SysConfigDBObject->DefaultSettingGet(
556        Name        => "TheName", # (required) Setting name - prefered parameter.
557                                  # or
558        DefaultID   => 4,         # (required) DefaultID. Slightly slower execution.
559        NoCache     => 0,         # (optional) Default 0. If 1, cache will not be created.
560    );
561
562Returns:
563
564    %DefaultSetting = (
565        DefaultID                => "123",
566        Name                     => "ProductName",
567        Description              => "Defines the name of the application ...",
568        Navigation               => "ASimple::Path::Structure",
569        IsInvisible              => 1,         # 1 or 0
570        IsReadonly               => 0,         # 1 or 0
571        IsRequired               => 1,         # 1 or 0
572        IsValid                  => 1,         # 1 or 0
573        HasConfigLevel           => 200,
574        UserModificationPossible => 0,         # 1 or 0
575        UserModificationActive   => 0,         # 1 or 0
576        UserPreferencesGroup     => 'Some Group',
577        XMLContentRaw            => "The XML structure as it is on the config file",
578        XMLContentParsed         => "XML parsed to Perl",
579        XMLFilename              => "Framework.xml",
580        EffectiveValue           => "Product 6",
581        IsDirty                  => 1,         # 1 or 0
582        ExclusiveLockGUID        => 'A32CHARACTERLONGSTRINGFORLOCKING',
583        ExclusiveLockUserID      => 1,
584        ExclusiveLockExpiryTime  => '2016-05-29 11:09:04',
585        CreateTime               => "2016-05-29 11:04:04",
586        CreateBy                 => 1,
587        ChangeTime               => "2016-05-29 11:04:04",
588        ChangeBy                 => 1,
589        SettingUID               => 'Default12320160529110404',
590    );
591
592=cut
593
594sub DefaultSettingGet {
595    my ( $Self, %Param ) = @_;
596
597    if ( !$Param{DefaultID} && !$Param{Name} ) {
598        $Kernel::OM->Get('Kernel::System::Log')->Log(
599            Priority => 'error',
600            Message  => 'Need DefaultID or Name!',
601        );
602        return;
603    }
604
605    my $SettingName = $Param{Name};
606
607    if ( !$SettingName ) {
608        my %SettingData = $Self->DefaultSettingLookup(
609            DefaultID => $Param{DefaultID},
610        );
611
612        if ( !%SettingData || !$SettingData{Name} ) {
613            $Kernel::OM->Get('Kernel::System::Log')->Log(
614                Priority => 'error',
615                Message  => "Setting with DefaultID = $Param{DefaultID} not found!",
616            );
617
618            return;
619        }
620
621        $SettingName = $SettingData{Name};
622    }
623
624    my $CacheType = "SysConfigDefault";
625    my $CacheKey  = "DefaultSettingGet::$SettingName";
626
627    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
628
629    # Return cache.
630    my $Cache = $CacheObject->Get(
631        Type => $CacheType,
632        Key  => $CacheKey,
633    );
634
635    return %{$Cache} if ref $Cache eq 'HASH';
636
637    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
638
639    # Get default from database.
640    return if !$DBObject->Prepare(
641        SQL => '
642            SELECT id, name, description, navigation, is_invisible, is_readonly, is_required, is_valid, has_configlevel,
643                user_modification_possible, user_modification_active, user_preferences_group, xml_content_raw,
644                xml_content_parsed, xml_filename, effective_value, is_dirty, exclusive_lock_guid, exclusive_lock_user_id,
645                exclusive_lock_expiry_time, create_time, create_by, change_time, change_by
646
647            FROM sysconfig_default
648            WHERE name = ?',
649        Bind => [ \$SettingName ],
650    );
651
652    my $YAMLObject = $Kernel::OM->Get('Kernel::System::YAML');
653
654    my %DefaultSetting;
655    while ( my @Data = $DBObject->FetchrowArray() ) {
656
657        # De-serialize default data.
658        my $XMLContentParsed = $YAMLObject->Load( Data => $Data[13] );
659        my $EffectiveValue   = $YAMLObject->Load( Data => $Data[15] );
660
661        my $TimeStamp = $Data[22];
662        $TimeStamp =~ s{:|-|[ ]}{}gmsx;
663
664        %DefaultSetting = (
665            DefaultID                => $Data[0],
666            Name                     => $Data[1],
667            Description              => $Data[2],
668            Navigation               => $Data[3],
669            IsInvisible              => $Data[4],
670            IsReadonly               => $Data[5],
671            IsRequired               => $Data[6],
672            IsValid                  => $Data[7],
673            HasConfigLevel           => $Data[8],
674            UserModificationPossible => $Data[9],
675            UserModificationActive   => $Data[10],
676            UserPreferencesGroup     => $Data[11] || '',
677            XMLContentRaw            => $Data[12],
678            XMLContentParsed         => $XMLContentParsed,
679            XMLFilename              => $Data[14],
680            EffectiveValue           => $EffectiveValue,
681            IsDirty                  => $Data[16] ? 1 : 0,
682            ExclusiveLockGUID        => $Data[17],
683            ExclusiveLockUserID      => $Data[18],
684            ExclusiveLockExpiryTime  => $Data[19],
685            CreateTime               => $Data[20],
686            CreateBy                 => $Data[21],
687            ChangeTime               => $Data[22],
688            ChangeBy                 => $Data[23],
689            SettingUID               => "Default$Data[0]$TimeStamp",
690        );
691    }
692
693    if ( !$Param{NoCache} ) {
694        $CacheObject->Set(
695            Type  => $CacheType,
696            Key   => $CacheKey,
697            Value => \%DefaultSetting,
698            TTL   => $Self->{CacheTTL},
699        );
700    }
701
702    return %DefaultSetting;
703}
704
705=head2 DefaultSettingLookup()
706
707Default setting lookup.
708
709    my %Result = $SysConfigDBObject->DefaultSettingLookup(
710        Name        => "TheName", # (required)
711                                  # or
712        DefaultID   => 4,         # (required)
713    );
714
715Returns:
716
717    %Result = (
718        DefaultID      => 4,
719        Name           => 'TheName',
720    );
721
722=cut
723
724sub DefaultSettingLookup {
725    my ( $Self, %Param ) = @_;
726
727    # Check needed stuff.
728    if ( !$Param{Name} && !$Param{DefaultID} ) {
729        $Kernel::OM->Get('Kernel::System::Log')->Log(
730            Priority => 'error',
731            Message  => "Need Name or DefaultID!",
732        );
733        return;
734    }
735
736    my @Bind;
737
738    my $SQL = '
739        SELECT id, name
740        FROM sysconfig_default
741        WHERE
742    ';
743
744    if ( $Param{Name} ) {
745        $SQL .= 'name = ? ';
746        push @Bind, \$Param{Name};
747    }
748    else {
749        $SQL .= "id = ? ";
750        push @Bind, \$Param{DefaultID};
751    }
752
753    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
754
755    # db query
756    return if !$DBObject->Prepare(
757        SQL   => $SQL,
758        Bind  => \@Bind,
759        Limit => 1,
760    );
761
762    my %Result;
763
764    ROW:
765    while ( my @Row = $DBObject->FetchrowArray() ) {
766        $Result{DefaultID} = $Row[0];
767        $Result{Name}      = $Row[1];
768        last ROW;
769    }
770
771    return %Result;
772}
773
774=head2 DefaultSettingDelete()
775
776Delete a default setting from the database.
777
778    my $Success = $SysConfigDBObject->DefaultSettingDelete(
779        DefaultID => 123,
780    );
781
782    my $Success = $SysConfigDBObject->DefaultSettingDelete(
783        Name => 'Name',
784    );
785
786Returns:
787
788    $Success = 1;       # or false in case of an error
789
790=cut
791
792sub DefaultSettingDelete {
793    my ( $Self, %Param ) = @_;
794
795    if ( !$Param{DefaultID} && !$Param{Name} ) {
796        $Kernel::OM->Get('Kernel::System::Log')->Log(
797            Priority => 'error',
798            Message  => 'Need DefaultID or Name!',
799        );
800        return;
801    }
802
803    my %DefaultSetting = $Self->DefaultSettingGet(
804        DefaultID => $Param{DefaultID} || undef,
805        Name      => $Param{Name}      || undef,
806    );
807    return 1 if !IsHashRefWithData( \%DefaultSetting );
808
809    my $DefaultID = $DefaultSetting{DefaultID};
810
811    # Delete the entries in SysConfig default version.
812    my $DeleteDefaultVersion = $Self->DefaultSettingVersionDelete(
813        DefaultID => $DefaultID,
814    );
815
816    # Return if not version deleted.
817    return if !$DeleteDefaultVersion;
818
819    # Delete default from the list.
820    return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
821        SQL  => 'DELETE FROM sysconfig_default WHERE id = ?',
822        Bind => [ \$DefaultID ],
823    );
824
825    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
826
827    $CacheObject->Delete(
828        Type => 'SysConfigDefault',
829        Key  => 'DefaultSettingGet::' . $DefaultSetting{Name},
830    );
831    $CacheObject->CleanUp(
832        Type => 'SysConfigDefaultListGet',
833    );
834    $CacheObject->Delete(
835        Type => 'SysConfigDefaultList',
836        Key  => 'DefaultSettingList',
837    );
838    $CacheObject->CleanUp(
839        Type => 'SysConfigNavigation',
840    );
841    $CacheObject->CleanUp(
842        Type => 'SysConfigEntities',
843    );
844    $CacheObject->CleanUp(
845        Type => 'SysConfigIsDirty',
846    );
847
848    # Clean cache for setting translations.
849    my %Languages = %{ $Kernel::OM->Get('Kernel::Config')->Get('DefaultUsedLanguages') };
850    for my $Language ( sort keys %Languages ) {
851        $CacheObject->Delete(
852            Type => 'SysConfig',
853            Key  => "SettingTranslatedGet::$Language" . "::$DefaultSetting{Name}",
854        );
855        $CacheObject->Delete(
856            Type => 'SysConfig',
857            Key  => "ConfigurationTranslatedGet::$Language",
858        );
859    }
860
861    return 1;
862}
863
864=head2 DefaultSettingUpdate()
865
866Update SysConfig default entry.
867
868    my $Success = $SysConfigDBObject->DefaultSettingUpdate(
869        DefaultID                => 123,
870        Name                     => "ProductName",
871        Description              => "Defines the name of the application ...",
872        Navigation               => "ASimple::Path::Structure",
873        IsInvisible              => 1,             # 1 or 0, optional, default 0
874        IsReadonly               => 0,             # 1 or 0, optional, default 0
875        IsRequired               => 1,             # 1 or 0, optional, default 0
876        IsValid                  => 1,             # 1 or 0, optional, default 0
877        HasConfigLevel           => 200,           # optional, default 0
878        UserModificationPossible => 0,             # 1 or 0, optional, default 0
879        UserModificationActive   => 0,             # 1 or 0, optional, default 0
880        UserPreferencesGroup     => 'Some Group',
881        UserPreferencesGroup     => 'Advanced',    # optional
882        XMLContentRaw            => $XMLString,    # the XML structure as it is on the config file
883        XMLContentParsed         => $XMLParsedToPerl,
884        XMLFilename              => 'Framework.xml',
885        ExclusiveLockGUID        => 1,
886        EffectiveValue           => $SettingEffectiveValue,
887        UserID                   => 1,
888    );
889
890Returns:
891
892    $Success = 1;   # or false in case of an error.
893
894=cut
895
896sub DefaultSettingUpdate {
897    my ( $Self, %Param ) = @_;
898
899    # Store params for further usage.
900    my %DefaultVersionParams = %Param;
901
902    for my $Key (
903        qw(
904        DefaultID Name Description Navigation XMLContentRaw XMLContentParsed XMLFilename
905        ExclusiveLockGUID EffectiveValue UserID
906        )
907        )
908    {
909        if ( !defined $Param{$Key} ) {
910            $Kernel::OM->Get('Kernel::System::Log')->Log(
911                Priority => 'error',
912                Message  => "Need $Key!",
913            );
914
915            return;
916        }
917    }
918
919    # Get default setting
920    my %DefaultSetting = $Self->DefaultSettingGet(
921        DefaultID => $Param{DefaultID},
922    );
923
924    # Check if setting exists.
925    return if !IsHashRefWithData( \%DefaultSetting );
926
927    # Don't allow name changes, due this is consider a NEW setting.
928    return if $DefaultSetting{Name} ne $Param{Name};
929
930    # Check if the setting is locked by the current user.
931    return if !$Self->DefaultSettingIsLockedByUser(
932        %Param,
933        ExclusiveLockUserID => $Param{UserID},
934    );
935
936    # Check config level, set 0 as default value.
937    $Param{HasConfigLevel} //= 0;
938
939    if ( !IsPositiveInteger( $Param{HasConfigLevel} ) && $Param{HasConfigLevel} ne '0' ) {
940        $Kernel::OM->Get('Kernel::System::Log')->Log(
941            Priority => 'error',
942            Message  => "HasConfigLevel must be an integer!",
943        );
944
945        return;
946    }
947
948    # Check boolean parameters, set 0 as default value.
949    for my $Key (qw(IsInvisible IsReadonly IsRequired IsValid UserModificationPossible UserModificationActive)) {
950        $Param{$Key} = ( defined $Param{$Key} && $Param{$Key} ? 1 : 0 );
951    }
952
953    # Check if we really need to update.
954    my $IsDifferent = 0;
955    for my $Key (
956        qw(
957        DefaultID Description Navigation IsInvisible HasConfigLevel XMLContentRaw
958        XMLContentParsed XMLFilename ExclusiveLockGUID EffectiveValue HasConfigLevel IsInvisible
959        IsReadonly IsRequired IsValid UserModificationPossible UserModificationActive UserPreferencesGroup
960        )
961        )
962    {
963        my $DataIsDifferent = DataIsDifferent(
964            Data1 => $Param{$Key},
965            Data2 => $DefaultSetting{$Key},
966        );
967        if ($DataIsDifferent) {
968            $IsDifferent = 1;
969        }
970    }
971
972    return 1 if !$IsDifferent;
973
974    # Serialize data as string.
975    for my $Key (qw(XMLContentParsed EffectiveValue)) {
976        $Param{$Key} = $Kernel::OM->Get('Kernel::System::YAML')->Dump(
977            Data => $Param{$Key},
978        );
979        $DefaultVersionParams{$Key} = $Param{$Key};
980    }
981
982    # Set is dirty value.
983    $Param{IsDirty} = 1;
984
985    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
986
987    # Insert the default.
988    return if !$DBObject->Do(
989        SQL => '
990            UPDATE sysconfig_default
991            SET description = ?, navigation = ?, is_invisible = ?, is_readonly = ?,
992                is_required = ?, is_valid = ?, has_configlevel = ?, user_modification_possible = ?, user_modification_active = ?,
993                user_preferences_group = ?, xml_content_raw = ?, xml_content_parsed = ?, xml_filename =?, effective_value = ?,
994                is_dirty = ?, change_time = current_timestamp, change_by = ?
995            WHERE id = ?',
996        Bind => [
997            \$Param{Description}, \$Param{Navigation}, \$Param{IsInvisible},    \$Param{IsReadonly},
998            \$Param{IsRequired},  \$Param{IsValid},    \$Param{HasConfigLevel}, \$Param{UserModificationPossible},
999            \$Param{UserModificationActive}, \$Param{UserPreferencesGroup}, \$Param{XMLContentRaw},
1000            \$Param{XMLContentParsed},
1001            \$Param{XMLFilename}, \$Param{EffectiveValue}, \$Param{IsDirty}, \$Param{UserID}, \$Param{DefaultID},
1002        ],
1003    );
1004
1005    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
1006
1007    $CacheObject->Delete(
1008        Type => 'SysConfigDefault',
1009        Key  => 'DefaultSettingGet::' . $DefaultSetting{Name},
1010    );
1011    $CacheObject->CleanUp(
1012        Type => 'SysConfigDefaultListGet',
1013    );
1014    $CacheObject->Delete(
1015        Type => 'SysConfigDefaultList',
1016        Key  => 'DefaultSettingList',
1017    );
1018    $CacheObject->CleanUp(
1019        Type => 'SysConfigNavigation',
1020    );
1021    $CacheObject->CleanUp(
1022        Type => 'SysConfigEntities',
1023    );
1024    $CacheObject->CleanUp(
1025        Type => 'SysConfigIsDirty',
1026    );
1027
1028    # Clean cache for setting translations.
1029    my %Languages = %{ $Kernel::OM->Get('Kernel::Config')->Get('DefaultUsedLanguages') };
1030    for my $Language ( sort keys %Languages ) {
1031        $CacheObject->Delete(
1032            Type => 'SysConfig',
1033            Key  => "SettingTranslatedGet::$Language" . "::$DefaultSetting{Name}",
1034        );
1035        $CacheObject->Delete(
1036            Type => 'SysConfig',
1037            Key  => "ConfigurationTranslatedGet::$Language",
1038        );
1039    }
1040
1041    # Add default setting version.
1042    my $DefaultVersionID = $Self->DefaultSettingVersionAdd(
1043        DefaultID => $Param{DefaultID},
1044        %DefaultVersionParams,
1045        NoVersionID => 1,
1046    );
1047
1048    # Return if not version inserted.
1049    return if !$DefaultVersionID;
1050
1051    # Unlock setting
1052    my $IsUnlock = $Self->DefaultSettingUnlock(
1053        DefaultID => $Param{DefaultID},
1054    );
1055    if ( !$IsUnlock ) {
1056        $Kernel::OM->Get('Kernel::System::Log')->Log(
1057            Priority => 'error',
1058            Message  => "Default setting with ID: $Param{DefaultID} was not possible to unlock!",
1059        );
1060        return;
1061    }
1062
1063    return 1;
1064}
1065
1066=head2 DefaultSettingSearch()
1067
1068Search for settings which contains given term(Search) in the xml_content_raw column.
1069
1070    my @Result = $SysConfigDBObject->DefaultSettingSearch(
1071        Search          => 'Entity',                              # Search term
1072        SearchType      => 'XMLContent',                          # XMLContent or Metadata
1073        CategoryFiles   => ['Framework.xml', 'Ticket.xml', ],     # (optional)
1074        Valid           => 0,                                     # (optional) By default, system returns all Settings (valid and invalid)
1075                                                                  #   if set to 1, search only for valid,
1076                                                                  #   if set to 0, search also for invalid.
1077        IncludeInvisible => 0,                                    # (optional) Default 0
1078    );
1079
1080or
1081
1082    my @Result = $SysConfigDBObject->DefaultSettingSearch(
1083        Search         => ['Framework.xml' 'Ticket,xml'],
1084        SearchType     => 'Filename',
1085        Valid          => 1,
1086    );
1087
1088Returns:
1089
1090    @Result = (
1091        'ACL::CacheTTL',
1092        'ACLKeysLevel1Change',
1093        ...
1094    );
1095
1096=cut
1097
1098sub DefaultSettingSearch {
1099    my ( $Self, %Param ) = @_;
1100
1101    if ( !$Param{Search} && !$Param{CategoryFiles} ) {
1102        $Kernel::OM->Get('Kernel::System::Log')->Log(
1103            Priority => 'error',
1104            Message  => "Need Search or CategoryFiles!",
1105        );
1106        return;
1107    }
1108
1109    if ( $Param{CategoryFiles} && !IsArrayRefWithData( $Param{CategoryFiles} ) ) {
1110        $Kernel::OM->Get('Kernel::System::Log')->Log(
1111            Priority => 'error',
1112            Message  => "CategoryFiles is invalid!",
1113        );
1114        return;
1115    }
1116
1117    $Param{SearchType}       //= 'XMLContent';
1118    $Param{IncludeInvisible} //= 0;
1119
1120    my $Valid = 0;
1121    if ( defined $Param{Valid} ) {
1122        $Valid = $Param{Valid} ? 1 : 0;
1123    }
1124
1125    my @Bind = ();
1126
1127    my $SQL = '
1128        SELECT name
1129        FROM sysconfig_default
1130        WHERE 1 = 1';
1131
1132    if ($Valid) {
1133        $SQL .= '
1134        AND is_valid = ?';
1135        push @Bind, \$Valid;
1136    }
1137
1138    if ( !$Param{IncludeInvisible} ) {
1139        $SQL .= '
1140        AND is_invisible = ?';
1141        push @Bind, \0;
1142    }
1143
1144    my $Column = 'xml_content_raw';
1145
1146    if ( lc $Param{SearchType} eq 'metadata' ) {
1147        $Column = [qw(name description navigation)];
1148    }
1149
1150    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
1151
1152    if ( $Param{Search} ) {
1153        my %QueryCondition = $DBObject->QueryCondition(
1154            Key          => $Column,
1155            Value        => $Param{Search},
1156            SearchPrefix => "*",
1157            SearchSuffix => "*",
1158            BindMode     => 1,
1159        );
1160
1161        $SQL .= ' AND ' . $QueryCondition{SQL};
1162        push @Bind, @{ $QueryCondition{Values} };
1163    }
1164
1165    if ( $Param{CategoryFiles} ) {
1166
1167        @{ $Param{CategoryFiles} } = map {"($_)"} @{ $Param{CategoryFiles} };
1168        $Param{CategoryFiles} = join 'OR', @{ $Param{CategoryFiles} };
1169
1170        my %QueryCondition = $DBObject->QueryCondition(
1171            Key          => 'xml_filename',
1172            Value        => $Param{CategoryFiles},
1173            SearchPrefix => "",
1174            SearchSuffix => "",
1175            BindMode     => 1,
1176        );
1177
1178        $SQL .= ' AND ( ' . $QueryCondition{SQL} . ' )';
1179        push @Bind, @{ $QueryCondition{Values} };
1180
1181    }
1182
1183    $SQL .= '
1184        ORDER BY name asc';
1185
1186    # db query
1187    return if !$DBObject->Prepare(
1188        SQL  => $SQL,
1189        Bind => \@Bind,
1190    );
1191
1192    my @Result;
1193
1194    ROW:
1195    while ( my @Row = $DBObject->FetchrowArray() ) {
1196        push @Result, $Row[0];
1197    }
1198
1199    return @Result;
1200}
1201
1202=head2 DefaultSettingListGet()
1203
1204Get default setting list with complete data.
1205
1206    my @List = $SysConfigDBObject->DefaultSettingListGet(
1207        IsInvisible              => 1,                                  # 1 or 0
1208        IsReadonly               => 0,                                  # 1 or 0
1209        IsRequired               => 1,                                  # 1 or 0
1210        IsValid                  => 1,                                  # 1 or 0
1211        IsDirty                  => 1,                                  # 1 or 0
1212        HasConfigLevel           => 0,                                  # 1 or 0
1213        UserModificationPossible => 0,                                  # 1 or 0
1214        UserModificationActive   => 0,                                  # 1 or 0
1215        UserPreferencesGroup     => 'Some Group',
1216        Navigation               => 'ASimple::Path::Structure',
1217        Locked                   => 1, # check for locked settings
1218        Category                 => 'OTRS',                             # optional (requires CategoryFiles)
1219        CategoryFiles            => ['Framework.xml', 'Ticket.xml', ],  # optional (requires Category)
1220        NoCache                  => 0,                                  # (optional) Default 0. If set, system will not generate cache.
1221    );
1222
1223Returns:
1224
1225    @List = (
1226        {
1227            DefaultID                => 123,
1228            Name                     => "ProductName",
1229            Description              => "Defines the name of the application ...",
1230            Navigation               => "ASimple::Path::Structure",
1231            IsInvisible              => 1,
1232            IsReadonly               => 0,
1233            IsRequired               => 1,
1234            IsValid                  => 1,
1235            HasConfigLevel           => 200,
1236            UserModificationPossible => 0,          # 1 or 0
1237            UserModificationActive   => 0,          # 1 or 0
1238            UserPreferencesGroup     => 'Advanced', # optional
1239            XMLContentRaw            => "The XML structure as it is on the config file",
1240            XMLContentParsed         => "XML parsed to Perl",
1241            XMLFilename              => "Framework.xml",
1242            EffectiveValue           => "Product 6",
1243            IsDirty                  => 1,       # 1 or 0
1244            ExclusiveLockGUID        => 'A32CHARACTERLONGSTRINGFORLOCKING',
1245            ExclusiveLockUserID      => 1,
1246            ExclusiveLockExpiryTime  => '2016-05-29 11:09:04',
1247            CreateTime               => "2016-05-29 11:04:04",
1248            CreateBy                 => 1,
1249            ChangeTime               => "2016-05-29 11:04:04",
1250            ChangeBy                 => 1,
1251            SettingUID               => 'Default4717141789',
1252        },
1253        {
1254            DefaultID => 321,
1255            Name      => 'FieldName',
1256            # ...
1257            ChangeTime => '2011-01-01 01:01:01',
1258            ChangeBy                 => 1,
1259            SettingUID               => 'Default4717141781',
1260        },
1261        # ...
1262    );
1263
1264=cut
1265
1266sub DefaultSettingListGet {
1267    my ( $Self, %Param ) = @_;
1268
1269    if ( $Param{Category} ) {
1270        if ( !IsArrayRefWithData( $Param{CategoryFiles} ) ) {
1271            $Kernel::OM->Get('Kernel::System::Log')->Log(
1272                Priority => 'error',
1273                Message  => "CategoryFiles are invalid!",
1274            );
1275            return;
1276        }
1277    }
1278    if ( $Param{CategoryFiles} && !$Param{Category} ) {
1279        $Kernel::OM->Get('Kernel::System::Log')->Log(
1280            Priority => 'error',
1281            Message  => "Need Category!",
1282        );
1283    }
1284
1285    # Define SQL filters to be used in the queries.
1286    my %FieldFilters = (
1287        IsInvisible              => 'is_invisible',
1288        IsReadonly               => 'is_readonly',
1289        IsRequired               => 'is_required',
1290        IsValid                  => 'is_valid',
1291        IsDirty                  => 'is_dirty',
1292        HasConfigLevel           => 'has_configlevel',
1293        UserModificationPossible => 'user_modification_possible',
1294        UserModificationActive   => 'user_modification_active',
1295        UserPreferencesGroup     => 'user_preferences_group',
1296        Navigation               => 'navigation',
1297    );
1298
1299    my @Filters;
1300    my @Bind;
1301
1302    my $CacheType = 'SysConfigDefaultListGet';
1303    my $CacheKey  = 'DefaultSettingListGet';     # this cache key gets more elements
1304
1305    # Check params have a default value.
1306    for my $Key ( sort keys %FieldFilters ) {
1307        if ( defined $Param{$Key} ) {
1308            push @Filters, " $FieldFilters{$Key} = ? ";
1309            push @Bind,    \$Param{$Key};
1310
1311            $CacheKey .= "::$FieldFilters{$Key} = $Param{$Key}";
1312        }
1313    }
1314
1315    # Check for locked settings.
1316    if ( $Param{Locked} ) {
1317        push @Filters, " exclusive_lock_guid != '0' ";
1318        $CacheKey .= "::exclusive_lock_guid != '0'";
1319    }
1320
1321    # Check for categories.
1322    if ( $Param{Category} ) {
1323        $CacheKey .= "::Category=$Param{Category}";
1324    }
1325
1326    my $SQLFilter = '';
1327
1328    # Loop over filters and set them on SQL and cache key.
1329    if ( IsArrayRefWithData( \@Filters ) ) {
1330        $SQLFilter = ' WHERE ' . join ' AND ', @Filters;
1331    }
1332
1333    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
1334
1335    if ( $Param{Category} ) {
1336        @{ $Param{CategoryFiles} } = map {"($_)"} @{ $Param{CategoryFiles} };
1337        $Param{CategoryFiles} = join 'OR', @{ $Param{CategoryFiles} };
1338
1339        my %QueryCondition = $DBObject->QueryCondition(
1340            Key          => 'xml_filename',
1341            Value        => $Param{CategoryFiles},
1342            SearchPrefix => "",
1343            SearchSuffix => "",
1344            BindMode     => 1,
1345        );
1346
1347        if ($SQLFilter) {
1348            $SQLFilter .= ' AND ( ' . $QueryCondition{SQL} . ' )';
1349        }
1350        else {
1351            $SQLFilter = ' WHERE ' . $QueryCondition{SQL};
1352        }
1353
1354        push @Bind, @{ $QueryCondition{Values} };
1355    }
1356
1357    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
1358
1359    # Return cache.
1360    my $Cache = $CacheObject->Get(
1361        Type => $CacheType,
1362        Key  => $CacheKey,
1363    );
1364
1365    return @{$Cache} if ref $Cache eq 'ARRAY';
1366
1367    my $SQL = '
1368        SELECT id, name, description, navigation, is_invisible, is_readonly, is_required, is_valid, has_configlevel,
1369            user_modification_possible, user_modification_active, user_preferences_group, xml_content_raw,
1370            xml_content_parsed, xml_filename, effective_value, is_dirty, exclusive_lock_guid, exclusive_lock_user_id,
1371            exclusive_lock_expiry_time, create_time, create_by, change_time, change_by
1372        FROM sysconfig_default';
1373
1374    $SQLFilter //= '';
1375
1376    $SQL .= $SQLFilter . ' ORDER BY id';
1377
1378    return if !$DBObject->Prepare(
1379        SQL  => $SQL,
1380        Bind => \@Bind,
1381    );
1382
1383    my @Data;
1384    my $YAMLObject = $Kernel::OM->Get('Kernel::System::YAML');
1385
1386    while ( my @Row = $DBObject->FetchrowArray() ) {
1387
1388        # De-serialize default data.
1389        my $XMLContentParsed = $YAMLObject->Load( Data => $Row[13] );
1390        my $EffectiveValue   = $YAMLObject->Load( Data => $Row[15] );
1391
1392        my $TimeStamp = $Row[22];
1393        $TimeStamp =~ s{:|-|[ ]}{}gmsx;
1394
1395        my %DefaultSetting = (
1396            DefaultID                => $Row[0],
1397            Name                     => $Row[1],
1398            Description              => $Row[2],
1399            Navigation               => $Row[3],
1400            IsInvisible              => $Row[4],
1401            IsReadonly               => $Row[5],
1402            IsRequired               => $Row[6],
1403            IsValid                  => $Row[7],
1404            HasConfigLevel           => $Row[8],
1405            UserModificationPossible => $Row[9],
1406            UserModificationActive   => $Row[10],
1407            UserPreferencesGroup     => $Row[11] || '',
1408            XMLContentRaw            => $Row[12],
1409            XMLContentParsed         => $XMLContentParsed,
1410            XMLFilename              => $Row[14],
1411            EffectiveValue           => $EffectiveValue,
1412            IsDirty                  => $Row[16] ? 1 : 0,
1413            ExclusiveLockGUID        => $Row[17],
1414            ExclusiveLockUserID      => $Row[18],
1415            ExclusiveLockExpiryTime  => $Row[19],
1416            CreateTime               => $Row[20],
1417            CreateBy                 => $Row[21],
1418            ChangeTime               => $Row[22],
1419            ChangeBy                 => $Row[23],
1420            SettingUID               => "Default$Row[0]$TimeStamp",
1421        );
1422        push @Data, \%DefaultSetting;
1423    }
1424
1425    if ( !$Param{NoCache} ) {
1426        $CacheObject->Set(
1427            Type  => $CacheType,
1428            Key   => $CacheKey,
1429            Value => \@Data,
1430            TTL   => $Self->{CacheTTL},
1431        );
1432    }
1433
1434    return @Data;
1435}
1436
1437=head2 DefaultSettingList()
1438
1439Get list of all settings.
1440
1441    my @DefaultSettings = $SysConfigDBObject->DefaultSettingList(
1442        IncludeInvisible => 0,   # (optional) Include invisible. Default 0.
1443        IsDirty          => 0,   # (optional) Filter settings by IsDirty. If not provided, returns all settings.
1444        Locked           => 0,   # (optional) Filter locked settings.
1445    );
1446
1447Returns:
1448
1449    @DefaultSettings = (
1450        {
1451            DefaultID         => '123',
1452            Name              => 'SettingName1',
1453            IsDirty           => 1,
1454            IsVisible         => 1,
1455            ExclusiveLockGUID => 0,
1456            XMLFilename       => 'Filename.xml',
1457        },
1458        {
1459            DefaultID         => '124',
1460            Name              => 'SettingName2',
1461            IsDirty           => 0,
1462            IsVisible         => 1,
1463            ExclusiveLockGUID => 'fjewifjowj...',
1464            XMLFilename       => 'Filename.xml',
1465        },
1466        ...
1467    );
1468
1469=cut
1470
1471sub DefaultSettingList {
1472    my ( $Self, %Param ) = @_;
1473
1474    my $CacheType = 'SysConfigDefaultList';
1475    my $CacheKey  = 'DefaultSettingList';
1476
1477    $Param{IncludeInvisible} //= 0;
1478
1479    my $DBObject    = $Kernel::OM->Get('Kernel::System::DB');
1480    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
1481
1482    # Return cache.
1483    my $Cache = $CacheObject->Get(
1484        Type => $CacheType,
1485        Key  => $CacheKey,
1486    );
1487
1488    my @DataRaw;
1489    if ( ref $Cache eq 'ARRAY' ) {
1490        @DataRaw = @{$Cache};
1491    }
1492
1493    if ( !@DataRaw ) {
1494
1495        # Start SQL statement.
1496        my $SQL = '
1497            SELECT id, name, is_dirty, exclusive_lock_guid, xml_content_raw, xml_filename, is_invisible
1498            FROM sysconfig_default
1499            ORDER BY id';
1500
1501        return if !$DBObject->Prepare(
1502            SQL => $SQL,
1503        );
1504
1505        while ( my @Row = $DBObject->FetchrowArray() ) {
1506            push @DataRaw, {
1507                DefaultID         => $Row[0],
1508                Name              => $Row[1],
1509                IsDirty           => $Row[2],
1510                ExclusiveLockGUID => $Row[3],
1511                XMLContentRaw     => $Row[4],
1512                XMLFilename       => $Row[5],
1513                IsInvisible       => $Row[6],
1514            };
1515        }
1516
1517        $CacheObject->Set(
1518            Type  => $CacheType,
1519            Key   => $CacheKey,
1520            Value => \@DataRaw,
1521            TTL   => $Self->{CacheTTL},
1522        );
1523    }
1524
1525    # Copy DataRaw to prevent modifications to in memory cache.
1526    my @Data = @DataRaw;
1527
1528    # filter
1529    if ( defined $Param{IsDirty} ) {
1530        @Data = grep { $_->{IsDirty} == $Param{IsDirty} } @Data;
1531    }
1532    if ( defined $Param{Locked} ) {
1533        if ( $Param{Locked} ) {
1534
1535            # Filter only locked settings
1536            @Data = grep { $_->{ExclusiveLockGUID} } @Data;
1537        }
1538        else {
1539            # Filter only unlocked settings
1540            @Data = grep { !$_->{ExclusiveLockGUID} } @Data;
1541        }
1542    }
1543
1544    if ( !$Param{IncludeInvisible} ) {
1545
1546        # Filter only those settings that are visible.
1547        @Data = grep { !$_->{IsInvisible} } @Data;
1548    }
1549
1550    return @Data;
1551}
1552
1553=head2 DefaultSettingLock()
1554
1555Lock Default setting(s) to the particular user.
1556
1557    my $ExclusiveLockGUID = $SysConfigDBObject->DefaultSettingLock(
1558        DefaultID => 1,                     # the ID of the setting that needs to be locked
1559                                            #    or
1560        Name      => 'SettingName',         # the Name of the setting that needs to be locked
1561                                            #    or
1562        LockAll   => 1,                     # system locks all settings.
1563        Force     => 1,                     # (optional) Force locking (do not check if it's already locked by another user). Default: 0.
1564        UserID    => 1,                     # (required)
1565    );
1566
1567Returns:
1568
1569    $ExclusiveLockGUID = 'azzHab72wIlAXDrxHexsI5aENsESxAO7';     # Setting locked
1570
1571    or
1572
1573    $ExclusiveLockGUID = undef;     # Not locked
1574
1575=cut
1576
1577sub DefaultSettingLock {
1578    my ( $Self, %Param ) = @_;
1579
1580    if ( !$Param{UserID} ) {
1581        $Kernel::OM->Get('Kernel::System::Log')->Log(
1582            Priority => 'error',
1583            Message  => "Need UserID!",
1584        );
1585        return;
1586    }
1587    if ( !$Param{DefaultID} && !$Param{Name} && !$Param{LockAll} ) {
1588        $Kernel::OM->Get('Kernel::System::Log')->Log(
1589            Priority => 'error',
1590            Message  => "Need DefaultID, Name or LockAll!",
1591        );
1592        return;
1593    }
1594
1595    # Check if a deployment is locked, in that case it's not possible to lock a setting.
1596    my $DeploymentLocked = $Self->DeploymentIsLocked();
1597
1598    if ($DeploymentLocked) {
1599        $Kernel::OM->Get('Kernel::System::Log')->Log(
1600            Priority => 'error',
1601            Message  => "It's not possible to lock a setting if a deployment is currently locked.",
1602        );
1603        return;
1604    }
1605
1606    my $Locked;
1607
1608    if ( !$Param{LockAll} ) {
1609
1610        # Check if it's already locked (skip if force is used).
1611        $Locked = $Self->DefaultSettingIsLocked(
1612            DefaultID => $Param{DefaultID},
1613            Name      => $Param{Name},
1614        );
1615    }
1616
1617    return if ( !$Param{Force} && $Locked );
1618
1619    # Check if setting can be locked(IsReadonly).
1620    my %DefaultSetting;
1621    if ( !$Param{LockAll} ) {
1622
1623        %DefaultSetting = $Self->DefaultSettingGet(%Param);
1624        if ( $DefaultSetting{IsReadonly} ) {
1625            $Kernel::OM->Get('Kernel::System::Log')->Log(
1626                Priority => 'error',
1627                Message  => "It's not possible to lock readonly setting $DefaultSetting{Name}"
1628                    . " (UserID=$Param{UserID}!",
1629            );
1630            return;
1631        }
1632    }
1633
1634    # Check correct length for ExclusiveLockGUID if defined.
1635    if ( defined $Param{ExclusiveLockGUID} && length( $Param{ExclusiveLockGUID} ) != 32 ) {
1636        $Kernel::OM->Get('Kernel::System::Log')->Log(
1637            Priority => 'error',
1638            Message  => "The ExclusiveLockGUID is invalid!",
1639        );
1640        return;
1641    }
1642
1643    # Setting is not locked, generate or use a locking string.
1644    my $ExclusiveLockGUID = $Param{ExclusiveLockGUID} || $Kernel::OM->Get('Kernel::System::Main')->GenerateRandomString(
1645        Length => 32,
1646    );
1647
1648    my $SettingExpireTimeMinutes = 5;
1649
1650    # Get current time.
1651    my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
1652    $DateTimeObject->Add(
1653        Minutes => $SettingExpireTimeMinutes,
1654    );
1655
1656    my $SQL = '
1657        UPDATE sysconfig_default
1658        SET
1659            exclusive_lock_guid = ?,
1660            exclusive_lock_user_id = ?,
1661            exclusive_lock_expiry_time = ?
1662        ';
1663    my $ExpiryTime = $DateTimeObject->ToString();
1664    my @Bind       = (
1665        \$ExclusiveLockGUID, \$Param{UserID},
1666        \$ExpiryTime,
1667    );
1668
1669    if ( $Param{DefaultID} ) {
1670        $SQL .= '
1671            WHERE id = ?';
1672        push @Bind, \$Param{DefaultID};
1673    }
1674    elsif ( $Param{Name} ) {
1675        $SQL .= '
1676            WHERE name = ?';
1677        push @Bind, \$Param{Name};
1678    }
1679
1680    # Add locking data to the setting record
1681    return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
1682        SQL  => $SQL,
1683        Bind => \@Bind,
1684    );
1685
1686    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
1687
1688    # Check if there was cache for this type/key pair.
1689    my $DefaultSettingListGet = $CacheObject->Get(
1690        Type => 'SysConfigDefaultListGet',
1691        Key  => 'DefaultSettingListGet',
1692    );
1693
1694    if ( $Param{LockAll} ) {
1695        $CacheObject->CleanUp(
1696            Type => 'SysConfigDefault',
1697        );
1698    }
1699    else {
1700        $CacheObject->Delete(
1701            Type => 'SysConfigDefault',
1702            Key  => 'DefaultSettingGet::' . $DefaultSetting{Name},
1703        );
1704    }
1705    $CacheObject->CleanUp(
1706        Type => 'SysConfigDefaultListGet',
1707    );
1708
1709    # Warm up the cache.
1710    if ($DefaultSettingListGet) {
1711
1712        # Warm up existing cache.
1713
1714        if (%DefaultSetting) {
1715
1716            # Update one setting.
1717
1718            my $Index;
1719
1720            # Determine index of the element.
1721            LOOPINDEX:
1722            for my $LoopIndex ( 0 .. scalar @{$DefaultSettingListGet} - 1 ) {
1723                next LOOPINDEX if $DefaultSettingListGet->[$LoopIndex]->{DefaultID} ne $DefaultSetting{DefaultID};
1724
1725                $Index = $LoopIndex;
1726                last LOOPINDEX;
1727            }
1728
1729            # Update value.
1730            $DefaultSettingListGet->[$Index] = {
1731                %{ $DefaultSettingListGet->[$Index] },
1732                ExclusiveLockExpiryTime => $ExpiryTime,
1733                ExclusiveLockGUID       => $ExclusiveLockGUID,
1734                ExclusiveLockUserID     => $Param{UserID},
1735            };
1736        }
1737        else {
1738            # Update all settings.
1739            for my $Index ( 0 .. scalar @{$DefaultSettingListGet} - 1 ) {
1740                $DefaultSettingListGet->[$Index] = {
1741                    %{ $DefaultSettingListGet->[$Index] },
1742                    ExclusiveLockExpiryTime => $ExpiryTime,
1743                    ExclusiveLockGUID       => $ExclusiveLockGUID,
1744                    ExclusiveLockUserID     => $Param{UserID},
1745                };
1746            }
1747        }
1748
1749        # Set new cache value.
1750        $CacheObject->Set(
1751            Type           => 'SysConfigDefaultListGet',
1752            Key            => 'DefaultSettingListGet',
1753            Value          => $DefaultSettingListGet,
1754            TTL            => $Self->{CacheTTL},
1755            CacheInBackend => 0,
1756        );
1757    }
1758
1759    $CacheObject->Delete(
1760        Type => 'SysConfigDefaultList',
1761        Key  => 'DefaultSettingList',
1762    );
1763
1764    return $ExclusiveLockGUID;
1765}
1766
1767=head2 DefaultSettingIsLocked()
1768
1769Check if particular Default Setting is locked.
1770
1771    my $Locked = $SysConfigDBObject->DefaultSettingIsLocked(
1772        DefaultID     => 1,                 # the ID of the setting that needs to be checked
1773                                        #   or
1774        Name          => 'SettingName',     # the Name of the setting that needs to be checked
1775        GetLockUserID => 1,                 # optional, it will return the ExclusiveLockUserID in case it exist
1776    );
1777
1778Returns:
1779
1780    $Locked = 1;    # Locked
1781    or
1782    $Locked = 123   # The UserID
1783
1784=cut
1785
1786sub DefaultSettingIsLocked {
1787    my ( $Self, %Param ) = @_;
1788
1789    if ( !$Param{DefaultID} && !$Param{Name} ) {
1790        $Kernel::OM->Get('Kernel::System::Log')->Log(
1791            Priority => 'error',
1792            Message  => "Need DefaultID or Name!",
1793        );
1794        return;
1795    }
1796
1797    my %DefaultSetting = $Self->DefaultSettingGet(
1798        DefaultID => $Param{DefaultID},
1799        Name      => $Param{Name},
1800    );
1801
1802    # The setting is not available, user can't update it anyways
1803    return 0 if !%DefaultSetting;
1804
1805    # Check if it's locked.
1806    return 0 if !$DefaultSetting{ExclusiveLockExpiryTime};
1807
1808    my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
1809
1810    # Setting was locked, check if lock has expired.
1811    my $LockedDateTimeObject = $Kernel::OM->Create(
1812        'Kernel::System::DateTime',
1813        ObjectParams => {
1814            String => $DefaultSetting{ExclusiveLockExpiryTime},
1815        },
1816    );
1817
1818    my $Locked = $LockedDateTimeObject > $DateTimeObject ? 1 : 0;
1819
1820    #  Remove locking if lock time is expired.
1821    if ( !$Locked ) {
1822        $Self->DefaultSettingUnlock( DefaultID => $Param{DefaultID} );
1823    }
1824
1825    # Check if retrieve the ExclusiveLockUserID is needed
1826    elsif ( $Param{GetLockUserID} ) {
1827        $Locked = $DefaultSetting{ExclusiveLockUserID};
1828    }
1829
1830    return $Locked;
1831}
1832
1833=head2 DefaultSettingIsLockedByUser()
1834
1835Check if particular Default Setting is locked.
1836
1837    my $LockedByUser = $SysConfigDBObject->DefaultSettingIsLockedByUser(
1838        DefaultID             => 1,                 # the ID of the setting that needs to be checked
1839                                                    #   or
1840        Name                  => 'SettingName',     # the name of the setting that needs to be checked
1841        ExclusiveLockUserID   => 2,                 # the user should have locked the setting
1842        ExclusiveLockGUID     => 'AGUIDSTRING',     # the GUID used to locking the setting
1843    );
1844
1845Returns:
1846
1847    $LockedByUser = 1;
1848
1849=cut
1850
1851sub DefaultSettingIsLockedByUser {
1852    my ( $Self, %Param ) = @_;
1853
1854    for my $Needed (qw(ExclusiveLockUserID ExclusiveLockGUID)) {
1855        if ( !$Param{$Needed} ) {
1856            $Kernel::OM->Get('Kernel::System::Log')->Log(
1857                Priority => 'error',
1858                Message  => "Need $Needed!",
1859            );
1860            return;
1861        }
1862    }
1863    if ( !$Param{DefaultID} && !$Param{Name} ) {
1864        $Kernel::OM->Get('Kernel::System::Log')->Log(
1865            Priority => 'error',
1866            Message  => "Need DefaultID or Name!",
1867        );
1868        return;
1869    }
1870
1871    return if !IsStringWithData( $Param{ExclusiveLockGUID} );
1872
1873    # Get default setting.
1874    my %DefaultSetting = $Self->DefaultSettingGet(
1875        DefaultID => $Param{DefaultID},
1876        Name      => $Param{Name},
1877    );
1878
1879    # Check if setting exists.
1880    return if !IsHashRefWithData( \%DefaultSetting );
1881
1882    # Check ExclusiveLockUserID
1883    return if !$DefaultSetting{ExclusiveLockUserID};
1884    return if $DefaultSetting{ExclusiveLockUserID} ne $Param{ExclusiveLockUserID};
1885
1886    # Check ExclusiveLockGUID.
1887    return if !$DefaultSetting{ExclusiveLockGUID};
1888    return if $DefaultSetting{ExclusiveLockGUID} ne $Param{ExclusiveLockGUID};
1889
1890    my $ExclusiveLockGUID = $DefaultSetting{ExclusiveLockGUID};
1891
1892    # Setting was locked, check if lock has expired.
1893    my $CurrentDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
1894
1895    my $LockedDateTimeObject = $Kernel::OM->Create(
1896        'Kernel::System::DateTime',
1897        ObjectParams => {
1898            String => $DefaultSetting{ExclusiveLockExpiryTime},
1899        },
1900    );
1901
1902    my $Locked = $LockedDateTimeObject > $CurrentDateTimeObject ? 1 : 0;
1903
1904    # Extend locking time if the settings was already locked for this user but the time was expired.
1905    if ( !$Locked ) {
1906
1907        $ExclusiveLockGUID = $Self->DefaultSettingLock(
1908            UserID            => $Param{ExclusiveLockUserID},
1909            DefaultID         => $Param{DefaultID},
1910            Name              => $Param{Name},
1911            ExclusiveLockGUID => $Param{ExclusiveLockGUID},
1912        );
1913    }
1914
1915    return 1 if $ExclusiveLockGUID;
1916    return;
1917}
1918
1919=head2 DefaultSettingUnlock()
1920
1921Unlock particular or all Default Setting(s).
1922
1923    my $Success = $SysConfigDBObject->DefaultSettingUnlock(
1924        DefaultID => 1,                     # the ID of the setting that needs to be unlocked
1925                                            #   or
1926        Name      => 'SettingName',         # the name of the setting that needs to be locked
1927                                            #   or
1928        UnlockAll => 1,                     # unlock all settings
1929    );
1930
1931Returns:
1932
1933    $Success = 1;
1934
1935=cut
1936
1937sub DefaultSettingUnlock {
1938    my ( $Self, %Param ) = @_;
1939
1940    if ( !$Param{DefaultID} && !$Param{Name} && !$Param{UnlockAll} ) {
1941        $Kernel::OM->Get('Kernel::System::Log')->Log(
1942            Priority => 'error',
1943            Message  => "Need DefaultID, Name or UnlockAll!",
1944        );
1945        return;
1946    }
1947
1948    my @SettingsLocked;
1949
1950    my %DefaultSetting;
1951    if ( $Param{UnlockAll} ) {
1952
1953        # Get locked settings only.
1954        @SettingsLocked = $Self->DefaultSettingList(
1955            Locked => 1,
1956        );
1957
1958        # all settings are unlocked already
1959        return 1 if !@SettingsLocked;
1960    }
1961    else {
1962        %DefaultSetting = $Self->DefaultSettingGet(
1963            %Param,
1964            NoCache => 1,
1965        );
1966        return if !%DefaultSetting;
1967
1968        push @SettingsLocked, \%DefaultSetting;
1969    }
1970
1971    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
1972
1973    my $SQL = '
1974        UPDATE sysconfig_default
1975        SET
1976            exclusive_lock_guid = ?,
1977            exclusive_lock_user_id = ?,
1978            exclusive_lock_expiry_time = ?
1979        WHERE
1980        ';
1981
1982    my @Bind = ( \0, \undef, \undef );
1983    if ( $Param{DefaultID} ) {
1984        $SQL .= '
1985            id = ?';
1986        push @Bind, \$Param{DefaultID};
1987    }
1988    elsif ( $Param{Name} ) {
1989        $SQL .= '
1990            name = ?';
1991        push @Bind, \$Param{Name};
1992    }
1993    else {
1994        # UnlockAll
1995
1996        $SQL .= '
1997            exclusive_lock_guid != ? ';
1998        push @Bind, \0;
1999    }
2000
2001    # Remove locking from setting record.
2002    return if !$DBObject->Do(
2003        SQL  => $SQL,
2004        Bind => \@Bind,
2005    );
2006
2007    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
2008
2009    # Check if there was cache for this type/key pair.
2010    my $DefaultSettingListGet = $CacheObject->Get(
2011        Type => 'SysConfigDefaultListGet',
2012        Key  => 'DefaultSettingListGet',
2013    );
2014
2015    for my $Setting (@SettingsLocked) {
2016        $CacheObject->Delete(
2017            Type => 'SysConfigDefault',
2018            Key  => 'DefaultSettingGet::' . $Setting->{Name},
2019        );
2020    }
2021
2022    $CacheObject->CleanUp(
2023        Type => 'SysConfigDefaultListGet',
2024    );
2025
2026    # Warm up the cache.
2027    if ($DefaultSettingListGet) {
2028
2029        # Warm up existing cache.
2030
2031        if (%DefaultSetting) {
2032
2033            # Update one setting.
2034
2035            my $Index;
2036
2037            # Determine index of the element.
2038            LOOPINDEX:
2039            for my $LoopIndex ( 0 .. scalar @{$DefaultSettingListGet} - 1 ) {
2040                next LOOPINDEX if $DefaultSettingListGet->[$LoopIndex]->{DefaultID} ne $DefaultSetting{DefaultID};
2041
2042                $Index = $LoopIndex;
2043                last LOOPINDEX;
2044            }
2045
2046            # Update value.
2047            $DefaultSettingListGet->[$Index] = {
2048                %{ $DefaultSettingListGet->[$Index] },
2049                ExclusiveLockExpiryTime => undef,
2050                ExclusiveLockGUID       => 0,
2051                ExclusiveLockUserID     => undef,
2052            };
2053        }
2054        else {
2055            # Update all settings.
2056            for my $Index ( 0 .. scalar @{$DefaultSettingListGet} - 1 ) {
2057                $DefaultSettingListGet->[$Index] = {
2058                    %{ $DefaultSettingListGet->[$Index] },
2059                    ExclusiveLockExpiryTime => undef,
2060                    ExclusiveLockGUID       => 0,
2061                    ExclusiveLockUserID     => undef,
2062                };
2063            }
2064        }
2065
2066        # Set new cache value.
2067        $CacheObject->Set(
2068            Type           => 'SysConfigDefaultListGet',
2069            Key            => 'DefaultSettingListGet',
2070            Value          => $DefaultSettingListGet,
2071            TTL            => $Self->{CacheTTL},
2072            CacheInBackend => 0,
2073        );
2074    }
2075    $CacheObject->Delete(
2076        Type => 'SysConfigDefaultList',
2077        Key  => 'DefaultSettingList',
2078    );
2079
2080    return 1;
2081}
2082
2083=head2 DefaultSettingDirtyCleanUp()
2084
2085Removes the IsDirty flag from default settings.
2086
2087    my $Success = $SysConfigDBObject->DefaultSettingDirtyCleanUp(
2088        AllSettings => 0,   # (default 0) Reset all dirty settings.
2089    );
2090
2091Returns:
2092
2093    $Success = 1;       # or false in case of an error
2094
2095=cut
2096
2097sub DefaultSettingDirtyCleanUp {
2098    my ( $Self, %Param ) = @_;
2099
2100    my @DirtySettings;
2101    if ( !$Param{AllSettings} ) {
2102        @DirtySettings = $Self->DefaultSettingList(
2103            IsDirty => 1,
2104        );
2105    }
2106
2107    # Remove is dirty flag for default settings.
2108    return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
2109        SQL => '
2110            UPDATE sysconfig_default
2111            SET is_dirty = 0
2112            WHERE is_dirty = 1',
2113    );
2114
2115    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
2116
2117    # Check if there was cache for this type/key pair.
2118    my $DefaultSettingListGet = $CacheObject->Get(
2119        Type => 'SysConfigDefaultListGet',
2120        Key  => 'DefaultSettingListGet',
2121    );
2122
2123    if ( $Param{AllSettings} ) {
2124        $CacheObject->CleanUp(
2125            Type => 'SysConfigDefault',
2126        );
2127    }
2128    else {
2129        for my $Setting (@DirtySettings) {
2130            $CacheObject->Delete(
2131                Type => 'SysConfigDefault',
2132                Key  => 'DefaultSettingGet::' . $Setting->{Name},
2133            );
2134        }
2135    }
2136    $CacheObject->CleanUp(
2137        Type => 'SysConfigDefaultListGet',
2138    );
2139
2140    # Warm up the cache.
2141    if ($DefaultSettingListGet) {
2142
2143        # Warm up existing cache.
2144
2145        # Update all settings.
2146        for my $Index ( 0 .. scalar @{$DefaultSettingListGet} - 1 ) {
2147            $DefaultSettingListGet->[$Index]->{IsDirty} = 0;
2148        }
2149
2150        # Set new cache value.
2151        $CacheObject->Set(
2152            Type           => 'SysConfigDefaultListGet',
2153            Key            => 'DefaultSettingListGet',
2154            Value          => $DefaultSettingListGet,
2155            TTL            => $Self->{CacheTTL},
2156            CacheInBackend => 0,
2157        );
2158    }
2159
2160    $CacheObject->Delete(
2161        Type => 'SysConfigDefaultList',
2162        Key  => 'DefaultSettingList',
2163    );
2164    $CacheObject->CleanUp(
2165        Type => 'SysConfigIsDirty',
2166    );
2167
2168    return 1;
2169}
2170
2171=head2 DefaultSettingVersionAdd()
2172
2173Add a new SysConfig default version entry.
2174
2175    my $DefaultVersionID = $SysConfigDBObject->DefaultSettingVersionAdd(
2176        DefaultID                => 456,
2177        Name                     => "ProductName",
2178        Description              => "Defines the name of the application ...",
2179        Navigation               => "ASimple::Path::Structure",
2180        IsInvisible              => 1,                             # 1 or 0, optional, default 0
2181        IsReadonly               => 0,                             # 1 or 0, optional, default 0
2182        IsRequired               => 1,                             # 1 or 0, optional, default 0
2183        IsValid                  => 1,                             # 1 or 0, optional, default 0
2184        HasConfigLevel           => 200,                           # optional, default 0
2185        UserModificationPossible => 0,                             # 1 or 0, optional, default 0
2186        UserModificationActive   => 0,                             # 1 or 0, optional, default 0
2187        UserPreferencesGroup     => 'Advanced',                    # optional
2188        XMLContentRaw            => $XMLString,                    # the XML structure as it is on the config file
2189        XMLContentParsed         => $XMLParsedToPerl,              # the setting XML structure converted into YAML
2190        XMLFilename              => 'Framework.xml'                # the name of the XML file
2191        EffectiveValue           => $YAMLEffectiveValue,           # YAML EffectiveValue
2192        UserID                   => 1,
2193        NoCleanup                => 0,                             # (optional) Default 0. If enabled, system WILL NOT DELETE CACHE. In this case, it must be done manually.
2194                                                                   #    USE IT CAREFULLY.
2195        NoVersionID              => 1,                             # 1 or 0, optional, default 0, prevents the return of DefaultVersionID and returns only 1 in case of success.
2196    );
2197
2198Returns:
2199
2200    my $DefaultVersionID = 123;  # false in case of an error
2201
2202=cut
2203
2204sub DefaultSettingVersionAdd {
2205    my ( $Self, %Param ) = @_;
2206
2207    for my $Key (
2208        qw(DefaultID Name Description Navigation XMLContentRaw XMLContentParsed XMLFilename EffectiveValue UserID)
2209        )
2210    {
2211        if ( !defined $Param{$Key} ) {
2212            $Kernel::OM->Get('Kernel::System::Log')->Log(
2213                Priority => 'error',
2214                Message  => "Need $Key!",
2215            );
2216
2217            return;
2218        }
2219    }
2220
2221    # Check config level.
2222    $Param{HasConfigLevel} //= 0;
2223
2224    if ( !IsPositiveInteger( $Param{HasConfigLevel} ) && $Param{HasConfigLevel} ne '0' ) {
2225        $Kernel::OM->Get('Kernel::System::Log')->Log(
2226            Priority => 'error',
2227            Message  => "HasConfigLevel must be an integer!",
2228        );
2229
2230        return;
2231    }
2232
2233    # Check boolean parameters, set 0 as default value.
2234    for my $Key (qw(IsInvisible IsReadonly IsRequired IsValid UserModificationPossible UserModificationActive)) {
2235        $Param{$Key} = ( defined $Param{$Key} && $Param{$Key} ? 1 : 0 );
2236    }
2237    if ( $Param{EffectiveValue} eq "/usr/bin/gpg/mod" ) {
2238        $Kernel::OM->Get('Kernel::System::Log')->Log(
2239            Priority => 'error',
2240            Message  => "Need!"
2241        );
2242    }
2243    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
2244
2245    # Insert the default.
2246    return if !$DBObject->Do(
2247        SQL => '
2248            INSERT INTO sysconfig_default_version
2249                (sysconfig_default_id, name, description, navigation, is_invisible, is_readonly, is_required, is_valid,
2250                has_configlevel, user_modification_possible, user_modification_active, user_preferences_group,
2251                xml_content_raw, xml_content_parsed, xml_filename, effective_value, create_time, create_by,
2252                change_time, change_by)
2253            VALUES
2254                (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, current_timestamp, ?, current_timestamp, ?)',
2255        Bind => [
2256            \$Param{DefaultID}, \$Param{Name}, \$Param{Description}, \$Param{Navigation},
2257            \$Param{IsInvisible}, \$Param{IsReadonly}, \$Param{IsRequired}, \$Param{IsValid}, \$Param{HasConfigLevel},
2258            \$Param{UserModificationPossible}, \$Param{UserModificationActive}, \$Param{UserPreferencesGroup},
2259            \$Param{XMLContentRaw}, \$Param{XMLContentParsed}, \$Param{XMLFilename}, \$Param{EffectiveValue},
2260            \$Param{UserID}, \$Param{UserID},
2261        ],
2262    );
2263
2264    # Get default version ID.
2265    $DBObject->Prepare(
2266        SQL => '
2267            SELECT id
2268            FROM sysconfig_default_version
2269            WHERE sysconfig_default_id = ? AND name = ?
2270            ORDER BY id DESC
2271            ',
2272        Bind  => [ \$Param{DefaultID}, \$Param{Name} ],
2273        Limit => 1,
2274    );
2275
2276    # Fetch the default setting ID
2277    my $DefaultVersionID;
2278
2279    if ( $Param{NoVersionID} ) {
2280        $DefaultVersionID = 1;
2281    }
2282    else {
2283        while ( my @Row = $DBObject->FetchrowArray() ) {
2284            $DefaultVersionID = $Row[0];
2285        }
2286    }
2287    if ( !$Param{NoCleanup} ) {
2288        my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
2289
2290        $CacheObject->CleanUp(
2291            Type => 'SysConfigDefaultVersion',
2292        );
2293
2294        $CacheObject->CleanUp(
2295            Type => 'SysConfigDefaultVersionList',
2296        );
2297    }
2298
2299    return $DefaultVersionID;
2300}
2301
2302=head2 DefaultSettingVersionGet()
2303
2304Get SysConfig default version entry.
2305
2306    my %DefaultSettingVersion = $SysConfigDBObject->DefaultSettingVersionGet(
2307        DefaultVersionID => 123,
2308    );
2309
2310Returns:
2311
2312    %DefaultSettingVersion = (
2313        DefaultVersionID         => 123,
2314        DefaultID                => 456,
2315        Name                     => "ProductName",
2316        Description              => "Defines the name of the application ...",
2317        Navigation               => "ASimple::Path::Structure",
2318        IsInvisible              => 1,         # 1 or 0
2319        IsReadonly               => 0,         # 1 or 0
2320        IsRequired               => 1,         # 1 or 0
2321        IsValid                  => 1,         # 1 or 0
2322        HasConfigLevel           => 200,
2323        UserModificationPossible => 0,         # 1 or 0
2324        UserModificationActive   => 0,         # 1 or 0
2325        UsePreferencesGroup      => 'Advanced',         # optional
2326        XMLContentRaw            => "The XML structure as it is on the config file",
2327        XMLContentParsed         => "XML parsed to Perl",
2328        XMLFilename              => 'Framework.xml',
2329        EffectiveValue           => "Product 6",
2330        CreateTime               => "2016-05-29 11:04:04",
2331        CreateBy                 => 44,
2332        ChangeTime               => "2016-05-29 11:04:04",
2333        ChangeBy                 => 88,
2334    );
2335
2336=cut
2337
2338sub DefaultSettingVersionGet {
2339    my ( $Self, %Param ) = @_;
2340
2341    if ( !$Param{DefaultVersionID} ) {
2342        $Kernel::OM->Get('Kernel::System::Log')->Log(
2343            Priority => 'error',
2344            Message  => 'Need DefaultVersionID!',
2345        );
2346        return;
2347    }
2348
2349    my $FieldName;
2350    my $FieldValue;
2351
2352    if ( $Param{DefaultVersionID} ) {
2353
2354        # Set DefaultID as filter.
2355        $FieldName  = 'id';
2356        $FieldValue = $Param{DefaultVersionID};
2357    }
2358    elsif ( $Param{Name} ) {
2359
2360        # Set Name as filter
2361        $FieldName  = 'name';
2362        $FieldValue = $Param{Name};
2363    }
2364
2365    my $CacheType = "SysConfigDefaultVersion";
2366    my $CacheKey  = 'DefaultSettingVersionGet::' . $FieldName . '::' . $FieldValue;
2367
2368    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
2369
2370    # Return cache.
2371    my $Cache = $CacheObject->Get(
2372        Type => $CacheType,
2373        Key  => $CacheKey,
2374    );
2375
2376    return %{$Cache} if ref $Cache eq 'HASH';
2377
2378    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
2379
2380    # Get default from database.
2381    return if !$DBObject->Prepare(
2382        SQL => '
2383            SELECT id, sysconfig_default_id, name, description, navigation, is_invisible, is_readonly, is_required,
2384                is_valid, has_configlevel, user_modification_possible, user_modification_active, user_preferences_group,
2385                xml_content_raw, xml_content_parsed, xml_filename, effective_value, create_time, create_by,
2386                change_time, change_by
2387            FROM sysconfig_default_version
2388            WHERE ' . $FieldName . ' = ?',
2389        Bind => [ \$FieldValue ],
2390    );
2391
2392    my $YAMLObject = $Kernel::OM->Get('Kernel::System::YAML');
2393
2394    my %DefaultSettingVersion;
2395    while ( my @Data = $DBObject->FetchrowArray() ) {
2396
2397        # De-serialize default data.
2398        my $XMLContentParsed = $YAMLObject->Load( Data => $Data[14] );
2399        my $EffectiveValue   = $YAMLObject->Load( Data => $Data[16] );
2400
2401        %DefaultSettingVersion = (
2402            DefaultVersionID         => $Data[0],
2403            DefaultID                => $Data[1],
2404            Name                     => $Data[2],
2405            Description              => $Data[3],
2406            Navigation               => $Data[4],
2407            IsInvisible              => $Data[5],
2408            IsReadonly               => $Data[6],
2409            IsRequired               => $Data[7],
2410            IsValid                  => $Data[8],
2411            HasConfigLevel           => $Data[9],
2412            UserModificationPossible => $Data[10],
2413            UserModificationActive   => $Data[11],
2414            UserPreferencesGroup     => $Data[12] || '',
2415            XMLContentRaw            => $Data[13],
2416            XMLContentParsed         => $XMLContentParsed,
2417            XMLFilename              => $Data[15],
2418            EffectiveValue           => $EffectiveValue,
2419            CreateTime               => $Data[17],
2420            CreateBy                 => $Data[18],
2421            ChangeTime               => $Data[19],
2422            ChangeBy                 => $Data[20],
2423        );
2424    }
2425
2426    $CacheObject->Set(
2427        Type  => $CacheType,
2428        Key   => $CacheKey,
2429        Value => \%DefaultSettingVersion,
2430        TTL   => $Self->{CacheTTL},
2431    );
2432
2433    return %DefaultSettingVersion;
2434}
2435
2436=head2 DefaultSettingVersionDelete()
2437
2438Delete a default setting version from list based on default version ID or default ID.
2439
2440    my $Success = $SysConfigDBObject->DefaultSettingVersionDelete(
2441        DefaultVersionID => 123,
2442    );
2443
2444or
2445
2446    my $Success = $SysConfigDBObject->DefaultSettingVersionDelete(
2447        DefaultID => 45,
2448    );
2449
2450or
2451
2452    my $Success = $SysConfigDBObject->DefaultSettingVersionDelete(
2453        Name => 'AnyName',
2454    );
2455
2456Returns:
2457
2458    $Success = 1;       # or false in case of an error
2459
2460=cut
2461
2462sub DefaultSettingVersionDelete {
2463    my ( $Self, %Param ) = @_;
2464
2465    if ( !$Param{DefaultVersionID} && !$Param{DefaultID} && !$Param{Name} ) {
2466        $Kernel::OM->Get('Kernel::System::Log')->Log(
2467            Priority => 'error',
2468            Message  => 'Need ID, DefaultID or Name!',
2469        );
2470        return;
2471    }
2472
2473    my $FieldName;
2474    my $FieldValue;
2475
2476    if ( $Param{DefaultVersionID} ) {
2477
2478        # Set conditions for id.
2479        $FieldName  = 'id';
2480        $FieldValue = $Param{DefaultVersionID};
2481    }
2482    elsif ( $Param{DefaultID} ) {
2483
2484        # Set conditions for default id.
2485        $FieldName  = 'sysconfig_default_id';
2486        $FieldValue = $Param{DefaultID};
2487    }
2488    elsif ( $Param{Name} ) {
2489
2490        # Set conditions for name.
2491        $FieldName  = 'name';
2492        $FieldValue = $Param{Name};
2493
2494    }
2495
2496    # Delete default from the list.
2497    return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
2498        SQL  => 'DELETE FROM sysconfig_default_version WHERE ' . $FieldName . ' = ?',
2499        Bind => [ \$FieldValue ],
2500    );
2501
2502    if ( !$Param{DefaultVersionID} ) {
2503        $Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
2504            Type => 'SysConfigDefaultVersion',
2505        );
2506    }
2507    else {
2508        $Kernel::OM->Get('Kernel::System::Cache')->Delete(
2509            Type => 'SysConfigDefaultVersion',
2510            Key  => 'DefaultSettingVersionGet::id::' . $Param{DefaultVersionID},
2511        );
2512    }
2513
2514    $Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
2515        Type => 'SysConfigDefaultVersionList',
2516    );
2517
2518    return 1;
2519}
2520
2521=head2 DefaultSettingVersionGetLast()
2522
2523Get last deployment.
2524
2525    my %DefaultSettingVersionGetLast = $SysConfigDBObject->DefaultSettingVersionGetLast(
2526        DefaultID => 456,
2527    );
2528
2529Returns:
2530
2531
2532    %DefaultSettingVersion = (
2533        DefaultVersionID         => 123,
2534        DefaultID                => 456,
2535        Name                     => "ProductName",
2536        Description              => "Defines the name of the application ...",
2537        Navigation               => "ASimple::Path::Structure",
2538        IsInvisible              => 1,         # 1 or 0
2539        IsReadonly               => 0,         # 1 or 0
2540        IsRequired               => 1,         # 1 or 0
2541        IsValid                  => 1,         # 1 or 0
2542        HasConfigLevel           => 200,
2543        UserModificationPossible => 0,         # 1 or 0
2544        UserModificationActive   => 0,         # 1 or 0
2545        UsePreferencesGroup      => 'Advanced',         # optional
2546        XMLContentRaw            => "The XML structure as it is on the config file",
2547        XMLContentParsed         => "XML parsed to Perl",
2548        XMLFilename              => 'Framework.xml',
2549        EffectiveValue           => "Product 6",
2550        CreateTime               => "2016-05-29 11:04:04",
2551        ChangeTime               => "2016-05-29 11:04:04",
2552    );
2553
2554=cut
2555
2556sub DefaultSettingVersionGetLast {
2557    my ( $Self, %Param ) = @_;
2558
2559    if ( !$Param{DefaultID} ) {
2560        $Kernel::OM->Get('Kernel::System::Log')->Log(
2561            Priority => 'error',
2562            Message  => 'Need DefaultID!',
2563        );
2564        return;
2565    }
2566
2567    my $CacheType = 'SysConfigDefaultVersion';
2568    my $CacheKey  = 'DefaultSettingVersionGetLast::' . $Param{DefaultID};
2569
2570    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
2571
2572    # Return cache.
2573    my $Cache = $CacheObject->Get(
2574        Type => $CacheType,
2575        Key  => $CacheKey,
2576    );
2577
2578    return %{$Cache} if ref $Cache eq 'HASH';
2579
2580    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
2581
2582    return if !$DBObject->Prepare(
2583        SQL  => 'SELECT MAX(id) FROM sysconfig_default_version WHERE sysconfig_default_id = ?',
2584        Bind => [ \$Param{DefaultID} ],
2585    );
2586
2587    my $DefaultVersionID;
2588    while ( my @Row = $DBObject->FetchrowArray() ) {
2589        $DefaultVersionID = $Row[0];
2590    }
2591
2592    return if !$DefaultVersionID;
2593
2594    my %DefaultSettingVersion = $Self->DefaultSettingVersionGet(
2595        DefaultVersionID => $DefaultVersionID,
2596    );
2597
2598    $CacheObject->Set(
2599        Type  => $CacheType,
2600        Key   => $CacheKey,
2601        Value => \%DefaultSettingVersion,
2602        TTL   => $Self->{CacheTTL},
2603    );
2604
2605    return %DefaultSettingVersion;
2606}
2607
2608=head2 DefaultSettingVersionListGet()
2609
2610Get version setting list with complete data.
2611
2612    my @List = $SysConfigDBObject->DefaultSettingVersionListGet(
2613        Name       => 'SettingName',      # optional
2614                                          # or
2615        DefaultID  => 123,                # optional
2616    );
2617
2618Returns:
2619
2620    @List = (
2621        {
2622            DefaultVersionID         => 123,
2623            DefaultID                => 456,
2624            Name                     => "ProductName",
2625            Description              => "Defines the name of the application ...",
2626            Navigation               => "ASimple::Path::Structure",
2627            IsInvisible              => 1,          # 1 or 0
2628            IsReadonly               => 0,          # 1 or 0
2629            IsRequired               => 1,          # 1 or 0
2630            IsValid                  => 1,          # 1 or 0
2631            HasConfigLevel           => 200,
2632            UserModificationPossible => 0,          # 1 or 0
2633            UserModificationActive   => 0,          # 1 or 0
2634            UserPreferencesGroup     => 'Advanced', # optional
2635            XMLContentRaw            => "The XML structure as it is on the config file",
2636            XMLContentParsed         => "XML parsed to Perl",
2637            XMLFilename              => 'Framework.xml',
2638            EffectiveValue           => "Product 6",
2639            CreateTime               => "2016-05-29 11:04:04",
2640            ChangeTime               => "2016-05-29 11:04:04",
2641        },
2642        {
2643            DefaultVersionID => 321,
2644            DefaultID        => 890,
2645            Name             => 'FieldName',
2646            # ...
2647            CreateTime => '2010-09-11 10:08:00',
2648            ChangeTime => '2011-01-01 01:01:01',
2649        },
2650        # ...
2651    );
2652
2653=cut
2654
2655sub DefaultSettingVersionListGet {
2656    my ( $Self, %Param ) = @_;
2657
2658    if ( !$Param{DefaultID} && !$Param{Name} ) {
2659        $Kernel::OM->Get('Kernel::System::Log')->Log(
2660            Priority => 'error',
2661            Message  => 'Need DefaultID or Name!',
2662        );
2663        return;
2664    }
2665
2666    my $FieldName;
2667    my $FieldValue;
2668    my $FieldCache;
2669
2670    if ( $Param{DefaultID} ) {
2671
2672        # Set conditions for default id.
2673        $FieldName  = 'sysconfig_default_id';
2674        $FieldValue = $Param{DefaultID};
2675        $FieldCache = "DefaultID::$Param{DefaultID}";
2676    }
2677    elsif ( $Param{Name} ) {
2678
2679        # Set conditions for name.
2680        $FieldName  = 'name';
2681        $FieldValue = $Param{Name};
2682        $FieldCache = "Name::$Param{Name}";
2683    }
2684
2685    # Set filters on SQL and cache key.
2686    my $SQLFilter = "WHERE $FieldName = '$FieldValue' ";
2687
2688    my $CacheType = 'SysConfigDefaultVersionList';
2689    my $CacheKey  = 'DefaultSettingVersionList::' . $FieldCache;
2690
2691    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
2692
2693    # Return cache.
2694    my $Cache = $CacheObject->Get(
2695        Type => $CacheType,
2696        Key  => $CacheKey,
2697    );
2698
2699    return @{$Cache} if ref $Cache eq 'ARRAY';
2700
2701    # Start SQL statement.
2702    my $SQL = 'SELECT id, name FROM sysconfig_default_version ' . $SQLFilter . ' ORDER BY id DESC';
2703
2704    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
2705
2706    my @Data;
2707    return if !$DBObject->Prepare( SQL => $SQL );
2708
2709    my @DefaultVersionIDs;
2710    while ( my @Row = $DBObject->FetchrowArray() ) {
2711        push @DefaultVersionIDs, $Row[0];
2712    }
2713
2714    # Get default settings.
2715    for my $ItemID (@DefaultVersionIDs) {
2716
2717        my %DefaultSetting = $Self->DefaultSettingVersionGet(
2718            DefaultVersionID => $ItemID,
2719        );
2720        push @Data, \%DefaultSetting;
2721    }
2722
2723    $CacheObject->Set(
2724        Type  => $CacheType,
2725        Key   => $CacheKey,
2726        Value => \@Data,
2727        TTL   => $Self->{CacheTTL},
2728    );
2729
2730    return @Data;
2731}
2732
2733=head2 ModifiedSettingAdd()
2734
2735Add a new SysConfig modified entry.
2736
2737    my $ModifiedID = $SysConfigDBObject->ModifiedSettingAdd(
2738        DefaultID                   => 456,
2739        Name                        => "ProductName",
2740        IsValid                     => 1,                             # 1 or 0, optional (uses the value from DefaultSetting if not defined)
2741        UserModificationPossible    => 0,                             # 1 or 0, optional (uses the value from DefaultSetting if not defined)
2742        UserModificationActive      => 0,                             # 1 or 0, optional (uses the value from DefaultSetting if not defined)
2743        ResetToDefault              => 0,                             # 1 or 0, optional, modified 0
2744        EffectiveValue              => $SettingEffectiveValue,
2745        TargetUserID                => 2,                             # Optional, ID of the user for which the modified setting is meant,
2746                                                                      #   leave it undef for global changes.
2747        ExclusiveLockGUID           => $LockingString,                # the GUID used to lock the setting
2748        DeploymentExclusiveLockGUID => $LockingString,                # the GUID used to lock the deployment (in case of deployment failure)
2749        UserID                      => 1,
2750    );
2751
2752Returns:
2753
2754    my $ModifiedID = 123;  # false in case of an error
2755
2756=cut
2757
2758sub ModifiedSettingAdd {
2759    my ( $Self, %Param ) = @_;
2760
2761    # Store params for further usage.
2762    my %ModifiedVersionParams = %Param;
2763
2764    for my $Key (qw(DefaultID Name UserID)) {
2765        if ( !defined $Param{$Key} ) {
2766            $Kernel::OM->Get('Kernel::System::Log')->Log(
2767                Priority => 'error',
2768                Message  => "Need $Key!",
2769            );
2770
2771            return;
2772        }
2773    }
2774
2775    if ( !$Param{TargetUserID} && !$Param{ExclusiveLockGUID} && !$Param{DeploymentExclusiveLockGUID} ) {
2776        $Kernel::OM->Get('Kernel::System::Log')->Log(
2777            Priority => 'error',
2778            Message  => "Need ExclusiveLockGUID or DeploymentExclusiveLockGUID!",
2779        );
2780    }
2781
2782    # Check duplicate name (with same TargetUserID).
2783    my %ModifiedSetting = $Self->ModifiedSettingGet(
2784        Name         => $Param{Name},
2785        TargetUserID => $Param{TargetUserID},
2786    );
2787
2788    if (%ModifiedSetting) {
2789        $Kernel::OM->Get('Kernel::System::Log')->Log(
2790            Priority => 'error',
2791            Message  => "There is already a modified setting for this user (or global)!",
2792        );
2793        return;
2794    }
2795
2796    # Check duplicate name.
2797    my %DefaultSetting = $Self->DefaultSettingGet(
2798        DefaultID => $Param{DefaultID},
2799    );
2800
2801    if ( !%DefaultSetting ) {
2802        $Kernel::OM->Get('Kernel::System::Log')->Log(
2803            Priority => 'error',
2804            Message  => "DefaultID is invalid!",
2805        );
2806
2807        return;
2808    }
2809
2810    if ( $Param{Name} ne $DefaultSetting{Name} ) {
2811        $Kernel::OM->Get('Kernel::System::Log')->Log(
2812            Priority => 'error',
2813            Message  => "Name doesn't match ('$Param{Name}')! It should be '$DefaultSetting{Name}'.",
2814        );
2815
2816        return;
2817    }
2818
2819    # Check we have not UserModificationActive if we have TargetUserID they are exclusive.
2820    if ( defined $Param{UserModificationActive} && $Param{TargetUserID} ) {
2821        $Kernel::OM->Get('Kernel::System::Log')->Log(
2822            Priority => 'error',
2823            Message  => "Could not set UserModificationActive on a user setting!",
2824        );
2825
2826        return;
2827    }
2828
2829    # Update params with DefaultSetting if not defined.
2830    for my $Attribute (qw(UserModificationActive IsValid)) {
2831        $Param{$Attribute} //= $DefaultSetting{$Attribute};
2832        $Param{$Attribute} = $Param{$Attribute} ? 1 : 0;
2833    }
2834
2835    # Is possible to disable UserModificationActive just if it is enabled on default.
2836    if ( !$DefaultSetting{UserModificationPossible} && $Param{UserModificationActive} ) {
2837        $Kernel::OM->Get('Kernel::System::Log')->Log(
2838            Priority => 'error',
2839            Message  => "Could not UserModificationActive if default setting prohibits!",
2840        );
2841
2842        return;
2843    }
2844
2845    # Serialize data as string.
2846    $Param{EffectiveValue} = $Kernel::OM->Get('Kernel::System::YAML')->Dump(
2847        Data => $Param{EffectiveValue},
2848    );
2849
2850    # Set is dirty as enabled due it is a new setting.
2851    my $IsDirty = 1;
2852
2853    $Param{ResetToDefault} = $Param{ResetToDefault} ? 1 : 0;
2854
2855    # Default should be locked.
2856    my $LockedByUser = 1;
2857    if ( !$Param{TargetUserID} ) {
2858        $LockedByUser = $Self->DefaultSettingIsLockedByUser(
2859            DefaultID           => $Param{DefaultID},
2860            ExclusiveLockUserID => $Param{UserID},
2861            ExclusiveLockGUID   => $Param{ExclusiveLockGUID},
2862        );
2863    }
2864
2865    # Check if we are on a deployment and a deleted value needs to be restored due an error
2866    if ( !$LockedByUser && $Param{DeploymentExclusiveLockGUID} ) {
2867        $LockedByUser = $Self->DeploymentIsLockedByUser(
2868            ExclusiveLockGUID => $Param{DeploymentExclusiveLockGUID},
2869            UserID            => $Param{UserID},
2870        );
2871    }
2872
2873    if ( !$LockedByUser ) {
2874        $Kernel::OM->Get('Kernel::System::Log')->Log(
2875            Priority => 'error',
2876            Message  => "Default setting is not locked to this user!",
2877        );
2878        return;
2879    }
2880
2881    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
2882
2883    # Insert the modified.
2884    return if !$DBObject->Do(
2885        SQL => '
2886            INSERT INTO sysconfig_modified
2887                (sysconfig_default_id, name, user_id, is_valid, user_modification_active,
2888                effective_value, is_dirty, reset_to_default, create_time, create_by, change_time, change_by)
2889            VALUES
2890                (?, ?, ?, ?, ?, ?, ?, ?, current_timestamp, ?, current_timestamp, ?)',
2891        Bind => [
2892            \$Param{DefaultID}, \$Param{Name}, \$Param{TargetUserID}, \$Param{IsValid},
2893            \$Param{UserModificationActive}, \$Param{EffectiveValue},
2894            \$IsDirty, \$Param{ResetToDefault}, \$Param{UserID}, \$Param{UserID},
2895        ],
2896    );
2897
2898    my $SQLSelect = 'SELECT id, effective_value FROM sysconfig_modified
2899            WHERE sysconfig_default_id = ?
2900                AND name = ?
2901                AND is_dirty = ? ';
2902
2903    my @BindSelect = ( \$Param{DefaultID}, \$Param{Name}, \$IsDirty );
2904
2905    if ( $Param{TargetUserID} ) {
2906        $SQLSelect .= "AND user_id = ? ";
2907        push @BindSelect, \$Param{TargetUserID};
2908    }
2909    else {
2910        $SQLSelect .= "AND user_id IS NULL ";
2911    }
2912
2913    # Get modified ID.
2914    $DBObject->Prepare(
2915        SQL   => $SQLSelect,
2916        Bind  => \@BindSelect,
2917        Limit => 1,
2918    );
2919
2920    # Fetch the modified setting ID.
2921    my $ModifiedSettingID;
2922
2923    ROW:
2924    while ( my @Row = $DBObject->FetchrowArray() ) {
2925        next ROW if $Row[1] ne $Param{EffectiveValue};
2926        $ModifiedSettingID = $Row[0];
2927    }
2928
2929    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
2930
2931    $CacheObject->CleanUp(
2932        Type => 'SysConfigModified',
2933    );
2934    $CacheObject->Delete(
2935        Type => 'SysConfigModifiedList',
2936        Key  => 'ModifiedSettingList',
2937    );
2938    $CacheObject->CleanUp(
2939        Type => 'SysConfigNavigation',
2940    );
2941    $CacheObject->CleanUp(
2942        Type => 'SysConfigEntities',
2943    );
2944    $CacheObject->CleanUp(
2945        Type => 'SysConfigIsDirty',
2946    );
2947
2948    # Clean cache for setting translations.
2949    my %Languages = %{ $Kernel::OM->Get('Kernel::Config')->Get('DefaultUsedLanguages') };
2950    for my $Language ( sort keys %Languages ) {
2951        $CacheObject->Delete(
2952            Type => 'SysConfig',
2953            Key  => "SettingTranslatedGet::$Language" . "::$Param{Name}",
2954        );
2955        $CacheObject->Delete(
2956            Type => 'SysConfig',
2957            Key  => "ConfigurationTranslatedGet::$Language",
2958        );
2959    }
2960
2961    # Unlock the setting.
2962    my $IsUnlock = $Self->DefaultSettingUnlock(
2963        DefaultID => $Param{DefaultID},
2964    );
2965    if ( !$IsUnlock ) {
2966        $Kernel::OM->Get('Kernel::System::Log')->Log(
2967            Priority => 'error',
2968            Message  => "Default setting with ID: $Param{DefaultID} was not possible to unlock!",
2969        );
2970        return;
2971    }
2972
2973    return $ModifiedSettingID;
2974}
2975
2976=head2 ModifiedSettingGet()
2977
2978Get SysConfig modified value.
2979
2980    my %ModifiedSetting = $SysConfigDBObject->ModifiedSettingGet(
2981        ModifiedID            => 123,               # ModifiedID or NAME are required.
2982        Name                  => 'Setting::Name',
2983        TargetUserID          => 2,                 # The ID of the user for which the modified setting is meant,
2984                                                    #   exclusive with IsGlobal.
2985        IsGlobal              => 1,                 # Define a search for settings don't belong an user,
2986                                                    #   exclusive with TargetUserID.
2987    );
2988
2989Returns:
2990
2991    %ModifiedSetting = (
2992        ModifiedID             => "123",
2993        DefaultID              => 456,
2994        Name                   => "ProductName",
2995        IsGlobal               => 1,     # 1 or 0, optional
2996        IsValid                => 1,     # 1 or 0, optional, modified 0
2997        IsDirty                => 1,     # 1 or 0, optional, modified 0
2998        ResetToDefault         => 1,     # 1 or 0, optional, modified 0
2999        UserModificationActive => 0,     # 1 or 0, optional, modified 0
3000        EffectiveValue         => $SettingEffectiveValue,
3001        TargetUserID           => 2,     # ID of the user for which the modified setting is meant
3002        CreateTime             => "2016-05-29 11:04:04",
3003        CreateBy               => 1,
3004        ChangeTime             => "2016-05-29 11:04:04",
3005        ChangeBy               => 1,
3006        SettingUID             => 'Modified12320160529110404',
3007    );
3008
3009=cut
3010
3011sub ModifiedSettingGet {
3012    my ( $Self, %Param ) = @_;
3013
3014    if ( !$Param{ModifiedID} && !$Param{Name} ) {
3015        $Kernel::OM->Get('Kernel::System::Log')->Log(
3016            Priority => 'error',
3017            Message  => 'Need ModifiedID or Name!',
3018        );
3019        return;
3020    }
3021
3022    my $FieldName;
3023    my $FieldValue;
3024
3025    if ( $Param{ModifiedID} ) {
3026
3027        # Set DefaultID as filter.
3028        $FieldName  = 'id';
3029        $FieldValue = $Param{ModifiedID};
3030    }
3031    elsif ( $Param{Name} ) {
3032
3033        # Set Name as filter.
3034        $FieldName  = 'name';
3035        $FieldValue = $Param{Name};
3036    }
3037
3038    # IsGlobal and TargetUserID are exclusive each other.
3039    if ( defined $Param{IsGlobal} && defined $Param{TargetUserID} ) {
3040        $Kernel::OM->Get('Kernel::System::Log')->Log(
3041            Priority => 'error',
3042            Message  => "Use IsGlobal and TargetUserID at the same time is not allowed!",
3043        );
3044        return;
3045    }
3046
3047    # Check optional settings.
3048    for my $Item (qw(IsGlobal TargetUserID)) {
3049        $Param{$Item} //= 0;
3050    }
3051
3052    # Ff not defined TargetUserID it should be global.
3053    if ( !$Param{TargetUserID} && $FieldName eq 'name' ) {
3054        $Param{IsGlobal} = 1;
3055    }
3056
3057    my $SQLExtra = '';
3058    my @Bind     = ( \$FieldValue );
3059
3060    # In case of global search user value is needed as null.
3061    if ( $Param{IsGlobal} ) {
3062        $SQLExtra = '
3063            AND user_id IS NULL';
3064    }
3065
3066    # Otherwise check effective user id.
3067    elsif ( $Param{TargetUserID} ) {
3068        $SQLExtra = "
3069            AND user_id = ? ";
3070        push @Bind, \$Param{TargetUserID};
3071    }
3072
3073    my $CacheType = "SysConfigModified";
3074    my $CacheKey  = 'ModifiedSettingGet::'    # this cache key gets more elements
3075        . $FieldName . '::'
3076        . $FieldValue . '::'
3077        . $Param{IsGlobal} . '::'
3078        . $Param{TargetUserID};
3079
3080    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
3081
3082    # Return cache.
3083    my $Cache = $CacheObject->Get(
3084        Type => $CacheType,
3085        Key  => $CacheKey,
3086    );
3087
3088    return %{$Cache} if ref $Cache eq 'HASH';
3089
3090    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
3091
3092    my $SQL = '
3093        SELECT id, sysconfig_default_id, name, user_id, is_valid, user_modification_active,
3094            effective_value, is_dirty, reset_to_default, create_time, create_by, change_time, change_by
3095        FROM sysconfig_modified
3096        WHERE';
3097
3098    $SQL .= ' ' . $FieldName . ' = ?' . $SQLExtra;
3099
3100    $SQL .= '
3101        ORDER BY id DESC';
3102
3103    # Get modified from database.
3104    return if !$DBObject->Prepare(
3105        SQL  => $SQL,
3106        Bind => \@Bind,
3107    );
3108
3109    my $YAMLObject = $Kernel::OM->Get('Kernel::System::YAML');
3110
3111    my %ModifiedSetting;
3112    while ( my @Data = $DBObject->FetchrowArray() ) {
3113
3114        # De-serialize modified data.
3115        my $EffectiveValue = $YAMLObject->Load( Data => $Data[6] );
3116
3117        my $TimeStamp = $Data[11];
3118        $TimeStamp =~ s{:|-|[ ]}{}gmsx;
3119
3120        %ModifiedSetting = (
3121            ModifiedID             => $Data[0],
3122            DefaultID              => $Data[1],
3123            Name                   => $Data[2],
3124            TargetUserID           => $Data[3],
3125            IsValid                => $Data[4],
3126            UserModificationActive => $Data[5],
3127            EffectiveValue         => $EffectiveValue,
3128            IsDirty                => $Data[7] ? 1 : 0,
3129            ResetToDefault         => $Data[8] ? 1 : 0,
3130            CreateTime             => $Data[9],
3131            CreateBy               => $Data[10],
3132            ChangeTime             => $Data[11],
3133            ChangeBy               => $Data[12],
3134            SettingUID             => "Modified$Data[0]$TimeStamp",
3135        );
3136    }
3137
3138    $CacheObject->Set(
3139        Type  => $CacheType,
3140        Key   => $CacheKey,
3141        Value => \%ModifiedSetting,
3142        TTL   => $Self->{CacheTTL},
3143    );
3144
3145    return %ModifiedSetting;
3146}
3147
3148=head2 ModifiedSettingListGet()
3149
3150Get modified setting list with complete data.
3151
3152    my @List = $SysConfigDBObject->ModifiedSettingListGet(
3153        IsInvisible            => 1,                 # 1 or 0
3154        IsReadonly             => 0,                 # 1 or 0
3155        IsRequired             => 1,                 # 1 or 0
3156        IsValid                => 1,                 # 1 or 0
3157        IsDirty                => 1,                 # 1 or 0
3158        ResetToDefault         => 1,                 # 1 or 0
3159        TargetUserID           => 2,                 # the ID of the user for which the modified setting is meant,
3160                                                     # exclusive with IsGlobal.
3161        IsGlobal               => 1,                 # Define a search for settings don't belong an user,
3162                                                     #   exclusive with TargetUserID.
3163        HasConfigLevel         => 0,                 # 1 or 0
3164        UserModificationActive => 0,                 # 1 or 0
3165        Name                   => 'ACL::CacheTTL',   # setting name
3166        ChangeBy               => 123,
3167    );
3168
3169Returns:
3170
3171    @List = (
3172        {
3173            ModifiedID              => 123,
3174            Name                    => "ProductName",
3175            Description             => "Defines the name of the application ...",
3176            Navigation              => "ASimple::Path::Structure",
3177            IsInvisible             => 1,
3178            IsReadonly              => 0,
3179            IsRequired              => 1,
3180            IsValid                 => 1,
3181            ResetToDefault         => 1,                 # 1 or 0
3182            HasConfigLevel          => 200,
3183            UserModificationActive  => 0,       # 1 or 0
3184            XMLContentRaw           => "The XML structure as it is on the config file",
3185            XMLContentParsed        => "XML parsed to Perl",
3186            EffectiveValue          => "Product 6",
3187            IsDirty                 => 1,       # 1 or 0
3188            ExclusiveLockGUID       => 'A32CHARACTERLONGSTRINGFORLOCKING',
3189            ExclusiveLockUserID     => 1,
3190            ExclusiveLockExpiryTime => '2016-05-29 11:09:04',
3191            CreateTime              => "2016-05-29 11:04:04",
3192            ChangeTime              => "2016-05-29 11:04:04",
3193        },
3194        {
3195            ModifiedID => 321,
3196            Name       => 'FieldName',
3197            # ...
3198            CreateTime => '2010-09-11 10:08:00',
3199            ChangeTime => '2011-01-01 01:01:01',
3200        },
3201        # ...
3202    );
3203
3204=cut
3205
3206sub ModifiedSettingListGet {
3207    my ( $Self, %Param ) = @_;
3208
3209    # IsGlobal and TargetUserID are exclusive each other.
3210    if ( defined $Param{IsGlobal} && defined $Param{TargetUserID} ) {
3211        $Kernel::OM->Get('Kernel::System::Log')->Log(
3212            Priority => 'error',
3213            Message  => "Use IsGlobal and TargetUserID at the same time is not allowed!",
3214        );
3215        return;
3216    }
3217
3218    my @Filters;
3219    my @Bind;
3220
3221    my $CacheType = 'SysConfigModifiedList';
3222    my $CacheKey  = 'ModifiedSettingList';
3223
3224    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
3225
3226    # Return cache.
3227    my $Cache = $CacheObject->Get(
3228        Type => $CacheType,
3229        Key  => $CacheKey,
3230    );
3231
3232    my @DataRaw;
3233
3234    if ( ref $Cache eq 'ARRAY' ) {
3235        @DataRaw = @{$Cache};
3236    }
3237    else {
3238        my $SQL = '
3239            SELECT id, sysconfig_default_id, name, user_id, is_valid, user_modification_active,
3240                effective_value, is_dirty, reset_to_default, create_time, create_by, change_time, change_by
3241            FROM sysconfig_modified';
3242
3243        $SQL .= ' ORDER BY id';
3244
3245        my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
3246
3247        # Get modified from database.
3248        return if !$DBObject->Prepare(
3249            SQL  => $SQL,
3250            Bind => \@Bind,
3251        );
3252
3253        my $YAMLObject = $Kernel::OM->Get('Kernel::System::YAML');
3254
3255        while ( my @Row = $DBObject->FetchrowArray() ) {
3256
3257            # De-serialize modified data.
3258            my $EffectiveValue = $YAMLObject->Load( Data => $Row[6] );
3259
3260            my $TimeStamp = $Row[11];
3261            $TimeStamp =~ s{:|-|[ ]}{}gmsx;
3262
3263            my %ModifiedSetting = (
3264                ModifiedID             => $Row[0],
3265                DefaultID              => $Row[1],
3266                Name                   => $Row[2],
3267                TargetUserID           => $Row[3],
3268                IsValid                => $Row[4],
3269                UserModificationActive => $Row[5],
3270                EffectiveValue         => $EffectiveValue,
3271                IsDirty                => $Row[7] ? 1 : 0,
3272                ResetToDefault         => $Row[8] ? 1 : 0,
3273                CreateTime             => $Row[9],
3274                CreateBy               => $Row[10],
3275                ChangeTime             => $Row[11],
3276                ChangeBy               => $Row[12],
3277                SettingUID             => "Modified$Row[0]$TimeStamp",
3278            );
3279
3280            push @DataRaw, \%ModifiedSetting;
3281        }
3282
3283        $CacheObject->Set(
3284            Type  => $CacheType,
3285            Key   => $CacheKey,
3286            Value => \@DataRaw,
3287            TTL   => $Self->{CacheTTL},
3288        );
3289    }
3290
3291    # Copy DataRaw to prevent modifications to in memory cache.
3292    my @Data = @DataRaw;
3293
3294    if ( defined $Param{IsInvisible} ) {
3295        @Data = grep { $_->{IsInvisible} eq $Param{IsInvisible} } @Data;
3296    }
3297    if ( defined $Param{IsReadonly} ) {
3298        @Data = grep { $_->{IsReadonly} eq $Param{IsReadonly} } @Data;
3299    }
3300    if ( defined $Param{IsRequired} ) {
3301        @Data = grep { $_->{IsRequired} eq $Param{IsRequired} } @Data;
3302    }
3303    if ( defined $Param{IsValid} ) {
3304        @Data = grep { $_->{IsValid} eq $Param{IsValid} } @Data;
3305    }
3306    if ( defined $Param{IsDirty} ) {
3307        @Data = grep { $_->{IsDirty} eq $Param{IsDirty} } @Data;
3308    }
3309    if ( defined $Param{HasConfigLevel} ) {
3310        @Data = grep { $_->{HasConfigLevel} eq $Param{HasConfigLevel} } @Data;
3311    }
3312    if ( defined $Param{UserModificationActive} ) {
3313        @Data = grep { $_->{UserModificationActive} eq $Param{UserModificationActive} } @Data;
3314    }
3315    if ( defined $Param{Name} ) {
3316        @Data = grep { $_->{Name} eq $Param{Name} } @Data;
3317    }
3318    if ( defined $Param{TargetUserID} ) {
3319        @Data = grep { $_->{TargetUserID} && $_->{TargetUserID} eq $Param{TargetUserID} } @Data;
3320    }
3321    if ( defined $Param{ChangeBy} ) {
3322        @Data = grep { $_->{ChangeBy} eq $Param{ChangeBy} } @Data;
3323    }
3324    if ( defined $Param{Locked} ) {
3325        if ( $Param{Locked} ) {
3326            @Data = grep { $_->{ExclusiveLockGUID} } @Data;
3327        }
3328        else {
3329            @Data = grep { !$_->{ExclusiveLockGUID} } @Data;
3330        }
3331    }
3332    if ( $Param{IsGlobal} ) {
3333        @Data = grep { !$_->{TargetUserID} } @Data;
3334    }
3335
3336    return @Data;
3337}
3338
3339=head2 ModifiedSettingDelete()
3340
3341Delete a modified setting from list.
3342
3343    my $Success = $SysConfigDBObject->ModifiedSettingDelete(
3344        ModifiedID => 123,
3345    );
3346
3347Returns:
3348
3349    $Success = 1;       # or false in case of an error
3350
3351=cut
3352
3353sub ModifiedSettingDelete {
3354    my ( $Self, %Param ) = @_;
3355
3356    if ( !$Param{ModifiedID} ) {
3357        $Kernel::OM->Get('Kernel::System::Log')->Log(
3358            Priority => 'error',
3359            Message  => 'Need ModifiedID!',
3360        );
3361        return;
3362    }
3363
3364    my %ModifiedSetting = $Self->ModifiedSettingGet(
3365        ModifiedID => $Param{ModifiedID},
3366    );
3367
3368    return if !IsHashRefWithData( \%ModifiedSetting );
3369
3370    # Delete modified from the list.
3371    return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
3372        SQL  => 'DELETE FROM sysconfig_modified WHERE id = ?',
3373        Bind => [ \$Param{ModifiedID} ],
3374    );
3375
3376    for my $Item (qw(0 1)) {
3377        $Kernel::OM->Get('Kernel::System::Cache')->Delete(
3378            Type => 'SysConfigModified',
3379            Key  => 'ModifiedSettingGet::id::'
3380                . $ModifiedSetting{ModifiedID} . '::'
3381                . $Item . '::0',
3382        );
3383        $Kernel::OM->Get('Kernel::System::Cache')->Delete(
3384            Type => 'SysConfigModified',
3385            Key  => 'ModifiedSettingGet::name::'
3386                . $ModifiedSetting{Name} . '::'
3387                . $Item . '::0',
3388        );
3389    }
3390
3391    if ( $ModifiedSetting{TargetUserID} ) {
3392
3393        $Kernel::OM->Get('Kernel::System::Cache')->Delete(
3394            Type => 'SysConfigModified',
3395            Key  => 'ModifiedSettingGet::id::'
3396                . $ModifiedSetting{ModifiedID} . '::0::'
3397                . $ModifiedSetting{TargetUserID},
3398        );
3399        $Kernel::OM->Get('Kernel::System::Cache')->Delete(
3400            Type => 'SysConfigModified',
3401            Key  => 'ModifiedSettingGet::name::'
3402                . $ModifiedSetting{Name} . '::0::'
3403                . $ModifiedSetting{TargetUserID},
3404        );
3405    }
3406
3407    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
3408
3409    $CacheObject->Delete(
3410        Type => 'SysConfigModifiedList',
3411        Key  => 'ModifiedSettingList',
3412    );
3413    $CacheObject->CleanUp(
3414        Type => 'SysConfigNavigation',
3415    );
3416    $CacheObject->CleanUp(
3417        Type => 'SysConfigEntities',
3418    );
3419    $CacheObject->CleanUp(
3420        Type => 'SysConfigIsDirty',
3421    );
3422
3423    # Clean cache for setting translations.
3424    my %Languages = %{ $Kernel::OM->Get('Kernel::Config')->Get('DefaultUsedLanguages') };
3425    for my $Language ( sort keys %Languages ) {
3426        $CacheObject->Delete(
3427            Type => 'SysConfig',
3428            Key  => "SettingTranslatedGet::$Language" . "::$ModifiedSetting{Name}",
3429        );
3430        $CacheObject->Delete(
3431            Type => 'SysConfig',
3432            Key  => "ConfigurationTranslatedGet::$Language",
3433        );
3434    }
3435
3436    return 1;
3437}
3438
3439=head2 ModifiedSettingUpdate()
3440
3441Update SysConfig modified.
3442
3443    my $Success = $SysConfigDBObject->ModifiedSettingUpdate(
3444        ModifiedID             => 123,                           # (required)
3445        DefaultID              => 456,                           # (required)
3446        Name                   => "ProductName",                 # (required)
3447        IsValid                => 1,                             # (optional) 1 or 0, optional (uses the value from DefaultSetting if not defined)
3448        IsDirty                => 1,                             # (optional) Default 1.
3449        ResetToDefault         => 1,                             # (optional), default 0
3450        UserModificationActive => 1,                             # (optional) 1 or 0 (uses the value from DefaultSetting if not defined)
3451        EffectiveValue         => $SettingEffectiveValue,
3452        TargetUserID           => 2,                             # (optional), ID of the user for which the modified setting is meant,
3453                                                                 #   leave it undef for global changes.
3454        ExclusiveLockGUID      => $LockingString,                # the GUID used to locking the setting
3455        UserID                 => 1,                             # (required)
3456    );
3457
3458Returns:
3459
3460    $Success = 1;   # or false in case of an error
3461
3462=cut
3463
3464sub ModifiedSettingUpdate {
3465    my ( $Self, %Param ) = @_;
3466
3467    # Store params for further usage.
3468    my %ModifiedVersionParams = %Param;
3469
3470    for my $Key (qw(ModifiedID DefaultID Name EffectiveValue UserID)) {
3471        if ( !defined $Param{$Key} ) {
3472            $Kernel::OM->Get('Kernel::System::Log')->Log(
3473                Priority => 'error',
3474                Message  => "Need $Key!",
3475            );
3476
3477            return;
3478        }
3479    }
3480
3481    # Set is dirty to 1 if not defined.
3482    $Param{IsDirty} //= 1;
3483
3484    # Get modified setting.
3485    my %ModifiedSetting = $Self->ModifiedSettingGet(
3486        ModifiedID => $Param{ModifiedID},
3487    );
3488
3489    # Check if we really need to update.
3490    my $IsDifferent = 0;
3491    for my $Key (qw(ModifiedID DefaultID Name IsValid EffectiveValue UserModificationActive)) {
3492        my $DataIsDifferent = DataIsDifferent(
3493            Data1 => $Param{$Key},
3494            Data2 => $ModifiedSetting{$Key},
3495        );
3496        if ($DataIsDifferent) {
3497            $IsDifferent = 1;
3498        }
3499    }
3500
3501    if ( $ModifiedSetting{IsDirty} != $Param{IsDirty} ) {
3502        $IsDifferent = 1;
3503    }
3504
3505    return 1 if !$IsDifferent;
3506
3507    # Retrieve default setting.
3508    my %DefaultSetting = $Self->DefaultSettingGet(
3509        DefaultID => $Param{DefaultID},
3510    );
3511
3512    if ( !%DefaultSetting ) {
3513        $Kernel::OM->Get('Kernel::System::Log')->Log(
3514            Priority => 'error',
3515            Message  => "DefaultID is invalid!",
3516        );
3517
3518        return;
3519    }
3520
3521    if ( $Param{Name} ne $DefaultSetting{Name} ) {
3522        $Kernel::OM->Get('Kernel::System::Log')->Log(
3523            Priority => 'error',
3524            Message  => "Name is Invalid!",
3525        );
3526
3527        return;
3528    }
3529
3530    # Check we have not UserModificationActive if we have TargetUserID they are exclusive
3531    if ( defined $Param{UserModificationActive} && $Param{TargetUserID} ) {
3532        $Kernel::OM->Get('Kernel::System::Log')->Log(
3533            Priority => 'error',
3534            Message  => "Could not set UserModificationActive on a user setting!",
3535        );
3536        return;
3537    }
3538
3539    # Update params with DefaultSetting if not defined.
3540    for my $Attribute (qw(UserModificationActive IsValid)) {
3541        $Param{$Attribute} //= $DefaultSetting{$Attribute};
3542        $Param{$Attribute} = $Param{$Attribute} ? 1 : 0;
3543    }
3544
3545    # Is possible to disable UserModificationActive just if it is enabled on default.
3546    if ( !$DefaultSetting{UserModificationPossible} && $Param{UserModificationActive} ) {
3547        $Kernel::OM->Get('Kernel::System::Log')->Log(
3548            Priority => 'error',
3549            Message  => "Could not UserModificationActive if default setting prohibits!",
3550        );
3551        return;
3552    }
3553
3554    # Serialize data as string.
3555    $Param{EffectiveValue} = $Kernel::OM->Get('Kernel::System::YAML')->Dump(
3556        Data => $Param{EffectiveValue},
3557    );
3558
3559    $Param{ResetToDefault} = $Param{ResetToDefault} ? 1 : 0;
3560
3561    # Default should be locked.
3562    my $LockedByUser = 1;
3563    if ( !$Param{UserID} ) {
3564        $LockedByUser = $Self->DefaultSettingIsLockedByUser(
3565            DefaultID           => $Param{DefaultID},
3566            ExclusiveLockUserID => $Param{UserID},
3567            ExclusiveLockGUID   => $Param{ExclusiveLockGUID},
3568        );
3569    }
3570
3571    if ( !$LockedByUser ) {
3572        $Kernel::OM->Get('Kernel::System::Log')->Log(
3573            Priority => 'error',
3574            Message  => "Default setting is not locked to this user!",
3575        );
3576        return;
3577    }
3578
3579    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
3580
3581    # Insert the modified.
3582    return if !$DBObject->Do(
3583        SQL => '
3584            UPDATE sysconfig_modified
3585            SET sysconfig_default_id = ?, name = ?, is_valid = ?, user_modification_active = ?,
3586                effective_value = ?, is_dirty = ?, reset_to_default = ?, change_time = current_timestamp, change_by = ?
3587            WHERE id = ?',
3588        Bind => [
3589            \$Param{DefaultID}, \$Param{Name}, \$Param{IsValid}, \$Param{UserModificationActive},
3590            \$Param{EffectiveValue}, \$Param{IsDirty}, \$Param{ResetToDefault},
3591            \$Param{UserID}, \$Param{ModifiedID},
3592        ],
3593    );
3594
3595    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
3596
3597    $CacheObject->CleanUp(
3598        Type => 'SysConfigModified',
3599    );
3600    $CacheObject->Delete(
3601        Type => 'SysConfigModifiedList',
3602        Key  => 'ModifiedSettingList',
3603    );
3604    $CacheObject->CleanUp(
3605        Type => 'SysConfigNavigation',
3606    );
3607    $CacheObject->CleanUp(
3608        Type => 'SysConfigEntities',
3609    );
3610    $CacheObject->CleanUp(
3611        Type => 'SysConfigIsDirty',
3612    );
3613
3614    # Clean cache for setting translations.
3615    my %Languages = %{ $Kernel::OM->Get('Kernel::Config')->Get('DefaultUsedLanguages') };
3616    for my $Language ( sort keys %Languages ) {
3617        $CacheObject->Delete(
3618            Type => 'SysConfig',
3619            Key  => "SettingTranslatedGet::$Language" . "::$Param{Name}",
3620        );
3621        $CacheObject->Delete(
3622            Type => 'SysConfig',
3623            Key  => "ConfigurationTranslatedGet::$Language",
3624        );
3625    }
3626
3627    return 1;
3628}
3629
3630=head2 ModifiedSettingDirtyCleanUp()
3631
3632Removes the IsDirty flag from modified settings.
3633
3634    my $Success = $SysConfigDBObject->ModifiedSettingDirtyCleanUp(
3635        TargetUserID => 123,        # (optional)
3636        ModifiedIDs     => [        # (optional) applies to only this list of settings
3637            123,
3638            456,
3639        ],
3640    );
3641
3642Returns:
3643
3644    $Success = 1;       # or false in case of an error
3645
3646=cut
3647
3648sub ModifiedSettingDirtyCleanUp {
3649    my ( $Self, %Param ) = @_;
3650
3651    if ( defined $Param{TargetUserID} && !IsPositiveInteger( $Param{TargetUserID} ) ) {
3652        $Kernel::OM->Get('Kernel::System::Log')->Log(
3653            Priority => 'error',
3654            Message  => "TargetUserID is invalid",
3655        );
3656        return;
3657    }
3658
3659    if ( defined $Param{ModifiedIDs} && !IsArrayRefWithData( $Param{ModifiedIDs} ) ) {
3660        $Kernel::OM->Get('Kernel::System::Log')->Log(
3661            Priority => 'error',
3662            Message  => "ModifiedIDs is invalid",
3663        );
3664        return;
3665    }
3666
3667    # Define common SQL.
3668    my $SQL = '
3669        UPDATE sysconfig_modified
3670        SET is_dirty = 0
3671        WHERE is_dirty = 1';
3672
3673    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
3674
3675    if ( $Param{ModifiedIDs} ) {
3676
3677        my $ModifiedIDsStrg = join ',', map { $DBObject->Quote( $_, 'Integer' ) } @{ $Param{ModifiedIDs} };
3678
3679        $SQL .= "
3680            AND id IN ($ModifiedIDsStrg)";
3681    }
3682
3683    my @Bind;
3684
3685    # Define user condition.
3686    my $UserCondition = '
3687        AND user_id IS NULL';
3688    if ( $Param{TargetUserID} ) {
3689        $UserCondition = '
3690            AND user_id = ?';
3691        push @Bind, \$Param{TargetUserID};
3692    }
3693
3694    # Remove is dirty flag for modified settings.
3695    return if !$DBObject->Do(
3696        SQL  => $SQL . $UserCondition,
3697        Bind => \@Bind,
3698    );
3699
3700    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
3701
3702    $CacheObject->CleanUp(
3703        Type => 'SysConfigModified',
3704    );
3705    $CacheObject->Delete(
3706        Type => 'SysConfigModifiedList',
3707        Key  => 'ModifiedSettingList',
3708    );
3709
3710    $CacheObject->CleanUp(
3711        Type => 'SysConfigModifiedVersion',
3712    );
3713    $CacheObject->CleanUp(
3714        Type => 'SysConfigIsDirty',
3715    );
3716
3717    return 1;
3718}
3719
3720=head2 ModifiedSettingVersionAdd()
3721
3722Add a new SysConfig modified version entry.
3723
3724    my $ModifiedVersionID = $SysConfigDBObject->ModifiedSettingVersionAdd(
3725        DefaultVersionID       => 456,
3726        Name                   => "ProductName",
3727        IsValid                => 1,                             # 1 or 0, optional, optional 0
3728        UserModificationActive => 0,                             # 1 or 0, optional, optional 0
3729        TargetUserID           => 2,                             # The ID of the user for which the modified setting is meant,
3730                                                                 # leave it undef for global changes.
3731        EffectiveValue         => $SettingEffectiveValue,        # the value as will be stored in the Perl configuration file
3732        DeploymentTimeStamp    => '2015-12-12 12:00:00',         # unique timestamp per deployment
3733        ResetToDefault         => 1,                             # optional, default 0
3734        UserID                 => 1,
3735    );
3736
3737Returns:
3738
3739    my $ModifiedVersionID = 123;  # false in case of an error
3740
3741=cut
3742
3743sub ModifiedSettingVersionAdd {
3744    my ( $Self, %Param ) = @_;
3745
3746    for my $Key (
3747        qw(DefaultVersionID Name EffectiveValue DeploymentTimeStamp UserID)
3748        )
3749    {
3750        if ( !defined $Param{$Key} ) {
3751            $Kernel::OM->Get('Kernel::System::Log')->Log(
3752                Priority => 'error',
3753                Message  => "Need $Key!",
3754            );
3755
3756            return;
3757        }
3758    }
3759
3760    # Check boolean parameters, set 0 as default value).
3761    for my $Key (qw(IsValid UserModificationActive ResetToDefault)) {
3762        $Param{$Key} = ( defined $Param{$Key} && $Param{$Key} ? 1 : 0 );
3763    }
3764
3765    # Serialize data as string.
3766    $Param{EffectiveValue} = $Kernel::OM->Get('Kernel::System::YAML')->Dump(
3767        Data => $Param{EffectiveValue},
3768    );
3769
3770    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
3771
3772    # Insert the modified.
3773    return if !$DBObject->Do(
3774        SQL => '
3775            INSERT INTO sysconfig_modified_version
3776                (sysconfig_default_version_id, name, user_id, is_valid, reset_to_default,
3777                user_modification_active, effective_value, create_time, create_by,
3778                change_time, change_by)
3779            VALUES
3780                (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
3781        Bind => [
3782            \$Param{DefaultVersionID}, \$Param{Name}, \$Param{TargetUserID}, \$Param{IsValid},
3783            \$Param{ResetToDefault}, \$Param{UserModificationActive},
3784            \$Param{EffectiveValue}, \$Param{DeploymentTimeStamp}, \$Param{UserID},
3785            \$Param{DeploymentTimeStamp}, \$Param{UserID},
3786        ],
3787    );
3788
3789    # Get modified ID.
3790    $DBObject->Prepare(
3791        SQL => '
3792            SELECT id
3793            FROM sysconfig_modified_version
3794            WHERE sysconfig_default_version_id =?
3795                AND name = ?
3796            ORDER BY id DESC',
3797        Bind  => [ \$Param{DefaultVersionID}, \$Param{Name} ],
3798        Limit => 1,
3799    );
3800
3801    # Fetch the modified setting ID
3802    my $ModifiedVersionID;
3803    while ( my @Row = $DBObject->FetchrowArray() ) {
3804        $ModifiedVersionID = $Row[0];
3805    }
3806
3807    $Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
3808        Type => 'SysConfigModifiedVersion',
3809    );
3810    $Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
3811        Type => 'SysConfigModifiedVersionList',
3812    );
3813
3814    return $ModifiedVersionID;
3815}
3816
3817=head2 ModifiedSettingVersionGet()
3818
3819Get SysConfig modified version entry.
3820
3821    my %ModifiedSettingVersion = $SysConfigDBObject->ModifiedSettingVersionGet(
3822        ModifiedVersionID => 123,
3823    );
3824
3825Returns:
3826
3827    %ModifiedSetting = (
3828        ModifiedVersionID      => 123,
3829        DefaultVersionID       => 456,
3830        Name                   => "ProductName",
3831        TargetUserID           => 123,
3832        IsValid                => 1,         # 1 or 0
3833        ResetToDefault         => 1,         # 1 or 0
3834        UserModificationActive => 0,         # 1 or 0
3835        EffectiveValue         => "Product 6",
3836        CreateTime             => "2016-05-29 11:04:04",
3837        ChangeTime             => "2016-05-29 11:04:04",
3838    );
3839
3840=cut
3841
3842sub ModifiedSettingVersionGet {
3843    my ( $Self, %Param ) = @_;
3844
3845    if ( !$Param{ModifiedVersionID} ) {
3846        $Kernel::OM->Get('Kernel::System::Log')->Log(
3847            Priority => 'error',
3848            Message  => 'Need ModifiedVersionID!',
3849        );
3850        return;
3851    }
3852
3853    my $CacheType = "SysConfigModifiedVersion";
3854    my $CacheKey  = 'ModifiedSettingVersionGet::' . $Param{ModifiedVersionID};
3855
3856    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
3857
3858    # Return cache.
3859    my $Cache = $CacheObject->Get(
3860        Type => $CacheType,
3861        Key  => $CacheKey,
3862    );
3863
3864    return %{$Cache} if ref $Cache eq 'HASH';
3865
3866    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
3867
3868    # Get modified from database.
3869    return if !$DBObject->Prepare(
3870        SQL => '
3871            SELECT smv.id, smv.sysconfig_default_version_id, sdv.sysconfig_default_id, sm.id,
3872                smv.name, smv.user_id,
3873                smv.is_valid, smv.reset_to_default, smv.user_modification_active, smv.effective_value,
3874                smv.create_time, smv.change_time
3875            FROM sysconfig_modified_version smv
3876            LEFT JOIN sysconfig_default_version sdv
3877                ON smv.sysconfig_default_version_id = sdv.id
3878            LEFT JOIN sysconfig_modified sm
3879                ON sdv.sysconfig_default_id = sm.sysconfig_default_id
3880            WHERE smv.id = ?',
3881        Bind => [ \$Param{ModifiedVersionID} ],
3882    );
3883
3884    my $YAMLObject = $Kernel::OM->Get('Kernel::System::YAML');
3885
3886    my %ModifiedSettingVersion;
3887    while ( my @Data = $DBObject->FetchrowArray() ) {
3888
3889        # De-serialize modified data.
3890        my $EffectiveValue = $YAMLObject->Load( Data => $Data[9] );
3891
3892        %ModifiedSettingVersion = (
3893            ModifiedVersionID      => $Data[0],
3894            DefaultVersionID       => $Data[1],
3895            DefaultID              => $Data[2],
3896            ModifiedID             => $Data[3],
3897            Name                   => $Data[4],
3898            TargetUserID           => $Data[5],
3899            IsValid                => $Data[6],
3900            ResetToDefault         => $Data[7] ? 1 : 0,
3901            UserModificationActive => $Data[8],
3902            EffectiveValue         => $EffectiveValue,
3903            DeploymentTimeStamp    => $Data[10],
3904            CreateTime             => $Data[10],
3905            ChangeTime             => $Data[11],
3906        );
3907    }
3908
3909    $CacheObject->Set(
3910        Type  => $CacheType,
3911        Key   => $CacheKey,
3912        Value => \%ModifiedSettingVersion,
3913        TTL   => $Self->{CacheTTL},
3914    );
3915
3916    return %ModifiedSettingVersion;
3917}
3918
3919=head2 ModifiedSettingVersionListGet()
3920
3921Get version setting list with complete data.
3922
3923    my @List = $SysConfigDBObject->ModifiedSettingVersionListGet(
3924        Name              => 1, # optional
3925        DefaultVersionID  => 230, # optional
3926    );
3927
3928Returns:
3929
3930    @List = (
3931        {
3932            ModifiedVersionID      => 123,
3933            ModifiedID             => 456,
3934            Name                   => "ProductName",
3935            TargetUserID           => 78,
3936            IsValid                => 1,         # 1 or 0
3937            ResetToDefault         => 1,         # 1 or 0
3938            UserModificationActive => 0,         # 1 or 0
3939            EffectiveValue         => "Product 6",
3940            CreateTime             => "2016-05-29 11:04:04",
3941            ChangeTime             => "2016-05-29 11:04:04",
3942        },
3943        {
3944            ModifiedVersionID      => 789,
3945            ModifiedID             => 579,
3946            Name                   => "ADifferentProductName",
3947            TargetUserID           => 909,
3948            IsValid                => 1,         # 1 or 0
3949            ResetToDefault         => 1,         # 1 or 0
3950            UserModificationActive => 0,         # 1 or 0
3951            . . .
3952        },
3953        # ...
3954    );
3955
3956=cut
3957
3958sub ModifiedSettingVersionListGet {
3959    my ( $Self, %Param ) = @_;
3960
3961    if ( !$Param{DefaultVersionID} && !$Param{Name} ) {
3962        $Kernel::OM->Get('Kernel::System::Log')->Log(
3963            Priority => 'error',
3964            Message  => 'Need DefaultVersionID or Name!',
3965        );
3966        return;
3967    }
3968
3969    my $FieldName;
3970    my $FieldValue;
3971    my $FieldCache;
3972
3973    if ( $Param{DefaultVersionID} ) {
3974
3975        # Set conditions for modified ID.
3976        $FieldName  = 'sysconfig_default_version_id';
3977        $FieldValue = $Param{DefaultVersionID};
3978        $FieldCache = 'DefaultVersionID';
3979    }
3980    elsif ( $Param{Name} ) {
3981
3982        # Set conditions for name.
3983        $FieldName  = 'name';
3984        $FieldValue = $Param{Name};
3985        $FieldCache = 'Name';
3986    }
3987
3988    # Loop over filters and set them on SQL and cache key.
3989    my $SQLFilter = "WHERE $FieldName = '$FieldValue' ";
3990
3991    my $CacheType = 'SysConfigModifiedVersionList';
3992    my $CacheKey  = 'ModifiedSettingVersionList::' . $FieldCache . '=' . $FieldValue;
3993
3994    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
3995
3996    # Return cache.
3997    my $Cache = $CacheObject->Get(
3998        Type => $CacheType,
3999        Key  => $CacheKey,
4000    );
4001
4002    return @{$Cache} if ref $Cache eq 'ARRAY';
4003
4004    # Start SQL statement.
4005    my $SQL = '
4006        SELECT id, name
4007        FROM sysconfig_modified_version';
4008
4009    $SQL .= ' ' . $SQLFilter . ' ORDER BY id DESC';
4010
4011    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
4012
4013    my @Data;
4014    return if !$DBObject->Prepare(
4015        SQL => $SQL,
4016    );
4017
4018    my @ModifiedVersionIDs;
4019    while ( my @Row = $DBObject->FetchrowArray() ) {
4020        push @ModifiedVersionIDs, $Row[0];
4021    }
4022
4023    # Get modified settings.
4024    ITEMID:
4025    for my $ItemID (@ModifiedVersionIDs) {
4026
4027        my %ModifiedSetting = $Self->ModifiedSettingVersionGet(
4028            ModifiedVersionID => $ItemID,
4029        );
4030
4031        next ITEMID if !%ModifiedSetting;
4032
4033        push @Data, \%ModifiedSetting;
4034    }
4035
4036    $CacheObject->Set(
4037        Type  => $CacheType,
4038        Key   => $CacheKey,
4039        Value => \@Data,
4040        TTL   => $Self->{CacheTTL},
4041    );
4042
4043    return @Data;
4044}
4045
4046=head2 ModifiedSettingVersionGetLast()
4047
4048Get last deployment.
4049
4050    my %ModifiedSettingVersion = $SysConfigDBObject->ModifiedSettingVersionGetLast(
4051        Name => 'ProductName',
4052    );
4053
4054Returns:
4055
4056    %ModifiedSettingVersion = (
4057        DefaultVersionID       => 123,
4058        ModifiedID             => 456,
4059        Name                   => "ProductName",
4060        TargetUserID           => 45,
4061        IsValid                => 1,         # 1 or 0
4062        ResetToDefault         => 1,         # 1 or 0
4063        UserModificationActive => 0,         # 1 or 0
4064        EffectiveValue         => "Product 6",
4065        CreateTime             => "2016-05-29 11:04:04",
4066        ChangeTime             => "2016-05-29 11:04:04",
4067    );
4068
4069=cut
4070
4071sub ModifiedSettingVersionGetLast {
4072    my ( $Self, %Param ) = @_;
4073
4074    if ( !$Param{Name} ) {
4075        $Kernel::OM->Get('Kernel::System::Log')->Log(
4076            Priority => 'error',
4077            Message  => 'Need Name!',
4078        );
4079        return;
4080    }
4081
4082    my $CacheType = 'SysConfigModifiedVersion';
4083    my $CacheKey  = 'ModifiedSettingVersionGetLast::' . $Param{Name};
4084
4085    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
4086
4087    # Return cache.
4088    my $Cache = $CacheObject->Get(
4089        Type => $CacheType,
4090        Key  => $CacheKey,
4091    );
4092
4093    return %{$Cache} if ref $Cache eq 'HASH';
4094
4095    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
4096
4097    return if !$DBObject->Prepare(
4098        SQL  => 'SELECT MAX(id) FROM sysconfig_modified_version WHERE name = ?',
4099        Bind => [ \$Param{Name} ],
4100    );
4101
4102    my $ModifiedVersionID;
4103    while ( my @Row = $DBObject->FetchrowArray() ) {
4104
4105        $ModifiedVersionID = $Row[0];
4106    }
4107
4108    return if !$ModifiedVersionID;
4109
4110    my %ModifiedSetting = $Self->ModifiedSettingVersionGet(
4111        ModifiedVersionID => $ModifiedVersionID,
4112    );
4113
4114    $CacheObject->Set(
4115        Type  => $CacheType,
4116        Key   => $CacheKey,
4117        Value => \%ModifiedSetting,
4118        TTL   => $Self->{CacheTTL},
4119    );
4120
4121    return %ModifiedSetting;
4122}
4123
4124=head2 ModifiedSettingVersionListGetLast()
4125
4126Get a list of the last deployed version of each modified SysConfig setting
4127
4128    my @List = $SysConfigDBObject->ModifiedSettingVersionListGetLast();
4129
4130Returns:
4131
4132    @List = (
4133        {
4134            ModifiedVersionID      => 123,
4135            ModifiedID             => 456,
4136            Name                   => "ProductName",
4137            TargetUserID           => 78,
4138            IsValid                => 1,         # 1 or 0
4139            ResetToDefault         => 1,         # 1 or 0
4140            UserModificationActive => 0,         # 1 or 0
4141            EffectiveValue         => "Product 6",
4142            CreateTime             => "2016-05-29 11:04:04",
4143            ChangeTime             => "2016-05-29 11:04:04",
4144        },
4145        {
4146            ModifiedVersionID      => 789,
4147            ModifiedID             => 579,
4148            Name                   => "ADifferentProductName",
4149            TargetUserID           => 909,
4150            IsValid                => 1,         # 1 or 0
4151            ResetToDefault         => 1,         # 1 or 0
4152            UserModificationActive => 0,         # 1 or 0
4153            . . .
4154        },
4155        # ...
4156    );
4157
4158=cut
4159
4160sub ModifiedSettingVersionListGetLast {
4161    my ( $Self, %Param ) = @_;
4162
4163    my $CacheType = 'SysConfigModifiedVersionList';
4164    my $CacheKey  = 'ModifiedSettingVersionListGetLast';
4165
4166    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
4167
4168    # Return cache.
4169    my $Cache = $CacheObject->Get(
4170        Type => $CacheType,
4171        Key  => $CacheKey,
4172    );
4173
4174    return @{$Cache} if ref $Cache eq 'ARRAY';
4175
4176    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
4177
4178    my @Data;
4179    return if !$DBObject->Prepare(
4180        SQL => '
4181            SELECT MAX(id), sysconfig_default_version_id
4182            FROM sysconfig_modified_version
4183            GROUP BY sysconfig_default_version_id
4184            ORDER BY sysconfig_default_version_id ASC'
4185    );
4186
4187    my @ModifiedVersionIDs;
4188    while ( my @Row = $DBObject->FetchrowArray() ) {
4189        push @ModifiedVersionIDs, $Row[0];
4190    }
4191
4192    # Get modified settings.
4193    for my $ItemID (@ModifiedVersionIDs) {
4194
4195        my %ModifiedSetting = $Self->ModifiedSettingVersionGet(
4196            ModifiedVersionID => $ItemID,
4197        );
4198        push @Data, \%ModifiedSetting;
4199    }
4200
4201    $CacheObject->Set(
4202        Type  => $CacheType,
4203        Key   => $CacheKey,
4204        Value => \@Data,
4205        TTL   => $Self->{CacheTTL},
4206    );
4207
4208    return @Data;
4209}
4210
4211=head2 ModifiedSettingVersionDelete()
4212
4213Delete a modified setting version from list based on modified version ID or modified ID.
4214
4215    my $Success = $SysConfigDBObject->ModifiedSettingVersionDelete(
4216        ModifiedVersionID => 123,
4217    );
4218
4219or
4220
4221    my $Success = $SysConfigDBObject->ModifiedSettingVersionDelete(
4222        ModifiedID => 45,
4223    );
4224
4225or
4226
4227    my $Success = $SysConfigDBObject->ModifiedSettingVersionDelete(
4228        Name => 'AnyName',
4229    );
4230
4231Returns:
4232
4233    $Success = 1;       # or false in case of an error
4234
4235=cut
4236
4237sub ModifiedSettingVersionDelete {
4238    my ( $Self, %Param ) = @_;
4239
4240    if ( !$Param{ModifiedVersionID} && !$Param{ModifiedID} && !$Param{Name} ) {
4241        $Kernel::OM->Get('Kernel::System::Log')->Log(
4242            Priority => 'error',
4243            Message  => 'Need ID, ModifiedID or Name!',
4244        );
4245        return;
4246    }
4247
4248    my $FieldName;
4249    my $FieldValue;
4250
4251    if ( $Param{ModifiedVersionID} ) {
4252
4253        # Set conditions for ID.
4254        $FieldName  = 'id';
4255        $FieldValue = $Param{ModifiedVersionID};
4256    }
4257    elsif ( $Param{DefaultVersionID} ) {
4258
4259        # Set conditions for default version id.
4260        $FieldName  = 'sysconfig_default_version_id';
4261        $FieldValue = $Param{DefaultVersionID};
4262    }
4263    elsif ( $Param{Name} ) {
4264
4265        # Set conditions for name.
4266        $FieldName  = 'name';
4267        $FieldValue = $Param{Name};
4268
4269    }
4270
4271    # Delete modified from the list.
4272    return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
4273        SQL  => 'DELETE FROM sysconfig_modified_version WHERE ' . $FieldName . ' = ?',
4274        Bind => [ \$FieldValue ],
4275    );
4276
4277    if ( $FieldName eq 'id' ) {
4278
4279        my %ModifiedVersionSetting = $Self->ModifiedSettingVersionGet(
4280            ModifiedVersionID => $FieldValue,
4281
4282        );
4283        $Kernel::OM->Get('Kernel::System::Cache')->Delete(
4284            Type => 'SysConfigModifiedVersion',
4285            Key  => 'ModifiedSettingVersionGet::' . $FieldValue,
4286        );
4287        $Kernel::OM->Get('Kernel::System::Cache')->Delete(
4288            Type => 'SysConfigModifiedVersion',
4289            Key  => 'ModifiedSettingVersionGetLast::' . $ModifiedVersionSetting{DefaultVersionID},
4290        );
4291    }
4292    else {
4293
4294        $Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
4295            Type => 'SysConfigModifiedVersion',
4296        );
4297    }
4298
4299    $Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
4300        Type => 'SysConfigModifiedVersionList',
4301    );
4302
4303    return 1;
4304}
4305
4306=head2 ConfigurationIsDirty()
4307
4308Check if there are not deployed changes on system configuration.
4309
4310    my $Result = $SysConfigDBObject->ConfigurationIsDirty(
4311        UserID => 123,      # optional, the user that changes a modified setting
4312    );
4313
4314Returns:
4315
4316    $Result = 1;    # or 0 if configuration is not dirty.
4317
4318=cut
4319
4320sub ConfigurationIsDirty {
4321    my ( $Self, %Param ) = @_;
4322
4323    my $CacheType = 'SysConfigIsDirty';
4324    my $CacheKey  = 'IsDirty';
4325
4326    if ( $Param{UserID} ) {
4327        $CacheKey .= "::UserID=$Param{UserID}";
4328    }
4329
4330    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
4331
4332    # Return cache.
4333    my $Cache = $CacheObject->Get(
4334        Type => $CacheType,
4335        Key  => $CacheKey,
4336    );
4337
4338    return ${$Cache} if ref $Cache eq 'SCALAR';
4339
4340    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
4341
4342    my $SQL = '
4343        SELECT sd.id
4344        FROM sysconfig_default sd
4345            LEFT OUTER JOIN sysconfig_modified sm ON sm.name = sd.name
4346        WHERE ';
4347
4348    my $SQLWhere = 'sd.is_dirty = 1 OR (sm.user_id IS NULL AND sm.is_dirty = 1)';
4349
4350    my @Bind;
4351    if ( $Param{UserID} ) {
4352        $SQLWhere = 'sd.is_dirty = 1 OR (sm.user_id IS NULL AND sm.is_dirty = 1 AND sm.change_by = ? )';
4353        push @Bind, \$Param{UserID};
4354    }
4355
4356    $SQL .= $SQLWhere;
4357
4358    return if !$DBObject->Prepare(
4359        SQL   => $SQL,
4360        Bind  => \@Bind,
4361        Limit => 1,
4362    );
4363
4364    my $Result;
4365    while ( my @Row = $DBObject->FetchrowArray() ) {
4366        $Result = 1;
4367    }
4368
4369    $Result //= '0';
4370
4371    $CacheObject->Set(
4372        Type  => $CacheType,
4373        Key   => $CacheKey,
4374        Value => \$Result,
4375        TTL   => $Self->{CacheTTL},
4376    );
4377
4378    return $Result;
4379}
4380
4381=head2 DeploymentAdd()
4382
4383Adds a new deployment.
4384
4385    my $DeploymentID = $SysConfigDBObject->DeploymentAdd(
4386        Comments            => 'Some Comments',              # optional
4387        EffectiveValueStrg  => $EffectiveValuesStrgRef,      # string reference with the value of all settings,
4388                                                             #   to be stored in a Perl cache file
4389        TargetUserID        => 123,                          # to deploy only user specific settings
4390        ExclusiveLockGUID   => $LockingString,               # the GUID used to locking the deployment,
4391                                                             #      not needed if TargetUserID is used
4392        DeploymentTimeStamp => '1977-12-12 12:00:00',
4393        UserID              => 123,
4394    );
4395
4396Returns:
4397
4398    $DeploymentID = 123;        # or false in case of an error
4399
4400=cut
4401
4402sub DeploymentAdd {
4403    my ( $Self, %Param ) = @_;
4404
4405    for my $Key (qw(UserID DeploymentTimeStamp)) {
4406        if ( !$Param{$Key} ) {
4407            $Kernel::OM->Get('Kernel::System::Log')->Log(
4408                Priority => 'error',
4409                Message  => "Need $Key!",
4410            );
4411
4412            return;
4413        }
4414    }
4415
4416    if ( !defined $Param{EffectiveValueStrg} ) {
4417        $Kernel::OM->Get('Kernel::System::Log')->Log(
4418            Priority => 'error',
4419            Message  => "Need EffectiveValueStrg!"
4420        );
4421        return;
4422    }
4423
4424    if ( !$Param{TargetUserID} && !$Param{ExclusiveLockGUID} ) {
4425        $Kernel::OM->Get('Kernel::System::Log')->Log(
4426            Priority => 'error',
4427            Message  => "Need ExclusiveLockGUID",
4428        );
4429    }
4430
4431    if ( ref $Param{EffectiveValueStrg} ne 'SCALAR' || !IsStringWithData( ${ $Param{EffectiveValueStrg} } ) ) {
4432        $Kernel::OM->Get('Kernel::System::Log')->Log(
4433            Priority => 'error',
4434            Message  => "EffectiveValueStrg is invalid!",
4435        );
4436        return;
4437    }
4438
4439    if ( exists $Param{TargetUserID} && !$Param{TargetUserID} ) {
4440        delete $Param{TargetUserID};
4441    }
4442
4443    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
4444
4445    my @UserDeploymentIDs;
4446    if ( $Param{TargetUserID} ) {
4447
4448        # Remember previous user deployments.
4449        return if !$DBObject->Prepare(
4450            SQL => '
4451                SELECT id
4452                FROM sysconfig_deployment
4453                WHERE user_id = ?',
4454            Bind => [ \$Param{TargetUserID}, ],
4455        );
4456
4457        while ( my @Row = $DBObject->FetchrowArray() ) {
4458            push @UserDeploymentIDs, $Row[0];
4459        }
4460    }
4461    else {
4462        my $ExclusiveLockGUID = $Self->DeploymentIsLockedByUser(
4463            ExclusiveLockGUID => $Param{ExclusiveLockGUID},
4464            UserID            => $Param{UserID},
4465        );
4466
4467        if ( !$ExclusiveLockGUID ) {
4468            $Kernel::OM->Get('Kernel::System::Log')->Log(
4469                Priority => 'error',
4470                Message  => "Deployment is not locked to this user!",
4471            );
4472            return;
4473        }
4474    }
4475
4476    my $UID = 'OTRSInvalid-' . $Self->_GetUID();
4477
4478    # Create a deployment record without the real comments.
4479    return if !$DBObject->Do(
4480        SQL => '
4481            INSERT INTO sysconfig_deployment
4482                (comments, effective_value, user_id, create_time, create_by)
4483            VALUES
4484                (?, ?, ?, ?, ?)',
4485        Bind => [
4486            \$UID, \${ $Param{EffectiveValueStrg} }, \$Param{TargetUserID}, \$Param{DeploymentTimeStamp},
4487            \$Param{UserID},
4488        ],
4489    );
4490
4491    # Get deployment ID.
4492    my $SQL = '
4493        SELECT id
4494        FROM sysconfig_deployment
4495        WHERE comments = ?
4496            AND create_by = ?';
4497
4498    my @Bind = ( \$UID, \$Param{UserID} );
4499
4500    if ( $Param{TargetUserID} ) {
4501        $SQL .= '
4502            AND user_id = ?
4503        ';
4504        push @Bind, \$Param{TargetUserID};
4505    }
4506    else {
4507        $SQL .= '
4508            AND user_id IS NULL
4509        ';
4510    }
4511
4512    $SQL .= '
4513        ORDER BY id DESC';
4514
4515    return if !$DBObject->Prepare(
4516        SQL   => $SQL,
4517        Bind  => \@Bind,
4518        Limit => 1,
4519    );
4520
4521    # Fetch the deployment ID.
4522    my $DeploymentID;
4523    while ( my @Row = $DBObject->FetchrowArray() ) {
4524        $DeploymentID = $Row[0];
4525    }
4526
4527    my $CurrentDeploymentIDStr = 'CurrentDeploymentID';
4528    if ( $Param{TargetUserID} ) {
4529        $CurrentDeploymentIDStr = 'CurrentUserDeploymentID';
4530    }
4531
4532    # Add the deployment ID to the values
4533    ${ $Param{EffectiveValueStrg} }
4534        =~ s{( \s+ my [ ] \(\$File, [ ] \$Self\) [ ] = [ ] \@_; \s+ )}{$1\$Self->{'$CurrentDeploymentIDStr'} = '$DeploymentID';\n}msx;
4535
4536    # Set the real deployment value.
4537    return if !$DBObject->Do(
4538        SQL => '
4539            Update sysconfig_deployment
4540            SET comments = ?, effective_value = ?
4541            WHERE id = ?',
4542        Bind => [
4543            \$Param{Comments}, \${ $Param{EffectiveValueStrg} }, \$DeploymentID,
4544        ],
4545    );
4546
4547    # Remove previous user deployments (if any).
4548    for my $DeploymentID (@UserDeploymentIDs) {
4549        $Self->DeploymentDelete(
4550            DeploymentID => $DeploymentID,
4551        );
4552    }
4553
4554    if ( $Param{TargetUserID} ) {
4555        $Kernel::OM->Get('Kernel::System::Cache')->Delete(
4556            Type => 'SysConfigDeployment',
4557            Key  => 'DeploymentUserList',
4558        );
4559    }
4560    else {
4561        $Kernel::OM->Get('Kernel::System::Cache')->Delete(
4562            Type => 'SysConfigDeployment',
4563            Key  => 'DeploymentList',
4564        );
4565        $Kernel::OM->Get('Kernel::System::Cache')->Delete(
4566            Type => 'SysConfigDeployment',
4567            Key  => 'DeploymentGetLast',
4568        );
4569    }
4570
4571    return $DeploymentID;
4572}
4573
4574=head2 DeploymentGet()
4575
4576Gets deployment information.
4577
4578    my %Deployment = $SysConfigDBObject->DeploymentGet(
4579        DeploymentID => 123,
4580        Valid        => 1,      # optional (this is deprecated and will be removed in next mayor release).
4581    );
4582
4583Returns:
4584
4585    %Deployment = (
4586        DeploymentID       => 123,
4587        Comments           => 'Some Comments',
4588        EffectiveValueStrg => $SettingEffectiveValues,      # string with the value of all settings,
4589                                                            #   as seen in the Perl configuration file.
4590        TargetUserID       => 123,                          # optional (only in case of user specific deployments).
4591        CreateTime         => "2016-05-29 11:04:04",
4592        CreateBy           => 123,
4593    );
4594
4595=cut
4596
4597sub DeploymentGet {
4598    my ( $Self, %Param ) = @_;
4599
4600    if ( !$Param{DeploymentID} ) {
4601        $Kernel::OM->Get('Kernel::System::Log')->Log(
4602            Priority => 'error',
4603            Message  => "Need DeploymentID!",
4604        );
4605
4606        return;
4607    }
4608
4609    my $CacheType = "SysConfigDeployment";
4610    my $CacheKey  = 'DeploymentGet::DeploymentID::' . $Param{DeploymentID};
4611
4612    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
4613
4614    # Return cache.
4615    my $Cache = $CacheObject->Get(
4616        Type => $CacheType,
4617        Key  => $CacheKey,
4618    );
4619
4620    return %{$Cache} if ref $Cache eq 'HASH';
4621
4622    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
4623
4624    # Get deployment from database.
4625    return if !$DBObject->Prepare(
4626        SQL => '
4627            SELECT id, comments, effective_value, user_id, create_time, create_by
4628            FROM sysconfig_deployment
4629            WHERE id = ?',
4630        Bind => [ \$Param{DeploymentID} ],
4631    );
4632
4633    my %Deployment;
4634    while ( my @Data = $DBObject->FetchrowArray() ) {
4635
4636        %Deployment = (
4637            DeploymentID       => $Data[0],
4638            Comments           => $Data[1],
4639            EffectiveValueStrg => $Data[2],
4640            TargetUserID       => $Data[3],
4641            CreateTime         => $Data[4],
4642            CreateBy           => $Data[5],
4643        );
4644    }
4645
4646    return if !%Deployment;
4647
4648    my $Valid = defined $Param{Valid} ? $Param{Valid} : 1;
4649
4650    if ( $Deployment{EffectiveValueStrg} eq 'Invalid' && $Valid ) {
4651        $Kernel::OM->Get('Kernel::System::Log')->Log(
4652            Priority => 'error',
4653            Message  => "The Deployment $Param{DeploymentID} is invalid!",
4654        );
4655        return;
4656    }
4657
4658    $CacheObject->Set(
4659        Type  => $CacheType,
4660        Key   => $CacheKey,
4661        Value => \%Deployment,
4662        TTL   => $Self->{CacheTTL},
4663    );
4664
4665    return %Deployment;
4666}
4667
4668=head2 DeploymentListGet()
4669
4670Get global deployment list with complete data.
4671
4672    my @List = $SysConfigDBObject->DeploymentListGet();
4673
4674Returns:
4675
4676    @List = (
4677        {
4678            DeploymentID       => 123,
4679            Comments           => 'Some Comments',
4680            EffectiveValueStrg => $SettingEffectiveValues,      # String with the value of all settings,
4681                                                                #   as seen in the Perl configuration file.
4682            CreateTime         => "2016-05-29 11:04:04",
4683            CreateBy           => 123,
4684        },
4685        {
4686            DeploymentID       => 456,
4687            Comments           => 'Some Comments',
4688            EffectiveValueStrg => $SettingEffectiveValues2,     # String with the value of all settings,
4689                                                                #   as seen in the Perl configuration file.
4690            CreateTime         => "2016-05-29 12:00:01",
4691            CreateBy           => 123,
4692        },
4693        # ...
4694    );
4695
4696=cut
4697
4698sub DeploymentListGet {
4699    my ( $Self, %Param ) = @_;
4700
4701    my $CacheKey = 'DeploymentList';
4702
4703    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
4704
4705    # Return cache.
4706    my $Cache = $CacheObject->Get(
4707        Type => 'SysConfigDeployment',
4708        Key  => $CacheKey,
4709    );
4710
4711    return @{$Cache} if ref $Cache eq 'ARRAY';
4712
4713    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
4714
4715    my @Data;
4716    return if !$DBObject->Prepare(
4717        SQL => '
4718            SELECT id
4719            FROM sysconfig_deployment
4720            WHERE user_id IS NULL
4721            ORDER BY id DESC',
4722    );
4723
4724    my @DeploymentIDs;
4725    while ( my @Row = $DBObject->FetchrowArray() ) {
4726        push @DeploymentIDs, $Row[0];
4727    }
4728
4729    # Get deployments.
4730    for my $ItemID (@DeploymentIDs) {
4731
4732        my %Deployment = $Self->DeploymentGet(
4733            DeploymentID => $ItemID,
4734        );
4735        push @Data, \%Deployment;
4736    }
4737
4738    $CacheObject->Set(
4739        Type  => 'SysConfigDeployment',
4740        Key   => $CacheKey,
4741        Value => \@Data,
4742        TTL   => $Self->{CacheTTL},
4743    );
4744
4745    return @Data;
4746}
4747
4748=head2 DeploymentUserList()
4749
4750Get DeploymentID -> UserID list of all user deployments.
4751
4752    my %List = $SysConfigDBObject->DeploymentUserList();
4753
4754Returns:
4755    %List = {
4756        9876 => 123,
4757        5432 => 456,
4758        # ...
4759    };
4760
4761=cut
4762
4763sub DeploymentUserList {
4764    my ( $Self, %Param ) = @_;
4765
4766    my $CacheKey = 'DeploymentUserList';
4767
4768    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
4769
4770    # Return cache.
4771    my $Cache = $CacheObject->Get(
4772        Type => 'SysConfigDeployment',
4773        Key  => $CacheKey,
4774    );
4775
4776    return @{$Cache} if ref $Cache eq 'HASH';
4777
4778    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
4779
4780    my @Data;
4781    return if !$DBObject->Prepare(
4782        SQL => '
4783            SELECT id, user_id
4784            FROM sysconfig_deployment
4785            WHERE user_id IS NOT NULL
4786            ORDER BY id DESC',
4787    );
4788
4789    my %Data;
4790    while ( my @Row = $DBObject->FetchrowArray() ) {
4791        $Data{ $Row[0] } = $Row[1];
4792    }
4793
4794    $CacheObject->Set(
4795        Type  => 'SysConfigDeployment',
4796        Key   => $CacheKey,
4797        Value => \@Data,
4798        TTL   => $Self->{CacheTTL},
4799    );
4800
4801    return %Data;
4802}
4803
4804=head2 DeploymentGetLast()
4805
4806Get last global deployment.
4807
4808    my %Deployment = $SysConfigDBObject->DeploymentGetLast();
4809
4810Returns:
4811
4812    %Deployment = (
4813        DeploymentID       => 123,
4814        Comments           => 'Some Comments',
4815        EffectiveValueStrg => $SettingEffectiveValues,      # String with the value of all settings,
4816                                                            #   as seen in the Perl configuration file.
4817        CreateTime         => "2016-05-29 11:04:04",
4818        CreateBy           => 123,
4819    );
4820
4821=cut
4822
4823sub DeploymentGetLast {
4824    my ( $Self, %Param ) = @_;
4825
4826    my $CacheKey = 'DeploymentGetLast';
4827
4828    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
4829
4830    # Return cache.
4831    my $Cache = $CacheObject->Get(
4832        Type => 'SysConfigDeployment',
4833        Key  => $CacheKey,
4834    );
4835
4836    return %{$Cache} if ref $Cache eq 'HASH';
4837
4838    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
4839
4840    my $DeploymentID;
4841    return if !$DBObject->Prepare(
4842        SQL => '
4843            SELECT MAX(id)
4844            FROM sysconfig_deployment
4845            WHERE user_id IS NULL',
4846    );
4847
4848    my @DeploymentID;
4849    while ( my @Row = $DBObject->FetchrowArray() ) {
4850        $DeploymentID = $Row[0];
4851    }
4852
4853    return if !$DeploymentID;
4854
4855    my %Deployment = $Self->DeploymentGet(
4856        DeploymentID => $DeploymentID,
4857    );
4858
4859    $CacheObject->Set(
4860        Type  => 'SysConfigDeployment',
4861        Key   => $CacheKey,
4862        Value => \%Deployment,
4863        TTL   => $Self->{CacheTTL},
4864    );
4865
4866    return %Deployment;
4867}
4868
4869=head2 DeploymentDelete()
4870
4871Delete a deployment from the database.
4872
4873    my $Success = $SysConfigDBObject->DeploymentDelete(
4874        DeploymentID => 123,
4875    );
4876
4877Returns:
4878
4879    $Success = 1;       # or false in case of an error
4880
4881=cut
4882
4883sub DeploymentDelete {
4884    my ( $Self, %Param ) = @_;
4885
4886    if ( !$Param{DeploymentID} ) {
4887        $Kernel::OM->Get('Kernel::System::Log')->Log(
4888            Priority => 'error',
4889            Message  => 'Need DeploymentID!',
4890        );
4891        return;
4892    }
4893
4894    my %Deployment = $Self->DeploymentGet(
4895        DeploymentID => $Param{DeploymentID},
4896        Valid        => 0,
4897    );
4898
4899    return 1 if !%Deployment;
4900
4901    # Delete deployment from the data base.
4902    return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
4903        SQL  => 'DELETE FROM sysconfig_deployment WHERE id = ?',
4904        Bind => [ \$Param{DeploymentID} ],
4905    );
4906
4907    $Kernel::OM->Get('Kernel::System::Cache')->Delete(
4908        Type => 'SysConfigDeployment',
4909        Key  => 'DeploymentGet::DeploymentID::' . $Param{DeploymentID},
4910    );
4911
4912    if ( $Deployment{TargetUserID} ) {
4913        $Kernel::OM->Get('Kernel::System::Cache')->Delete(
4914            Type => 'SysConfigDeployment',
4915            Key  => 'DeploymentUserList',
4916        );
4917    }
4918    else {
4919        $Kernel::OM->Get('Kernel::System::Cache')->Delete(
4920            Type => 'SysConfigDeployment',
4921            Key  => 'DeploymentList',
4922        );
4923        $Kernel::OM->Get('Kernel::System::Cache')->Delete(
4924            Type => 'SysConfigDeployment',
4925            Key  => 'DeploymentGetLast',
4926        );
4927    }
4928
4929    return 1;
4930}
4931
4932=head2 DeploymentLock()
4933
4934Lock global deployment to a particular user.
4935
4936    my $ExclusiveLockGUID = $SysConfigDBObject->DeploymentLock(
4937        UserID            => 123,
4938        ExclusiveLockGUID => $LockingString,    # optional (if specific GUID is needed)
4939        Force             => 1,                 # Optional, locks the deployment even if is already
4940                                                #   locked to another user, also removes locks for
4941                                                #   all settings.
4942    );
4943
4944Returns:
4945
4946    $ExclusiveLockGUID = 'SomeLockingString';   # or false in case of an error or already locked
4947
4948=cut
4949
4950sub DeploymentLock {
4951    my ( $Self, %Param ) = @_;
4952
4953    if ( !$Param{UserID} ) {
4954        $Kernel::OM->Get('Kernel::System::Log')->Log(
4955            Priority => 'error',
4956            Message  => "Need UserID!",
4957        );
4958        return;
4959    }
4960
4961    my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
4962
4963    # It's not possible to lock a deployment if a setting is locked on a valid time.
4964    my @LockedList;
4965    if ( $Param{Force} ) {
4966
4967        my $Success = $Self->DefaultSettingUnlock(
4968            UnlockAll => 1,
4969        );
4970    }
4971    else {
4972        @LockedList = $Self->DefaultSettingListGet(
4973            Locked => 1,
4974        );
4975    }
4976
4977    # Get current time.
4978    my $CurrentDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
4979
4980    if ( IsArrayRefWithData( \@LockedList ) ) {
4981        LOCKED:
4982        for my $Locked (@LockedList) {
4983
4984            next LOCKED if $Locked->{ExclusiveLockUserID} ne $Param{UserID};
4985
4986            my $LockedDateTimeObject = $Kernel::OM->Create(
4987                'Kernel::System::DateTime',
4988                ObjectParams => {
4989                    String => $Locked->{ExclusiveLockExpiryTime},
4990                },
4991            );
4992
4993            my $IsLocked = $LockedDateTimeObject > $CurrentDateTimeObject ? 1 : 0;
4994
4995            if ($IsLocked) {
4996                $Kernel::OM->Get('Kernel::System::Log')->Log(
4997                    Priority => 'error',
4998                    Message =>
4999                        "It's not possible to lock a deployment if a setting is currently locked ($Locked->{Name}).",
5000                );
5001                return;
5002            }
5003        }
5004    }
5005
5006    if ( defined $Param{ExclusiveLockGUID} && length $Param{ExclusiveLockGUID} != 32 ) {
5007        $Kernel::OM->Get('Kernel::System::Log')->Log(
5008            Priority => 'error',
5009            Message  => "ExclusiveLockGUID is invalid",
5010        );
5011        return;
5012    }
5013
5014    if ( $Param{Force} ) {
5015        my $Success = $Self->DeploymentUnlock(
5016            All => 1,
5017        );
5018    }
5019
5020    # Check if it's already locked.
5021    return if $Self->DeploymentIsLocked();
5022
5023    # If its not locked use or create a new locking string.
5024    my $ExclusiveLockGUID = $Param{ExclusiveLockGUID} || $Kernel::OM->Get('Kernel::System::Main')->GenerateRandomString(
5025        Length => 32,
5026    );
5027
5028    my $DeploymentExpireTimeMinutes = 5;
5029
5030    # Set expire time.
5031    $DateTimeObject->Add(
5032        Minutes => $DeploymentExpireTimeMinutes,
5033    );
5034    my $ExpiryTime = $DateTimeObject->ToString();
5035
5036    my $SQL = '
5037        INSERT INTO sysconfig_deployment_lock
5038            ( exclusive_lock_guid, exclusive_lock_user_id, exclusive_lock_expiry_time )
5039        VALUES
5040            ( ?, ?, ? )';
5041
5042    my @Bind = (
5043        \$ExclusiveLockGUID, \$Param{UserID},
5044        \$ExpiryTime,
5045    );
5046
5047    # Create db record.
5048    return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
5049        SQL  => $SQL,
5050        Bind => \@Bind,
5051    );
5052
5053    return $ExclusiveLockGUID;
5054}
5055
5056=head2 DeploymentIsLocked()
5057
5058Check if global deployment is locked.
5059
5060    my $Locked = $SysConfigDBObject->DeploymentIsLocked();
5061
5062Returns:
5063
5064   $Locked = 1;     # or false if it is not locked
5065
5066=cut
5067
5068sub DeploymentIsLocked {
5069    my ( $Self, %Param ) = @_;
5070
5071    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
5072
5073    # Get deployment lock from the DB.
5074    my %DeploymentLock = $Self->_DeploymentLockGet();
5075
5076    # Return no lock if there is no locking information.
5077    return if !%DeploymentLock;
5078
5079    # Return no lock if there is no lock expiry time.
5080    return if !$DeploymentLock{ExclusiveLockExpiryTime};
5081
5082    # Deployment was locked, check if lock has expired.
5083
5084    # Get current time.
5085    my $CurrentDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
5086
5087    my $LockedDateTimeObject = $Kernel::OM->Create(
5088        'Kernel::System::DateTime',
5089        ObjectParams => {
5090            String => $DeploymentLock{ExclusiveLockExpiryTime},
5091        },
5092    );
5093
5094    my $Locked = $LockedDateTimeObject > $CurrentDateTimeObject ? 1 : 0;
5095
5096    if ( !$Locked ) {
5097        $Self->DeploymentUnlock(
5098            All => 1,
5099        );
5100    }
5101
5102    return $Locked;
5103}
5104
5105=head2 DeploymentIsLockedByUser()
5106
5107Check if global deployment is locked for a determined user.
5108
5109    my $LockedByUser = $SysConfigDBObject->DeploymentIsLockedByUser(
5110        ExclusiveLockGUID => $LockingString,    # the GUID used to locking the deployment
5111        UserID            => 123,               # the user should have locked the deployment
5112    );
5113
5114Returns:
5115
5116    $LockedByUser = 'SomeLockingString';    # or false in case of not locked
5117
5118=cut
5119
5120sub DeploymentIsLockedByUser {
5121    my ( $Self, %Param ) = @_;
5122
5123    # Check needed stuff.
5124    for my $Needed (qw(UserID ExclusiveLockGUID)) {
5125        if ( !$Param{$Needed} ) {
5126            $Kernel::OM->Get('Kernel::System::Log')->Log(
5127                Priority => 'error',
5128                Message  => "Need $Needed!",
5129            );
5130            return;
5131        }
5132    }
5133
5134    return if !IsStringWithData( $Param{ExclusiveLockGUID} );
5135
5136    # ExclusiveLockGUID should not be 0
5137    return if !$Param{ExclusiveLockGUID};
5138
5139    # Get deployment lock from the DB.
5140    my %DeploymentLock = $Self->_DeploymentLockGet();
5141
5142    # Return no lock if there is no locking information.
5143    return if !%DeploymentLock;
5144
5145    # Return no lock if there is no lock expiry time.
5146    return if !$DeploymentLock{ExclusiveLockExpiryTime};
5147
5148    # Check ExclusiveLockUserID
5149    return if $DeploymentLock{ExclusiveLockUserID} ne $Param{UserID};
5150
5151    # Check ExclusiveLockGUID
5152    return if !$DeploymentLock{ExclusiveLockGUID};
5153    return if $DeploymentLock{ExclusiveLockGUID} ne $Param{ExclusiveLockGUID};
5154
5155    my $ExclusiveLockGUID = $DeploymentLock{ExclusiveLockGUID};
5156
5157    # If deployment was locked, check if lock has expired.
5158    my $CurrentDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
5159
5160    my $LockedDateTimeObject = $Kernel::OM->Create(
5161        'Kernel::System::DateTime',
5162        ObjectParams => {
5163            String => $DeploymentLock{ExclusiveLockExpiryTime},
5164        },
5165    );
5166
5167    my $Locked = $LockedDateTimeObject > $CurrentDateTimeObject ? 1 : 0;
5168
5169    # Extend the time if it was already locked for the user but time has been expired
5170    if ( !$Locked ) {
5171        $ExclusiveLockGUID = $Self->DeploymentLock(
5172            UserID            => $Param{UserID},
5173            ExclusiveLockGUID => $Param{ExclusiveLockGUID},
5174        );
5175
5176        return if !$ExclusiveLockGUID;
5177    }
5178
5179    return 1;
5180}
5181
5182=head2 DeploymentModifiedVersionList()
5183
5184Return a list of modified versions for a global deployment based on the deployment time. Limited
5185to a particular deployment or including also all previous deployments
5186
5187    my %ModifiedVersionList = $SysConfigDBObject->DeploymentModifiedVersionList(
5188        DeploymentID => 123,        # the deployment id
5189        Mode         => 'Equals'    # (optional) default 'Equals'
5190                                    #   Equals: only the settings from the given deployment
5191                                    #   GreaterThan: only the settings after the given deployment
5192                                    #   GreaterThanEquals: includes the settings of the given deployment and after
5193                                    #   SmallerThan: only the settings before the given deployment
5194                                    #   SmallerThanEquals: includes the settings of the given deployment and before
5195    );
5196
5197Returns:
5198
5199    %ModifiedVersionIDs = (
5200        123 => 'Setting1',
5201        124 => 'Setting2',
5202        125 => 'Setting3'
5203    );
5204
5205=cut
5206
5207sub DeploymentModifiedVersionList {
5208    my ( $Self, %Param ) = @_;
5209
5210    if ( !$Param{DeploymentID} ) {
5211        $Kernel::OM->Get('Kernel::System::Log')->Log(
5212            Priority => 'error',
5213            Message  => "Need DeploymentID!",
5214        );
5215        return;
5216    }
5217
5218    my $Mode        = $Param{Mode} // 'Equals';
5219    my %ModeMapping = (
5220        Equals            => '=',
5221        GreaterThan       => '>',
5222        GreaterThanEquals => '>=',
5223        SmallerThan       => '<',
5224        SmallerThanEquals => '<=',
5225    );
5226    if ( !$ModeMapping{$Mode} ) {
5227        $Self->{LogObject}->Log(
5228            Priority => 'error',
5229            Message  => "Mode $Mode is invalid!",
5230        );
5231        return;
5232    }
5233
5234    my %Deployment = $Self->DeploymentGet(
5235        DeploymentID => $Param{DeploymentID},
5236    );
5237    return if !IsHashRefWithData( \%Deployment );
5238    return if $Deployment{TargetUserID};
5239
5240    my $CreateTime = $Deployment{CreateTime} || '';
5241    return if !$CreateTime;
5242
5243    my $SQL = "
5244        SELECT max(smv.id) id, smv.name
5245        FROM sysconfig_modified_version smv
5246        WHERE smv.create_time $ModeMapping{$Mode} ?
5247        GROUP BY smv.name";
5248
5249    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
5250
5251    return if !$DBObject->Prepare(
5252        SQL  => $SQL,
5253        Bind => [ \$CreateTime, ]
5254    );
5255
5256    my %ModifiedVersionList;
5257    while ( my @Row = $DBObject->FetchrowArray() ) {
5258        $ModifiedVersionList{ $Row[0] } = $Row[1];
5259    }
5260
5261    return %ModifiedVersionList;
5262
5263}
5264
5265=head2 DeploymentUnlock()
5266
5267Unlock global deployment.
5268
5269    my $Success = $SysConfigDBObject->DeploymentUnlock(
5270        ExclusiveLockGUID => '12ad34f21b',
5271        UserID            => 123,
5272    );
5273
5274or
5275
5276    my $Success = $SysConfigDBObject->DeploymentUnlock(
5277        All  => 1,
5278    );
5279
5280Returns:
5281
5282    $Success = 1;       # or false in case of an error
5283
5284=cut
5285
5286sub DeploymentUnlock {
5287    my ( $Self, %Param ) = @_;
5288
5289    # Check needed
5290    if ( !$Param{ExclusiveLockGUID} && !$Param{UserID} && !$Param{All} ) {
5291        $Kernel::OM->Get('Kernel::System::Log')->Log(
5292            Priority => 'error',
5293            Message  => 'All or ExclusiveLockGUID and UserID are needed!',
5294        );
5295        return;
5296    }
5297
5298    if ( ( $Param{ExclusiveLockGUID} && !$Param{UserID} ) || ( $Param{UserID} && !$Param{ExclusiveLockGUID} ) ) {
5299        $Kernel::OM->Get('Kernel::System::Log')->Log(
5300            Priority => 'error',
5301            Message  => 'ExclusiveLockGUID and UserID are needed!',
5302        );
5303        return;
5304    }
5305
5306    my $SQL = 'DELETE FROM sysconfig_deployment_lock';
5307    my @Bind;
5308
5309    if ( $Param{ExclusiveLockGUID} ) {
5310        $SQL .= '
5311            WHERE exclusive_lock_guid = ?
5312                AND exclusive_lock_user_id = ?';
5313
5314        @Bind = ( \$Param{ExclusiveLockGUID}, \$Param{UserID} );
5315    }
5316
5317    # Update db record.
5318    return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
5319        SQL  => $SQL,
5320        Bind => \@Bind,
5321    );
5322
5323    return 1;
5324}
5325
5326=head2 DeploymentListCleanup()
5327
5328Removes invalid deployments from the database.
5329
5330    my $Success = $SysConfigDBObject->DeploymentListCleanup( );
5331
5332Returns:
5333
5334    $Success = 1;       # Returns 1 if all records are valid (or all invalid was removed)
5335                        # Returns -1 if there is an invalid deployment that could be in adding process
5336                        # Returns false in case of an error
5337
5338=cut
5339
5340sub DeploymentListCleanup {
5341    my ( $Self, %Param ) = @_;
5342
5343    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
5344
5345    return if !$DBObject->Prepare(
5346        SQL => '
5347            SELECT id, create_time
5348            FROM sysconfig_deployment
5349            WHERE effective_value LIKE \'Invalid%\'
5350                OR comments LIKE \'OTRSInvalid-%\'
5351            ORDER BY id DESC',
5352    );
5353
5354    my @Deployments;
5355    while ( my @Row = $DBObject->FetchrowArray() ) {
5356        my %Deployment = (
5357            DeploymentID => $Row[0],
5358            CreateTime   => $Row[1],
5359        );
5360        push @Deployments, \%Deployment;
5361    }
5362
5363    my $Success               = 1;
5364    my $CurrentDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
5365
5366    DEPLOYMENT:
5367    for my $Deployment (@Deployments) {
5368
5369        my $DeploymentDateTimeObject = $Kernel::OM->Create(
5370            'Kernel::System::DateTime',
5371            ObjectParams => {
5372                String => $Deployment->{CreateTime},
5373            },
5374        );
5375
5376        my $Delta = $CurrentDateTimeObject->Delta( DateTimeObject => $DeploymentDateTimeObject );
5377
5378        # Remove deployment only if it is old (more than 20 secs)
5379        if ( $DeploymentDateTimeObject < $CurrentDateTimeObject && $Delta >= 20 ) {
5380            $Kernel::OM->Get('Kernel::System::Log')->Log(
5381                Priority => 'error',
5382                Message  => "Deployment $Deployment->{DeploymentID} is invalid and will be removed!",
5383            );
5384            my $DeleteSuccess = $Self->DeploymentDelete(
5385                DeploymentID => $Deployment->{DeploymentID},
5386            );
5387            if ( !$DeleteSuccess ) {
5388                $Kernel::OM->Get('Kernel::System::Log')->Log(
5389                    Priority => 'error',
5390                    Message  => "Could not delete deployment $Deployment->{DeploymentID}",
5391                );
5392                $Success = 0;
5393            }
5394            last DEPLOYMENT;
5395        }
5396
5397        # Otherwise just log that there is something wrong with the deployment but do not remove it
5398        $Kernel::OM->Get('Kernel::System::Log')->Log(
5399            Priority => 'error',
5400            Message  => "Deployment $Deployment->{DeploymentID} seams to be invalid or its not fully updated",
5401        );
5402        $Success = -1;
5403    }
5404
5405    return $Success;
5406}
5407
5408=head1 PRIVATE INTERFACE
5409
5410=head2 _DeploymentLockGet()
5411
5412Get deployment lock entry.
5413
5414    my %DeploymentLock = $SysConfigDBObject->_DeploymentLockGet();
5415
5416Returns:
5417
5418    %DeploymentLock = (
5419        DeploymentLoclID        => "123",
5420        ExclusiveLockGUID       => $LockingString,
5421        ExclusiveLockUserID     => 1,
5422        ExclusiveLockExpiryTime => '2016-05-29 11:09:04',
5423        CreateTime              => "2016-05-29 11:04:04",
5424    );
5425
5426=cut
5427
5428sub _DeploymentLockGet {
5429    my ( $Self, %Param ) = @_;
5430
5431    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
5432
5433    # Get deployment lock from the DB.
5434    return if !$DBObject->Prepare(
5435        SQL => '
5436            SELECT id, exclusive_lock_guid, exclusive_lock_user_id, exclusive_lock_expiry_time
5437            FROM sysconfig_deployment_lock
5438            ORDER BY exclusive_lock_expiry_time DESC',
5439        Limit => 1,
5440    );
5441
5442    my %DeploymentLock;
5443    while ( my @Data = $DBObject->FetchrowArray() ) {
5444        $DeploymentLock{ID}                      = $Data[0];
5445        $DeploymentLock{ExclusiveLockGUID}       = $Data[1];
5446        $DeploymentLock{ExclusiveLockUserID}     = $Data[2];
5447        $DeploymentLock{ExclusiveLockExpiryTime} = $Data[3];
5448    }
5449
5450    return %DeploymentLock;
5451}
5452
5453=head2 _BulkInsert()
5454
5455Add batch entries to the DB into a given table.
5456
5457    my $Success = $SysConfigDBObject->_BulkInsert(
5458        Table   => 'table_name',    # (required) Table name
5459        Columns => [                # (required) Array of column names
5460            'column_name',
5461            ...
5462        ],
5463        Data    => [                # (required) AoA with data
5464            [
5465                'record 1',
5466                'record 2',
5467            ],
5468            [
5469                ...
5470            ],
5471            ...
5472        ],
5473    );
5474
5475=cut
5476
5477sub _BulkInsert {
5478    my ( $Self, %Param ) = @_;
5479
5480    for my $Needed (qw(Table)) {
5481        if ( !$Param{$Needed} ) {
5482            $Kernel::OM->Get('Kernel::System::Log')->Log(
5483                Priority => 'error',
5484                Message  => "Need $Needed!",
5485            );
5486            return;
5487        }
5488    }
5489
5490    for my $Parameter (qw(Columns Data)) {
5491        if ( !IsArrayRefWithData( $Param{Columns} ) ) {
5492            $Kernel::OM->Get('Kernel::System::Log')->Log(
5493                Priority => 'error',
5494                Message  => "$Parameter must be not empty array!"
5495            );
5496            return;
5497        }
5498    }
5499
5500    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
5501
5502    # Get the database type.
5503    my $DBType = $DBObject->GetDatabaseFunction('Type');
5504
5505    # Define database specific SQL for the multi-line inserts.
5506    my %DatabaseSQL;
5507
5508    my $Columns = join( ',', @{ $Param{Columns} } );
5509
5510    # Check the first array in Data and create Mask (current_timestamp is always on same place in array).
5511    my @Mask = map {
5512        ( $_ && $_ eq 'current_timestamp' )
5513            ? 'current_timestamp' : '?'
5514    } @{ $Param{Data}->[0] };
5515
5516    my $MaskedValues = join( ',', @Mask );
5517
5518    if ( $DBType eq 'oracle' ) {
5519
5520        %DatabaseSQL = (
5521            Start     => 'INSERT ALL ',
5522            FirstLine => "
5523                INTO $Param{Table} ($Columns)
5524                VALUES ( $MaskedValues ) ",
5525            NextLine => "
5526                INTO $Param{Table} (
5527                    $Columns
5528                )
5529                VALUES ( $MaskedValues ) ",
5530            End => 'SELECT * FROM DUAL',
5531        );
5532    }
5533    else {
5534        %DatabaseSQL = (
5535            Start => "
5536                INSERT INTO $Param{Table} (
5537                    $Columns
5538                )",
5539            FirstLine => "VALUES ( $MaskedValues )",
5540            NextLine  => ", ( $MaskedValues ) ",
5541            End       => '',
5542        );
5543    }
5544
5545    my $SQL = '';
5546    my @Bind;
5547
5548    my $Count = 0;
5549
5550    RECORD:
5551    for my $Entry ( @{ $Param{Data} } ) {
5552
5553        $Count++;
5554
5555        if ( !IsArrayRefWithData($Entry) ) {
5556            $Kernel::OM->Get('Kernel::System::Log')->Log(
5557                Priority => 'error',
5558                Message  => "Data contains empty array, skipped!"
5559            );
5560            next RECORD;
5561        }
5562
5563        if ( scalar @{$Entry} != scalar @{ $Param{Columns} } ) {
5564            $Kernel::OM->Get('Kernel::System::Log')->Log(
5565                Priority => 'error',
5566                Message  => "Array size doesn't match Columns size, skipped!"
5567            );
5568            my $Col = scalar @{ $Param{Columns} };
5569            next RECORD;
5570        }
5571
5572        # Now the article entry is validated and can be added to sql.
5573        if ( !$SQL ) {
5574            $SQL = $DatabaseSQL{Start} . $DatabaseSQL{FirstLine};
5575        }
5576        else {
5577            $SQL .= $DatabaseSQL{NextLine};
5578        }
5579
5580        BIND:
5581        for my $Item ( @{$Entry} ) {
5582
5583            next BIND if ( $Item && $Item eq 'current_timestamp' );    # Already included in the SQL part.
5584
5585            push @Bind, \$Item;
5586        }
5587
5588        # Check the length of the SQL string
5589        # (some databases only accept SQL strings up to 4k,
5590        # so we want to stay safe here with just 3500 bytes).
5591        if ( length $SQL > 3500 || $Count == scalar @{ $Param{Data} } ) {
5592
5593            # Add the end line to sql string.
5594            $SQL .= $DatabaseSQL{End};
5595
5596            # Insert multiple entries.
5597            return if !$DBObject->Do(
5598                SQL  => $SQL,
5599                Bind => \@Bind,
5600            );
5601
5602            # Reset the SQL string and the Bind array.
5603            $SQL  = '';
5604            @Bind = ();
5605        }
5606    }
5607
5608    return 1;
5609}
5610
5611=head2 _GetUID()
5612
5613Generates a unique identifier.
5614
5615    my $UID = $TicketNumberObject->_GetUID();
5616
5617Returns:
5618
5619    my $UID = 14906327941360ed8455f125d0450277;
5620
5621=cut
5622
5623sub _GetUID {
5624    my ( $Self, %Param ) = @_;
5625
5626    my $NodeID = $Kernel::OM->Get('Kernel::Config')->Get('NodeID') || 1;
5627    my ( $Seconds, $Microseconds ) = Time::HiRes::gettimeofday();
5628    my $ProcessID = $$;
5629
5630    my $CounterUID = $ProcessID . $Seconds . $Microseconds . $NodeID;
5631
5632    my $RandomString = $Kernel::OM->Get('Kernel::System::Main')->GenerateRandomString(
5633        Length     => 32 - length $CounterUID,
5634        Dictionary => [ 0 .. 9, 'a' .. 'f' ],    # hexadecimal
5635    );
5636
5637    $CounterUID .= $RandomString;
5638
5639    return $CounterUID;
5640}
5641
56421;
5643
5644=head1 TERMS AND CONDITIONS
5645
5646This software is part of the OTRS project (L<https://otrs.org/>).
5647
5648This software comes with ABSOLUTELY NO WARRANTY. For details, see
5649the enclosed file COPYING for license information (GPL). If you
5650did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
5651
5652=cut
5653