Files
scripts/Perl OTRS/Kernel/System/ITSMChange/History.pm
2024-10-14 00:08:40 +02:00

1210 lines
39 KiB
Perl

# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::ITSMChange::History;
use strict;
use warnings;
use Kernel::Language qw(Translatable);
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Cache',
'Kernel::System::DB',
'Kernel::System::Log',
'Kernel::System::User',
);
=head1 NAME
Kernel::System::ITSMChange::History - all change and workorder history functions
=head1 PUBLIC INTERFACE
=head2 new()
create an object
use Kernel::System::ObjectManager;
local $Kernel::OM = Kernel::System::ObjectManager->new();
my $HistoryObject = $Kernel::OM->Get('Kernel::System::ITSMChange::History');
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# get the cache type and TTL (in seconds)
$Self->{CacheType} = 'ITSMChangeManagement';
$Self->{CacheTTL} = $Kernel::OM->Get('Kernel::Config')->Get('ITSMChange::CacheTTL') * 60;
# set default debug flag
$Self->{Debug} ||= 0;
return $Self;
}
=head2 HistoryAdd()
Adds a single history entry to the history. Returns 1 on success, C<undef> otherwise.
my $Success = $HistoryObject->HistoryAdd(
ChangeID => 1234, # either ChangeID or WorkOrderID is needed
WorkOrderID => 123, # either ChangeID or WorkOrderID is needed
HistoryType => 'WorkOrderAdd', # either HistoryType or HistoryTypeID is needed
HistoryTypeID => 1, # either HistoryType or HistoryTypeID is needed
Fieldname => 'Justification', # optional
ContentNew => 'Any useful information', # optional
ContentOld => 'Old value of field', # optional
UserID => 1,
);
=cut
sub HistoryAdd {
my ( $Self, %Param ) = @_;
# check needed stuff
# ChangeID is always needed, workorder id is only needed for workorder events
for my $Needed (qw(UserID ChangeID)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
# either HistoryType or HistoryTypeID is needed
if ( !( $Param{HistoryType} || $Param{HistoryTypeID} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need HistoryType or HistoryTypeID!',
);
return;
}
# get history type id from history type if history type is given
if ( $Param{HistoryType} ) {
# lookup the history type id
my $ID = $Self->HistoryTypeLookup(
HistoryType => $Param{HistoryType},
);
# no valid history type given
if ( !$ID ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Invalid history type '$Param{HistoryType}' given!",
);
return;
}
$Param{HistoryTypeID} = $ID;
}
# convert array params
CONTENT:
for my $Content (qw(ContentNew ContentOld)) {
next CONTENT if ref $Param{$Content} ne 'ARRAY';
$Param{$Content} = join ', ', @{ $Param{$Content} };
}
# insert history entry
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => 'INSERT INTO change_history ( change_id, workorder_id, content_new, '
. 'content_old, create_by, create_time, type_id, fieldname ) '
. 'VALUES ( ?, ?, ?, ?, ?, current_timestamp, ?, ? )',
Bind => [
\$Param{ChangeID},
\$Param{WorkOrderID},
\$Param{ContentNew},
\$Param{ContentOld},
\$Param{UserID},
\$Param{HistoryTypeID},
\$Param{Fieldname},
],
);
return 1;
}
=head2 HistoryAddMultiple()
Adds multiple history entries to the history. Returns 1 on success, C<undef> otherwise.
my $Success = $HistoryObject->HistoryAddMultiple(
Data => \@HistoryAddData,
);
=cut
sub HistoryAddMultiple {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{Data} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need Data!",
);
return;
}
# check needed stuff
if ( ref $Param{Data} ne 'ARRAY' ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Data must be an array reference!",
);
return;
}
# check needed stuff
if ( !@{ $Param{Data} } ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Data array must not be empty!",
);
return;
}
# define database specific SQL for the multiline inserts
my %DatabaseSQL;
# mysql
$DatabaseSQL{mysql} = {
Start => 'INSERT INTO change_history ( change_id, workorder_id, content_new, '
. 'content_old, create_by, create_time, type_id, fieldname ) ',
FirstLine => 'VALUES ( ?, ?, ?, ?, ?, current_timestamp, ?, ? )',
NextLine => ', ( ?, ?, ?, ?, ?, current_timestamp, ?, ? ) ',
End => '',
};
# postgres 8.2 and newer (same SQL code as for mysql)
$DatabaseSQL{postgres} = $DatabaseSQL{mysql};
# oracle
$DatabaseSQL{oracle} = {
Start => 'INSERT ALL ',
FirstLine => 'INTO change_history ( change_id, workorder_id, content_new, '
. 'content_old, create_by, create_time, type_id, fieldname ) '
. 'VALUES ( ?, ?, ?, ?, ?, current_timestamp, ?, ? ) ',
NextLine => 'INTO change_history ( change_id, workorder_id, content_new, '
. 'content_old, create_by, create_time, type_id, fieldname ) '
. 'VALUES ( ?, ?, ?, ?, ?, current_timestamp, ?, ? ) ',
End => 'SELECT * FROM DUAL',
};
# get the database type
my $DBType = $Kernel::OM->Get('Kernel::System::DB')->GetDatabaseFunction('Type');
# make multiline inserts for defined databases
if ( $DatabaseSQL{$DBType} ) {
my $SQL = '';
my @Bind;
HISTORYENTRY:
for my $HistoryEntry ( @{ $Param{Data} } ) {
# check needed stuff
# ChangeID is always needed, workorder id is only needed for workorder events
for my $Needed (qw(UserID ChangeID)) {
if ( !$HistoryEntry->{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
next HISTORYENTRY;
}
}
# either HistoryType or HistoryTypeID is needed
if ( !( $HistoryEntry->{HistoryType} || $HistoryEntry->{HistoryTypeID} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need HistoryType or HistoryTypeID!',
);
next HISTORYENTRY;
}
# get history type id from history type if history type is given
if ( $HistoryEntry->{HistoryType} ) {
# lookup the history type id
my $ID = $Self->HistoryTypeLookup(
HistoryType => $HistoryEntry->{HistoryType},
);
# no valid history type given
if ( !$ID ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Invalid history type '$HistoryEntry->{HistoryType}' given!",
);
next HISTORYENTRY;
}
$HistoryEntry->{HistoryTypeID} = $ID;
}
# now the history entry is validated and can be added to sql
if ( !$SQL ) {
$SQL = $DatabaseSQL{$DBType}->{Start} . $DatabaseSQL{$DBType}->{FirstLine};
}
else {
$SQL .= $DatabaseSQL{$DBType}->{NextLine};
}
# convert array params
CONTENT:
for my $Content (qw(ContentNew ContentOld)) {
next CONTENT if ref $HistoryEntry->{$Content} ne 'ARRAY';
$HistoryEntry->{$Content} = join ', ', @{ $HistoryEntry->{$Content} };
}
push @Bind, (
\$HistoryEntry->{ChangeID},
\$HistoryEntry->{WorkOrderID},
\$HistoryEntry->{ContentNew},
\$HistoryEntry->{ContentOld},
\$HistoryEntry->{UserID},
\$HistoryEntry->{HistoryTypeID},
\$HistoryEntry->{Fieldname},
);
# check the length of the SQL string
# (some databases only accept SQL strings up to 4k,
# so we want to stay safe here with just 3500 bytes)
if ( length $SQL > 3500 ) {
# add the end line to sql string
$SQL .= $DatabaseSQL{$DBType}->{End};
# insert multiple history entries
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => $SQL,
Bind => \@Bind,
);
# reset the SQL string and the Bind array
$SQL = '';
@Bind = ();
}
}
# if there is some SQL left to execute
if ($SQL) {
# add the end line to sql string
$SQL .= $DatabaseSQL{$DBType}->{End};
# insert multiple history entries
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => $SQL,
Bind => \@Bind,
);
}
}
# database supports no multiline inserts, so we do it line by line
else {
my $Error;
for my $HistoryEntry ( @{ $Param{Data} } ) {
# add history entries one by one with the HistoryAdd function
my $Success = $Self->HistoryAdd(
%{$HistoryEntry},
);
# check error
if ( !$Success ) {
$Error = 1;
}
}
return if $Error;
}
return 1;
}
=head2 WorkOrderHistoryGet()
Returns a list of all history entries that belong to the given WorkOrderID. The
list contains hash references with these information:
$Info{HistoryEntryID}
$Info{ChangeID}
$Info{WorkOrderID}
$Info{HistoryType}
$Info{HistoryTypeID}
$Info{Fieldname}
$Info{ContentNew}
$Info{ContentOld}
$Info{CreateBy}
$Info{CreateTime}
$Info{UserID}
$Info{UserLogin}
$Info{UserLastname}
$Info{UserFirstname}
my $HistoryEntries = $HistoryObject->WorkOrderHistoryGet(
WorkOrderID => 123,
UserID => 1,
);
=cut
sub WorkOrderHistoryGet {
my ( $Self, %Param ) = @_;
# check for needed stuff
for my $Attribute (qw(WorkOrderID UserID)) {
if ( !$Param{$Attribute} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Attribute!",
);
return;
}
}
# run the sql statement to get history
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT ch.id, change_id, workorder_id, content_new, content_old, '
. 'ch.create_by, ch.create_time, type_id, cht.name, fieldname '
. 'FROM change_history ch, change_history_type cht '
. 'WHERE ch.type_id = cht.id '
. 'AND workorder_id = ? ORDER BY ch.create_time, ch.id',
Bind => [ \$Param{WorkOrderID} ],
);
# fetch the entries and save information in array
my @HistoryEntries;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
my %HistoryEntry = (
HistoryEntryID => $Row[0],
ChangeID => $Row[1],
WorkOrderID => $Row[2],
ContentNew => defined( $Row[3] ) ? $Row[3] : '',
ContentOld => defined( $Row[4] ) ? $Row[4] : '',
CreateBy => $Row[5],
CreateTime => $Row[6],
HistoryTypeID => $Row[7],
HistoryType => $Row[8],
Fieldname => $Row[9],
);
push @HistoryEntries, \%HistoryEntry;
}
# get more information about user who created entry
for my $HistoryEntry (@HistoryEntries) {
# get user name
my %User = $Kernel::OM->Get('Kernel::System::User')->GetUserData(
UserID => $HistoryEntry->{CreateBy},
Cache => 1,
);
# save user info in history entry
if (%User) {
$HistoryEntry->{UserID} = $User{UserID};
$HistoryEntry->{UserLogin} = $User{UserLogin};
$HistoryEntry->{UserFirstname} = $User{UserFirstname};
$HistoryEntry->{UserLastname} = $User{UserLastname};
$HistoryEntry->{UserFullname} = $User{UserFullname};
}
}
return \@HistoryEntries;
}
=head2 ChangeHistoryGet()
Returns a list of all history entries that belong to the given ChangeID - including
history entries for C<workorders>. The list contains hash references with these information:
$Info{HistoryEntryID}
$Info{ChangeID}
$Info{WorkOrderID}
$Info{HistoryType}
$Info{HistoryTypeID}
$Info{Fieldname}
$Info{ContentNew}
$Info{ContentOld}
$Info{CreateBy}
$Info{CreateTime}
$Info{UserID}
$Info{UserLogin}
$Info{UserLastname}
$Info{UserFirstname}
my $HistoryEntries = $HistoryObject->ChangeHistoryGet(
ChangeID => 123,
UserID => 1,
);
=cut
sub ChangeHistoryGet {
my ( $Self, %Param ) = @_;
# check for needed stuff
for my $Attribute (qw(ChangeID UserID)) {
if ( !$Param{$Attribute} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Attribute!",
);
return;
}
}
# run the sql statement to get history
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT ch.id, change_id, workorder_id, content_new, content_old, '
. 'ch.create_by, ch.create_time, type_id, cht.name, fieldname '
. 'FROM change_history ch, change_history_type cht '
. 'WHERE ch.type_id = cht.id '
. 'AND change_id = ? ORDER BY ch.create_time, ch.id',
Bind => [ \$Param{ChangeID} ],
);
# fetch the entries and save information in array
my @HistoryEntries;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
my %HistoryEntry = (
HistoryEntryID => $Row[0],
ChangeID => $Row[1],
WorkOrderID => $Row[2],
ContentNew => defined( $Row[3] ) ? $Row[3] : '',
ContentOld => defined( $Row[4] ) ? $Row[4] : '',
CreateBy => $Row[5],
CreateTime => $Row[6],
HistoryTypeID => $Row[7],
HistoryType => $Row[8],
Fieldname => $Row[9],
);
# cleanup time stamps (some databases are using e. g. 2008-02-25 22:03:00.000000)
$HistoryEntry{CreateTime}
=~ s{ \A ( \d\d\d\d - \d\d - \d\d \s \d\d:\d\d:\d\d ) \. .+? \z }{$1}xms;
push @HistoryEntries, \%HistoryEntry;
}
# get more information about user who created entry
for my $HistoryEntry (@HistoryEntries) {
# get user name
my %User = $Kernel::OM->Get('Kernel::System::User')->GetUserData(
UserID => $HistoryEntry->{CreateBy},
Cache => 1,
);
# save user info in history entry
if (%User) {
$HistoryEntry->{UserID} = $User{UserID};
$HistoryEntry->{UserLogin} = $User{UserLogin};
$HistoryEntry->{UserFirstname} = $User{UserFirstname};
$HistoryEntry->{UserLastname} = $User{UserLastname};
$HistoryEntry->{UserFullname} = $User{UserFullname};
}
}
return \@HistoryEntries;
}
=head2 HistoryEntryGet()
Returns a single history entry. The hash reference has these information:
$Info{HistoryEntryID}
$Info{ChangeID}
$Info{WorkOrderID}
$Info{HistoryType}
$Info{HistoryTypeID}
$Info{Fieldname}
$Info{ContentNew}
$Info{ContentOld}
$Info{CreateBy}
$Info{CreateTime}
$Info{UserID}
$Info{UserLogin}
$Info{UserLastname}
$Info{UserFirstname}
$Info{UserFullname}
my $HistoryEntry = $HistoryObject->HistoryEntryGet(
HistoryEntryID => 123,
UserID => 1,
);
=cut
sub HistoryEntryGet {
my ( $Self, %Param ) = @_;
# check for needed stuff
for my $Attribute (qw(HistoryEntryID UserID)) {
if ( !$Param{$Attribute} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Attribute!",
);
return;
}
}
# run the sql statement to get history
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT ch.id, change_id, workorder_id, content_new, content_old, '
. 'ch.create_by, ch.create_time, type_id, cht.name, fieldname '
. 'FROM change_history ch, change_history_type cht '
. 'WHERE ch.type_id = cht.id '
. 'AND ch.id = ?',
Bind => [ \$Param{HistoryEntryID} ],
);
# fetch the entries and save information in array
my %HistoryEntry;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
%HistoryEntry = (
HistoryEntryID => $Row[0],
ChangeID => $Row[1],
WorkOrderID => $Row[2],
ContentNew => $Row[3],
ContentOld => $Row[4],
CreateBy => $Row[5],
CreateTime => $Row[6],
HistoryTypeID => $Row[7],
HistoryType => $Row[8],
Fieldname => $Row[9],
);
# cleanup time stamps (some databases are using e. g. 2008-02-25 22:03:00.000000)
$HistoryEntry{CreateTime}
=~ s{ \A ( \d\d\d\d - \d\d - \d\d \s \d\d:\d\d:\d\d ) \. .+? \z }{$1}xms;
}
# get user name
my %User = $Kernel::OM->Get('Kernel::System::User')->GetUserData(
UserID => $HistoryEntry{CreateBy},
Cache => 1,
);
# save user info in history entry
if (%User) {
$HistoryEntry{UserID} = $User{UserID};
$HistoryEntry{UserLogin} = $User{UserLogin};
$HistoryEntry{UserFirstname} = $User{UserFirstname};
$HistoryEntry{UserLastname} = $User{UserLastname};
$HistoryEntry{UserFullname} = $User{UserFullname};
}
return \%HistoryEntry;
}
=head2 WorkOrderHistoryDelete()
Deletes all entries in history table that belongs to the given WorkOrderID.
The method returns 1 on success and C<undef> otherwise.
my $Success = $HistoryObject->WorkOrderHistoryDelete(
WorkOrderID => 123,
UserID => 1,
);
=cut
sub WorkOrderHistoryDelete {
my ( $Self, %Param ) = @_;
# check for needed stuff
for my $Attribute (qw(WorkOrderID UserID)) {
if ( !$Param{$Attribute} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Attribute!",
);
return;
}
}
# delete entries for the given workorder
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => 'DELETE FROM change_history WHERE workorder_id = ?',
Bind => [ \$Param{WorkOrderID} ],
);
# success
return 1;
}
=head2 ChangeHistoryDelete()
Deletes all entries in history table that belongs to the given ChangeID.
The method returns 1 on success and C<undef> otherwise.
my $Success = $HistoryObject->ChangeHistoryDelete(
ChangeID => 123,
UserID => 1,
);
=cut
sub ChangeHistoryDelete {
my ( $Self, %Param ) = @_;
# check for needed stuff
for my $Attribute (qw(ChangeID UserID)) {
if ( !$Param{$Attribute} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Attribute!",
);
return;
}
}
# delete entries for the given change
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => 'DELETE FROM change_history WHERE change_id = ?',
Bind => [ \$Param{ChangeID} ],
);
# success
return 1;
}
=head2 HistoryUpdate()
This method updates a history entry. It returns 1 on success, and C<undef> otherwise.
my $Success = $HistoryObject->HistoryUpdate(
HistoryEntryID => 123,
WorkOrderID => 3451, # optional
ChangeID => 1235, # optional
ContentNew => 'new value', # optional
ContentOld => 'old value', # optional
UserID => 1,
);
=cut
sub HistoryUpdate {
my ( $Self, %Param ) = @_;
# check for needed stuff
for my $Attribute (qw(HistoryEntryID UserID)) {
if ( !$Param{$Attribute} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Attribute!",
);
return;
}
}
# get history entry id and user id
my $HistoryEntryID = delete $Param{HistoryEntryID};
my $UserID = delete $Param{UserID};
# we have to update at least one column
if ( !keys %Param ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need at least one column to update!',
);
return;
}
# what columns should be updated
my @Columns = keys %Param;
my @Bind;
# get bind variables
for my $Column (@Columns) {
push @Bind, \$Param{$Column};
}
# get bind string
my %ParamKey2ColumnName = (
ChangeID => 'change_id',
WorkOrderID => 'workorder_id',
ContentNew => 'content_new',
ContentOld => 'content_old',
HistoryTypeID => 'type_id',
Fieldname => 'fieldname',
);
my $Binds = join ', ', map { $ParamKey2ColumnName{$_} . ' = ? ' } @Columns;
# do the update
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => 'UPDATE change_history SET ' . $Binds . ' '
. 'WHERE id = ?',
Bind => [ @Bind, \$HistoryEntryID ],
);
return 1;
}
=head2 HistorySearch()
Searches for changes/C<workorders> that matches the given search criteria. It returns
an array reference with change or C<workorder> IDs. If "Result => 'COUNT'" then the
number of found changes is returned.
my $IDs = $HistoryObject->HistorySearch(
Type => 'Change', # Change|Workorder which IDs should be returned
Attribute => 'ChangeState',
OldValues => [ 'rejected' ], # optional - OldValues OR NewValues is needed
NewValues => [ 'approved' ], # optional - OldValues OR NewValues is needed
ChangeBy => [ 1, 2, 3 ], # optional
ChangeDateNewerDate => '2009-01-13 00:00:01', #optional
ChangeDateOlderDate => '2009-01-13 00:00:01', #optional
UserID => 1,
UsingWildcards => 0, # 0|1 - default 1
Result => 'ARRAY', # ARRAY|COUNT - default ARRAY
);
=cut
sub HistorySearch {
my ( $Self, %Param ) = @_;
# check for needed stuff
for my $Attribute (qw(Type Attribute UserID)) {
if ( !$Param{$Attribute} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Attribute!",
);
return;
}
}
# verify that all passed array parameters contain an arrayref
ARGUMENT:
for my $Argument (
qw(
OldValues
NewValues
ChangeBy
)
)
{
if ( !defined $Param{$Argument} ) {
$Param{$Argument} ||= [];
next ARGUMENT;
}
if ( ref $Param{$Argument} ne 'ARRAY' ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "$Argument must be an array reference!",
);
return;
}
}
# set default values
if ( !defined $Param{UsingWildcards} ) {
$Param{UsingWildcards} = 1;
}
# set the default behaviour for the return type
my $Result = $Param{Result} || 'ARRAY';
my @SQLWhere; # assemble the conditions used in the WHERE clause
# add string params to the WHERE clause
my %StringParams = (
# strings in change_history
Attribute => 'ch.fieldname',
);
# add string params to sql-where-array
STRINGPARAM:
for my $StringParam ( sort keys %StringParams ) {
# check string params for useful values, the string '0' is allowed
next STRINGPARAM if !exists $Param{$StringParam};
next STRINGPARAM if !defined $Param{$StringParam};
next STRINGPARAM if $Param{$StringParam} eq '';
# quote
$Param{$StringParam} = $Kernel::OM->Get('Kernel::System::DB')->Quote( $Param{$StringParam} );
# wildcards are used
if ( $Param{UsingWildcards} ) {
# get like escape string needed for some databases (e.g. oracle)
my $LikeEscapeString = $Kernel::OM->Get('Kernel::System::DB')->GetDatabaseFunction('LikeEscapeString');
# Quote
$Param{$StringParam} = $Kernel::OM->Get('Kernel::System::DB')->Quote( $Param{$StringParam}, 'Like' );
# replace * with %
$Param{$StringParam} =~ s{ \*+ }{%}xmsg;
# do not use string params which contain only %
next STRINGPARAM if $Param{$StringParam} =~ m{ \A %* \z }xms;
push @SQLWhere,
"LOWER($StringParams{$StringParam}) LIKE LOWER('$Param{$StringParam}') $LikeEscapeString";
}
# no wildcards are used
else {
push @SQLWhere,
"LOWER($StringParams{$StringParam}) = LOWER('$Param{$StringParam}')";
}
}
# set array params
my %ArrayParams = (
OldValues => 'ch.content_old',
NewValues => 'ch.content_new',
ChangeBy => 'ch.create_by',
);
# add array params to sql-where-array
ARRAYPARAM:
for my $ArrayParam ( sort keys %ArrayParams ) {
# ignore empty lists
next ARRAYPARAM if !@{ $Param{$ArrayParam} };
# quote
for my $OneParam ( @{ $Param{$ArrayParam} } ) {
$OneParam = $Kernel::OM->Get('Kernel::System::DB')->Quote($OneParam);
# for strings we need single quotes
if ( $OneParam !~ m{ \A [+-]? \d+ (?:\.\d+)? \z }xms ) {
$OneParam = "'$OneParam'";
}
}
# create string
my $InString = join ', ', @{ $Param{$ArrayParam} };
push @SQLWhere, "$ArrayParams{$ArrayParam} IN ($InString)";
}
# set time params
my %TimeParams = (
# times in change_history
ChangeTimeNewerDate => 'ch.create_time >=',
ChangeTimeOlderDate => 'ch.create_time <=',
);
# check and add time params to WHERE
TIMEPARAM:
for my $TimeParam ( sort keys %TimeParams ) {
next TIMEPARAM if !$Param{$TimeParam};
if ( $Param{$TimeParam} !~ m{ \A \d\d\d\d-\d\d-\d\d \s \d\d:\d\d:\d\d \z }xms ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "The parameter $TimeParam has an invalid date format!",
);
return;
}
$Param{$TimeParam} = $Kernel::OM->Get('Kernel::System::DB')->Quote( $Param{$TimeParam} );
# the time attributes of change_history show up in the WHERE clause
push @SQLWhere, "$TimeParams{$TimeParam} '$Param{$TimeParam}'";
}
# create SQL statement
my $Type = lc $Param{Type};
my $SQL = "SELECT DISTINCT ch.${Type}_id FROM change_history ch ";
if ( $Result eq 'COUNT' ) {
$SQL = "SELECT COUNT( DISTINCT( ch.${Type}_id ) ) FROM change_history ch ";
}
# add the WHERE clause
if (@SQLWhere) {
$SQL .= 'WHERE ';
$SQL .= join ' AND ', map {"( $_ )"} @SQLWhere;
$SQL .= ' ';
}
# ignore the parameter 'Limit' when result type is 'COUNT'
if ( $Result eq 'COUNT' ) {
delete $Param{Limit};
}
# ask database
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => $SQL,
Limit => $Param{Limit},
);
# fetch the result
my @IDs;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
push @IDs, $Row[0];
}
if ( $Result eq 'COUNT' ) {
# return the COUNT(c.id) attribute
return $IDs[0];
}
else {
return \@IDs;
}
}
=head2 HistoryTypeLookup()
This method does a lookup for a history type. If a history type id is given,
it returns the name of the history type. If a history type is given, the appropriate
id is returned.
my $Name = $HistoryObject->HistoryTypeLookup(
HistoryTypeID => 1234,
);
my $ID = $HistoryObject->HistoryTypeLookup(
HistoryType => 'WorkOrderAdd',
);
=cut
sub HistoryTypeLookup {
my ( $Self, %Param ) = @_;
# check for needed stuff
if ( !$Param{HistoryTypeID} && !$Param{HistoryType} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need HistoryTypeID or HistoryType!',
);
return;
}
# if both valid keys are given, return
if ( $Param{HistoryTypeID} && $Param{HistoryType} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need either HistoryTypeID OR HistoryType - not both!',
);
return;
}
# find out what the used key is
my $Key = 'HistoryType';
if ( $Param{HistoryTypeID} ) {
$Key = 'HistoryTypeID';
}
# check the cache
my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
Type => $Self->{CacheType},
Key => 'HistoryTypeLookup::' . $Key . '::' . $Param{$Key},
);
# if result is cached return that result
return $Cache if $Cache;
# set the appropriate SQL statement
my $SQL = 'SELECT name FROM change_history_type WHERE id = ?';
if ( $Key eq 'HistoryType' ) {
$SQL = 'SELECT id FROM change_history_type WHERE name = ?';
}
# fetch the requested value
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => $SQL,
Bind => [ \$Param{$Key} ],
Limit => 1,
);
my $Value;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
$Value = $Row[0];
}
# save value in cache
$Kernel::OM->Get('Kernel::System::Cache')->Set(
Type => $Self->{CacheType},
Key => 'HistoryTypeLookup::' . $Key . '::' . $Param{$Key},
Value => $Value,
TTL => $Self->{CacheTTL},
);
return $Value;
}
=head2 HistoryTypeList()
returns an array reference with hash references. The key of the hash reference is the id
of an recipient and the name is the value.
=cut
sub HistoryTypeList {
my ( $Self, %Param ) = @_;
# check the cache
my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
Type => $Self->{CacheType},
Key => 'HistoryTypeList',
);
# if result is cached return that result
return $Cache if $Cache;
# prepare db query
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT id, name FROM change_history_type ORDER BY name',
);
# retrieve data
my @HistoryTypes;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
my $Entry = {
Key => $Row[0],
Value => $Row[1],
};
push @HistoryTypes, $Entry;
}
# save values in cache
$Kernel::OM->Get('Kernel::System::Cache')->Set(
Type => $Self->{CacheType},
Key => 'HistoryTypeList',
Value => \@HistoryTypes,
TTL => $Self->{CacheTTL},
);
return \@HistoryTypes;
}
=head2 HistoryStringsList()
Returns a hash of all translatable history strings.
=cut
sub HistoryStringsList {
my ( $Self, %Param ) = @_;
# Define translatable history strings.
my %HistoryStrings = (
'ChangeHistory::ActionAdd' => Translatable('%s: %s'),
'ChangeHistory::ActionAddID' => Translatable('New Action (ID=%s)'),
'ChangeHistory::ActionDelete' => Translatable('Action (ID=%s) deleted'),
'ChangeHistory::ActionDeleteAll' => Translatable('All Actions of Condition (ID=%s) deleted'),
'ChangeHistory::ActionExecute' => Translatable('Action (ID=%s) executed: %s'),
'ChangeHistory::ActionUpdate' => Translatable('%s (Action ID=%s): (new=%s, old=%s)'),
'ChangeHistory::ChangeActualEndTimeReached' => Translatable('Change (ID=%s) reached actual end time.'),
'ChangeHistory::ChangeActualStartTimeReached' => Translatable('Change (ID=%s) reached actual start time.'),
'ChangeHistory::ChangeAdd' => Translatable('New Change (ID=%s)'),
'ChangeHistory::ChangeAttachmentAdd' => Translatable('New Attachment: %s'),
'ChangeHistory::ChangeAttachmentDelete' => Translatable('Deleted Attachment %s'),
'ChangeHistory::ChangeCABDelete' => Translatable('CAB Deleted %s'),
'ChangeHistory::ChangeCABUpdate' => Translatable('%s: (new=%s, old=%s)'),
'ChangeHistory::ChangeLinkAdd' => Translatable('Link to %s (ID=%s) added'),
'ChangeHistory::ChangeLinkDelete' => Translatable('Link to %s (ID=%s) deleted'),
'ChangeHistory::ChangeNotificationSent' => Translatable('Notification sent to %s (Event: %s)'),
'ChangeHistory::ChangePlannedEndTimeReached' => Translatable('Change (ID=%s) reached planned end time.'),
'ChangeHistory::ChangePlannedStartTimeReached' => Translatable('Change (ID=%s) reached planned start time.'),
'ChangeHistory::ChangeRequestedTimeReached' => Translatable('Change (ID=%s) reached requested time.'),
'ChangeHistory::ChangeUpdate' => Translatable('%s: (new=%s, old=%s)'),
'ChangeHistory::ConditionAdd' => Translatable('%s: %s'),
'ChangeHistory::ConditionAddID' => Translatable('New Condition (ID=%s)'),
'ChangeHistory::ConditionDelete' => Translatable('Condition (ID=%s) deleted'),
'ChangeHistory::ConditionDeleteAll' => Translatable('All Conditions of Change (ID=%s) deleted'),
'ChangeHistory::ConditionUpdate' => Translatable('%s (Condition ID=%s): (new=%s, old=%s)'),
'ChangeHistory::ExpressionAdd' => Translatable('%s: %s'),
'ChangeHistory::ExpressionAddID' => Translatable('New Expression (ID=%s)'),
'ChangeHistory::ExpressionDelete' => Translatable('Expression (ID=%s) deleted'),
'ChangeHistory::ExpressionDeleteAll' => Translatable('All Expressions of Condition (ID=%s) deleted'),
'ChangeHistory::ExpressionUpdate' => Translatable('%s (Expression ID=%s): (new=%s, old=%s)'),
'WorkOrderHistory::WorkOrderActualEndTimeReached' => Translatable('Workorder (ID=%s) reached actual end time.'),
'WorkOrderHistory::WorkOrderActualEndTimeReachedWithWorkOrderID' =>
Translatable('Workorder (ID=%s) reached actual end time.'),
'WorkOrderHistory::WorkOrderActualStartTimeReached' =>
Translatable('Workorder (ID=%s) reached actual start time.'),
'WorkOrderHistory::WorkOrderActualStartTimeReachedWithWorkOrderID' =>
Translatable('Workorder (ID=%s) reached actual start time.'),
'WorkOrderHistory::WorkOrderAdd' => Translatable('New Workorder (ID=%s)'),
'WorkOrderHistory::WorkOrderAddWithWorkOrderID' => Translatable('New Workorder (ID=%s)'),
'WorkOrderHistory::WorkOrderAttachmentAdd' => Translatable('New Attachment for WorkOrder: %s'),
'WorkOrderHistory::WorkOrderAttachmentAddWithWorkOrderID' =>
Translatable('(ID=%s) New Attachment for WorkOrder: %s'),
'WorkOrderHistory::WorkOrderAttachmentDelete' => Translatable('Deleted Attachment from WorkOrder: %s'),
'WorkOrderHistory::WorkOrderAttachmentDeleteWithWorkOrderID' =>
Translatable('(ID=%s) Deleted Attachment from WorkOrder: %s'),
'WorkOrderHistory::WorkOrderReportAttachmentAdd' => Translatable('New Report Attachment for WorkOrder: %s'),
'WorkOrderHistory::WorkOrderReportAttachmentAddWithWorkOrderID' =>
Translatable('(ID=%s) New Report Attachment for WorkOrder: %s'),
'WorkOrderHistory::WorkOrderReportAttachmentDelete' =>
Translatable('Deleted Report Attachment from WorkOrder: %s'),
'WorkOrderHistory::WorkOrderReportAttachmentDeleteWithWorkOrderID' =>
Translatable('(ID=%s) Deleted Report Attachment from WorkOrder: %s'),
'WorkOrderHistory::WorkOrderDelete' => Translatable('Workorder (ID=%s) deleted'),
'WorkOrderHistory::WorkOrderDeleteWithWorkOrderID' => Translatable('Workorder (ID=%s) deleted'),
'WorkOrderHistory::WorkOrderLinkAdd' => Translatable('Link to %s (ID=%s) added'),
'WorkOrderHistory::WorkOrderLinkAddWithWorkOrderID' => Translatable('(ID=%s) Link to %s (ID=%s) added'),
'WorkOrderHistory::WorkOrderLinkDelete' => Translatable('Link to %s (ID=%s) deleted'),
'WorkOrderHistory::WorkOrderLinkDeleteWithWorkOrderID' => Translatable('(ID=%s) Link to %s (ID=%s) deleted'),
'WorkOrderHistory::WorkOrderNotificationSent' => Translatable('Notification sent to %s (Event: %s)'),
'WorkOrderHistory::WorkOrderNotificationSentWithWorkOrderID' =>
Translatable('(ID=%s) Notification sent to %s (Event: %s)'),
'WorkOrderHistory::WorkOrderPlannedEndTimeReached' =>
Translatable('Workorder (ID=%s) reached planned end time.'),
'WorkOrderHistory::WorkOrderPlannedEndTimeReachedWithWorkOrderID' =>
Translatable('Workorder (ID=%s) reached planned end time.'),
'WorkOrderHistory::WorkOrderPlannedStartTimeReached' =>
Translatable('Workorder (ID=%s) reached planned start time.'),
'WorkOrderHistory::WorkOrderPlannedStartTimeReachedWithWorkOrderID' =>
Translatable('Workorder (ID=%s) reached planned start time.'),
'WorkOrderHistory::WorkOrderUpdate' => Translatable('%s: (new=%s, old=%s)'),
'WorkOrderHistory::WorkOrderUpdateWithWorkOrderID' => Translatable('(ID=%s) %s: (new=%s, old=%s)'),
);
return %HistoryStrings;
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<https://otrs.org/>).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut