├── .github └── workflows │ └── opm-maker.yml ├── Custom └── RocketChat.sopm ├── Kernel ├── Config │ └── Files │ │ └── XML │ │ └── RocketChat.xml ├── GenericInterface │ └── Operation │ │ └── RocketChat │ │ └── IncomingChat.pm ├── Output │ └── HTML │ │ ├── FilterElementPost │ │ └── RocketChat.pm │ │ └── Templates │ │ └── Standard │ │ ├── AdminNotificationEventTransportRocketChat.tt │ │ ├── RocketChat.tt │ │ └── RocketChat │ │ └── ChatTemplate.tt └── System │ └── Ticket │ └── Event │ └── NotificationEvent │ └── Transport │ └── RocketChat.pm ├── README └── var └── packagesetup └── RocketChat.pm /.github/workflows/opm-maker.yml: -------------------------------------------------------------------------------- 1 | name: OPM Maker 2 | 3 | on: 4 | push: 5 | paths: 6 | - "Custom/*.sopm" 7 | - ".github/workflows/*.yml" 8 | workflow_dispatch: 9 | inputs: 10 | logLevel: 11 | description: 'Log level' 12 | required: true 13 | default: 'warning' 14 | 15 | jobs: 16 | 17 | build: 18 | 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Build OPM 24 | run: docker run --rm -v ${{ github.workspace }}:/pkg -w /pkg ligero/otrs-itsm otrs.Console.pl Dev::Package::Build --allow-root --module-directory=/pkg Custom/*.sopm /pkg 25 | 26 | 27 | # Test 28 | #- name: OPM tests 29 | # uses: 30 | # run: docker-compose --file scripts/test/docker-compose.test.yml 31 | 32 | - name: Get version tag 33 | id: get_version 34 | run: echo ::set-output name=VERSION::$(grep '' Custom/*.sopm | sed -r 's/.+>(.+)<.+/\1/') 35 | 36 | - uses: rlespinasse/github-slug-action@1.1.1 37 | - name: Print slug variables 38 | run: | 39 | echo ${{ env.GITHUB_REF_SLUG }} 40 | echo ${{ env.GITHUB_HEAD_REF_SLUG }} 41 | echo ${{ env.GITHUB_BASE_REF_SLUG }} 42 | 43 | - name: Upload OPM 44 | uses: "marvinpinto/action-automatic-releases@latest" 45 | with: 46 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 47 | automatic_release_tag: ${{ env.GITHUB_REF_SLUG }} 48 | prerelease: false 49 | title: ${{ steps.get_version.outputs.VERSION }} 50 | files: | 51 | *.opm 52 | -------------------------------------------------------------------------------- /Custom/RocketChat.sopm: -------------------------------------------------------------------------------- 1 | 2 | 3 | RocketChat 4 | 6.0.5 5 | Ligero [Community] 6 | http://ligerosmart.com/ 7 | GNU GENERAL PUBLIC LICENSE Version 2, June 1991 8 | First Release 9 | BETA This addon enables OTRS to integrate with Rocket.Chat. You can have a livechat on your 10 | OTRS or any site of your company. 11 | BETA Este AddOn realiza a integração do OTRS com o Rocket.Chat. Você pode ter um chat ao vivo no seu 12 | OTRS ou qualquer site da sua empresa 13 | 6.0.x 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | my $CodeModule = 'var::packagesetup::' . $Param{Structure}->{Name}->{Content}; 26 | $Kernel::OM->Get($CodeModule)->CodeInstall(); 27 | 28 | 29 | my $CodeModule = 'var::packagesetup::' . $Param{Structure}->{Name}->{Content}; 30 | $Kernel::OM->Get($CodeModule)->CodeUpgrade(); 31 | 32 | 33 | -------------------------------------------------------------------------------- /Kernel/Config/Files/XML/RocketChat.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Rocket Chat Livechat Output Filter 5 | RocketChat::Frontend::Output::FilterElementPost 6 | 7 | 8 | Kernel::Output::HTML::FilterElementPost::RocketChat 9 | 0 10 | 11 | 12 | 1 13 | 14 | 15 | 16 | 17 | 18 | 19 | GenericInterface module registration for the operation layer. 20 | RocketChat::GenericInterface::Operation::ModuleRegistration 21 | 22 | 23 | IncomingChat 24 | RocketChat 25 | AdminGenericInterfaceOperationDefault 26 | 27 | 28 | 29 | 30 | Javascript Code generated by your RocketChat Server 31 | RocketChat::Settings 32 | 33 | 34 | <!-- Start of Rocket.Chat Livechat Script --> 35 | <script type="text/javascript"> 36 | (function(w, d, s, u) { 37 | w.RocketChat = function(c) { w.RocketChat._.push(c) }; w.RocketChat._ = []; w.RocketChat.url = u; 38 | var h = d.getElementsByTagName(s)[0], j = d.createElement(s); 39 | j.async = true; j.src = 'https://yourserver/packages/rocketchat_livechat/assets/rocketchat-livechat.min.js?_=XXXXXXXXX'; 40 | h.parentNode.insertBefore(j, h); 41 | })(window, document, 'script', 'https://yourserver/livechat'); 42 | </script> 43 | <!-- End of Rocket.Chat Livechat Script --> 44 | 45 | 46 | 47 | 48 | Defines all the parameters for this notification transport. 49 | RocketChat::Frontend::Admin::View::NotificationEvent 50 | 51 | 52 | Kernel::System::Ticket::Event::NotificationEvent::Transport::RocketChat 53 | Rocket.Chat 54 | fa fa-rocket 55 | 103 56 | 0 57 | 1 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Kernel/GenericInterface/Operation/RocketChat/IncomingChat.pm: -------------------------------------------------------------------------------- 1 | package Kernel::GenericInterface::Operation::RocketChat::IncomingChat; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Data::Dumper; 7 | 8 | use Digest::MD5 qw(md5_hex); 9 | 10 | use Date::Parse; 11 | 12 | use Kernel::System::VariableCheck qw(:all); 13 | use utf8; 14 | use Encode qw(decode encode); 15 | 16 | use base qw( 17 | Kernel::GenericInterface::Operation::Common 18 | ); 19 | 20 | our $ObjectManagerDisabled = 1; 21 | 22 | =head1 ADDON 23 | 24 | RocketChat 25 | 26 | 27 | =head1 NAME 28 | 29 | Kernel::GenericInterface::Operation::RocketChat::IncomingChat - GenericInterface RocketChat Integration. 30 | 31 | 32 | =head1 SYNOPSIS 33 | 34 | This is a web service operation for treating incoming messages of RocketChat on OTRS 35 | 36 | =head1 PUBLIC INTERFACE 37 | 38 | =over 4 39 | 40 | =cut 41 | 42 | =item new() 43 | 44 | usually, you want to create an instance of this 45 | by using Kernel::GenericInterface::Operation->new(); 46 | 47 | =cut 48 | 49 | sub new { 50 | my ( $Type, %Param ) = @_; 51 | 52 | my $Self = {}; 53 | bless( $Self, $Type ); 54 | 55 | # check needed objects 56 | for my $Needed (qw( DebuggerObject WebserviceID )) { 57 | if ( !$Param{$Needed} ) { 58 | 59 | return { 60 | Success => 0, 61 | ErrorMessage => "Got no $Needed!" 62 | }; 63 | } 64 | 65 | $Self->{$Needed} = $Param{$Needed}; 66 | } 67 | 68 | return $Self; 69 | } 70 | 71 | =item Run() 72 | 73 | Receive data from RocketChat webservice after livechat is closed and create a new ticket or 74 | a new article on an existent ticket if the ticket number is informed as TAG. 75 | 76 | Note that it's essential to have a well customized xslt mapping in order to correctly 77 | create a new ticket, according to your system queues, states and so on. 78 | 79 | Returns a hash like this: 80 | 81 | { 82 | Success => 1, 83 | Data => { 84 | Success => 1, 85 | Tickets => [123, 323] # ID of Tickets that were created or modified 86 | }, 87 | } 88 | 89 | =cut 90 | 91 | sub Run { 92 | my ( $Self, %Param ) = @_; 93 | 94 | my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 95 | my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); 96 | my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField'); 97 | my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); 98 | my $DynamicFieldValueObject = $Kernel::OM->Get('Kernel::System::DynamicFieldValue'); 99 | 100 | if ( 101 | !$Param{Data}->{UserLogin} 102 | && !$Param{Data}->{CustomerUserLogin} 103 | && !$Param{Data}->{SessionID} 104 | ) 105 | { 106 | return $Self->ReturnError( 107 | ErrorCode => 'RocketChat.MissingParameter', 108 | ErrorMessage => "RocketChat: UserLogin, CustomerUserLogin or SessionID is required!", 109 | ); 110 | } 111 | 112 | if ( $Param{Data}->{UserLogin} || $Param{Data}->{CustomerUserLogin} ) { 113 | 114 | if ( !$Param{Data}->{Password} ) 115 | { 116 | return $Self->ReturnError( 117 | ErrorCode => 'RocketChat.MissingParameter', 118 | ErrorMessage => "RocketChat: Password or SessionID is required!", 119 | ); 120 | } 121 | } 122 | 123 | my ( $UserID, $UserType ) = $Self->Auth( 124 | %Param, 125 | ); 126 | 127 | if ( !$UserID ) { 128 | return $Self->ReturnError( 129 | ErrorCode => 'RocketChat.AuthFail', 130 | ErrorMessage => "RocketChat: User could not be authenticated!", 131 | ); 132 | } 133 | 134 | my %Data; 135 | $Data{UserLogin} = $Param{Data}->{agent}->{username} || ''; 136 | $Data{CustomerUserLogin} = $Param{Data}->{visitor}->{customFields}->{username} || ''; 137 | $Data{CustomerUserName} = $Param{Data}->{visitor}->{name} || ''; 138 | $Data{CustomerUserEmail} = $Param{Data}->{visitor}->{email} || ''; 139 | $Data{Queue} = $Param{Data}->{visitor}->{department} || ''; 140 | 141 | # Se nova, Verifica se existe chamado associado a este chat 142 | my @TicketIDs; 143 | my $MainTicket; 144 | if($Param{Data}->{"_id"}){ 145 | 146 | $Self->{CacheType} = 'RocketChatTicketRoom'; 147 | $Self->{CacheTTL} = 60 * 60 * 24 * 20; 148 | $Self->{CacheKey} = 'RocketChatTicketRoom::' . $Param{Data}->{"_id"}; 149 | 150 | my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get( 151 | Type => $Self->{CacheType}, 152 | Key => $Self->{CacheKey}, 153 | ); 154 | 155 | if($Cache){ 156 | push @TicketIDs, $Cache; 157 | } else { 158 | @TicketIDs = $TicketObject->TicketSearch( 159 | UserID => 1, 160 | Result => 'ARRAY', 161 | "DynamicField_RocketChatLiveChatID" => { 162 | Equals => $Param{Data}->{"_id"} 163 | } 164 | ); 165 | # set cache 166 | $Kernel::OM->Get('Kernel::System::Cache')->Set( 167 | Type => $Self->{CacheType}, 168 | TTL => $Self->{CacheTTL}, 169 | Key => $Self->{CacheKey}, 170 | Value => $TicketIDs[0], 171 | ) if $TicketIDs[0]; # Caso o Cache tenha sido apagado, reaplicamos aqui 172 | } 173 | } 174 | 175 | if (@TicketIDs){ 176 | $MainTicket = $TicketIDs[0]; 177 | } 178 | 179 | my $Topic = $Param{Data}->{topic} || 180 | $LayoutObject->{LanguageObject}->Translate('Chat Ticket'); 181 | 182 | # Verifica se é Mensagem nova ou encerramento do Chat 183 | if($Param{Data}->{type} eq 'Message'){ 184 | if (@TicketIDs){ 185 | # return result 186 | return { 187 | Success => 1, 188 | Data => { 189 | Success => 1, 190 | ReturnMessage => "Nothing updated. Ticket for this chat was already created", 191 | Tickets => \@TicketIDs 192 | }, 193 | }; 194 | } 195 | # Se não encontrou chamado associado, significa que é a primeira mensagem do Chat 196 | # Cria então um chamado 197 | my $CustomerUser = $Param{Data}->{visitor}->{customFields}->{username} || 'rocketchat'; 198 | my @CustomerIDs = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerIDs( 199 | User => $CustomerUser, 200 | ); 201 | my $CustomerCompany = $CustomerIDs[0] || $CustomerUser; 202 | my $TicketID = $TicketObject->TicketCreate( 203 | Title => $Topic, 204 | CustomerUser => $CustomerUser, 205 | CustomerID => $CustomerCompany, 206 | %{$Param{Data}->{NewChat}} 207 | ); 208 | # set cache 209 | $Kernel::OM->Get('Kernel::System::Cache')->Set( 210 | Type => $Self->{CacheType}, 211 | TTL => $Self->{CacheTTL}, 212 | Key => $Self->{CacheKey}, 213 | Value => $TicketID, 214 | ) if $TicketID; 215 | 216 | $MainTicket = $TicketID; 217 | 218 | # Notifies Rocket.Chat session 219 | if($Param{Data}->{NewTicketNotification} 220 | && $Param{Data}->{NewTicketNotification}->{RocketChatAPIUrl} 221 | && $Param{Data}->{NewTicketNotification}->{Message} 222 | && $Param{Data}->{_id} 223 | && $Param{Data}->{visitor} 224 | && $Param{Data}->{visitor}->{token} 225 | ) 226 | { 227 | use LWP; 228 | use HTTP::Request; 229 | use Encode; 230 | my $JSONObject = $Kernel::OM->Get('Kernel::System::JSON'); 231 | 232 | my %Ticket = $TicketObject->TicketGet(TicketID => $TicketID, UserID => 1); 233 | my $TN = $Ticket{TicketNumber}; 234 | 235 | my $json; 236 | 237 | { 238 | no warnings; 239 | $json='{ 240 | "token": "'.$Param{Data}->{visitor}->{token}.'", 241 | "rid": "'.$Param{Data}->{_id}.'", 242 | "msg": "'.sprintf(encode('UTF-8',$Param{Data}->{NewTicketNotification}->{Message}),$TN,$TN,$TN,$TN,$TN).'" 243 | }'; 244 | } 245 | my $url = $Param{Data}->{NewTicketNotification}->{RocketChatAPIUrl}; 246 | my $req = HTTP::Request->new(POST => $url); 247 | $req->content_type('application/json'); 248 | $req->content($json); 249 | my $ua = LWP::UserAgent->new; # You might want some options here 250 | # Disable Certificate verification 251 | $ua->ssl_opts(verify_hostname => 0); 252 | $ua->ssl_opts(SSL_verify_mode => 0x00); 253 | 254 | my $res = $ua->request($req); 255 | my $Result = $JSONObject->Decode(Data=>$res->decoded_content); 256 | my $Success = $Result->{success}; 257 | } 258 | 259 | my $DynamicField = $DynamicFieldObject->DynamicFieldGet( 260 | Name => "RocketChatLiveChatID", 261 | ); 262 | my $Success = $DynamicFieldValueObject->ValueSet( 263 | FieldID => $DynamicField->{ID}, 264 | ObjectID => $TicketID, 265 | Value => [ 266 | { 267 | ValueText => $Param{Data}->{"_id"} 268 | } 269 | ], 270 | UserID => 1 271 | ); 272 | push @TicketIDs, $TicketID; 273 | 274 | # Enviar mensagem para o usuario 275 | 276 | # Sair do fluxo 277 | # return result 278 | return { 279 | Success => 1, 280 | Data => { 281 | Success => 1, 282 | ReturnMessage => "Ticket Created!", 283 | Tickets => \@TicketIDs 284 | }, 285 | }; 286 | 287 | } elsif($Param{Data}->{type} eq 'LivechatSession'){ 288 | 289 | use LWP::UserAgent; 290 | use MIME::Base64; 291 | 292 | # Se é encerramento do Chat 293 | # Verifica nas tags se houve algum número de chamado informado com "#" 294 | my @Tags = (); 295 | if($Param{Data}->{tags}){ 296 | # Quando há apenas uma tag, ela vem como string e não como array, então temos que converter 297 | if(IsString($Param{Data}->{tags})){ 298 | push @Tags,$Param{Data}->{tags}; 299 | } else { 300 | (@Tags) = @{$Param{Data}->{tags}}; 301 | } 302 | } 303 | 304 | # Para cada tag, verifica se possui o padrão #999999. Se sim, procura por um ticketid 305 | TAG: 306 | for my $Tag (@Tags){ 307 | next TAG if $Tag !~ m/^#/; 308 | my $TicketID; 309 | $Tag =~ s/(#)//; 310 | $TicketID = $TicketObject->TicketIDLookup( 311 | TicketNumber => $Tag, 312 | UserID => 1, 313 | ); 314 | push @TicketIDs, $TicketID if $TicketID; 315 | } 316 | 317 | # Prepara html a ser criado com base nos "messages" 318 | my @Messages = (); 319 | if($Param{Data}->{messages} && IsArrayRefWithData($Param{Data}->{messages})){ 320 | (@Messages) = @{$Param{Data}->{messages}}; 321 | } elsif ($Param{Data}->{messages}) { 322 | push @Messages, $Param{Data}->{messages}; 323 | } 324 | 325 | my $LastDate= ''; 326 | my $LastAuthor=''; 327 | 328 | my $AgentGravatar = md5_hex($Param{Data}->{agent}->{email}) ||'00000000000000000000000000000000'; 329 | my $CustomerGravatar = md5_hex($Param{Data}->{visitor}->{email}) ||'00000000000000000000000000000000'; 330 | 331 | my $AgentName = $Param{Data}->{agent}->{name} || $Param{Data}->{agent}->{username} || $LayoutObject->{LanguageObject}->Translate('Agent'); 332 | my $CustomerName = $Param{Data}->{visitor}->{name} || $Param{Data}->{visitor}->{username} || $LayoutObject->{LanguageObject}->Translate('Customer'); 333 | 334 | my @Attachments; 335 | for my $message (@Messages){ 336 | # Obs: the time of the message is always send in UTF 337 | my $time = str2time($message->{ts}); 338 | # the following field is set by javascript as a customfield on RocketChat. We need that to calculate 339 | # customer time of the message 340 | my $tz = $Param{Data}->{visitor}->{customFields}->{timezone} || 0; 341 | $time += ($tz*60); 342 | # Clean Time string Epoch 343 | $time =~ s/\..*//; 344 | my $DateObj = $Kernel::OM->Create( 345 | 'Kernel::System::DateTime', 346 | ObjectParams => { 347 | Epoch => $time, 348 | }, 349 | ); 350 | 351 | $DateObj->ToTimeZone( 352 | TimeZone => $DateObj->UserDefaultTimeZoneGet() 353 | ); 354 | 355 | 356 | ### Left Widget (Avatar maybe) 357 | # Verifies if need to print author 358 | if($message->{username} ne $LastAuthor){ 359 | $LastAuthor = $message->{username}; 360 | my $Avatar; 361 | my $Name; 362 | if ($message->{username} =~ m/^guest/){ 363 | $Name = $CustomerName; 364 | $Avatar = $CustomerGravatar; 365 | } else { 366 | $Name = $AgentName; 367 | $Avatar = $AgentGravatar; 368 | } 369 | ### Print Author 370 | $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Block( 371 | Name => 'Author', 372 | Data => { 373 | Left => "", 374 | Author => $Name, 375 | Time => $DateObj->Format( 376 | Format => "%H:%M" 377 | ) . " (".$DateObj->UserDefaultTimeZoneGet().")", 378 | }, 379 | ); 380 | } 381 | 382 | if (defined($message->{fileUpload})) { 383 | 384 | my $ua = LWP::UserAgent->new; 385 | # Disable Certificate verification 386 | $ua->ssl_opts(verify_hostname => 0); 387 | $ua->ssl_opts(SSL_verify_mode => 0x00); 388 | my $resp = $ua->get( $message->{fileUpload}->{publicFilePath} ); 389 | if ($resp->is_success) { 390 | my $aObj = { 391 | #Content => MIME::Base64::encode($resp->decoded_content( charset => 'none' ),''), 392 | Content => $resp->content(), 393 | ContentType => $message->{file}->{type}, 394 | Filename => $message->{file}->{name} 395 | }; 396 | push @Attachments, $aObj; 397 | } 398 | 399 | if ($message->{file}->{type} =~ m/image/ig) { 400 | $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Block( 401 | Name => 'Message', 402 | Data => { 403 | Content => "$message->{file}->{name}" 404 | }, 405 | ); 406 | } else { 407 | $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Block( 408 | Name => 'Message', 409 | Data => { 410 | Content => "( $message->{file}->{name} )" 411 | }, 412 | ); 413 | } 414 | 415 | } else { 416 | $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Block( 417 | Name => 'Message', 418 | Data => { 419 | Content => $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToHTML( 420 | String => $message->{msg}, 421 | ) 422 | }, 423 | ); 424 | } 425 | 426 | } 427 | 428 | my $ChatHTML = $LayoutObject->Output( 429 | TemplateFile => 'RocketChat/ChatTemplate' 430 | ); 431 | 432 | my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article'); 433 | my $ArticleBackendObject = $ArticleObject->BackendForChannel( ChannelName => 'Phone' ); 434 | 435 | for my $TicketID(@TicketIDs){ 436 | my $ArticleID = $ArticleBackendObject->ArticleCreate( 437 | #NoAgentNotify => $Article->{NoAgentNotify} || 0, 438 | TicketID => $TicketID, 439 | ArticleType => 'webrequest', 440 | SenderType => 'customer', 441 | From => $Param{Data}->{visitor}->{name} || 'Chat Guest', 442 | To => $Param{Data}->{agent}->{name} || 'Agent', 443 | Subject => $Topic, 444 | Body => $ChatHTML, 445 | MimeType => 'text/html', 446 | Charset => 'utf-8', 447 | UserID => '1', 448 | HistoryType => 'AddNote', 449 | HistoryComment => '%%ChatAdded%%', 450 | IsVisibleForCustomer => 1, 451 | Attachment => \@Attachments 452 | ); 453 | } 454 | 455 | # Update the ticket that was created by Rocket.Chat 456 | my ( $UserID, $UserType ) = $Self->Auth(%Param); 457 | my $Update = $Param{Data}->{ChatClosed}; 458 | $Self->_TicketUpdate( 459 | TicketID => $MainTicket, 460 | Ticket => $Update, 461 | UserID => $UserID, 462 | UserType => $UserType, 463 | ); 464 | } 465 | my $LogError = $Kernel::OM->Get('Kernel::System::Log')->GetLogEntry( 466 | Type => 'error', # error|info|notice 467 | What => 'Message', # Message|Traceback 468 | ); 469 | 470 | if($LogError){ 471 | $Data{Error}=$LogError; 472 | } 473 | 474 | # return result 475 | return { 476 | Success => 1, 477 | Data => { 478 | Success => 1, 479 | ReturnMessage => "Chat closed. Tickets updated!", 480 | Tickets => \@TicketIDs 481 | }, 482 | }; 483 | } 484 | 485 | 486 | 487 | =item _CheckUpdatePermissions() 488 | check if user has permissions to update ticket attributes. 489 | my $Response = $OperationObject->_CheckUpdatePermissions( 490 | TicketID => 123 491 | Ticket => $Ticket, # all ticket parameters 492 | Article => $Ticket, # all attachment parameters 493 | DynamicField => $Ticket, # all dynamic field parameters 494 | Attachment => $Ticket, # all attachment parameters 495 | UserID => 123, 496 | ); 497 | returns: 498 | $Response = { 499 | Success => 1, # if everything is OK 500 | } 501 | $Response = { 502 | Success => 0, 503 | ErrorCode => "function.error", # if error 504 | ErrorMessage => "Error description" 505 | } 506 | =cut 507 | 508 | sub _CheckUpdatePermissions { 509 | my ( $Self, %Param ) = @_; 510 | 511 | my $TicketID = $Param{TicketID}; 512 | my $Ticket = $Param{Ticket}; 513 | my $Article = $Param{Article}; 514 | my $DynamicFieldList = $Param{DynamicFieldList}; 515 | my $AttachmentList = $Param{AttachmentList}; 516 | 517 | # get ticket object 518 | my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 519 | 520 | # check Article permissions 521 | if ( IsHashRefWithData($Article) ) { 522 | my $Access = $TicketObject->TicketPermission( 523 | Type => 'note', 524 | TicketID => $TicketID, 525 | UserID => $Param{UserID}, 526 | ); 527 | if ( !$Access ) { 528 | return { 529 | ErrorCode => 'TicketUpdateOrCreate.AccessDenied', 530 | ErrorMessage => "TicketUpdateOrCreate: Does not have permissions to create new articles!", 531 | }; 532 | } 533 | } 534 | 535 | # check dynamic field permissions 536 | if ( IsArrayRefWithData($DynamicFieldList) ) { 537 | my $Access = $TicketObject->TicketPermission( 538 | Type => 'rw', 539 | TicketID => $TicketID, 540 | UserID => $Param{UserID}, 541 | ); 542 | if ( !$Access ) { 543 | return { 544 | ErrorCode => 'TicketUpdateOrCreate.AccessDenied', 545 | ErrorMessage => "TicketUpdateOrCreate: Does not have permissions to update dynamic fields!", 546 | }; 547 | } 548 | } 549 | 550 | # check queue permissions 551 | if ( $Ticket->{Queue} || $Ticket->{QueueID} ) { 552 | my $Access = $TicketObject->TicketPermission( 553 | Type => 'move', 554 | TicketID => $TicketID, 555 | UserID => $Param{UserID}, 556 | ); 557 | if ( !$Access ) { 558 | return { 559 | ErrorCode => 'TicketUpdateOrCreate.AccessDenied', 560 | ErrorMessage => "TicketUpdateOrCreate: Does not have permissions to update queue!", 561 | }; 562 | } 563 | } 564 | 565 | # check owner permissions 566 | if ( $Ticket->{Owner} || $Ticket->{OwnerID} ) { 567 | my $Access = $TicketObject->TicketPermission( 568 | Type => 'owner', 569 | TicketID => $TicketID, 570 | UserID => $Param{UserID}, 571 | ); 572 | if ( !$Access ) { 573 | return { 574 | ErrorCode => 'TicketUpdateOrCreate.AccessDenied', 575 | ErrorMessage => "TicketUpdateOrCreate: Does not have permissions to update owner!", 576 | }; 577 | } 578 | } 579 | 580 | # check responsible permissions 581 | if ( $Ticket->{Responsible} || $Ticket->{ResponsibleID} ) { 582 | my $Access = $TicketObject->TicketPermission( 583 | Type => 'responsible', 584 | TicketID => $TicketID, 585 | UserID => $Param{UserID}, 586 | ); 587 | if ( !$Access ) { 588 | return { 589 | ErrorCode => 'TicketUpdateOrCreate.AccessDenied', 590 | ErrorMessage => "TicketUpdateOrCreate: Does not have permissions to update responsibe!", 591 | }; 592 | } 593 | } 594 | 595 | # check priority permissions 596 | if ( $Ticket->{Priority} || $Ticket->{PriorityID} ) { 597 | my $Access = $TicketObject->TicketPermission( 598 | Type => 'priority', 599 | TicketID => $TicketID, 600 | UserID => $Param{UserID}, 601 | ); 602 | if ( !$Access ) { 603 | return { 604 | ErrorCode => 'TicketUpdateOrCreate.AccessDenied', 605 | ErrorMessage => "TicketUpdateOrCreate: Does not have permissions to update priority!", 606 | }; 607 | } 608 | } 609 | 610 | # check state permissions 611 | if ( $Ticket->{State} || $Ticket->{StateID} ) { 612 | 613 | # get State Data 614 | my %StateData; 615 | my $StateID; 616 | 617 | # get state object 618 | my $StateObject = $Kernel::OM->Get('Kernel::System::State'); 619 | 620 | if ( $Ticket->{StateID} ) { 621 | $StateID = $Ticket->{StateID}; 622 | } 623 | else { 624 | $StateID = $StateObject->StateLookup( 625 | State => $Ticket->{State}, 626 | ); 627 | } 628 | 629 | %StateData = $StateObject->StateGet( 630 | ID => $StateID, 631 | ); 632 | 633 | my $Access = 1; 634 | 635 | if ( $StateData{TypeName} =~ /^close/i ) { 636 | $Access = $TicketObject->TicketPermission( 637 | Type => 'close', 638 | TicketID => $TicketID, 639 | UserID => $Param{UserID}, 640 | ); 641 | } 642 | 643 | # set pending time 644 | elsif ( $StateData{TypeName} =~ /^pending/i ) { 645 | $Access = $TicketObject->TicketPermission( 646 | Type => 'close', 647 | TicketID => $TicketID, 648 | UserID => $Param{UserID}, 649 | ); 650 | } 651 | if ( !$Access ) { 652 | return { 653 | ErrorCode => 'TicketUpdateOrCreate.AccessDenied', 654 | ErrorMessage => "TicketUpdateOrCreate: Does not have permissions to update state!", 655 | }; 656 | } 657 | } 658 | 659 | return { 660 | Success => 1, 661 | } 662 | } 663 | 664 | 665 | =item _TicketUpdate() 666 | updates a ticket and creates an article and sets dynamic fields and attachments if specified. 667 | my $Response = $OperationObject->_TicketUpdate( 668 | TicketID => 123 669 | Ticket => $Ticket, # all ticket parameters 670 | Article => $Article, # all attachment parameters 671 | DynamicField => $DynamicField, # all dynamic field parameters 672 | Attachment => $Attachment, # all attachment parameters 673 | UserID => 123, 674 | UserType => 'Agent' # || 'Customer 675 | ); 676 | returns: 677 | $Response = { 678 | Success => 1, # if everything is OK 679 | Data => { 680 | TicketID => 123, 681 | TicketNumber => 'TN3422332', 682 | ArticleID => 123, # if new article was created 683 | } 684 | } 685 | $Response = { 686 | Success => 0, # if unexpected error 687 | ErrorMessage => "$Param{ErrorCode}: $Param{ErrorMessage}", 688 | } 689 | =cut 690 | 691 | sub _TicketUpdate { 692 | my ( $Self, %Param ) = @_; 693 | 694 | my $TicketID = $Param{TicketID}; 695 | my $Ticket = $Param{Ticket}; 696 | my $Article = $Param{Article}; 697 | my $DynamicFieldList = $Param{DynamicFieldList}; 698 | my $AttachmentList = $Param{AttachmentList}; 699 | 700 | my $Access = $Self->_CheckUpdatePermissions(%Param); 701 | 702 | # if no permissions return error 703 | if ( !$Access->{Success} ) { 704 | return $Self->ReturnError( %{$Access} ); 705 | } 706 | 707 | my %CustomerUserData; 708 | 709 | # get customer information 710 | if ( $Ticket->{CustomerUser} ) { 711 | %CustomerUserData = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerUserDataGet( 712 | User => $Ticket->{CustomerUser}, 713 | ); 714 | } 715 | 716 | # get ticket object 717 | my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 718 | 719 | # get current ticket data 720 | my %TicketData = $TicketObject->TicketGet( 721 | TicketID => $TicketID, 722 | DynamicFields => 0, 723 | UserID => $Param{UserID}, 724 | ); 725 | 726 | # update ticket parameters 727 | # update Ticket->Title 728 | if ( 729 | defined $Ticket->{Title} 730 | && $Ticket->{Title} ne '' 731 | && $Ticket->{Title} ne $TicketData{Title} 732 | ) 733 | { 734 | my $Success = $TicketObject->TicketTitleUpdate( 735 | Title => $Ticket->{Title}, 736 | TicketID => $TicketID, 737 | UserID => $Param{UserID}, 738 | ); 739 | if ( !$Success ) { 740 | return { 741 | Success => 0, 742 | Errormessage => 743 | 'Ticket title could not be updated, please contact system administrator!', 744 | } 745 | } 746 | } 747 | 748 | # update Ticket->Queue 749 | if ( $Ticket->{Queue} || $Ticket->{QueueID} ) { 750 | my $Success; 751 | if ( defined $Ticket->{Queue} && $Ticket->{Queue} ne $TicketData{Queue} ) { 752 | $Success = $TicketObject->TicketQueueSet( 753 | Queue => $Ticket->{Queue}, 754 | TicketID => $TicketID, 755 | UserID => $Param{UserID}, 756 | ); 757 | } 758 | elsif ( defined $Ticket->{QueueID} && $Ticket->{QueueID} ne $TicketData{QueueID} ) { 759 | $Success = $TicketObject->TicketQueueSet( 760 | QueueID => $Ticket->{QueueID}, 761 | TicketID => $TicketID, 762 | UserID => $Param{UserID}, 763 | ); 764 | } 765 | else { 766 | 767 | # data is the same as in ticket nothing to do 768 | $Success = 1; 769 | } 770 | 771 | if ( !$Success ) { 772 | return { 773 | Success => 0, 774 | ErrorMessage => 775 | 'Ticket queue could not be updated, please contact system administrator!', 776 | } 777 | } 778 | } 779 | 780 | # update Ticket->Lock 781 | if ( $Ticket->{Lock} || $Ticket->{LockID} ) { 782 | my $Success; 783 | if ( defined $Ticket->{Lock} && $Ticket->{Lock} ne $TicketData{Lock} ) { 784 | $Success = $TicketObject->TicketLockSet( 785 | Lock => $Ticket->{Lock}, 786 | TicketID => $TicketID, 787 | UserID => $Param{UserID}, 788 | ); 789 | } 790 | elsif ( defined $Ticket->{LockID} && $Ticket->{LockID} ne $TicketData{LockID} ) { 791 | $Success = $TicketObject->TicketLockSet( 792 | LockID => $Ticket->{LockID}, 793 | TicketID => $TicketID, 794 | UserID => $Param{UserID}, 795 | ); 796 | } 797 | else { 798 | 799 | # data is the same as in ticket nothing to do 800 | $Success = 1; 801 | } 802 | 803 | if ( !$Success ) { 804 | return { 805 | Success => 0, 806 | Errormessage => 807 | 'Ticket lock could not be updated, please contact system administrator!', 808 | } 809 | } 810 | } 811 | 812 | # update Ticket->Type 813 | if ( $Ticket->{Type} || $Ticket->{TypeID} ) { 814 | my $Success; 815 | if ( defined $Ticket->{Type} && $Ticket->{Type} ne $TicketData{Type} ) { 816 | $Success = $TicketObject->TicketTypeSet( 817 | Type => $Ticket->{Type}, 818 | TicketID => $TicketID, 819 | UserID => $Param{UserID}, 820 | ); 821 | } 822 | elsif ( defined $Ticket->{TypeID} && $Ticket->{TypeID} ne $TicketData{TypeID} ) 823 | { 824 | $Success = $TicketObject->TicketTypeSet( 825 | TypeID => $Ticket->{TypeID}, 826 | TicketID => $TicketID, 827 | UserID => $Param{UserID}, 828 | ); 829 | } 830 | else { 831 | 832 | # data is the same as in ticket nothing to do 833 | $Success = 1; 834 | } 835 | 836 | if ( !$Success ) { 837 | return { 838 | Success => 0, 839 | Errormessage => 840 | 'Ticket type could not be updated, please contact system administrator!', 841 | } 842 | } 843 | } 844 | 845 | # update Ticket>State 846 | # depending on the state, might require to unlock ticket or enables pending time set 847 | if ( $Ticket->{State} || $Ticket->{StateID} ) { 848 | 849 | # get State Data 850 | my %StateData; 851 | my $StateID; 852 | 853 | # get state object 854 | my $StateObject = $Kernel::OM->Get('Kernel::System::State'); 855 | 856 | if ( $Ticket->{StateID} ) { 857 | $StateID = $Ticket->{StateID}; 858 | } 859 | else { 860 | $StateID = $StateObject->StateLookup( 861 | State => $Ticket->{State}, 862 | ); 863 | } 864 | 865 | %StateData = $StateObject->StateGet( 866 | ID => $StateID, 867 | ); 868 | 869 | # force unlock if state type is close 870 | if ( $StateData{TypeName} =~ /^close/i ) { 871 | 872 | # set lock 873 | $TicketObject->TicketLockSet( 874 | TicketID => $TicketID, 875 | Lock => 'unlock', 876 | UserID => $Param{UserID}, 877 | ); 878 | } 879 | 880 | # set pending time 881 | elsif ( $StateData{TypeName} =~ /^pending/i ) { 882 | 883 | # set pending time 884 | if ( defined $Ticket->{PendingTime} ) { 885 | my $Success = $TicketObject->TicketPendingTimeSet( 886 | UserID => $Param{UserID}, 887 | TicketID => $TicketID, 888 | %{ $Ticket->{PendingTime} }, 889 | ); 890 | 891 | if ( !$Success ) { 892 | return { 893 | Success => 0, 894 | Errormessage => 895 | 'Ticket pendig time could not be updated, please contact system' 896 | . ' administrator!', 897 | } 898 | } 899 | } 900 | else { 901 | return $Self->ReturnError( 902 | ErrorCode => 'TicketUpdateOrCreate.MissingParameter', 903 | ErrorMessage => 'Can\'t set a ticket on a pending state without pendig time!' 904 | ) 905 | } 906 | } 907 | 908 | my $Success; 909 | if ( defined $Ticket->{State} && $Ticket->{State} ne $TicketData{State} ) { 910 | $Success = $TicketObject->TicketStateSet( 911 | State => $Ticket->{State}, 912 | TicketID => $TicketID, 913 | UserID => $Param{UserID}, 914 | ); 915 | } 916 | elsif ( defined $Ticket->{StateID} && $Ticket->{StateID} ne $TicketData{StateID} ) 917 | { 918 | $Success = $TicketObject->TicketStateSet( 919 | StateID => $Ticket->{StateID}, 920 | TicketID => $TicketID, 921 | UserID => $Param{UserID}, 922 | ); 923 | } 924 | else { 925 | 926 | # data is the same as in ticket nothing to do 927 | $Success = 1; 928 | } 929 | 930 | if ( !$Success ) { 931 | return { 932 | Success => 0, 933 | Errormessage => 934 | 'Ticket state could not be updated, please contact system administrator!', 935 | } 936 | } 937 | } 938 | 939 | # update Ticket->Service 940 | # this might reset SLA if current SLA is not available for the new service 941 | if ( $Ticket->{Service} || $Ticket->{ServiceID} ) { 942 | 943 | # check if ticket has a SLA assigned 944 | if ( $TicketData{SLAID} ) { 945 | 946 | # check if old SLA is still valid 947 | if ( 948 | !$Self->ValidateSLA( 949 | SLAID => $TicketData{SLAID}, 950 | Service => $Ticket->{Service} || '', 951 | ServiceID => $Ticket->{ServiceID} || '', 952 | ) 953 | ) 954 | { 955 | 956 | # remove current SLA if is not compatible with new service 957 | my $Success = $TicketObject->TicketSLASet( 958 | SLAID => '', 959 | TicketID => $TicketID, 960 | UserID => $Param{UserID}, 961 | ); 962 | } 963 | } 964 | 965 | my $Success; 966 | 967 | # prevent comparison errors on undefined values 968 | if ( !defined $TicketData{Service} ) { 969 | $TicketData{Service} = ''; 970 | } 971 | if ( !defined $TicketData{ServiceID} ) { 972 | $TicketData{ServiceID} = ''; 973 | } 974 | 975 | if ( defined $Ticket->{Service} && $Ticket->{Service} ne $TicketData{Service} ) { 976 | $Success = $TicketObject->TicketServiceSet( 977 | Service => $Ticket->{Service}, 978 | TicketID => $TicketID, 979 | UserID => $Param{UserID}, 980 | ); 981 | } 982 | elsif ( defined $Ticket->{ServiceID} && $Ticket->{ServiceID} ne $TicketData{ServiceID} ) 983 | { 984 | $Success = $TicketObject->TicketServiceSet( 985 | ServiceID => $Ticket->{ServiceID}, 986 | TicketID => $TicketID, 987 | UserID => $Param{UserID}, 988 | ); 989 | } 990 | else { 991 | 992 | # data is the same as in ticket nothing to do 993 | $Success = 1; 994 | } 995 | 996 | if ( !$Success ) { 997 | return { 998 | Success => 0, 999 | Errormessage => 1000 | 'Ticket service could not be updated, please contact system administrator!', 1001 | } 1002 | } 1003 | } 1004 | 1005 | # update Ticket->SLA 1006 | if ( $Ticket->{SLA} || $Ticket->{SLAID} ) { 1007 | my $Success; 1008 | 1009 | # prevent comparison errors on undefined values 1010 | if ( !defined $TicketData{SLA} ) { 1011 | $TicketData{SLA} = ''; 1012 | } 1013 | if ( !defined $TicketData{SLAID} ) { 1014 | $TicketData{SLAID} = ''; 1015 | } 1016 | 1017 | if ( defined $Ticket->{SLA} && $Ticket->{SLA} ne $TicketData{SLA} ) { 1018 | $Success = $TicketObject->TicketSLASet( 1019 | SLA => $Ticket->{SLA}, 1020 | TicketID => $TicketID, 1021 | UserID => $Param{UserID}, 1022 | ); 1023 | } 1024 | elsif ( defined $Ticket->{SLAID} && $Ticket->{SLAID} ne $TicketData{SLAID} ) 1025 | { 1026 | $Success = $TicketObject->TicketSLASet( 1027 | SLAID => $Ticket->{SLAID}, 1028 | TicketID => $TicketID, 1029 | UserID => $Param{UserID}, 1030 | ); 1031 | } 1032 | else { 1033 | 1034 | # data is the same as in ticket nothing to do 1035 | $Success = 1; 1036 | } 1037 | 1038 | if ( !$Success ) { 1039 | return { 1040 | Success => 0, 1041 | Errormessage => 1042 | 'Ticket SLA could not be updated, please contact system administrator!', 1043 | } 1044 | } 1045 | } 1046 | 1047 | # update Ticket->CustomerUser && Ticket->CustomerID 1048 | if ( $Ticket->{CustomerUser} || $Ticket->{CustomerID} ) { 1049 | 1050 | # set values to empty if they are not defined 1051 | $TicketData{CustomerUserID} = $TicketData{CustomerUserID} || ''; 1052 | $TicketData{CustomerID} = $TicketData{CustomerID} || ''; 1053 | $Ticket->{CustomerUser} = $Ticket->{CustomerUser} || ''; 1054 | $Ticket->{CustomerID} = $Ticket->{CustomerID} || ''; 1055 | 1056 | my $Success; 1057 | if ( 1058 | $Ticket->{CustomerUser} ne $TicketData{CustomerUserID} 1059 | || $Ticket->{CustomerID} ne $TicketData{CustomerID} 1060 | ) 1061 | { 1062 | my $CustomerID = $CustomerUserData{UserCustomerID} || ''; 1063 | 1064 | # use user defined CustomerID if defined 1065 | if ( defined $Ticket->{CustomerID} && $Ticket->{CustomerID} ne '' ) { 1066 | $CustomerID = $Ticket->{CustomerID}; 1067 | } 1068 | 1069 | $Success = $TicketObject->TicketCustomerSet( 1070 | No => $CustomerID, 1071 | User => $Ticket->{CustomerUser}, 1072 | TicketID => $TicketID, 1073 | UserID => $Param{UserID}, 1074 | ); 1075 | } 1076 | else { 1077 | 1078 | # data is the same as in ticket nothing to do 1079 | $Success = 1; 1080 | } 1081 | 1082 | if ( !$Success ) { 1083 | return { 1084 | Success => 0, 1085 | Errormessage => 1086 | 'Ticket customer user could not be updated, please contact system administrator!', 1087 | } 1088 | } 1089 | } 1090 | 1091 | # update Ticket->Priority 1092 | if ( $Ticket->{Priority} || $Ticket->{PriorityID} ) { 1093 | my $Success; 1094 | if ( defined $Ticket->{Priority} && $Ticket->{Priority} ne $TicketData{Priority} ) { 1095 | $Success = $TicketObject->TicketPrioritySet( 1096 | Priority => $Ticket->{Priority}, 1097 | TicketID => $TicketID, 1098 | UserID => $Param{UserID}, 1099 | ); 1100 | } 1101 | elsif ( defined $Ticket->{PriorityID} && $Ticket->{PriorityID} ne $TicketData{PriorityID} ) 1102 | { 1103 | $Success = $TicketObject->TicketPrioritySet( 1104 | PriorityID => $Ticket->{PriorityID}, 1105 | TicketID => $TicketID, 1106 | UserID => $Param{UserID}, 1107 | ); 1108 | } 1109 | else { 1110 | 1111 | # data is the same as in ticket nothing to do 1112 | $Success = 1; 1113 | } 1114 | 1115 | if ( !$Success ) { 1116 | return { 1117 | Success => 0, 1118 | Errormessage => 1119 | 'Ticket priority could not be updated, please contact system administrator!', 1120 | } 1121 | } 1122 | } 1123 | 1124 | my $UnlockOnAway = 1; 1125 | 1126 | # update Ticket->Owner 1127 | if ( $Ticket->{Owner} || $Ticket->{OwnerID} ) { 1128 | my $Success; 1129 | if ( defined $Ticket->{Owner} && $Ticket->{Owner} ne $TicketData{Owner} ) { 1130 | $Success = $TicketObject->TicketOwnerSet( 1131 | NewUser => $Ticket->{Owner}, 1132 | TicketID => $TicketID, 1133 | UserID => $Param{UserID}, 1134 | ); 1135 | $UnlockOnAway = 0; 1136 | } 1137 | elsif ( defined $Ticket->{OwnerID} && $Ticket->{OwnerID} ne $TicketData{OwnerID} ) 1138 | { 1139 | $Success = $TicketObject->TicketOwnerSet( 1140 | NewUserID => $Ticket->{OwnerID}, 1141 | TicketID => $TicketID, 1142 | UserID => $Param{UserID}, 1143 | ); 1144 | $UnlockOnAway = 0; 1145 | } 1146 | else { 1147 | 1148 | # data is the same as in ticket nothing to do 1149 | $Success = 1; 1150 | } 1151 | 1152 | if ( !$Success ) { 1153 | return { 1154 | Success => 0, 1155 | Errormessage => 1156 | 'Ticket owner could not be updated, please contact system administrator!', 1157 | } 1158 | } 1159 | } 1160 | 1161 | # update Ticket->Responsible 1162 | if ( $Ticket->{Responsible} || $Ticket->{ResponsibleID} ) { 1163 | my $Success; 1164 | if ( 1165 | defined $Ticket->{Responsible} 1166 | && $Ticket->{Responsible} ne $TicketData{Responsible} 1167 | ) 1168 | { 1169 | $Success = $TicketObject->TicketResponsibleSet( 1170 | NewUser => $Ticket->{Responsible}, 1171 | TicketID => $TicketID, 1172 | UserID => $Param{UserID}, 1173 | ); 1174 | } 1175 | elsif ( 1176 | defined $Ticket->{ResponsibleID} 1177 | && $Ticket->{ResponsibleID} ne $TicketData{ResponsibleID} 1178 | ) 1179 | { 1180 | $Success = $TicketObject->TicketResponsibleSet( 1181 | NewUserID => $Ticket->{ResponsibleID}, 1182 | TicketID => $TicketID, 1183 | UserID => $Param{UserID}, 1184 | ); 1185 | } 1186 | else { 1187 | 1188 | # data is the same as in ticket nothing to do 1189 | $Success = 1; 1190 | } 1191 | 1192 | if ( !$Success ) { 1193 | return { 1194 | Success => 0, 1195 | Errormessage => 1196 | 'Ticket responsible could not be updated, please contact system administrator!', 1197 | } 1198 | } 1199 | } 1200 | 1201 | my $ArticleID; 1202 | if ( IsHashRefWithData($Article) ) { 1203 | 1204 | # set Article From 1205 | my $From; 1206 | if ( $Article->{From} ) { 1207 | $From = $Article->{From}; 1208 | } 1209 | elsif ( $Param{UserType} eq 'Customer' ) { 1210 | 1211 | # use data from customer user (if customer user is in database) 1212 | if ( IsHashRefWithData( \%CustomerUserData ) ) { 1213 | $From = '"' 1214 | . $CustomerUserData{UserFirstname} . ' ' 1215 | . $CustomerUserData{UserLastname} . '"' 1216 | . ' <' . $CustomerUserData{UserEmail} . '>'; 1217 | } 1218 | 1219 | # otherwise use customer user as sent from the request (it should be an email) 1220 | else { 1221 | $From = $Ticket->{CustomerUser}; 1222 | } 1223 | } 1224 | else { 1225 | my %UserData = $Kernel::OM->Get('Kernel::System::User')->GetUserData( 1226 | UserID => $Param{UserID}, 1227 | ); 1228 | $From = $UserData{UserFirstname} . ' ' . $UserData{UserLastname}; 1229 | } 1230 | 1231 | # set Article To 1232 | my $To = ''; 1233 | 1234 | # create article 1235 | $ArticleID = $TicketObject->ArticleCreate( 1236 | NoAgentNotify => $Article->{NoAgentNotify} || 0, 1237 | TicketID => $TicketID, 1238 | ArticleTypeID => $Article->{ArticleTypeID} || '', 1239 | ArticleType => $Article->{ArticleType} || '', 1240 | SenderTypeID => $Article->{SenderTypeID} || '', 1241 | SenderType => $Article->{SenderType} || '', 1242 | From => $From, 1243 | To => $To, 1244 | Subject => $Article->{Subject}, 1245 | Body => $Article->{Body}, 1246 | MimeType => $Article->{MimeType} || '', 1247 | Charset => $Article->{Charset} || '', 1248 | ContentType => $Article->{ContentType} || '', 1249 | UserID => $Param{UserID}, 1250 | HistoryType => $Article->{HistoryType}, 1251 | HistoryComment => $Article->{HistoryComment} || '%%', 1252 | AutoResponseType => $Article->{AutoResponseType}, 1253 | UnlockOnAway => $UnlockOnAway, 1254 | OrigHeader => { 1255 | From => $From, 1256 | To => $To, 1257 | Subject => $Article->{Subject}, 1258 | Body => $Article->{Body}, 1259 | 1260 | }, 1261 | ); 1262 | 1263 | if ( !$ArticleID ) { 1264 | return { 1265 | Success => 0, 1266 | ErrorMessage => 1267 | 'Article could not be created, please contact the system administrator' 1268 | } 1269 | } 1270 | 1271 | # time accounting 1272 | if ( $Article->{TimeUnit} ) { 1273 | $TicketObject->TicketAccountTime( 1274 | TicketID => $TicketID, 1275 | ArticleID => $ArticleID, 1276 | TimeUnit => $Article->{TimeUnit}, 1277 | UserID => $Param{UserID}, 1278 | ); 1279 | } 1280 | } 1281 | 1282 | # set dynamic fields 1283 | for my $DynamicField ( @{$DynamicFieldList} ) { 1284 | my $Result = $Self->SetDynamicFieldValue( 1285 | %{$DynamicField}, 1286 | TicketID => $TicketID, 1287 | ArticleID => $ArticleID || '', 1288 | UserID => $Param{UserID}, 1289 | ); 1290 | 1291 | if ( !$Result->{Success} ) { 1292 | my $ErrorMessage = 1293 | $Result->{ErrorMessage} || "Dynamic Field $DynamicField->{Name} could not be set," 1294 | . " please contact the system administrator"; 1295 | 1296 | return { 1297 | Success => 0, 1298 | ErrorMessage => $ErrorMessage, 1299 | }; 1300 | } 1301 | } 1302 | 1303 | # set attachments 1304 | 1305 | for my $Attachment ( @{$AttachmentList} ) { 1306 | my $Result = $Self->CreateAttachment( 1307 | Attachment => $Attachment, 1308 | ArticleID => $ArticleID || '', 1309 | UserID => $Param{UserID} 1310 | ); 1311 | 1312 | if ( !$Result->{Success} ) { 1313 | my $ErrorMessage = 1314 | $Result->{ErrorMessage} || "Attachment could not be created, please contact the " 1315 | . " system administrator"; 1316 | 1317 | return { 1318 | Success => 0, 1319 | ErrorMessage => $ErrorMessage, 1320 | }; 1321 | } 1322 | } 1323 | 1324 | if ($ArticleID) { 1325 | return { 1326 | Success => 1, 1327 | Data => { 1328 | TicketID => $TicketID, 1329 | TicketNumber => $TicketData{TicketNumber}, 1330 | ArticleID => $ArticleID, 1331 | }, 1332 | }; 1333 | } 1334 | return { 1335 | Success => 1, 1336 | Data => { 1337 | TicketID => $TicketID, 1338 | TicketNumber => $TicketData{TicketNumber}, 1339 | }, 1340 | }; 1341 | } 1342 | 1343 | 1; 1344 | 1345 | =back 1346 | -------------------------------------------------------------------------------- /Kernel/Output/HTML/FilterElementPost/RocketChat.pm: -------------------------------------------------------------------------------- 1 | package Kernel::Output::HTML::FilterElementPost::RocketChat; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Data::Dumper; 7 | 8 | 9 | our @ObjectDependencies = ( 10 | 'Kernel::Output::HTML::Layout', 11 | ); 12 | 13 | =head1 ADDON 14 | 15 | RocketChat 16 | 17 | 18 | =head1 NAME 19 | 20 | Kernel::Output::HTML::FilterElementPost::RocketChat 21 | 22 | 23 | =head1 SYNOPSIS 24 | 25 | Output element filter that includes rocket.chat script 26 | on OTRS's Customer interface 27 | 28 | =head1 PUBLIC INTERFACE 29 | 30 | =over 4 31 | 32 | =cut 33 | 34 | =item new() 35 | 36 | Creates the object. Also takes user information and sets into Self element 37 | 38 | =cut 39 | sub new { 40 | my ( $Type, %Param ) = @_; 41 | 42 | # allocate new hash for object 43 | my $Self = {}; 44 | bless( $Self, $Type ); 45 | 46 | $Self->{UserLogin} = $Param{UserLogin}; 47 | $Self->{UserFirstname} = $Param{UserFirstname} || 'Guest'; 48 | $Self->{UserLastname} = $Param{UserLastname} || ''; 49 | $Self->{UserEmail} = $Param{UserEmail}; 50 | 51 | return $Self; 52 | } 53 | 54 | =item Run() 55 | 56 | Execute output filter element for includin Rocket.Chat livechat script on 57 | OTRS's Customer Interface 58 | 59 | =cut 60 | sub Run { 61 | my ( $Self, %Param ) = @_; 62 | 63 | my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); 64 | 65 | my %Data = ( 66 | UserLogin => $Self->{UserLogin}, 67 | UserFirstname => $Self->{UserFirstname}, 68 | UserLastname => $Self->{UserLastname}, 69 | UserEmail => $Self->{UserEmail}, 70 | RocketChatJavascript => $Kernel::OM->Get('Kernel::Config')->Get('RocketChat::Code') 71 | ); 72 | 73 | my $Content = $LayoutObject->Output( 74 | TemplateFile => 'RocketChat', 75 | Data => { 76 | %Data 77 | }, 78 | ); 79 | ## Add Rocket Chat script 80 | ${ $Param{Data} } .= $Content; 81 | 82 | return 1; 83 | } 84 | 85 | 1; 86 | -------------------------------------------------------------------------------- /Kernel/Output/HTML/Templates/Standard/AdminNotificationEventTransportRocketChat.tt: -------------------------------------------------------------------------------- 1 | # -- 2 | # Copyright (C) 2001-2018 Complemento, http://www.complemento.net.br 3 | # -- 4 | # This software comes with ABSOLUTELY NO WARRANTY. For details, see 5 | # the enclosed file COPYING for license information (AGPL). If you 6 | # did not receive this file, see http://www.gnu.org/licenses/agpl.txt. 7 | # -- 8 | 9 |

[% Translate("This method sends the notification to your Rocket.Chat!") | html %]

10 | 11 | 12 |
13 | 14 |
15 |
16 | -------------------------------------------------------------------------------- /Kernel/Output/HTML/Templates/Standard/RocketChat.tt: -------------------------------------------------------------------------------- 1 | 2 | 3 | [% Data.RocketChatJavascript %] 4 | 5 | 38 | -------------------------------------------------------------------------------- /Kernel/Output/HTML/Templates/Standard/RocketChat/ChatTemplate.tt: -------------------------------------------------------------------------------- 1 | 43 |
44 | # [% RenderBlockStart("Row") %] 45 | #[% RenderBlockStart("Date") %] 46 | #
47 | #
[% Data.Content | Localize("Date") %]
48 | #[% RenderBlockEnd("Date") %] 49 | 50 | [% RenderBlockStart("Author") %] 51 |
52 | [% Data.Left %] 53 |
54 |
55 | 56 | [% Data.Author | html %] 57 | [% Data.Time | Localize("TimeShort") %] 58 | 59 | [% RenderBlockStart("Message") %] 60 | 61 | [% Data.Content %] 62 | 63 | [% RenderBlockEnd("Message") %] 64 |
65 | [% RenderBlockEnd("Author") %] 66 | # [% RenderBlockEnd("Row") %] 67 |
68 | -------------------------------------------------------------------------------- /Kernel/System/Ticket/Event/NotificationEvent/Transport/RocketChat.pm: -------------------------------------------------------------------------------- 1 | # -- 2 | # Copyright (C) 2001-2018 Complemento, http://complemento.net.br/ 3 | # -- 4 | # This software comes with ABSOLUTELY NO WARRANTY. For details, see 5 | # the enclosed file COPYING for license information (AGPL). If you 6 | # did not receive this file, see http://www.gnu.org/licenses/agpl.txt. 7 | # -- 8 | 9 | package Kernel::System::Ticket::Event::NotificationEvent::Transport::RocketChat; 10 | ## nofilter(TidyAll::Plugin::OTRS::Perl::LayoutObject) 11 | ## nofilter(TidyAll::Plugin::OTRS::Perl::ParamObject) 12 | 13 | use strict; 14 | use warnings; 15 | use JSON::PP; 16 | 17 | use LWP; 18 | use HTTP::Request; 19 | use XML::Simple; 20 | use Encode qw(decode encode); 21 | use MIME::Base64; 22 | use Data::Dumper; 23 | use utf8; 24 | 25 | use Kernel::System::VariableCheck qw(:all); 26 | use Kernel::Language qw(Translatable); 27 | 28 | use base qw(Kernel::System::Ticket::Event::NotificationEvent::Transport::Email); 29 | 30 | our @ObjectDependencies = ( 31 | 'Kernel::Config', 32 | 'Kernel::Output::HTML::Layout', 33 | 'Kernel::System::CustomerUser', 34 | 'Kernel::System::Email', 35 | 'Kernel::System::Log', 36 | 'Kernel::System::Main', 37 | 'Kernel::System::Queue', 38 | 'Kernel::System::SystemAddress', 39 | 'Kernel::System::Ticket', 40 | 'Kernel::System::User', 41 | 'Kernel::System::Web::Request', 42 | 'Kernel::System::Crypt::PGP', 43 | 'Kernel::System::Crypt::SMIME', 44 | 'Kernel::System::DynamicField', 45 | 'Kernel::System::DynamicField::Backend', 46 | ); 47 | 48 | =head1 NAME 49 | 50 | Kernel::System::Ticket::Event::NotificationEvent::Transport::SmsNotify - sms transport layer 51 | 52 | =head1 SYNOPSIS 53 | 54 | Notification event transport layer. 55 | 56 | =head1 PUBLIC INTERFACE 57 | 58 | =over 4 59 | 60 | =cut 61 | 62 | =item new() 63 | 64 | create a notification transport object. Do not use it directly, instead use: 65 | 66 | use Kernel::System::ObjectManager; 67 | local $Kernel::OM = Kernel::System::ObjectManager->new(''); 68 | my $TransportObject = $Kernel::OM->Get('Kernel::System::Ticket::Event::NotificationEvent::Transport::SmsNotify'); 69 | 70 | =cut 71 | 72 | #sub new { 73 | #my ( $Type, %Param ) = @_; 74 | 75 | ## allocate new hash for object 76 | #my $Self = {}; 77 | #bless( $Self, $Type ); 78 | 79 | #return $Self; 80 | #} 81 | 82 | sub SendNotification { 83 | my ( $Self, %Param ) = @_; 84 | 85 | # check needed stuff 86 | for my $Needed (qw(TicketID UserID Notification Recipient)) { 87 | if ( !$Param{$Needed} ) { 88 | $Kernel::OM->Get('Kernel::System::Log')->Log( 89 | Priority => 'error', 90 | Message => 'Need $Needed!', 91 | ); 92 | return; 93 | } 94 | } 95 | 96 | # cleanup event data 97 | $Self->{EventData} = undef; 98 | 99 | # get needed objects 100 | my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 101 | my $SystemAddressObject = $Kernel::OM->Get('Kernel::System::SystemAddress'); 102 | my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); 103 | 104 | 105 | my %Notification = %{ $Param{Notification} }; 106 | 107 | return if !$Param{Notification}->{Data}->{RecipientWebhookURL}; 108 | 109 | # get ticket object 110 | my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 111 | 112 | # send notification 113 | my $Sent = $Self->_ScheduleMessage( 114 | Subject => $Notification{Subject}, 115 | Body => $Notification{Body}, 116 | RecipientWebhookURL => $Param{Notification}->{Data}->{RecipientWebhookURL} 117 | ); 118 | 119 | if ( !$Sent ) { 120 | $Kernel::OM->Get('Kernel::System::Log')->Log( 121 | Priority => 'error', 122 | Message => "'$Notification{Name}' notification could not be sent to Rocket.Chat", 123 | ); 124 | return; 125 | } 126 | 127 | # log event 128 | $Kernel::OM->Get('Kernel::System::Log')->Log( 129 | Priority => 'info', 130 | Message => "Sent agent '$Notification{Name}' notification to Rocket.Chat.", 131 | ); 132 | 133 | return 1; 134 | } 135 | 136 | sub GetTransportRecipients { 137 | my ( $Self, %Param ) = @_; 138 | 139 | for my $Needed (qw(Notification)) { 140 | if ( !$Param{$Needed} ) { 141 | $Kernel::OM->Get('Kernel::System::Log')->Log( 142 | Priority => 'error', 143 | Message => "Need $Needed", 144 | ); 145 | } 146 | } 147 | 148 | my @Recipients; 149 | 150 | my %Recipient; 151 | $Recipient{Type} = 'Agent'; 152 | $Recipient{WebhookURL} = $Param{Notification}->{Data}->{RecipientWebhookURL}; 153 | 154 | push @Recipients, \%Recipient; 155 | return @Recipients; 156 | } 157 | 158 | sub TransportSettingsDisplayGet { 159 | my ( $Self, %Param ) = @_; 160 | 161 | KEY: 162 | for my $Key (qw(RecipientWebhookURL)) { 163 | next KEY if !$Param{Data}->{$Key}; 164 | next KEY if !defined $Param{Data}->{$Key}->[0]; 165 | $Param{$Key} = $Param{Data}->{$Key}->[0]; 166 | } 167 | 168 | # get layout object 169 | my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); 170 | 171 | # generate HTML 172 | my $Output = $LayoutObject->Output( 173 | TemplateFile => 'AdminNotificationEventTransportRocketChat', 174 | Data => \%Param, 175 | ); 176 | 177 | return $Output; 178 | } 179 | 180 | sub TransportParamSettingsGet { 181 | my ( $Self, %Param ) = @_; 182 | 183 | for my $Needed (qw(GetParam)) { 184 | if ( !$Param{$Needed} ) { 185 | $Kernel::OM->Get('Kernel::System::Log')->Log( 186 | Priority => 'error', 187 | Message => "Need $Needed", 188 | ); 189 | } 190 | } 191 | 192 | # get param object 193 | my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); 194 | 195 | PARAMETER: 196 | for my $Parameter ( 197 | qw(RecipientWebhookURL) 198 | ) 199 | { 200 | my @Data = $ParamObject->GetArray( Param => $Parameter ); 201 | next PARAMETER if !@Data; 202 | $Param{GetParam}->{Data}->{$Parameter} = \@Data; 203 | } 204 | 205 | # Note: Example how to set errors and use them 206 | # on the normal AdminNotificationEvent screen 207 | # # set error 208 | # $Param{GetParam}->{$Parameter.'ServerError'} = 'ServerError'; 209 | 210 | return 1; 211 | } 212 | 213 | sub _ScheduleMessage { 214 | my ( $Self, %Param ) = @_; 215 | 216 | # For Asynchronous sending 217 | my $TaskName = substr "Recipient".rand().$Param{RecipientWebhookURL}, 0, 255; 218 | 219 | # create a new task 220 | my $TaskID = $Kernel::OM->Get('Kernel::System::Scheduler')->TaskAdd( 221 | Type => 'AsynchronousExecutor', 222 | Name => $TaskName, 223 | Attempts => 1, 224 | MaximumParallelInstances => 0, 225 | Data => { 226 | Object => 'Kernel::System::Ticket::Event::NotificationEvent::Transport::RocketChat', 227 | Function => 'SendMessageRocketChat', 228 | Params => { 229 | RecipientWebhookURL => $Param{RecipientWebhookURL}, 230 | Subject => $Param{Subject}, 231 | Body => $Param{Body}, 232 | }, 233 | }, 234 | ); 235 | } 236 | 237 | sub SendMessageRocketChat { 238 | my ( $Self, %Param ) = @_; 239 | 240 | # Convert Body to pure text 241 | $Param{Body} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToAscii( String => $Param{Body} ); 242 | $Param{Body} = encode("utf8", $Param{Body}); 243 | 244 | my $ua = LWP::UserAgent->new; 245 | 246 | my %Data; 247 | $Data{text} = $Param{Subject} ."\n\n".$Param{Body}; 248 | 249 | my $jsonData = $Kernel::OM->Get('Kernel::System::JSON')->Encode( 250 | Data => \%Data, 251 | ); 252 | 253 | $ua->default_header("Content-Type" => "application/json"); 254 | $ua->default_header("Accept" => "application/json"); 255 | 256 | my $response = $ua->post($Param{RecipientWebhookURL}->[0], Content_Type => 'application/json', Content => $jsonData); 257 | 258 | my $ResponseData = $Kernel::OM->Get('Kernel::System::JSON')->Decode( 259 | Data => $response->decoded_content, 260 | ); 261 | 262 | if(!$ResponseData->{success}){ 263 | $Kernel::OM->Get('Kernel::System::Log')->Log( 264 | Priority => 'error', 265 | Message => "Error on sending message to Rocket.Chat", 266 | ); 267 | return 0; 268 | } else { 269 | return 1; 270 | } 271 | 272 | } 273 | 1; 274 | 275 | =back 276 | 277 | =head1 TERMS AND CONDITIONS 278 | 279 | This software comes with ABSOLUTELY NO WARRANTY. For details, see 280 | the enclosed file COPYING for license information (AGPL). If you 281 | did not receive this file, see L. 282 | 283 | =cut 284 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | * Ligero - Rocket Chat Livechat Integration * 3 | 4 | 1) Install this package on Ligero 5 | 6 | 2) Enable Livechat on Rocket Chat ( refer to https://docs.rocket.chat/guides/omnichannel-guides/omnichannel ) 7 | 8 | 3) As a livechat manager, access Omnichannel -> Webhook and fill the form as below 9 | 10 | *Webhook URL:* https:///otrs/nph-genericinterface.pl/Webservice/RocketChat/?UserLogin=&Password= 11 | *Secret Token:* ( Leave empty ) 12 | *Send Request on:* [Chat Close], [Visitor Messages] 13 | 14 | 4) Save 15 | -------------------------------------------------------------------------------- /var/packagesetup/RocketChat.pm: -------------------------------------------------------------------------------- 1 | # -- 2 | # This software comes with ABSOLUTELY NO WARRANTY. For details, see 3 | # the enclosed file COPYING for license information (AGPL). If you 4 | # did not receive this file, see http://www.gnu.org/licenses/agpl.txt. 5 | # -- 6 | 7 | package var::packagesetup::RocketChat; 8 | 9 | =head1 ADDON 10 | 11 | RocketChat 12 | 13 | 14 | =head1 NAME 15 | 16 | var::packagesetup::RocketChat - AddOn Auto installer script 17 | 18 | 19 | =head1 SYNOPSIS 20 | 21 | Create a RocketChat default Web Service on OTRS 22 | 23 | =head1 PUBLIC INTERFACE 24 | 25 | =over 4 26 | 27 | =cut 28 | 29 | use strict; 30 | use warnings; 31 | 32 | use Kernel::Output::Template::Provider; 33 | 34 | our @ObjectDependencies = ( 35 | 'Kernel::Config', 36 | 'Kernel::System::DB', 37 | 'Kernel::System::DynamicField', 38 | 'Kernel::System::Log', 39 | 'Kernel::System::State', 40 | 'Kernel::System::Stats', 41 | 'Kernel::System::SysConfig', 42 | 'Kernel::System::Type', 43 | 'Kernel::System::Valid', 44 | ); 45 | 46 | =item new() 47 | 48 | Creates the Object 49 | 50 | =cut 51 | sub new { 52 | my ( $Type, %Param ) = @_; 53 | 54 | # allocate new hash for object 55 | my $Self = {}; 56 | bless( $Self, $Type ); 57 | 58 | return $Self; 59 | } 60 | 61 | =item new() 62 | 63 | Call all subroutines needed to the package installation 64 | 65 | =cut 66 | sub CodeInstall { 67 | my ( $Self, %Param ) = @_; 68 | 69 | $Self->_CreateDynamicFields(); 70 | #$Self->_UpdateConfig(); 71 | $Self-> _CreateWebServices(); 72 | return 1; 73 | } 74 | 75 | =item new() 76 | 77 | Call all subroutines needed to the package Upgrade 78 | 79 | =cut 80 | sub CodeUpgrade { 81 | my ( $Self, %Param ) = @_; 82 | 83 | #$Self->_CreateDynamicFields(); 84 | $Self->_CreateWebServices(); 85 | #$Self->_UpdateConfig(); 86 | 87 | return 1; 88 | } 89 | 90 | sub _UpdateConfig { 91 | my ( $Self, %Param ) = @_; 92 | 93 | my $SysConfigObject = $Kernel::OM->Get('Kernel::System::SysConfig'); 94 | # my @Configs = ( 95 | # { 96 | # ConfigItem => 'CustomerFrontend::CommonParam###Action', 97 | # Value => 'CustomerServiceCatalog' 98 | # }, 99 | # ); 100 | 101 | # CONFIGITEM: 102 | # for my $Config (@Configs) { 103 | # # set new setting, 104 | # my $Success = $SysConfigObject->ConfigItemUpdate( 105 | # Valid => 1, 106 | # Key => $Config->{ConfigItem}, 107 | # Value => $Config->{Value}, 108 | # ); 109 | 110 | # } 111 | 112 | return 1; 113 | } 114 | 115 | sub _CreateDynamicFields { 116 | my ( $Self, %Param ) = @_; 117 | 118 | my $ValidID = $Kernel::OM->Get('Kernel::System::Valid')->ValidLookup( 119 | Valid => 'valid', 120 | ); 121 | 122 | # get all current dynamic fields 123 | my $DynamicFieldList = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( 124 | Valid => 0, 125 | ); 126 | 127 | # get the list of order numbers (is already sorted). 128 | my @DynamicfieldOrderList; 129 | for my $Dynamicfield ( @{$DynamicFieldList} ) { 130 | push @DynamicfieldOrderList, $Dynamicfield->{FieldOrder}; 131 | } 132 | 133 | # get the last element from the order list and add 1 134 | my $NextOrderNumber = 1; 135 | if (@DynamicfieldOrderList) { 136 | $NextOrderNumber = $DynamicfieldOrderList[-1] + 1; 137 | } 138 | 139 | # get the definition for all dynamic fields for ITSM 140 | my @DynamicFields = $Self->_GetITSMDynamicFieldsDefinition(); 141 | 142 | # create a dynamic fields lookup table 143 | my %DynamicFieldLookup; 144 | DYNAMICFIELD: 145 | for my $DynamicField ( @{$DynamicFieldList} ) { 146 | next DYNAMICFIELD if ref $DynamicField ne 'HASH'; 147 | $DynamicFieldLookup{ $DynamicField->{Name} } = $DynamicField; 148 | } 149 | 150 | # create or update dynamic fields 151 | DYNAMICFIELD: 152 | for my $DynamicField (@DynamicFields) { 153 | 154 | my $CreateDynamicField; 155 | 156 | if ( ref $DynamicFieldLookup{ $DynamicField->{Name} } eq 'HASH' ) { 157 | # Deletes DF 158 | my $DynamicFieldID = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldGet( 159 | Name => $DynamicField->{Name}, 160 | ); 161 | if ($DynamicFieldID->{ID}){ 162 | my $Success = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldDelete( 163 | ID => $DynamicFieldID->{ID}, 164 | UserID => 1, 165 | Reorder => 1, # or 0, to trigger reorder function, default 1 166 | ); 167 | } 168 | } 169 | 170 | # check if new field has to be created 171 | # if ($CreateDynamicField) { 172 | 173 | 174 | # create a new field 175 | my $FieldID = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldAdd( 176 | InternalField => 1, 177 | Name => $DynamicField->{Name}, 178 | Label => $DynamicField->{Label}, 179 | FieldOrder => $NextOrderNumber, 180 | FieldType => $DynamicField->{FieldType}, 181 | ObjectType => $DynamicField->{ObjectType}, 182 | Config => $DynamicField->{Config}, 183 | ValidID => $ValidID, 184 | UserID => 1, 185 | ); 186 | next DYNAMICFIELD if !$FieldID; 187 | 188 | # increase the order number 189 | $NextOrderNumber++; 190 | # } 191 | } 192 | 193 | return 1; 194 | } 195 | 196 | sub _GetITSMDynamicFieldsDefinition { 197 | my ( $Self, %Param ) = @_; 198 | 199 | # define all dynamic fields for ITSM 200 | my @DynamicFields = ( 201 | { 202 | Name => 'RocketChatLiveChatID', 203 | Label => 'RocketChatLiveChatID', 204 | FieldType => 'Text', 205 | ObjectType => 'Ticket', 206 | Config => { 207 | DefaultValue => '', 208 | }, 209 | }, 210 | ); 211 | 212 | return @DynamicFields; 213 | } 214 | 215 | sub _CreateWebServices { 216 | my ( $Self, %Param ) = @_; 217 | 218 | #Verify if it already exists 219 | my $List = $Kernel::OM->Get('Kernel::System::GenericInterface::Webservice')->WebserviceList(); 220 | my %WebServiceLookup = reverse %{$List}; 221 | my $Name = 'RocketChat'; 222 | if ( $WebServiceLookup{$Name} ) { 223 | return 1; 224 | } 225 | 226 | # if doesn't exists 227 | my $YAML = <<"_END_"; 228 | --- 229 | Debugger: 230 | DebugThreshold: debug 231 | TestMode: '0' 232 | Description: '' 233 | FrameworkVersion: 6.0.x 234 | Provider: 235 | Operation: 236 | Chat: 237 | Description: '' 238 | MappingInbound: 239 | Config: 240 | Template: "\\r\\n\\r\\n 242 | \\ \\r\\n \\r\\n \\r\\n \\r\\n \\r\\n \\r\\n 245 | \\ Raw\\r\\n Raw\\r\\n 246 | \\ Raw\\r\\n 247 | \\ Raw\\r\\n 248 | \\ Raw\\r\\n \\r\\n \\r\\n\\r\\n \\r\\n 250 | \\ \\r\\n \\r\\n 251 | \\ \\r\\n \\r\\n\\r\\n \\r\\n \\r\\n 254 | \\ \\r\\n http://172.17.0.1:3000/api/v1/livechat/message\\r\\n 255 | \\ The following ticket was created for this chat:\\\\n\%s\\r\\n 256 | \\ \\r\\n \\r\\n \\r\\n 257 | \\ \\r\\n \\r\\n 258 | \\ new\\r\\n unlock\\r\\n 259 | \\ 3 normal\\r\\n 1\\r\\n 260 | \\ 1\\r\\n \\r\\n \\r\\n 261 | \\ closed successful\\r\\n unlock\\r\\n 262 | \\ \\r\\n \\r\\n 263 | \\ \\r\\n \\r\\n \\r\\n" 265 | Type: XSLT 266 | MappingOutbound: {} 267 | Type: RocketChat::IncomingChat 268 | Transport: 269 | Config: 270 | KeepAlive: '' 271 | MaxLength: '999999999' 272 | RouteOperationMapping: 273 | Chat: 274 | RequestMethod: 275 | - GET 276 | - POST 277 | Route: / 278 | Type: HTTP::REST 279 | RemoteSystem: '' 280 | Requester: 281 | Transport: 282 | Type: '' 283 | 284 | _END_ 285 | 286 | my $Config = $Kernel::OM->Get('Kernel::System::YAML')->Load( Data => $YAML ); 287 | 288 | # add new web service 289 | my $ID = $Kernel::OM->Get('Kernel::System::GenericInterface::Webservice')->WebserviceAdd( 290 | Name => $Name, 291 | Config => $Config, 292 | ValidID => 1, 293 | UserID => 1, 294 | ); 295 | 296 | return 1; 297 | } 298 | =back 299 | 1 300 | --------------------------------------------------------------------------------