package Email::Simple::Markdown;
our $AUTHORITY = 'cpan:YANICK';
# ABSTRACT: simple email creation with auto text and html multipart body
$Email::Simple::Markdown::VERSION = '0.7.3';
use strict;
use warnings;
use Carp;
use Email::Abstract;
use Email::MIME;
use Email::Simple;
use List::Util 1.29 qw/ first pairmap /;
use Module::Runtime qw/ use_module /;
use Moo;
extends 'Email::Simple';
sub BUILDARGS {
return {};
}
around create => sub {
my ( $orig, $self, %arg ) = @_;
my @local_args = qw/ css markdown_engine pre_markdown_filter charset /;
my %md_arg;
@md_arg{@local_args} = delete @arg{@local_args};
my $email = $orig->( $self, %arg );
$email->markdown_engine_set(
$md_arg{markdown_engine}||'auto'
);
$email->charset_set( $md_arg{charset} ) if $md_arg{charset};
$email->css_set($md_arg{css}) if $md_arg{css};
$email->pre_markdown_filter_set($md_arg{pre_markdown_filter})
if $md_arg{pre_markdown_filter};
return $email;
};
our @SUPPORTED_ENGINES = qw/ Text::MultiMarkdown Text::Markdown /;
has markdown_engine => (
is => 'rw',
lazy => 1,
default => sub { $_[0]->find_markdown_engine },
clearer => 1,
coerce => sub {
my( $value ) = @_;
return $value eq 'auto'
? __PACKAGE__->find_markdown_engine
: $value;
},
trigger => sub{
my( $self, $engine ) = @_;
die "engine '$engine' not implementing a 'markdown' method\n"
unless use_module($engine)->can('markdown');
$self->clear_markdown_object;
},
writer => 'markdown_engine_set',
);
has markdown_object => (
is => 'rw',
lazy => 1,
clearer => 1,
default => sub { $_[0]->{markdown_engine}->new },
handles => {
_markdown => 'markdown',
}
);
sub find_markdown_engine {
first { eval { use_module($_) } } @SUPPORTED_ENGINES
or die "No supported markdown engine found"
}
has markdown_css => (
is => 'rw',
reader => 'css',
writer => 'css_set',
coerce => sub {
my $css = shift;
if ( ref $css eq 'ARRAY' ) {
my @css = @$css;
croak "number of argument is not even" if @css % 2;
$css = join "\n", pairmap { "$a { $b }" } @css;
}
$css;
},
);
has markdown_filter => (
is => 'rw',
writer => 'pre_markdown_filter_set',
);
has markdown_charset => (
is => 'rw',
writer => 'charset_set',
);
sub with_markdown {
my $self = shift;
my $body = $self->body;
my $mail = Email::Abstract->new($self->SUPER::as_string)
->cast('Email::MIME');
$mail->content_type_set('multipart/alternative');
$mail->parts_set([
Email::MIME->create(
attributes => {
content_type => 'text/plain',
charset => $self->markdown_charset
},
body => $body,
),
Email::MIME->create(
attributes => {
content_type => 'text/html',
charset => $self->markdown_charset,
encoding => 'quoted-printable',
},
body => $self->markdown_part,
)
]);
return Email::Abstract->new($mail);
}
sub markdown_part {
my $self = shift;
my $markdown = $self->body;
if( my $filter = $self->markdown_filter ) {
local $_ = $markdown;
$filter->();
$markdown = $_;
}
$markdown = $self->_markdown($markdown);
$markdown = ''
. $markdown
if $self->css;
return $markdown;
}
sub as_string { $_[0]->with_markdown->as_string }
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Email::Simple::Markdown - simple email creation with auto text and html multipart body
=head1 VERSION
version 0.7.3
=head1 SYNOPSIS
use Email::Simple::Markdown;
my $email = Email::Simple::Markdown->create(
header => [
From => 'me@here.com',
To => 'you@there.com',
Subject => q{Here's a multipart email},
],
body => '[this](http://metacpan.org/search?q=Email::Simple::Markdown) is *amazing*',
);
print $email->as_string;
=head1 DESCRIPTION
I behaves almost exactly like L,
excepts for one detail: when its method C is invoked, the
returned string representation of the email has multipart body with a
I element (the original body), and a I element,
the markdown rendering of the text body.
The markdown convertion is done using L.
=head1 METHODS
I inherits all the methods if L.
In addition, it provides one more method: I.
=head2 create( ... )
Behaves like L's C, but accepts the following
additional arguments:
=over
=item markdown_engine => $module
See C. If not given, defaults to C.
=item css => $stylesheet
If provided, the html part of the email will be prepended with the given
stylesheet, wrapped by a I tag.
=item pre_markdown_filter => sub { ... }
See C.
=item charset => $charset
The character set supplied to C. By default, no character set
is passed.
=back
=head2 markdown_engine
Returns the markdown engine used by the object.
=head2 markdown_engine_set( $module )
Sets the markdown engine to be used by the object.
Accepts C, L, L, or any module
implementing a C method.
If not specified or set to C, the object will use the first markdown module it finds
between L and L.
=head2 css
Returns the cascading stylesheet that is applied to the html part of the
email.
=head2 css_set( $stylesheet )
Sets the cascading stylesheet for the html part of the email to be
I<$stylesheet>.
$email->css_set( <<'END_CSS' );
p { color: red; }
pre { border-style: dotted; }
END_CSS
The I<$stylesheet> can also be an array ref, holding key/value pairs where
the key is the css selector and the value the attached style. For example,
the equivalent call to the one given above would be:
$email->css_set([
p => 'color: red;',
pre => 'border-style: dotted;',
]);
=head2 pre_markdown_filter_set( sub{ ... } );
Sets a filter to be run on the body before the markdown transformation is
done. The body will be passed as C<$_> and should be modified in-place.
E.g., to add a header to the email:
$mail->pre_markdown_filter_set(sub {
s#^##;
});
=head2 charset_set( $charset )
Sets the charset to be used by the email.
=head2 with_markdown()
Returns an L representation of the email, with
its multipart body.
=head1 AUTHOR
Yanick Champoux
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2022, 2017, 2014, 2013, 2012 by Yanick Champoux.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut