Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 24ab7ec0

Von Moritz Bunkus vor etwa 9 Jahren hinzugefügt

  • ID 24ab7ec0bfb052edce7a3c7a6e37c151f9cd6a04
  • Vorgänger eead7ecd
  • Nachfolger c825bf71

E-Mail-Journal: verschickte E-Mails speichern

Unterschiede anzeigen:

SL/DB/EmailJournal.pm
1
package SL::DB::EmailJournal;
2

  
3
use strict;
4

  
5
use SL::DB::MetaSetup::EmailJournal;
6
use SL::DB::Manager::EmailJournal;
7

  
8
__PACKAGE__->meta->add_relationship(
9
  attachments  => {
10
    type       => 'one to many',
11
    class      => 'SL::DB::EmailJournalAttachment',
12
    column_map => { id => 'email_journal_id' },
13
  },
14
);
15

  
16
__PACKAGE__->meta->initialize;
17

  
18
1;
SL/DB/EmailJournalAttachment.pm
1
package SL::DB::EmailJournalAttachment;
2

  
3
use strict;
4

  
5
use SL::DB::MetaSetup::EmailJournalAttachment;
6
use SL::DB::Manager::EmailJournalAttachment;
7
use SL::DB::Helper::ActsAsList (group_by => [ qw(email_journal_id) ]);
8

  
9
__PACKAGE__->meta->initialize;
10

  
11
1;
SL/DB/Helper/ALL.pm
42 42
use SL::DB::Draft;
43 43
use SL::DB::Dunning;
44 44
use SL::DB::DunningConfig;
45
use SL::DB::EmailJournal;
46
use SL::DB::EmailJournalAttachment;
45 47
use SL::DB::Employee;
46 48
use SL::DB::Exchangerate;
47 49
use SL::DB::Finanzamt;
SL/DB/Helper/Mappings.pm
126 126
  drafts                         => 'draft',
127 127
  dunning                        => 'dunning',
128 128
  dunning_config                 => 'dunning_config',
129
  email_journal                  => 'EmailJournal',
130
  email_journal_attachments      => 'EmailJournalAttachment',
129 131
  employee                       => 'employee',
130 132
  exchangerate                   => 'exchangerate',
131 133
  finanzamt                      => 'finanzamt',
SL/DB/Manager/EmailJournal.pm
1
package SL::DB::Manager::EmailJournal;
2

  
3
use strict;
4

  
5
use parent qw(SL::DB::Helper::Manager);
6

  
7
sub object_class { 'SL::DB::EmailJournal' }
8

  
9
__PACKAGE__->make_manager_methods;
10

  
11
1;
SL/DB/Manager/EmailJournalAttachment.pm
1
# This file has been auto-generated only because it didn't exist.
2
# Feel free to modify it at will; it will not be overwritten automatically.
3

  
4
package SL::DB::Manager::EmailJournalAttachment;
5

  
6
use strict;
7

  
8
use parent qw(SL::DB::Helper::Manager);
9

  
10
sub object_class { 'SL::DB::EmailJournalAttachment' }
11

  
12
__PACKAGE__->make_manager_methods;
13

  
14
1;
SL/DB/MetaSetup/EmailJournal.pm
1
# This file has been auto-generated. Do not modify it; it will be overwritten
2
# by rose_auto_create_model.pl automatically.
3
package SL::DB::EmailJournal;
4

  
5
use strict;
6

  
7
use parent qw(SL::DB::Object);
8

  
9
__PACKAGE__->meta->table('email_journal');
10

  
11
__PACKAGE__->meta->columns(
12
  body            => { type => 'text', not_null => 1 },
13
  extended_status => { type => 'text', not_null => 1 },
14
  from            => { type => 'text', not_null => 1 },
15
  headers         => { type => 'text', not_null => 1 },
16
  id              => { type => 'integer', not_null => 1, sequence => 'email_journal_id_seq1' },
17
  itime           => { type => 'timestamp', default => 'now()', not_null => 1 },
18
  mtime           => { type => 'timestamp', default => 'now()', not_null => 1 },
19
  recipients      => { type => 'text', not_null => 1 },
20
  sender_id       => { type => 'integer' },
21
  sent_on         => { type => 'timestamp', default => 'now()', not_null => 1 },
22
  status          => { type => 'text', not_null => 1 },
23
  subject         => { type => 'text', not_null => 1 },
24
);
25

  
26
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
27

  
28
__PACKAGE__->meta->allow_inline_column_values(1);
29

  
30
__PACKAGE__->meta->foreign_keys(
31
  sender => {
32
    class       => 'SL::DB::Employee',
33
    key_columns => { sender_id => 'id' },
34
  },
35
);
36

  
37
1;
38
;
SL/DB/MetaSetup/EmailJournalAttachment.pm
1
# This file has been auto-generated. Do not modify it; it will be overwritten
2
# by rose_auto_create_model.pl automatically.
3
package SL::DB::EmailJournalAttachment;
4

  
5
use strict;
6

  
7
use parent qw(SL::DB::Object);
8

  
9
__PACKAGE__->meta->table('email_journal_attachments');
10

  
11
__PACKAGE__->meta->columns(
12
  content          => { type => 'bytea', not_null => 1 },
13
  email_journal_id => { type => 'integer', not_null => 1 },
14
  id               => { type => 'serial', not_null => 1 },
15
  itime            => { type => 'timestamp', default => 'now()', not_null => 1 },
16
  mime_type        => { type => 'text', not_null => 1 },
17
  mtime            => { type => 'timestamp', default => 'now()', not_null => 1 },
18
  name             => { type => 'text', not_null => 1 },
19
  position         => { type => 'integer', not_null => 1 },
20
);
21

  
22
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
23

  
24
__PACKAGE__->meta->allow_inline_column_values(1);
25

  
26
__PACKAGE__->meta->foreign_keys(
27
  email_journal => {
28
    class       => 'SL::DB::EmailJournal',
29
    key_columns => { email_journal_id => 'id' },
30
  },
31
);
32

  
33
1;
34
;
SL/Mailer.pm
25 25
use Email::Address;
26 26
use Email::MIME::Creator;
27 27
use File::Slurp;
28
use List::UtilsBy qw(bundle_by);
28 29

  
29 30
use SL::Common;
31
use SL::DB::EmailJournal;
32
use SL::DB::EmailJournalAttachment;
33
use SL::DB::Employee;
30 34
use SL::MIME;
31 35
use SL::Template;
32 36

  
......
185 189
  # Create driver for delivery method (sendmail/SMTP)
186 190
  $self->{driver} = eval { $self->_create_driver };
187 191
  if (!$self->{driver}) {
188
    $::lxdebug->leave_sub();
192
    $self->_store_in_journal('failed', 'driver could not be created; check your configuration');
189 193
    return "send email : $@";
190 194
  }
191 195

  
......
198 202
    'X-Mailer'           => "kivitendo $self->{version}",
199 203
  ];
200 204

  
201
  # Clean up To/Cc/Bcc address fields
202
  $self->_cleanup_addresses;
203
  $self->_create_address_headers;
205
  my $error;
206
  my $ok = eval {
207
    # Clean up To/Cc/Bcc address fields
208
    $self->_cleanup_addresses;
209
    $self->_create_address_headers;
204 210

  
205
  my $email = $self->_create_message;
211
    my $email = $self->_create_message;
206 212

  
207
  # $::lxdebug->message(0, "message: " . $email->as_string);
208
  # return "boom";
213
    # $::lxdebug->message(0, "message: " . $email->as_string);
214
    # return "boom";
209 215

  
210
  $self->{driver}->start_mail(from => $self->{from}, to => [ map { @{ $self->{addresses}->{$_} } } qw(to cc bcc) ]);
211
  $self->{driver}->print($email->as_string);
212
  $self->{driver}->send;
216
    $self->{driver}->start_mail(from => $self->{from}, to => [ $self->_all_recipients ]);
217
    $self->{driver}->print($email->as_string);
218
    $self->{driver}->send;
213 219

  
214
  return '';
220
    1;
221
  };
222

  
223
  $error = $@ if !$ok;
224

  
225
  $self->_store_in_journal;
226

  
227
  return $ok ? '' : "send email: $error";
228
}
229

  
230
sub _all_recipients {
231
  my ($self) = @_;
232

  
233
  $self->{addresses} ||= {};
234
  return map { @{ $self->{addresses}->{$_} || [] } } qw(to cc bcc);
235
}
236

  
237
sub _store_in_journal {
238
  my ($self, $status, $extended_status) = @_;
239

  
240
  $status          //= $self->{driver}->status if $self->{driver};
241
  $status          //= 'failed';
242
  $extended_status //= $self->{driver}->extended_status if $self->{driver};
243
  $extended_status //= 'unknown error';
244

  
245
  my @attachments = grep { $_ } map {
246
    my $part = $self->_create_attachment_part($_);
247
    if ($part) {
248
      SL::DB::EmailJournalAttachment->new(
249
        name      => $part->filename,
250
        mime_type => $part->content_type,
251
        content   => $part->body,
252
      )
253
    }
254
  } @{ $self->{attachments} || [] };
255

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

  
258
  SL::DB::EmailJournal->new(
259
    sender          => SL::DB::Manager::Employee->current,
260
    from            => $self->{from}    // '',
261
    recipients      => join(', ', $self->_all_recipients),
262
    subject         => $self->{subject} // '',
263
    headers         => $headers,
264
    body            => $self->{message} // '',
265
    sent_on         => DateTime->now_local,
266
    attachments     => \@attachments,
267
    status          => $status,
268
    extended_status => $extended_status,
269
  )->save;
215 270
}
216 271

  
217 272
1;
SL/Mailer/SMTP.pm
6 6

  
7 7
use Rose::Object::MakeMethods::Generic
8 8
(
9
  scalar => [ qw(myconfig mailer form) ]
9
  scalar => [ qw(myconfig mailer form status extended_status) ]
10 10
);
11 11

  
12 12
my %security_config = (
......
18 18
sub init {
19 19
  my ($self) = @_;
20 20

  
21
  Rose::Object::init(@_);
21
  Rose::Object::init(
22
    @_,
23
    status          => 'failed',
24
    extended_status => 'no send attempt made',
25
  );
22 26

  
23 27
  my $cfg           = $::lx_office_conf{mail_delivery} || {};
24 28
  $self->{security} = exists $security_config{lc $cfg->{security}} ? lc $cfg->{security} : 'none';
25 29
  my $sec_cfg       = $security_config{ $self->{security} };
26 30

  
27
  eval "require $sec_cfg->{require_module}" or die "$@";
31
  eval "require $sec_cfg->{require_module}" or do {
32
    $self->extended_status("$@");
33
    die $self->extended_status;
34
  };
28 35

  
29 36
  $self->{smtp} = $sec_cfg->{package}->new($cfg->{host} || 'localhost', Port => $cfg->{port} || $sec_cfg->{port});
30
  die unless $self->{smtp};
31

  
32
  $self->{smtp}->starttls(SSL_verify_mode => 0) || die if $self->{security} eq 'tls';
37
  if (!$self->{smtp}) {
38
    $self->extended_status('SMTP connection could not be initialized');
39
    die $self->extended_status;
40
  }
41

  
42
  if ($self->{security} eq 'tls') {
43
    $self->{smtp}->starttls(SSL_verify_mode => 0) or do {
44
      $self->extended_status("$@");
45
      die $self->extended_status;
46
    };
47
  }
33 48

  
34 49
  # Backwards compatibility: older Versions used 'user' instead of the
35 50
  # intended 'login'. Support both.
......
37 52

  
38 53
  return 1 unless $login;
39 54

  
40
  $self->{smtp}->auth($login, $cfg->{password}) or die;
55
  if (!$self->{smtp}->auth($login, $cfg->{password})) {
56
    $self->extended_status('SMTP authentication failed');
57
    die $self->extended_status;
58
  }
41 59
}
42 60

  
43 61
sub start_mail {
44 62
  my ($self, %params) = @_;
45 63

  
46
  $self->{smtp}->mail($params{from});
47
  $self->{smtp}->recipient(@{ $params{to} });
48
  $self->{smtp}->data;
64
  $self->{smtp}->mail($params{from})         or do { $self->extended_status($self->{smtp}->message); die $self->extended_status; };
65
  $self->{smtp}->recipient(@{ $params{to} }) or do { $self->extended_status($self->{smtp}->message); die $self->extended_status; };
66
  $self->{smtp}->data                        or do { $self->extended_status($self->{smtp}->message); die $self->extended_status; };
49 67
}
50 68

  
51 69
sub print {
......
76 94
sub send {
77 95
  my ($self) = @_;
78 96

  
79
  $self->{smtp}->dataend;
97
  my $ok = $self->{smtp}->dataend;
98
  $self->extended_status($self->{smtp}->message);
99
  $self->status('ok') if $ok;
100

  
80 101
  $self->{smtp}->quit;
102

  
81 103
  delete $self->{smtp};
82 104
}
83 105

  
SL/Mailer/Sendmail.pm
10 10

  
11 11
use Rose::Object::MakeMethods::Generic
12 12
(
13
  scalar => [ qw(myconfig mailer form) ]
13
  scalar => [ qw(myconfig mailer form status extended_status) ]
14 14
);
15 15

  
16 16
sub init {
17 17
  my ($self) = @_;
18 18

  
19
  Rose::Object::init(@_);
19
  Rose::Object::init(
20
    @_,
21
    status          => 'failed',
22
    extended_status => 'no send attempt made',
23
  );
20 24

  
21 25
  my $email         =  Encode::encode('utf-8', $self->myconfig->{email});
22 26
  $email            =~ s/[^\w\.\-\+=@]//ig;
......
26 30
  my $sendmail      = $::lx_office_conf{applications}->{sendmail} || $::lx_office_conf{mail_delivery}->{sendmail} || "sendmail -t";
27 31
  $sendmail         = $template->parse_block($sendmail);
28 32

  
29
  $self->{sendmail} = IO::File->new("|$sendmail") || die "sendmail($sendmail): $!";
33
  $self->{sendmail} = IO::File->new("|$sendmail") or do { $self->extended_status("sendmail($sendmail): $!"); die $self->extended_status; };
30 34
  $self->{sendmail}->binmode(':utf8');
31 35
}
32 36

  
......
36 40
sub print {
37 41
  my $self = shift;
38 42

  
39
  $self->{sendmail}->print(@_);
43
  $self->{sendmail}->print(@_) or do { $self->extended_status("sendmail: $!"); die $self->extended_status; };
40 44
}
41 45

  
42 46
sub send {
43 47
  my ($self) = @_;
44
  $self->{sendmail}->close;
48

  
49
  $self->{sendmail}->close or do { $self->extended_status("sendmail: $!"); die $self->extended_status; };
50

  
51
  $self->status('ok');
52
  $self->extended_status('');
53

  
45 54
  delete $self->{sendmail};
46 55
}
47 56

  
sql/Pg-upgrade2/email_journal.sql
1
-- @tag: email_journal
2
-- @description: Journal für verschickte E-Mails
3
-- @depends: release_3_3_0
4

  
5
-- Note: sender_id may be NULL to indicate a mail sent by the system
6
-- without a user being logged in – e.g. by the task server.
7
CREATE TABLE email_journal (
8
  id              SERIAL    NOT NULL,
9
  sender_id       INTEGER,
10
  "from"          TEXT      NOT NULL,
11
  recipients      TEXT      NOT NULL,
12
  sent_on         TIMESTAMP NOT NULL DEFAULT now(),
13
  subject         TEXT      NOT NULL,
14
  body            TEXT      NOT NULL,
15
  headers         TEXT      NOT NULL,
16
  status          TEXT      NOT NULL,
17
  extended_status TEXT      NOT NULL,
18
  itime           TIMESTAMP NOT NULL DEFAULT now(),
19
  mtime           TIMESTAMP NOT NULL DEFAULT now(),
20

  
21
  PRIMARY KEY (id),
22
  FOREIGN KEY (sender_id) REFERENCES employee (id),
23
  CONSTRAINT valid_status CHECK (status IN ('ok', 'failed'))
24
);
25

  
26
CREATE TABLE email_journal_attachments (
27
  id               SERIAL    NOT NULL,
28
  position         INTEGER   NOT NULL,
29
  email_journal_id INTEGER   NOT NULL,
30
  name             TEXT      NOT NULL,
31
  mime_type        TEXT      NOT NULL,
32
  content          BYTEA     NOT NULL,
33
  itime            TIMESTAMP NOT NULL DEFAULT now(),
34
  mtime            TIMESTAMP NOT NULL DEFAULT now(),
35

  
36
  PRIMARY KEY (id),
37
  FOREIGN KEY (email_journal_id) REFERENCES email_journal (id) ON DELETE CASCADE
38
);
39

  
40
CREATE TRIGGER mtime_email_journal             BEFORE UPDATE ON email_journal             FOR EACH ROW EXECUTE PROCEDURE set_mtime();
41
CREATE TRIGGER mtime_email_journal_attachments BEFORE UPDATE ON email_journal_attachments FOR EACH ROW EXECUTE PROCEDURE set_mtime();

Auch abrufbar als: Unified diff