Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision a40f0c2f

Von Martin Helmling martin.helmling@octosoft.eu vor fast 8 Jahren hinzugefügt

  • ID a40f0c2f7523a7d6936ca483901c22dfe86358c9
  • Vorgänger bd4debfb
  • Nachfolger 58c266ea

Dateimanagement: Alle Anhänge per E-Mail versendbar machen

Es können an eine E-Mail alle Anhänge eines Dokumentes,
sowie die Anhänge am Kunden/Liefranten sowie die Anhänge an Artikeln
mitgesendet werden.

Falls ein Dokument bereits existiert muss es nicht noch neu erzeugt werden.

Als MIME Types werden die bereits in der Datenbank abgespeicherten Typen verwendet.

Es werden in Perl nun MIME::Entity und MIME::Parser verwendet,
deshalb ist der installationcheck erweitet.

Unterschiede anzeigen:

SL/Controller/EmailJournal.pm
60 60
  if (!$self->can_view_all && ($attachment->email_journal->sender_id != SL::DB::Manager::Employee->current->id)) {
61 61
    $::form->error(t8('You do not have permission to access this entry.'));
62 62
  }
63

  
64
  $self->send_file(\$attachment->content, name => $attachment->name, type => $attachment->mime_type);
63
  my $ref = \$attachment->content;
64
  if ( $attachment->file_id > 0 ) {
65
    my $file = SL::File->get(id => $attachment->file_id );
66
    $ref = SL::File->get_content(dbfile => $file) if $file;
67
  }
68
  $self->send_file($ref, name => $attachment->name, type => $attachment->mime_type);
65 69
}
66 70

  
67 71
#
SL/DB/MetaSetup/EmailJournalAttachment.pm
11 11
__PACKAGE__->meta->columns(
12 12
  content          => { type => 'bytea', not_null => 1 },
13 13
  email_journal_id => { type => 'integer', not_null => 1 },
14
  file_id          => { type => 'integer', default => '0', not_null => 1 },
14 15
  id               => { type => 'serial', not_null => 1 },
15 16
  itime            => { type => 'timestamp', default => 'now()', not_null => 1 },
16 17
  mime_type        => { type => 'text', not_null => 1 },
SL/Form.pm
1102 1102

  
1103 1103
  if ( !$self->{preview} && $ext_for_format eq 'pdf' && $::instance_conf->get_doc_storage) {
1104 1104
    $self->{attachment_filename} ||= $self->generate_attachment_filename;
1105
    $self->store_pdf($self);
1105
    $self->{print_file_id} = $self->store_pdf($self);
1106 1106
  }
1107 1107
  if ($self->{media} eq 'email') {
1108
    if ( getcwd() eq $self->{"tmpdir"} ) {
1109
      # in the case of generating pdf we are in the tmpdir, but WHY ???
1110
      $self->{tmpfile} = $userspath."/".$self->{tmpfile};
1111
      chdir("$self->{cwd}");
1112
    }
1113
    $self->send_email(\%::myconfig,$ext_for_format);
1114
  }
1115
  else {
1116
    $self->{OUT}      = $out;
1117
    $self->{OUT_MODE} = $out_mode;
1118
    $self->output_file($template->get_mime_type,$command_formatter);
1119
  }
1120
  delete $self->{print_file_id};
1108 1121

  
1109
    my $mail = Mailer->new;
1110

  
1111
    map { $mail->{$_} = $self->{$_} }
1112
      qw(cc bcc subject message version format);
1113
    $mail->{to} = $self->{EMAIL_RECIPIENT} ? $self->{EMAIL_RECIPIENT} : $self->{email};
1114
    $mail->{from}   = qq|"$myconfig->{name}" <$myconfig->{email}>|;
1115
    $mail->{fileid} = time() . '.' . $$ . '.';
1116
    my $full_signature     =  $self->create_email_signature();
1117
    $full_signature        =~ s/\r//g;
1118

  
1119
    # if we send html or plain text inline
1120
    if (($self->{format} eq 'html') && ($self->{sendmode} eq 'inline')) {
1121
      $mail->{contenttype}    =  "text/html";
1122
      $mail->{message}        =~ s/\r//g;
1123
      $mail->{message}        =~ s/\n/<br>\n/g;
1124
      $full_signature         =~ s/\n/<br>\n/g;
1125
      $mail->{message}       .=  $full_signature;
1126

  
1127
      open(IN, "<:encoding(UTF-8)", $self->{tmpfile})
1128
        or $self->error($self->cleanup . "$self->{tmpfile} : $!");
1129
      $mail->{message} .= $_ while <IN>;
1130
      close(IN);
1122
  $self->cleanup;
1131 1123

  
1132
    } else {
1124
  chdir("$self->{cwd}");
1125
  $main::lxdebug->leave_sub();
1126
}
1133 1127

  
1134
      if (!$self->{"do_not_attach"}) {
1135
        my $attachment_name  =  $self->{attachment_filename} || $self->{tmpfile};
1136
        $attachment_name     =~ s/\.(.+?)$/.${ext_for_format}/ if ($ext_for_format);
1137
        $mail->{attachments} =  [{ "filename" => $self->{tmpfile},
1138
                                   "name"     => $attachment_name }];
1139
      }
1128
sub get_bcc_defaults {
1129
  my ($self, $myconfig, $mybcc) = @_;
1130
#  if (SL::DB::Default->get->bcc_to_login) {
1131
#    $mybcc .= ", " if $mybcc;
1132
#    $mybcc .= $myconfig->{email};
1133
#  }
1134
  my $otherbcc = SL::DB::Default->get->global_bcc;
1135
  if ($otherbcc) {
1136
    $mybcc .= ", " if $mybcc;
1137
    $mybcc .= $otherbcc;
1138
  }
1139
  return $mybcc;
1140
}
1140 1141

  
1141
      $mail->{message} .= $full_signature;
1142
    }
1142
sub send_email {
1143
  $main::lxdebug->enter_sub();
1144
  my ($self, $myconfig, $ext_for_format) = @_;
1145
  my $mail = Mailer->new;
1143 1146

  
1144
    my $err = $mail->send();
1145
    $self->error($self->cleanup . "$err") if ($err);
1147
  map { $mail->{$_} = $self->{$_} }
1148
    qw(cc subject message version format);
1146 1149

  
1147
  } else {
1150
  $mail->{bcc}    = $self->get_bcc_defaults($myconfig, $self->{bcc});
1151
  $mail->{to}     = $self->{EMAIL_RECIPIENT} ? $self->{EMAIL_RECIPIENT} : $self->{email};
1152
  $mail->{from}   = qq|"$myconfig->{name}" <$myconfig->{email}>|;
1153
  $mail->{fileid} = time() . '.' . $$ . '.';
1154
  my $full_signature     =  $self->create_email_signature();
1155
  $full_signature        =~ s/\r//g;
1148 1156

  
1149
    $self->{OUT}      = $out;
1150
    $self->{OUT_MODE} = $out_mode;
1157
  $mail->{attachments} =  [];
1158
  my @attfiles;
1159
  # if we send html or plain text inline
1160
  if (($self->{format} eq 'html') && ($self->{sendmode} eq 'inline')) {
1161
    $mail->{contenttype}    =  "text/html";
1162
    $mail->{message}        =~ s/\r//g;
1163
    $mail->{message}        =~ s/\n/<br>\n/g;
1164
    $full_signature         =~ s/\n/<br>\n/g;
1165
    $mail->{message}       .=  $full_signature;
1151 1166

  
1152
    my $numbytes = (-s $self->{tmpfile});
1153 1167
    open(IN, "<", $self->{tmpfile})
1154 1168
      or $self->error($self->cleanup . "$self->{tmpfile} : $!");
1155
    binmode IN;
1169
    $mail->{message} .= $_ while <IN>;
1170
    close(IN);
1156 1171

  
1157
    $self->{copies} = 1 unless $self->{media} eq 'printer';
1172
  } else {
1173
    $main::lxdebug->message(LXDebug->DEBUG2(),"action_oldfile=" . $self->{action_oldfile}." action_nofile=".$self->{action_nofile});
1174
    if (!$self->{"do_not_attach"} && !$self->{action_nofile}) {
1175
      my $attachment_name  =  $self->{attachment_filename}  || $self->{tmpfile};
1176
      $attachment_name     =~ s/\.(.+?)$/.${ext_for_format}/ if ($ext_for_format);
1177
      if ( $self->{action_oldfile} ) {
1178
        $main::lxdebug->message(LXDebug->DEBUG2(),"object_id =>". $self->{id}." object_type =>". $self->{formname});
1179
        my ( $attfile ) = SL::File->get_all(object_id   => $self->{id},
1180
                                            object_type => $self->{formname},
1181
                                            file_type   => 'document');
1182
        $main::lxdebug->message(LXDebug->DEBUG2(), "old file obj=".$attfile);
1183
        push @attfiles, $attfile if $attfile;
1184
      } else {
1185
        push @{ $mail->{attachments} }, { path => $self->{tmpfile},
1186
                                          id   => $self->{print_file_id},
1187
                                          type => "application/pdf",
1188
                                          name => $attachment_name };
1189
      }
1190
    }
1191
  }
1192
  if (!$self->{"do_not_attach"}) {
1193
    for my $i (1 .. $self->{attfile_count}) {
1194
      if (  $self->{"attsel_$i"} ) {
1195
        my $attfile = SL::File->get(id => $self->{"attfile_$i"});
1196
        $main::lxdebug->message(LXDebug->DEBUG2(), "att file=".$self->{"attfile_$i"}." obj=".$attfile);
1197
        push @attfiles, $attfile if $attfile;
1198
      }
1199
    }
1200
    for my $i (1 .. $self->{attfile_cv_count}) {
1201
      if (  $self->{"attsel_cv_$i"} ) {
1202
        my $attfile = SL::File->get(id => $self->{"attfile_cv_$i"});
1203
        $main::lxdebug->message(LXDebug->DEBUG2(), "att file=".$self->{"attfile_$i"}." obj=".$attfile);
1204
        push @attfiles, $attfile if $attfile;
1205
      }
1206
    }
1207
    for my $i (1 .. $self->{attfile_part_count}) {
1208
      if (  $self->{"attsel_part_$i"} ) {
1209
        my $attfile = SL::File->get(id => $self->{"attfile_part_$i"});
1210
        $main::lxdebug->message(LXDebug->DEBUG2(), "att file=".$self->{"attfile_$i"}." obj=".$attfile);
1211
        push @attfiles, $attfile if $attfile;
1212
      }
1213
    }
1214
    foreach my $attfile ( @attfiles ) {
1215
      push @{ $mail->{attachments} }, { path => SL::File->get_file_path(dbfile => $attfile),
1216
                                        id   => $attfile->id,
1217
                                        type => $attfile->file_mime_type,
1218
                                        name => $attfile->file_name };
1219
    }
1220
  }
1221
  $mail->{message}  =~ s/\r//g;
1222
  $mail->{message} .= $full_signature;
1223
  $self->{emailerr} = $mail->send();
1224
  # $self->error($self->cleanup . "$err") if $self->{emailerr};
1225
  $self->{email_journal_id} = $mail->{journalentry};
1158 1226

  
1159
    chdir("$self->{cwd}");
1160
    #print(STDERR "Kopien $self->{copies}\n");
1161
    #print(STDERR "OUT $self->{OUT}\n");
1162
    for my $i (1 .. $self->{copies}) {
1163
      if ($self->{OUT}) {
1164
        $self->{OUT} = $command_formatter->($self->{OUT_MODE}, $self->{OUT});
1227
  #write back for message info and mail journal
1228
  $self->{cc}  = $mail->{cc};
1229
  $self->{bcc} = $mail->{bcc};
1230
  $self->{email} = $mail->{to};
1165 1231

  
1166
        open  OUT, $self->{OUT_MODE}, $self->{OUT} or $self->error($self->cleanup . "$self->{OUT} : $!");
1167
        print OUT $_ while <IN>;
1168
        close OUT;
1169
        seek  IN, 0, 0;
1232
  $main::lxdebug->leave_sub();
1233
}
1170 1234

  
1171
      } else {
1172
        my %headers = ('-type'       => $template->get_mime_type,
1173
                       '-connection' => 'close',
1174
                       '-charset'    => 'UTF-8');
1175

  
1176
        $self->{attachment_filename} ||= $self->generate_attachment_filename;
1177

  
1178
        if ($self->{attachment_filename}) {
1179
          %headers = (
1180
            %headers,
1181
            '-attachment'     => $self->{attachment_filename},
1182
            '-content-length' => $numbytes,
1183
            '-charset'        => '',
1184
          );
1185
        }
1235
sub output_file {
1236
  $main::lxdebug->enter_sub();
1186 1237

  
1187
        print $::request->cgi->header(%headers);
1238
  my ($self,$mimeType,$command_formatter) = @_;
1239
  my $numbytes = (-s $self->{tmpfile});
1240
  open(IN, "<", $self->{tmpfile})
1241
    or $self->error($self->cleanup . "$self->{tmpfile} : $!");
1242
  binmode IN;
1188 1243

  
1189
        $::locale->with_raw_io(\*STDOUT, sub { print while <IN> });
1190
      }
1191
    }
1244
  $self->{copies} = 1 unless $self->{media} eq 'printer';
1192 1245

  
1193
    close(IN);
1194
  }
1246
  chdir("$self->{cwd}");
1247
  for my $i (1 .. $self->{copies}) {
1248
    if ($self->{OUT}) {
1249
      $self->{OUT} = $command_formatter->($self->{OUT_MODE}, $self->{OUT});
1195 1250

  
1196
  $self->cleanup;
1251
      open  OUT, $self->{OUT_MODE}, $self->{OUT} or $self->error($self->cleanup . "$self->{OUT} : $!");
1252
      print OUT $_ while <IN>;
1253
      close OUT;
1254
      seek  IN, 0, 0;
1197 1255

  
1198
  chdir("$self->{cwd}");
1256
    } else {
1257
      my %headers = ('-type'       => $mimeType,
1258
                     '-connection' => 'close',
1259
                     '-charset'    => 'UTF-8');
1260

  
1261
      $self->{attachment_filename} ||= $self->generate_attachment_filename;
1262

  
1263
      if ($self->{attachment_filename}) {
1264
        %headers = (
1265
          %headers,
1266
          '-attachment'     => $self->{attachment_filename},
1267
          '-content-length' => $numbytes,
1268
          '-charset'        => '',
1269
        );
1270
      }
1271

  
1272
      print $::request->cgi->header(%headers);
1273

  
1274
      $::locale->with_raw_io(\*STDOUT, sub { print while <IN> });
1275
    }
1276
  }
1277
  close(IN);
1199 1278
  $main::lxdebug->leave_sub();
1200 1279
}
1201 1280

  
SL/InstallationCheck.pm
29 29
  { name => "Digest::SHA",                         url => "http://search.cpan.org/~mshelor/",   debian => 'libdigest-sha-perl' },
30 30
  { name => "Email::Address",                      url => "http://search.cpan.org/~rjbs/",      debian => 'libemail-address-perl' },
31 31
  { name => "Email::MIME",                         url => "http://search.cpan.org/~rjbs/",      debian => 'libemail-mime-perl' },
32
  { name => "MIME::Entity",                        url => "http://search.cpan.org/~dskoll/",    debian => '' },
33
  { name => "MIME::Parser",                        url => "http://search.cpan.org/~dskoll/",    debian => '' },
32 34
  { name => "FCGI",            version => '0.72',  url => "http://search.cpan.org/~mstrout/",   debian => 'libfcgi-perl' },
33 35
  { name => "File::Copy::Recursive",               url => "http://search.cpan.org/~dmuey/",     debian => 'libfile-copy-recursive-perl' },
34 36
  { name => "GD",                                  url => "http://search.cpan.org/~lds/",       debian => 'libgd-gd2-perl', },
SL/Mailer.pm
24 24
package Mailer;
25 25

  
26 26
use Email::Address;
27
use Email::MIME::Creator;
27
use MIME::Entity;
28
use MIME::Parser;
29
use File::MimeInfo::Magic;
28 30
use File::Slurp;
29 31
use List::UtilsBy qw(bundle_by);
30 32

  
......
32 34
use SL::DB::EmailJournal;
33 35
use SL::DB::EmailJournalAttachment;
34 36
use SL::DB::Employee;
35
use SL::MIME;
36 37
use SL::Template;
37 38

  
38 39
use strict;
40
use Encode;
39 41

  
40 42
my $num_sent = 0;
43
my $parser;
41 44

  
42 45
my %mail_delivery_modules = (
43 46
  sendmail => 'SL::Mailer::Sendmail',
......
47 50
sub new {
48 51
  my ($type, %params) = @_;
49 52
  my $self = { %params };
53
  $parser = new MIME::Parser;
54
  $parser->output_under("users");
50 55

  
51 56
  bless $self, $type;
52 57
}
......
117 122
        $addr_obj->phrase($phrase);
118 123
      }
119 124

  
120
      push @header_addresses, $addr_obj->format;
125
      push @header_addresses, encode('MIME-Header',$addr_obj->format);
121 126
    }
122 127

  
123 128
    push @{ $self->{headers} }, ( ucfirst($item) => join(', ', @header_addresses) ) if @header_addresses;
......
128 133
  my ($self, $attachment) = @_;
129 134

  
130 135
  my %attributes = (
131
    disposition  => 'attachment',
132
    encoding     => 'base64',
136
    Disposition  => 'attachment',
137
    Encoding     => 'base64',
133 138
  );
134 139

  
140
  my $file_id = 0;
135 141
  my $attachment_content;
142
  my $email_journal = $::instance_conf->get_email_journal;
136 143

  
144
  $main::lxdebug->message(LXDebug->DEBUG2(), "mail5 att=".$attachment." email_journal=". $email_journal." id=".$attachment->{id});
137 145
  if (ref($attachment) eq "HASH") {
138
    $attributes{filename} = $attachment->{name};
139
    $attachment_content   = $attachment->{content} // eval { read_file($attachment->{filename}) };
146
    $attributes{Path}     = $attachment->{path} || $attachment->{filename};
147
    $attributes{Filename} = $attachment->{name};
148
    $file_id              = $attachment->{id}   || '0';
149
    $attributes{Type}     = $attachment->{type} || 'application/pdf';
150
    $attachment_content   = eval { read_file($attachment->{path}) } if $email_journal > 1;
140 151

  
141 152
  } else {
142 153
    # strip path
143
    $attributes{filename} =  $attachment;
144
    $attributes{filename} =~ s:.*\Q$self->{fileid}\E:: if $self->{fileid};
145
    $attributes{filename} =~ s:.*/::g;
146
    $attachment_content   =  eval { read_file($attachment) };
154
    $attributes{Path}     =  $attachment;
155
    $attributes{Filename} =  $attachment;
156
    $attributes{Filename} =~ s:.*\Q$self->{fileid}\E:: if $self->{fileid};
157
    $attributes{Filename} =~ s:.*/::g;
158

  
159
    my $application     = ($attachment =~ /(^\w+$)|\.(html|text|txt|sql)$/) ? 'text' : 'application';
160
    $attributes{Type}   = File::MimeInfo::Magic::magic($attachment);
161
    $attributes{Type} ||= "${application}/$self->{format}" if $self->{format};
162
    $attributes{Type} ||= 'application/octet-stream';
163
    $attachment_content = eval { read_file($attachment) } if $email_journal > 1;
147 164
  }
148 165

  
149
  return undef if !defined $attachment_content;
166
  return undef if $email_journal > 1 && !defined $attachment_content;
167
  $attachment_content ||= ' ';
168
  $main::lxdebug->message(LXDebug->DEBUG2(), "mail6 mtype=".$attributes{Type}." path=".
169
                            $attributes{Path}." filename=".$attributes{Filename});
150 170

  
151
  my $application             = ($attachment =~ /(^\w+$)|\.(html|text|txt|sql)$/) ? 'text' : 'application';
152
  $attributes{content_type}   = SL::MIME->mime_type_from_ext($attributes{filename});
153
  $attributes{content_type} ||= "${application}/$self->{format}" if $self->{format};
154
  $attributes{content_type} ||= 'application/octet-stream';
155
  $attributes{charset}        = $self->{charset} if lc $application eq 'text' && $self->{charset};
171
# $attributes{Charset}  = $self->{charset} if lc $application eq 'text' && $self->{charset};
172
  $attributes{Charset}  = $self->{charset} if $self->{charset};
156 173

  
157
  return Email::MIME->create(
158
    attributes => \%attributes,
159
    body       => $attachment_content,
174
  my $ent;
175
  if ( $attributes{Type} eq 'message/rfc822' ) {
176
    my $fh = IO::File->new($attributes{Path}, "r");
177
    if (! defined $fh) {
178
      return undef;
179
    }
180
    $ent = $parser->parse($fh);
181
    undef $fh;
182
    my $head = $ent->head;
183
    $head->replace('Content-disposition','attachment; filename='.$attributes{Filename});
184
  } else {
185
    $ent = MIME::Entity->build(%attributes);
186
  }
187
  push @{ $self->{mail_attachments}} , SL::DB::EmailJournalAttachment->new(
188
    name      => $attributes{Filename},
189
    mime_type => $attributes{Type},
190
    content   => ( $email_journal > 1 ? $attachment_content : ' '),
191
    file_id   => $file_id,
160 192
  );
193
  return $ent;
161 194
}
162 195

  
163 196
sub _create_message {
164 197
  my ($self) = @_;
165 198

  
166
  my @parts;
167

  
199
  push @{ $self->{headers} }, ('Type'    =>"multipart/mixed" );
200
  my  $top = MIME::Entity->build(@{$self->{headers}});
168 201
  if ($self->{message}) {
169
    push @parts, Email::MIME->create(
170
      attributes => {
171
        content_type => $self->{contenttype},
172
        charset      => $self->{charset},
173
        encoding     => 'quoted-printable',
174
      },
175
      body_str => $self->{message},
176
    );
177

  
178
    push @{ $self->{headers} }, (
179
      'Content-Type' => qq|$self->{contenttype}; charset="$self->{charset}"|,
180
    );
202
    $top->attach(Data => encode($self->{charset},$self->{message}),
203
                 Charset => $self->{charset},
204
                 Type => $self->{contenttype},
205
                 Encoding    => 'quoted-printable');
181 206
  }
182 207

  
183
  push @parts, grep { $_ } map { $self->_create_attachment_part($_) } @{ $self->{attachments} || [] };
184

  
185
  return Email::MIME->create(
186
    header_str => $self->{headers},
187
    parts      => \@parts,
188
  );
208
  map { $top->add_part($self->_create_attachment_part($_)) } @{ $self->{attachments} || [] };
209
  return $top;
189 210
}
190 211

  
191 212
sub send {
......
202 223
  $self->{charset}       =  'UTF-8';
203 224
  $self->{contenttype} ||=  "text/plain";
204 225
  $self->{headers}       =  [
205
    Subject              => $self->{subject},
226
    Subject              => encode('MIME-Header',$self->{subject}),
206 227
    'Message-ID'         => '<' . $self->_create_message_id . '>',
207 228
    'X-Mailer'           => "kivitendo $self->{version}",
208 229
  ];
230
  $self->{mail_attachments} = [];
231
  $self->{content_by_name}  = $::instance_conf->get_email_journal == 1 && $::instance_conf->get_doc_files;
209 232

  
210 233
  my $error;
211 234
  my $ok = eval {
......
215 238

  
216 239
    my $email = $self->_create_message;
217 240

  
218
    # $::lxdebug->message(0, "message: " . $email->as_string);
241
    #$::lxdebug->message(0, "message: " . $email->as_string);
219 242
    # return "boom";
220 243

  
221
    $self->{driver}->start_mail(from => $self->{from}, to => [ $self->_all_recipients ]);
244
    $self->{driver}->start_mail(from => encode('MIME-Header',$self->{from}), to => [ $self->_all_recipients ]);
222 245
    $self->{driver}->print($email->as_string);
223 246
    $self->{driver}->send;
224 247

  
......
227 250

  
228 251
  $error = $@ if !$ok;
229 252

  
230
  $self->_store_in_journal;
253
  $self->{journalentry} = $self->_store_in_journal;
254
  $parser->filer->purge;
231 255

  
232 256
  return $ok ? '' : "send email: $error";
233 257
}
234 258

  
235 259
sub _all_recipients {
236 260
  my ($self) = @_;
237

  
238 261
  $self->{addresses} ||= {};
239 262
  return map { @{ $self->{addresses}->{$_} || [] } } qw(to cc bcc);
240 263
}
......
251 274
  $extended_status //= $self->{driver}->extended_status if $self->{driver};
252 275
  $extended_status //= 'unknown error';
253 276

  
254
  my @attachments;
255

  
256
  @attachments = grep { $_ } map {
257
    my $part = $self->_create_attachment_part($_);
258
    if ($part) {
259
      SL::DB::EmailJournalAttachment->new(
260
        name      => $part->filename,
261
        mime_type => $part->content_type,
262
        content   => $part->body,
263
      )
264
    }
265
  } @{ $self->{attachments} || [] } if $journal_enable > 1;
266

  
267 277
  my $headers = join "\r\n", (bundle_by { join(': ', @_) } 2, @{ $self->{headers} || [] });
268 278

  
269
  SL::DB::EmailJournal->new(
279
  my $jentry = SL::DB::EmailJournal->new(
270 280
    sender          => SL::DB::Manager::Employee->current,
271 281
    from            => $self->{from}    // '',
272 282
    recipients      => join(', ', $self->_all_recipients),
......
274 284
    headers         => $headers,
275 285
    body            => $self->{message} // '',
276 286
    sent_on         => DateTime->now_local,
277
    attachments     => \@attachments,
287
    attachments     => \@{ $self->{mail_attachments} },
278 288
    status          => $status,
279 289
    extended_status => $extended_status,
280 290
  )->save;
291
  return $jentry->id;
281 292
}
282 293

  
283 294
1;
bin/mozilla/io.pl
50 50
use SL::Locale::String qw(t8);
51 51
use SL::IC;
52 52
use SL::IO;
53
use SL::File;
53 54
use SL::PriceSource;
54 55

  
55 56
use SL::DB::Customer;
......
58 59
use SL::DB::Printer;
59 60
use SL::DB::Vendor;
60 61
use SL::Helper::CreatePDF;
61
use SL::Helper::Flash qw(flash);
62
use SL::Helper::Flash;
62 63

  
63 64
require "bin/mozilla/common.pl";
64 65

  
......
1066 1067
  @dont_hide_key{@dont_hide_key_list} = (1) x @dont_hide_key_list;
1067 1068
  @hidden_keys = sort grep { !$dont_hide_key{$_} } grep { !ref $form->{$_} } keys %$form;
1068 1069

  
1070
  my (@files, @vc_files, @part_files, $has_document);
1071

  
1072
  if ($::instance_conf->get_doc_storage) {
1073
    @files = SL::File->get_all_versions(object_id => $form->{id}, object_type => $form->{type}, file_type => 'document');
1074
    $has_document = 1 if scalar(@files) > 0;
1075
    @files = SL::File->get_all(object_id => $form->{id}, object_type => $form->{type}, file_type => 'attachment');
1076
    @vc_files = SL::File->get_all(object_id => $form->{"$form->{vc}_id"}, object_type => $form->{vc})
1077
      if $form->{vc} && $form->{"$form->{vc}_id"};
1078

  
1079
    my %part_id_map = map { $_->{id} => $_ } grep { $_->{id} } map {
1080
      {
1081
        'id'       => $form->{"id_$_"},
1082
        'partname' => $form->{"partnumber_$_"}
1083
      }
1084
    } (1 .. $form->{rowcount});
1085

  
1086
    foreach my $partid (keys %part_id_map) {
1087
      my @pfiles = SL::File->get_all(object_id => $partid, object_type => 'part');
1088
      push @part_files, map { $_->{partname} = $part_id_map{$partid}->{partname}; $_ } @pfiles;
1089
    }
1090
  }
1091

  
1069 1092
  print $form->parse_html_template('generic/edit_email',
1070 1093
                                   { title         => $title,
1071 1094
                                     a_filename    => $attachment_filename,
1072 1095
                                     subject       => $subject,
1096
                                     has_document  => $has_document,
1073 1097
                                     print_options => print_options('inline' => 1),
1074 1098
                                     action        => 'send_email',
1099
                                     FILES         => \@files,
1100
                                     VC_FILES      => \@vc_files,
1101
                                     PART_FILES    => \@part_files,
1075 1102
                                     HIDDEN        => [ map +{ name => $_, value => $form->{$_} }, @hidden_keys ],
1076 1103
                                     SHOW_BCC      => $::auth->assert('email_bcc', 'may fail') });
1077 1104

  
......
1088 1115

  
1089 1116
  my $callback = $form->{script} . "?action=edit";
1090 1117
  map({ $callback .= "\&${_}=" . E($form->{$_}); } qw(type id));
1118
  if ( $form->{action_oldfile} || $form->{action_nofile} ) {
1119
    if (!$form->{email} || $form->{email} =~ /^\s*$/) {
1120
      flash('error', $::locale->text('E-mail address missing!'));
1121
    }
1122
    else {
1123
      $form->send_email(\%myconfig,'pdf');
1124
    }
1125
  }
1126
  else {
1127
    print_form("return");
1128
    $form->{addition} = "SCREENED";
1129
    $form->save_history;
1130
    $form->{addition} = "MAILED";
1131
  }
1132

  
1133
  flash_later('info' , $::locale->text('E-Mail is sent to #1', $form->{email})) if !$form->{emailerr};
1134
  flash_later('error', $::locale->text($form->{emailerr})) if $form->{emailerr};
1091 1135

  
1092
  print_form("return");
1136
  delete $form->{emailerr};
1093 1137

  
1094 1138
  Common->save_email_status(\%myconfig, $form);
1139
  ##TODO andere SAVE HISTORY
1095 1140

  
1096 1141
  $form->{callback} = $callback;
1097 1142
  $form->redirect();
js/locale/de.js
57 57
"If you switch to a different tab without saving you will lose the data you've entered in the current tab.":"Wenn Sie auf einen anderen Tab wechseln, ohne vorher zu speichern, so gehen die im aktuellen Tab eingegebenen Daten verloren.",
58 58
"Map":"Karte",
59 59
"No":"Nein",
60
"No articles have been added yet.":"Es wurden noch keine Artikel hinzugefügt.",
61 60
"No delievery orders selected, please set one checkbox!":"Kein Lieferschein selektiert, bitte eine Box anklicken!",
62 61
"No delivery orders have been selected.":"Es wurden keine Lieferscheine ausgewählt.",
63 62
"No entries have been selected.":"Es wurden keine Einträge ausgewählt.",
locale/de/all
767 767
  'Customer'                    => 'Kunde',
768 768
  'Customer (database ID)'      => 'Kunde (Datenbank-ID)',
769 769
  'Customer (name)'             => 'Kunde (Name)',
770
  'Customer Attachments'        => 'Anhänge des Kunden',
770 771
  'Customer Discount'           => 'Kundenrabatt',
771 772
  'Customer Master Data'        => 'Kundenstammdaten',
772 773
  'Customer Name'               => 'Kundenname',
......
1042 1043
  'Duplicate in CSV file'       => 'Duplikat in CSV-Datei',
1043 1044
  'Duplicate in database'       => 'Duplikat in Datenbank',
1044 1045
  'During the next update a taxkey 0 with tax rate of 0 will automatically created.' => 'Beim nächsten Ausführen des Updates wird ein Steuerschlüssel 0 mit einem Steuersatz von 0% automatisch erzeugt.',
1046
  'E-Mail is sent to #1'        => 'E-Mail wurde an #1 gesendet',
1045 1047
  'E-mail'                      => 'E-Mail',
1046 1048
  'E-mail Statement to'         => 'Fälligkeitsabrechnung als E-Mail an',
1047 1049
  'E-mail address missing!'     => 'E-Mail-Adresse fehlt!',
......
1480 1482
  'Illegal characters have been removed from the following fields: #1' => 'Ungültige Zeichen wurden aus den folgenden Feldern entfernt: #1',
1481 1483
  'Illegal date'                => 'Ungültiges Datum',
1482 1484
  'Image'                       => 'Grafik',
1485
  'ImagePreview'                => 'Bildvorschau',
1486
  'Images'                      => 'Bilder',
1483 1487
  'Import'                      => 'Import',
1484 1488
  'Import AP from Scanner or Email' => 'Einkaufsbelege importieren vom Scanner oder von Email',
1485 1489
  'Import AR from Scanner or Email' => 'Verkaufsbelege importieren vom Scanner oder von Email',
......
1707 1711
  'Make (vendor\'s database ID, number or name; with X being a number)' => 'Lieferant (Datenbank-ID, Nummer oder Name des Lieferanten; X ist eine fortlaufende Zahl)',
1708 1712
  'Make compatible for import'  => 'Für den Import kompatibel machen',
1709 1713
  'Make default profile'        => 'Zu Standardprofil machen',
1714
  'Make new document'           => 'Erzeuge ein neue Datei',
1710 1715
  'Makemodel Price'             => 'Lieferantenpreis',
1711 1716
  'Manage Custom Variables'     => 'Benutzerdefinierte Variablen',
1712 1717
  'Mandantennummer'             => 'Mandantennummer',
......
1840 1845
  'No delivery orders have been selected.' => 'Es wurden keine Lieferscheine ausgewählt.',
1841 1846
  'No delivery term has been created yet.' => 'Es wurden noch keine Lieferbedingungen angelegt',
1842 1847
  'No department has been created yet.' => 'Es wurde noch keine Abteilung erfasst.',
1848
  'No document'                 => 'Kein Datei mitschicken (ggf. Anhänge)',
1843 1849
  'No draft was found.'         => 'Kein Entwurf gefunden.',
1844 1850
  'No dunnings have been selected for printing.' => 'Es wurden keine Mahnungen zum Drucken ausgew&auml;hlt.',
1845 1851
  'No end date given, setting to today' => 'Kein Enddatum gegeben, setze Enddatum auf heute',
......
2037 2043
  'Part "#1" has chargenumber or best before date set. So it cannot be transfered automatically.' => 'Bei Artikel "#1" ist eine Chargenummer oder ein Mindesthaltbarkeitsdatum vergeben. Deshalb kann dieser Artikel nicht automatisch ausgelagert werden.',
2038 2044
  'Part (database ID)'          => 'Artikel (Datenbank-ID)',
2039 2045
  'Part (typeabbreviation)'     => 'W',
2046
  'Part Attachments'            => 'Anhänge der Artikel',
2040 2047
  'Part Classification'         => 'Artikel-Klassifizierung',
2041 2048
  'Part Description'            => 'Artikelbeschreibung',
2042 2049
  'Part Description missing!'   => 'Artikelbezeichnung fehlt!',
......
3596 3603
  'Zip, City'                   => 'PLZ, Ort',
3597 3604
  'Zipcode'                     => 'PLZ',
3598 3605
  '[email]'                     => '[email]',
3606
  'a_credit_note'               => '',
3607
  'a_purchase_delivery_order'   => '',
3608
  'a_purchase_invoice'          => '',
3609
  'a_purchase_order'            => '',
3610
  'a_request_quotation'         => '',
3611
  'a_sales_delivery_order'      => '',
3612
  'a_sales_order'               => '',
3613
  'a_sales_quotation'           => '',
3599 3614
  'absolute'                    => 'absolut',
3600 3615
  'account_description'         => 'Beschreibung',
3601 3616
  'accrual'                     => 'Soll-Versteuerung',
......
3603 3618
  'active'                      => 'aktiv',
3604 3619
  'all'                         => 'Alle',
3605 3620
  'all entries'                 => 'alle Einträge',
3621
  'an_invoice'                  => '',
3606 3622
  'and'                         => 'und',
3607 3623
  'ap_aging_list'               => 'liste_offene_verbindlichkeiten',
3608 3624
  'ar_aging_list'               => 'liste_offene_forderungen',
......
3718 3734
  'list_of_payments'            => 'zahlungsausgaenge',
3719 3735
  'list_of_receipts'            => 'zahlungseingaenge',
3720 3736
  'list_of_transactions'        => 'buchungsliste',
3737
  'mail_greeting #1'            => '',
3721 3738
  'male'                        => 'männlich',
3722 3739
  'mark as paid'                => 'als bezahlt markieren',
3723 3740
  'max filesize'                => 'maximale Dateigröße',
......
3752 3769
  'only OB Transactions'        => 'nur EB-Buchungen',
3753 3770
  'open'                        => 'Offen',
3754 3771
  'order'                       => 'Reihenfolge',
3772
  'other Document Attachments'  => 'Weitere Dateianhänge',
3755 3773
  'our vendor number at customer' => 'Unsere Lieferanten-Nr. beim Kunden',
3756 3774
  'parsing csv'                 => 'Parse CSV Daten',
3757 3775
  'part'                        => 'Ware',
......
3838 3856
  'unconfigured'                => 'unkonfiguriert',
3839 3857
  'unnamed record template'     => 'unbenannte Belegvorlage',
3840 3858
  'until'                       => 'bis',
3859
  'uploaded'                    => 'Hochgeladen',
3860
  'use actual document'         => 'Verwende aktuelle Datei',
3841 3861
  'use program settings'        => 'benutze Programmeinstellungen',
3842 3862
  'use user config'             => 'Verwende Benutzereinstellung',
3843 3863
  'used'                        => 'Verbraucht',
sql/Pg-upgrade2/email_journal_attachments_add_fileid.sql
1
-- @tag: email_journal_attachments_add_fileid
2
-- @description: attachments mit file_id
3
-- @depends: email_journal filemanagement_feature files
4
ALTER TABLE email_journal_attachments ADD COLUMN file_id integer default 0 NOT NULL;
templates/webpages/generic/edit_email.html
6 6

  
7 7
<table width="100%">
8 8
  <tr>
9
    <td>
9
    <td style="width: 500px">
10 10
      <table>
11 11
        <tr>
12 12
          <th align="right" nowrap>[% 'To' | $T8 %]</th>
......
33 33
        </tr>
34 34
      </table>
35 35
    </td>
36
[%- IF INSTANCE_CONF.get_doc_storage %]
37
    <td align="left" rowspan="2">
38
      <table>
39
[%- USE ATT_it = Iterator(FILES) %]
40
[% FOREACH file = ATT_it %]
41
[% END %]
42
[%- IF ATT_it.size > 0 %]  
43
        <tr class="listheading">
44
          <th colspan="3" align="left" nowrap>[% LxERP.t8('other Document Attachments') %]</th>
45
          <input type="hidden" name="attfile_count" id="m_attfile_count" value="[% ATT_it.size %]">
46
        </tr>
47
         <tr class="">
48
           <th align="left" nowrap></th>
49
           <th align="left" nowrap>[% LxERP.t8('Filename') %]</th>
50
           <th align="left" nowrap></th>
51
        </tr>
52
        <tr><td colspan="3"><hr size="1" style="height:1px;background-color:#000;" noshade></td></tr>
53
       [% FOREACH file = ATT_it %]
54
         <tr class="listrow">
55
          <td></td><td>[% file.file_name %]
56
            <input type="hidden" name="attfile_[% ATT_it.count %]" value="[% file.id %]">
57
            <td><input name="attsel_[% ATT_it.count %]" type="checkbox" class="checkbox" ></td>
58
         </tr>
59
        [% END %]
60
[%- END %]
61
[%- USE ATT_it = Iterator(VC_FILES) %]
62
[% FOREACH file = ATT_it %]
63
[% END %]
64
[%- IF ATT_it.size > 0 %]  
65
        <tr><td colspan="3"><hr size="1" noshade></td></tr>
66
         <tr class="listheading">
67
           <th colspan="3" align="left" nowrap>
68
             <input type="hidden" name="attfile_cv_count" id="m_attfile_cv_count" value="[% ATT_it.size %]">
69
             [% LxERP.t8('Customer Attachments') %]
70
           </th>
71
         </tr>
72
        <tr class="">
73
           <th align="left" nowrap></th>
74
           <th align="left" nowrap>[% LxERP.t8('Filename') %]</th>
75
           <th align="left" nowrap></th>
76
        </tr>
77
        <tr><td colspan="3"><hr size="1" style="height:1px;background-color:#000;" noshade></td></tr>
78
        [% FOREACH file = ATT_it %]
79
         <tr class="listrow">
80
          <td></td><td>[% file.file_name %]
81
            <input type="hidden" name="attfile_cv_[% ATT_it.count %]" value="[% file.id %]">
82
            <td><input name="attsel_cv_[% ATT_it.count %]" type="checkbox" class="checkbox" ></td>
83
         </tr>
84
        [% END %]
85
[%- END %]
86
[%- USE ATT_it = Iterator(PART_FILES) %]
87
[%- SET lastpartid = '' %]  
88
[%- FOREACH file = ATT_it %]
89
[%- END %]
90
[%- IF ATT_it.size > 0 %]  
91
        <tr><td colspan="3"><hr size="1" noshade></td></tr>
92
         <tr class="listheading">
93
          <th colspan="3" align="left" nowrap>
94
            <input type="hidden" name="attfile_part_count" id="m_attfile_part_count" value="[% ATT_it.size %]">
95
            [% LxERP.t8('Part Attachments') %]
96
          </th>
97
        </tr>
98
        <tr class="">
99
           <th align="left" nowrap>[% LxERP.t8('Part Number') %]</th>
100
           <th align="left" nowrap>[% LxERP.t8('Filename') %]</th>
101
           <th align="left" nowrap></th>
102
        </tr>
103
        [% FOREACH file = ATT_it %]
104
          [%- IF lastpartid != file.trans_id %]
105
             [%- SET lastpartid = file.trans_id %][%- SET partname = file.partname %]
106
        <tr><td colspan="3"><hr size="1" style="height:1px;background-color:#000;" noshade></td></tr>
107
          [%- ELSE %][%- SET partname = '' %][% END %]          
108
        <tr class="listrow">
109
          <td>[% partname %]</td>
110
          <td>[% file.file_name %]
111
            <input type="hidden" name="attfile_part_[% ATT_it.count %]" value="[% file.id %]">
112
            <td><input name="attsel_part_[% ATT_it.count %]" type="checkbox" class="checkbox" ></td>
113
         </tr>
114
        [% END %]
115
[%- END %]
116
      </table>
117
    </td>
118
[%- ELSE %]
119
    <td rowspan="2">
120
    </td>
121
[%- END %]
36 122
  </tr>
37 123

  
38 124
  <tr>
......
49 135
    </td>
50 136
  </tr>
51 137
  <tr>
52
    <td>
138
    <td colspan="2">
53 139

  
54 140
[% print_options %]
55 141
[% FOREACH row = HIDDEN %]<input type="hidden" name="[% row.name %]" value="[% HTML.escape(row.value) %]">
......
59 145
  </tr>
60 146

  
61 147
  <tr>
62
    <td><hr size="3" noshade></td>
148
    <td colspan="2"><hr size="3" noshade></td>
63 149
  </tr>
64 150
</table>
65 151

  
66 152
[% L.hidden_tag('action', action) %]
67 153

  
68 154
<br>
69
[% L.submit_tag('dummy_action', LxERP.t8('Continue'), onclick="return check_prerequisites();") %]
155
[% L.submit_tag('action_newfile', LxERP.t8('Make new document'), onclick="return check_prerequisites();") %]
156
[%- IF has_document %]  
157
[% L.submit_tag('action_oldfile', LxERP.t8('use actual document'), onclick="return check_prerequisites();") %]
158
[%- END %]
159
[% L.submit_tag('action_nofile',  LxERP.t8('No document'), onclick="return check_prerequisites();") %]
70 160
</form>
71 161

  
72 162
<script type="text/javascript">

Auch abrufbar als: Unified diff