File Coverage

blib/lib/Email/Mailer.pm
Criterion Covered Total %
statement 75 77 97.4
branch 24 28 85.7
condition 26 40 65.0
subroutine 13 13 100.0
pod 2 2 100.0
total 140 160 87.5


line stmt bran cond sub pod time code
1             package Email::Mailer;
2             # ABSTRACT: Multi-purpose emailer for HTML, auto-text, attachments, and templates
3              
4 3     3   594396 use 5.014;
  3         15  
5 3     3   2011 use exact -noautoclean;
  3         173163  
  3         18  
6              
7 3     3   15785 use Email::MessageID;
  3         4281  
  3         204  
8 3     3   2317 use Email::MIME 1.940;
  3         192770  
  3         464  
9 3     3   2191 use Email::MIME::CreateHTML;
  3         560688  
  3         374  
10 3     3   1936 use Email::Sender::Simple 'sendmail';
  3         909826  
  3         38  
11 3     3   2715 use HTML::FormatText;
  3         139585  
  3         367  
12 3     3   3214 use HTML::TreeBuilder;
  3         45242  
  3         384  
13 3     3   2740 use IO::All 'io';
  3         55402  
  3         26  
14 3     3   357 use MIME::Words 'encode_mimewords';
  3         9  
  3         369  
15 3     3   26 use Encode qw( encode is_utf8 );
  3         6  
  3         9691  
16              
17             our $VERSION = '1.22'; # VERSION
18              
19             sub new {
20 38     38 1 412745 my $self = shift;
21              
22 38 100       105 unless ( ref $self ) {
23             # $self is not an object, is incoming pair values = make $self object
24 20         107 $self = bless( {@_}, $self );
25             }
26             else {
27             # $self is an object = make a new $self object incorporating any new values
28 18         117 $self = bless( { %$self, @_ }, ref $self );
29             }
30              
31             # for a certain set of keys, ensure they are all lower-case
32             $self->{ lc $_ } = delete $self->{$_}
33 38 100       139 for ( grep { /^(?:to|from|subject|html|text)$/i and /[A-Z]/ } keys %$self );
  106         533  
34              
35 38         201 return $self;
36             }
37              
38             sub send {
39 20     20 1 376727 my $self = shift;
40              
41             # if @_ is a set of hashrefs, map them into new mail objects; otherwise, just merge in new values;
42             # then iterate through the objects inside the map
43             my @mails = map {
44             # make a clean copy of the data so we can return the mail object unchanged at the end
45 21         77 my $mail = {%$_};
46              
47             # process any template functionality (look for values that are scalarrefs)
48 21 100       96 if ( ref $mail->{process} eq 'CODE' ) {
49 2         20 $mail->{$_} = $mail->{process}->( ${ $mail->{$_} }, $mail->{data} || {} )
50 1   50     3 for ( grep { ref $mail->{$_} eq 'SCALAR' } keys %$mail );
  6         9  
51             }
52              
53             # automatically create the text version from HTML if there is no text version and there is HTML
54 21 100 100     135 if ( $mail->{html} and not $mail->{text} ) {
55 10   100     48 my $width = $mail->{width} // 72;
56 10   100     38 $width ||= 1_000_000;
57              
58             $mail->{text} = HTML::FormatText
59             ->new( leftmargin => 0, rightmargin => $width )
60 10         115 ->format( HTML::TreeBuilder->new->parse( $mail->{html} . "\n" ) );
61             }
62              
63 21 100       32142 $mail->{text} = encode( 'UTF-8', $mail->{text} ) if is_utf8( $mail->{text} );
64              
65 21         373 my @keys = keys %$mail;
66 21         58 for my $name ( qw( to from subject ) ) {
67 63         90 my ($key) = grep { lc($_) eq $name } @keys;
  240         400  
68             $mail->{$key} = encode_mimewords( $mail->{$key}, Charset => 'UTF-8' )
69 63 50 66     254 if ( $key and defined $mail->{$key} and $mail->{$key} =~ /[^[:ascii:]]/ );
      66        
70             }
71              
72 21   33     218 $mail->{'Message-Id'} //= Email::MessageID->new->in_brackets;
73              
74             # create a headers hashref (delete things from a data copy that known to not be headers)
75             my $headers = [
76             map {
77 48 50       121 $mail->{$_} = join( ',', @{ $mail->{$_} } ) if ( ref $mail->{$_} eq 'ARRAY' );
  0         0  
78 48 50       101 $mail->{$_} = join( ',', values %{ $mail->{$_} } ) if ( ref $mail->{$_} eq 'HASH' );
  0         0  
79 48         165 ucfirst($_) => $mail->{$_};
80             }
81 21         5487 grep { not /^(?:html|text|embed|attachments|process|data|transport|width)$/i }
  101         337  
82             sort keys %$mail
83             ];
84              
85             # build up an attachments arrayref of attachment MIME objects
86             my $attachments = ( not $mail->{attachments} or ref $mail->{attachments} ne 'ARRAY' ) ? [] : [
87             map {
88             Email::MIME->create(
89             attributes => {
90             disposition => 'attachment',
91             content_type => $_->{ctype} || 'application/octet-stream',
92             encoding => $_->{encoding} // 'base64',
93             filename => $_->{name} || $_->{filename} || $_->{source},
94             name => $_->{name} || $_->{filename} || $_->{source},
95             },
96 8 100 50     2152 body => ( ( $_->{content} ) ? $_->{content} : io( $_->{source} )->binary->all ),
      50        
      33        
      33        
97             ),
98 21 100 66     149 } @{ $mail->{attachments} }
  7         27  
99             ];
100              
101             # build a single MIME email object to send based on what data we have for the email
102 21         15396 my $email_mime;
103 21 100 66     247 if ( $mail->{text} and not $mail->{html} and @$attachments == 0 ) {
    100 100        
      66        
104             $email_mime = Email::MIME->create(
105             header_str => $headers,
106             body => $mail->{text},
107 3         32 attributes => {
108             charset => 'UTF-8',
109             encoding => 'quoted-printable',
110             },
111             );
112             }
113             elsif ( $mail->{text} and not $mail->{html} ) {
114             $email_mime = Email::MIME->create(
115             header_str => $headers,
116             attributes => { content_type => 'multipart/mixed' },
117             parts => [
118             Email::MIME->create(
119             header_str => [],
120             body => $mail->{text},
121 2         23 attributes => {
122             charset => 'UTF-8',
123             encoding => 'quoted-printable',
124             },
125             ),
126             @$attachments,
127             ],
128             );
129             }
130             else {
131             my $html_email = Email::MIME->create_html(
132             header => [],
133             body => $mail->{html},
134             text_body => $mail->{text},
135             embed => $mail->{embed},
136 16         211 text_body_attributes => {
137             charset => 'UTF-8',
138             encoding => 'quoted-printable',
139             },
140             );
141              
142 16         201070 $email_mime = Email::MIME->create(
143             header_str => $headers,
144             attributes => { content_type => 'multipart/mixed' },
145             parts => [ $html_email, @$attachments ],
146             );
147             }
148              
149             # send the email with Email::Sender::Simple
150 21         88991 sendmail( $email_mime, { transport => $mail->{transport} } );
151              
152 21         321 $_;
153 20 100       124 } ( ref $_[0] eq 'HASH' ) ? ( map { $self->new(%$_) } @_ ) : $self->new(@_);
  2         5  
154              
155             # return the mail objects as desired by the caller
156 20 50       115 return ( wantarray() ) ? (@mails) : \@mails;
157             }
158              
159             1;
160              
161             __END__