diff --git a/README.md b/README.md index a2ec880..38e5743 100644 --- a/README.md +++ b/README.md @@ -324,6 +324,16 @@ Get stats for a campaign AhoyEmail.stats("my-campaign") ``` +## HTML5 Parsing + +By default, this gem uses Nokogiri's HTML 4 parser to rewrite href attributes for the `utm_params` and `track_clicks` features. This can cause link tags to be prematurely closed if they were wrapping table elements, because doing so violates the HTML 4 spec. + +To use HTML5 parsing instead, set this in an initializer: + +```ruby +AhoyEmail.html5 = true +``` + ## History View the [changelog](https://github.com/ankane/ahoy_email/blob/master/CHANGELOG.md) diff --git a/lib/ahoy_email.rb b/lib/ahoy_email.rb index 9e25eff..5b693a7 100644 --- a/lib/ahoy_email.rb +++ b/lib/ahoy_email.rb @@ -24,7 +24,7 @@ require_relative "ahoy_email/engine" if defined?(Rails) module AhoyEmail - mattr_accessor :secret_token, :default_options, :subscribers, :invalid_redirect_url, :track_method, :api, :preserve_callbacks, :save_token + mattr_accessor :secret_token, :default_options, :subscribers, :invalid_redirect_url, :track_method, :api, :preserve_callbacks, :save_token, :html5 mattr_writer :message_model self.api = false @@ -79,6 +79,8 @@ module AhoyEmail self.save_token = false + self.html5 = false + self.subscribers = [] self.preserve_callbacks = [] diff --git a/lib/ahoy_email/processor.rb b/lib/ahoy_email/processor.rb index 8f22fbf..7261596 100644 --- a/lib/ahoy_email/processor.rb +++ b/lib/ahoy_email/processor.rb @@ -53,7 +53,7 @@ def track_links if html_part? part = message.html_part || message - doc = Nokogiri::HTML::Document.parse(part.body.raw_source) + doc = parse_message(part.body.raw_source) doc.css("a[href]").each do |link| uri = parse_uri(link["href"]) next unless trackable?(uri) @@ -92,6 +92,14 @@ def track_links end end + def parse_message(raw_source) + if AhoyEmail.html5 + Nokogiri::HTML5.parse(raw_source) + else + Nokogiri::HTML::Document.parse(raw_source) + end + end + def html_part? (message.html_part || message).content_type =~ /html/ end diff --git a/test/internal/app/mailers/utm_params_mailer.rb b/test/internal/app/mailers/utm_params_mailer.rb index b16a96a..3ed6dd3 100644 --- a/test/internal/app/mailers/utm_params_mailer.rb +++ b/test/internal/app/mailers/utm_params_mailer.rb @@ -20,6 +20,10 @@ def nested mail_html('') end + def nested_table + mail_html('
') + end + def multiple mail_html('Test') end diff --git a/test/test_helper.rb b/test/test_helper.rb index 58cbe77..96f30fc 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -85,6 +85,12 @@ def with_save_token yield end end + + def with_html5 + AhoyEmail.stub(:html5, true) do + yield + end + end end class ActionDispatch::IntegrationTest diff --git a/test/utm_params_test.rb b/test/utm_params_test.rb index fad9128..256d4ad 100644 --- a/test/utm_params_test.rb +++ b/test/utm_params_test.rb @@ -30,6 +30,23 @@ def test_nested assert_body '', message end + # When nokogiri parses with html5, it allows an tag to wrap a tag + def test_nested_table_html5 + with_html5 do + message = UtmParamsMailer.nested_table.deliver_now + assert_body "utm_medium=email", message + assert_body '
', message + end + end + + # When nokogiri parses with html4, it disallows an tag to wrap a tag, + # and closes the tag before the
tag + def test_nested_table_html4 + message = UtmParamsMailer.nested_table.deliver_now + assert_body "utm_medium=email", message + assert_body '
', message + end + def test_multiple message = UtmParamsMailer.multiple.deliver_now assert_body "utm_campaign=second", message