html2email 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/LICENSE +23 -0
- data/README.markdown +110 -0
- data/Rakefile +57 -0
- data/bin/html2email +5 -0
- data/html2email.gemspec +47 -0
- data/lib/html2email/context.rb +42 -0
- data/lib/html2email/html_email.rb +59 -0
- data/lib/html2email/html_mailer.rb +49 -0
- data/lib/html2email/vendor/premailer/.gitignore +1 -0
- data/lib/html2email/vendor/premailer/CHANGELOG.rdoc +62 -0
- data/lib/html2email/vendor/premailer/LICENSE.rdoc +42 -0
- data/lib/html2email/vendor/premailer/README.rdoc +66 -0
- data/lib/html2email/vendor/premailer/bin/premailer +72 -0
- data/lib/html2email/vendor/premailer/bin/trollop.rb +739 -0
- data/lib/html2email/vendor/premailer/init.rb +1 -0
- data/lib/html2email/vendor/premailer/lib/premailer/html_to_plain_text.rb +81 -0
- data/lib/html2email/vendor/premailer/lib/premailer/premailer.rb +464 -0
- data/lib/html2email/vendor/premailer/lib/premailer.rb +9 -0
- data/lib/html2email/vendor/premailer/misc/client_support.yaml +230 -0
- data/lib/html2email/vendor/premailer/premailer.gemspec +20 -0
- data/lib/html2email/vendor/premailer/rakefile.rb +42 -0
- data/lib/html2email/vendor/premailer/tests/files/base.html +145 -0
- data/lib/html2email/vendor/premailer/tests/files/contact_bg.png +0 -0
- data/lib/html2email/vendor/premailer/tests/files/dialect.png +0 -0
- data/lib/html2email/vendor/premailer/tests/files/dots_end.png +0 -0
- data/lib/html2email/vendor/premailer/tests/files/dots_h.gif +0 -0
- data/lib/html2email/vendor/premailer/tests/files/import.css +13 -0
- data/lib/html2email/vendor/premailer/tests/files/inc/2009-placeholder.png +0 -0
- data/lib/html2email/vendor/premailer/tests/files/noimport.css +13 -0
- data/lib/html2email/vendor/premailer/tests/files/styles.css +102 -0
- data/lib/html2email/vendor/premailer/tests/test_helper.rb +2 -0
- data/lib/html2email/vendor/premailer/tests/test_html_to_plain_text.rb +73 -0
- data/lib/html2email/vendor/premailer/tests/test_link_resolver.rb +49 -0
- data/lib/html2email/vendor/premailer/tests/test_premailer.rb +124 -0
- data/lib/html2email.rb +110 -0
- data/spec/html2email.spec.rb +9 -0
- metadata +176 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
pkg/
|
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Html2Email: Tilt + Premailer + SMTP
|
2
|
+
http://github.com/guns/html2email
|
3
|
+
Copyright (c) 2010 Sung Pae <sung@metablu.com>
|
4
|
+
|
5
|
+
Published under the MIT License:
|
6
|
+
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
9
|
+
in the Software without restriction, including without limitation the rights
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
12
|
+
furnished to do so, subject to the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be included in
|
15
|
+
all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
Html2Email: Tilt + Premailer + SMTP
|
2
|
+
===================================
|
3
|
+
|
4
|
+
Making rich HTML emails is a pain. You throw away everything you know about
|
5
|
+
semantic markup, CSS, and keeping things DRY, and memorize a large heap of
|
6
|
+
senseless special cases for displaying correctly across at __least__ 30
|
7
|
+
combinations of email clients, operating systems, browsers, and online email
|
8
|
+
services; and even then, it's still not going to look right in Outlook 2007.
|
9
|
+
|
10
|
+
It's a strange game. The only winning move is not to play.
|
11
|
+
|
12
|
+
If you have to play though, you should have a workflow:
|
13
|
+
|
14
|
+
* Use your favorite templating engine: use any engine supported by [Tilt][]
|
15
|
+
* Don't abandon stylesheets: [Premailer][] inlines external styles for you
|
16
|
+
* Use a layout file for similar projects
|
17
|
+
* Override information in the layout per email
|
18
|
+
* Email previews to a list of test addresses right from the command line
|
19
|
+
|
20
|
+
Before you start, be sure to understand what you're getting into so that you
|
21
|
+
don't lose too much hair over your project:
|
22
|
+
|
23
|
+
<http://www.campaignmonitor.com/design-guidelines/>
|
24
|
+
|
25
|
+
Synopsis
|
26
|
+
--------
|
27
|
+
|
28
|
+
Command line, render email template with layout:
|
29
|
+
|
30
|
+
$ html2email --layout layout.haml client/offer.erb
|
31
|
+
# => Creates client/offer.html
|
32
|
+
|
33
|
+
Command line, pipe text into `html2email` with layout, get text back on STDOUT:
|
34
|
+
|
35
|
+
$ curl example.com/offer.html | html2email -l layout.haml > offer.html
|
36
|
+
|
37
|
+
Command line, send test email after render:
|
38
|
+
|
39
|
+
html2email -l layout.haml --email sung@metablu.com client/offer.erb
|
40
|
+
|
41
|
+
In the layout file, layout.haml. Note that linked stylesheets can be CSS,
|
42
|
+
[Sass][], or [LESS][]:
|
43
|
+
|
44
|
+
- @client_name = 'Example Inc.'
|
45
|
+
%html
|
46
|
+
%head
|
47
|
+
%title&= @page_title || 'A generic title'
|
48
|
+
%link{ :rel => 'stylesheet', :href => 'layout.sass',
|
49
|
+
:type => 'text/css', :media => 'screen' }
|
50
|
+
%body
|
51
|
+
= yield
|
52
|
+
|
53
|
+
In the email template, offer.erb. The `:prebinding` method takes a block which
|
54
|
+
makes available to both the layout and the template the variables created
|
55
|
+
in the block. It should ideally be declared at the top of the template file:
|
56
|
+
|
57
|
+
<% prebinding do %>
|
58
|
+
<% @page_title = 'An example offer!' %>
|
59
|
+
<% end %>
|
60
|
+
<table>
|
61
|
+
<tr>
|
62
|
+
<td>Hello, <%= @client_name %></td>
|
63
|
+
</tr>
|
64
|
+
</table>
|
65
|
+
|
66
|
+
Ruby interface:
|
67
|
+
|
68
|
+
require 'html2email/html_email'
|
69
|
+
|
70
|
+
html = HtmlEmail.new('client/offer.erb', 'layout.haml').render
|
71
|
+
File.open('client/offer.html', 'w') { |f| f.write html }
|
72
|
+
|
73
|
+
NOTE
|
74
|
+
----
|
75
|
+
|
76
|
+
Html2Email was put together very quickly to address a personal need. Most of the
|
77
|
+
work is done by [Tilt][] and [Premailer][], so it works just fine, but it lacks
|
78
|
+
tests and polish. Those are forthcoming.
|
79
|
+
|
80
|
+
Premailer is forked and vendored in `lib/html2email/vendor/premailer/`
|
81
|
+
|
82
|
+
Html2Email is not an email sending engine! It is only meant to ease the
|
83
|
+
development of HTML emails. You should be using a service like
|
84
|
+
[Campaign Monitor][] or [MailChimp][] to actually send bulk emails.
|
85
|
+
|
86
|
+
TODO
|
87
|
+
----
|
88
|
+
|
89
|
+
* Live mode (auto-compile with webrick server)
|
90
|
+
* Partials
|
91
|
+
* Tests
|
92
|
+
* Create <td width='\d+'> attributes in Premailer
|
93
|
+
* Mail configuration (external SMTP servers)
|
94
|
+
* Automatic conversion and upload of relative image links to a host via ssh/ftp?
|
95
|
+
* Maybe import ActionView or Sinatra::Helpers?
|
96
|
+
|
97
|
+
LICENSE
|
98
|
+
-------
|
99
|
+
|
100
|
+
http://github.com/guns/html2email
|
101
|
+
Copyright (c) 2010 Sung Pae <sung@metablu.com>
|
102
|
+
Distributed under the MIT license.
|
103
|
+
http://www.opensource.org/licenses/mit-license.php
|
104
|
+
|
105
|
+
[Tilt]: http://github.com/rtomayko/tilt
|
106
|
+
[Premailer]: http://github.com/alexdunae/premailer/
|
107
|
+
[Campaign Monitor]: http://www.campaignmonitor.com/
|
108
|
+
[MailChimp]: http://www.mailchimp.com/
|
109
|
+
[Sass]: http://sass-lang.com/
|
110
|
+
[LESS]: http://lesscss.org/
|
data/Rakefile
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec/rake/spectask'
|
2
|
+
require File.expand_path '../lib/html2email', __FILE__
|
3
|
+
|
4
|
+
### Helpers
|
5
|
+
|
6
|
+
verbose false
|
7
|
+
|
8
|
+
def gemspec
|
9
|
+
Gem::Specification.new do |gem|
|
10
|
+
gem.name = (@name = 'html2email')
|
11
|
+
gem.version = Html2Email::VERSION
|
12
|
+
gem.author = 'guns'
|
13
|
+
gem.email = 'sung@metablu.com'
|
14
|
+
gem.homepage = 'http://github.com/guns/html2email'
|
15
|
+
gem.summary = 'Html2Email: Tilt + Premailer + SMTP'
|
16
|
+
gem.description = 'Convert ruby html templates to an email compatible form.'
|
17
|
+
gem.executables = ['html2email']
|
18
|
+
gem.files = %x{git ls-files}.split("\n").reject { |f| f[/vendor/] } +
|
19
|
+
%x{cd #{base = 'lib/html2email/vendor/premailer'}; git ls-files}
|
20
|
+
.split("\n").map { |f| "#{base}/#{f}" }
|
21
|
+
gem.add_dependency 'rack'
|
22
|
+
gem.add_dependency 'tilt'
|
23
|
+
# Premailer
|
24
|
+
gem.add_dependency 'hpricot', '>= 0.6'
|
25
|
+
gem.add_dependency 'css_parser', '>= 0.9.1'
|
26
|
+
gem.add_dependency 'text-reform', '>= 0.2.0'
|
27
|
+
gem.add_dependency 'htmlentities', '>= 4.0.0'
|
28
|
+
# gem.add_development_dependency 'rspec'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
### Tasks
|
33
|
+
|
34
|
+
task :default => :build
|
35
|
+
|
36
|
+
desc 'Run unit tests'
|
37
|
+
Spec::Rake::SpecTask.new :spec do |t|
|
38
|
+
t.spec_files = FileList['spec/**/*.spec.rb']
|
39
|
+
t.spec_opts = %w[--color --format=progress --debugger]
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'Write gemspec and build gem'
|
43
|
+
task :build => :spec do
|
44
|
+
if (spec = gemspec).respond_to? :to_ruby
|
45
|
+
puts "### New gemspec: #{specfile = "#{@name}.gemspec"}"
|
46
|
+
File.open(specfile, 'w') { |f| f.write(spec.to_ruby) }
|
47
|
+
end
|
48
|
+
gemfile = Gem::Builder.new(spec) { |pkg| pkg.need_tar = true }.build
|
49
|
+
puts "### New gem: #{gemfile}"
|
50
|
+
mkdir_p 'pkg'; mv gemfile, (@gem = "pkg/#{@gemfile}")
|
51
|
+
end
|
52
|
+
|
53
|
+
desc 'Install gem'
|
54
|
+
task :install => :build do
|
55
|
+
puts "### Installing #{@gem}:"
|
56
|
+
system 'gem', 'install', @gem
|
57
|
+
end
|
data/bin/html2email
ADDED
data/html2email.gemspec
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{html2email}
|
5
|
+
s.version = "0.1.2"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["guns"]
|
9
|
+
s.date = %q{2010-05-24}
|
10
|
+
s.default_executable = %q{html2email}
|
11
|
+
s.description = %q{Convert ruby html templates to an email compatible form.}
|
12
|
+
s.email = %q{sung@metablu.com}
|
13
|
+
s.executables = ["html2email"]
|
14
|
+
s.files = [".gitignore", "LICENSE", "README.markdown", "Rakefile", "bin/html2email", "html2email.gemspec", "lib/html2email.rb", "lib/html2email/context.rb", "lib/html2email/html_email.rb", "lib/html2email/html_mailer.rb", "spec/html2email.spec.rb", "lib/html2email/vendor/premailer/.gitignore", "lib/html2email/vendor/premailer/CHANGELOG.rdoc", "lib/html2email/vendor/premailer/LICENSE.rdoc", "lib/html2email/vendor/premailer/README.rdoc", "lib/html2email/vendor/premailer/bin/premailer", "lib/html2email/vendor/premailer/bin/trollop.rb", "lib/html2email/vendor/premailer/init.rb", "lib/html2email/vendor/premailer/lib/premailer.rb", "lib/html2email/vendor/premailer/lib/premailer/html_to_plain_text.rb", "lib/html2email/vendor/premailer/lib/premailer/premailer.rb", "lib/html2email/vendor/premailer/misc/client_support.yaml", "lib/html2email/vendor/premailer/premailer.gemspec", "lib/html2email/vendor/premailer/rakefile.rb", "lib/html2email/vendor/premailer/tests/files/base.html", "lib/html2email/vendor/premailer/tests/files/contact_bg.png", "lib/html2email/vendor/premailer/tests/files/dialect.png", "lib/html2email/vendor/premailer/tests/files/dots_end.png", "lib/html2email/vendor/premailer/tests/files/dots_h.gif", "lib/html2email/vendor/premailer/tests/files/import.css", "lib/html2email/vendor/premailer/tests/files/inc/2009-placeholder.png", "lib/html2email/vendor/premailer/tests/files/noimport.css", "lib/html2email/vendor/premailer/tests/files/styles.css", "lib/html2email/vendor/premailer/tests/test_helper.rb", "lib/html2email/vendor/premailer/tests/test_html_to_plain_text.rb", "lib/html2email/vendor/premailer/tests/test_link_resolver.rb", "lib/html2email/vendor/premailer/tests/test_premailer.rb"]
|
15
|
+
s.homepage = %q{http://github.com/guns/html2email}
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubygems_version = %q{1.3.6}
|
18
|
+
s.summary = %q{Html2Email: Tilt + Premailer + SMTP}
|
19
|
+
|
20
|
+
if s.respond_to? :specification_version then
|
21
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
22
|
+
s.specification_version = 3
|
23
|
+
|
24
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
25
|
+
s.add_runtime_dependency(%q<rack>, [">= 0"])
|
26
|
+
s.add_runtime_dependency(%q<tilt>, [">= 0"])
|
27
|
+
s.add_runtime_dependency(%q<hpricot>, [">= 0.6"])
|
28
|
+
s.add_runtime_dependency(%q<css_parser>, [">= 0.9.1"])
|
29
|
+
s.add_runtime_dependency(%q<text-reform>, [">= 0.2.0"])
|
30
|
+
s.add_runtime_dependency(%q<htmlentities>, [">= 4.0.0"])
|
31
|
+
else
|
32
|
+
s.add_dependency(%q<rack>, [">= 0"])
|
33
|
+
s.add_dependency(%q<tilt>, [">= 0"])
|
34
|
+
s.add_dependency(%q<hpricot>, [">= 0.6"])
|
35
|
+
s.add_dependency(%q<css_parser>, [">= 0.9.1"])
|
36
|
+
s.add_dependency(%q<text-reform>, [">= 0.2.0"])
|
37
|
+
s.add_dependency(%q<htmlentities>, [">= 4.0.0"])
|
38
|
+
end
|
39
|
+
else
|
40
|
+
s.add_dependency(%q<rack>, [">= 0"])
|
41
|
+
s.add_dependency(%q<tilt>, [">= 0"])
|
42
|
+
s.add_dependency(%q<hpricot>, [">= 0.6"])
|
43
|
+
s.add_dependency(%q<css_parser>, [">= 0.9.1"])
|
44
|
+
s.add_dependency(%q<text-reform>, [">= 0.2.0"])
|
45
|
+
s.add_dependency(%q<htmlentities>, [">= 4.0.0"])
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rack/utils'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Templates are evaluated in the scope of Context.new
|
5
|
+
#
|
6
|
+
class Context
|
7
|
+
include Rack::Utils
|
8
|
+
alias_method :h, :escape_html
|
9
|
+
|
10
|
+
class PrebindingException < Exception; end
|
11
|
+
|
12
|
+
class Attr < Hash
|
13
|
+
def to_s
|
14
|
+
self.map { |k,v|
|
15
|
+
"#{Rack::Utils.escape_html k}='#{Rack::Utils.escape_html v}'"
|
16
|
+
}.join(' ')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@binding, @test_recipients = nil, []
|
22
|
+
end
|
23
|
+
|
24
|
+
def prebinding(&block)
|
25
|
+
return if @binding
|
26
|
+
@binding = (block.call; binding)
|
27
|
+
raise PrebindingException
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :test_recipients
|
31
|
+
def add_test_recipients(*list)
|
32
|
+
@test_recipients += list.flatten
|
33
|
+
end
|
34
|
+
|
35
|
+
def link_to(text, url, opts = {})
|
36
|
+
%Q(<a href='#{h url}' target='_blank' #{Attr[opts]}>#{text}</a>)
|
37
|
+
end
|
38
|
+
|
39
|
+
def image_tag(src, opts = {})
|
40
|
+
%Q(<img src='#{h src}' #{Attr[opts]} />)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'tilt'
|
3
|
+
require 'html2email/context'
|
4
|
+
require 'html2email/vendor/premailer/lib/premailer'
|
5
|
+
|
6
|
+
class HtmlEmail
|
7
|
+
def initialize(template, layout = nil, options = {})
|
8
|
+
@template, @layout, @options = template, layout, options
|
9
|
+
@options[:default_type] ||= 'str'
|
10
|
+
@context = Context.new
|
11
|
+
end
|
12
|
+
|
13
|
+
# returns converted html string
|
14
|
+
def render
|
15
|
+
buf = if @layout
|
16
|
+
# run through template once to catch prebinding block
|
17
|
+
begin
|
18
|
+
tilt_render @template
|
19
|
+
rescue Context::PrebindingException
|
20
|
+
rescue
|
21
|
+
# some other StandardError was raised, probably a NameError;
|
22
|
+
# reset the context to minimize the leaky abstraction
|
23
|
+
@context = Context.new
|
24
|
+
end
|
25
|
+
# return the full render with the new lexical binding
|
26
|
+
tilt_render(@layout) { tilt_render @template }
|
27
|
+
else
|
28
|
+
tilt_render @template
|
29
|
+
end
|
30
|
+
inline_css buf
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_recipients
|
34
|
+
@context.test_recipients
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def tilt_render(file)
|
40
|
+
klass = Tilt[file] || Tilt.mappings[@options[:default_type]]
|
41
|
+
klass.new(file).render(@context) { yield if block_given? }
|
42
|
+
end
|
43
|
+
|
44
|
+
# use Premailer to embed styles
|
45
|
+
def inline_css(html)
|
46
|
+
# Premailer only works on files, hence the tempfile
|
47
|
+
base = File.expand_path File.dirname(@layout || @template)
|
48
|
+
# any relative links are resolved by Premailer relative to the filepath
|
49
|
+
Tempfile.open(self.class.to_s, base) do |tmp|
|
50
|
+
tmp.write html; tmp.rewind
|
51
|
+
pre = Premailer.new tmp.path, :warn_level => Premailer::Warnings::RISKY
|
52
|
+
pre.warnings.each do |w|
|
53
|
+
warn "#{w[:message]} (#{w[:level]}) " +
|
54
|
+
"may not render properly in #{w[:clients]}"
|
55
|
+
end
|
56
|
+
pre.to_inline_css
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'net/smtp'
|
2
|
+
|
3
|
+
class HtmlMailer
|
4
|
+
MAX_RECIPIENTS = 10
|
5
|
+
|
6
|
+
attr_reader :from_addr
|
7
|
+
|
8
|
+
def initialize(messages, list, options = {})
|
9
|
+
@messages, @list = messages, list
|
10
|
+
@from_addr = options[:from_addr] ||
|
11
|
+
"do-not-reply@#{ENV['HOSTNAME'] || 'localhost'}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def html_send
|
15
|
+
check_recipients @list
|
16
|
+
|
17
|
+
Net::SMTP.start 'localhost' do |smtp|
|
18
|
+
@messages.each do |html|
|
19
|
+
smtp.send_message header(page_title(html)) + html, from_addr, @list
|
20
|
+
end
|
21
|
+
end
|
22
|
+
# user may not have a local mail server; let them down gentle
|
23
|
+
rescue Errno::ECONNREFUSED
|
24
|
+
warn '# Connection to localhost:25 refused! Is your mailserver running?'
|
25
|
+
end
|
26
|
+
|
27
|
+
def header(title)
|
28
|
+
%{From: Html2Email <#{from_addr}>
|
29
|
+
MIME-Version: 1.0
|
30
|
+
Content-type: text/html
|
31
|
+
Subject: Html2Email test#{": #{title}" if title && !title.empty?}\n
|
32
|
+
}.gsub(/^ +/,'')
|
33
|
+
end
|
34
|
+
|
35
|
+
def page_title(html)
|
36
|
+
html[/<head.*?>.*?<title.*?>(.*?)<\/title>/m, 1]
|
37
|
+
end
|
38
|
+
|
39
|
+
def check_recipients(list)
|
40
|
+
if list.empty?
|
41
|
+
raise '# No recipients defined for email test!'
|
42
|
+
elsif list.size > MAX_RECIPIENTS
|
43
|
+
raise "# Too many recipients defined! You shouldn't need any more than " +
|
44
|
+
MAX_RECIPIENTS.to_s
|
45
|
+
else
|
46
|
+
warn "# Sending test to #{list.join ', '}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
.DS_Store
|
@@ -0,0 +1,62 @@
|
|
1
|
+
= Premailer CHANGELOG
|
2
|
+
|
3
|
+
== Version 1.6.0
|
4
|
+
* --css option for specifying additional stylesheets
|
5
|
+
* copy related CSS attributes to HTML (inspired by http://github.com/peterbe/premailer/commit/c4f2634a99fc5005011ffde54ae0336ea1497ef5)
|
6
|
+
* ignore spaces around links in HREFs
|
7
|
+
|
8
|
+
== Version 1.5.4
|
9
|
+
* new bin/premailer script
|
10
|
+
* added missing htmlentities depenency to gemspec (thanks to http://github.com/usefulthink )
|
11
|
+
* fixed handling of unspecified <link> media types
|
12
|
+
|
13
|
+
== Version 1.5.3
|
14
|
+
* improved plaintext conversion
|
15
|
+
|
16
|
+
== Version 1.5.2
|
17
|
+
* released to GitHub
|
18
|
+
* fixed handling of mailto links
|
19
|
+
* various minor updates
|
20
|
+
|
21
|
+
== Version 1.5.1
|
22
|
+
* bugfix (http://code.google.com/p/premailer/issues/detail?id=1 and http://code.google.com/p/premailer/issues/detail?id=2) thanks to Russell Norris
|
23
|
+
* bugfix (http://code.google.com/p/premailer/issues/detail?id=4) thanks to Dave Holmes
|
24
|
+
|
25
|
+
== Version 1.5.0
|
26
|
+
* preview release of Ruby gem
|
27
|
+
|
28
|
+
== Version 1.4
|
29
|
+
* incremental parsing improvements
|
30
|
+
* respect <tt>@media</tt> rule (http://www.w3.org/TR/CSS21/media.html#at-media-rule)
|
31
|
+
* better quote escaping
|
32
|
+
|
33
|
+
== Version 1.3
|
34
|
+
* separate CSS parser into its own library
|
35
|
+
* handle <tt>background: red url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR42mP4%2F58BAAT%2FAf9jgNErAAAAAElFTkSuQmCC);</tt>
|
36
|
+
* preserve <tt>:hover</tt> etc... in head styles
|
37
|
+
|
38
|
+
== Version 1.2
|
39
|
+
* respect <tt>LINK</tt> media types
|
40
|
+
* better style folding
|
41
|
+
* incremental parsing improvements
|
42
|
+
|
43
|
+
== Version 1.1
|
44
|
+
* proper calculation of selector specificity per CSS 2.1 spec
|
45
|
+
* support for <tt>@import</tt>
|
46
|
+
* preliminary support for shorthand CSS properties (<tt>margin</tt>, <tt>padding</tt>)
|
47
|
+
* preliminary separation of CSS parser
|
48
|
+
|
49
|
+
== Version 1.0
|
50
|
+
* ported web interface to eRuby
|
51
|
+
* incremental parsing improvements
|
52
|
+
|
53
|
+
== Version 0.9
|
54
|
+
* initial proof-of-concept
|
55
|
+
* PHP web version
|
56
|
+
|
57
|
+
== TODO: Future
|
58
|
+
* complete shorthand properties support (<tt>border-width</tt>, <tt>font</tt>, <tt>background</tt>)
|
59
|
+
* UTF-8 and other charsets (test page: http://kianga.kcore.de/2004/09/21/utf8_test)
|
60
|
+
* make warnings for <tt>border</tt> match <tt>border-left</tt>, etc...
|
61
|
+
* Integrate CSS validator
|
62
|
+
* Remove unused classes and IDs
|
@@ -0,0 +1,42 @@
|
|
1
|
+
= Premailer License
|
2
|
+
|
3
|
+
Copyright (c) 2007-09 Alex Dunae
|
4
|
+
|
5
|
+
Premailer is copyrighted free software by Alex Dunae (http://dunae.ca/).
|
6
|
+
You can redistribute it and/or modify it under the conditions below:
|
7
|
+
|
8
|
+
1. You may make and give away verbatim copies of the source form of the
|
9
|
+
software without restriction, provided that you duplicate all of the
|
10
|
+
original copyright notices and associated disclaimers.
|
11
|
+
|
12
|
+
2. You may modify your copy of the software in any way, provided that
|
13
|
+
you do at least ONE of the following:
|
14
|
+
|
15
|
+
a) place your modifications in the Public Domain or otherwise
|
16
|
+
make them Freely Available, such as by posting said
|
17
|
+
modifications to the internet or an equivalent medium, or by
|
18
|
+
allowing the author to include your modifications in the software.
|
19
|
+
|
20
|
+
b) use the modified software only within your corporation or
|
21
|
+
organization.
|
22
|
+
|
23
|
+
c) rename any non-standard executables so the names do not conflict
|
24
|
+
with standard executables, which must also be provided.
|
25
|
+
|
26
|
+
d) make other distribution arrangements with the author.
|
27
|
+
|
28
|
+
3. You may modify and include the part of the software into any other
|
29
|
+
software (possibly commercial) as long as clear acknowledgement and
|
30
|
+
a link back to the original software (http://code.dunae.ca/premailer.web/)
|
31
|
+
is provided.
|
32
|
+
|
33
|
+
5. The scripts and library files supplied as input to or produced as
|
34
|
+
output from the software do not automatically fall under the
|
35
|
+
copyright of the software, but belong to whomever generated them,
|
36
|
+
and may be sold commercially, and may be aggregated with this
|
37
|
+
software.
|
38
|
+
|
39
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
40
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
41
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
42
|
+
PURPOSE.
|
@@ -0,0 +1,66 @@
|
|
1
|
+
= Premailer README
|
2
|
+
|
3
|
+
=== What is this?
|
4
|
+
|
5
|
+
For the best HTML e-mail delivery results, CSS should be inline. This is a
|
6
|
+
huge pain and a simple newsletter becomes un-managable very quickly. This
|
7
|
+
script is my solution.
|
8
|
+
|
9
|
+
* CSS styles are converted to inline style attributes
|
10
|
+
Checks <tt>style</tt> and <tt>link[rel=stylesheet]</tt> tags and preserves existing inline attributes
|
11
|
+
* Relative paths are converted to absolute paths
|
12
|
+
Checks links in <tt>href</tt>, <tt>src</tt> and CSS <tt>url('')</tt>
|
13
|
+
* CSS properties are checked against e-mail client capabilities
|
14
|
+
Based on the Email Standards Project's guides
|
15
|
+
* A plain text version is created
|
16
|
+
Optional
|
17
|
+
|
18
|
+
|
19
|
+
=== Installation
|
20
|
+
|
21
|
+
Download the Premailer gem from GemCutter.
|
22
|
+
|
23
|
+
gem sources -a http://gemcutter.org
|
24
|
+
sudo gem install premailer
|
25
|
+
|
26
|
+
=== Example
|
27
|
+
premailer = Premailer.new('http://example.com/myfile.html', :warn_level => Premailer::Warnings::SAFE)
|
28
|
+
|
29
|
+
# Write the HTML output
|
30
|
+
fout = File.open("output.html", "w")
|
31
|
+
fout.puts premailer.to_inline_css
|
32
|
+
fout.close
|
33
|
+
|
34
|
+
# Write the plain-text output
|
35
|
+
fout = File.open("ouput.txt", "w")
|
36
|
+
fout.puts premailer.to_plain_text
|
37
|
+
fout.close
|
38
|
+
|
39
|
+
# Output any CSS warnings
|
40
|
+
premailer.warnings.each do |w|
|
41
|
+
puts "#{w[:message]} (#{w[:level]}) may not render properly in #{w[:clients]}"
|
42
|
+
end
|
43
|
+
|
44
|
+
=== Contributions
|
45
|
+
|
46
|
+
Contributions are most welcome. Premailer was rotting away in a private SVN repository for too long and could use some TLC. Pull and patch to your heart's content.
|
47
|
+
|
48
|
+
A few areas that are particularly in need of love:
|
49
|
+
* Testing suite
|
50
|
+
There were unit tests but they were so funky that it was better to just strip them out.
|
51
|
+
* Test running Premailer on local files
|
52
|
+
* Create a binary file for easing command line use, allowing the output to be piped in *nix systems
|
53
|
+
* Ruby 1.9 testing
|
54
|
+
* Test with Rails
|
55
|
+
* Move un-repeated background images defined in CSS to <tt><td background=""></tt> for Outlook
|
56
|
+
* Correctly parse http://www.webstandards.org/files/acid2/test.html
|
57
|
+
|
58
|
+
=== Credits and code
|
59
|
+
|
60
|
+
Premailer is written in Ruby.
|
61
|
+
|
62
|
+
The web interface can be found at http://premailer.dialect.ca .
|
63
|
+
|
64
|
+
The source code can be found at http://github.com/alexdunae/premailer .
|
65
|
+
|
66
|
+
Written by Alex Dunae (dunae.ca, e-mail 'code' at the same domain), 2008-2009.
|
@@ -0,0 +1,72 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# = Premailer
|
4
|
+
require 'trollop'
|
5
|
+
require File.join(File.dirname(__FILE__), '../lib/premailer')
|
6
|
+
|
7
|
+
opts = Trollop::options do
|
8
|
+
version "Premailer #{Premailer::VERSION} (c) 2008-2009 Alex Dunae"
|
9
|
+
banner <<-EOS
|
10
|
+
Improve the rendering of HTML emails by making CSS inline, converting links and warning about unsupported code.
|
11
|
+
|
12
|
+
Usage:
|
13
|
+
premailer [options] inputfile [outputfile] [warningsfile]
|
14
|
+
|
15
|
+
Example
|
16
|
+
premailer http://example.com/
|
17
|
+
premailer http://example.com/ out.html out.txt warnings.txt
|
18
|
+
premailer --base-url=http://example.com/ src.html out.html
|
19
|
+
|
20
|
+
Options:
|
21
|
+
EOS
|
22
|
+
opt :base_url, "Manually set the base URL, useful for local files", :type => String
|
23
|
+
opt :query_string, "Query string to append to links", :type => String, :short => 'q'
|
24
|
+
opt :line_length, "Length of lines when creating plaintext version", :type => :int, :default => 65, :short => 'l'
|
25
|
+
opt :remove_classes, "Remove classes from the HTML document?", :default => false
|
26
|
+
opt :css, "Manually specify css stylesheets", :type => String, :multi => true
|
27
|
+
opt :verbose, '', :default => false, :short => 'v'
|
28
|
+
end
|
29
|
+
|
30
|
+
inputfile = ARGV.shift
|
31
|
+
outfile = ARGV.shift
|
32
|
+
txtfile = ARGV.shift
|
33
|
+
warningsfile = ARGV.shift
|
34
|
+
|
35
|
+
Trollop::die "inputfile is missing" if inputfile.nil?
|
36
|
+
|
37
|
+
premailer_opts = {
|
38
|
+
:base_url => opts[:base_url],
|
39
|
+
:query_string => opts[:query_string],
|
40
|
+
:show_warnings => opts[:show_warnings] ? Premailer::Warnings::SAFE : Premailer::Warnings::NONE,
|
41
|
+
:line_length => opts[:line_length],
|
42
|
+
:remove_classes => opts[:remove_classes],
|
43
|
+
:css => opts[:css]
|
44
|
+
}
|
45
|
+
|
46
|
+
premailer = Premailer.new(inputfile, premailer_opts)
|
47
|
+
|
48
|
+
# html output
|
49
|
+
if outfile
|
50
|
+
fout = File.open(outfile, 'w')
|
51
|
+
fout.puts premailer.to_inline_css
|
52
|
+
fout.close
|
53
|
+
else
|
54
|
+
p premailer.to_inline_css
|
55
|
+
exit
|
56
|
+
end
|
57
|
+
|
58
|
+
# plaintext output
|
59
|
+
if txtfile
|
60
|
+
fout = File.open(txtfile, 'w')
|
61
|
+
fout.puts premailer.to_plain_text
|
62
|
+
fout.close
|
63
|
+
end
|
64
|
+
|
65
|
+
# warnings output
|
66
|
+
if warningsfile
|
67
|
+
fout = File.open(warningsfile, 'w')
|
68
|
+
premailer.warnings.each do |w|
|
69
|
+
fout.puts "- #{w[:message]} (#{w[:level]}) may not render properly in #{w[:clients]}"
|
70
|
+
end
|
71
|
+
fout.close
|
72
|
+
end
|