# -- # 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::Output::PDF::Ticket; use strict; use warnings; our @ObjectDependencies = ( 'Kernel::Config', 'Kernel::Output::HTML::Layout', 'Kernel::System::LinkObject', 'Kernel::System::Log', 'Kernel::System::PDF', 'Kernel::System::JSON', 'Kernel::System::User', 'Kernel::System::CustomerUser', 'Kernel::System::Ticket', 'Kernel::System::Ticket::Article', 'Kernel::System::DynamicField', 'Kernel::System::DynamicField::Backend', ); use Kernel::System::VariableCheck qw(IsHashRefWithData); sub new { my ( $Type, %Param ) = @_; # Allocate new hash for object. my $Self = {}; bless( $Self, $Type ); return $Self; } sub GeneratePDF { my ( $Self, %Param ) = @_; # Check needed params. for my $Needed (qw(TicketID UserID Interface)) { if ( !$Param{$Needed} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => "error", Message => "Need $Needed!" ); return; } } # Get appropriate interface flag. my %Interface; if ( $Param{Interface} eq 'Agent' ) { $Interface{Agent} = 1; } elsif ( $Param{Interface} eq 'Customer' ) { $Interface{Customer} = 1; $Interface{IsVisibleForCustomer} = { IsVisibleForCustomer => 1, }; } # Get ticket content. my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); my %Ticket = $TicketObject->TicketGet( TicketID => $Param{TicketID}, DynamicFields => 0, UserID => $Param{UserID}, ); # Get article list. my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article'); my @MetaArticles = $ArticleObject->ArticleList( TicketID => $Ticket{TicketID}, UserID => $Param{UserID}, %{ $Interface{IsVisibleForCustomer} }, ); # Check if only one article should be printed in agent interface. if ( $Param{ArticleID} ) { @MetaArticles = grep { $_->{ArticleID} == $Param{ArticleID} } @MetaArticles; } # Get article content. my @ArticleBox; for my $MetaArticle (@MetaArticles) { my $ArticleBackendObject = $ArticleObject->BackendForArticle( %{$MetaArticle} ); my %Article = $ArticleBackendObject->ArticleGet( %{$MetaArticle}, DynamicFields => 0, ); my %Attachments = $ArticleBackendObject->ArticleAttachmentIndex( %{$MetaArticle}, ExcludePlainText => 1, ExcludeHTMLBody => 1, ExcludeInline => 1, ); $Article{Atms} = \%Attachments; push @ArticleBox, \%Article; } my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); my $LinkObject = $Kernel::OM->Get('Kernel::System::LinkObject'); # Get necessary data only for agent interface. my %TicketInfo; my %LinkData; if ( $Interface{Agent} ) { # Show total accounted time if feature is active. if ( $ConfigObject->Get('Ticket::Frontend::AccountTime') ) { $Ticket{TicketTimeUnits} = $TicketObject->TicketAccountedTimeGet( TicketID => $Ticket{TicketID}, ); } # Get user data. my $UserObject = $Kernel::OM->Get('Kernel::System::User'); my %User = $UserObject->GetUserData( UserID => $Param{UserID}, ); $TicketInfo{User} = \%User; # Get owner info. my %Owner = $UserObject->GetUserData( User => $Ticket{Owner}, ); $TicketInfo{Owner} = \%Owner; # Get responsible info. if ( $ConfigObject->Get('Ticket::Responsible') && $Ticket{Responsible} ) { my %Responsible = $UserObject->GetUserData( User => $Ticket{Responsible}, ); $TicketInfo{Responsible} = \%Responsible; } # Get link ticket data. my $LinkListWithData = $LinkObject->LinkListWithData( Object => 'Ticket', Key => $Param{TicketID}, State => 'Valid', UserID => $Param{UserID}, ObjectParameters => { Ticket => { IgnoreLinkedTicketStateTypes => 1, }, }, ); # Get the link data. if ( $LinkListWithData && ref $LinkListWithData eq 'HASH' && %{$LinkListWithData} ) { %LinkData = $LayoutObject->LinkObjectTableCreate( LinkListWithData => $LinkListWithData, ViewMode => 'SimpleRaw', ); } } # Get customer info. my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser'); my %CustomerData; if ( $Ticket{CustomerUserID} ) { %CustomerData = $CustomerUserObject->CustomerUserDataGet( User => $Ticket{CustomerUserID}, ); } elsif ( $Ticket{CustomerID} ) { %CustomerData = $CustomerUserObject->CustomerUserDataGet( CustomerID => $Ticket{CustomerID}, ); } # Transform time values into human readable form. $Ticket{Age} = $LayoutObject->CustomerAge( Age => $Ticket{Age}, Space => ' ', ); if ( $Ticket{UntilTime} ) { $Ticket{PendingUntil} = $LayoutObject->CustomerAge( Age => $Ticket{UntilTime}, Space => ' ', ); } # Get maximum number of pages. my %Page; $Page{MaxPages} = $ConfigObject->Get('PDF::MaxPages'); if ( !$Page{MaxPages} || $Page{MaxPages} < 1 || $Page{MaxPages} > 1000 ) { $Page{MaxPages} = 100; } my $HeaderRight = $ConfigObject->Get('Ticket::Hook') . $Ticket{TicketNumber}; my $HeadlineLeft = $HeaderRight; my $Title = $HeaderRight; if ( $Ticket{Title} ) { $HeadlineLeft = $Ticket{Title}; $Title .= ' / ' . $Ticket{Title}; } $Page{MarginTop} = 30; $Page{MarginRight} = 40; $Page{MarginBottom} = 40; $Page{MarginLeft} = 40; $Page{HeaderRight} = $HeaderRight; $Page{FooterLeft} = ''; $Page{PageText} = $LayoutObject->{LanguageObject}->Translate('Page'); $Page{PageCount} = 1; # Create new PDF document. my $PDFObject = $Kernel::OM->Get('Kernel::System::PDF'); $PDFObject->DocumentNew( Title => $ConfigObject->Get('Product') . ': ' . $Title, Encode => $LayoutObject->{UserCharset}, ); # Create first PDF page. $PDFObject->PageNew( %Page, FooterRight => $Page{PageText} . ' ' . $Page{PageCount}, ); $Page{PageCount}++; $PDFObject->PositionSet( Move => 'relativ', Y => -6, ); # Output title. $PDFObject->Text( Text => $Ticket{Title}, FontSize => 13, ); $PDFObject->PositionSet( Move => 'relativ', Y => -6, ); # Output "printed by". my $PrintedBy = $LayoutObject->{LanguageObject}->Translate('printed by'); my $DateTimeString = $Kernel::OM->Create('Kernel::System::DateTime')->ToString(); my $Time = $LayoutObject->{LanguageObject}->FormatTimeString( $DateTimeString, 'DateFormat', ); if ( $Interface{Agent} ) { $PrintedBy .= ' ' . $TicketInfo{User}{UserFullname} . ' (' . $TicketInfo{User}{UserEmail} . ')' . ', ' . $Time; } elsif ( $Interface{Customer} ) { $PrintedBy .= ' ' . $CustomerData{UserFullname} . ' (' . $CustomerData{UserEmail} . ')' . ', ' . $Time; } $PDFObject->Text( Text => $PrintedBy, FontSize => 9, ); $PDFObject->PositionSet( Move => 'relativ', Y => -14, ); # Output ticket infos. $Self->_PDFOutputTicketInfos( PageData => \%Page, TicketData => \%Ticket, OwnerData => $TicketInfo{Owner}, ResponsibleData => $TicketInfo{Responsible}, Interface => \%Interface, ); $PDFObject->PositionSet( Move => 'relativ', Y => -6, ); # Output ticket dynamic fields. $Self->_PDFOutputTicketDynamicFields( PageData => \%Page, TicketData => \%Ticket, Interface => \%Interface, ); $PDFObject->PositionSet( Move => 'relativ', Y => -6, ); # Output linked objects. if (%LinkData) { # Get link type list. my %LinkTypeList = $LinkObject->TypeList( UserID => $Param{UserID}, ); $Self->_PDFOutputLinkedObjects( PageData => \%Page, LinkData => \%LinkData, LinkTypeList => \%LinkTypeList, ); } # Output customer infos. if (%CustomerData) { $Self->_PDFOutputCustomerInfos( PageData => \%Page, CustomerData => \%CustomerData, ); } # Output articles. $Self->_PDFOutputArticles( PageData => \%Page, ArticleData => \@ArticleBox, ArticleNumber => $Param{ArticleNumber}, Interface => \%Interface, ); # Return the PDF document. return $PDFObject->DocumentOutput(); } sub _PDFOutputTicketInfos { my ( $Self, %Param ) = @_; # Check needed stuff for both interfaces. for my $Needed (qw(PageData TicketData Interface)) { if ( !defined( $Param{$Needed} ) ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Needed!" ); return; } } # Check needed param for agent interface. if ( $Param{Interface}{Agent} && !defined( $Param{OwnerData} ) ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need OwnerData!" ); return; } my %Ticket = %{ $Param{TicketData} }; my %Page = %{ $Param{PageData} }; # Get needed objects. my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); # Create left table. my $TableLeft = []; my $CustomerConfig; if ( $Param{Interface}{Customer} ) { $CustomerConfig = $ConfigObject->Get("Ticket::Frontend::CustomerTicketZoom"); } # Add ticket data, respecting AttributesView configuration for customer interface. for my $Attribute (qw(State Priority Queue Lock CustomerID)) { if ( $Param{Interface}{Agent} || $CustomerConfig->{AttributesView}->{$Attribute} ) { my $Row = { Key => $LayoutObject->{LanguageObject}->Translate($Attribute), Value => $LayoutObject->{LanguageObject}->Translate( $Ticket{$Attribute} ) || $Ticket{$Attribute} || '-', }; push( @{$TableLeft}, $Row ); } } # Add Owner data, different output between interfaces. if ( $Param{Interface}{Agent} ) { my $OwnerRow = { Key => $LayoutObject->{LanguageObject}->Translate('Owner'), Value => $Ticket{Owner} . ' (' . $Param{OwnerData}->{UserFullname} . ')', }; push( @{$TableLeft}, $OwnerRow ); } elsif ( $Param{Interface}{Customer} && $CustomerConfig->{AttributesView}->{Owner} ) { my $OwnerRow = { Key => $LayoutObject->{LanguageObject}->Translate('Owner'), Value => $Ticket{Owner}, }; push( @{$TableLeft}, $OwnerRow ); } # Add Responsible row, if feature is enabled. if ( $ConfigObject->Get('Ticket::Responsible') ) { my $Responsible = '-'; if ( $Param{Interface}{Agent} && $Ticket{Responsible} ) { $Responsible = $Ticket{Responsible} . ' (' . $Param{ResponsibleData}->{UserFullname} . ')'; } elsif ( $Param{Interface}{Customer} && $CustomerConfig->{AttributesView}->{Responsible} && $Ticket{Responsible} ) { $Responsible = $Ticket{Responsible}; } my $Row = { Key => $LayoutObject->{LanguageObject}->Translate('Responsible'), Value => $Responsible, }; push( @{$TableLeft}, $Row ); } # Add Type row, if feature is enabled. if ( $ConfigObject->Get('Ticket::Type') && ( $Param{Interface}{Agent} || $CustomerConfig->{AttributesView}->{Type} ) ) { my $Row = { Key => $LayoutObject->{LanguageObject}->Translate('Type'), Value => $Ticket{Type}, }; push( @{$TableLeft}, $Row ); } # Add Service and SLA row, if feature is enabled. if ( $ConfigObject->Get('Ticket::Service') ) { if ( $Param{Interface}{Agent} || $CustomerConfig->{AttributesView}->{Service} ) { my $RowService = { Key => $LayoutObject->{LanguageObject}->Translate('Service'), Value => $Ticket{Service} || '-', }; push( @{$TableLeft}, $RowService ); } if ( $Param{Interface}{Agent} || $CustomerConfig->{AttributesView}->{SLA} ) { my $RowSLA = { Key => $LayoutObject->{LanguageObject}->Translate('SLA'), Value => $Ticket{SLA} || '-', }; push( @{$TableLeft}, $RowSLA ); } } # Create right table. my $TableRight = [ { Key => $LayoutObject->{LanguageObject}->Translate('Age'), Value => $LayoutObject->{LanguageObject}->Translate( $Ticket{Age} ), }, { Key => $LayoutObject->{LanguageObject}->Translate('Created'), Value => $LayoutObject->{LanguageObject}->FormatTimeString( $Ticket{Created}, 'DateFormat', ), }, ]; if ( $Param{Interface}{Customer} ) { unshift( @{$TableRight}, { Key => $LayoutObject->{LanguageObject}->Translate('CustomerID'), Value => $Ticket{CustomerID}, } ); } elsif ( $Param{Interface}{Agent} ) { # Show created by if different then User ID 1. if ( $Ticket{CreateBy} > 1 ) { my $Row = { Key => $LayoutObject->{LanguageObject}->Translate('Created by'), Value => $Kernel::OM->Get('Kernel::System::User')->UserName( UserID => $Ticket{CreateBy} ), }; push( @{$TableRight}, $Row ); } # Show accounted time. if ( $ConfigObject->Get('Ticket::Frontend::AccountTime') ) { my $Row = { Key => $LayoutObject->{LanguageObject}->Translate('Accounted time'), Value => $Ticket{TicketTimeUnits}, }; push( @{$TableRight}, $Row ); } # Only show pending until unless it is really pending. if ( $Ticket{PendingUntil} ) { my $Row = { Key => $LayoutObject->{LanguageObject}->Translate('Pending till'), Value => $Ticket{PendingUntil}, }; push( @{$TableRight}, $Row ); } # Add first response time row. if ( defined( $Ticket{FirstResponseTime} ) ) { my $Row = { Key => $LayoutObject->{LanguageObject}->Translate('First Response Time'), Value => $LayoutObject->{LanguageObject}->FormatTimeString( $Ticket{FirstResponseTimeDestinationDate}, 'DateFormat', 'NoSeconds', ), }; push( @{$TableRight}, $Row ); } # Add update time row. if ( defined( $Ticket{UpdateTime} ) ) { my $Row = { Key => $LayoutObject->{LanguageObject}->Translate('Update Time'), Value => $LayoutObject->{LanguageObject}->FormatTimeString( $Ticket{UpdateTimeDestinationDate}, 'DateFormat', 'NoSeconds', ), }; push( @{$TableRight}, $Row ); } # Add solution time row. if ( defined( $Ticket{SolutionTime} ) ) { my $Row = { Key => $LayoutObject->{LanguageObject}->Translate('Solution Time'), Value => $LayoutObject->{LanguageObject}->FormatTimeString( $Ticket{SolutionTimeDestinationDate}, 'DateFormat', 'NoSeconds', ), }; push( @{$TableRight}, $Row ); } } my $Rows = @{$TableLeft}; if ( @{$TableRight} > $Rows ) { $Rows = @{$TableRight}; } my %TableParam; for my $Row ( 1 .. $Rows ) { $Row--; $TableParam{CellData}[$Row][0]{Content} = $TableLeft->[$Row]->{Key}; $TableParam{CellData}[$Row][0]{Font} = 'ProportionalBold'; $TableParam{CellData}[$Row][1]{Content} = $TableLeft->[$Row]->{Value}; $TableParam{CellData}[$Row][2]{Content} = ' '; $TableParam{CellData}[$Row][2]{BackgroundColor} = '#FFFFFF'; $TableParam{CellData}[$Row][3]{Content} = $TableRight->[$Row]->{Key}; $TableParam{CellData}[$Row][3]{Font} = 'ProportionalBold'; $TableParam{CellData}[$Row][4]{Content} = $TableRight->[$Row]->{Value}; } $TableParam{ColumnData}[0]{Width} = 70; $TableParam{ColumnData}[1]{Width} = 156.5; $TableParam{ColumnData}[2]{Width} = 1; $TableParam{ColumnData}[3]{Width} = 70; $TableParam{ColumnData}[4]{Width} = 156.5; $TableParam{Type} = 'Cut'; $TableParam{Border} = 0; $TableParam{FontSize} = 7; $TableParam{BackgroundColorEven} = '#F2F2F2'; $TableParam{Padding} = 6; $TableParam{PaddingTop} = 3; $TableParam{PaddingBottom} = 3; # Output table. my $PDFObject = $Kernel::OM->Get('Kernel::System::PDF'); PAGE: for ( $Page{PageCount} .. $Page{MaxPages} ) { # Output table (or a fragment of it). %TableParam = $PDFObject->Table( %TableParam, ); # Stop output or output next page. if ( $TableParam{State} ) { last PAGE; } else { $PDFObject->PageNew( %Page, FooterRight => $Page{PageText} . ' ' . $Page{PageCount}, ); $Page{PageCount}++; } } return 1; } sub _PDFOutputLinkedObjects { my ( $Self, %Param ) = @_; # Check needed stuff. for my $Needed (qw(PageData LinkData LinkTypeList)) { if ( !defined( $Param{$Needed} ) ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Needed!" ); return; } } my %Page = %{ $Param{PageData} }; my %TypeList = %{ $Param{LinkTypeList} }; my %TableParam; my $Row = 0; # Get needed objects. my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); my $PDFObject = $Kernel::OM->Get('Kernel::System::PDF'); for my $LinkTypeLinkDirection ( sort { lc $a cmp lc $b } keys %{ $Param{LinkData} } ) { # Investigate link type name. my @LinkData = split q{::}, $LinkTypeLinkDirection; my $LinkTypeName = $TypeList{ $LinkData[0] }->{ $LinkData[1] . 'Name' }; $LinkTypeName = $LayoutObject->{LanguageObject}->Translate($LinkTypeName); # Define headline. $TableParam{CellData}[$Row][0]{Content} = $LinkTypeName . ':'; $TableParam{CellData}[$Row][0]{Font} = 'ProportionalBold'; $TableParam{CellData}[$Row][1]{Content} = ''; # Extract object list. my $ObjectList = $Param{LinkData}->{$LinkTypeLinkDirection}; for my $Object ( sort { lc $a cmp lc $b } keys %{$ObjectList} ) { for my $Item ( @{ $ObjectList->{$Object} } ) { $TableParam{CellData}[$Row][0]{Content} ||= ''; $TableParam{CellData}[$Row][1]{Content} = $Item->{Title} || ''; } continue { $Row++; } } } $TableParam{ColumnData}[0]{Width} = 80; $TableParam{ColumnData}[1]{Width} = 431; $PDFObject->HLine( Color => '#aaa', LineWidth => 0.5, ); # Set new position. $PDFObject->PositionSet( Move => 'relativ', Y => -10, ); # Output headline. $PDFObject->Text( Text => $LayoutObject->{LanguageObject}->Translate('Linked Objects'), Height => 10, Type => 'Cut', Font => 'Proportional', FontSize => 9, Color => '#666666', ); # Set new position. $PDFObject->PositionSet( Move => 'relativ', Y => -4, ); # Table params. $TableParam{Type} = 'Cut'; $TableParam{Border} = 0; $TableParam{FontSize} = 6; $TableParam{Padding} = 1; $TableParam{PaddingTop} = 3; $TableParam{PaddingBottom} = 3; # Output table. PAGE: for ( $Page{PageCount} .. $Page{MaxPages} ) { # Output table (or a fragment of it). %TableParam = $PDFObject->Table( %TableParam, ); # Stop output or output next page. if ( $TableParam{State} ) { last PAGE; } else { $PDFObject->PageNew( %Page, FooterRight => $Page{PageText} . ' ' . $Page{PageCount}, ); $Page{PageCount}++; } } return 1; } sub _PDFOutputTicketDynamicFields { my ( $Self, %Param ) = @_; # Check needed stuff. for my $Needed (qw(PageData TicketData)) { if ( !defined( $Param{$Needed} ) ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Needed!" ); return; } } my $Output = 0; my %Ticket = %{ $Param{TicketData} }; my %Page = %{ $Param{PageData} }; my %TableParam; my $Row = 0; # Get dynamic field config for appropriate frontend module. my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); my $DynamicFieldFilter; if ( $Param{Interface}{Agent} ) { $DynamicFieldFilter = $ConfigObject->Get("Ticket::Frontend::AgentTicketPrint")->{DynamicField}; } elsif ( $Param{Interface}{Customer} ) { $DynamicFieldFilter = $ConfigObject->Get("Ticket::Frontend::CustomerTicketPrint")->{DynamicField}; } # Get the dynamic fields for ticket object. my $DynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( Valid => 1, ObjectType => ['Ticket'], FieldFilter => $DynamicFieldFilter || {}, ); # Get needed objects. my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # Generate table, cycle trough the activated Dynamic Fields for ticket object. DYNAMICFIELD: for my $DynamicFieldConfig ( @{$DynamicField} ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); if ( $Param{Interface}{Customer} ) { # Skip dynamic field if is not designed for customer interface. my $IsCustomerInterfaceCapable = $DynamicFieldBackendObject->HasBehavior( DynamicFieldConfig => $DynamicFieldConfig, Behavior => 'IsCustomerInterfaceCapable', ); next DYNAMICFIELD if !$IsCustomerInterfaceCapable; } my $Value = $DynamicFieldBackendObject->ValueGet( DynamicFieldConfig => $DynamicFieldConfig, ObjectID => $Ticket{TicketID}, ); next DYNAMICFIELD if !$Value; next DYNAMICFIELD if $Value eq ""; # Get print string for this dynamic field. my $ValueStrg = $DynamicFieldBackendObject->DisplayValueRender( DynamicFieldConfig => $DynamicFieldConfig, Value => $Value, HTMLOutput => 0, LayoutObject => $LayoutObject, ); $TableParam{CellData}[$Row][0]{Content} = $LayoutObject->{LanguageObject}->Translate( $DynamicFieldConfig->{Label} ) . ':'; $TableParam{CellData}[$Row][0]{Font} = 'ProportionalBold'; $TableParam{CellData}[$Row][1]{Content} = $ValueStrg->{Value}; $Row++; $Output = 1; } $TableParam{ColumnData}[0]{Width} = 80; $TableParam{ColumnData}[1]{Width} = 431; # Output ticket dynamic fields. if ($Output) { my $PDFObject = $Kernel::OM->Get('Kernel::System::PDF'); $PDFObject->HLine( Color => '#aaa', LineWidth => 0.5, ); # Set new position. $PDFObject->PositionSet( Move => 'relativ', Y => -10, ); # Output headline. $PDFObject->Text( Text => $LayoutObject->{LanguageObject}->Translate('Ticket Dynamic Fields'), Height => 10, Type => 'Cut', Font => 'Proportional', FontSize => 8, Color => '#666666', ); # Set new position. $PDFObject->PositionSet( Move => 'relativ', Y => -4, ); # Table params. $TableParam{Type} = 'Cut'; $TableParam{Border} = 0; $TableParam{FontSize} = 6; $TableParam{Padding} = 1; $TableParam{PaddingTop} = 3; $TableParam{PaddingBottom} = 3; # Output table. PAGE: for ( $Page{PageCount} .. $Page{MaxPages} ) { # Output table (or a fragment of it). %TableParam = $PDFObject->Table( %TableParam, ); # Stop output or output next page. if ( $TableParam{State} ) { last PAGE; } else { $PDFObject->PageNew( %Page, FooterRight => $Page{PageText} . ' ' . $Page{PageCount}, ); $Page{PageCount}++; } } } return 1; } sub _PDFOutputCustomerInfos { my ( $Self, %Param ) = @_; # Check needed stuff. for my $Needed (qw(PageData CustomerData)) { if ( !defined( $Param{$Needed} ) ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Needed!" ); return; } } my $Output = 0; my %CustomerData = %{ $Param{CustomerData} }; my %Page = %{ $Param{PageData} }; my %TableParam; my $Row = 0; my $Map = $CustomerData{Config}->{Map}; # Check if customer company support is enabled. if ( $CustomerData{Config}->{CustomerCompanySupport} ) { my $Map2 = $CustomerData{CompanyConfig}->{Map}; if ($Map2) { push( @{$Map}, @{$Map2} ); } } my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); for my $Field ( @{$Map} ) { if ( ${$Field}[3] && $CustomerData{ ${$Field}[0] } ) { $TableParam{CellData}[$Row][0]{Content} = $LayoutObject->{LanguageObject}->Translate( ${$Field}[1] ) . ':'; $TableParam{CellData}[$Row][0]{Font} = 'ProportionalBold'; $TableParam{CellData}[$Row][1]{Content} = $CustomerData{ ${$Field}[0] }; $Row++; $Output = 1; } } $TableParam{ColumnData}[0]{Width} = 80; $TableParam{ColumnData}[1]{Width} = 431; if ($Output) { my $PDFObject = $Kernel::OM->Get('Kernel::System::PDF'); # Set new position. $PDFObject->PositionSet( Move => 'relativ', Y => -10, ); $PDFObject->HLine( Color => '#aaa', LineWidth => 0.5, ); # Set new position. $PDFObject->PositionSet( Move => 'relativ', Y => -4, ); # Output headline. $PDFObject->Text( Text => $LayoutObject->{LanguageObject}->Translate('Customer Information'), Height => 10, Type => 'Cut', Font => 'Proportional', FontSize => 8, Color => '#666666', ); # Set new position. $PDFObject->PositionSet( Move => 'relativ', Y => -4, ); # Table params. $TableParam{Type} = 'Cut'; $TableParam{Border} = 0; $TableParam{FontSize} = 6; $TableParam{Padding} = 1; $TableParam{PaddingTop} = 3; $TableParam{PaddingBottom} = 3; # Output table. PAGE: for ( $Page{PageCount} .. $Page{MaxPages} ) { # Output table (or a fragment of it). %TableParam = $PDFObject->Table( %TableParam, ); # Stop output or output next page. if ( $TableParam{State} ) { last PAGE; } else { $PDFObject->PageNew( %Page, FooterRight => $Page{PageText} . ' ' . $Page{PageCount}, ); $Page{PageCount}++; } } } return 1; } sub _PDFOutputArticles { my ( $Self, %Param ) = @_; # Check needed stuff. for my $Needed (qw(PageData ArticleData)) { if ( !defined( $Param{$Needed} ) ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Needed!" ); return; } } my %Page = %{ $Param{PageData} }; # Get needed objects. my $PDFObject = $Kernel::OM->Get('Kernel::System::PDF'); my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article'); my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); my @ArticleData = @{ $Param{ArticleData} }; my $ArticleCount = scalar @ArticleData; # Get config settings. my $ZoomExpandSort = $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Frontend::ZoomExpandSort'); # Resort article order. if ( $Param{Interface}{Agent} && $ZoomExpandSort eq 'reverse' ) { @ArticleData = reverse(@ArticleData); } my $ArticleCounter = 1; for my $ArticleTmp (@ArticleData) { my %Article = %{$ArticleTmp}; # Get attachment string. my %AtmIndex = (); if ( $Article{Atms} ) { %AtmIndex = %{ $Article{Atms} }; } my $Attachments; for my $FileID ( sort keys %AtmIndex ) { my %File = %{ $AtmIndex{$FileID} }; my $Filesize = $LayoutObject->HumanReadableDataSize( Size => $File{FilesizeRaw} ); $Attachments .= $File{Filename} . ' (' . $Filesize . ")\n"; } # Show total accounted time if feature is active. if ( $Param{Interface}{Agent} && $ConfigObject->Get('Ticket::Frontend::AccountTime') ) { $Article{'Accounted time'} = $ArticleObject->ArticleAccountedTimeGet( ArticleID => $Article{ArticleID}, ); } # Generate article info table. my %TableParam1; my $Row = 0; $PDFObject->PositionSet( Move => 'relativ', Y => -6, ); # Get article number. my $ArticleNumber; if ( $Param{ArticleNumber} ) { $ArticleNumber = $Param{ArticleNumber}; } else { $ArticleNumber = $ZoomExpandSort eq 'reverse' ? $ArticleCount - $ArticleCounter + 1 : $ArticleCounter; } if ( $Param{Interface}{Customer} ) { $ArticleNumber = $ArticleCounter; } # Article number tag. $PDFObject->Text( Text => $LayoutObject->{LanguageObject}->Translate('Article') . ' #' . $ArticleNumber, Height => 10, Type => 'Cut', Font => 'Proportional', FontSize => 8, Color => '#666666', ); $PDFObject->PositionSet( Move => 'relativ', Y => 2, ); my %ArticleFields = $LayoutObject->ArticleFields(%Article); # Display article fields. ARTICLE_FIELD: for my $ArticleFieldKey ( sort { $ArticleFields{$a}->{Prio} <=> $ArticleFields{$b}->{Prio} } keys %ArticleFields ) { my %ArticleField = %{ $ArticleFields{$ArticleFieldKey} // {} }; next ARTICLE_FIELD if $Param{Interface}->{Customer} && $ArticleField{HideInCustomerInterface}; next ARTICLE_FIELD if $ArticleField{HideInTicketPrint}; next ARTICLE_FIELD if !$ArticleField{Value}; $TableParam1{CellData}[$Row][0]{Content} = $LayoutObject->{LanguageObject}->Translate( $ArticleField{Label} ) . ':'; $TableParam1{CellData}[$Row][0]{Font} = 'ProportionalBold'; $TableParam1{CellData}[$Row][1]{Content} = $ArticleField{Value}; $Row++; } # Display article accounted time. if ( $Param{Interface}{Agent} ) { my $ArticleTime = $ArticleObject->ArticleAccountedTimeGet( ArticleID => $Article{ArticleID}, ); if ($ArticleTime) { $TableParam1{CellData}[$Row][0]{Content} = $LayoutObject->{LanguageObject}->Translate('Accounted time') . ':'; $TableParam1{CellData}[$Row][0]{Font} = 'ProportionalBold'; $TableParam1{CellData}[$Row][1]{Content} = $ArticleTime; $Row++; } } $TableParam1{CellData}[$Row][0]{Content} = $LayoutObject->{LanguageObject}->Translate('Created') . ':'; $TableParam1{CellData}[$Row][0]{Font} = 'ProportionalBold'; $TableParam1{CellData}[$Row][1]{Content} = $LayoutObject->{LanguageObject}->FormatTimeString( $Article{CreateTime}, 'DateFormat', ); $TableParam1{CellData}[$Row][1]{Content} .= ' ' . $LayoutObject->{LanguageObject}->Translate('by'); my $SenderType = $ArticleObject->ArticleSenderTypeLookup( SenderTypeID => $Article{SenderTypeID}, ); $TableParam1{CellData}[$Row][1]{Content} .= ' ' . $LayoutObject->{LanguageObject}->Translate($SenderType); $Row++; # Get dynamic field config for appropriate frontend module. my $DynamicFieldFilter; if ( $Param{Interface}{Agent} ) { $DynamicFieldFilter = $ConfigObject->Get("Ticket::Frontend::AgentTicketPrint")->{DynamicField}; } elsif ( $Param{Interface}{Customer} ) { $DynamicFieldFilter = $ConfigObject->Get("Ticket::Frontend::CustomerTicketPrint")->{DynamicField}; } # Get the dynamic fields for ticket object. my $DynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( Valid => 1, ObjectType => ['Article'], FieldFilter => $DynamicFieldFilter || {}, ); # Generate table, cycle trough the activated Dynamic Fields for ticket object. DYNAMICFIELD: for my $DynamicFieldConfig ( @{$DynamicField} ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); if ( $Param{Interface}{Customer} ) { # Skip the dynamic field if it is not designed for customer interface. my $IsCustomerInterfaceCapable = $DynamicFieldBackendObject->HasBehavior( DynamicFieldConfig => $DynamicFieldConfig, Behavior => 'IsCustomerInterfaceCapable', ); next DYNAMICFIELD if !$IsCustomerInterfaceCapable; } my $Value = $DynamicFieldBackendObject->ValueGet( DynamicFieldConfig => $DynamicFieldConfig, ObjectID => $Article{ArticleID}, ); next DYNAMICFIELD if !$Value; next DYNAMICFIELD if $Value eq ""; # Get print string for this dynamic field. my $ValueStrg = $DynamicFieldBackendObject->DisplayValueRender( DynamicFieldConfig => $DynamicFieldConfig, Value => $Value, HTMLOutput => 0, LayoutObject => $LayoutObject, ); $TableParam1{CellData}[$Row][0]{Content} = $LayoutObject->{LanguageObject}->Translate( $DynamicFieldConfig->{Label} ) . ':'; $TableParam1{CellData}[$Row][0]{Font} = 'ProportionalBold'; $TableParam1{CellData}[$Row][1]{Content} = $ValueStrg->{Value}; $Row++; } if ($Attachments) { $TableParam1{CellData}[$Row][0]{Content} = $LayoutObject->{LanguageObject}->Translate('Attachment') . ':'; $TableParam1{CellData}[$Row][0]{Font} = 'ProportionalBold'; chomp($Attachments); $TableParam1{CellData}[$Row][1]{Content} = $Attachments; } $TableParam1{ColumnData}[0]{Width} = 80; $TableParam1{ColumnData}[1]{Width} = 431; $PDFObject->PositionSet( Move => 'relativ', Y => -6, ); $PDFObject->HLine( Color => '#aaa', LineWidth => 0.5, ); $PDFObject->PositionSet( Move => 'relativ', Y => -6, ); # Table params (article infos). $TableParam1{Type} = 'Cut'; $TableParam1{Border} = 0; $TableParam1{FontSize} = 6; $TableParam1{Padding} = 1; $TableParam1{PaddingTop} = 3; $TableParam1{PaddingBottom} = 3; # Output table (article infos). PAGE: for ( $Page{PageCount} .. $Page{MaxPages} ) { # Output table (or a fragment of it). %TableParam1 = $PDFObject->Table( %TableParam1, ); # Stop output or output next page. if ( $TableParam1{State} ) { last PAGE; } else { $PDFObject->PageNew( %Page, FooterRight => $Page{PageText} . ' ' . $Page{PageCount}, ); $Page{PageCount}++; } } my $ArticlePreview = $LayoutObject->ArticlePreview( %Article, ResultType => 'plain', ); # Table params (article body). my %TableParam2; $TableParam2{CellData}[0][0]{Content} = $ArticlePreview || ' '; $TableParam2{Type} = 'Cut'; $TableParam2{Border} = 0; $TableParam2{Font} = 'Monospaced'; $TableParam2{FontSize} = 7; $TableParam2{BackgroundColor} = '#f2f2f2'; $TableParam2{Padding} = 4; $TableParam2{PaddingTop} = 4; $TableParam2{PaddingBottom} = 4; # Output table (article body). PAGE: for ( $Page{PageCount} .. $Page{MaxPages} ) { # Output table (or a fragment of it). %TableParam2 = $PDFObject->Table( %TableParam2, ); # Stop output or output next page. if ( $TableParam2{State} ) { last PAGE; } else { $PDFObject->PageNew( %Page, FooterRight => $Page{PageText} . ' ' . $Page{PageCount}, ); $Page{PageCount}++; } } $ArticleCounter++; } return 1; } 1;