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## nofilter(TidyAll::Plugin::OTRS::Perl::LayoutObject) 9package Kernel::System::SysConfig; 10 11use strict; 12use warnings; 13 14use Time::HiRes(); 15use utf8; 16 17use Kernel::System::VariableCheck qw(:all); 18use Kernel::Language qw(Translatable); 19use Kernel::Config; 20 21use parent qw(Kernel::System::AsynchronousExecutor); 22 23our @ObjectDependencies = ( 24 'Kernel::Config', 25 'Kernel::Language', 26 'Kernel::Output::HTML::SysConfig', 27 'Kernel::System::Cache', 28 'Kernel::System::Log', 29 'Kernel::System::Main', 30 'Kernel::System::Package', 31 'Kernel::System::Storable', 32 'Kernel::System::SysConfig::DB', 33 'Kernel::System::SysConfig::Migration', 34 'Kernel::System::SysConfig::XML', 35 'Kernel::System::User', 36 'Kernel::System::YAML', 37); 38 39=head1 NAME 40 41Kernel::System::SysConfig - Functions to manage system configuration settings. 42 43=head1 DESCRIPTION 44 45All functions to manage system configuration settings. 46 47=head1 PUBLIC INTERFACE 48 49=head2 new() 50 51Don't use the constructor directly, use the ObjectManager instead: 52 53 my $SysConfigObject = $Kernel::OM->Get('Kernel::System::SysConfig'); 54 55=cut 56 57## no critic (StringyEval) 58 59sub new { 60 my ( $Type, %Param ) = @_; 61 62 # allocate new hash for object 63 my $Self = {}; 64 bless( $Self, $Type ); 65 66 $Self->{ConfigObject} = $Kernel::OM->Get('Kernel::Config'); 67 68 # get home directory 69 $Self->{Home} = $Self->{ConfigObject}->Get('Home'); 70 71 # set utf-8 if used 72 $Self->{utf8} = 1; 73 $Self->{FileMode} = ':utf8'; 74 75 $Self->{ConfigDefaultObject} = Kernel::Config->new( Level => 'Default' ); 76 $Self->{ConfigObject} = Kernel::Config->new( Level => 'First' ); 77 $Self->{ConfigClearObject} = Kernel::Config->new( Level => 'Clear' ); 78 79 # Load base files. 80 my $BaseDir = $Self->{Home} . '/Kernel/System/SysConfig/Base/'; 81 82 my $MainObject = $Kernel::OM->Get('Kernel::System::Main'); 83 84 FILENAME: 85 for my $Filename (qw(Framework.pm OTRSBusiness.pm)) { 86 my $BaseFile = $BaseDir . $Filename; 87 next FILENAME if !-e $BaseFile; 88 89 $BaseFile =~ s{\A.*\/(.+?).pm\z}{$1}xms; 90 my $BaseClassName = "Kernel::System::SysConfig::Base::$BaseFile"; 91 if ( !$MainObject->RequireBaseClass($BaseClassName) ) { 92 $Self->FatalDie( 93 Message => "Could not load class $BaseClassName.", 94 ); 95 } 96 97 } 98 99 return $Self; 100} 101 102=head2 SettingGet() 103 104Get SysConfig setting attributes. 105 106 my %Setting = $SysConfigObject->SettingGet( 107 Name => 'Setting::Name', # (required) Setting name 108 Default => 1, # (optional) Returns the default setting attributes only 109 ModifiedID => '123', # (optional) Get setting value for given ModifiedID. 110 TargetUserID => 1, # (optional) Get setting value for specific user. 111 Deployed => 1, # (optional) Get deployed setting value. Default 0. 112 OverriddenInXML => 1, # (optional) Consider changes made in perl files. Default 0. 113 Translate => 1, # (optional) Translate translatable strings in EffectiveValue. Default 0. 114 NoLog => 1, # (optional) Do not log error if a setting does not exist. 115 NoCache => 1, # (optional) Do not create cache. 116 UserID => 1, # Required only if OverriddenInXML is set. 117 ); 118 119Returns: 120 121 %Setting = ( 122 DefaultID => 123, 123 ModifiedID => 456, # optional 124 Name => "ProductName", 125 Description => "Defines the name of the application ...", 126 Navigation => "ASimple::Path::Structure", 127 IsInvisible => 1, # 1 or 0 128 IsReadonly => 0, # 1 or 0 129 IsRequired => 1, # 1 or 0 130 IsModified => 1, # 1 or 0 131 IsValid => 1, # 1 or 0 132 HasConfigLevel => 200, 133 UserModificationPossible => 0, # 1 or 0 134 UserModificationActive => 0, # 1 or 0 135 UserPreferencesGroup => 'Advanced', # optional 136 XMLContentRaw => "The XML structure as it is on the config file", 137 XMLContentParsed => "XML parsed to Perl", 138 XMLFilename => "Framework.xml", 139 EffectiveValue => "Product 6", 140 IsDirty => 1, # 1 or 0 141 ExclusiveLockGUID => 'A32CHARACTERLONGSTRINGFORLOCKING', 142 ExclusiveLockUserID => 1, 143 ExclusiveLockExpiryTime => '2016-05-29 11:09:04', 144 CreateTime => "2016-05-29 11:04:04", 145 CreateBy => 1, 146 ChangeTime => "2016-05-29 11:04:04", 147 ChangeBy => 1, 148 DefaultValue => 'Old default value', 149 OverriddenFileName => '/opt/otrs/Kernel/Config/Files/ZZZ.pm', 150 ); 151 152=cut 153 154sub SettingGet { 155 my ( $Self, %Param ) = @_; 156 157 # Check needed stuff. 158 if ( !$Param{Name} ) { 159 $Kernel::OM->Get('Kernel::System::Log')->Log( 160 Priority => 'error', 161 Message => 'Need Name!', 162 ); 163 return; 164 } 165 166 if ( $Param{OverriddenInXML} && !$Param{UserID} ) { 167 $Kernel::OM->Get('Kernel::System::Log')->Log( 168 Priority => 'error', 169 Message => 'UserID is needed when OverriddenInXML is set!', 170 ); 171 return; 172 } 173 174 $Param{Translate} //= 0; # don't translate by default 175 176 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 177 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 178 179 # Get default setting. 180 my %Setting = $SysConfigDBObject->DefaultSettingGet( 181 Name => $Param{Name}, 182 NoCache => $Param{NoCache}, 183 ); 184 185 # setting was not found 186 if ( !%Setting ) { 187 188 # do not log an error if parameter NoLog is true 189 if ( !$Param{NoLog} ) { 190 $Kernel::OM->Get('Kernel::System::Log')->Log( 191 Priority => 'error', 192 Message => "Setting $Param{Name} is invalid!", 193 ); 194 } 195 196 return; 197 } 198 199 $Setting{DefaultValue} = $Setting{EffectiveValue}; 200 201 # Return default setting if specified (otherwise continue with modified setting). 202 if ( $Param{Default} ) { 203 return %Setting; 204 } 205 206 # Check if modified setting available 207 my %ModifiedSetting; 208 if ( $Param{ModifiedID} ) { 209 210 # Get settings with given ModifiedID. 211 %ModifiedSetting = $SysConfigDBObject->ModifiedSettingGet( 212 ModifiedID => $Param{ModifiedID}, 213 IsGlobal => 1, 214 NoCache => $Param{NoCache}, 215 ); 216 217 # prevent using both parameters. 218 $Param{Deployed} = undef; 219 $Param{TargetUserID} = undef; 220 } 221 else { 222 223 # Get latest modified setting. 224 %ModifiedSetting = $SysConfigDBObject->ModifiedSettingGet( 225 Name => $Param{Name}, 226 IsGlobal => 1, 227 NoCache => $Param{NoCache}, 228 ); 229 } 230 231 if ( $Param{TargetUserID} ) { 232 233 if ( IsHashRefWithData( \%ModifiedSetting ) ) { 234 235 # There is modified setting, but we need last deployed version. 236 %ModifiedSetting = $SysConfigDBObject->ModifiedSettingVersionGetLast( 237 Name => $ModifiedSetting{Name}, 238 ); 239 240 # Use global (deployed) modified settings as "default" (if any) 241 if ( IsHashRefWithData( \%ModifiedSetting ) ) { 242 %Setting = ( 243 %Setting, 244 %ModifiedSetting, 245 ); 246 } 247 } 248 249 # get user specific settings 250 %ModifiedSetting = $SysConfigDBObject->ModifiedSettingGet( 251 Name => $Param{Name}, 252 TargetUserID => $Param{TargetUserID}, 253 ); 254 255 # prevent using both parameters. 256 $Param{Deployed} = undef; 257 } 258 259 if ( $Param{Deployed} ) { 260 261 # get the previous deployed state of this setting 262 my %SettingDeployed = $SysConfigDBObject->ModifiedSettingVersionGetLast( 263 Name => $Setting{Name}, 264 ); 265 266 if ( !IsHashRefWithData( \%SettingDeployed ) ) { 267 268 # if this setting was never deployed before, get the default state 269 270 # Get default version. 271 %SettingDeployed = $SysConfigDBObject->DefaultSettingGet( 272 DefaultID => $Setting{DefaultID}, 273 NoCache => $Param{NoCache}, 274 ); 275 } 276 277 if ( IsHashRefWithData( \%SettingDeployed ) ) { 278 %Setting = ( 279 %Setting, 280 %SettingDeployed 281 ); 282 } 283 } 284 285 # default 286 $Setting{IsModified} = 0; 287 288 if ( IsHashRefWithData( \%ModifiedSetting ) ) { 289 290 my $IsModified = DataIsDifferent( 291 Data1 => \$Setting{EffectiveValue}, 292 Data2 => \$ModifiedSetting{EffectiveValue}, 293 ) || 0; 294 295 $IsModified ||= $ModifiedSetting{IsValid} != $Setting{IsValid}; 296 $IsModified ||= $ModifiedSetting{UserModificationActive} != $Setting{UserModificationActive}; 297 298 $Setting{IsModified} = $IsModified ? 1 : 0; 299 300 if ( !$Param{Deployed} ) { 301 302 # Update setting attributes. 303 ATTRIBUTE: 304 for my $Attribute ( 305 qw(ModifiedID IsValid UserModificationActive EffectiveValue IsDirty 306 CreateTime CreateBy ChangeTime ChangeBy SettingUID 307 ) 308 ) 309 { 310 next ATTRIBUTE if !defined $ModifiedSetting{$Attribute}; 311 312 $Setting{$Attribute} = $ModifiedSetting{$Attribute}; 313 } 314 } 315 } 316 317 if ( $Param{OverriddenInXML} ) { 318 319 # get the previous deployed state of this setting 320 my %SettingDeployed = $SysConfigDBObject->ModifiedSettingVersionGetLast( 321 Name => $Setting{Name}, 322 ); 323 324 if ( !IsHashRefWithData( \%SettingDeployed ) ) { 325 326 # if this setting was never deployed before, get the default state 327 328 # Get default version. 329 %SettingDeployed = $SysConfigDBObject->DefaultSettingGet( 330 DefaultID => $Setting{DefaultID}, 331 NoCache => $Param{NoCache}, 332 ); 333 } 334 335 # Get real EffectiveValue - EffectiveValue from DB could be modified in the ZZZAbc.pm file. 336 my $LoadedEffectiveValue = $Self->GlobalEffectiveValueGet( 337 SettingName => $Setting{Name}, 338 ); 339 340 my $IsOverridden = DataIsDifferent( 341 Data1 => $SettingDeployed{EffectiveValue} // {}, 342 Data2 => $LoadedEffectiveValue // {}, 343 ); 344 345 if ($IsOverridden) { 346 $Setting{OverriddenFileName} = $Self->OverriddenFileNameGet( 347 SettingName => $Setting{Name}, 348 EffectiveValue => $Setting{EffectiveValue}, 349 UserID => $Param{UserID}, 350 ); 351 352 # Update EffectiveValue. 353 if ( $Setting{OverriddenFileName} ) { 354 $Setting{EffectiveValue} = $LoadedEffectiveValue; 355 } 356 } 357 } 358 359 if ( $Param{Translate} ) { 360 361 if (%ModifiedSetting) { 362 $Setting{XMLContentParsed}->{Value} = $Self->SettingModifiedXMLContentParsedGet( 363 ModifiedSetting => { 364 EffectiveValue => $Setting{EffectiveValue}, 365 }, 366 DefaultSetting => { 367 XMLContentParsed => $Setting{XMLContentParsed}, 368 }, 369 ); 370 } 371 372 # Update EffectiveValue with translated strings 373 $Setting{EffectiveValue} = $Self->SettingEffectiveValueGet( 374 Value => $Setting{XMLContentParsed}->{Value}, 375 Translate => 1, 376 ); 377 378 $Setting{Description} = $Kernel::OM->Get('Kernel::Language')->Translate( 379 $Setting{Description}, 380 ); 381 } 382 383 # If setting is overridden in the perl file, using the "delete" statement, EffectiveValue is undef. 384 $Setting{EffectiveValue} //= ''; 385 386 # Return updated default. 387 return %Setting; 388} 389 390=head2 SettingUpdate() 391 392Update an existing SysConfig Setting. 393 394 my %Result = $SysConfigObject->SettingUpdate( 395 Name => 'Setting::Name', # (required) setting name 396 IsValid => 1, # (optional) 1 or 0, modified 0 397 EffectiveValue => $SettingEffectiveValue, # (optional) 398 UserModificationActive => 0, # (optional) 1 or 0, modified 0 399 TargetUserID => 2, # (optional) ID of the user for which the modified setting is meant, 400 # leave it undef for global changes. 401 ExclusiveLockGUID => $LockingString, # the GUID used to locking the setting 402 UserID => 1, # (required) UserID 403 NoValidation => 1, # (optional) no value type validation. 404 ); 405 406Returns: 407 408 %Result = ( 409 Success => 1, # or false in case of an error 410 Error => undef, # error message 411 ); 412 413=cut 414 415sub SettingUpdate { 416 my ( $Self, %Param ) = @_; 417 418 for my $Needed (qw(Name UserID)) { 419 if ( !$Param{$Needed} ) { 420 $Kernel::OM->Get('Kernel::System::Log')->Log( 421 Priority => 'error', 422 Message => "Need $Needed!", 423 ); 424 425 return; 426 } 427 } 428 429 if ( !$Param{TargetUserID} && !$Param{ExclusiveLockGUID} ) { 430 $Kernel::OM->Get('Kernel::System::Log')->Log( 431 Priority => 'error', 432 Message => "Need TargetUserID or ExclusiveLockGUID!", 433 ); 434 } 435 436 my %Result = ( 437 Success => 1, 438 ); 439 440 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 441 442 # Get default setting 443 my %Setting = $SysConfigDBObject->DefaultSettingGet( 444 Name => $Param{Name}, 445 ); 446 447 # Make sure that required settings can't be disabled. 448 if ( $Setting{IsRequired} ) { 449 $Param{IsValid} = 1; 450 } 451 452 # Return if setting does not exists. 453 if ( !%Setting ) { 454 $Kernel::OM->Get('Kernel::System::Log')->Log( 455 Priority => 'error', 456 Message => "Setting $Param{Name} does not exists!", 457 ); 458 459 %Result = ( 460 Success => 0, 461 Error => $Kernel::OM->Get('Kernel::Language')->Translate( 462 "Setting %s does not exists!", 463 $Param{Name}, 464 ), 465 ); 466 return %Result; 467 } 468 469 # Default should be locked (for global updates). 470 my $LockedByUser; 471 if ( !$Param{TargetUserID} ) { 472 $LockedByUser = $SysConfigDBObject->DefaultSettingIsLockedByUser( 473 DefaultID => $Setting{DefaultID}, 474 ExclusiveLockUserID => $Param{UserID}, 475 ExclusiveLockGUID => $Param{ExclusiveLockGUID}, 476 ); 477 478 if ( !$LockedByUser ) { 479 $Kernel::OM->Get('Kernel::System::Log')->Log( 480 Priority => 'error', 481 Message => "Setting $Param{Name} is not locked to this user!", 482 ); 483 484 %Result = ( 485 Success => 0, 486 Error => $Kernel::OM->Get('Kernel::Language')->Translate( 487 "Setting %s is not locked to this user!", 488 $Param{Name}, 489 ), 490 ); 491 return %Result; 492 } 493 } 494 495 # Do not perform EffectiveValueCheck if user wants to disable the setting. 496 if ( $Param{IsValid} ) { 497 498 # Effective value must match in structure to the default and individual values should be 499 # valid according to its value types. 500 my %EffectiveValueCheck = $Self->SettingEffectiveValueCheck( 501 XMLContentParsed => $Setting{XMLContentParsed}, 502 EffectiveValue => $Param{EffectiveValue}, 503 NoValidation => $Param{NoValidation} //= 0, 504 UserID => $Param{UserID}, 505 ); 506 507 if ( !$EffectiveValueCheck{Success} ) { 508 my $Error = $EffectiveValueCheck{Error} || 'Unknown error!'; 509 510 $Kernel::OM->Get('Kernel::System::Log')->Log( 511 Priority => 'error', 512 Message => "EffectiveValue is invalid! $Error", 513 ); 514 515 %Result = ( 516 Success => 0, 517 Error => $Kernel::OM->Get('Kernel::Language')->Translate( 518 "Setting value is not valid!", 519 ), 520 ); 521 return %Result; 522 } 523 } 524 525 # Get modified setting (if any). 526 my %ModifiedSetting = $SysConfigDBObject->ModifiedSettingGet( 527 Name => $Param{Name}, 528 IsGlobal => 1, 529 ); 530 531 if ( !defined $Param{EffectiveValue} ) { 532 533 # In the case that we want only to enable/disable setting, 534 # old effective value will be preserved. 535 $Param{EffectiveValue} = $ModifiedSetting{EffectiveValue} // $Setting{EffectiveValue}; 536 } 537 538 my $UserModificationActive = $Param{UserModificationActive} //= $Setting{UserModificationActive}; 539 540 if ( $Param{TargetUserID} ) { 541 if ( IsHashRefWithData( \%ModifiedSetting ) ) { 542 543 # override default setting with global modified setting 544 %Setting = ( 545 %Setting, 546 %ModifiedSetting, 547 ); 548 } 549 550 %ModifiedSetting = $SysConfigDBObject->ModifiedSettingGet( 551 Name => $Param{Name}, 552 TargetUserID => $Param{TargetUserID}, 553 ); 554 555 $UserModificationActive = undef; # prevent setting this value 556 557 my %GlobalSetting = $Self->SettingGet( 558 Name => $Param{Name}, 559 OverriddenInXML => 1, 560 UserID => 1, 561 ); 562 563 $Setting{EffectiveValue} = $GlobalSetting{EffectiveValue}; 564 } 565 566 # Add new modified setting (if there wasn't). 567 if ( !%ModifiedSetting ) { 568 569 # Check if provided EffectiveValue is same as in Default 570 my $IsDifferent = DataIsDifferent( 571 Data1 => \$Setting{EffectiveValue}, 572 Data2 => \$Param{EffectiveValue}, 573 ) || 0; 574 575 if ( defined $Param{IsValid} ) { 576 $IsDifferent ||= $Setting{IsValid} != $Param{IsValid}; 577 } 578 579 $IsDifferent ||= $Setting{UserModificationActive} != $Param{UserModificationActive}; 580 581 if ($IsDifferent) { 582 583 my $ModifiedID = $SysConfigDBObject->ModifiedSettingAdd( 584 DefaultID => $Setting{DefaultID}, 585 Name => $Setting{Name}, 586 IsValid => $Param{IsValid} //= $Setting{IsValid}, 587 EffectiveValue => $Param{EffectiveValue}, 588 UserModificationActive => $UserModificationActive, 589 TargetUserID => $Param{TargetUserID}, 590 ExclusiveLockGUID => $Param{ExclusiveLockGUID}, 591 UserID => $Param{UserID}, 592 ); 593 if ( !$ModifiedID ) { 594 $Kernel::OM->Get('Kernel::System::Log')->Log( 595 Priority => 'error', 596 Message => "Could not add modified setting!", 597 ); 598 %Result = ( 599 Success => 0, 600 Error => $Kernel::OM->Get('Kernel::Language')->Translate( 601 "Could not add modified setting!", 602 ), 603 ); 604 return %Result; 605 } 606 } 607 } 608 else { 609 610 # Check if provided EffectiveValue is same as in last modified EffectiveValue 611 my $IsDifferent = DataIsDifferent( 612 Data1 => \$ModifiedSetting{EffectiveValue}, 613 Data2 => \$Param{EffectiveValue}, 614 ) || 0; 615 616 if ( defined $Param{IsValid} ) { 617 $IsDifferent ||= $ModifiedSetting{IsValid} != $Param{IsValid}; 618 } 619 620 $IsDifferent ||= $ModifiedSetting{UserModificationActive} != $Param{UserModificationActive}; 621 622 if ($IsDifferent) { 623 624 my %ModifiedSettingVersion = $SysConfigDBObject->ModifiedSettingVersionGetLast( 625 Name => $ModifiedSetting{Name}, 626 ); 627 628 my $EffectiveValueModifiedSinceDeployment = 1; 629 if ( $ModifiedSettingVersion{ModifiedVersionID} ) { 630 631 my %ModifiedSettingLastDeployed = $SysConfigDBObject->ModifiedSettingVersionGet( 632 ModifiedVersionID => $ModifiedSettingVersion{ModifiedVersionID}, 633 ); 634 635 $EffectiveValueModifiedSinceDeployment = DataIsDifferent( 636 Data1 => \$ModifiedSettingLastDeployed{EffectiveValue}, 637 Data2 => \$Param{EffectiveValue}, 638 ) || 0; 639 640 if ( defined $Param{IsValid} ) { 641 $EffectiveValueModifiedSinceDeployment ||= $ModifiedSettingLastDeployed{IsValid} != $Param{IsValid}; 642 } 643 644 $EffectiveValueModifiedSinceDeployment 645 ||= $ModifiedSettingLastDeployed{UserModificationActive} != $Param{UserModificationActive}; 646 647 } 648 elsif ( !IsHashRefWithData( \%ModifiedSettingVersion ) ) { 649 $EffectiveValueModifiedSinceDeployment = DataIsDifferent( 650 Data1 => \$Setting{EffectiveValue}, 651 Data2 => \$Param{EffectiveValue}, 652 ) || 0; 653 654 if ( defined $Param{IsValid} ) { 655 $EffectiveValueModifiedSinceDeployment ||= $Setting{IsValid} != $Param{IsValid}; 656 } 657 658 $EffectiveValueModifiedSinceDeployment 659 ||= $Setting{UserModificationActive} != $Param{UserModificationActive}; 660 } 661 662 # Update the existing modified setting. 663 my $Success = $SysConfigDBObject->ModifiedSettingUpdate( 664 ModifiedID => $ModifiedSetting{ModifiedID}, 665 DefaultID => $Setting{DefaultID}, 666 Name => $Setting{Name}, 667 IsValid => $Param{IsValid} //= $ModifiedSetting{IsValid}, 668 EffectiveValue => $Param{EffectiveValue}, 669 UserModificationActive => $UserModificationActive, 670 TargetUserID => $Param{TargetUserID} //= $ModifiedSetting{TargetUserID}, 671 ExclusiveLockGUID => $Param{ExclusiveLockGUID}, 672 UserID => $Param{UserID}, 673 IsDirty => $EffectiveValueModifiedSinceDeployment ? 1 : 0, 674 ); 675 if ( !$Success ) { 676 $Kernel::OM->Get('Kernel::System::Log')->Log( 677 Priority => 'error', 678 Message => "Could not update modified setting!", 679 ); 680 %Result = ( 681 Success => 0, 682 Error => $Kernel::OM->Get('Kernel::Language')->Translate( 683 "Could not update modified setting!", 684 ), 685 ); 686 return %Result; 687 } 688 } 689 } 690 691 # When a setting is set to invalid all modified settings for users has to be removed. 692 if ( 693 !$Param{IsValid} 694 && !$Param{TargetUserID} 695 && $Self->can('UserSettingValueDelete') # OTRS Business Solution™ 696 ) 697 { 698 $Self->UserSettingValueDelete( 699 Name => $Setting{Name}, 700 ModifiedID => 'All', 701 UserID => $Param{UserID}, 702 ); 703 } 704 705 if ( !$Param{TargetUserID} ) { 706 707 # Unlock setting so it can be locked again afterwards. 708 my $Success = $SysConfigDBObject->DefaultSettingUnlock( 709 DefaultID => $Setting{DefaultID}, 710 ); 711 if ( !$Success ) { 712 $Kernel::OM->Get('Kernel::System::Log')->Log( 713 Priority => 'error', 714 Message => "Setting could not be unlocked!", 715 ); 716 %Result = ( 717 Success => 0, 718 Error => $Kernel::OM->Get('Kernel::Language')->Translate( 719 "Setting could not be unlocked!", 720 ), 721 ); 722 return %Result; 723 } 724 } 725 726 return %Result; 727} 728 729=head2 SettingLock() 730 731Lock setting(s) to the particular user. 732 733 my $ExclusiveLockGUID = $SysConfigObject->SettingLock( 734 DefaultID => 1, # the ID of the setting that needs to be locked 735 # or 736 Name => 'SettingName', # the Name of the setting that needs to be locked 737 # or 738 LockAll => 1, # system locks all settings 739 Force => 1, # (optional) Force locking (do not check if it's already locked by another user). Default: 0. 740 UserID => 1, # (required) 741 ); 742 743Returns: 744 745 $ExclusiveLockGUID = 'azzHab72wIlAXDrxHexsI5aENsESxAO7'; # Setting locked 746 747 or 748 749 $ExclusiveLockGUID = undef; # Not locked 750 751=cut 752 753sub SettingLock { 754 my ( $Self, %Param ) = @_; 755 756 return $Kernel::OM->Get('Kernel::System::SysConfig::DB')->DefaultSettingLock(%Param); 757} 758 759=head2 SettingUnlock() 760 761Unlock particular or all Setting(s). 762 763 my $Success = $SysConfigObject->SettingUnlock( 764 DefaultID => 1, # the ID of the setting that needs to be unlocked 765 # or 766 Name => 'SettingName', # the name of the setting that needs to be locked 767 # or 768 UnlockAll => 1, # unlock all settings 769 ); 770 771Returns: 772 773 $Success = 1; 774 775=cut 776 777sub SettingUnlock { 778 my ( $Self, %Param ) = @_; 779 780 return $Kernel::OM->Get('Kernel::System::SysConfig::DB')->DefaultSettingUnlock(%Param); 781} 782 783=head2 SettingLockCheck() 784 785Check setting lock status. 786 787 my %Result = $SysConfigObject->SettingLockCheck( 788 DefaultID => 1, # the ID of the setting that needs to be checked 789 ExclusiveLockGUID => 1, # lock GUID 790 ExclusiveLockUserID => 1, # UserID 791 ); 792 793Returns: 794 795 %Result = ( 796 Locked => 1, # lock status; 797 # 0 - unlocked 798 # 1 - locked to another user 799 # 2 - locked to provided user 800 User => { # User data, provided only if Locked = 1 801 UserLogin => ..., 802 UserFirstname => ..., 803 UserLastname => ..., 804 ... 805 }, 806 ); 807 808=cut 809 810sub SettingLockCheck { 811 my ( $Self, %Param ) = @_; 812 813 for my $Needed (qw(DefaultID ExclusiveLockGUID ExclusiveLockUserID)) { 814 if ( !$Param{$Needed} ) { 815 $Kernel::OM->Get('Kernel::System::Log')->Log( 816 Priority => 'error', 817 Message => "Need $Needed!", 818 ); 819 return; 820 } 821 } 822 823 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 824 825 my %Result = ( 826 Locked => 0, 827 ); 828 829 my $LockedByUser = $SysConfigDBObject->DefaultSettingIsLockedByUser( 830 DefaultID => $Param{DefaultID}, 831 ExclusiveLockUserID => $Param{ExclusiveLockUserID}, 832 ExclusiveLockGUID => $Param{ExclusiveLockGUID}, 833 ); 834 835 if ($LockedByUser) { 836 837 # setting locked to the provided user 838 $Result{Locked} = 2; 839 } 840 else { 841 # check if setting is locked to another user 842 my $UserID = $SysConfigDBObject->DefaultSettingIsLocked( 843 DefaultID => $Param{DefaultID}, 844 GetLockUserID => 1, 845 ); 846 847 if ($UserID) { 848 849 # get user data 850 $Result{Locked} = 1; 851 852 my %User = $Kernel::OM->Get('Kernel::System::User')->GetUserData( 853 UserID => $UserID, 854 ); 855 856 $Result{User} = \%User; 857 } 858 } 859 860 return %Result; 861} 862 863=head2 SettingEffectiveValueGet() 864 865Calculate effective value for a given parsed XML structure. 866 867 my $Result = $SysConfigObject->SettingEffectiveValueGet( 868 Translate => 1, # (optional) Translate translatable strings. Default 0. 869 Value => [ # (required) parsed XML structure 870 { 871 'Item' => [ 872 { 873 'ValueType' => 'String', 874 'Content' => '3600', 875 'ValueRegex' => '' 876 }, 877 ], 878 }, 879 Objects => { 880 Select => { ... }, 881 PerlModule => { ... }, 882 # ... 883 } 884 ]; 885 ); 886 887Returns: 888 889 $Result = '3600'; 890 891=cut 892 893sub SettingEffectiveValueGet { 894 my ( $Self, %Param ) = @_; 895 896 for my $Needed (qw(Value)) { 897 if ( !$Param{$Needed} ) { 898 $Kernel::OM->Get('Kernel::System::Log')->Log( 899 Priority => 'error', 900 Message => "Need $Needed!", 901 ); 902 return; 903 } 904 } 905 906 my %ForbiddenValueTypes = %{ $Self->{ForbiddenValueTypes} || {} }; 907 908 if ( !%ForbiddenValueTypes ) { 909 %ForbiddenValueTypes = $Self->ForbiddenValueTypesGet(); 910 $Self->{ForbiddenValueTypes} = \%ForbiddenValueTypes; 911 } 912 913 $Param{Translate} //= 0; 914 915 my $Result; 916 917 my %Objects; 918 if ( $Param{Objects} ) { 919 %Objects = %{ $Param{Objects} }; 920 } 921 922 # Make sure structure is correct. 923 return $Result if !IsArrayRefWithData( $Param{Value} ); 924 return $Result if !IsHashRefWithData( $Param{Value}->[0] ); 925 926 my %Attributes; 927 928 if ( $Param{Value}->[0]->{Item} ) { 929 930 # Make sure structure is correct. 931 return $Result if !IsArrayRefWithData( $Param{Value}->[0]->{Item} ); 932 return $Result if !IsHashRefWithData( $Param{Value}->[0]->{Item}->[0] ); 933 934 # Set default ValueType. 935 my $ValueType = "String"; 936 937 if ( $Param{Value}->[0]->{Item}->[0]->{ValueType} ) { 938 $ValueType = $Param{Value}->[0]->{Item}->[0]->{ValueType}; 939 } 940 941 if ( !$Objects{$ValueType} ) { 942 943 # Make sure value type backend is available and syntactically correct. 944 my $Loaded = $Kernel::OM->Get('Kernel::System::Main')->Require( 945 "Kernel::System::SysConfig::ValueType::$ValueType", 946 ); 947 948 return $Result if !$Loaded; 949 950 $Objects{$ValueType} = $Kernel::OM->Get( 951 "Kernel::System::SysConfig::ValueType::$ValueType", 952 ); 953 } 954 955 # Create a local clone of the value to prevent any modification. 956 my $Value = $Kernel::OM->Get('Kernel::System::Storable')->Clone( 957 Data => $Param{Value}->[0]->{Item}, 958 ); 959 960 $Result = $Objects{$ValueType}->EffectiveValueGet( 961 Value => $Value, 962 Translate => $Param{Translate}, 963 ); 964 } 965 elsif ( $Param{Value}->[0]->{Hash} ) { 966 967 # Make sure structure is correct. 968 return {} if !IsArrayRefWithData( $Param{Value}->[0]->{Hash} ); 969 return {} if !IsHashRefWithData( $Param{Value}->[0]->{Hash}->[0] ); 970 return {} if !IsArrayRefWithData( $Param{Value}->[0]->{Hash}->[0]->{Item} ); 971 972 # Check for additional attributes in the DefaultItem. 973 if ( 974 $Param{Value}->[0]->{Hash}->[0]->{DefaultItem} 975 && ref $Param{Value}->[0]->{Hash}->[0]->{DefaultItem} eq 'ARRAY' 976 && scalar $Param{Value}->[0]->{Hash}->[0]->{DefaultItem} 977 && ref $Param{Value}->[0]->{Hash}->[0]->{DefaultItem}->[0] eq 'HASH' 978 ) 979 { 980 %Attributes = (); 981 982 my @ValueAttributeList = $Self->ValueAttributeList(); 983 984 ATTRIBUTE: 985 for my $Attribute ( sort keys %{ $Param{Value}->[0]->{Hash}->[0]->{DefaultItem}->[0] } ) { 986 if ( 987 ( grep { $_ eq $Attribute } qw (Array Hash) ) 988 && $Param{Value}->[0]->{Hash}->[0]->{DefaultItem}->[0]->{$Attribute}->[0]->{DefaultItem} 989 ) 990 { 991 $Attributes{DefaultItem} 992 = $Param{Value}->[0]->{Hash}->[0]->{DefaultItem}->[0]->{$Attribute}->[0]->{DefaultItem}; 993 } 994 next ATTRIBUTE if grep { $Attribute eq $_ } ( qw (Array Hash), @ValueAttributeList ); 995 996 if ( 997 $Param{Value}->[0]->{Hash}->[0]->{DefaultItem}->[0]->{Item} 998 && $Param{Value}->[0]->{Hash}->[0]->{DefaultItem}->[0]->{ValueType} 999 ) 1000 { 1001 my $DefaultItemValueType = $Param{Value}->[0]->{Hash}->[0]->{DefaultItem}->[0]->{ValueType}; 1002 if ( $ForbiddenValueTypes{$DefaultItemValueType} ) { 1003 my $SubValueType 1004 = $Param{Value}->[0]->{Hash}->[0]->{DefaultItem}->[0]->{Item}->[0]->{ValueType}; 1005 1006 if ( !grep { $_ eq $SubValueType } @{ $ForbiddenValueTypes{$DefaultItemValueType} } ) { 1007 next ATTRIBUTE; 1008 } 1009 } 1010 } 1011 1012 $Attributes{$Attribute} = $Param{Value}->[0]->{Hash}->[0]->{DefaultItem}->[0]->{$Attribute}; 1013 } 1014 } 1015 1016 ITEM: 1017 for my $Item ( @{ $Param{Value}->[0]->{Hash}->[0]->{Item} } ) { 1018 1019 next ITEM if !IsHashRefWithData($Item); 1020 next ITEM if !defined $Item->{Key}; 1021 1022 if ( $Item->{Hash} || $Item->{Array} ) { 1023 1024 my $ItemKey = $Item->{Hash} ? 'Hash' : 'Array'; 1025 1026 ATTRIBUTE: 1027 for my $Attribute ( sort keys %Attributes ) { 1028 next ATTRIBUTE if defined $Item->{$Attribute}; # skip redefined values 1029 1030 $Item->{$ItemKey}->[0]->{$Attribute} = $Attributes{$Attribute}; 1031 } 1032 1033 my $Value = $Self->SettingEffectiveValueGet( 1034 Value => [$Item], 1035 Objects => \%Objects, 1036 Translate => $Param{Translate}, 1037 ); 1038 1039 $Result->{ $Item->{Key} } = $Value; 1040 } 1041 elsif ( $Attributes{ValueType} || $Item->{ValueType} ) { 1042 1043 # Create a local clone of the item to prevent any modification. 1044 my $Clone = $Kernel::OM->Get('Kernel::System::Storable')->Clone( 1045 Data => $Item, 1046 ); 1047 1048 ATTRIBUTE: 1049 for my $Attribute ( sort keys %Attributes ) { 1050 next ATTRIBUTE if defined $Clone->{$Attribute}; # skip redefined values 1051 1052 $Clone->{$Attribute} = $Attributes{$Attribute}; 1053 } 1054 1055 my $Value = $Self->SettingEffectiveValueGet( 1056 Value => [ 1057 { 1058 Item => [$Clone], 1059 }, 1060 ], 1061 Objects => \%Objects, 1062 Translate => $Param{Translate}, 1063 ); 1064 1065 $Result->{ $Item->{Key} } = $Value; 1066 } 1067 else { 1068 1069 $Item->{Content} //= ''; 1070 1071 # Remove empty space at start and the end (with new lines). 1072 $Item->{Content} =~ s{^\n\s*(.*?)\n\s*$}{$1}gsmx; 1073 1074 $Result->{ $Item->{Key} } = $Item->{Content}; 1075 } 1076 } 1077 } 1078 elsif ( $Param{Value}->[0]->{Array} ) { 1079 1080 # Make sure structure is correct 1081 return [] if !IsArrayRefWithData( $Param{Value}->[0]->{Array} ); 1082 return [] if !IsHashRefWithData( $Param{Value}->[0]->{Array}->[0] ); 1083 return [] if !IsArrayRefWithData( $Param{Value}->[0]->{Array}->[0]->{Item} ); 1084 1085 # Check for additional attributes in the DefaultItem. 1086 if ( 1087 $Param{Value}->[0]->{Array}->[0]->{DefaultItem} 1088 && ref $Param{Value}->[0]->{Array}->[0]->{DefaultItem} eq 'ARRAY' 1089 && scalar $Param{Value}->[0]->{Array}->[0]->{DefaultItem} 1090 && ref $Param{Value}->[0]->{Array}->[0]->{DefaultItem}->[0] eq 'HASH' 1091 ) 1092 { 1093 %Attributes = (); 1094 1095 ATTRIBUTE: 1096 for my $Attribute ( sort keys %{ $Param{Value}->[0]->{Array}->[0]->{DefaultItem}->[0] } ) { 1097 if ( 1098 ( grep { $_ eq $Attribute } qw (Array Hash) ) 1099 && $Param{Value}->[0]->{Array}->[0]->{DefaultItem}->[0]->{$Attribute}->[0]->{DefaultItem} 1100 ) 1101 { 1102 $Attributes{DefaultItem} 1103 = $Param{Value}->[0]->{Array}->[0]->{DefaultItem}->[0]->{$Attribute}->[0]->{DefaultItem}; 1104 } 1105 next ATTRIBUTE if grep { $Attribute eq $_ } qw (Array Hash Content SelectedID); 1106 1107 $Attributes{$Attribute} = $Param{Value}->[0]->{Array}->[0]->{DefaultItem}->[0]->{$Attribute}; 1108 } 1109 } 1110 1111 my @Items; 1112 1113 ITEM: 1114 for my $Item ( @{ $Param{Value}->[0]->{Array}->[0]->{Item} } ) { 1115 next ITEM if !IsHashRefWithData($Item); 1116 1117 if ( $Item->{Hash} || $Item->{Array} ) { 1118 my $ItemKey = $Item->{Hash} ? 'Hash' : 'Array'; 1119 1120 ATTRIBUTE: 1121 for my $Attribute ( sort keys %Attributes ) { 1122 next ATTRIBUTE if defined $Item->{$Attribute}; # skip redefined values 1123 1124 $Item->{$ItemKey}->[0]->{$Attribute} = $Attributes{$Attribute}; 1125 } 1126 1127 my $Value = $Self->SettingEffectiveValueGet( 1128 Value => [$Item], 1129 Objects => \%Objects, 1130 Translate => $Param{Translate}, 1131 ); 1132 1133 push @Items, $Value; 1134 } 1135 elsif ( $Attributes{ValueType} ) { 1136 1137 # Create a local clone of the item to prevent any modification. 1138 my $Clone = $Kernel::OM->Get('Kernel::System::Storable')->Clone( 1139 Data => $Item, 1140 ); 1141 1142 ATTRIBUTE: 1143 for my $Attribute ( sort keys %Attributes ) { 1144 next ATTRIBUTE if defined $Clone->{$Attribute}; # skip redefined values 1145 1146 $Clone->{$Attribute} = $Attributes{$Attribute}; 1147 } 1148 1149 my $Value = $Self->SettingEffectiveValueGet( 1150 Value => [ 1151 { 1152 Item => [$Clone], 1153 }, 1154 ], 1155 Objects => \%Objects, 1156 Translate => $Param{Translate}, 1157 ); 1158 1159 push @Items, $Value; 1160 } 1161 else { 1162 $Item->{Content} //= ''; 1163 1164 # Remove empty space at start and the end (with new lines). 1165 $Item->{Content} =~ s{^\n\s*(.*?)\n\s*$}{$1}gsmx; 1166 1167 push @Items, $Item->{Content}; 1168 } 1169 } 1170 $Result = \@Items; 1171 } 1172 1173 return $Result; 1174} 1175 1176=head2 SettingRender() 1177 1178Wrapper for Kernel::Output::HTML::SysConfig::SettingRender() - Returns the specific HTML for the setting. 1179 1180 my $HTMLStr = $SysConfigObject->SettingRender( 1181 Setting => { 1182 Name => 'Setting Name', 1183 XMLContentParsed => $XMLParsedToPerl, 1184 EffectiveValue => "Product 6", # or a complex structure 1185 DefaultValue => "Product 5", # or a complex structure 1186 IsAjax => 1, # (optional) is AJAX request. Default 0. 1187 # ... 1188 }, 1189 RW => 1, # (optional) Allow editing. Default 0. 1190 ); 1191 1192Returns: 1193 1194 $HTMLStr = '<div class="Setting"><div class "Field"...</div></div>' # or false in case of an error 1195 1196=cut 1197 1198sub SettingRender { 1199 my ( $Self, %Param ) = @_; 1200 1201 return $Kernel::OM->Get('Kernel::Output::HTML::SysConfig')->SettingRender(%Param); 1202} 1203 1204=head2 SettingAddItem() 1205 1206Wrapper for Kernel::Output::HTML::SysConfig::SettingAddItem() - Returns response that is sent when user adds new array/hash item. 1207 1208 my %Result = $SysConfigObject->SettingAddItem( 1209 SettingStructure => [], # (required) array that contains structure 1210 # where a new item should be inserted (can be empty) 1211 Setting => { # (required) Setting hash (from SettingGet()) 1212 'DefaultID' => '8905', 1213 'DefaultValue' => [ 'Item 1', 'Item 2' ], 1214 'Description' => 'Simple array item(Min 1, Max 3).', 1215 'Name' => 'TestArray', 1216 ... 1217 }, 1218 Key => 'HashKey', # (optional) hash key 1219 IDSuffix => '_Array3, # (optional) suffix that will be added to all input/select fields 1220 # (it is used in the JS on Update, during EffectiveValue calculation) 1221 Value => [ # (optional) Perl structure 1222 { 1223 'Array' => [ 1224 'Item' => [ 1225 { 1226 'Content' => 'Item 1', 1227 }, 1228 ... 1229 ], 1230 ], 1231 }, 1232 ], 1233 AddSettingContent => 0, # (optional) if enabled, result will be inside of div with class "SettingContent" 1234 ); 1235 1236Returns: 1237 1238 %Result = ( 1239 'Item' => '<div class=\'SettingContent\'> 1240<input type=\'text\' id=\'TestArray_Array4\' 1241 value=\'Default value\' name=\'TestArray\' class=\' Entry\'/></div>', 1242 ); 1243 1244 or 1245 1246 %Result = ( 1247 'Error' => 'Error description', 1248 ); 1249 1250=cut 1251 1252sub SettingAddItem { 1253 my ( $Self, %Param ) = @_; 1254 1255 return $Kernel::OM->Get('Kernel::Output::HTML::SysConfig')->SettingAddItem(%Param); 1256} 1257 1258=head2 SettingsUpdatedList() 1259 1260Checks which settings has been updated from provided Setting list and returns updated values. 1261 1262 my @List = $SysConfigObject->SettingsUpdatedList( 1263 Settings => [ # (required) List of settings that needs to be checked 1264 { 1265 SettingName => 'SettingName', 1266 ChangeTime => '2017-01-13 11:23:07', 1267 IsLockedByAnotherUser => 0, 1268 }, 1269 ... 1270 ], 1271 UserID => 1, # (required) Current user id 1272 ); 1273 1274Returns: 1275 1276 @List = [ 1277 { 1278 ChangeTime => '2017-01-07 11:29:38', 1279 IsLockedByAnotherUser => 1, 1280 IsModified => 1, 1281 SettingName => 'SettingName', 1282 }, 1283 ... 1284 ]; 1285 1286=cut 1287 1288sub SettingsUpdatedList { 1289 my ( $Self, %Param ) = @_; 1290 1291 # Check needed stuff. 1292 for my $Needed (qw(Settings UserID)) { 1293 if ( !$Param{$Needed} ) { 1294 $Kernel::OM->Get('Kernel::System::Log')->Log( 1295 Priority => 'error', 1296 Message => "Need $Needed!", 1297 ); 1298 return; 1299 } 1300 } 1301 1302 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 1303 1304 my @Result; 1305 1306 SETTING: 1307 for my $Setting ( @{ $Param{Settings} } ) { 1308 next SETTING if !IsHashRefWithData($Setting); 1309 1310 my %SettingData = $Self->SettingGet( 1311 Name => $Setting->{SettingName}, 1312 ); 1313 1314 my $LockedUserID = $SysConfigDBObject->DefaultSettingIsLocked( 1315 Name => $Setting->{SettingName}, 1316 GetLockUserID => 1, 1317 ); 1318 1319 my $IsLockedByAnotherUser = $LockedUserID ? 1 : 0; 1320 1321 # Skip if setting was locked by current user during AJAX call. 1322 next SETTING if $LockedUserID == $Param{UserID}; 1323 1324 my $Updated = $SettingData{ChangeTime} ne $Setting->{ChangeTime}; 1325 $Updated ||= $IsLockedByAnotherUser != $Setting->{IsLockedByAnotherUser}; 1326 1327 $Setting->{IsLockedByAnotherUser} = $IsLockedByAnotherUser; 1328 1329 next SETTING if !$Updated; 1330 1331 push @Result, $Setting; 1332 } 1333 1334 return @Result; 1335} 1336 1337=head2 SettingEffectiveValueCheck() 1338 1339Check if provided EffectiveValue matches structure defined in DefaultSetting. Also returns EffectiveValue that might be changed. 1340 1341 my %Result = $SysConfigObject->SettingEffectiveValueCheck( 1342 EffectiveValue => 'open', # (optional) 1343 XMLContentParsed => { # (required) 1344 Value => [ 1345 { 1346 'Item' => [ 1347 { 1348 'Content' => "Scalar value", 1349 }, 1350 ], 1351 }, 1352 ], 1353 }, 1354 StoreCache => 1, # (optional) Store result in the Cache. Default 0. 1355 SettingUID => 'Default1234' # (required if StoreCache) 1356 NoValidation => 1, # (optional) no value type validation. 1357 CurrentSystemTime => 1507894796935, # (optional) Use provided 1507894796935, otherwise calculate 1358 ExpireTime => 1507894896935, # (optional) Use provided ExpireTime for cache, otherwise calculate 1359 UserID => 1, # (required) UserID 1360 ); 1361 1362Returns: 1363 1364 %Result = ( 1365 EffectiveValue => 'closed', # Note that resulting effective value can be different 1366 Success => 1, 1367 Error => undef, 1368 ); 1369 1370=cut 1371 1372sub SettingEffectiveValueCheck { 1373 my ( $Self, %Param ) = @_; 1374 1375 for my $Needed (qw(XMLContentParsed UserID)) { 1376 if ( !$Param{$Needed} ) { 1377 $Kernel::OM->Get('Kernel::System::Log')->Log( 1378 Priority => 'error', 1379 Message => "Need $Needed!" 1380 ); 1381 return; 1382 } 1383 } 1384 1385 $Param{EffectiveValue} //= ''; 1386 $Param{NoValidation} //= 0; 1387 1388 my $StoreCache = $Param{StoreCache}; 1389 1390 if ( $Param{StoreCache} && !$Param{SettingUID} ) { 1391 $Kernel::OM->Get('Kernel::System::Log')->Log( 1392 Priority => 'error', 1393 Message => "SettingEffectiveValueCheck() called with StoreCache but without SettingUID parameter!" 1394 ); 1395 $StoreCache = 0; # Fallback, do not use cache. 1396 } 1397 1398 my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); 1399 1400 my $CacheType = 'SysConfigPersistent'; 1401 my $CacheKey = "EffectiveValueCheck::$Param{NoValidation}"; 1402 my $SettingKey; 1403 1404 my $Cache; 1405 1406 my $DateTimeObject; 1407 1408 my $CurrentSystemTime = $Param{CurrentSystemTime}; 1409 1410 # Get current system time, if not provided. 1411 if ( !$CurrentSystemTime ) { 1412 $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); 1413 $CurrentSystemTime = $DateTimeObject->ToEpoch(); 1414 } 1415 1416 my $ExpireTime = $Param{ExpireTime}; 1417 1418 # Get cache expire time, if not provided. 1419 if ( !$ExpireTime ) { 1420 if ( !$DateTimeObject ) { 1421 $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); 1422 } 1423 1424 # Set expire date. 1425 $DateTimeObject->Add( 1426 Months => 1, 1427 ); 1428 1429 $ExpireTime = $DateTimeObject->ToEpoch(); 1430 } 1431 1432 if ( $Param{SettingUID} ) { 1433 1434 my $MainObject = $Kernel::OM->Get('Kernel::System::Main'); 1435 my $StorableObject = $Kernel::OM->Get('Kernel::System::Storable'); 1436 1437 my $ValueString = $Param{EffectiveValue}; 1438 if ( ref $ValueString ) { 1439 my $String = $StorableObject->Serialize( 1440 Data => $Param{EffectiveValue}, 1441 ); 1442 $ValueString = $MainObject->MD5sum( 1443 String => \$String, 1444 ); 1445 } 1446 1447 $SettingKey = "$Param{SettingUID}::${ValueString}"; 1448 1449 $Cache = $CacheObject->Get( 1450 Type => $CacheType, 1451 Key => $CacheKey, 1452 ); 1453 1454 if ( $Cache && !$Self->{EffectiveValueCheckCacheDeleted} ) { 1455 1456 # Delete all expired keys. 1457 my @ExpiredKeys = grep { $CurrentSystemTime > ( $Cache->{$_}->{ExpireTime} || 0 ) } keys %{$Cache}; 1458 delete @{$Cache}{@ExpiredKeys}; 1459 1460 if (@ExpiredKeys) { 1461 1462 # Update cache. 1463 $CacheObject->Set( 1464 Type => $CacheType, 1465 Key => $CacheKey, 1466 Value => $Cache, 1467 TTL => 20 * 24 * 60 * 60, 1468 ); 1469 } 1470 1471 # Remember delete in this round 1472 $Self->{EffectiveValueCheckCacheDeleted} = 1; 1473 } 1474 1475 if ( 1476 ref $Cache eq 'HASH' 1477 && $Cache->{$SettingKey} 1478 ) 1479 { 1480 return %{ $Cache->{$SettingKey} }; 1481 } 1482 } 1483 1484 my %Result = ( 1485 Success => 0, 1486 ); 1487 1488 my %Parameters = %{ $Param{Parameters} || {} }; 1489 my $Value = $Param{XMLContentParsed}->{Value}; 1490 1491 if ( $Value->[0]->{Item} || $Value->[0]->{ValueType} ) { 1492 1493 # get ValueType from parent or use default 1494 my $ValueType = $Parameters{ValueType} || $Value->[0]->{ValueType} || 'String'; 1495 1496 # ValueType is defined explicitly(override parent definition) 1497 if ( 1498 $Value->[0]->{Item} 1499 && $Value->[0]->{Item}->[0]->{ValueType} 1500 ) 1501 { 1502 $ValueType = $Value->[0]->{Item}->[0]->{ValueType}; 1503 } 1504 1505 my %ForbiddenValueTypes = %{ $Self->{ForbiddenValueTypes} || {} }; 1506 1507 if ( !%ForbiddenValueTypes ) { 1508 %ForbiddenValueTypes = $Self->ForbiddenValueTypesGet(); 1509 $Self->{ForbiddenValueTypes} = \%ForbiddenValueTypes; 1510 } 1511 1512 my @SkipValueTypes; 1513 1514 for my $Item ( sort keys %ForbiddenValueTypes ) { 1515 if ( !grep { $_ eq $ForbiddenValueTypes{$Item} } @SkipValueTypes ) { 1516 push @SkipValueTypes, @{ $ForbiddenValueTypes{$Item} }; 1517 } 1518 } 1519 1520 if ( 1521 $Param{NoValidation} 1522 || grep { $_ eq $ValueType } @SkipValueTypes 1523 ) 1524 { 1525 $Result{Success} = 1; 1526 $Result{EffectiveValue} = $Param{EffectiveValue}; 1527 return %Result; 1528 } 1529 1530 my $BackendObject = $Self->{ValueTypeBackendObject}->{$ValueType} || ''; 1531 1532 if ( !$BackendObject ) { 1533 1534 my $Loaded = $Kernel::OM->Get('Kernel::System::Main')->Require( 1535 "Kernel::System::SysConfig::ValueType::$ValueType", 1536 ); 1537 1538 if ( !$Loaded ) { 1539 $Result{Error} = "Kernel::System::SysConfig::ValueType::$ValueType"; 1540 return %Result; 1541 } 1542 1543 $BackendObject = $Kernel::OM->Get( 1544 "Kernel::System::SysConfig::ValueType::$ValueType", 1545 ); 1546 1547 $Self->{ValueTypeBackendObject}->{$ValueType} = $BackendObject; 1548 } 1549 1550 %Result = $BackendObject->SettingEffectiveValueCheck(%Param); 1551 $Param{EffectiveValue} = $Result{EffectiveValue} if $Result{Success}; 1552 } 1553 elsif ( $Value->[0]->{Hash} ) { 1554 1555 if ( ref $Param{EffectiveValue} ne 'HASH' ) { 1556 $Result{Error} = 'Its not a hash!'; 1557 return %Result; 1558 } 1559 1560 PARAMETER: 1561 for my $Parameter ( sort keys %{ $Value->[0]->{Hash}->[0] } ) { 1562 1563 next PARAMETER if !grep { $_ eq $Parameter } qw(MinItems MaxItems); 1564 1565 $Parameters{$Parameter} = $Value->[0]->{Hash}->[0]->{$Parameter} || ''; 1566 } 1567 1568 if ( $Parameters{MinItems} && $Parameters{MinItems} > keys %{ $Param{EffectiveValue} } ) { 1569 $Result{Error} = "Number of items in hash is less than MinItems($Parameters{MinItems})!"; 1570 return %Result; 1571 } 1572 1573 if ( $Parameters{MaxItems} && $Parameters{MaxItems} < keys %{ $Param{EffectiveValue} } ) { 1574 $Result{Error} = "Number of items in hash is more than MaxItems($Parameters{MaxItems})!"; 1575 return %Result; 1576 } 1577 1578 my @Items = (); 1579 1580 if ( 1581 scalar @{ $Value->[0]->{Hash} } 1582 && $Value->[0]->{Hash}->[0]->{Item} 1583 && ref $Value->[0]->{Hash}->[0]->{Item} eq 'ARRAY' 1584 ) 1585 { 1586 @Items = @{ $Value->[0]->{Hash}->[0]->{Item} }; 1587 } 1588 1589 my $DefaultItem; 1590 1591 KEY: 1592 for my $Key ( sort keys %{ $Param{EffectiveValue} } ) { 1593 $DefaultItem = $Value->[0]->{Hash}->[0]->{DefaultItem}; 1594 1595 if ( $Value->[0]->{Hash}->[0]->{Item} ) { 1596 my @ItemWithSameKey = grep { $Key eq ( $Value->[0]->{Hash}->[0]->{Item}->[$_]->{Key} || '' ) } 1597 0 .. scalar @{ $Value->[0]->{Hash}->[0]->{Item} }; 1598 if ( scalar @ItemWithSameKey ) { 1599 $DefaultItem = [ 1600 $Value->[0]->{Hash}->[0]->{Item}->[ $ItemWithSameKey[0] ], 1601 ]; 1602 } 1603 1604 my $StructureType; 1605 if ( $DefaultItem->[0]->{Array} ) { 1606 $StructureType = 'Array'; 1607 } 1608 elsif ( $DefaultItem->[0]->{Hash} ) { 1609 $StructureType = 'Hash'; 1610 } 1611 1612 # check if default item is defined in this sub-structure 1613 if ( 1614 $StructureType 1615 && !$DefaultItem->[0]->{$StructureType}->[0]->{DefaultItem} 1616 && $Value->[0]->{Hash}->[0]->{DefaultItem} 1617 && $Value->[0]->{Hash}->[0]->{DefaultItem}->[0]->{$StructureType} 1618 && $Value->[0]->{Hash}->[0]->{DefaultItem}->[0]->{$StructureType}->[0]->{DefaultItem} 1619 ) 1620 { 1621 # Default Item is not defined here, but it's defined in previous call. 1622 $DefaultItem->[0]->{$StructureType}->[0]->{DefaultItem} = 1623 $Value->[0]->{Hash}->[0]->{DefaultItem}->[0]->{$StructureType}->[0]->{DefaultItem}; 1624 } 1625 } 1626 1627 my $Ref = ref $Param{EffectiveValue}->{$Key}; 1628 1629 if ($Ref) { 1630 1631 if ( IsArrayRefWithData($DefaultItem) ) { 1632 1633 my $KeyNeeded; 1634 1635 if ( $Ref eq 'HASH' ) { 1636 $KeyNeeded = 'Hash'; 1637 } 1638 elsif ( $Ref eq 'ARRAY' ) { 1639 $KeyNeeded = 'Array'; 1640 } 1641 else { 1642 $Result{Error} = "Wrong format!"; 1643 last KEY; 1644 } 1645 1646 if ( $DefaultItem->[0]->{Item} ) { 1647 1648 # So far everything is OK, we need to check deeper (recursive). 1649 my %SubResult = $Self->_SettingEffectiveValueCheck( 1650 XMLContentParsed => { 1651 Value => $DefaultItem, 1652 }, 1653 EffectiveValue => $Param{EffectiveValue}->{$Key}, 1654 NoValidation => $Param{NoValidation}, 1655 CurrentSystemTime => $Param{CurrentSystemTime}, 1656 ExpireTime => $Param{ExpireTime}, 1657 UserID => $Param{UserID}, 1658 ); 1659 $Param{EffectiveValue}->{$Key} = $SubResult{EffectiveValue} if $SubResult{Success}; 1660 1661 if ( $SubResult{Error} ) { 1662 %Result = %SubResult; 1663 last KEY; 1664 } 1665 } 1666 elsif ( !defined $DefaultItem->[0]->{$KeyNeeded} ) { 1667 my $ExpectedText = ''; 1668 1669 if ( $DefaultItem->[0]->{Array} ) { 1670 $ExpectedText = "an array reference!"; 1671 } 1672 elsif ( $DefaultItem->[0]->{Hash} ) { 1673 $ExpectedText = "a hash reference!"; 1674 } 1675 else { 1676 $ExpectedText = "a scalar!"; 1677 } 1678 1679 $Result{Error} = "Item with key $Key must be $ExpectedText"; 1680 last KEY; 1681 } 1682 else { 1683 1684 # So far everything is OK, we need to check deeper (recursive). 1685 my %SubResult = $Self->_SettingEffectiveValueCheck( 1686 XMLContentParsed => { 1687 Value => $DefaultItem, 1688 }, 1689 EffectiveValue => $Param{EffectiveValue}->{$Key}, 1690 NoValidation => $Param{NoValidation}, 1691 CurrentSystemTime => $Param{CurrentSystemTime}, 1692 ExpireTime => $Param{ExpireTime}, 1693 UserID => $Param{UserID}, 1694 ); 1695 $Param{EffectiveValue}->{$Key} = $SubResult{EffectiveValue} if $SubResult{Success}; 1696 1697 if ( $SubResult{Error} ) { 1698 %Result = %SubResult; 1699 last KEY; 1700 } 1701 } 1702 } 1703 else { 1704 # Hash is empty in the Defaults, value should be scalar. 1705 $Result{Error} = "Item with key $Key must be a scalar!"; 1706 last KEY; 1707 } 1708 } 1709 else { 1710 1711 # scalar value 1712 if ( IsArrayRefWithData($DefaultItem) ) { 1713 if ( $DefaultItem->[0]->{Item} || $DefaultItem->[0]->{Content} ) { 1714 1715 # So far everything is OK, we need to check deeper (recursive). 1716 my %SubResult = $Self->_SettingEffectiveValueCheck( 1717 XMLContentParsed => { 1718 Value => [ 1719 { 1720 Item => $DefaultItem, 1721 }, 1722 ], 1723 }, 1724 EffectiveValue => $Param{EffectiveValue}->{$Key}, 1725 NoValidation => $Param{NoValidation}, 1726 CurrentSystemTime => $Param{CurrentSystemTime}, 1727 ExpireTime => $Param{ExpireTime}, 1728 UserID => $Param{UserID}, 1729 ); 1730 1731 if ( $SubResult{Error} ) { 1732 %Result = %SubResult; 1733 last KEY; 1734 } 1735 else { 1736 $Param{EffectiveValue}->{$Key} = $SubResult{EffectiveValue}; 1737 } 1738 1739 } 1740 elsif ( $DefaultItem->[0]->{Hash} ) { 1741 $Result{Error} = "Item with key $Key must be a hash reference!"; 1742 last KEY; 1743 } 1744 elsif ( $DefaultItem->[0]->{Array} ) { 1745 $Result{Error} = "Item with key $Key must be an array reference!"; 1746 last KEY; 1747 } 1748 } 1749 } 1750 } 1751 1752 # Check which Value type is default 1753 my $DefaultValueTypeDefined = 'String'; 1754 if ( 1755 $Value->[0]->{Hash}->[0]->{DefaultItem} 1756 && $Value->[0]->{Hash}->[0]->{DefaultItem}->[0]->{ValueType} 1757 ) 1758 { 1759 $DefaultValueTypeDefined = $Value->[0]->{Hash}->[0]->{DefaultItem}->[0]->{ValueType}; 1760 } 1761 1762 # Get persistent keys(items with value type different then value type defined in the DefaultItem) 1763 my @PersistentKeys; 1764 for my $Item ( @{ $Value->[0]->{Hash}->[0]->{Item} } ) { 1765 my $ValueType = $DefaultValueTypeDefined; 1766 1767 if ( $Item->{ValueType} ) { 1768 $ValueType = $Item->{ValueType}; 1769 } 1770 elsif ( 1771 $Item->{Item} 1772 && $Item->{Item}->[0]->{ValueType} 1773 ) 1774 { 1775 $ValueType = $Item->{Item}->[0]->{ValueType}; 1776 } 1777 1778 if ( $ValueType ne $DefaultValueTypeDefined && $Item->{Key} ) { 1779 push @PersistentKeys, $Item->{Key}; 1780 } 1781 } 1782 1783 # Validate if all persistent keys are present 1784 PERSISTENT_KEY: 1785 for my $Key (@PersistentKeys) { 1786 if ( !defined $Param{EffectiveValue}->{$Key} ) { 1787 $Result{Error} = $Kernel::OM->Get('Kernel::Language')->Translate( "Missing key %s!", $Key ); 1788 last PERSISTENT_KEY; 1789 } 1790 } 1791 1792 if ( $Result{Error} ) { 1793 return %Result; 1794 } 1795 1796 $Result{Success} = 1; 1797 } 1798 elsif ( $Value->[0]->{Array} ) { 1799 1800 if ( ref $Param{EffectiveValue} ne 'ARRAY' ) { 1801 $Result{Error} = 'Its not an array!'; 1802 return %Result; 1803 } 1804 1805 PARAMETER: 1806 for my $Parameter ( sort keys %{ $Value->[0]->{Array}->[0] } ) { 1807 next PARAMETER if !grep { $_ eq $Parameter } qw(MinItems MaxItems); 1808 1809 $Parameters{$Parameter} = $Value->[0]->{Array}->[0]->{$Parameter} || ''; 1810 } 1811 1812 if ( $Parameters{MinItems} && $Parameters{MinItems} > scalar @{ $Param{EffectiveValue} } ) { 1813 $Result{Error} = "Number of items in array is less than MinItems($Parameters{MinItems})!"; 1814 return %Result; 1815 } 1816 1817 if ( $Parameters{MaxItems} && $Parameters{MaxItems} < scalar @{ $Param{EffectiveValue} } ) { 1818 $Result{Error} = "Number of items in array is more than MaxItems($Parameters{MaxItems})!"; 1819 return %Result; 1820 } 1821 1822 my @Items = (); 1823 if ( 1824 scalar @{ $Value->[0]->{Array} } 1825 && $Value->[0]->{Array}->[0]->{Item} 1826 && ref $Value->[0]->{Array}->[0]->{Item} eq 'ARRAY' 1827 ) 1828 { 1829 @Items = @{ $Value->[0]->{Array}->[0]->{Item} }; 1830 } 1831 1832 my $DefaultItem; 1833 1834 INDEX: 1835 for my $Index ( 0 .. scalar @{ $Param{EffectiveValue} } - 1 ) { 1836 1837 $DefaultItem = $Value->[0]->{Array}->[0]->{DefaultItem}; 1838 1839 my $Ref = ref $Param{EffectiveValue}->[$Index]; 1840 if ($Ref) { 1841 if ($DefaultItem) { 1842 my $KeyNeeded; 1843 1844 if ( $Ref eq 'HASH' ) { 1845 $KeyNeeded = 'Hash'; 1846 } 1847 elsif ( $Ref eq 'ARRAY' ) { 1848 $KeyNeeded = 'Array'; 1849 } 1850 else { 1851 $Result{Error} = "Wrong format!"; 1852 last INDEX; 1853 } 1854 1855 if ( $DefaultItem->[0]->{Item} ) { 1856 1857 # So far everything is OK, we need to check deeper (recursive). 1858 my %SubResult = $Self->_SettingEffectiveValueCheck( 1859 XMLContentParsed => { 1860 Value => [ 1861 { 1862 Item => $DefaultItem, 1863 }, 1864 ], 1865 }, 1866 EffectiveValue => $Param{EffectiveValue}->[$Index], 1867 NoValidation => $Param{NoValidation}, 1868 CurrentSystemTime => $Param{CurrentSystemTime}, 1869 ExpireTime => $Param{ExpireTime}, 1870 UserID => $Param{UserID}, 1871 ); 1872 $Param{EffectiveValue}->[$Index] = $SubResult{EffectiveValue} if $SubResult{Success}; 1873 1874 if ( $SubResult{Error} ) { 1875 %Result = %SubResult; 1876 last INDEX; 1877 } 1878 } 1879 elsif ( !defined $DefaultItem->[0]->{$KeyNeeded} ) { 1880 my $ExpectedText = ''; 1881 1882 if ( $DefaultItem->[0]->{Array} ) { 1883 $ExpectedText = "an array reference!"; 1884 } 1885 elsif ( $DefaultItem->[0]->{Hash} ) { 1886 $ExpectedText = "a hash reference!"; 1887 } 1888 elsif ( $DefaultItem->[0]->{Content} ) { 1889 $ExpectedText = "a scalar!"; 1890 } 1891 1892 $Result{Error} = "Item with index $Index must be $ExpectedText"; 1893 last INDEX; 1894 } 1895 else { 1896 1897 # So far everything is OK, we need to check deeper (recursive). 1898 my %SubResult = $Self->_SettingEffectiveValueCheck( 1899 XMLContentParsed => { 1900 Value => $DefaultItem, 1901 }, 1902 EffectiveValue => $Param{EffectiveValue}->[$Index], 1903 NoValidation => $Param{NoValidation}, 1904 CurrentSystemTime => $Param{CurrentSystemTime}, 1905 ExpireTime => $Param{ExpireTime}, 1906 UserID => $Param{UserID}, 1907 ); 1908 $Param{EffectiveValue}->[$Index] = $SubResult{EffectiveValue} if $SubResult{Success}; 1909 1910 if ( $SubResult{Error} ) { 1911 %Result = %SubResult; 1912 last INDEX; 1913 } 1914 } 1915 } 1916 else { 1917 1918 # Array is empty in the Defaults, value should be scalar. 1919 $Result{Error} = "Item with index $Index must be a scalar!"; 1920 last INDEX; 1921 } 1922 } 1923 else { 1924 1925 # scalar 1926 if ($DefaultItem) { 1927 1928 if ( $DefaultItem->[0]->{Item} || $DefaultItem->[0]->{ValueType} ) { 1929 1930 # Item with ValueType 1931 1932 # So far everything is OK, we need to check deeper (recursive). 1933 my %SubResult = $Self->_SettingEffectiveValueCheck( 1934 XMLContentParsed => { 1935 Value => [ 1936 { 1937 Item => $DefaultItem, 1938 }, 1939 ], 1940 }, 1941 EffectiveValue => $Param{EffectiveValue}->[$Index], 1942 NoValidation => $Param{NoValidation}, 1943 CurrentSystemTime => $Param{CurrentSystemTime}, 1944 ExpireTime => $Param{ExpireTime}, 1945 UserID => $Param{UserID}, 1946 ); 1947 $Param{EffectiveValue}->[$Index] = $SubResult{EffectiveValue} if $SubResult{Success}; 1948 1949 if ( $SubResult{Error} ) { 1950 %Result = %SubResult; 1951 last INDEX; 1952 } 1953 } 1954 elsif ( $DefaultItem->[0]->{Hash} ) { 1955 $Result{Error} = "Item with index $Index must be a hash reference!"; 1956 last INDEX; 1957 } 1958 elsif ( $DefaultItem->[0]->{Array} ) { 1959 $Result{Error} = "Item with index $Index must be an array reference!"; 1960 last INDEX; 1961 } 1962 } 1963 } 1964 } 1965 if ( $Result{Error} ) { 1966 return %Result; 1967 } 1968 1969 $Result{Success} = 1; 1970 } 1971 1972 if ( $Result{Success} ) { 1973 $Result{EffectiveValue} = $Param{EffectiveValue}; 1974 } 1975 1976 $Result{ExpireTime} = $ExpireTime; 1977 1978 if ($StoreCache) { 1979 1980 $Cache->{$SettingKey} = \%Result; 1981 1982 $CacheObject->Set( 1983 Type => $CacheType, 1984 Key => $CacheKey, 1985 Value => $Cache, 1986 TTL => 20 * 24 * 60 * 60, 1987 ); 1988 } 1989 1990 return %Result; 1991} 1992 1993=head2 SettingReset() 1994 1995Reset the modified value to the default value. 1996 1997 my $Result = $SysConfigObject->SettingReset( 1998 Name => 'Setting Name', # (required) Setting name 1999 TargetUserID => 2, # (optional) UserID for settings in AgentPreferences 2000 # or 2001 ExclusiveLockGUID => $LockingString, # (optional) the GUID used to locking the setting 2002 UserID => 1, # (required) UserID that creates modification 2003 ); 2004 2005Returns: 2006 2007 $Result = 1; # or false in case of an error 2008 2009=cut 2010 2011sub SettingReset { 2012 my ( $Self, %Param ) = @_; 2013 2014 for my $Needed (qw(Name ExclusiveLockGUID UserID)) { 2015 if ( !$Param{$Needed} ) { 2016 $Kernel::OM->Get('Kernel::System::Log')->Log( 2017 Priority => 'error', 2018 Message => "Need $Needed!", 2019 ); 2020 return; 2021 } 2022 } 2023 2024 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 2025 2026 # Check if the setting exists. 2027 my %DefaultSetting = $SysConfigDBObject->DefaultSettingGet( 2028 Name => $Param{Name}, 2029 ); 2030 2031 return if !%DefaultSetting; 2032 2033 # Default should be locked. 2034 my $LockedByUser = $SysConfigDBObject->DefaultSettingIsLockedByUser( 2035 DefaultID => $DefaultSetting{DefaultID}, 2036 ExclusiveLockUserID => $Param{UserID}, 2037 ExclusiveLockGUID => $Param{ExclusiveLockGUID}, 2038 ); 2039 2040 if ( !$LockedByUser ) { 2041 $Kernel::OM->Get('Kernel::System::Log')->Log( 2042 Priority => 'error', 2043 Message => "Setting $Param{Name} is not locked to this user!", 2044 ); 2045 return; 2046 } 2047 2048 my %ModifiedSetting = $SysConfigDBObject->ModifiedSettingGet( 2049 Name => $Param{Name}, 2050 IsGlobal => 1, 2051 ); 2052 2053 # Setting already had default value. 2054 return 1 if !%ModifiedSetting; 2055 2056 my %SettingDeployed = $Self->SettingGet( 2057 Name => $Param{Name}, 2058 Deployed => 1, 2059 ); 2060 2061 my $IsModified = DataIsDifferent( 2062 Data1 => \$SettingDeployed{EffectiveValue}, 2063 Data2 => \$DefaultSetting{EffectiveValue}, 2064 ) || 0; 2065 2066 $IsModified ||= $SettingDeployed{IsValid} != $DefaultSetting{IsValid}; 2067 $IsModified ||= $SettingDeployed{UserModificationActive} != $DefaultSetting{UserModificationActive}; 2068 $ModifiedSetting{IsDirty} = $IsModified ? 1 : 0; 2069 2070 # Copy values from default. 2071 for my $Field (qw(IsValid UserModificationActive EffectiveValue)) { 2072 $ModifiedSetting{$Field} = $DefaultSetting{$Field}; 2073 } 2074 2075 # Set reset flag. 2076 $ModifiedSetting{ResetToDefault} = 1; 2077 2078 # Delete modified setting. 2079 my $ResetResult = $SysConfigDBObject->ModifiedSettingUpdate( 2080 %ModifiedSetting, 2081 ExclusiveLockGUID => $Param{ExclusiveLockGUID}, 2082 UserID => $Param{UserID}, 2083 ); 2084 2085 if ( !$ResetResult ) { 2086 $Kernel::OM->Get('Kernel::System::Log')->Log( 2087 Priority => 'error', 2088 Message => "System was unable to update Modified setting: $Param{Name}!", 2089 ); 2090 } 2091 2092 return $ResetResult; 2093} 2094 2095=head2 ConfigurationTranslatedGet() 2096 2097Returns a hash with all settings and translated metadata. 2098 2099 my %Result = $SysConfigObject->ConfigurationTranslatedGet(); 2100 2101Returns: 2102 2103 %Result = ( 2104 'ACL::CacheTTL' => { 2105 'Category' => 'OTRS', 2106 'IsInvisible' => '0', 2107 'Metadata' => "ACL::CacheTTL--- '3600' 2108Cache-Zeit in Sekunden f\x{fc}r Datenbank ACL-Backends.", 2109 ... 2110 ); 2111 2112=cut 2113 2114sub ConfigurationTranslatedGet { 2115 my ( $Self, %Param ) = @_; 2116 2117 my $LanguageObject = $Kernel::OM->Get('Kernel::Language'); 2118 my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); 2119 2120 my $CacheType = 'SysConfig'; 2121 my $CacheKey = "ConfigurationTranslatedGet::$LanguageObject->{UserLanguage}"; 2122 2123 # Return cache. 2124 my $Cache = $CacheObject->Get( 2125 Type => $CacheType, 2126 Key => $CacheKey, 2127 ); 2128 2129 return %{$Cache} if ref $Cache eq 'HASH'; 2130 2131 my @SettingList = $Self->ConfigurationList( 2132 IncludeInvisible => 1, 2133 ); 2134 2135 my %Result; 2136 2137 for my $Setting (@SettingList) { 2138 2139 my %SettingTranslated = $Self->_SettingTranslatedGet( 2140 Language => $LanguageObject->{UserLanguage}, 2141 Name => $Setting->{Name}, 2142 ); 2143 2144 # Append to the result. 2145 $Result{ $Setting->{Name} } = $SettingTranslated{ $Setting->{Name} }; 2146 } 2147 2148 $CacheObject->Set( 2149 Type => $CacheType, 2150 Key => $CacheKey, 2151 Value => \%Result, 2152 TTL => $Self->{CacheTTL} || 24 * 60 * 60, 2153 ); 2154 2155 return %Result; 2156} 2157 2158=head2 SettingNavigationToPath() 2159 2160Returns path structure for given navigation group. 2161 2162 my @Path = $SysConfigObject->SettingNavigationToPath( 2163 Navigation => 'Frontend::Agent::ToolBarModule', # (optional) 2164 ); 2165 2166Returns: 2167 2168 @Path = ( 2169 { 2170 'Value' => 'Frontend', 2171 'Name' => 'Frontend', 2172 }, 2173 { 2174 'Value' => 'Frontend::Agent', 2175 'Name' => 'Agent', 2176 }, 2177 ... 2178 ); 2179 2180=cut 2181 2182sub SettingNavigationToPath { 2183 my ( $Self, %Param ) = @_; 2184 2185 my @NavigationNames = split( '::', $Param{Navigation} ); 2186 my @Path; 2187 2188 INDEX: 2189 for my $Index ( 0 .. $#NavigationNames ) { 2190 2191 $Path[$Index]->{Name} = $NavigationNames[$Index]; 2192 2193 my @SubArray = @NavigationNames[ 0 .. $Index ]; 2194 $Path[$Index]->{Value} = join '::', @SubArray; 2195 } 2196 2197 return @Path; 2198} 2199 2200=head2 ConfigurationTranslatableStrings() 2201 2202Returns a unique list of all translatable strings from the default settings. 2203 2204 my @TranslatableStrings = $SysConfigObject->ConfigurationTranslatableStrings(); 2205 2206=cut 2207 2208sub ConfigurationTranslatableStrings { 2209 my ( $Self, %Param ) = @_; 2210 2211 # Reset translation list. 2212 $Self->{ConfigurationTranslatableStrings} = {}; 2213 2214 # Get all default settings. 2215 my @SettingsList = $Kernel::OM->Get('Kernel::System::SysConfig::DB')->DefaultSettingListGet(); 2216 2217 SETTING: 2218 for my $Setting (@SettingsList) { 2219 2220 next SETTING if !$Setting; 2221 next SETTING if !defined $Setting->{XMLContentParsed}; 2222 2223 # Get translatable strings. 2224 $Self->_ConfigurationTranslatableStrings( Data => $Setting->{XMLContentParsed} ); 2225 2226 } 2227 2228 my @Strings; 2229 for my $Key ( sort keys %{ $Self->{ConfigurationTranslatableStrings} } ) { 2230 push @Strings, $Key; 2231 } 2232 return @Strings; 2233} 2234 2235=head2 ConfigurationEntitiesGet() 2236 2237Get all entities that are referred in any enabled Setting in complete SysConfig. 2238 2239 my %Result = $SysConfigObject->ConfigurationEntitiesGet(); 2240 2241Returns: 2242 2243 %Result = ( 2244 'Priority' => { 2245 '3 normal' => [ 2246 'Ticket::Frontend::AgentTicketNote###PriorityDefault', 2247 'Ticket::Frontend::AgentTicketPhone###Priority', 2248 ... 2249 ], 2250 }, 2251 'Queue' => { 2252 'Postmaster' => [ 2253 'Ticket::Frontend::CustomerTicketMessage###QueueDefault', 2254 ], 2255 'Raw' => [ 2256 'PostmasterDefaultQueue', 2257 ], 2258 }, 2259 ... 2260 ); 2261 2262=cut 2263 2264sub ConfigurationEntitiesGet { 2265 my ( $Self, %Param ) = @_; 2266 2267 my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); 2268 2269 my $CacheType = "SysConfigEntities"; 2270 my $CacheKey = "UsedEntities"; 2271 2272 my $CacheData = $CacheObject->Get( 2273 Type => $CacheType, 2274 Key => $CacheKey, 2275 ); 2276 2277 # Return cached data if available. 2278 return %{$CacheData} if $CacheData; 2279 2280 my %Result = (); 2281 2282 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 2283 2284 my @EntitySettings = $SysConfigDBObject->DefaultSettingSearch( 2285 Search => 'ValueEntityType', 2286 ); 2287 2288 SETTING: 2289 for my $SettingName (@EntitySettings) { 2290 2291 my %Setting = $SysConfigDBObject->DefaultSettingGet( 2292 Name => $SettingName, 2293 ); 2294 2295 # Check if there is modified value. 2296 my %ModifiedSetting = $SysConfigDBObject->ModifiedSettingGet( 2297 Name => $SettingName, 2298 ); 2299 2300 if (%ModifiedSetting) { 2301 my $XMLContentParsed = $Self->SettingModifiedXMLContentParsedGet( 2302 ModifiedSetting => \%ModifiedSetting, 2303 DefaultSetting => \%Setting, 2304 ); 2305 2306 $Setting{XMLContentParsed}->{Value} = $XMLContentParsed; 2307 } 2308 2309 %Result = $Self->_ConfigurationEntitiesGet( 2310 Value => $Setting{XMLContentParsed}->{Value}, 2311 Result => \%Result, 2312 Name => $Setting{XMLContentParsed}->{Name}, 2313 ); 2314 } 2315 2316 # Cache the results. 2317 $CacheObject->Set( 2318 Type => $CacheType, 2319 Key => $CacheKey, 2320 Value => \%Result, 2321 TTL => 30 * 24 * 60 * 60, 2322 ); 2323 2324 return %Result; 2325} 2326 2327=head2 ConfigurationEntityCheck() 2328 2329Check if there are any enabled settings that refers to the provided Entity. 2330 2331 my @Result = $SysConfigObject->ConfigurationEntityCheck( 2332 EntityType => 'Priority', 2333 EntityName => '3 normal', 2334 ); 2335 2336Returns: 2337 2338 @Result = ( 2339 'Ticket::Frontend::AgentTicketNote###PriorityDefault', 2340 'Ticket::Frontend::AgentTicketPhone###Priority', 2341 'Ticket::Frontend::AgentTicketBulk###PriorityDefault', 2342 ... 2343 ); 2344 2345=cut 2346 2347sub ConfigurationEntityCheck { 2348 my ( $Self, %Param ) = @_; 2349 2350 for my $Needed (qw(EntityType EntityName)) { 2351 if ( !defined $Param{$Needed} ) { 2352 $Kernel::OM->Get('Kernel::System::Log')->Log( 2353 Priority => 'error', 2354 Message => "Need $Needed!" 2355 ); 2356 return; 2357 } 2358 } 2359 if ( !$Param{EntityType} ) { 2360 $Kernel::OM->Get('Kernel::System::Log')->Log( 2361 Priority => 'error', 2362 Message => "EntityType is invalid!" 2363 ); 2364 return; 2365 } 2366 2367 # If name is an empty string there is nothing to do, return an empty array. 2368 return () if !$Param{EntityName}; 2369 2370 my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); 2371 2372 my $CacheType = "SysConfigEntities"; 2373 my $CacheKey = "ConfigurationEntityCheck::$Param{EntityType}::$Param{EntityName}"; 2374 2375 my $CacheData = $CacheObject->Get( 2376 Type => $CacheType, 2377 Key => $CacheKey, 2378 ); 2379 2380 # Return cached data if available. 2381 return @{$CacheData} if $CacheData; 2382 2383 my %EntitySettings = $Self->ConfigurationEntitiesGet(); 2384 2385 my @Result = (); 2386 2387 for my $EntityType ( sort keys %EntitySettings ) { 2388 2389 # Check conditions. 2390 if ( 2391 $EntityType eq $Param{EntityType} 2392 && $EntitySettings{$EntityType}{ $Param{EntityName} } 2393 ) 2394 { 2395 @Result = @{ $EntitySettings{$EntityType}->{ $Param{EntityName} } }; 2396 } 2397 } 2398 2399 # Cache the results. 2400 $CacheObject->Set( 2401 Type => $CacheType, 2402 Key => $CacheKey, 2403 Value => \@Result, 2404 TTL => 30 * 24 * 60 * 60, 2405 ); 2406 2407 return @Result; 2408} 2409 2410=head2 ConfigurationXML2DB() 2411 2412Load Settings defined in XML files to the database. 2413 2414 my $Success = $SysConfigObject->ConfigurationXML2DB( 2415 UserID => 1, # UserID 2416 Directory => '/some/folder', # (optional) Provide directory where XML files are stored (default: Kernel/Config/Files/XML). 2417 Force => 1, # (optional) Force Setting update, even if it's locked by another user. Default: 0. 2418 CleanUp => 1, # (optional) Remove all settings that are not present in XML files. Default: 0. 2419 ); 2420 2421Returns: 2422 2423 $Success = 1; # or false in case of an error. 2424 2425=cut 2426 2427sub ConfigurationXML2DB { 2428 my ( $Self, %Param ) = @_; 2429 2430 if ( !$Param{UserID} ) { 2431 $Kernel::OM->Get('Kernel::System::Log')->Log( 2432 Priority => 'error', 2433 Message => "Need UserID!" 2434 ); 2435 return; 2436 } 2437 2438 my $Directory = $Param{Directory} || "$Self->{Home}/Kernel/Config/Files/XML/"; 2439 2440 if ( !-e $Directory ) { 2441 $Kernel::OM->Get('Kernel::System::Log')->Log( 2442 Priority => 'error', 2443 Message => "Directory '$Directory' does not exists", 2444 ); 2445 2446 return; 2447 } 2448 2449 my $MainObject = $Kernel::OM->Get('Kernel::System::Main'); 2450 2451 # Load xml config files, ordered by file name 2452 my @Files = $MainObject->DirectoryRead( 2453 Directory => $Directory, 2454 Filter => "*.xml", 2455 ); 2456 2457 my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); 2458 my $SysConfigXMLObject = $Kernel::OM->Get('Kernel::System::SysConfig::XML'); 2459 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 2460 2461 my %SettingsByInit = ( 2462 Framework => [], 2463 Application => [], 2464 Config => [], 2465 Changes => [], 2466 ); 2467 2468 my %Data; 2469 FILE: 2470 for my $File (@Files) { 2471 2472 my $MD5Sum = $MainObject->MD5sum( 2473 Filename => $File, 2474 ); 2475 2476 # Cleanup filename for cache type 2477 my $Filename = $File; 2478 $Filename =~ s{\/\/}{\/}g; 2479 $Filename =~ s{\A .+ Kernel/Config/Files/XML/ (.+)\.xml\z}{$1}msx; 2480 $Filename =~ s{\A .+ scripts/test/sample/SysConfig/XML/ (.+)\.xml\z}{$1}msx; 2481 2482 my $CacheType = 'SysConfigPersistent'; 2483 my $CacheKey = "ConfigurationXML2DB::${Filename}::${MD5Sum}"; 2484 2485 my $Cache = $CacheObject->Get( 2486 Type => $CacheType, 2487 Key => $CacheKey, 2488 ); 2489 2490 if ( 2491 ref $Cache eq 'HASH' 2492 && $Cache->{Init} 2493 && ref $Cache->{Settings} eq 'ARRAY' 2494 ) 2495 { 2496 @{ $SettingsByInit{ $Cache->{Init} } } 2497 = ( @{ $SettingsByInit{ $Cache->{Init} } }, @{ $Cache->{Settings} } ); 2498 next FILE; 2499 } 2500 2501 # Read XML file. 2502 my $ConfigFile = $MainObject->FileRead( 2503 Location => $File, 2504 Mode => 'utf8', 2505 Result => 'SCALAR', 2506 ); 2507 if ( !ref $ConfigFile || !${$ConfigFile} ) { 2508 $Kernel::OM->Get('Kernel::System::Log')->Log( 2509 Priority => 'error', 2510 Message => "Can't open file $File: $!", 2511 ); 2512 next FILE; 2513 } 2514 2515 # Check otrs_config Init attribute. 2516 $$ConfigFile =~ m{<otrs_config.*?init="(.*?)"}gsmx; 2517 my $InitValue = $1; 2518 2519 # Check if InitValue is Valid. 2520 if ( !defined $SettingsByInit{$InitValue} ) { 2521 $Kernel::OM->Get('Kernel::System::Log')->Log( 2522 Priority => 'error', 2523 Message => 2524 "Invalid otrs_config Init value ($InitValue)! Allowed values: Framework, Application, Config, Changes.", 2525 ); 2526 next FILE; 2527 } 2528 2529 my $XMLFilename = $File; 2530 $XMLFilename =~ s{$Directory(.*\.xml)\z}{$1}gmsx; 2531 $XMLFilename =~ s{\A/}{}gmsx; 2532 2533 # Remove comments. 2534 ${$ConfigFile} =~ s{<!--.*?-->}{}gs; 2535 2536 my @ParsedSettings = $SysConfigXMLObject->SettingListParse( 2537 XMLInput => ${$ConfigFile}, 2538 XMLFilename => $XMLFilename, 2539 ); 2540 2541 @{ $SettingsByInit{$InitValue} } = ( @{ $SettingsByInit{$InitValue} }, @ParsedSettings ); 2542 2543 # There might be an error parsing file. If we cache the result, error message will not be present. 2544 if (@ParsedSettings) { 2545 $CacheObject->Set( 2546 Key => $CacheKey, 2547 Type => $CacheType, 2548 Value => { 2549 Init => $InitValue, 2550 Settings => \@ParsedSettings, 2551 }, 2552 TTL => 60 * 60 * 24 * 20, 2553 ); 2554 } 2555 } 2556 2557 # Combine everything together in the correct order. 2558 my %Settings; 2559 for my $Init (qw(Framework Application Config Changes)) { 2560 SETTING: 2561 for my $Setting ( @{ $SettingsByInit{$Init} } ) { 2562 my $Name = $Setting->{XMLContentParsed}->{Name}; 2563 next SETTING if !$Name; 2564 2565 $Settings{$Name} = $Setting; 2566 } 2567 } 2568 2569 # Find and remove all settings that are in DB, but are not defined in XML files. 2570 if ( $Param{CleanUp} ) { 2571 $Self->_DBCleanUp( Settings => \%Settings ); 2572 } 2573 2574 # Lock all settings to be able to update them if needed. 2575 my $ExclusiveLockGUID = $SysConfigDBObject->DefaultSettingLock( 2576 UserID => $Param{UserID}, 2577 LockAll => 1, 2578 ); 2579 if ( !$ExclusiveLockGUID ) { 2580 $Kernel::OM->Get('Kernel::System::Log')->Log( 2581 Priority => 'error', 2582 Message => "System was unable to lock Default Settings ", 2583 ); 2584 return; 2585 } 2586 2587 my @SettingList = $Self->ConfigurationList( 2588 IncludeInvisible => 1, 2589 ); 2590 2591 my $StorableObject = $Kernel::OM->Get('Kernel::System::Storable'); 2592 2593 if ( !@SettingList ) { 2594 my $Success = $Self->_DefaultSettingAddBulk( 2595 Settings => \%Settings, 2596 SettingList => \@SettingList, 2597 UserID => $Param{UserID}, 2598 ); 2599 2600 return if !$Success; 2601 } 2602 else { 2603 2604 my %DefaultSettingsAdd; 2605 2606 # Build setting list hash, to avoid slow grep expressions. 2607 my %SettingListLookup = map { $_->{Name} => $_ } @SettingList; 2608 2609 # Create/Update settings in DB. 2610 SETTING: 2611 for my $SettingName ( sort keys %Settings ) { 2612 2613 my $DefaultSetting = $SettingListLookup{$SettingName}; 2614 2615 if ( IsHashRefWithData($DefaultSetting) ) { 2616 2617 # Compare new Setting XML with the old one (skip if there is no difference). 2618 my $Updated = $Settings{$SettingName}->{XMLContentRaw} ne $DefaultSetting->{XMLContentRaw}; 2619 $Updated ||= $Settings{$SettingName}->{XMLFilename} ne $DefaultSetting->{XMLFilename}; 2620 2621 next SETTING if !$Updated; 2622 2623 # Create a local clone of the value to prevent any modification. 2624 my $Value = $StorableObject->Clone( 2625 Data => $Settings{$SettingName}->{XMLContentParsed}->{Value}, 2626 ); 2627 2628 my $EffectiveValue = $Self->SettingEffectiveValueGet( 2629 Value => $Value, 2630 ); 2631 2632 # Update default setting. 2633 my $Success = $SysConfigDBObject->DefaultSettingUpdate( 2634 DefaultID => $DefaultSetting->{DefaultID}, 2635 Name => $Settings{$SettingName}->{XMLContentParsed}->{Name}, 2636 Description => $Settings{$SettingName}->{XMLContentParsed}->{Description}->[0]->{Content} || '', 2637 Navigation => $Settings{$SettingName}->{XMLContentParsed}->{Navigation}->[0]->{Content} || '', 2638 IsInvisible => $Settings{$SettingName}->{XMLContentParsed}->{Invisible} || 0, 2639 IsReadonly => $Settings{$SettingName}->{XMLContentParsed}->{ReadOnly} || 0, 2640 IsRequired => $Settings{$SettingName}->{XMLContentParsed}->{Required} || 0, 2641 IsValid => $Settings{$SettingName}->{XMLContentParsed}->{Valid} || 0, 2642 HasConfigLevel => $Settings{$SettingName}->{XMLContentParsed}->{ConfigLevel} || 100, 2643 UserModificationPossible => $Settings{$SettingName}->{XMLContentParsed}->{UserModificationPossible} 2644 || 0, 2645 UserModificationActive => $Settings{$SettingName}->{XMLContentParsed}->{UserModificationActive} 2646 || 0, 2647 UserPreferencesGroup => $Settings{$SettingName}->{XMLContentParsed}->{UserPreferencesGroup}, 2648 XMLContentRaw => $Settings{$SettingName}->{XMLContentRaw}, 2649 XMLContentParsed => $Settings{$SettingName}->{XMLContentParsed}, 2650 XMLFilename => $Settings{$SettingName}->{XMLFilename}, 2651 EffectiveValue => $EffectiveValue, 2652 UserID => $Param{UserID}, 2653 ExclusiveLockGUID => $ExclusiveLockGUID, 2654 ); 2655 if ( !$Success ) { 2656 $Kernel::OM->Get('Kernel::System::Log')->Log( 2657 Priority => 'error', 2658 Message => 2659 "DefaultSettingUpdate failed for Config Item: $SettingName!", 2660 ); 2661 } 2662 2663 my @ModifiedList = $SysConfigDBObject->ModifiedSettingListGet( 2664 Name => $Settings{$SettingName}->{XMLContentParsed}->{Name}, 2665 ); 2666 2667 for my $ModifiedSetting (@ModifiedList) { 2668 2669 # So far everything is OK, if the structure or values does 2670 # not match anymore, modified values must be deleted. 2671 my %ValueCheckResult = $Self->SettingEffectiveValueCheck( 2672 EffectiveValue => $ModifiedSetting->{EffectiveValue}, 2673 XMLContentParsed => $Settings{$SettingName}->{XMLContentParsed}, 2674 SettingUID => $ModifiedSetting->{SettingUID}, 2675 StoreCache => 1, 2676 UserID => $Param{UserID}, 2677 ); 2678 2679 if ( !$ValueCheckResult{Success} ) { 2680 2681 $SysConfigDBObject->ModifiedSettingDelete( 2682 ModifiedID => $ModifiedSetting->{ModifiedID}, 2683 ); 2684 2685 $Kernel::OM->Get('Kernel::System::Log')->Log( 2686 Priority => 'error', 2687 Message => $ValueCheckResult{Error}, 2688 ); 2689 } 2690 } 2691 } 2692 else { 2693 2694 # Create a local clone of the value to prevent any modification. 2695 my $Value = $StorableObject->Clone( 2696 Data => $Settings{$SettingName}->{XMLContentParsed}->{Value}, 2697 ); 2698 2699 my $EffectiveValue = $Self->SettingEffectiveValueGet( 2700 Value => $Value, 2701 ); 2702 2703 $DefaultSettingsAdd{ $Settings{$SettingName}->{XMLContentParsed}->{Name} } = { 2704 Name => $Settings{$SettingName}->{XMLContentParsed}->{Name}, 2705 Description => $Settings{$SettingName}->{XMLContentParsed}->{Description}->[0]->{Content} || '', 2706 Navigation => $Settings{$SettingName}->{XMLContentParsed}->{Navigation}->[0]->{Content} || '', 2707 IsInvisible => $Settings{$SettingName}->{XMLContentParsed}->{Invisible} || 0, 2708 IsReadonly => $Settings{$SettingName}->{XMLContentParsed}->{ReadOnly} || 0, 2709 IsRequired => $Settings{$SettingName}->{XMLContentParsed}->{Required} || 0, 2710 IsValid => $Settings{$SettingName}->{XMLContentParsed}->{Valid} || 0, 2711 HasConfigLevel => $Settings{$SettingName}->{XMLContentParsed}->{ConfigLevel} || 100, 2712 UserModificationPossible => $Settings{$SettingName}->{XMLContentParsed}->{UserModificationPossible} 2713 || 0, 2714 UserModificationActive => $Settings{$SettingName}->{XMLContentParsed}->{UserModificationActive} 2715 || 0, 2716 UserPreferencesGroup => $Settings{$SettingName}->{XMLContentParsed}->{UserPreferencesGroup}, 2717 XMLContentRaw => $Settings{$SettingName}->{XMLContentRaw}, 2718 XMLContentParsed => $Settings{$SettingName}->{XMLContentParsed}, 2719 XMLFilename => $Settings{$SettingName}->{XMLFilename}, 2720 EffectiveValue => $EffectiveValue, 2721 NoCleanup => 1, 2722 UserID => $Param{UserID}, 2723 }; 2724 2725 # Delete individual cache. 2726 $CacheObject->Delete( 2727 Type => 'SysConfigDefault', 2728 Key => 'DefaultSettingGet::' . $Settings{$SettingName}->{XMLContentParsed}->{Name}, 2729 ); 2730 } 2731 } 2732 2733 if (%DefaultSettingsAdd) { 2734 my $Success = $Self->_DefaultSettingAddBulk( 2735 Settings => \%DefaultSettingsAdd, 2736 SettingList => \@SettingList, 2737 UserID => $Param{UserID}, 2738 ); 2739 return if !$Success; 2740 } 2741 } 2742 2743 # Unlock all the settings so they can be locked again afterwards. 2744 $SysConfigDBObject->DefaultSettingUnlock( 2745 UnlockAll => 1, 2746 ); 2747 2748 return 1; 2749} 2750 2751=head2 ConfigurationNavigationTree() 2752 2753Returns navigation tree in the hash format. 2754 2755 my %Result = $SysConfigObject->ConfigurationNavigationTree( 2756 RootNavigation => 'Parent', # (optional) If provided only sub groups of the root navigation are returned. 2757 UserModificationActive => 1, # (optional) Return settings that can be modified on user level only. 2758 IsValid => 1, # (optional) By default, display all settings. 2759 Category => 'OTRS' # (optional) 2760 ); 2761 2762Returns: 2763 2764 %Result = ( 2765 'Core' => { 2766 'Core::Cache' => {}, 2767 'Core::CustomerCompany' => {}, 2768 'Core::CustomerUser' => {}, 2769 'Core::Daemon' => { 2770 'Core::Daemon::ModuleRegistration' => {}, 2771 }, 2772 ... 2773 'Crypt' =>{ 2774 ... 2775 }, 2776 ... 2777 ); 2778 2779=cut 2780 2781sub ConfigurationNavigationTree { 2782 my ( $Self, %Param ) = @_; 2783 2784 $Param{RootNavigation} //= ''; 2785 $Param{UserModificationActive} //= '0'; 2786 2787 my $CacheType = 'SysConfigNavigation'; 2788 my $CacheKey = "NavigationTree::$Param{RootNavigation}::$Param{UserModificationActive}"; 2789 if ( defined $Param{IsValid} ) { 2790 if ( $Param{IsValid} ) { 2791 $CacheKey .= '::Valid'; 2792 } 2793 else { 2794 $CacheKey .= '::Invalid'; 2795 } 2796 } 2797 if ( defined $Param{Category} && $Param{Category} ) { 2798 if ( $Param{Category} eq 'All' ) { 2799 delete $Param{Category}; 2800 } 2801 else { 2802 $CacheKey .= "::Category=$Param{Category}"; 2803 } 2804 } 2805 2806 my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); 2807 2808 my $Cache = $CacheObject->Get( 2809 Type => $CacheType, 2810 Key => $CacheKey, 2811 ); 2812 2813 return %{$Cache} if ref $Cache eq 'HASH'; 2814 2815 my %CategoryOptions; 2816 if ( $Param{Category} ) { 2817 my %Categories = $Self->ConfigurationCategoriesGet(); 2818 if ( $Categories{ $Param{Category} } ) { 2819 %CategoryOptions = ( 2820 Category => $Param{Category}, 2821 CategoryFiles => $Categories{ $Param{Category} }->{Files}, 2822 ); 2823 } 2824 } 2825 2826 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 2827 2828 # Get all default settings 2829 my @SettingsRaw = $SysConfigDBObject->DefaultSettingListGet( 2830 %CategoryOptions, 2831 UserModificationActive => $Param{UserModificationActive} || undef, 2832 IsValid => $Param{IsValid}, 2833 ); 2834 2835 # For AgentPreference take into account which settings are Forbidden to update by user or disabled when counting 2836 # settings. See bug#13488 (https://bugs.otrs.org/show_bug.cgi?id=13488). 2837 if ( $Param{Action} && $Param{Action} eq 'AgentPreferences' ) { 2838 2839 # Get List of all modified settings which are valid and forbidden to update by user. 2840 my @ForbiddenSettings = $SysConfigDBObject->ModifiedSettingListGet( 2841 %CategoryOptions, 2842 UserModificationActive => 0, 2843 IsValid => 1, 2844 ); 2845 2846 # Get List of all modified settings which are invalid and allowed to update by user. 2847 my @InvalidSettings = $SysConfigDBObject->ModifiedSettingListGet( 2848 %CategoryOptions, 2849 UserModificationActive => 1, 2850 IsValid => 0, 2851 ); 2852 2853 my @ModifiedSettings; 2854 for my $Setting (@SettingsRaw) { 2855 push @ModifiedSettings, $Setting 2856 if !grep { $_->{Name} eq $Setting->{Name} } ( @ForbiddenSettings, @InvalidSettings ); 2857 } 2858 @SettingsRaw = @ModifiedSettings; 2859 2860 # Add settings which by default are not UserModifiedActive and are changed, to the navigation list 2861 # in preference screen. Please see bug#13489 for more information. 2862 @ModifiedSettings = $SysConfigDBObject->ModifiedSettingListGet( 2863 %CategoryOptions, 2864 UserModificationActive => 1, 2865 IsValid => 1, 2866 ); 2867 for my $Setting (@ModifiedSettings) { 2868 my %DefaultSetting = $SysConfigDBObject->DefaultSettingGet( 2869 Name => $Setting->{Name}, 2870 ); 2871 if ( !grep { $_->{Name} eq $DefaultSetting{Name} } @SettingsRaw ) { 2872 push @SettingsRaw, \%DefaultSetting; 2873 } 2874 } 2875 } 2876 2877 my @Settings; 2878 2879 # Skip invisible settings from the navigation tree 2880 SETTING: 2881 for my $Setting (@SettingsRaw) { 2882 next SETTING if $Setting->{IsInvisible}; 2883 2884 push @Settings, { 2885 Name => $Setting->{Name}, 2886 Navigation => $Setting->{Navigation}, 2887 }; 2888 } 2889 2890 my %Result = (); 2891 2892 my @RootNavigation; 2893 if ( $Param{RootNavigation} ) { 2894 @RootNavigation = split "::", $Param{RootNavigation}; 2895 } 2896 2897 # Remember ancestors. 2898 for my $Index ( 1 .. $#RootNavigation ) { 2899 $RootNavigation[$Index] = $RootNavigation[ $Index - 1 ] . '::' . $RootNavigation[$Index]; 2900 } 2901 2902 SETTING: 2903 for my $Setting (@Settings) { 2904 next SETTING if !$Setting->{Navigation}; 2905 my @Path = split "::", $Setting->{Navigation}; 2906 2907 # Remember ancestors. 2908 for my $Index ( 1 .. $#Path ) { 2909 $Path[$Index] = $Path[ $Index - 1 ] . '::' . $Path[$Index]; 2910 } 2911 2912 # Check if RootNavigation matches current setting. 2913 for my $Index ( 0 .. $#RootNavigation ) { 2914 next SETTING if !$Path[$Index]; 2915 2916 if ( $RootNavigation[$Index] ne $Path[$Index] ) { 2917 next SETTING; 2918 } 2919 } 2920 2921 # Remove root groups from Path. 2922 for my $Index ( 0 .. $#RootNavigation ) { 2923 shift @Path; 2924 } 2925 2926 %Result = $Self->_NavigationTree( 2927 Tree => \%Result, 2928 Array => \@Path, 2929 ); 2930 } 2931 2932 # Until now we have structure of the Navigation tree without sub-node count. We need this number to disable 2933 # click on empty nodes. We could implement that in the _NavigationTree, but it's not efficient(loop of 1800+ settings). 2934 # Instead, we extend result in the _NavigationTreeNodeCount. 2935 %Result = $Self->_NavigationTreeNodeCount( 2936 Tree => \%Result, 2937 Settings => \@Settings, 2938 ); 2939 2940 # Cache the results. 2941 $CacheObject->Set( 2942 Type => $CacheType, 2943 Key => $CacheKey, 2944 Value => \%Result, 2945 TTL => 30 * 24 * 60 * 60, 2946 ); 2947 2948 return %Result; 2949} 2950 2951=head2 ConfigurationListGet() 2952 2953Returns list of settings that matches provided parameters. 2954 2955 my @List = $SysConfigObject->ConfigurationListGet( 2956 Navigation => 'SomeNavigationGroup', # (optional) limit to the settings that have provided navigation 2957 TargetUserID => 2, # (optional) if provided, system returns setting for particular user only, 2958 # otherwise, returns global setting list 2959 IsValid => 1, # (optional) by default returns valid and invalid settings. 2960 Invisible => 0, # (optional) Include Invisible settings. By default, not included. 2961 UserPreferencesGroup => 'Advanced', # (optional) filter list by group. 2962 Translate => 0, # (optional) Translate translatable string in EffectiveValue. Default 0. 2963 OverriddenInXML => 1, # (optional) Consider changes made in Perl files. Default 0. Use it in modules only! 2964 UserID => 1, # Required if OverriddenInXML is set. 2965 ); 2966 2967Returns: 2968 2969 @List = ( 2970 { 2971 DefaultID => 123, 2972 ModifiedID => 456, # if modified 2973 Name => "ProductName", 2974 Description => "Defines the name of the application ...", 2975 Navigation => "ASimple::Path::Structure", 2976 IsInvisible => 1, 2977 IsReadonly => 0, 2978 IsRequired => 1, 2979 IsValid => 1, 2980 HasConfigLevel => 200, 2981 UserModificationPossible => 0, # 1 or 0 2982 UserModificationActive => 0, # 1 or 0 2983 UserPreferencesGroup => 'Advanced', # optional 2984 XMLContentRaw => "The XML structure as it is on the config file", 2985 XMLContentParsed => "XML parsed to Perl", 2986 XMLFilename => "Daemon.xml", 2987 EffectiveValue => "Product 6", 2988 DefaultValue => "Product 5", 2989 IsModified => 1, # 1 or 0 2990 IsDirty => 1, # 1 or 0 2991 ExclusiveLockGUID => 'A32CHARACTERLONGSTRINGFORLOCKING', 2992 ExclusiveLockUserID => 1, 2993 ExclusiveLockExpiryTime => '2016-05-29 11:09:04', 2994 CreateTime => "2016-05-29 11:04:04", 2995 ChangeTime => "2016-05-29 11:04:04", 2996 OverriddenFileName => 'ZZZDefauls.pm' 2997 }, 2998 { 2999 DefaultID => 321, 3000 Name => 'FieldName', 3001 # ... 3002 CreateTime => '2010-09-11 10:08:00', 3003 ChangeTime => '2011-01-01 01:01:01', 3004 }, 3005 # ... 3006 ); 3007 3008=cut 3009 3010sub ConfigurationListGet { 3011 my ( $Self, %Param ) = @_; 3012 3013 if ( $Param{OverriddenInXML} && !$Param{UserID} ) { 3014 $Kernel::OM->Get('Kernel::System::Log')->Log( 3015 Priority => 'error', 3016 Message => 'UserID is needed when OverriddenInXML is set!', 3017 ); 3018 return; 3019 } 3020 3021 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 3022 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 3023 3024 $Param{Translate} //= 0; # don't translate by default 3025 3026 my %CategoryOptions; 3027 if ( $Param{Category} ) { 3028 my %Categories = $Self->ConfigurationCategoriesGet(); 3029 if ( $Categories{ $Param{Category} } ) { 3030 %CategoryOptions = ( 3031 Category => $Param{Category}, 3032 CategoryFiles => $Categories{ $Param{Category} }->{Files}, 3033 ); 3034 } 3035 } 3036 3037 # Get all default settings for this navigation group. 3038 my @ConfigurationList = $SysConfigDBObject->DefaultSettingListGet( 3039 Navigation => $Param{Navigation}, 3040 UserModificationPossible => $Param{TargetUserID} ? 1 : undef, 3041 UserPreferencesGroup => $Param{UserPreferencesGroup} || undef, 3042 IsInvisible => $Param{Invisible} ? undef : 0, 3043 %CategoryOptions, 3044 ); 3045 3046 my $StorableObject = $Kernel::OM->Get('Kernel::System::Storable'); 3047 3048 # Update setting values with the modified settings. 3049 SETTING: 3050 for my $Setting (@ConfigurationList) { 3051 3052 if ( $Param{TargetUserID} ) { 3053 my %SettingGlobal = $Self->SettingGet( 3054 Name => $Setting->{Name}, 3055 IsGlobal => 1, 3056 Translate => $Param{Translate}, 3057 ); 3058 3059 if ( %SettingGlobal && $SettingGlobal{ModifiedID} ) { 3060 3061 # There is modified setting, but we need last deployed version. 3062 my %SettingDeployed = $SysConfigDBObject->ModifiedSettingVersionGetLast( 3063 Name => $Setting->{Name}, 3064 ); 3065 3066 if ( !IsHashRefWithData( \%SettingDeployed ) ) { 3067 %SettingDeployed = $Self->SettingGet( 3068 Name => $Setting->{Name}, 3069 Default => 1, 3070 Translate => $Param{Translate}, 3071 ); 3072 } 3073 3074 $Setting = { 3075 %SettingGlobal, 3076 %SettingDeployed, 3077 }; 3078 } 3079 else { 3080 3081 # Use default value. 3082 my %SettingDefault = $Self->SettingGet( 3083 Name => $Setting->{Name}, 3084 Default => 1, 3085 Translate => $Param{Translate}, 3086 ); 3087 3088 $Setting = \%SettingDefault; 3089 } 3090 } 3091 3092 # Remember default value. 3093 $Setting->{DefaultValue} = $Setting->{EffectiveValue}; 3094 if ( ref $Setting->{EffectiveValue} ) { 3095 $Setting->{DefaultValue} = $StorableObject->Clone( 3096 Data => $Setting->{EffectiveValue}, 3097 ); 3098 } 3099 3100 my %ModifiedSetting = $Self->SettingGet( 3101 Name => $Setting->{Name}, 3102 TargetUserID => $Param{TargetUserID} // undef, 3103 Translate => $Param{Translate}, 3104 OverriddenInXML => $Param{OverriddenInXML}, 3105 UserID => $Param{UserID}, 3106 ); 3107 3108 # Skip if setting is invalid. 3109 next SETTING if !IsHashRefWithData( \%ModifiedSetting ); 3110 next SETTING if !defined $ModifiedSetting{EffectiveValue}; 3111 3112 # Mark setting as modified. 3113 my $IsModified = DataIsDifferent( 3114 Data1 => \$Setting->{EffectiveValue}, 3115 Data2 => \$ModifiedSetting{EffectiveValue}, 3116 ) || 0; 3117 3118 $IsModified ||= $ModifiedSetting{IsValid} != $Setting->{IsValid}; 3119 $IsModified ||= $ModifiedSetting{UserModificationActive} != $Setting->{UserModificationActive}; 3120 3121 $Setting->{IsModified} = $IsModified ? 1 : 0; 3122 3123 # Update setting attributes. 3124 ATTRIBUTE: 3125 for my $Attribute ( 3126 qw(ModifiedID IsValid UserModificationActive UserPreferencesGroup EffectiveValue IsDirty ChangeTime XMLContentParsed SettingUID OverriddenFileName) 3127 ) 3128 { 3129 next ATTRIBUTE if !defined $ModifiedSetting{$Attribute}; 3130 3131 $Setting->{$Attribute} = $ModifiedSetting{$Attribute}; 3132 } 3133 } 3134 3135 if ( defined $Param{IsValid} ) { 3136 @ConfigurationList = grep { $_->{IsValid} == $Param{IsValid} } @ConfigurationList; 3137 } 3138 3139 if ( $Param{TargetUserID} ) { 3140 3141 # List contains all settings that can be activated. Get only those that are really activated. 3142 @ConfigurationList = grep { $_->{UserModificationActive} } @ConfigurationList; 3143 } 3144 3145 return @ConfigurationList; 3146} 3147 3148=head2 ConfigurationList() 3149 3150Wrapper of Kernel::System::SysConfig::DB::DefaultSettingList() - Get list of all settings. 3151 3152 my @SettingList = $SysConfigObject->ConfigurationList(); 3153 3154Returns: 3155 3156 @SettingList = ( 3157 { 3158 DefaultID => '123', 3159 Name => 'SettingName1', 3160 IsDirty => 1, 3161 }, 3162 { 3163 DefaultID => '124', 3164 Name => 'SettingName2', 3165 IsDirty => 0 3166 }, 3167 ... 3168 ); 3169 3170=cut 3171 3172sub ConfigurationList { 3173 my ( $Self, %Param ) = @_; 3174 3175 return $Kernel::OM->Get('Kernel::System::SysConfig::DB')->DefaultSettingList(%Param); 3176} 3177 3178=head2 ConfigurationInvalidList() 3179 3180Returns list of enabled settings that have invalid effective value. 3181 3182 my @List = $SysConfigObject->ConfigurationInvalidList( 3183 CachedOnly => 0, # (optional) Default 0. If enabled, system will return cached value. 3184 # If there is no cache yet, system will return empty list, but 3185 # it will also trigger async call to generate cache. 3186 Undeployed => 1, # (optional) Default 0. Check settings that are not deployed as well. 3187 NoCache => 1, # (optional) Default 0. If enabled, system won't check the cached value. 3188 ); 3189 3190Returns: 3191 3192 @List = ( "Setting1", "Setting5", ... ); 3193 3194=cut 3195 3196sub ConfigurationInvalidList { 3197 my ( $Self, %Param ) = @_; 3198 3199 my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); 3200 3201 my $CacheType = 'SysConfig'; 3202 my $CacheKey = 'ConfigurationInvalidList'; 3203 3204 if ( $Param{Undeployed} ) { 3205 $CacheKey .= '::Undeployed'; 3206 } 3207 3208 # Return cache. 3209 my $Cache = $CacheObject->Get( 3210 Type => $CacheType, 3211 Key => $CacheKey, 3212 ); 3213 3214 return @{$Cache} if ref $Cache eq 'ARRAY' && !$Param{NoCache}; 3215 3216 if ( $Param{CachedOnly} ) { 3217 3218 # There is no cache but caller expects quick answer. Return empty array, but create cache in async call. 3219 $Self->AsyncCall( 3220 ObjectName => 'Kernel::System::SysConfig', 3221 FunctionName => 'ConfigurationInvalidList', 3222 FunctionParams => {}, 3223 MaximumParallelInstances => 1, 3224 ); 3225 3226 return (); 3227 } 3228 3229 my @SettingsEnabled = $Self->ConfigurationListGet( 3230 IsValid => 1, 3231 Translate => 0, 3232 ); 3233 3234 my @InvalidSettings; 3235 3236 my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); 3237 my $CurrentSystemTime = $DateTimeObject->ToEpoch(); 3238 3239 $DateTimeObject->Add( 3240 Months => 1, 3241 ); 3242 my $ExpireTime = $DateTimeObject->ToEpoch(); 3243 3244 for my $Setting (@SettingsEnabled) { 3245 my %SettingData = $Self->SettingGet( 3246 Name => $Setting->{Name}, 3247 Deployed => $Param{Undeployed} ? undef : 1, 3248 NoCache => $Param{NoCache}, 3249 ); 3250 3251 my %EffectiveValueCheck = $Self->SettingEffectiveValueCheck( 3252 EffectiveValue => $SettingData{EffectiveValue}, 3253 XMLContentParsed => $Setting->{XMLContentParsed}, 3254 CurrentSystemTime => $CurrentSystemTime, 3255 ExpireTime => $ExpireTime, 3256 UserID => 1, 3257 ); 3258 3259 if ( $EffectiveValueCheck{Error} ) { 3260 push @InvalidSettings, $Setting->{Name}; 3261 } 3262 } 3263 3264 $CacheObject->Set( 3265 Type => $CacheType, 3266 Key => $CacheKey, 3267 Value => \@InvalidSettings, 3268 TTL => 60 * 60, # 1 hour 3269 ); 3270 3271 return @InvalidSettings; 3272} 3273 3274=head2 ConfigurationDeploy() 3275 3276Write configuration items from database into a perl module file. 3277 3278 my %Result = $SysConfigObject->ConfigurationDeploy( 3279 Comments => "Some comments", # (optional) 3280 NoValidation => 0, # (optional) 1 or 0, default 0, skips settings validation 3281 UserID => 123, # if ExclusiveLockGUID is used, UserID must match the user that creates the lock 3282 Force => 1, # (optional) proceed even if lock is set to another user 3283 NotDirty => 1, # (optional) do not use any values from modified dirty settings 3284 AllSettings => 1, # (optional) use dirty modified settings from all users 3285 DirtySettings => [ # (optional) use only this dirty modified settings from the current user 3286 'SettingOne', 3287 'SettingTwo', 3288 ], 3289 ); 3290 3291Returns: 3292 3293 %Result = ( 3294 Success => 1, # Deployment successful. 3295 ); 3296 3297 or 3298 3299 %Result = ( 3300 Success => 0, # Deployment failed. 3301 Error => 'Error...', # Error message (if available) 3302 ); 3303 3304=cut 3305 3306sub ConfigurationDeploy { 3307 my ( $Self, %Param ) = @_; 3308 3309 my %Result = ( 3310 Success => 0, 3311 ); 3312 3313 if ( !$Param{UserID} ) { 3314 $Kernel::OM->Get('Kernel::System::Log')->Log( 3315 Priority => 'error', 3316 Message => "Need UserID!", 3317 ); 3318 3319 return %Result; 3320 } 3321 if ( !IsPositiveInteger( $Param{UserID} ) ) { 3322 $Kernel::OM->Get('Kernel::System::Log')->Log( 3323 Priority => 'error', 3324 Message => "UserID is invalid!", 3325 ); 3326 return %Result; 3327 } 3328 3329 my $LanguageObject = $Kernel::OM->Get('Kernel::Language'); 3330 3331 if ( $Param{AllSettings} ) { 3332 $Param{NotDirty} = 0; 3333 $Param{DirtySettings} = undef; 3334 } 3335 elsif ( $Param{NotDirty} ) { 3336 $Param{AllSettings} = 0; 3337 $Param{DirtySettings} = undef; 3338 } 3339 3340 $Param{NoValidation} //= 0; 3341 3342 my $BasePath = 'Kernel/Config/Files/'; 3343 3344 # Parameter 'FileName' is intentionally not documented in the API as it is only used for testing. 3345 my $TargetPath = $BasePath . ( $Param{FileName} || "ZZZAAuto.pm" ); 3346 3347 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 3348 3349 my @UserDirtySettings = $SysConfigDBObject->ModifiedSettingListGet( 3350 IsDirty => 1, 3351 ChangeBy => $Param{UserID}, 3352 ); 3353 my %UserDirtySettingsLookup = map { $_->{Name} => 1 } @UserDirtySettings; 3354 3355 # Determine dirty settings to deploy (if not specified get all dirty settings from current user). 3356 if ( !$Param{DirtySettings} && !$Param{AllSettings} && !$Param{NotDirty} ) { 3357 @{ $Param{DirtySettings} } = keys %UserDirtySettingsLookup; 3358 } 3359 elsif ( $Param{DirtySettings} && !$Param{AllSettings} && !$Param{NotDirty} ) { 3360 3361 my @DirtySettings; 3362 3363 SETTING: 3364 for my $Setting ( @{ $Param{DirtySettings} } ) { 3365 next SETTING if !$UserDirtySettingsLookup{$Setting}; 3366 push @DirtySettings, $Setting; 3367 } 3368 3369 $Param{DirtySettings} = \@DirtySettings; 3370 } 3371 3372 my @DirtyDefaultList = $SysConfigDBObject->DefaultSettingList( 3373 IsDirty => 1, 3374 ); 3375 3376 my $AddNewDeployment; 3377 3378 # Check if deployment is really needed 3379 if ( $Param{NotDirty} ) { 3380 3381 # Check if default settings are to be deployed 3382 if (@DirtyDefaultList) { 3383 $AddNewDeployment = 1; 3384 } 3385 } 3386 elsif ( $Param{AllSettings} ) { 3387 3388 # Check if default settings are to be deployed 3389 if (@DirtyDefaultList) { 3390 $AddNewDeployment = 1; 3391 } 3392 else { 3393 3394 my @DirtyModifiedSettings = $SysConfigDBObject->ModifiedSettingListGet( 3395 IsDirty => 1, 3396 ); 3397 3398 # Check if modified settings are to be deployed 3399 if (@DirtyModifiedSettings) { 3400 $AddNewDeployment = 1; 3401 } 3402 } 3403 } 3404 elsif ( $Param{DirtySettings} ) { 3405 3406 # Check if default settings or user modified settings are to be deployed 3407 if ( @DirtyDefaultList || IsArrayRefWithData( $Param{DirtySettings} ) ) { 3408 $AddNewDeployment = 1; 3409 } 3410 } 3411 3412 # In case none of the previous options applied and there is no deployment in the database, 3413 # a new deployment is needed. 3414 my %LastDeployment = $SysConfigDBObject->DeploymentGetLast(); 3415 if ( !%LastDeployment ) { 3416 $AddNewDeployment = 1; 3417 } 3418 3419 my $EffectiveValueStrg = ''; 3420 3421 my @Settings = $Self->_GetSettingsToDeploy( 3422 %Param, 3423 NoCache => %LastDeployment ? 0 : 1, # do not cache only during initial rebuild config 3424 ); 3425 3426 my %EffectiveValueCheckResult; 3427 3428 my $MainObject = $Kernel::OM->Get('Kernel::System::Main'); 3429 my $StorableObject = $Kernel::OM->Get('Kernel::System::Storable'); 3430 3431 my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); 3432 my $CurrentSystemTime = $DateTimeObject->ToEpoch(); 3433 3434 $DateTimeObject->Add( 3435 Months => 1, 3436 ); 3437 my $ExpireTime = $DateTimeObject->ToEpoch(); 3438 3439 SETTING: 3440 for my $CurrentSetting (@Settings) { 3441 next SETTING if !$CurrentSetting->{IsValid}; 3442 3443 my %EffectiveValueCheck = $Self->SettingEffectiveValueCheck( 3444 XMLContentParsed => $CurrentSetting->{XMLContentParsed}, 3445 EffectiveValue => $CurrentSetting->{EffectiveValue}, 3446 NoValidation => $Param{NoValidation}, 3447 SettingUID => $CurrentSetting->{SettingUID}, 3448 CurrentSystemTime => $CurrentSystemTime, 3449 ExpireTime => $ExpireTime, 3450 UserID => $Param{UserID}, 3451 ); 3452 3453 # Instead of caching for each setting(1800+), skip caching, but remember results and cache only once. 3454 my $ValueString = $Param{EffectiveValue} // ''; 3455 if ( ref $ValueString ) { 3456 my $String = $StorableObject->Serialize( 3457 Data => $Param{EffectiveValue}, 3458 ); 3459 $ValueString = $MainObject->MD5sum( 3460 String => \$String, 3461 ); 3462 } 3463 3464 my $SettingKey = "$CurrentSetting->{SettingUID}::${ValueString}"; 3465 $EffectiveValueCheckResult{$SettingKey} = \%EffectiveValueCheck; 3466 3467 next SETTING if $EffectiveValueCheck{Success}; 3468 3469 # Check if setting is overridden, in this case allow deployment. 3470 my $OverriddenFileName = $Self->OverriddenFileNameGet( 3471 SettingName => $CurrentSetting->{Name}, 3472 UserID => $Param{UserID}, 3473 EffectiveValue => $CurrentSetting->{EffectiveValue}, 3474 ); 3475 3476 if ($OverriddenFileName) { 3477 3478 # Setting in the DB has invalid value, but it's overridden in perl file. 3479 3480 # Note: This check can't be moved to the SettingEffectiveValueCheck(), since it works with Cache, 3481 # so if perl file is updated, changes won't be reflected. 3482 next SETTING; 3483 } 3484 3485 $Kernel::OM->Get('Kernel::System::Log')->Log( 3486 Priority => 'error', 3487 Message => "Setting $CurrentSetting->{Name} Effective value is not correct: $EffectiveValueCheck{Error}", 3488 ); 3489 3490 $Result{Error} = $LanguageObject->Translate( "Invalid setting: %s", $CurrentSetting->{Name} ); 3491 return %Result; 3492 } 3493 3494 # Set cache for SettingEffectiveValueCheck(). 3495 $Self->_SettingEffectiveValueCheckCacheSet( 3496 Value => \%EffectiveValueCheckResult, 3497 NoValidation => $Param{NoValidation}, 3498 ); 3499 3500 # Combine settings effective values into a perl string 3501 if ( IsArrayRefWithData( \@Settings ) ) { 3502 $EffectiveValueStrg = $Self->_EffectiveValues2PerlFile( 3503 Settings => \@Settings, 3504 TargetPath => $TargetPath, 3505 ); 3506 if ( !defined $EffectiveValueStrg ) { 3507 $Kernel::OM->Get('Kernel::System::Log')->Log( 3508 Priority => 'error', 3509 Message => "Could not combine settings values into a perl hash", 3510 ); 3511 3512 $Result{Error} = $LanguageObject->Translate("Could not combine settings values into a perl hash."); 3513 return %Result; 3514 } 3515 } 3516 3517 # Force new deployment if current DB settings are different from the last deployment. 3518 if ( !$AddNewDeployment ) { 3519 3520 # Remove CurrentDeploymentID line for easy compare. 3521 my $LastDeploymentStrg = $LastDeployment{EffectiveValueStrg}; 3522 $LastDeploymentStrg =~ s{\$Self->\{'CurrentDeploymentID'\} [ ] = [ ] '\d+';\n}{}msx; 3523 3524 if ( $EffectiveValueStrg ne $LastDeploymentStrg ) { 3525 $AddNewDeployment = 1; 3526 } 3527 } 3528 3529 if ($AddNewDeployment) { 3530 3531 # Lock the deployment to be able add it to the DB. 3532 my $ExclusiveLockGUID = $SysConfigDBObject->DeploymentLock( 3533 UserID => $Param{UserID}, 3534 Force => $Param{Force}, 3535 ); 3536 if ( !$ExclusiveLockGUID ) { 3537 $Kernel::OM->Get('Kernel::System::Log')->Log( 3538 Priority => 'error', 3539 Message => "Can not lock the deployment for UserID '$Param{UserID}'!", 3540 ); 3541 3542 $Result{Error} = $LanguageObject->Translate( 3543 "Can not lock the deployment for UserID '%s'!", 3544 $Param{UserID}, 3545 ); 3546 return %Result; 3547 } 3548 3549 # Get system time stamp (string formatted). 3550 my $DateTimeObject = $Kernel::OM->Create( 3551 'Kernel::System::DateTime' 3552 ); 3553 my $TimeStamp = $DateTimeObject->ToString(); 3554 3555 my $HandleSettingsSuccess = $Self->_HandleSettingsToDeploy( 3556 %Param, 3557 DeploymentExclusiveLockGUID => $ExclusiveLockGUID, 3558 DeploymentTimeStamp => $TimeStamp, 3559 ); 3560 3561 my $DeploymentID; 3562 3563 if ($HandleSettingsSuccess) { 3564 3565 # Add a new deployment in the DB. 3566 $DeploymentID = $SysConfigDBObject->DeploymentAdd( 3567 Comments => $Param{Comments}, 3568 EffectiveValueStrg => \$EffectiveValueStrg, 3569 ExclusiveLockGUID => $ExclusiveLockGUID, 3570 DeploymentTimeStamp => $TimeStamp, 3571 UserID => $Param{UserID}, 3572 ); 3573 if ( !$DeploymentID ) { 3574 $Kernel::OM->Get('Kernel::System::Log')->Log( 3575 Priority => 'error', 3576 Message => "Could not create the deployment in the DB!", 3577 ); 3578 3579 } 3580 } 3581 3582 # Unlock the deployment, so new deployments can be added afterwards. 3583 my $Unlock = $SysConfigDBObject->DeploymentUnlock( 3584 ExclusiveLockGUID => $ExclusiveLockGUID, 3585 UserID => $Param{UserID}, 3586 ); 3587 if ( !$Unlock ) { 3588 $Kernel::OM->Get('Kernel::System::Log')->Log( 3589 Priority => 'error', 3590 Message => "Could not remove deployment lock for UserID '$Param{UserID}'", 3591 ); 3592 } 3593 3594 # Make sure to return on errors after we unlock the deployment. 3595 if ( !$HandleSettingsSuccess || !$DeploymentID ) { 3596 return %Result; 3597 } 3598 3599 # If setting is updated on global level, check all user specific settings, maybe it's needed 3600 # to remove duplicates. 3601 if ( $Self->can('UserConfigurationResetToGlobal') ) { # OTRS Business Solution™ 3602 3603 my @DeployedSettings; 3604 if ( $Param{DirtySettings} ) { 3605 @DeployedSettings = @{ $Param{DirtySettings} }; 3606 } 3607 else { 3608 for my $Setting (@Settings) { 3609 push @DeployedSettings, $Setting->{Name}; 3610 } 3611 } 3612 3613 if ( scalar @DeployedSettings ) { 3614 $Self->UserConfigurationResetToGlobal( 3615 Settings => \@DeployedSettings, 3616 UserID => $Param{UserID}, 3617 ); 3618 } 3619 } 3620 3621 my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); 3622 3623 # Delete categories cache. 3624 $CacheObject->Delete( 3625 Type => 'SysConfig', 3626 Key => 'ConfigurationCategoriesGet', 3627 ); 3628 3629 $CacheObject->Delete( 3630 Type => 'SysConfig', 3631 Key => 'ConfigurationInvalidList', 3632 ); 3633 $CacheObject->Delete( 3634 Type => 'SysConfig', 3635 Key => 'ConfigurationInvalidList::Undeployed', 3636 ); 3637 } 3638 else { 3639 $EffectiveValueStrg = $LastDeployment{EffectiveValueStrg}; 3640 } 3641 3642 # Base folder for deployment could be not present. 3643 if ( !-d $BasePath ) { 3644 mkdir $BasePath; 3645 } 3646 3647 $Result{Success} = $Self->_FileWriteAtomic( 3648 Filename => "$Self->{Home}/$TargetPath", 3649 Content => \$EffectiveValueStrg, 3650 ); 3651 3652 return %Result; 3653} 3654 3655=head2 ConfigurationDeployList() 3656 3657Get deployment list with complete data. 3658 3659 my @List = $SysConfigObject->ConfigurationDeployList(); 3660 3661Returns: 3662 3663 @List = ( 3664 { 3665 DeploymentID => 123, 3666 Comments => 'Some Comments', 3667 EffectiveValueStrg => $SettingEffectiveValues, # String with the value of all settings, 3668 # as seen in the Perl configuration file. 3669 CreateTime => "2016-05-29 11:04:04", 3670 CreateBy => 123, 3671 }, 3672 { 3673 DeploymentID => 456, 3674 Comments => 'Some Comments', 3675 EffectiveValueStrg => $SettingEffectiveValues2, # String with the value of all settings, 3676 # as seen in the Perl configuration file. 3677 CreateTime => "2016-05-29 12:00:01", 3678 CreateBy => 123, 3679 }, 3680 # ... 3681 ); 3682 3683=cut 3684 3685sub ConfigurationDeployList { 3686 my ( $Self, %Param ) = @_; 3687 3688 return $Kernel::OM->Get('Kernel::System::SysConfig::DB')->DeploymentListGet(); 3689} 3690 3691=head2 ConfigurationDeploySync() 3692Updates C<ZZZAAuto.pm> to the latest deployment found in the database. 3693 3694 my $Success = $SysConfigObject->ConfigurationDeploySync(); 3695 3696=cut 3697 3698sub ConfigurationDeploySync { 3699 my ( $Self, %Param ) = @_; 3700 3701 my $Home = $Self->{Home}; 3702 my $TargetPath = "$Home/Kernel/Config/Files/ZZZAAuto.pm"; 3703 3704 if ( -e $TargetPath ) { 3705 if ( !require $TargetPath ) { 3706 $Kernel::OM->Get('Kernel::System::Log')->Log( 3707 Priority => 'error', 3708 Message => "Could not load $TargetPath, $1", 3709 ); 3710 return; 3711 } 3712 3713 do $TargetPath; 3714 } 3715 3716 $Kernel::OM->ObjectsDiscard( 3717 Objects => [ 'Kernel::Config', ], 3718 ); 3719 3720 my $CurrentDeploymentID = $Kernel::OM->Get('Kernel::Config')->Get('CurrentDeploymentID') || 0; 3721 3722 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 3723 3724 # Check that all deployments are valid, but wait if there are deployments in add procedure 3725 my $CleanupSuccess; 3726 TRY: 3727 for my $Try ( 1 .. 40 ) { 3728 $CleanupSuccess = $SysConfigDBObject->DeploymentListCleanup(); 3729 last TRY if !$CleanupSuccess; 3730 last TRY if $CleanupSuccess == 1; 3731 sleep .5; 3732 } 3733 if ( $CleanupSuccess != 1 ) { 3734 $Kernel::OM->Get('Kernel::System::Log')->Log( 3735 Priority => 'error', 3736 Message => "There are invalid deployments in the database that could not be removed!", 3737 ); 3738 return; 3739 } 3740 3741 my %LastDeployment = $SysConfigDBObject->DeploymentGetLast(); 3742 3743 if ( !%LastDeployment ) { 3744 $Kernel::OM->Get('Kernel::System::Log')->Log( 3745 Priority => 'error', 3746 Message => "No deployments found in Database!", 3747 ); 3748 return; 3749 } 3750 3751 if ( $CurrentDeploymentID ne $LastDeployment{DeploymentID} ) { 3752 3753 # Write latest deployment to ZZZAAuto.pm 3754 my $EffectiveValueStrg = $LastDeployment{EffectiveValueStrg}; 3755 my $Success = $Self->_FileWriteAtomic( 3756 Filename => $TargetPath, 3757 Content => \$EffectiveValueStrg, 3758 ); 3759 3760 return if !$Success; 3761 } 3762 3763 # Sync also user specific settings (if available). 3764 return 1 if !$Self->can('UserConfigurationDeploySync'); # OTRS Business Solution™ 3765 $Self->UserConfigurationDeploySync(); 3766 3767 return 1; 3768} 3769 3770=head2 ConfigurationDeployCleanup() 3771 3772Cleanup old deployments from the database. 3773 3774 my $Success = $SysConfigObject->ConfigurationDeployCleanup(); 3775 3776Returns: 3777 3778 $Success = 1; # or false in case of an error 3779 3780=cut 3781 3782sub ConfigurationDeployCleanup { 3783 my ( $Self, %Param ) = @_; 3784 3785 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 3786 3787 my @List = $SysConfigDBObject->DeploymentListGet(); 3788 my @ListIDs = map { $_->{DeploymentID} } @List; 3789 3790 my $RemainingDeploments = $Kernel::OM->Get('Kernel::Config')->Get('SystemConfiguration::MaximumDeployments') // 20; 3791 @ListIDs = splice( @ListIDs, $RemainingDeploments ); 3792 3793 DEPLOYMENT: 3794 for my $DeploymentID (@ListIDs) { 3795 3796 my $Success = $SysConfigDBObject->DeploymentDelete( 3797 DeploymentID => $DeploymentID, 3798 ); 3799 3800 if ( !$Success ) { 3801 $Kernel::OM->Get('Kernel::System::Log')->Log( 3802 Priority => 'notice', 3803 Message => "Was not possible to delete deployment $DeploymentID!", 3804 ); 3805 next DEPLOYMENT; 3806 } 3807 } 3808 3809 return 1; 3810} 3811 3812=head2 ConfigurationDeployGet() 3813 3814Wrapper of Kernel::System::SysConfig::DB::DeploymentGet() - Get deployment information. 3815 3816 my %Deployment = $SysConfigObject->ConfigurationDeployGet( 3817 DeploymentID => 123, 3818 ); 3819 3820Returns: 3821 3822 %Deployment = ( 3823 DeploymentID => 123, 3824 Comments => 'Some Comments', 3825 EffectiveValueStrg => $SettingEffectiveValues, # String with the value of all settings, 3826 # as seen in the Perl configuration file. 3827 CreateTime => "2016-05-29 11:04:04", 3828 CreateBy => 123, 3829 ); 3830 3831=cut 3832 3833sub ConfigurationDeployGet { 3834 my ( $Self, %Param ) = @_; 3835 3836 return $Kernel::OM->Get('Kernel::System::SysConfig::DB')->DeploymentGet(%Param); 3837} 3838 3839=head2 ConfigurationDeployGetLast() 3840 3841Wrapper of Kernel::System::SysConfig::DBDeploymentGetLast() - Get last deployment information. 3842 3843 my %Deployment = $SysConfigObject->ConfigurationDeployGetLast(); 3844 3845Returns: 3846 3847 %Deployment = ( 3848 DeploymentID => 123, 3849 Comments => 'Some Comments', 3850 EffectiveValueStrg => $SettingEffectiveValues, # String with the value of all settings, 3851 # as seen in the Perl configuration file. 3852 CreateTime => "2016-05-29 11:04:04", 3853 CreateBy => 123, 3854 ); 3855 3856=cut 3857 3858sub ConfigurationDeployGetLast { 3859 my ( $Self, %Param ) = @_; 3860 3861 return $Kernel::OM->Get('Kernel::System::SysConfig::DB')->DeploymentGetLast(); 3862} 3863 3864=head2 ConfigurationDeploySettingsListGet() 3865 3866Gets full modified settings information contained on a given deployment. 3867 3868 my @List = $SysConfigObject->ConfigurationDeploySettingsListGet( 3869 DeploymentID => 123, 3870 ); 3871 3872Returns: 3873 3874 @List = ( 3875 { 3876 DefaultID => 123, 3877 ModifiedID => 456, 3878 ModifiedVersionID => 789, 3879 Name => "ProductName", 3880 Description => "Defines the name of the application ...", 3881 Navigation => "ASimple::Path::Structure", 3882 IsInvisible => 1, 3883 IsReadonly => 0, 3884 IsRequired => 1, 3885 IsValid => 1, 3886 HasConfigLevel => 200, 3887 UserModificationPossible => 0, # 1 or 0 3888 XMLContentRaw => "The XML structure as it is on the config file", 3889 XMLContentParsed => "XML parsed to Perl", 3890 EffectiveValue => "Product 6", 3891 DefaultValue => "Product 5", 3892 IsModified => 1, # 1 or 0 3893 IsDirty => 1, # 1 or 0 3894 ExclusiveLockGUID => 'A32CHARACTERLONGSTRINGFORLOCKING', 3895 ExclusiveLockUserID => 1, 3896 ExclusiveLockExpiryTime => '2016-05-29 11:09:04', 3897 CreateTime => "2016-05-29 11:04:04", 3898 ChangeTime => "2016-05-29 11:04:04", 3899 }, 3900 { 3901 DefaultID => 321, 3902 ModifiedID => 654, 3903 ModifiedVersionID => 987, 3904 Name => 'FieldName', 3905 # ... 3906 CreateTime => '2010-09-11 10:08:00', 3907 ChangeTime => '2011-01-01 01:01:01', 3908 }, 3909 # ... 3910 ); 3911 3912=cut 3913 3914sub ConfigurationDeploySettingsListGet { 3915 my ( $Self, %Param ) = @_; 3916 3917 if ( !$Param{DeploymentID} ) { 3918 $Kernel::OM->Get('Kernel::System::Log')->Log( 3919 Priority => 'error', 3920 Message => "Need DeploymentID", 3921 ); 3922 return; 3923 } 3924 3925 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 3926 3927 # get modified version of this deployment 3928 my %ModifiedVersionList = $SysConfigDBObject->DeploymentModifiedVersionList( 3929 DeploymentID => $Param{DeploymentID}, 3930 ); 3931 3932 my @ModifiedVersions = sort keys %ModifiedVersionList; 3933 3934 my @Settings; 3935 for my $ModifiedVersionID ( sort @ModifiedVersions ) { 3936 3937 my %Versions; 3938 3939 # Get the modified version. 3940 my %ModifiedSettingVersion = $SysConfigDBObject->ModifiedSettingVersionGet( 3941 ModifiedVersionID => $ModifiedVersionID, 3942 ); 3943 3944 # Get default version. 3945 my %DefaultSetting = $SysConfigDBObject->DefaultSettingVersionGet( 3946 DefaultVersionID => $ModifiedSettingVersion{DefaultVersionID}, 3947 ); 3948 3949 # Update default setting attributes. 3950 for my $Attribute ( 3951 qw(ModifiedID IsValid EffectiveValue IsDirty CreateTime ChangeTime) 3952 ) 3953 { 3954 $DefaultSetting{$Attribute} = $ModifiedSettingVersion{$Attribute}; 3955 } 3956 3957 $DefaultSetting{ModifiedVersionID} = $ModifiedVersionID; 3958 3959 push @Settings, \%DefaultSetting; 3960 } 3961 3962 return @Settings; 3963} 3964 3965=head2 ConfigurationIsDirtyCheck() 3966 3967Check if there are not deployed changes on system configuration. 3968 3969 my $Result = $SysConfigObject->ConfigurationIsDirtyCheck( 3970 UserID => 123, # optional, the user that changes a modified setting 3971 ); 3972 3973Returns: 3974 3975 $Result = 1; # or 0 if configuration is not dirty. 3976 3977=cut 3978 3979sub ConfigurationIsDirtyCheck { 3980 my ( $Self, %Param ) = @_; 3981 3982 return $Kernel::OM->Get('Kernel::System::SysConfig::DB')->ConfigurationIsDirty(%Param); 3983} 3984 3985=head2 ConfigurationDump() 3986 3987Creates a YAML file with the system configuration settings. 3988 3989 my $ConfigurationDumpYAML = $SysConfigObject->ConfigurationDump( 3990 OnlyValues => 0, # optional, default 0, dumps only the setting effective value instead of the whole setting attributes. 3991 SkipDefaultSettings => 0, # optional, default 0, do not include default settings 3992 SkipModifiedSettings => 0, # optional, default 0, do not include modified settings 3993 SkipUserSettings => 0, # optional, default 0, do not include user settings 3994 DeploymentID => 123, # optional, if it is provided the modified settings are retrieved from versions 3995 ); 3996 3997Returns: 3998 3999 my $ConfigurationDumpYAML = '--- 4000Default: 4001 Setting1: 4002 DefaultID: 23766 4003 Name: Setting1 4004 # ... 4005 Setting2: 4006 # ... 4007Modified: 4008 Setting1 4009 DefaultID: 23776 4010 ModifiedID: 1250 4011 Name: Setting1 4012 # ... 4013 # ... 4014JDoe: 4015 Setting2 4016 DefaultID: 23777 4017 ModifiedID: 1251 4018 Name: Setting2 4019 # ... 4020 # ... 4021# ... 4022 4023or 4024 4025 my $ConfigurationDumpYAML = $SysConfigObject->ConfigurationDump( 4026 OnlyValues => 1, 4027 ); 4028 4029Returns: 4030 4031 my $ConfigurationDumpYAML = '--- 4032Default: 4033 Setting1: Test 4034 Setting2: Test 4035 # ... 4036Modified: 4037 Setting1: TestUpdate 4038 # ... 4039JDoe: 4040 Setting2: TestUser 4041 # ... 4042# ... 4043'; 4044 4045=cut 4046 4047sub ConfigurationDump { 4048 my ( $Self, %Param ) = @_; 4049 4050 my $Result = {}; 4051 4052 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 4053 4054 if ( !$Param{SkipDefaultSettings} ) { 4055 4056 my @SettingsList = $SysConfigDBObject->DefaultSettingListGet(); 4057 4058 SETTING: 4059 for my $Setting (@SettingsList) { 4060 if ( $Param{OnlyValues} ) { 4061 $Result->{Default}->{ $Setting->{Name} } = $Setting->{EffectiveValue}; 4062 next SETTING; 4063 } 4064 $Result->{Default}->{ $Setting->{Name} } = $Setting; 4065 } 4066 4067 } 4068 4069 if ( !$Param{SkipModifiedSettings} || !$Param{SkipUserSettings} ) { 4070 my @SettingsList; 4071 4072 if ( !$Param{DeploymentID} ) { 4073 @SettingsList = $SysConfigDBObject->ModifiedSettingListGet(); 4074 } 4075 else { 4076 # Get the modified versions involved into the deployment 4077 my %ModifiedVersionList = $SysConfigDBObject->DeploymentModifiedVersionList( 4078 DeploymentID => $Param{DeploymentID}, 4079 ); 4080 4081 return if !%ModifiedVersionList; 4082 4083 my @ModifiedVersions = sort keys %ModifiedVersionList; 4084 4085 MODIFIEDVERSIONID: 4086 for my $ModifiedVersionID (@ModifiedVersions) { 4087 4088 my %ModifiedSettingVersion = $SysConfigDBObject->ModifiedSettingVersionGet( 4089 ModifiedVersionID => $ModifiedVersionID, 4090 ); 4091 next MODIFIEDVERSIONID if !%ModifiedSettingVersion; 4092 4093 push @SettingsList, \%ModifiedSettingVersion; 4094 } 4095 } 4096 4097 if ( !$Param{SkipModifiedSettings} ) { 4098 SETTING: 4099 for my $Setting (@SettingsList) { 4100 next SETTING if $Setting->{TargetUserID}; 4101 4102 if ( $Param{OnlyValues} ) { 4103 $Result->{'Modified'}->{ $Setting->{Name} } = $Setting->{EffectiveValue}; 4104 next SETTING; 4105 } 4106 $Result->{'Modified'}->{ $Setting->{Name} } = $Setting; 4107 } 4108 } 4109 4110 if ( !$Param{SkipUserSettings} && $Self->can('UserConfigurationDump') ) { # OTRS Business Solution™ 4111 my %UserSettings = $Self->UserConfigurationDump( 4112 SettingList => \@SettingsList, 4113 OnlyValues => $Param{OnlyValues}, 4114 ); 4115 if ( scalar keys %UserSettings ) { 4116 %{$Result} = ( %{$Result}, %UserSettings ); 4117 } 4118 } 4119 } 4120 4121 my $YAMLString = $Kernel::OM->Get('Kernel::System::YAML')->Dump( 4122 Data => $Result, 4123 ); 4124 4125 return $YAMLString; 4126} 4127 4128=head2 ConfigurationLoad() 4129 4130Takes a YAML file with settings definition and try to import it into the system. 4131 4132 my $Success = $SysConfigObject->ConfigurationLoad( 4133 ConfigurationYAML => $YAMLString, # a YAML string in the format of L<ConfigurationDump()> 4134 UserID => 123, 4135 ); 4136 4137=cut 4138 4139sub ConfigurationLoad { 4140 my ( $Self, %Param ) = @_; 4141 4142 for my $Needed (qw(ConfigurationYAML UserID)) { 4143 if ( !$Param{$Needed} ) { 4144 $Kernel::OM->Get('Kernel::System::Log')->Log( 4145 Priority => 'error', 4146 Message => "Need $Needed!", 4147 ); 4148 4149 return; 4150 } 4151 } 4152 4153 my %ConfigurationRaw 4154 = %{ $Kernel::OM->Get('Kernel::System::YAML')->Load( Data => $Param{ConfigurationYAML} ) || {} }; 4155 4156 if ( !%ConfigurationRaw ) { 4157 $Kernel::OM->Get('Kernel::System::Log')->Log( 4158 Priority => 'error', 4159 Message => "ConfigurationYAML is invalid!", 4160 ); 4161 4162 return; 4163 } 4164 4165 my $UserObject = $Kernel::OM->Get('Kernel::System::User'); 4166 4167 # Get the configuration sections to import (skip Default and non existing users). 4168 my $ValidSections; 4169 my %Configuration; 4170 SECTION: 4171 for my $Section ( sort keys %ConfigurationRaw ) { 4172 4173 next SECTION if $Section eq 'Default'; 4174 4175 if ( $Section eq 'Modified' ) { 4176 $Configuration{$Section} = $ConfigurationRaw{$Section}; 4177 next SECTION; 4178 } 4179 4180 my $UserID = $UserObject->UserLookup( 4181 UserLogin => $Section, 4182 Silent => 1, 4183 ); 4184 4185 if ( !$UserID ) { 4186 $Kernel::OM->Get('Kernel::System::Log')->Log( 4187 Priority => 'notice', 4188 Message => "Settings for user $Section could not be added! User does not exists.", 4189 ); 4190 next SECTION; 4191 } 4192 4193 $Configuration{$UserID} = $ConfigurationRaw{$Section}; 4194 4195 } 4196 4197 # Early return if there is nothing to update. 4198 return 1 if !%Configuration; 4199 4200 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 4201 my $Result = 1; 4202 4203 SECTION: 4204 for my $Section ( sort keys %Configuration ) { 4205 4206 my $UserID = ''; 4207 my $ScopeString = '(global)'; 4208 4209 my $TargetUserID = undef; 4210 if ( lc $Section ne lc 'Modified' ) { 4211 $TargetUserID = $Section; 4212 $UserID = $Section; 4213 $ScopeString = "(for user $Section)"; 4214 } 4215 4216 SETTINGNAME: 4217 for my $SettingName ( sort keys %{ $Configuration{$Section} } ) { 4218 4219 my %CurrentSetting = $Self->SettingGet( 4220 Name => $SettingName, 4221 ); 4222 4223 # Set error in case non existing settings (either default or modified); 4224 if ( !%CurrentSetting ) { 4225 $Result = '-1'; 4226 next SETTINGNAME; 4227 } 4228 4229 my $ExclusiveLockGUID = $SysConfigDBObject->DefaultSettingLock( 4230 Name => $SettingName, 4231 Force => 1, 4232 UserID => $UserID || $Param{UserID}, 4233 ); 4234 4235 my $UserModificationActive = $TargetUserID ? undef : $CurrentSetting{UserModificationActive}; 4236 4237 my %Result = $Self->SettingUpdate( 4238 Name => $SettingName, 4239 IsValid => $Configuration{$Section}->{$SettingName}->{IsValid}, 4240 EffectiveValue => $Configuration{$Section}->{$SettingName}->{EffectiveValue}, 4241 UserModificationActive => $UserModificationActive, 4242 TargetUserID => $TargetUserID, 4243 ExclusiveLockGUID => $ExclusiveLockGUID, 4244 UserID => $UserID || $Param{UserID}, 4245 ); 4246 if ( !$Result{Success} ) { 4247 $Kernel::OM->Get('Kernel::System::Log')->Log( 4248 Priority => 'error', 4249 Message => "Setting $SettingName $ScopeString was not correctly updated!", 4250 ); 4251 4252 $Result = '-1'; 4253 } 4254 } 4255 4256 # Only deploy user specific settings; 4257 next SECTION if !$TargetUserID; 4258 next SECTION if !$Self->can('UserConfigurationDeploy'); # OTRS Business Solution™ 4259 4260 # Deploy user configuration requires another package to be installed. 4261 my $Success = $Self->UserConfigurationDeploy( 4262 TargetUserID => $TargetUserID, 4263 UserID => $Param{UserID}, 4264 ); 4265 4266 } 4267 4268 return $Result; 4269} 4270 4271=head2 ConfigurationDirtySettingsList() 4272 4273Returns a list of setting names that are dirty. 4274 4275 my @Result = $SysConfigObject->ConfigurationDirtySettingsList( 4276 ChangeBy => 123, 4277 ); 4278 4279Returns: 4280 4281 $Result = ['SettingA', 'SettingB', 'SettingC']; 4282 4283=cut 4284 4285sub ConfigurationDirtySettingsList { 4286 my ( $Self, %Param ) = @_; 4287 4288 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 4289 4290 my @DefaultSettingsList = $SysConfigDBObject->DefaultSettingListGet( 4291 IsDirty => 1, 4292 ); 4293 @DefaultSettingsList = map { $_->{Name} } @DefaultSettingsList; 4294 4295 my @ModifiedSettingsList = $SysConfigDBObject->ModifiedSettingListGet( 4296 IsDirty => 1, 4297 IsGlobal => 1, 4298 ChangeBy => $Param{ChangeBy} || undef, 4299 ); 4300 @ModifiedSettingsList = map { $_->{Name} } @ModifiedSettingsList; 4301 4302 # Combine Default and Modified dirty settings. 4303 my @ListNames = ( @DefaultSettingsList, @ModifiedSettingsList ); 4304 my %Names = map { $_ => 1 } @ListNames; 4305 @ListNames = sort keys %Names; 4306 4307 return @ListNames; 4308} 4309 4310=head2 ConfigurationLockedSettingsList() 4311 4312Returns a list of setting names that are locked in general or by user. 4313 4314 my @Result = $SysConfigObject->ConfigurationLockedSettingsList( 4315 ExclusiveLockUserID => 2, # Optional, ID of the user for which the default setting is locked 4316 ); 4317 4318Returns: 4319 4320 $Result = ['SettingA', 'SettingB', 'SettingC']; 4321 4322=cut 4323 4324sub ConfigurationLockedSettingsList { 4325 my ( $Self, %Param ) = @_; 4326 4327 my @DefaultSettingsList = $Kernel::OM->Get('Kernel::System::SysConfig::DB')->DefaultSettingListGet( 4328 Locked => 1, 4329 ); 4330 4331 return if !IsArrayRefWithData( \@DefaultSettingsList ); 4332 4333 if ( $Param{ExclusiveLockUserID} ) { 4334 @DefaultSettingsList 4335 = map { $_->{Name} } grep { $_->{ExclusiveLockUserID} eq $Param{ExclusiveLockUserID} } @DefaultSettingsList; 4336 } 4337 else { 4338 @DefaultSettingsList = map { $_->{Name} } @DefaultSettingsList; 4339 } 4340 4341 return @DefaultSettingsList; 4342} 4343 4344=head2 ConfigurationSearch() 4345 4346Returns a list of setting names. 4347 4348 my @Result = $SysConfigObject->ConfigurationSearch( 4349 Search => 'The search string', # (optional) 4350 Category => 'OTRS' # (optional) 4351 IncludeInvisible => 1, # (optional) Default 0. 4352 ); 4353 4354Returns: 4355 4356 $Result = ['SettingA', 'SettingB', 'SettingC']; 4357 4358=cut 4359 4360sub ConfigurationSearch { 4361 my ( $Self, %Param ) = @_; 4362 4363 if ( !$Param{Search} && !$Param{Category} ) { 4364 $Kernel::OM->Get('Kernel::System::Log')->Log( 4365 Priority => 'error', 4366 Message => "Search or Category is needed", 4367 ); 4368 return; 4369 } 4370 4371 $Param{Search} ||= ''; 4372 $Param{Category} ||= ''; 4373 4374 my $Search = lc $Param{Search}; 4375 4376 my %Settings = $Self->ConfigurationTranslatedGet( 4377 IncludeInvisible => $Param{IncludeInvisible}, 4378 ); 4379 4380 my %Result; 4381 4382 SETTING: 4383 for my $SettingName ( sort keys %Settings ) { 4384 4385 # check category 4386 if ( 4387 $Param{Category} && 4388 $Param{Category} ne 'All' && 4389 $Settings{$SettingName}->{Category} && 4390 $Settings{$SettingName}->{Category} ne $Param{Category} 4391 ) 4392 { 4393 next SETTING; 4394 } 4395 4396 # check invisible 4397 if ( 4398 !$Param{IncludeInvisible} 4399 && $Settings{$SettingName}->{IsInvisible} 4400 ) 4401 { 4402 next SETTING; 4403 } 4404 4405 if ( !$Param{Search} ) { 4406 $Result{$SettingName} = 1; 4407 next SETTING; 4408 } 4409 4410 $Param{Search} =~ s{ +}{ }g; 4411 my @SearchTerms = split ' ', $Param{Search}; 4412 4413 SEARCHTERM: 4414 for my $SearchTerm (@SearchTerms) { 4415 4416 # do not search with the x and/or g modifier as it would produce wrong search results! 4417 if ( $Settings{$SettingName}->{Metadata} =~ m{\Q$SearchTerm\E}msi ) { 4418 4419 next SEARCHTERM if $Result{$SettingName}; 4420 4421 $Result{$SettingName} = 1; 4422 } 4423 } 4424 } 4425 4426 return ( sort keys %Result ); 4427} 4428 4429=head2 ConfigurationCategoriesGet() 4430 4431Returns a list of categories with their filenames. 4432 4433 my %Categories = $SysConfigObject->ConfigurationCategoriesGet(); 4434 4435Returns: 4436 4437 %Categories = ( 4438 All => { 4439 DisplayName => 'All Settings', 4440 Files => [], 4441 }, 4442 OTRS => { 4443 DisplayName => 'OTRS', 4444 Files => ['Calendar.xml', CloudServices.xml', 'Daemon.xml', 'Framework.xml', 'GenericInterface.xml', 'ProcessManagement.xml', 'Ticket.xml' ], 4445 }, 4446 # ... 4447 ); 4448 4449=cut 4450 4451sub ConfigurationCategoriesGet { 4452 my ( $Self, %Param ) = @_; 4453 4454 my $CacheType = 'SysConfig'; 4455 my $CacheKey = 'ConfigurationCategoriesGet'; 4456 4457 my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); 4458 4459 # Return cache. 4460 my $Cache = $CacheObject->Get( 4461 Type => $CacheType, 4462 Key => $CacheKey, 4463 ); 4464 4465 return %{$Cache} if ref $Cache eq 'HASH'; 4466 4467 # Set framework files. 4468 my %Result = ( 4469 All => { 4470 DisplayName => Translatable('All Settings'), 4471 Files => [], 4472 }, 4473 OTRS => { 4474 DisplayName => 'OTRS', 4475 Files => [ 4476 'Calendar.xml', 'CloudServices.xml', 'Daemon.xml', 'Framework.xml', 4477 'GenericInterface.xml', 'ProcessManagement.xml', 'Ticket.xml', 4478 ], 4479 }, 4480 ); 4481 4482 my @PackageList = $Kernel::OM->Get('Kernel::System::Package')->RepositoryList(); 4483 4484 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 4485 4486 # Get files from installed packages. 4487 PACKAGE: 4488 for my $Package (@PackageList) { 4489 4490 next PACKAGE if !$Package->{Name}->{Content}; 4491 next PACKAGE if !IsArrayRefWithData( $Package->{Filelist} ); 4492 4493 my @XMLFiles; 4494 FILE: 4495 for my $File ( @{ $Package->{Filelist} } ) { 4496 $File->{Location} =~ s/\/\//\//g; 4497 my $Search = 'Kernel/Config/Files/XML/'; 4498 4499 if ( substr( $File->{Location}, 0, length $Search ) ne $Search ) { 4500 next FILE; 4501 } 4502 4503 my $Filename = $File->{Location}; 4504 $Filename =~ s{\AKernel/Config/Files/XML/(.+\.xml)\z}{$1}msxi; 4505 push @XMLFiles, $Filename; 4506 } 4507 4508 next PACKAGE if !@XMLFiles; 4509 4510 my $PackageName = $Package->{Name}->{Content}; 4511 my $DisplayName = $ConfigObject->Get("SystemConfiguration::Category::Name::$PackageName") || $PackageName; 4512 4513 # special treatment for OTRS Business Solution™ 4514 if ( $DisplayName eq 'OTRSBusiness' ) { 4515 $DisplayName = 'OTRS Business Solution™'; 4516 } 4517 4518 $Result{$PackageName} = { 4519 DisplayName => $DisplayName, 4520 Files => \@XMLFiles, 4521 }; 4522 } 4523 4524 $CacheObject->Set( 4525 Type => $CacheType, 4526 Key => $CacheKey, 4527 Value => \%Result, 4528 TTL => 24 * 3600 * 30, # 1 month 4529 ); 4530 4531 return %Result; 4532} 4533 4534=head2 ForbiddenValueTypesGet() 4535 4536Returns a hash of forbidden value types. 4537 4538 my %ForbiddenValueTypes = $SysConfigObject->ForbiddenValueTypesGet(); 4539 4540Returns: 4541 4542 %ForbiddenValueType = ( 4543 String => [], 4544 Select => ['Option'], 4545 ... 4546 ); 4547 4548=cut 4549 4550sub ForbiddenValueTypesGet { 4551 my ( $Self, %Param ) = @_; 4552 4553 my $CacheType = 'SysConfig'; 4554 my $CacheKey = 'ForbiddenValueTypesGet'; 4555 4556 my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); 4557 4558 # Return cache. 4559 my $Cache = $CacheObject->Get( 4560 Type => $CacheType, 4561 Key => $CacheKey, 4562 ); 4563 4564 return %{$Cache} if ref $Cache eq 'HASH'; 4565 4566 my $MainObject = $Kernel::OM->Get('Kernel::System::Main'); 4567 4568 my @ValueTypes = $Self->_ValueTypesList(); 4569 4570 my %Result; 4571 4572 for my $ValueType (@ValueTypes) { 4573 4574 my $Loaded = $MainObject->Require( 4575 "Kernel::System::SysConfig::ValueType::$ValueType", 4576 ); 4577 4578 if ($Loaded) { 4579 my $ValueTypeObject = $Kernel::OM->Get( 4580 "Kernel::System::SysConfig::ValueType::$ValueType", 4581 ); 4582 4583 my @ForbiddenValueTypes = $ValueTypeObject->ForbiddenValueTypes(); 4584 if ( scalar @ForbiddenValueTypes ) { 4585 $Result{$ValueType} = \@ForbiddenValueTypes; 4586 } 4587 } 4588 } 4589 4590 $CacheObject->Set( 4591 Type => $CacheType, 4592 Key => $CacheKey, 4593 Value => \%Result, 4594 TTL => 24 * 3600, # 1 day 4595 ); 4596 4597 return %Result; 4598} 4599 4600=head2 ValueAttributeList() 4601 4602Returns a hash of forbidden value types. 4603 4604 my @ValueAttributeList = $SysConfigObject->ValueAttributeList(); 4605 4606Returns: 4607 4608 @ValueAttributeList = ( 4609 "Content", 4610 "SelectedID", 4611 ); 4612 4613=cut 4614 4615sub ValueAttributeList { 4616 4617 my ( $Self, %Param ) = @_; 4618 4619 my $CacheType = 'SysConfig'; 4620 my $CacheKey = 'ValueAttributeList'; 4621 4622 my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); 4623 4624 # Return cache. 4625 my $Cache = $CacheObject->Get( 4626 Type => $CacheType, 4627 Key => $CacheKey, 4628 ); 4629 4630 return @{$Cache} if ref $Cache eq 'ARRAY'; 4631 4632 my $MainObject = $Kernel::OM->Get('Kernel::System::Main'); 4633 4634 my @ValueTypes = $Self->_ValueTypesList(); 4635 4636 my @Result; 4637 4638 for my $ValueType (@ValueTypes) { 4639 4640 my $Loaded = $MainObject->Require( 4641 "Kernel::System::SysConfig::ValueType::$ValueType", 4642 ); 4643 4644 if ($Loaded) { 4645 my $ValueTypeObject = $Kernel::OM->Get( 4646 "Kernel::System::SysConfig::ValueType::$ValueType", 4647 ); 4648 4649 my $ValueAttribute = $ValueTypeObject->ValueAttributeGet(); 4650 if ( !grep { $_ eq $ValueAttribute } @Result ) { 4651 push @Result, $ValueAttribute; 4652 } 4653 } 4654 } 4655 4656 $CacheObject->Set( 4657 Type => $CacheType, 4658 Key => $CacheKey, 4659 Value => \@Result, 4660 TTL => 24 * 3600, # 1 day 4661 ); 4662 4663 return @Result; 4664} 4665 4666=head2 SettingsSet() 4667 4668This method locks provided settings(by force), updates them and deploys the changes (by force). 4669 4670 my $Success = $SysConfigObject->SettingsSet( 4671 UserID => 1, # (required) UserID 4672 Comments => 'Deployment comment', # (optional) Comment 4673 Settings => [ # (required) List of settings to update. 4674 { 4675 Name => 'Setting::Name', # (required) 4676 EffectiveValue => 'Value', # (optional) 4677 IsValid => 1, # (optional) 4678 UserModificationActive => 1, # (optional) 4679 }, 4680 ... 4681 ], 4682 ); 4683 4684Returns: 4685 4686 $Success = 1; 4687 4688=cut 4689 4690sub SettingsSet { 4691 my ( $Self, %Param ) = @_; 4692 4693 if ( !$Param{UserID} ) { 4694 $Kernel::OM->Get('Kernel::System::Log')->Log( 4695 Priority => 'error', 4696 Message => "Needed UserID!" 4697 ); 4698 4699 return; 4700 } 4701 4702 if ( !IsArrayRefWithData( $Param{Settings} ) ) { 4703 $Kernel::OM->Get('Kernel::System::Log')->Log( 4704 Priority => 'error', 4705 Message => "Settings must be array with data!" 4706 ); 4707 4708 return; 4709 } 4710 4711 my @DeploySettings; 4712 4713 for my $Setting ( @{ $Param{Settings} } ) { 4714 4715 my $ExclusiveLockGUID = $Self->SettingLock( 4716 Name => $Setting->{Name}, 4717 Force => 1, 4718 UserID => $Param{UserID}, 4719 ); 4720 4721 return if !$ExclusiveLockGUID; 4722 4723 my %UpdateResult = $Self->SettingUpdate( 4724 %{$Setting}, 4725 ExclusiveLockGUID => $ExclusiveLockGUID, 4726 UserID => $Param{UserID}, 4727 ); 4728 4729 if ( $UpdateResult{Success} ) { 4730 push @DeploySettings, $Setting->{Name}; 4731 } 4732 } 4733 4734 # Deploy successfully updated settings. 4735 my %DeploymentResult = $Self->ConfigurationDeploy( 4736 Comments => $Param{Comments} || '', 4737 UserID => $Param{UserID}, 4738 Force => 1, 4739 DirtySettings => \@DeploySettings, 4740 ); 4741 4742 return $DeploymentResult{Success}; 4743} 4744 4745=head2 OverriddenFileNameGet() 4746 4747Returns file name which overrides setting Effective value. 4748 4749 my $FileName = $SysConfigObject->OverriddenFileNameGet( 4750 SettingName => 'Setting::Name', # (required) 4751 UserID => 1, # (required) 4752 EffectiveValue => '3', # (optional) EffectiveValue stored in the DB. 4753 ); 4754 4755Returns: 4756 4757 $FileName = 'ZZZUpdate.pm'; 4758 4759=cut 4760 4761sub OverriddenFileNameGet { 4762 my ( $Self, %Param ) = @_; 4763 4764 # Check needed stuff. 4765 for my $Needed (qw(SettingName UserID)) { 4766 if ( !$Param{$Needed} ) { 4767 $Kernel::OM->Get('Kernel::System::Log')->Log( 4768 Priority => 'error', 4769 Message => "Need $Needed!", 4770 ); 4771 return; 4772 } 4773 } 4774 4775 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 4776 4777 my $LoadedEffectiveValue = $Self->GlobalEffectiveValueGet( 4778 SettingName => $Param{SettingName}, 4779 ); 4780 my @SettingStructure = split( '###', $Param{SettingName} ); 4781 4782 my $EffectiveValue = $Param{EffectiveValue}; 4783 4784 # Replace config variables in effective values. 4785 # NOTE: First level only, make sure to update this code once same mechanism has been improved in Defaults.pm. 4786 # Please see bug#12916 and bug#13376 for more information. 4787 $EffectiveValue =~ s/\<OTRS_CONFIG_(.+?)\>/$ConfigObject->{$1}/g; 4788 4789 my $IsOverridden = DataIsDifferent( 4790 Data1 => $EffectiveValue // {}, 4791 Data2 => $LoadedEffectiveValue // {}, 4792 ); 4793 4794 # This setting is not Overridden in perl file, return. 4795 return if !$IsOverridden; 4796 4797 my $Result; 4798 4799 my $Home = $ConfigObject->Get('Home'); 4800 my $Directory = "$Home/Kernel/Config/Files"; 4801 4802 my $MainObject = $Kernel::OM->Get('Kernel::System::Main'); 4803 4804 # Get all .pm files that start with 'ZZZ'. 4805 my @FilesInDirectory = $MainObject->DirectoryRead( 4806 Directory => $Directory, 4807 Filter => 'ZZZ*.pm', 4808 ); 4809 4810 my @Modules; 4811 4812 FILE: 4813 for my $File (@FilesInDirectory) { 4814 4815 # Get only file name, without full path and extension. 4816 $File =~ m{^.*/(.*?)\.pm$}; 4817 my $FileName = $1; 4818 4819 # Skip the file that was regularly deployed. 4820 next FILE if $FileName eq 'ZZZAAuto'; 4821 4822 push @Modules, { 4823 "Kernel::Config::Files::$FileName" => $File, 4824 }; 4825 } 4826 4827 # Check Config.pm as well. 4828 push @Modules, { 4829 'Kernel::Config' => 'Kernel/Config.pm', 4830 }; 4831 4832 # Get effective values. Try cached version first. 4833 my $ConfigFromDB = {}; 4834 if ( $Self->{ConfigFromDB} ) { 4835 $ConfigFromDB = $Self->{ConfigFromDB}; 4836 } 4837 4838 # Check if we have a valid ZZZAAuto.pm. It is regarded as a reliable source of information. 4839 elsif ( 4840 -f $Self->{Home} . '/Kernel/Config/Files/ZZZAAuto.pm' 4841 && $MainObject->Require('Kernel::Config::Files::ZZZAAuto') 4842 ) 4843 { 4844 Kernel::Config::Files::ZZZAAuto->Load($ConfigFromDB); 4845 4846 return if !$ConfigFromDB; 4847 $Self->{ConfigFromDB} = $ConfigFromDB; 4848 } 4849 4850 # Try retrieving data from DB. 4851 elsif ( my %LastDeployment = $Kernel::OM->Get('Kernel::System::SysConfig::DB')->DeploymentGetLast() ) { 4852 return if !$LastDeployment{EffectiveValueStrg}; 4853 4854 { 4855 eval $LastDeployment{EffectiveValueStrg}; 4856 Kernel::Config::Files::ZZZAAuto->Load($ConfigFromDB); 4857 } 4858 4859 return if !$ConfigFromDB; 4860 $Self->{ConfigFromDB} = $ConfigFromDB; 4861 } 4862 4863 # No usable data found. This should only happen during initial setup before the initial deployment. 4864 else { 4865 return; 4866 } 4867 4868 for my $Module (@Modules) { 4869 my $ModuleName = ( keys %{$Module} )[0]; 4870 4871 # Check if this module overrides our setting. 4872 my $SettingFound = $Self->_IsOverriddenInModule( 4873 Module => $ModuleName, 4874 SettingStructure => \@SettingStructure, 4875 ConfigFromDB => $ConfigFromDB, 4876 ); 4877 4878 if ($SettingFound) { 4879 $Result = $Module->{$ModuleName}; 4880 } 4881 } 4882 4883 if ($Result) { 4884 $Result =~ s/^$Home\/?(.*)$/$1/; 4885 } 4886 4887 return $Result; 4888} 4889 4890=head2 GlobalEffectiveValueGet() 4891 4892Returns global effective value for provided setting name. 4893 4894 my $EffectiveValue = $SysConfigObject->GlobalEffectiveValueGet( 4895 SettingName => 'Setting::Name', # (required) 4896 ); 4897 4898Returns: 4899 4900 $EffectiveValue = 'test'; 4901 4902=cut 4903 4904sub GlobalEffectiveValueGet { 4905 my ( $Self, %Param ) = @_; 4906 4907 # Check needed stuff. 4908 if ( !$Param{SettingName} ) { 4909 $Kernel::OM->Get('Kernel::System::Log')->Log( 4910 Priority => 'error', 4911 Message => "Need SettingName!", 4912 ); 4913 return; 4914 } 4915 4916 my $GlobalConfigObject = Kernel::Config->new(); 4917 4918 my $LoadedEffectiveValue; 4919 4920 my @SettingStructure = split( '###', $Param{SettingName} ); 4921 for my $Key (@SettingStructure) { 4922 if ( !defined $LoadedEffectiveValue ) { 4923 4924 # first iteration 4925 $LoadedEffectiveValue = $GlobalConfigObject->Get($Key); 4926 } 4927 elsif ( ref $LoadedEffectiveValue eq 'HASH' ) { 4928 $LoadedEffectiveValue = $LoadedEffectiveValue->{$Key}; 4929 } 4930 } 4931 4932 return $LoadedEffectiveValue; 4933} 4934 4935=head1 PRIVATE INTERFACE 4936 4937=head2 _IsOverriddenInModule() 4938 4939Helper method to check if setting is overridden in specific module. 4940 4941 my $Overridden = $SysConfigObject->_IsOverriddenInModule( 4942 Module => "Kernel::Config::Files::ZZZAAuto", 4943 SettingStructure => [ 'DashboardBackend', '0000-ProductNotify' ], 4944 LoadedEffectiveValue => 'Value', 4945 ); 4946 4947=cut 4948 4949sub _IsOverriddenInModule { 4950 my ( $Self, %Param ) = @_; 4951 4952 # Check needed stuff. 4953 for my $Needed (qw(Module SettingStructure ConfigFromDB)) { 4954 if ( !$Param{$Needed} ) { 4955 $Kernel::OM->Get('Kernel::System::Log')->Log( 4956 Priority => 'error', 4957 Message => "Need $Needed!", 4958 ); 4959 return; 4960 } 4961 } 4962 4963 if ( !IsArrayRefWithData( $Param{SettingStructure} ) ) { 4964 $Kernel::OM->Get('Kernel::System::Log')->Log( 4965 Priority => 'error', 4966 Message => "SettingStructure must be an array!" 4967 ); 4968 return; 4969 } 4970 4971 my $Result; 4972 4973 my $Loaded = $Kernel::OM->Get('Kernel::System::Main')->Require( 4974 $Param{Module}, 4975 Silent => 1, 4976 ); 4977 4978 # If module couldn't be loaded, there is no user specific setting. 4979 return $Result if !$Loaded; 4980 4981 # Get effective value from the DB. 4982 my $OverriddenSettings = $Kernel::OM->Get('Kernel::System::Storable')->Clone( 4983 Data => $Param{ConfigFromDB}, 4984 ); 4985 4986 if ( $Param{Module} eq 'Kernel::Config' ) { 4987 bless( $OverriddenSettings, 'Kernel::Config' ); 4988 $OverriddenSettings->Load(); 4989 } 4990 else { 4991 # Apply changes from this file only. 4992 $Param{Module}->Load($OverriddenSettings); 4993 } 4994 4995 # OverriddenSettings contains EffectiveValues from DB, overridden by provided Module, 4996 # so we can compare if setting was changed in this file. 4997 4998 # Loaded hash is empty, return. 4999 return $Result if !IsHashRefWithData($OverriddenSettings) && ref $OverriddenSettings ne 'Kernel::Config'; 5000 5001 # Check if this file overrides our setting. 5002 my $SettingFound = 0; 5003 my $LoadedEffectiveValue; 5004 my $ConfigFromDB; 5005 5006 KEY: 5007 for my $Key ( @{ $Param{SettingStructure} } ) { 5008 if ( !defined $LoadedEffectiveValue ) { 5009 5010 # First iteration. 5011 $LoadedEffectiveValue = $OverriddenSettings->{$Key}; 5012 $ConfigFromDB = $Param{ConfigFromDB}->{$Key}; 5013 5014 if ( defined $ConfigFromDB && !defined $LoadedEffectiveValue ) { 5015 5016 # Setting is overridden using the "delete" statement. 5017 $SettingFound = 1; 5018 } 5019 elsif ( 5020 DataIsDifferent( 5021 Data1 => $LoadedEffectiveValue // {}, 5022 Data2 => $ConfigFromDB // {}, 5023 ) 5024 ) 5025 { 5026 $SettingFound = 1; 5027 } 5028 else { 5029 last KEY; 5030 } 5031 } 5032 elsif ( ref $LoadedEffectiveValue eq 'HASH' ) { 5033 $LoadedEffectiveValue = $LoadedEffectiveValue->{$Key}; 5034 $ConfigFromDB = $ConfigFromDB->{$Key}; 5035 5036 if ( defined $ConfigFromDB && !defined $LoadedEffectiveValue ) { 5037 5038 # Setting is overridden using the "delete" statement. 5039 $SettingFound = 1; 5040 } 5041 elsif ( 5042 DataIsDifferent( 5043 Data1 => $LoadedEffectiveValue // {}, 5044 Data2 => $ConfigFromDB // {}, 5045 ) 5046 ) 5047 { 5048 $SettingFound = 1; 5049 } 5050 else { 5051 $SettingFound = 0; 5052 } 5053 } 5054 else { 5055 $Kernel::OM->Get('Kernel::System::Log')->Log( 5056 Priority => 'error', 5057 Message => "Unhandled exception!" 5058 ); 5059 } 5060 } 5061 5062 return $SettingFound; 5063} 5064 5065=head2 _FileWriteAtomic() 5066 5067Writes a file in an atomic operation. This is achieved by creating 5068a temporary file, filling and renaming it. This avoids inconsistent states 5069when the file is updated. 5070 5071 my $Success = $SysConfigObject->_FileWriteAtomic( 5072 Filename => "$Self->{Home}/Kernel/Config/Files/ZZZAAuto.pm", 5073 Content => \$NewContent, 5074 ); 5075 5076=cut 5077 5078sub _FileWriteAtomic { 5079 my ( $Self, %Param ) = @_; 5080 5081 for my $Needed (qw(Filename Content)) { 5082 if ( !defined $Param{$Needed} ) { 5083 $Kernel::OM->Get('Kernel::System::Log')->Log( 5084 Priority => 'error', 5085 Message => "Need $Needed!" 5086 ); 5087 return; 5088 } 5089 } 5090 5091 my $TempFilename = $Param{Filename} . '.' . $$; 5092 my $FH; 5093 5094 ## no critic 5095 if ( !open( $FH, ">$Self->{FileMode}", $TempFilename ) ) { 5096 ## use critic 5097 5098 $Kernel::OM->Get('Kernel::System::Log')->Log( 5099 Priority => 'error', 5100 Message => "Can't open file $TempFilename: $!", 5101 ); 5102 return; 5103 } 5104 5105 print $FH ${ $Param{Content} }; 5106 close $FH; 5107 5108 if ( !rename $TempFilename, $Param{Filename} ) { 5109 $Kernel::OM->Get('Kernel::System::Log')->Log( 5110 Priority => 'error', 5111 Message => "Could not rename $TempFilename to $Param{Filename}: $!" 5112 ); 5113 return; 5114 } 5115 5116 return 1; 5117} 5118 5119=head2 _ConfigurationTranslatableStrings() 5120 5121Gathers strings marked as translatable from a setting XML parsed content and saves it on 5122ConfigurationTranslatableStrings global variable. 5123 5124 $SysConfigObject->_ConfigurationTranslatableStrings( 5125 Data => $Data, # could be SCALAR, ARRAY or HASH 5126 ); 5127 5128=cut 5129 5130sub _ConfigurationTranslatableStrings { 5131 my ( $Self, %Param ) = @_; 5132 5133 for my $Needed (qw(Data)) { 5134 if ( !defined $Param{$Needed} ) { 5135 $Kernel::OM->Get('Kernel::System::Log')->Log( 5136 Priority => 'error', 5137 Message => "Need $Needed!" 5138 ); 5139 return; 5140 } 5141 } 5142 5143 # Start recursion if its an array. 5144 if ( ref $Param{Data} eq 'ARRAY' ) { 5145 5146 KEY: 5147 for my $Key ( @{ $Param{Data} } ) { 5148 next KEY if !$Key; 5149 $Self->_ConfigurationTranslatableStrings( Data => $Key ); 5150 } 5151 return; 5152 } 5153 5154 # Start recursion if its a Hash. 5155 if ( ref $Param{Data} eq 'HASH' ) { 5156 for my $Key ( sort keys %{ $Param{Data} } ) { 5157 if ( 5158 ref $Param{Data}->{$Key} eq '' 5159 && $Param{Data}->{Translatable} 5160 && $Param{Data}->{Content} 5161 ) 5162 { 5163 return if !$Param{Data}->{Content}; 5164 return if $Param{Data}->{Content} =~ /^\d+$/; 5165 $Self->{ConfigurationTranslatableStrings}->{ $Param{Data}->{Content} } = 1; 5166 } 5167 $Self->_ConfigurationTranslatableStrings( Data => $Param{Data}->{$Key} ); 5168 } 5169 } 5170 return; 5171} 5172 5173=head2 _DBCleanUp(); 5174 5175Removes all settings defined in the database (including default and modified) that are not included 5176in the settings parameter 5177 5178 my $Success = $SysConfigObject->_DBCleanUp( 5179 Settings => { 5180 'ACL::CacheTTL' => { 5181 XMLContentParsed => ' 5182 <Setting Name="SettingName" Required="1" Valid="1"> 5183 <Description Translatable="1">Test.</Description> 5184 # ... 5185 </Setting>', 5186 XMLContentRaw => { 5187 Description => [ 5188 { 5189 Content => 'Test.', 5190 Translatable => '1', 5191 }, 5192 ], 5193 Name => 'Test', 5194 # ... 5195 }, 5196 # ... 5197 }; 5198 ); 5199 5200Returns: 5201 5202 $Success = 1; # or false in case of a failure 5203 5204=cut 5205 5206sub _DBCleanUp { 5207 my ( $Self, %Param ) = @_; 5208 5209 for my $Needed (qw(Settings)) { 5210 if ( !$Param{$Needed} ) { 5211 $Kernel::OM->Get('Kernel::System::Log')->Log( 5212 Priority => 'error', 5213 Message => "Need $Needed!" 5214 ); 5215 return; 5216 } 5217 } 5218 if ( !IsHashRefWithData( $Param{Settings} ) ) { 5219 $Kernel::OM->Get('Kernel::System::Log')->Log( 5220 Priority => 'error', 5221 Message => "Settings must be an HashRef!" 5222 ); 5223 return; 5224 } 5225 5226 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 5227 5228 my @SettingsDB = $SysConfigDBObject->DefaultSettingList( 5229 IncludeInvisible => 1, 5230 ); 5231 5232 my ( $DefaultUpdated, $ModifiedUpdated ); 5233 5234 for my $SettingDB (@SettingsDB) { 5235 5236 # Cleanup database if the setting is not present in the XML files. 5237 if ( !$Param{Settings}->{ $SettingDB->{Name} } ) { 5238 5239 # Get all modified settings. 5240 my @ModifiedSettings = $SysConfigDBObject->ModifiedSettingListGet( 5241 Name => $SettingDB->{Name}, 5242 ); 5243 5244 for my $ModifiedSetting (@ModifiedSettings) { 5245 5246 # Delete from modified table. 5247 my $SuccessDeleteModified = $SysConfigDBObject->ModifiedSettingDelete( 5248 ModifiedID => $ModifiedSetting->{ModifiedID}, 5249 ); 5250 if ( !$SuccessDeleteModified ) { 5251 $Kernel::OM->Get('Kernel::System::Log')->Log( 5252 Priority => 'error', 5253 Message => "System couldn't delete $SettingDB->{Name} from DB (sysconfig_modified)!" 5254 ); 5255 } 5256 } 5257 5258 my @ModifiedSettingVersions = $SysConfigDBObject->ModifiedSettingVersionListGet( 5259 Name => $SettingDB->{Name}, 5260 ); 5261 5262 for my $ModifiedSettingVersion (@ModifiedSettingVersions) { 5263 5264 # Delete from modified table. 5265 my $SuccessDeleteModifiedVersion = $SysConfigDBObject->ModifiedSettingVersionDelete( 5266 ModifiedVersionID => $ModifiedSettingVersion->{ModifiedVersionID}, 5267 ); 5268 if ( !$SuccessDeleteModifiedVersion ) { 5269 $Kernel::OM->Get('Kernel::System::Log')->Log( 5270 Priority => 'error', 5271 Message => "System couldn't delete $SettingDB->{Name} from DB (sysconfig_modified_version)!" 5272 ); 5273 } 5274 } 5275 5276 # Delete from default table. 5277 my $SuccessDefaultSetting = $SysConfigDBObject->DefaultSettingDelete( 5278 Name => $SettingDB->{Name}, 5279 ); 5280 if ( !$SuccessDefaultSetting ) { 5281 $Kernel::OM->Get('Kernel::System::Log')->Log( 5282 Priority => 'error', 5283 Message => "System couldn't delete $SettingDB->{Name} from DB (sysconfig_default)!" 5284 ); 5285 } 5286 } 5287 } 5288 5289 return 1; 5290} 5291 5292=head2 _NavigationTree(); 5293 5294Returns navigation as a tree (in a hash). 5295 5296 my %Result = $SysConfigObject->_NavigationTree( 5297 'Array' => [ # Array of setting navigation items 5298 'Core', 5299 'Core::CustomerUser', 5300 'Frontend', 5301 ], 5302 'Tree' => { # Result from previous recursive call 5303 'Core' => { 5304 'Core::CustomerUser' => {}, 5305 }, 5306 }, 5307 ); 5308 5309Returns: 5310 5311 %Result = ( 5312 'Core' => { 5313 'Core::CustomerUser' => {}, 5314 }, 5315 'Frontend' => {}, 5316 ); 5317 5318=cut 5319 5320sub _NavigationTree { 5321 my ( $Self, %Param ) = @_; 5322 5323 for my $Needed (qw(Tree Array)) { 5324 if ( !defined $Param{$Needed} ) { 5325 $Kernel::OM->Get('Kernel::System::Log')->Log( 5326 Priority => 'error', 5327 Message => "Need $Needed!", 5328 ); 5329 return; 5330 } 5331 } 5332 5333 my %Result = %{ $Param{Tree} }; 5334 5335 return %Result if !IsArrayRefWithData( $Param{Array} ); 5336 5337 # Check if first item exists. 5338 if ( !defined $Result{ $Param{Array}->[0] } ) { 5339 $Result{ $Param{Array}->[0] } = { 5340 Subitems => {}, 5341 }; 5342 } 5343 5344 # Check if it's deeper tree. 5345 if ( scalar @{ $Param{Array} } > 1 ) { 5346 my @SubArray = splice( @{ $Param{Array} }, 1 ); 5347 my %Hash = $Self->_NavigationTree( 5348 Tree => $Result{ $Param{Array}->[0] }->{Subitems}, 5349 Array => \@SubArray, 5350 ); 5351 5352 if (%Hash) { 5353 $Result{ $Param{Array}->[0] } = { 5354 Subitems => \%Hash, 5355 }; 5356 } 5357 } 5358 5359 return %Result; 5360} 5361 5362sub _NavigationTreeNodeCount { 5363 my ( $Self, %Param ) = @_; 5364 5365 # Check needed stuff. 5366 for my $Needed (qw(Settings)) { 5367 if ( !$Param{$Needed} ) { 5368 $Kernel::OM->Get('Kernel::System::Log')->Log( 5369 Priority => 'error', 5370 Message => "Need $Needed!", 5371 ); 5372 return; 5373 } 5374 } 5375 5376 my %Result = %{ $Param{Tree} // {} }; 5377 5378 NODE_NAME: 5379 for my $NodeName ( sort keys %Result ) { 5380 5381 my @Matches = grep { $_->{Navigation} eq $NodeName } @{ $Param{Settings} }; 5382 $Result{$NodeName}->{Count} = scalar @Matches; 5383 5384 my %SubResult = $Self->_NavigationTreeNodeCount( 5385 Tree => $Result{$NodeName}->{Subitems}, 5386 Settings => $Param{Settings}, 5387 ); 5388 5389 $Result{$NodeName}->{Subitems} = { 5390 %{ $Result{$NodeName}->{Subitems} }, 5391 %SubResult, 5392 }; 5393 } 5394 5395 return %Result; 5396} 5397 5398=head2 _ConfigurationEntitiesGet(); 5399 5400Returns hash of used entities for provided Setting value. 5401 5402 my %Result = $SysConfigObject->_ConfigurationEntitiesGet( 5403 'Name' => 'Ticket::Frontend::AgentTicketPriority###Entity', # setting name 5404 'Result' => {}, # result from previous recursive call 5405 'Value' => [ # setting Value 5406 { 5407 'Item' => [ 5408 { 5409 'Content' => '3 medium', 5410 'ValueEntityType' => 'Priority', 5411 'ValueRegex' => '', 5412 'ValueType' => 'Entity', 5413 }, 5414 ], 5415 }, 5416 ], 5417 ); 5418 5419Returns: 5420 5421 %Result = { 5422 'Priority' => { 5423 '3 medium' => [ 5424 'Ticket::Frontend::AgentTicketPriority###Entity', 5425 ], 5426 }, 5427 }; 5428 5429=cut 5430 5431sub _ConfigurationEntitiesGet { 5432 my ( $Self, %Param ) = @_; 5433 5434 for my $Needed (qw(Value Result Name)) { 5435 if ( !$Param{$Needed} ) { 5436 $Kernel::OM->Get('Kernel::System::Log')->Log( 5437 Priority => 'error', 5438 Message => "Need $Needed!", 5439 ); 5440 return; 5441 } 5442 } 5443 5444 my %Result = %{ $Param{Result} || {} }; 5445 my $ValueEntityType = $Param{ValueEntityType} || ''; 5446 5447 if ( ref $Param{Value} eq 'ARRAY' ) { 5448 for my $Item ( @{ $Param{Value} } ) { 5449 %Result = $Self->_ConfigurationEntitiesGet( 5450 %Param, 5451 Value => $Item, 5452 Result => \%Result, 5453 ); 5454 } 5455 } 5456 elsif ( ref $Param{Value} eq 'HASH' ) { 5457 if ( $Param{Value}->{ValueEntityType} ) { 5458 $ValueEntityType = $Param{Value}->{ValueEntityType}; 5459 } 5460 5461 if ( $Param{Value}->{Content} ) { 5462 5463 # If there is no hash item, create new. 5464 if ( !defined $Result{$ValueEntityType} ) { 5465 $Result{$ValueEntityType} = {}; 5466 } 5467 5468 # Extract value (without white space). 5469 my $Value = $Param{Value}->{Content}; 5470 $Value =~ s{^\s*(.*?)\s*$}{$1}gsmx; 5471 $Value //= ''; 5472 5473 # If there is no array, create 5474 if ( !IsArrayRefWithData( $Result{$ValueEntityType}->{$Value} ) ) { 5475 $Result{$ValueEntityType}->{$Value} = []; 5476 } 5477 5478 # Check if current config is not in the array. 5479 if ( !grep { $_ eq $Param{Name} } @{ $Result{$ValueEntityType}->{$Value} } ) { 5480 push @{ $Result{$ValueEntityType}->{$Value} }, $Param{Name}; 5481 } 5482 } 5483 else { 5484 for my $Key (qw(Item Hash Array)) { 5485 if ( defined $Param{Value}->{$Key} ) { 5486 5487 # Contains children 5488 %Result = $Self->_ConfigurationEntitiesGet( 5489 %Param, 5490 ValueEntityType => $ValueEntityType, 5491 Value => $Param{Value}->{$Key}, 5492 Result => \%Result, 5493 ); 5494 } 5495 } 5496 } 5497 } 5498 5499 return %Result; 5500} 5501 5502=head2 _EffectiveValues2PerlFile() 5503 5504Converts effective values from settings into a combined perl hash ready to write into a file. 5505 5506 my $FileString = $SysConfigObject->_EffectiveValues2PerlFile( 5507 Settings => [ 5508 { 5509 Name => 'SettingName', 5510 IsValid => 1, 5511 EffectiveValue => $ValueStructure, 5512 }, 5513 { 5514 Name => 'AnotherSettingName', 5515 IsValid => 0, 5516 EffectiveValue => $AnotherValueStructure, 5517 }, 5518 # ... 5519 ], 5520 TargetPath => 'Kernel/Config/Files/ZZZAAuto.pm', 5521 ); 5522 5523=cut 5524 5525sub _EffectiveValues2PerlFile { 5526 my ( $Self, %Param ) = @_; 5527 5528 for my $Needed (qw(Settings TargetPath)) { 5529 if ( !$Param{$Needed} ) { 5530 $Kernel::OM->Get('Kernel::System::Log')->Log( 5531 Priority => 'error', 5532 Message => "Need $Needed!", 5533 ); 5534 5535 return; 5536 } 5537 } 5538 if ( !IsArrayRefWithData( $Param{Settings} ) ) { 5539 $Kernel::OM->Get('Kernel::System::Log')->Log( 5540 Priority => 'error', 5541 Message => "Settings parameter is invalid!", 5542 ); 5543 5544 return; 5545 } 5546 5547 my $MainObject = $Kernel::OM->Get('Kernel::System::Main'); 5548 5549 my $PerlHashStrg; 5550 5551 my $StorableObject = $Kernel::OM->Get('Kernel::System::Storable'); 5552 my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); 5553 5554 my $CacheType = 'SysConfigPersistent'; 5555 my $CacheKey = 'EffectiveValues2PerlFile'; 5556 5557 my $Cache = $CacheObject->Get( 5558 Type => $CacheType, 5559 Key => $CacheKey, 5560 ) // {}; 5561 5562 my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); 5563 my $CurrentSystemTime = $DateTimeObject->ToEpoch(); 5564 5565 $DateTimeObject->Add( 5566 Months => 1, 5567 ); 5568 my $ExpireTime = $DateTimeObject->ToEpoch(); 5569 5570 my $CacheDifferent; 5571 5572 # Delete all expired keys. 5573 my @ExpiredKeys = grep { $CurrentSystemTime > $Cache->{$_}->{ExpireTime} } keys %{$Cache}; 5574 delete @{$Cache}{@ExpiredKeys}; 5575 5576 # If there are expired keys, cache needs to be set to a new value. 5577 $CacheDifferent = scalar @ExpiredKeys ? 1 : 0; 5578 5579 # Convert all settings from DB format to perl file. 5580 for my $Setting ( @{ $Param{Settings} } ) { 5581 5582 my $Name = $Setting->{Name}; 5583 $Name =~ s/\\/\\\\/g; 5584 $Name =~ s/'/\'/g; 5585 $Name =~ s/###/'}->{'/g; 5586 5587 if ( $Setting->{IsValid} ) { 5588 5589 my $EffectiveValue; 5590 5591 my $ValueString = $Setting->{EffectiveValue} // ''; 5592 if ( ref $ValueString ) { 5593 my $String = $StorableObject->Serialize( 5594 Data => $Setting->{EffectiveValue}, 5595 ); 5596 $ValueString = $MainObject->MD5sum( 5597 String => \$String, 5598 ); 5599 } 5600 5601 if ( 5602 $Cache->{$ValueString} 5603 && $Cache->{$ValueString}->{Value} 5604 ) 5605 { 5606 $EffectiveValue = $Cache->{$ValueString}->{Value}; 5607 } 5608 else { 5609 $EffectiveValue = $MainObject->Dump( $Setting->{EffectiveValue} ); 5610 5611 $Cache->{$ValueString} = { 5612 Value => $EffectiveValue, 5613 ExpireTime => $ExpireTime, 5614 }; 5615 5616 # Cache has been changed, it needs to be set. 5617 $CacheDifferent = 1; 5618 } 5619 5620 $EffectiveValue =~ s/\$VAR1 =//; 5621 $PerlHashStrg .= "\$Self->{'$Name'} = $EffectiveValue"; 5622 } 5623 elsif ( eval( '$Self->{ConfigDefaultObject}->{\'' . $Name . '\'}' ) ) { 5624 $PerlHashStrg .= "delete \$Self->{'$Name'};\n"; 5625 } 5626 } 5627 5628 if ($CacheDifferent) { 5629 5630 $CacheObject->Set( 5631 Type => $CacheType, 5632 Key => $CacheKey, 5633 Value => $Cache, 5634 TTL => 20 * 24 * 60 * 60, 5635 ); 5636 } 5637 5638 chomp $PerlHashStrg; 5639 5640 # Convert TargetPath to Package. 5641 my $TargetPath = $Param{TargetPath}; 5642 $TargetPath =~ s{(.*)\.(?:.*)}{$1}msx; 5643 $TargetPath =~ s{ / }{::}msxg; 5644 5645 # Write default config file. 5646 my $FileStrg = <<"EOF"; 5647# OTRS config file (automatically generated) 5648# VERSION:2.0 5649package $TargetPath; 5650use strict; 5651use warnings; 5652no warnings 'redefine'; ## no critic 5653EOF 5654 5655 if ( $Self->{utf8} ) { 5656 $FileStrg .= "use utf8;\n"; 5657 } 5658 5659 $FileStrg .= <<"EOF"; 5660sub Load { 5661 my (\$File, \$Self) = \@_; 5662$PerlHashStrg 5663 return; 5664} 56651; 5666EOF 5667 5668 return $FileStrg; 5669} 5670 5671=head2 _SettingEffectiveValueCheck() 5672 5673Recursive helper for SettingEffectiveValueCheck(). 5674 5675 my %Result = $SysConfigObject->_SettingEffectiveValueCheck( 5676 EffectiveValue => 'open', # (optional) The EffectiveValue to be checked, 5677 # (could be also a complex structure). 5678 XMLContentParsed => { # (required) The XMLContentParsed value from Default Setting. 5679 Value => [ 5680 { 5681 'Item' => [ 5682 { 5683 'Content' => "Scalar value", 5684 }, 5685 ], 5686 }, 5687 ], 5688 }, 5689 NoValidation => $Param{NoValidation}, # (optional), skip validation 5690 CurrentSystemTime => 1507894796935, # (optional) Use provided 1507894796935, otherwise calculate 5691 ExpireTime => 1507894896935, # (optional) Use provided ExpireTime for cache, otherwise calculate 5692 UserID => 1, # (required) UserID 5693 ); 5694 5695Returns: 5696 5697 %Result = ( 5698 EffectiveValue => 'closed', # Note that EffectiveValue can be changed. 5699 Success => 1, # or false in case of fail 5700 Error => undef, # or error string 5701 ); 5702 5703=cut 5704 5705sub _SettingEffectiveValueCheck { 5706 my ( $Self, %Param ) = @_; 5707 5708 # Check needed stuff. 5709 for my $Needed (qw(XMLContentParsed UserID)) { 5710 if ( !$Param{$Needed} ) { 5711 $Kernel::OM->Get('Kernel::System::Log')->Log( 5712 Priority => 'error', 5713 Message => "Need $Needed!", 5714 ); 5715 return; 5716 } 5717 } 5718 5719 # So far everything is OK, we need to check deeper (recursive). 5720 my $StorableObject = $Kernel::OM->Get('Kernel::System::Storable'); 5721 5722 my $Default = $StorableObject->Clone( 5723 Data => $Param{XMLContentParsed}, 5724 ); 5725 5726 my $EffectiveValue = $Param{EffectiveValue}; 5727 5728 if ( ref $Param{EffectiveValue} ) { 5729 $EffectiveValue = $StorableObject->Clone( 5730 Data => $Param{EffectiveValue}, 5731 ); 5732 } 5733 5734 return $Self->SettingEffectiveValueCheck( 5735 XMLContentParsed => $Default, 5736 EffectiveValue => $EffectiveValue, 5737 NoValidation => $Param{NoValidation}, 5738 CurrentSystemTime => $Param{CurrentSystemTime}, 5739 ExpireTime => $Param{ExpireTime}, 5740 UserID => $Param{UserID}, 5741 ); 5742} 5743 5744=head2 _SettingEffectiveValueCheckCacheSet() 5745Sets cache for EffectiveValueCheck to the provided value. 5746 5747 $SysConfigObject->_SettingEffectiveValueCheckCacheSet( 5748 Value => { (required) 5749 Default180920170714165331 => { 5750 Success => 1, 5751 }, 5752 ... 5753 }, 5754 NoValidation => 0, (optional) 5755 ); 5756 5757=cut 5758 5759sub _SettingEffectiveValueCheckCacheSet { 5760 my ( $Self, %Param ) = @_; 5761 5762 # Check needed stuff. 5763 for my $Needed (qw(Value)) { 5764 if ( !defined $Param{$Needed} ) { 5765 $Kernel::OM->Get('Kernel::System::Log')->Log( 5766 Priority => 'error', 5767 Message => "Need $Needed!", 5768 ); 5769 return; 5770 } 5771 } 5772 5773 my $CacheType = 'SysConfigPersistent'; 5774 my $CacheKey = "EffectiveValueCheck::$Param{NoValidation}"; 5775 5776 return $Kernel::OM->Get('Kernel::System::Cache')->Set( 5777 Type => $CacheType, 5778 Key => $CacheKey, 5779 Value => $Param{Value}, 5780 TTL => 20 * 24 * 60 * 60, 5781 ); 5782} 5783 5784=head2 _GetSettingsToDeploy() 5785 5786Returns the correct list of settings for a deployment taking the settings from different sources: 5787 5788 NotDirty: fetch default settings plus already deployed modified settings. 5789 AllSettings: fetch default settings plus all modified settings already deployed or not. 5790 DirtySettings: fetch default settings plus already deployed settings plus all not deployed settings in the list. 5791 5792 my @SettingList = $SysConfigObject->_GetSettingsToDeploy( 5793 NotDirty => 1, # optional - exclusive (1||0) 5794 All => 1, # optional - exclusive (1||0) 5795 DirtySettings => [ 'SettingName1', 'SettingName2' ], # optional - exclusive 5796 ); 5797 5798 @SettingList = ( 5799 { 5800 DefaultID => 123, 5801 Name => "ProductName", 5802 Description => "Defines the name of the application ...", 5803 Navigation => "ASimple::Path::Structure", 5804 IsInvisible => 1, 5805 IsReadonly => 0, 5806 IsRequired => 1, 5807 IsValid => 1, 5808 HasConfigLevel => 200, 5809 UserModificationPossible => 0, # 1 or 0 5810 UserModificationActive => 0, # 1 or 0 5811 UserPreferencesGroup => 'Advanced', # optional 5812 XMLContentRaw => "The XML structure as it is on the config file", 5813 XMLContentParsed => "XML parsed to Perl", 5814 EffectiveValue => "Product 6", 5815 DefaultValue => "Product 5", 5816 IsModified => 1, # 1 or 0 5817 IsDirty => 1, # 1 or 0 5818 ExclusiveLockGUID => 'A32CHARACTERLONGSTRINGFORLOCKING', 5819 ExclusiveLockUserID => 1, 5820 ExclusiveLockExpiryTime => '2016-05-29 11:09:04', 5821 CreateTime => "2016-05-29 11:04:04", 5822 ChangeTime => "2016-05-29 11:04:04", 5823 }, 5824 { 5825 DefaultID => 321, 5826 Name => 'FieldName', 5827 # ... 5828 CreateTime => '2010-09-11 10:08:00', 5829 ChangeTime => '2011-01-01 01:01:01', 5830 }, 5831 # ... 5832 ); 5833 5834=cut 5835 5836sub _GetSettingsToDeploy { 5837 my ( $Self, %Param ) = @_; 5838 5839 if ( !$Param{NotDirty} && !$Param{DirtySettings} ) { 5840 $Param{AllSettings} = 1; 5841 } 5842 5843 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 5844 5845 my @DefaultSettingsList = $SysConfigDBObject->DefaultSettingListGet( 5846 NoCache => $Param{NoCache}, 5847 ); 5848 5849 # Create a lookup table for the default settings (for easy adding modified). 5850 my %SettingsLookup = map { $_->{Name} => $_ } @DefaultSettingsList; 5851 5852 my @ModifiedSettingsList; 5853 5854 # Use if - else statement, as the gathering of the settings could be expensive. 5855 if ( $Param{NotDirty} ) { 5856 @ModifiedSettingsList = $SysConfigDBObject->ModifiedSettingVersionListGetLast(); 5857 } 5858 else { 5859 @ModifiedSettingsList = $SysConfigDBObject->ModifiedSettingListGet( 5860 IsGlobal => 1, 5861 ); 5862 } 5863 5864 if ( $Param{AllSettings} || $Param{NotDirty} ) { 5865 5866 # Create a lookup table for the modified settings (for easy merging with defaults). 5867 my %ModifiedSettingsLookup = map { $_->{Name} => $_ } @ModifiedSettingsList; 5868 5869 # Merge modified into defaults. 5870 KEY: 5871 for my $Key ( sort keys %SettingsLookup ) { 5872 next KEY if !$ModifiedSettingsLookup{$Key}; 5873 5874 $SettingsLookup{$Key} = { 5875 %{ $SettingsLookup{$Key} }, 5876 %{ $ModifiedSettingsLookup{$Key} }, 5877 }; 5878 } 5879 5880 my @Settings = map { $SettingsLookup{$_} } ( sort keys %SettingsLookup ); 5881 5882 return @Settings; 5883 } 5884 5885 my %DirtySettingsLookup = map { $_ => 1 } @{ $Param{DirtySettings} }; 5886 5887 SETTING: 5888 for my $Setting (@ModifiedSettingsList) { 5889 5890 my $SettingName = $Setting->{Name}; 5891 5892 # Skip invalid settings (all modified needs to have a default). 5893 next SETTING if !$SettingsLookup{$SettingName}; 5894 5895 # Remember modified. 5896 my %ModifiedSetting = %{$Setting}; 5897 5898 # If setting is not in the given list, then do not use current value but last deployed. 5899 if ( $Setting->{IsDirty} && !$DirtySettingsLookup{$SettingName} ) { 5900 %ModifiedSetting = $SysConfigDBObject->ModifiedSettingVersionGetLast( 5901 Name => $Setting->{Name}, 5902 ); 5903 5904 # If there is not previous version then skip to keep the default intact. 5905 next SETTING if !%ModifiedSetting; 5906 } 5907 5908 $SettingsLookup{$SettingName} = { 5909 %{ $SettingsLookup{$SettingName} }, 5910 %ModifiedSetting, 5911 }; 5912 } 5913 5914 my @Settings = map { $SettingsLookup{$_} } ( sort keys %SettingsLookup ); 5915 5916 return @Settings; 5917} 5918 5919=head2 _HandleSettingsToDeploy() 5920 5921Creates modified versions of dirty settings to deploy and removed the dirty flag. 5922 5923 NotDirty: Removes dirty flag just for default settings 5924 AllSettings: Create a version for all dirty settings and removed dirty flags for all default and modified settings 5925 DirtySettings: Create a version and remove dirty fag for the modified settings in the list, remove dirty flag for all default settings 5926 5927 my $Success = $SysConfigObject->_HandleSettingsToDeploy( 5928 NotDirty => 1, # optional - exclusive (1||0) 5929 AllSettings => 1, # optional - exclusive (1||0) 5930 DirtySettings => [ 'SettingName1', 'SettingName2' ], # optional - exclusive 5931 DeploymentTimeStamp => 2017-12-12 12:00:00' 5932 UserID => 123, 5933 ); 5934 5935Returns: 5936 5937 $Success = 1; # or false in case of a failure 5938 5939=cut 5940 5941sub _HandleSettingsToDeploy { 5942 my ( $Self, %Param ) = @_; 5943 5944 for my $Needed (qw(UserID DeploymentTimeStamp)) { 5945 if ( !$Param{$Needed} ) { 5946 $Kernel::OM->Get('Kernel::System::Log')->Log( 5947 Priority => 'error', 5948 Message => "Need $Needed", 5949 ); 5950 return; 5951 } 5952 } 5953 5954 if ( !$Param{NotDirty} && !$Param{DirtySettings} ) { 5955 $Param{AllSettings} = 1; 5956 } 5957 5958 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 5959 5960 # Remove is dirty flag for default settings. 5961 my $DefaultCleanup = $SysConfigDBObject->DefaultSettingDirtyCleanUp( 5962 AllSettings => $Param{AllSettings}, 5963 ); 5964 if ( !$DefaultCleanup ) { 5965 $Kernel::OM->Get('Kernel::System::Log')->Log( 5966 Priority => 'error', 5967 Message => "Could not remove IsDirty flag from default settings", 5968 ); 5969 } 5970 5971 return 1 if $Param{NotDirty}; 5972 5973 my %DirtySettingsLookup = map { $_ => 1 } @{ $Param{DirtySettings} // [] }; 5974 5975 # Get all dirty modified settings. 5976 my @DirtyModifiedList = $SysConfigDBObject->ModifiedSettingListGet( 5977 IsGlobal => 1, 5978 IsDirty => 1, 5979 ); 5980 5981 my %VersionsAdded; 5982 my @ModifiedDeleted; 5983 my @ModifiedIDs; 5984 my $Error; 5985 5986 # Create a new version for the modified settings. 5987 SETTING: 5988 for my $Setting (@DirtyModifiedList) { 5989 5990 # Skip setting if it is not in the list (and it is not a full deployment) 5991 next SETTING if !$Param{AllSettings} && !$DirtySettingsLookup{ $Setting->{Name} }; 5992 5993 my %DefaultSettingVersionGetLast = $SysConfigDBObject->DefaultSettingVersionGetLast( 5994 DefaultID => $Setting->{DefaultID}, 5995 ); 5996 5997 my $ModifiedVersionID = $SysConfigDBObject->ModifiedSettingVersionAdd( 5998 %{$Setting}, 5999 DefaultVersionID => $DefaultSettingVersionGetLast{DefaultVersionID}, 6000 DeploymentTimeStamp => $Param{DeploymentTimeStamp}, 6001 UserID => $Param{UserID}, 6002 ); 6003 6004 if ( !$ModifiedVersionID ) { 6005 $Kernel::OM->Get('Kernel::System::Log')->Log( 6006 Priority => 'error', 6007 Message => "Could not create a modified setting version for $Setting->{Name}! Rolling back.", 6008 ); 6009 $Error = 1; 6010 last SETTING; 6011 } 6012 6013 $VersionsAdded{ $Setting->{Name} } = $ModifiedVersionID; 6014 6015 if ( !$Setting->{ResetToDefault} ) { 6016 push @ModifiedIDs, $Setting->{ModifiedID}; 6017 next SETTING; 6018 } 6019 6020 # In case a setting value reset, delete the modified value. 6021 my $ModifiedDelete = $SysConfigDBObject->ModifiedSettingDelete( 6022 ModifiedID => $Setting->{ModifiedID}, 6023 ); 6024 6025 if ( !$ModifiedDelete ) { 6026 $Kernel::OM->Get('Kernel::System::Log')->Log( 6027 Priority => 'error', 6028 Message => 6029 "Could not delete the modified setting for $Setting->{Name} on reset action! Rolling back.", 6030 ); 6031 $Error = 1; 6032 last SETTING; 6033 } 6034 6035 push @ModifiedDeleted, $Setting; 6036 } 6037 6038 # In case of an error: 6039 # Remove "all" added versions for "all" settings for this deployment. 6040 # Restore "all" deleted modified settings. 6041 if ($Error) { 6042 for my $SettingName ( sort keys %VersionsAdded ) { 6043 my $Success = $SysConfigDBObject->ModifiedSettingVersionDelete( 6044 ModifiedVersionID => $VersionsAdded{$SettingName}, 6045 ); 6046 } 6047 6048 for my $Setting (@ModifiedDeleted) { 6049 my $Success = $SysConfigDBObject->ModifiedSettingAdd( 6050 %{$Setting}, 6051 DeploymentExclusiveLockGUID => $Param{DeploymentExclusiveLockGUID}, 6052 UserID => $Setting->{ChangeBy}, 6053 ); 6054 } 6055 6056 return; 6057 } 6058 6059 # Do not clean dirty flag if no setting version was created and it is not a full deployment 6060 return 1 if !$Param{AllSettings} && !@ModifiedIDs; 6061 6062 my %Options; 6063 if ( !$Param{AllSettings} ) { 6064 $Options{ModifiedIDs} = \@ModifiedIDs; 6065 } 6066 6067 # Remove is dirty flag for modified settings. 6068 my $ModifiedCleanup = $SysConfigDBObject->ModifiedSettingDirtyCleanUp(%Options); 6069 if ( !$ModifiedCleanup ) { 6070 $Kernel::OM->Get('Kernel::System::Log')->Log( 6071 Priority => 'error', 6072 Message => "Could not remove IsDirty flag from modified settings", 6073 ); 6074 } 6075 6076 return 1; 6077} 6078 6079=head2 _SettingTranslatedGet() 6080 6081Helper method for ConfigurationTranslatedGet(). 6082 6083 my %Result = $SysConfigObject->_SettingTranslatedGet( 6084 Language => 'de', # (required) User language 6085 Name => 'SettingName', # (required) Setting name 6086 Silent => 1, # (optional) Default 1 6087 ); 6088 6089Returns: 6090 6091 %Result = ( 6092 'ACL::CacheTTL' => { 6093 'Category' => 'OTRS', 6094 'IsInvisible' => '0', 6095 'Metadata' => "ACL::CacheTTL--- '3600' 6096Cache-Zeit in Sekunden f\x{fc}r Datenbank ACL-Backends.", 6097 ); 6098 6099=cut 6100 6101sub _SettingTranslatedGet { 6102 my ( $Self, %Param ) = @_; 6103 6104 # Check needed stuff. 6105 for my $Needed (qw(Language Name)) { 6106 if ( !$Param{$Needed} ) { 6107 $Kernel::OM->Get('Kernel::System::Log')->Log( 6108 Priority => 'error', 6109 Message => "Need $Needed!", 6110 ); 6111 return; 6112 } 6113 } 6114 6115 my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); 6116 6117 my $CacheType = 'SysConfig'; 6118 my $CacheKey = "SettingTranslatedGet::$Param{Language}::$Param{Name}"; 6119 6120 # Return cache. 6121 my $Cache = $CacheObject->Get( 6122 Type => $CacheType, 6123 Key => $CacheKey, 6124 ); 6125 6126 return %{$Cache} if ref $Cache eq 'HASH'; 6127 6128 my $YAMLObject = $Kernel::OM->Get('Kernel::System::YAML'); 6129 my %Categories = $Self->ConfigurationCategoriesGet(); 6130 6131 my %SettingTranslated = $Self->SettingGet( 6132 Name => $Param{Name}, 6133 Translate => 1, 6134 ); 6135 6136 my $Metadata = $Param{Name}; 6137 $Metadata .= $YAMLObject->Dump( 6138 Data => $SettingTranslated{EffectiveValue}, 6139 ); 6140 $Metadata .= $SettingTranslated{Description}; 6141 6142 my %Result; 6143 $Result{ $Param{Name} }->{Metadata} = lc $Metadata; 6144 6145 # Check setting category. 6146 my $SettingCategory; 6147 6148 my $Silent = $Param{Silent} // 1; 6149 6150 CATEGORY: 6151 for my $Category ( sort keys %Categories ) { 6152 if ( grep { $_ eq $SettingTranslated{XMLFilename} } @{ $Categories{$Category}->{Files} } ) { 6153 $SettingCategory = $Category; 6154 last CATEGORY; 6155 } 6156 } 6157 6158 if ( !$SettingCategory ) { 6159 if ( !$Silent ) { 6160 $Kernel::OM->Get('Kernel::System::Log')->Log( 6161 Priority => 'error', 6162 Message => "Category couldn't be determined for $Param{Name}!", 6163 ); 6164 } 6165 $SettingCategory = '-Unknown-'; 6166 } 6167 $Result{ $Param{Name} }->{Category} = $SettingCategory; 6168 $Result{ $Param{Name} }->{IsInvisible} = $SettingTranslated{IsInvisible}; 6169 6170 $CacheObject->Set( 6171 Type => $CacheType, 6172 Key => $CacheKey, 6173 Value => \%Result, 6174 TTL => $Self->{CacheTTL} || 24 * 60 * 60, 6175 ); 6176 6177 return %Result; 6178} 6179 6180=head2 _ValueTypesList() 6181 6182Returns a hash of forbidden value types. 6183 6184 my @ValueTypes = $SysConfigObject->_ValueTypesList(); 6185 6186Returns: 6187 6188 @ValueTypes = ( 6189 "Checkbox", 6190 "Select", 6191 ... 6192 ); 6193 6194=cut 6195 6196sub _ValueTypesList { 6197 my ( $Self, %Param ) = @_; 6198 6199 my $CacheType = 'SysConfig'; 6200 my $CacheKey = '_ValueTypesList'; 6201 6202 my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); 6203 6204 # Return cache. 6205 my $Cache = $CacheObject->Get( 6206 Type => $CacheType, 6207 Key => $CacheKey, 6208 ); 6209 6210 return @{$Cache} if ref $Cache eq 'ARRAY'; 6211 6212 my $MainObject = $Kernel::OM->Get('Kernel::System::Main'); 6213 6214 my @Files = $MainObject->DirectoryRead( 6215 Directory => $Self->{Home} . "/Kernel/System/SysConfig/ValueType", 6216 Filter => '*.pm', 6217 ); 6218 6219 my @Result; 6220 for my $File (@Files) { 6221 6222 my $ValueType = $File; 6223 6224 # Remove folder path. 6225 $ValueType =~ s{^.*/}{}sm; 6226 6227 # Remove extension 6228 $ValueType =~ s{\.pm$}{}sm; 6229 6230 push @Result, $ValueType; 6231 } 6232 6233 $CacheObject->Set( 6234 Type => $CacheType, 6235 Key => $CacheKey, 6236 Value => \@Result, 6237 TTL => 24 * 3600, # 1 day 6238 ); 6239 6240 return @Result; 6241} 6242 6243=head2 _DefaultSettingAddBulk() 6244 6245Helper method for ConfigurationXML2DB() - bulk insert. 6246 6247 my $Success = $SysConfigObject->_DefaultSettingAddBulk( 6248 Settings => { # (required) Hash of settings to insert 6249 'SettingName' => { 6250 6251 }, 6252 ... 6253 }, 6254 SettingList => [ # (required) List of settings 6255 ... 6256 ], 6257 UserID => 1, # (required) UserID 6258 ); 6259 6260=cut 6261 6262sub _DefaultSettingAddBulk { 6263 my ( $Self, %Param ) = @_; 6264 6265 # Check needed stuff. 6266 for my $Needed (qw(Settings SettingList UserID)) { 6267 if ( !$Param{$Needed} ) { 6268 $Kernel::OM->Get('Kernel::System::Log')->Log( 6269 Priority => 'error', 6270 Message => "Need $Needed!", 6271 ); 6272 return; 6273 } 6274 } 6275 6276 # Check needed stuff. 6277 if ( ref $Param{Settings} ne 'HASH' ) { 6278 $Kernel::OM->Get('Kernel::System::Log')->Log( 6279 Priority => 'error', 6280 Message => "Settings must be a HASH ref!", 6281 ); 6282 return; 6283 } 6284 6285 my $StorableObject = $Kernel::OM->Get('Kernel::System::Storable'); 6286 my $SysConfigDBObject = $Kernel::OM->Get('Kernel::System::SysConfig::DB'); 6287 my $YAMLObject = $Kernel::OM->Get('Kernel::System::YAML'); 6288 6289 my %Settings = %{ $Param{Settings} }; 6290 my @SettingList = @{ $Param{SettingList} }; 6291 6292 for my $SettingName ( sort keys %{ $Param{Settings} } ) { 6293 6294 # Create a local clone of the value to prevent any modification. 6295 my $Value = $StorableObject->Clone( 6296 Data => $Settings{$SettingName}->{XMLContentParsed}->{Value}, 6297 ); 6298 6299 $Settings{$SettingName}->{EffectiveValue} = $Self->SettingEffectiveValueGet( 6300 Value => $Value, 6301 ); 6302 6303 # Serialize values that doesn't have string representation. 6304 $Settings{$SettingName}->{EffectiveValue} = $YAMLObject->Dump( 6305 Data => $Settings{$SettingName}->{EffectiveValue}, 6306 ); 6307 $Settings{$SettingName}->{XMLContentParsedYAML} = $YAMLObject->Dump( 6308 Data => $Settings{$SettingName}->{XMLContentParsed}, 6309 ); 6310 } 6311 6312 my $Success = $SysConfigDBObject->DefaultSettingBulkAdd( 6313 Settings => \%Settings, 6314 SettingList => \@SettingList, 6315 UserID => $Param{UserID}, 6316 ); 6317 6318 if ( !$Success ) { 6319 $Kernel::OM->Get('Kernel::System::Log')->Log( 6320 Priority => 'error', 6321 Message => "System was unable to rebuild config!" 6322 ); 6323 return; 6324 } 6325 6326 # Get again all settings. 6327 @SettingList = $Self->ConfigurationList( 6328 IncludeInvisible => 1, 6329 ); 6330 6331 $Success = $SysConfigDBObject->DefaultSettingVersionBulkAdd( 6332 Settings => \%Settings, 6333 SettingList => \@SettingList, 6334 UserID => $Param{UserID}, 6335 ); 6336 6337 if ( !$Success ) { 6338 $Kernel::OM->Get('Kernel::System::Log')->Log( 6339 Priority => 'error', 6340 Message => "System was unable to rebuild config!" 6341 ); 6342 return; 6343 } 6344 6345 return 1; 6346} 6347 63481; 6349 6350=head1 TERMS AND CONDITIONS 6351 6352This software is part of the OTRS project (L<https://otrs.org/>). 6353 6354This software comes with ABSOLUTELY NO WARRANTY. For details, see 6355the enclosed file COPYING for license information (GPL). If you 6356did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>. 6357 6358=cut 6359