# --
# OTOBO is a web-based ticketing system for service organisations.
# --
# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/
# Copyright (C) 2019-2023 Rother OSS GmbH, https://otobo.de/
# --
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
# --
package Kernel::System::CustomerUser;
use strict;
use warnings;
use parent qw(Kernel::System::EventHandler);
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::Language',
'Kernel::System::Cache',
'Kernel::System::CustomerCompany',
'Kernel::System::DB',
'Kernel::System::DynamicField',
'Kernel::System::DynamicField::Backend',
'Kernel::System::Encode',
'Kernel::System::Log',
'Kernel::System::Main',
'Kernel::System::Valid',
);
=head1 NAME
Kernel::System::CustomerUser - customer user lib
=head1 DESCRIPTION
All customer user functions. E. g. to add and update customer users.
=head1 PUBLIC INTERFACE
=head2 new()
Don't use the constructor directly, use the ObjectManager instead:
my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser');
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
$Self->{CacheType} = 'CustomerUser';
$Self->{CacheTTL} = 60 * 60 * 24 * 20;
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# load generator customer preferences module
my $GeneratorModule = $ConfigObject->Get('CustomerPreferences')->{Module}
|| 'Kernel::System::CustomerUser::Preferences::DB';
# get main object
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
if ( $MainObject->Require($GeneratorModule) ) {
$Self->{PreferencesObject} = $GeneratorModule->new();
}
# load customer user backend module
SOURCE:
for my $Count ( '', 1 .. 10 ) {
next SOURCE if !$ConfigObject->Get("CustomerUser$Count");
my $GenericModule = $ConfigObject->Get("CustomerUser$Count")->{Module};
if ( !$MainObject->Require($GenericModule) ) {
$MainObject->Die("Can't load backend module $GenericModule! $@");
}
$Self->{"CustomerUser$Count"} = $GenericModule->new(
Count => $Count,
PreferencesObject => $Self->{PreferencesObject},
CustomerUserMap => $ConfigObject->Get("CustomerUser$Count"),
);
}
# init of event handler
$Self->EventHandlerInit(
Config => 'CustomerUser::EventModulePost',
);
return $Self;
}
=head2 CustomerSourceList()
return customer source list
my %List = $CustomerUserObject->CustomerSourceList(
ReadOnly => 0 # optional, 1 returns only RO backends, 0 returns writable, if not passed returns all backends
);
=cut
sub CustomerSourceList {
my ( $Self, %Param ) = @_;
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my %Data;
SOURCE:
for my $Count ( '', 1 .. 10 ) {
next SOURCE if !$ConfigObject->Get("CustomerUser$Count");
if ( defined $Param{ReadOnly} ) {
my $CustomerBackendConfig = $ConfigObject->Get("CustomerUser$Count");
if ( $Param{ReadOnly} ) {
next SOURCE if !$CustomerBackendConfig->{ReadOnly} || $CustomerBackendConfig->{Module} !~ /LDAP/i;
}
else {
next SOURCE if $CustomerBackendConfig->{ReadOnly} || $CustomerBackendConfig->{Module} =~ /LDAP/i;
}
}
$Data{"CustomerUser$Count"} = $ConfigObject->Get("CustomerUser$Count")->{Name}
|| "No Name $Count";
}
return %Data;
}
=head2 CustomerSearch()
to search users
# text search
my %List = $CustomerUserObject->CustomerSearch(
Search => '*some*', # also 'hans+huber' possible
Valid => 1, # (optional) default 1
Limit => 100, # (optional) overrides limit of the config
# RotherOSS / ConsoleCommands
Source => ['ldap'], # (optional) limit search to certain backends
# EO ConsoleCommands
);
# username search
my %List = $CustomerUserObject->CustomerSearch(
UserLogin => '*some*',
Valid => 1, # (optional) default 1
);
# email search
my %List = $CustomerUserObject->CustomerSearch(
PostMasterSearch => 'email@example.com',
Valid => 1, # (optional) default 1
);
# search by CustomerID
my %List = $CustomerUserObject->CustomerSearch(
CustomerID => 'CustomerID123',
Valid => 1, # (optional) default 1
);
=cut
sub CustomerSearch {
my ( $Self, %Param ) = @_;
# RotherOSS / ConsoleCommands
if ( $Param{Source} && !IsArrayRefWithData( $Param{Source} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'debug',
Message => "Source filter is empty, ignoring it.",
);
}
# EO ConsoleCommands
# remove leading and ending spaces
if ( $Param{Search} ) {
$Param{Search} =~ s/^\s+//;
$Param{Search} =~ s/\s+$//;
}
# Get dynamic field object.
my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField');
my $DynamicFieldConfigs = $DynamicFieldObject->DynamicFieldListGet(
ObjectType => 'CustomerUser',
Valid => 1,
);
my %DynamicFieldLookup = map { $_->{Name} => $_ } @{$DynamicFieldConfigs};
# Get dynamic field backend object.
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
my %Data;
SOURCE:
for my $Count ( '', 1 .. 10 ) {
# RotherOSS / ConsoleCommands
next SOURCE if $Param{Source} && !grep { $_ eq "CustomerUser$Count" } $Param{Source}->@*;
# EO ConsoleCommands
next SOURCE if !$Self->{"CustomerUser$Count"};
# search dynamic field values, if configured
my $Map = $Self->{"CustomerUser$Count"}->{CustomerUserMap}->{Map};
if ( IsArrayRefWithData($Map) ) {
# fetch dynamic field names that are configured in Map
# only these will be considered for any other search config
# [ 'DynamicField_Name_X', undef, 'Name_X', 0, 0, 'dynamic_field', undef, 0, undef, undef, ],
my %DynamicFieldNames = map { $_->[2] => 1 } grep { $_->[5] eq 'dynamic_field' } @{$Map};
if ( IsHashRefWithData( \%DynamicFieldNames ) ) {
my $FoundDynamicFieldObjectIDs;
my $SearchFields;
my $SearchParam;
# check which of the dynamic fields configured in Map are also
# configured in SearchFields
# param Search
if ( defined $Param{Search} && length $Param{Search} ) {
$SearchFields = $Self->{"CustomerUser$Count"}->{CustomerUserMap}->{CustomerUserSearchFields};
$SearchParam = $Param{Search};
}
# param PostMasterSearch
elsif ( defined $Param{PostMasterSearch} && length $Param{PostMasterSearch} ) {
$SearchFields = $Self->{"CustomerUser$Count"}->{CustomerUserMap}->{CustomerUserPostMasterSearchFields};
$SearchParam = $Param{PostMasterSearch};
}
# search dynamic field values
if ( IsArrayRefWithData($SearchFields) ) {
my @SearchDynamicFieldNames = grep { exists $DynamicFieldNames{$_} } @{$SearchFields};
my %FoundDynamicFieldObjectIDs;
FIELDNAME:
for my $FieldName (@SearchDynamicFieldNames) {
my $DynamicFieldConfig = $DynamicFieldLookup{$FieldName};
next FIELDNAME if !IsHashRefWithData($DynamicFieldConfig);
my $DynamicFieldValues = $DynamicFieldBackendObject->ValueSearch(
DynamicFieldConfig => $DynamicFieldConfig,
Search => $SearchParam,
);
if ( IsArrayRefWithData($DynamicFieldValues) ) {
for my $DynamicFieldValue ( @{$DynamicFieldValues} ) {
$FoundDynamicFieldObjectIDs{ $DynamicFieldValue->{ObjectID} } = 1;
}
}
}
$FoundDynamicFieldObjectIDs = [ keys %FoundDynamicFieldObjectIDs ];
}
# execute backend search for found object IDs
# this data is being merged with the following CustomerSearch call
if ( IsArrayRefWithData($FoundDynamicFieldObjectIDs) ) {
my $ObjectNames = $DynamicFieldObject->ObjectMappingGet(
ObjectID => $FoundDynamicFieldObjectIDs,
ObjectType => 'CustomerUser',
);
OBJECTNAME:
for my $ObjectName ( values %{$ObjectNames} ) {
next OBJECTNAME if exists $Data{$ObjectName};
my %SearchParam = %Param;
delete $SearchParam{Search};
delete $SearchParam{PostMasterSearch};
$SearchParam{UserLogin} = $ObjectName;
my %SubData = $Self->{"CustomerUser$Count"}->CustomerSearch(%SearchParam);
# UserLogin search does a wild-card search, but in this case only the
# exact matching user login is relevant
if ( IsHashRefWithData( \%SubData ) && exists $SubData{$ObjectName} ) {
%Data = (
$ObjectName => $SubData{$ObjectName},
%Data
);
}
}
}
}
}
# get customer search result of backend and merge it
my %SubData = $Self->{"CustomerUser$Count"}->CustomerSearch(%Param);
%Data = ( %SubData, %Data );
}
return %Data;
}
=head2 CustomerSearchDetail()
To find customer users in the system.
The search criteria are logically AND connected.
When a list is passed as criteria, the individual members are OR connected.
When an undef or a reference to an empty array is passed, then the search criteria
is ignored.
Returns either a list, as an arrayref, or a count of found customer user ids.
The count of results is returned when the parameter C is passed.
my $CustomerUserIDsRef = $CustomerUserObject->CustomerSearchDetail(
# all fields which are defined in a CustomerUserMap, except password fields, are searchable
UserLogin => 'example*', # (optional)
UserFirstname => 'Firstn*', # (optional)
# special parameters
CustomerCompanySearchCustomerIDs => [ 'example.com' ], # (optional)
ExcludeUserLogins => [ 'example', 'doejohn' ], # (optional)
# array parameters are used with logical OR operator (all values are possible which
are defined in the config selection hash for the field)
UserCountry => [ 'Austria', 'Germany', ], # (optional)
# DynamicFields
# At least one operator must be specified. Operators will be connected with AND,
# values in an operator with OR.
# You can also pass more than one argument to an operator: ['value1', 'value2']
DynamicField_FieldNameX => {
Equals => 123,
Like => 'value*', # "equals" operator with wildcard support
GreaterThan => '2001-01-01 01:01:01',
GreaterThanEquals => '2001-01-01 01:01:01',
SmallerThan => '2002-02-02 02:02:02',
SmallerThanEquals => '2002-02-02 02:02:02',
}
OrderBy => [ 'UserLogin', 'UserCustomerID' ], # (optional)
# ignored if the result type is 'COUNT'
# default: [ 'UserLogin' ]
# (all fields which are defined in a CustomerUserMap can be used for ordering)
# Additional information for OrderBy:
# The OrderByDirection can be specified for each OrderBy attribute.
# The pairing is made by the array indices.
OrderByDirection => [ 'Down', 'Up' ], # (optional)
# ignored if the result type is 'COUNT'
# (Down | Up) Default: [ 'Down' ]
Result => 'ARRAY' || 'COUNT', # (optional)
# default: ARRAY, returns an array of change ids
# COUNT returns a scalar with the number of found changes
Limit => 100, # (optional)
# ignored if the result type is 'COUNT'
);
Returns:
Result: 'ARRAY'
@CustomerUserIDs = ( 1, 2, 3 );
Result: 'COUNT'
$CustomerUserIDs = 10;
=cut
sub CustomerSearchDetail {
my ( $Self, %Param ) = @_;
# get all general search fields (without a restriction to a source)
my @AllSearchFields = $Self->CustomerUserSearchFields();
# generate a hash with the customer user sources which must be searched
my %SearchCustomerUserSources;
SOURCE:
for my $Count ( '', 1 .. 10 ) {
next SOURCE if !$Self->{"CustomerUser$Count"};
# get the search fields for the current source
my @SourceSearchFields = $Self->CustomerUserSearchFields(
Source => "CustomerUser$Count",
);
my %LookupSourceSearchFields = map { $_->{Name} => 1 } @SourceSearchFields;
# check if all search param exists in the search fields from the current source
SEARCHFIELD:
for my $SearchField (@AllSearchFields) {
next SEARCHFIELD if !$Param{ $SearchField->{Name} };
next SOURCE if !$LookupSourceSearchFields{ $SearchField->{Name} };
}
$SearchCustomerUserSources{"CustomerUser$Count"} = \@SourceSearchFields;
}
# set the default behaviour for the return type
$Param{Result} ||= 'ARRAY';
if ( $Param{Result} eq 'COUNT' ) {
my $IDsCount = 0;
SOURCE:
for my $Source ( sort keys %SearchCustomerUserSources ) {
next SOURCE if !$Self->{$Source};
my $SubIDsCount = $Self->{$Source}->CustomerSearchDetail(
%Param,
SearchFields => $SearchCustomerUserSources{$Source},
);
return if !defined $SubIDsCount;
$IDsCount += $SubIDsCount || 0;
}
return $IDsCount;
}
else {
my @IDs;
my $ResultCount = 0;
SOURCE:
for my $Source ( sort keys %SearchCustomerUserSources ) {
next SOURCE if !$Self->{$Source};
my $SubIDs = $Self->{$Source}->CustomerSearchDetail(
%Param,
SearchFields => $SearchCustomerUserSources{$Source},
);
return if !defined $SubIDs;
next SOURCE if !IsArrayRefWithData($SubIDs);
push @IDs, @{$SubIDs};
$ResultCount++;
}
# if we have more then one search results from diffrent sources, we need a resorting
# because of the merged single results
if ( $ResultCount > 1 ) {
my @UserDataList;
for my $ID (@IDs) {
my %UserData = $Self->CustomerUserDataGet(
User => $ID,
);
push @UserDataList, \%UserData;
}
my $OrderBy = 'UserLogin';
if ( IsArrayRefWithData( $Param{OrderBy} ) ) {
$OrderBy = $Param{OrderBy}->[0];
}
if ( IsArrayRefWithData( $Param{OrderByDirection} ) && $Param{OrderByDirection}->[0] eq 'Up' ) {
@UserDataList = sort { lc( $a->{$OrderBy} ) cmp lc( $b->{$OrderBy} ) } @UserDataList;
}
else {
@UserDataList = sort { lc( $b->{$OrderBy} ) cmp lc( $a->{$OrderBy} ) } @UserDataList;
}
if ( $Param{Limit} && scalar @UserDataList > $Param{Limit} ) {
splice @UserDataList, $Param{Limit};
}
@IDs = map { $_->{UserLogin} } @UserDataList;
}
return \@IDs;
}
}
=head2 CustomerUserSearchFields()
Get a list of the defined search fields (optional only the relevant fields for the given source).
my @SeachFields = $CustomerUserObject->CustomerUserSearchFields(
Source => 'CustomerUser', # optional, but important in the CustomerSearchDetail to get the right database fields
);
Returns an array of hash references.
@SeachFields = (
{
Name => 'UserEmail',
Label => 'Email',
Type => 'Input',
DatabaseField => 'mail',
},
{
Name => 'UserCountry',
Label => 'Country',
Type => 'Selection',
SelectionsData => {
'Germany' => 'Germany',
'United Kingdom' => 'United Kingdom',
'United States' => 'United States',
...
},
DatabaseField => 'country',
},
{
Name => 'DynamicField_SkypeAccountName',
Label => '',
Type => 'DynamicField',
DatabaseField => 'SkypeAccountName',
},
);
=cut
sub CustomerUserSearchFields {
my ( $Self, %Param ) = @_;
# Get the search fields from all customer user maps (merge from all maps together).
my @SearchFields;
my %SearchFieldsExists;
SOURCE:
for my $Count ( '', 1 .. 10 ) {
next SOURCE if !$Self->{"CustomerUser$Count"};
next SOURCE if $Param{Source} && $Param{Source} ne "CustomerUser$Count";
ENTRY:
for my $Entry ( @{ $Self->{"CustomerUser$Count"}->{CustomerUserMap}->{Map} } ) {
my $SearchFieldName = $Entry->[0];
next ENTRY if $SearchFieldsExists{$SearchFieldName};
next ENTRY if $SearchFieldName =~ m{(Password|Pw)\d*$}smxi;
# Remember the already collected search field name.
$SearchFieldsExists{$SearchFieldName} = 1;
my %FieldConfig = $Self->GetFieldConfig(
FieldName => $SearchFieldName,
Source => $Param{Source}, # to get the right database field for the given source
);
next ENTRY if !%FieldConfig;
my %SearchFieldData = (
%FieldConfig,
Name => $SearchFieldName,
);
my %SelectionsData = $Self->GetFieldSelections(
FieldName => $SearchFieldName,
);
if ( $SearchFieldData{StorageType} eq 'dynamic_field' ) {
$SearchFieldData{Type} = 'DynamicField';
}
elsif (%SelectionsData) {
$SearchFieldData{Type} = 'Selection';
$SearchFieldData{SelectionsData} = \%SelectionsData;
}
else {
$SearchFieldData{Type} = 'Input';
}
push @SearchFields, \%SearchFieldData;
}
}
return @SearchFields;
}
=head2 GetFieldConfig()
This function collect some field config information from the customer user map.
my %FieldConfig = $CustomerUserObject->GetFieldConfig(
FieldName => 'UserEmail',
Source => 'CustomerUser', # optional
);
Returns some field config information:
my %FieldConfig = (
Label => 'Email',
DatabaseField => 'email',
StorageType => 'var',
);
=cut
sub GetFieldConfig {
my ( $Self, %Param ) = @_;
if ( !$Param{FieldName} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need FieldName!"
);
return;
}
SOURCE:
for my $Count ( '', 1 .. 10 ) {
next SOURCE if !$Self->{"CustomerUser$Count"};
next SOURCE if $Param{Source} && $Param{Source} ne "CustomerUser$Count";
# Search the right field and return some config information from the field.
ENTRY:
for my $Entry ( @{ $Self->{"CustomerUser$Count"}->{CustomerUserMap}->{Map} } ) {
next ENTRY if $Param{FieldName} ne $Entry->[0];
my %FieldConfig = (
Label => $Entry->[1],
DatabaseField => $Entry->[2],
StorageType => $Entry->[5],
);
return %FieldConfig;
}
}
return;
}
=head2 GetFieldSelections()
This function collect the selections for the given field name, if the field has some selections.
my %SelectionsData = $CustomerUserObject->GetFieldSelections(
FieldName => 'UserTitle',
);
Returns the selections for the given field name (merged from all sources) or a empty hash:
my %SelectionData = (
'Mr.' => 'Mr.',
'Mrs.' => 'Mrs.',
);
=cut
sub GetFieldSelections {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{FieldName} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need FieldName!"
);
return;
}
my %SelectionsData;
SOURCE:
for my $Count ( '', 1 .. 10 ) {
next SOURCE if !$Self->{"CustomerUser$Count"};
next SOURCE if !$Self->{"CustomerUser$Count"}->{CustomerUserMap}->{Selections}->{ $Param{FieldName} };
%SelectionsData = (
%SelectionsData, %{ $Self->{"CustomerUser$Count"}->{CustomerUserMap}->{Selections}->{ $Param{FieldName} } }
);
}
# Make sure the encoding stamp is set.
for my $Key ( sort keys %SelectionsData ) {
$SelectionsData{$Key} = $Kernel::OM->Get('Kernel::System::Encode')->EncodeInput( $SelectionsData{$Key} );
}
# Default handling for field 'ValidID'.
if ( !%SelectionsData && $Param{FieldName} =~ /^ValidID/i ) {
%SelectionsData = $Kernel::OM->Get('Kernel::System::Valid')->ValidList();
}
return %SelectionsData;
}
=head2 CustomerIDList()
return a list of with all known unique CustomerIDs of the registered customers users (no SearchTerm),
or a filtered list where the CustomerIDs must contain a search term.
my @CustomerIDs = $CustomerUserObject->CustomerIDList(
SearchTerm => 'somecustomer', # optional
Valid => 1, # optional
);
=cut
sub CustomerIDList {
my ( $Self, %Param ) = @_;
my @Data;
SOURCE:
for my $Count ( '', 1 .. 10 ) {
next SOURCE if !$Self->{"CustomerUser$Count"};
# get customer list result of backend and merge it
push @Data, $Self->{"CustomerUser$Count"}->CustomerIDList(%Param);
}
# make entries unique
my %Tmp;
@Tmp{@Data} = undef;
@Data = sort { lc $a cmp lc $b } keys %Tmp;
return @Data;
}
=head2 CustomerName()
get customer user name
my $Name = $CustomerUserObject->CustomerName(
UserLogin => 'some-login',
);
=cut
sub CustomerName {
my ( $Self, %Param ) = @_;
SOURCE:
for my $Count ( '', 1 .. 10 ) {
next SOURCE if !$Self->{"CustomerUser$Count"};
# Get customer name and return it.
my $Name = $Self->{"CustomerUser$Count"}->CustomerName(%Param);
if ($Name) {
return $Name;
}
}
return;
}
=head2 CustomerIDs()
get customer user customer ids
my @CustomerIDs = $CustomerUserObject->CustomerIDs(
User => 'some-login',
);
=cut
sub CustomerIDs {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{User} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need User!'
);
return;
}
# get customer ids (stop after first source with results)
my @CustomerIDs;
SOURCE:
for my $Count ( '', 1 .. 10 ) {
next SOURCE if !$Self->{"CustomerUser$Count"};
# get customer ids from source
my @SourceCustomerIDs = $Self->{"CustomerUser$Count"}->CustomerIDs(%Param);
next SOURCE if !@SourceCustomerIDs;
@CustomerIDs = @SourceCustomerIDs;
last SOURCE;
}
# create hash with existing customer ids
my %CustomerIDs = map { $_ => 1 } @CustomerIDs;
# get related customer ids
my @RelatedCustomerIDs = $Self->CustomerUserCustomerMemberList(
CustomerUserID => $Param{User},
);
# add related customer ids if not found in source
RELATEDCUSTOMERID:
for my $RelatedCustomerID (@RelatedCustomerIDs) {
next RELATEDCUSTOMERID if $CustomerIDs{$RelatedCustomerID};
push @CustomerIDs, $RelatedCustomerID;
}
# return customer ids
return @CustomerIDs;
}
=head2 CustomerUserDataGet()
get user data (UserLogin, UserFirstname, UserLastname, UserEmail, ...)
my %User = $CustomerUserObject->CustomerUserDataGet(
User => 'franz',
);
=cut
sub CustomerUserDataGet {
my ( $Self, %Param ) = @_;
return if !$Param{User};
# fetch dynamic field configurations for CustomerUser.
my $DynamicFieldConfigs = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
ObjectType => 'CustomerUser',
Valid => 1,
);
my %DynamicFieldLookup = map { $_->{Name} => $_ } @{$DynamicFieldConfigs};
# Get needed objects.
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $CustomerCompanyObject = $Kernel::OM->Get('Kernel::System::CustomerCompany');
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
SOURCE:
for my $Count ( '', 1 .. 10 ) {
next SOURCE if !$Self->{"CustomerUser$Count"};
my %Customer = $Self->{"CustomerUser$Count"}->CustomerUserDataGet(%Param);
next SOURCE if !%Customer;
# generate the full name and save it in the hash
my $UserFullname = $Self->CustomerName(%Customer);
# save the generated fullname in the hash.
$Customer{UserFullname} = $UserFullname;
# add preferences defaults
my $Config = $ConfigObject->Get('CustomerPreferencesGroups');
if ($Config) {
KEY:
for my $Key ( sort keys %{$Config} ) {
next KEY if !defined $Config->{$Key}->{DataSelected};
next KEY if defined $Customer{ $Config->{$Key}->{PrefKey} };
# set default data
$Customer{ $Config->{$Key}->{PrefKey} } = $Config->{$Key}->{DataSelected};
}
}
# check if customer company support is enabled and get company data
my %Company;
if (
$ConfigObject->Get("CustomerCompany")
&& $ConfigObject->Get("CustomerUser$Count")->{CustomerCompanySupport}
)
{
%Company = $CustomerCompanyObject->CustomerCompanyGet(
CustomerID => $Customer{UserCustomerID},
);
$Company{CustomerCompanyValidID} = $Company{ValidID};
}
# fetch dynamic field values
if ( IsArrayRefWithData( $Self->{"CustomerUser$Count"}->{CustomerUserMap}->{Map} ) ) {
CUSTOMERUSERFIELD:
for my $CustomerUserField ( @{ $Self->{"CustomerUser$Count"}->{CustomerUserMap}->{Map} } ) {
next CUSTOMERUSERFIELD if $CustomerUserField->[5] ne 'dynamic_field';
next CUSTOMERUSERFIELD if !$DynamicFieldLookup{ $CustomerUserField->[2] };
my $Value = $DynamicFieldBackendObject->ValueGet(
DynamicFieldConfig => $DynamicFieldLookup{ $CustomerUserField->[2] },
ObjectName => $Customer{UserID},
);
$Customer{ $CustomerUserField->[0] } = $Value;
}
}
# return customer data
return (
%Company,
%Customer,
Source => "CustomerUser$Count",
Config => $ConfigObject->Get("CustomerUser$Count"),
CompanyConfig => $ConfigObject->Get( $Company{Source} // 'CustomerCompany' ),
);
}
return;
}
=head2 CustomerUserAdd()
to add new customer users
my $UserLogin = $CustomerUserObject->CustomerUserAdd(
Source => 'CustomerUser', # CustomerUser source config
UserFirstname => 'Huber',
UserLastname => 'Manfred',
UserCustomerID => 'A124',
UserLogin => 'mhuber',
UserPassword => 'some-pass', # not required
UserEmail => 'email@example.com',
ValidID => 1,
UserID => 123,
);
=cut
sub CustomerUserAdd {
my ( $Self, %Param ) = @_;
# check data source
if ( !$Param{Source} ) {
$Param{Source} = 'CustomerUser';
}
# check if user exists
if ( $Param{UserLogin} ) {
my %User = $Self->CustomerUserDataGet( User => $Param{UserLogin} );
if (%User) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => $Kernel::OM->Get('Kernel::Language')->Translate( 'Customer user "%s" already exists.', $Param{UserLogin} ),
);
return;
}
}
# store customer user data
my $Result = $Self->{ $Param{Source} }->CustomerUserAdd(%Param);
return if !$Result;
# trigger event
$Self->EventHandler(
Event => 'CustomerUserAdd',
Data => {
UserLogin => $Param{UserLogin},
NewData => \%Param,
},
UserID => $Param{UserID},
);
return $Result;
}
=head2 CustomerUserUpdate()
to update customer users
$CustomerUserObject->CustomerUserUpdate(
Source => 'CustomerUser', # CustomerUser source config
ID => 'mh' # current user login
UserLogin => 'mhuber', # new user login
UserFirstname => 'Huber',
UserLastname => 'Manfred',
UserPassword => 'some-pass', # not required
UserEmail => 'email@example.com',
ValidID => 1,
UserID => 123,
);
=cut
sub CustomerUserUpdate {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{UserLogin} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need UserLogin!"
);
return;
}
# check for UserLogin-renaming and if new UserLogin already exists...
if ( $Param{ID} && ( lc $Param{UserLogin} ne lc $Param{ID} ) ) {
my %User = $Self->CustomerUserDataGet( User => $Param{UserLogin} );
if (%User) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => $Kernel::OM->Get('Kernel::Language')->Translate( 'Customer user "%s" already exists.', $Param{UserLogin} ),
);
return;
}
}
# check if user exists
my %User = $Self->CustomerUserDataGet( User => $Param{ID} || $Param{UserLogin} );
if ( !%User ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "No such user '$Param{UserLogin}'!",
);
return;
}
my $Result = $Self->{ $User{Source} }->CustomerUserUpdate(%Param);
return if !$Result;
# trigger event
$Self->EventHandler(
Event => 'CustomerUserUpdate',
Data => {
UserLogin => $Param{ID} || $Param{UserLogin},
NewData => \%Param,
OldData => \%User,
},
UserID => $Param{UserID},
);
return $Result;
}
=head2 SetPassword()
to set customer users passwords
$CustomerUserObject->SetPassword(
UserLogin => 'some-login',
PW => 'some-new-password'
);
=cut
sub SetPassword {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{UserLogin} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'User UserLogin!'
);
return;
}
# check if user exists
my %User = $Self->CustomerUserDataGet( User => $Param{UserLogin} );
if ( !%User ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "No such user '$Param{UserLogin}'!",
);
return;
}
return $Self->{ $User{Source} }->SetPassword(%Param);
}
=head2 GenerateRandomPassword()
generate a random password
my $Password = $CustomerUserObject->GenerateRandomPassword();
or
my $Password = $CustomerUserObject->GenerateRandomPassword(
Size => 16,
);
=cut
sub GenerateRandomPassword {
my ( $Self, %Param ) = @_;
return $Self->{CustomerUser}->GenerateRandomPassword(%Param);
}
=head2 SetPreferences()
set customer user preferences
$CustomerUserObject->SetPreferences(
Key => 'UserComment',
Value => 'some comment',
UserID => 'some-login',
);
=cut
sub SetPreferences {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need UserID!'
);
return;
}
# Don't allow overwriting of native user data.
my %Blacklisted = (
UserID => 1,
UserLogin => 1,
UserPassword => 1,
UserFirstname => 1,
UserLastname => 1,
UserFullname => 1,
UserStreet => 1,
UserCity => 1,
UserZip => 1,
UserCountry => 1,
UserComment => 1,
UserCustomerID => 1,
UserTitle => 1,
UserEmail => 1,
ChangeTime => 1,
ChangeBy => 1,
CreateTime => 1,
CreateBy => 1,
UserPhone => 1,
UserMobile => 1,
UserFax => 1,
UserMailString => 1,
ValidID => 1,
);
return 0 if $Blacklisted{ $Param{Key} };
# check if user exists
my %User = $Self->CustomerUserDataGet( User => $Param{UserID} );
if ( !%User ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "No such user '$Param{UserID}'!",
);
return;
}
# call new api (2.4.8 and higher)
if ( $Self->{ $User{Source} }->can('SetPreferences') ) {
return $Self->{ $User{Source} }->SetPreferences(%Param);
}
# call old api
return $Self->{PreferencesObject}->SetPreferences(%Param);
}
=head2 GetPreferences()
get customer user preferences
my %Preferences = $CustomerUserObject->GetPreferences(
UserID => 'some-login',
);
=cut
sub GetPreferences {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need UserID!'
);
return;
}
# check if user exists
my %User = $Self->CustomerUserDataGet( User => $Param{UserID} );
if ( !%User ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "No such user '$Param{UserID}'!",
);
return;
}
# call new api (2.4.8 and higher)
if ( $Self->{ $User{Source} }->can('GetPreferences') ) {
return $Self->{ $User{Source} }->GetPreferences(%Param);
}
# call old api
return $Self->{PreferencesObject}->GetPreferences(%Param);
}
=head2 SearchPreferences()
search in user preferences
my %UserList = $CustomerUserObject->SearchPreferences(
Key => 'UserSomeKey',
Value => 'SomeValue', # optional, limit to a certain value/pattern
);
=cut
sub SearchPreferences {
my ( $Self, %Param ) = @_;
my %Data;
SOURCE:
for my $Count ( '', 1 .. 10 ) {
next SOURCE if !$Self->{"CustomerUser$Count"};
# get customer search result of backend and merge it
# call new api (2.4.8 and higher)
my %SubData;
if ( $Self->{"CustomerUser$Count"}->can('SearchPreferences') ) {
%SubData = $Self->{"CustomerUser$Count"}->SearchPreferences(%Param);
}
# call old api
else {
%SubData = $Self->{PreferencesObject}->SearchPreferences(%Param);
}
%Data = ( %SubData, %Data );
}
return %Data;
}
=head2 TokenGenerate()
generate a random token
my $Token = $CustomerUserObject->TokenGenerate(
UserID => 123,
);
=cut
sub TokenGenerate {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need UserID!"
);
return;
}
my $Token = $Kernel::OM->Get('Kernel::System::Main')->GenerateRandomString(
Length => 14,
);
# save token in preferences
$Self->SetPreferences(
Key => 'UserToken',
Value => $Token,
UserID => $Param{UserID},
);
return $Token;
}
=head2 TokenCheck()
check password token
my $Valid = $CustomerUserObject>TokenCheck(
Token => $Token,
UserID => 123,
);
=cut
sub TokenCheck {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{Token} || !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need Token and UserID!"
);
return;
}
# get preferences token
my %Preferences = $Self->GetPreferences(
UserID => $Param{UserID},
);
# check requested vs. stored token
return if !$Preferences{UserToken};
return if $Preferences{UserToken} ne $Param{Token};
# reset password token
$Self->SetPreferences(
Key => 'UserToken',
Value => '',
UserID => $Param{UserID},
);
return 1;
}
=head2 CustomerUserCacheClear()
clear cache of customer user data
$CustomerUserObject->CustomerUserCacheClear(
UserLogin => 'mhuber',
);
=cut
sub CustomerUserCacheClear {
my ( $Self, %Param ) = @_;
SOURCE:
for my $Count ( '', 1 .. 10 ) {
next SOURCE if !$Self->{"CustomerUser$Count"};
$Self->{"CustomerUser$Count"}->_CustomerUserCacheClear(
UserLogin => $Param{UserLogin},
);
}
return 1;
}
=head2 CustomerUserCustomerMemberAdd()
to add a customer user to a customer
my $Success = $CustomerUserObject->CustomerUserCustomerMemberAdd(
CustomerUserID => 123,
CustomerID => 123,
Active => 1, # optional
UserID => 123,
);
=cut
sub CustomerUserCustomerMemberAdd {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Argument (qw(CustomerUserID CustomerID UserID)) {
if ( !$Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!",
);
return;
}
}
# delete affected caches
my $CacheKey = 'Cache::CustomerUserCustomerMemberList::';
$Kernel::OM->Get('Kernel::System::Cache')->Delete(
Type => $Self->{CacheType},
Key => $CacheKey . 'CustomerUserID::' . $Param{CustomerUserID},
);
$Kernel::OM->Get('Kernel::System::Cache')->Delete(
Type => $Self->{CacheType},
Key => $CacheKey . 'CustomerID::' . $Param{CustomerID},
);
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# delete existing relation
return if !$DBObject->Do(
SQL => 'DELETE FROM customer_user_customer
WHERE user_id = ?
AND customer_id = ?',
Bind => [ \$Param{CustomerUserID}, \$Param{CustomerID} ],
);
# return if relation is not active
return 1 if !$Param{Active};
# insert new relation
return if !$DBObject->Do(
SQL => '
INSERT INTO customer_user_customer (user_id, customer_id, create_time, create_by,
change_time, change_by)
VALUES (?, ?, current_timestamp, ?, current_timestamp, ?)',
Bind => [ \$Param{CustomerUserID}, \$Param{CustomerID}, \$Param{UserID}, \$Param{UserID}, ],
);
return 1;
}
=head2 CustomerUserCustomerMemberList()
get related customer IDs of a customer user
my @CustomerIDs = $CustomerUserObject->CustomerUserCustomerMemberList(
CustomerUserID => 123,
);
Returns:
@CustomerIDs = (
'123',
'456',
);
get related customer users of a customer ID
my @CustomerUsers = $CustomerUserObject->CustomerUserCustomerMemberList(
CustomerID => 123,
);
Returns:
@CustomerUsers = (
'123',
'456',
);
=cut
sub CustomerUserCustomerMemberList {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{CustomerUserID} && !$Param{CustomerID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Got no CustomerUserID or CustomerID!',
);
return;
}
# get needed objects
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
my $CacheKey = 'Cache::CustomerUserCustomerMemberList::';
if ( $Param{CustomerUserID} ) {
# check if this result is present (in cache)
$CacheKey .= 'CustomerUserID::' . $Param{CustomerUserID};
my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
Type => $Self->{CacheType},
Key => $CacheKey,
);
return @{$Cache} if $Cache;
# get customer ids
return if !$DBObject->Prepare(
SQL =>
'SELECT customer_id
FROM customer_user_customer
WHERE user_id = ?
ORDER BY customer_id',
Bind => [ \$Param{CustomerUserID}, ],
);
# fetch the result
my @CustomerIDs;
while ( my @Row = $DBObject->FetchrowArray() ) {
push @CustomerIDs, $Row[0];
}
# cache the result
$Kernel::OM->Get('Kernel::System::Cache')->Set(
Type => $Self->{CacheType},
TTL => $Self->{CacheTTL},
Key => $CacheKey,
Value => \@CustomerIDs,
);
return @CustomerIDs;
}
else {
# check if this result is present (in cache)
$CacheKey .= 'CustomerID::' . $Param{CustomerID};
my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
Type => $Self->{CacheType},
Key => $CacheKey,
);
return @{$Cache} if $Cache;
# get customer users
return if !$DBObject->Prepare(
SQL =>
'SELECT user_id
FROM customer_user_customer WHERE
customer_id = ?
ORDER BY user_id',
Bind => [ \$Param{CustomerID}, ],
);
# fetch the result
my @CustomerUserIDs;
while ( my @Row = $DBObject->FetchrowArray() ) {
push @CustomerUserIDs, $Row[0];
}
# cache the result
$Kernel::OM->Get('Kernel::System::Cache')->Set(
Type => $Self->{CacheType},
TTL => $Self->{CacheTTL},
Key => $CacheKey,
Value => \@CustomerUserIDs,
);
return @CustomerUserIDs;
}
}
=head2 DeleteOnePreference()
get customer user preferences
my %Preferences = $CustomerUserObject->DeleteOnePreference(
UserID => 'some-login',
Key => 'PreferenceKey',
);
=cut
sub DeleteOnePreference {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need UserID!'
);
return;
}
# check if user exists
my %User = $Self->CustomerUserDataGet( User => $Param{UserID} );
if ( !%User ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "No such user '$Param{UserID}'!",
);
return;
}
# call new api (2.4.8 and higher)
if ( $Self->{ $User{Source} }->can('DeleteOnePreference') ) {
return $Self->{ $User{Source} }->DeleteOnePreference(%Param);
}
# call old api
return $Self->{PreferencesObject}->DeleteOnePreference(%Param);
}
sub DESTROY {
my $Self = shift;
# execute all transaction events
$Self->EventHandlerTransaction();
return 1;
}
1;