html2email 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. data/.gitignore +1 -0
  2. data/LICENSE +23 -0
  3. data/README.markdown +110 -0
  4. data/Rakefile +57 -0
  5. data/bin/html2email +5 -0
  6. data/html2email.gemspec +47 -0
  7. data/lib/html2email/context.rb +42 -0
  8. data/lib/html2email/html_email.rb +59 -0
  9. data/lib/html2email/html_mailer.rb +49 -0
  10. data/lib/html2email/vendor/premailer/.gitignore +1 -0
  11. data/lib/html2email/vendor/premailer/CHANGELOG.rdoc +62 -0
  12. data/lib/html2email/vendor/premailer/LICENSE.rdoc +42 -0
  13. data/lib/html2email/vendor/premailer/README.rdoc +66 -0
  14. data/lib/html2email/vendor/premailer/bin/premailer +72 -0
  15. data/lib/html2email/vendor/premailer/bin/trollop.rb +739 -0
  16. data/lib/html2email/vendor/premailer/init.rb +1 -0
  17. data/lib/html2email/vendor/premailer/lib/premailer/html_to_plain_text.rb +81 -0
  18. data/lib/html2email/vendor/premailer/lib/premailer/premailer.rb +464 -0
  19. data/lib/html2email/vendor/premailer/lib/premailer.rb +9 -0
  20. data/lib/html2email/vendor/premailer/misc/client_support.yaml +230 -0
  21. data/lib/html2email/vendor/premailer/premailer.gemspec +20 -0
  22. data/lib/html2email/vendor/premailer/rakefile.rb +42 -0
  23. data/lib/html2email/vendor/premailer/tests/files/base.html +145 -0
  24. data/lib/html2email/vendor/premailer/tests/files/contact_bg.png +0 -0
  25. data/lib/html2email/vendor/premailer/tests/files/dialect.png +0 -0
  26. data/lib/html2email/vendor/premailer/tests/files/dots_end.png +0 -0
  27. data/lib/html2email/vendor/premailer/tests/files/dots_h.gif +0 -0
  28. data/lib/html2email/vendor/premailer/tests/files/import.css +13 -0
  29. data/lib/html2email/vendor/premailer/tests/files/inc/2009-placeholder.png +0 -0
  30. data/lib/html2email/vendor/premailer/tests/files/noimport.css +13 -0
  31. data/lib/html2email/vendor/premailer/tests/files/styles.css +102 -0
  32. data/lib/html2email/vendor/premailer/tests/test_helper.rb +2 -0
  33. data/lib/html2email/vendor/premailer/tests/test_html_to_plain_text.rb +73 -0
  34. data/lib/html2email/vendor/premailer/tests/test_link_resolver.rb +49 -0
  35. data/lib/html2email/vendor/premailer/tests/test_premailer.rb +124 -0
  36. data/lib/html2email.rb +110 -0
  37. data/spec/html2email.spec.rb +9 -0
  38. 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
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require 'html2email'
5
+ Html2Email.new(ARGV).run
@@ -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