1# -- 2# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/ 3# -- 4# This software comes with ABSOLUTELY NO WARRANTY. For details, see 5# the enclosed file COPYING for license information (GPL). If you 6# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt. 7# -- 8 9use strict; 10use warnings; 11use utf8; 12 13use vars (qw($Self)); 14 15my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 16my $MainObject = $Kernel::OM->Get('Kernel::System::Main'); 17my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article'); 18my $ArticleBackendObject = $ArticleObject->BackendForChannel( ChannelName => 'Internal' ); 19 20$Kernel::OM->ObjectParamAdd( 21 'Kernel::System::UnitTest::Helper' => { 22 RestoreDatabase => 1, 23 UseTmpArticleDir => 1, 24 }, 25); 26my $Helper = $Kernel::OM->Get('Kernel::System::UnitTest::Helper'); 27 28# Create test ticket. 29my $TicketID = $Kernel::OM->Get('Kernel::System::Ticket')->TicketCreate( 30 Title => 'Some Ticket_Title', 31 QueueID => 1, 32 Lock => 'unlock', 33 Priority => '3 normal', 34 State => 'closed successful', 35 CustomerNo => '123465', 36 CustomerUser => 'customer@example.com', 37 OwnerID => 1, 38 UserID => 1, 39); 40$Self->True( 41 $TicketID, 42 'TicketCreate()', 43); 44 45my $ArticleID = $ArticleBackendObject->ArticleCreate( 46 TicketID => $TicketID, 47 SenderType => 'agent', 48 IsVisibleForCustomer => 0, 49 From => 'Some Agent <email@example.com>', 50 To => 'Some Customer <customer-a@example.com>', 51 Subject => 'some short description', 52 Body => 'the message text', 53 ContentType => 'text/plain; charset=ISO-8859-15', 54 HistoryType => 'OwnerUpdate', 55 HistoryComment => 'Some free text!', 56 UserID => 1, 57 NoAgentNotify => 1, 58); 59 60$Self->True( 61 $ArticleID, 62 'ArticleCreate()', 63); 64 65# article attachment checks 66for my $Backend (qw(DB FS)) { 67 68 # Make sure that the article backend object gets recreated for each loop. 69 $Kernel::OM->ObjectsDiscard( Objects => [ ref $ArticleBackendObject ] ); 70 71 $ConfigObject->Set( 72 Key => 'Ticket::Article::Backend::MIMEBase::ArticleStorage', 73 Value => 'Kernel::System::Ticket::Article::Backend::MIMEBase::ArticleStorage' . $Backend, 74 ); 75 76 $ArticleBackendObject = $ArticleObject->BackendForChannel( ChannelName => 'Internal' ); 77 78 $Self->Is( 79 $ArticleBackendObject->{ArticleStorageModule}, 80 'Kernel::System::Ticket::Article::Backend::MIMEBase::ArticleStorage' . $Backend, 81 'Article backend loaded the correct storage module' 82 ); 83 84 for my $File ( 85 qw(Ticket-Article-Test1.xls Ticket-Article-Test1.txt Ticket-Article-Test1.doc 86 Ticket-Article-Test1.png Ticket-Article-Test1.pdf Ticket-Article-Test-utf8-1.txt Ticket-Article-Test-utf8-1.bin Ticket-Article-Test-empty.txt) 87 ) 88 { 89 my $Location = $ConfigObject->Get('Home') . "/scripts/test/sample/Ticket/$File"; 90 my $ContentRef = $MainObject->FileRead( 91 Location => $Location, 92 Mode => 'binmode', 93 ); 94 95 for my $FileName ( 96 'SimpleFile', 97 'ÄÖÜカスタマ-', # Unicode NFC 98 'Второй_файл', # Unicode NFD 99 ) 100 { 101 my $Content = ${$ContentRef}; 102 my $FileNew = $FileName . $File; 103 my $MD5Orig = $MainObject->MD5sum( String => $Content ); 104 my $ArticleWriteAttachment = $ArticleBackendObject->ArticleWriteAttachment( 105 Content => $Content, 106 Filename => $FileNew, 107 ContentType => 'image/png', 108 ArticleID => $ArticleID, 109 UserID => 1, 110 ); 111 $Self->True( 112 $ArticleWriteAttachment, 113 "$Backend ArticleWriteAttachment() - $FileNew", 114 ); 115 116 my %AttachmentIndex = $ArticleBackendObject->ArticleAttachmentIndex( 117 ArticleID => $ArticleID, 118 ); 119 120 my $TargetFilename = $FileName . $File; 121 122 $Self->Is( 123 $AttachmentIndex{1}->{Filename}, 124 $TargetFilename, 125 "$Backend ArticleAttachmentIndex() Filename - $FileNew" 126 ); 127 128 my %Data = $ArticleBackendObject->ArticleAttachment( 129 ArticleID => $ArticleID, 130 FileID => 1, 131 ); 132 $Self->True( 133 $Data{ContentType}, 134 "$Backend ArticleAttachment() ContentType - $FileNew", 135 ); 136 $Self->True( 137 $Data{Content} eq $Content, 138 "$Backend ArticleWriteAttachment() / ArticleAttachment() - $FileNew", 139 ); 140 $Self->True( 141 $Data{ContentType} eq 'image/png', 142 "$Backend ArticleWriteAttachment() / ArticleAttachment() - $File", 143 ); 144 my $MD5New = $MainObject->MD5sum( String => $Data{Content} ); 145 $Self->Is( 146 $MD5Orig || '1', 147 $MD5New || '2', 148 "$Backend MD5 - $FileNew", 149 ); 150 my $Delete = $ArticleBackendObject->ArticleDeleteAttachment( 151 ArticleID => $ArticleID, 152 UserID => 1, 153 ); 154 $Self->True( 155 $Delete, 156 "$Backend ArticleDeleteAttachment() - $FileNew", 157 ); 158 159 %AttachmentIndex = $ArticleBackendObject->ArticleAttachmentIndex( 160 ArticleID => $ArticleID, 161 ); 162 163 $Self->IsDeeply( 164 \%AttachmentIndex, 165 {}, 166 "$Backend ArticleAttachmentIndex() after delete - $FileNew" 167 ); 168 } 169 } 170} 171 172# filename collision checks 173for my $Backend (qw(DB FS)) { 174 175 # Make sure that the article backend object gets recreated for each loop. 176 $Kernel::OM->ObjectsDiscard( Objects => [ ref $ArticleBackendObject ] ); 177 178 $ConfigObject->Set( 179 Key => 'Ticket::Article::Backend::MIMEBase::ArticleStorage', 180 Value => 'Kernel::System::Ticket::Article::Backend::MIMEBase::ArticleStorage' . $Backend, 181 ); 182 183 $ArticleBackendObject = $ArticleObject->BackendForChannel( ChannelName => 'Internal' ); 184 185 $Self->Is( 186 $ArticleBackendObject->{ArticleStorageModule}, 187 'Kernel::System::Ticket::Article::Backend::MIMEBase::ArticleStorage' . $Backend, 188 'Article backend loaded the correct storage module', 189 ); 190 191 # Store file 2 times 192 my $FileName = "[Terminology Guide äöß].pdf"; 193 my $Content = '123'; 194 my $FileNew = $FileName; 195 my $ArticleWriteAttachment = $ArticleBackendObject->ArticleWriteAttachment( 196 Content => $Content, 197 Filename => $FileNew, 198 ContentType => 'image/png', 199 ArticleID => $ArticleID, 200 UserID => 1, 201 ); 202 $Self->True( 203 $ArticleWriteAttachment, 204 "$Backend ArticleWriteAttachment() - collision check created $FileNew", 205 ); 206 207 $ArticleWriteAttachment = $ArticleBackendObject->ArticleWriteAttachment( 208 Content => $Content, 209 Filename => $FileNew, 210 ContentType => 'image/png', 211 ArticleID => $ArticleID, 212 UserID => 1, 213 ); 214 $Self->True( 215 $ArticleWriteAttachment, 216 "$Backend ArticleWriteAttachment() - collision check created $FileNew second time", 217 ); 218 219 my %AttachmentIndex = $ArticleBackendObject->ArticleAttachmentIndex( 220 ArticleID => $ArticleID, 221 ); 222 223 my $TargetFilename = '[Terminology Guide äöß]'; 224 225 if ( $Backend eq 'FS' ) { 226 $TargetFilename = '_Terminology_Guide_äöß_'; 227 } 228 229 $Self->Is( 230 scalar keys %AttachmentIndex, 231 2, 232 "$Backend ArticleWriteAttachment() - collision check number of attachments", 233 ); 234 235 my ($Entry1) = grep { $AttachmentIndex{$_}->{Filename} eq "$TargetFilename.pdf" } keys %AttachmentIndex; 236 my ($Entry2) = grep { $AttachmentIndex{$_}->{Filename} eq "$TargetFilename-1.pdf" } 237 keys %AttachmentIndex; 238 239 $Self->IsDeeply( 240 $AttachmentIndex{$Entry1}, 241 { 242 'ContentAlternative' => '', 243 'ContentID' => '', 244 'ContentType' => 'image/png', 245 'Filename' => "$TargetFilename.pdf", 246 'FilesizeRaw' => '3', 247 'Disposition' => 'attachment', 248 }, 249 "$Backend ArticleAttachmentIndex - collision check entry 1", 250 ); 251 252 $Self->IsDeeply( 253 $AttachmentIndex{$Entry2}, 254 { 255 'ContentAlternative' => '', 256 'ContentID' => '', 257 'ContentType' => 'image/png', 258 'Filename' => "$TargetFilename-1.pdf", 259 'FilesizeRaw' => '3', 260 'Disposition' => 'attachment', 261 }, 262 "$Backend ArticleAttachmentIndex - collision check entry 2", 263 ); 264 265 my $Delete = $ArticleBackendObject->ArticleDeleteAttachment( 266 ArticleID => $ArticleID, 267 UserID => 1, 268 ); 269 270 $Self->True( 271 $Delete, 272 "$Backend ArticleDeleteAttachment()", 273 ); 274 275 %AttachmentIndex = $ArticleBackendObject->ArticleAttachmentIndex( 276 ArticleID => $ArticleID, 277 ); 278 279 $Self->IsDeeply( 280 \%AttachmentIndex, 281 {}, 282 "$Backend ArticleAttachmentIndex() after delete", 283 ); 284} 285 286# cleanup is done by RestoreDatabase. 287 2881; 289