html2email 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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