Markdown to text/plain and text/html for multipart email - ruby-on-rails

I’m looking for a solution to send DRY multipart emails in Rails. With DRY I mean that the content for the mail is only defined once.
I’ve thought about some possible solutions but haven’t found any existing implementations.
The solutions I’ve thought about are:
load the text from I18n and apply Markdown for the html mail and apply Markdown with a special output type for the text mail where
links are put in parenthesis after the link text
bold, italic and other formatting that doesn't make sense are removed
ordered and unordered lists are maintained
generate only the html mail and convert that to text according to the above conditions
Is there any available solution out there? Which one is probably the better way to do it?

In Chapter 4 of Crafting Rails Applications, Jóse Valim walks you through how to make a "merb" handler that uses markdown with interspersed erb and can compile to text and html. Then you make a mailer generator that generates a single merb template for each of your mail actions.
You can read an excerpt from that chapter on the page I linked you to. I highly recommend buying the book.
If you're interested in using my sorry version of what he describes in that book, you can slap this in your Gemfile:
gem 'handlers', :git => "git://github.com/chadoh/handlers.git"
Be warned that I barely know what I'm doing, that I'm not versioning that gem, and that I probably won't really even maintain it. Frankly, I wish I could find someone else who was doing a better job, but I've been unsuccessful in doing so. If you want to fork my project and be the person doing that better job, go for it!

This is a PITA, but is the only way to DRY mail such that you can support both HTML (multipart) & plaintext:
Put the html email copy in a partial file in your ActionMailer view directory with the following extension: _action.html.erb
Replace "action" with whatever action name you are using.
Then create 2 more files in the same directory:
action.text.html.erb and
action.text.plain.erb
In the text.html partial:
<%= render "action.html", :locals => {:html => true} %>
In the text.plain partial:
<% content = render "action.html", :locals => {:html => false} %>
<%= strip_tags(content) %>
That works for me, though it certainly makes me want to pay the monthly service for madmimi

Use the maildown gem.
This gems does the heavy lifting of allowing you to use email.md.erb instead of email.html.erb and email.text.erb. Write it once in a sane format and have it automatically display in HTML and in Plain Text. Win.
There are some intricacies here that you'll want to look at based on your use-case, but here's some of what we did to get it working well:
Create a maildown.rb initializer to setup some sane defaults:
Maildown.allow_indentation = true # Prevents code blocks from forming when using indentiation in markdown emails.
Maildown::MarkdownEngine.set_text do |text|
text.gsub( /{:.*}\n?/, "" ) # Removes Kramdown annotations that apply classes, etc. with `{: .class }`.
This allows you to use indents in your blocks, etc. But also precludes the ability to add indents in your Plain Text. It also removes Kramdown-specific annotation from Plain Text.
Then just replace your HTML and Plain Text files with a single .md.erb file and test it out to make sure it looks good in both versions.
Note, until you remove the .html.erb and .text.erb files, it will show those first before looking for a .md.erb file. This may actually be a nice feature if you ever needed to write separate formats for a specific email (maybe a marketing one that requires more complex formatting than Markdown can provide) without having to specify anything anywhere.
Works a treat.

Related

rails 5.x: add nofollow to all links in 'sanitize'

I am working on a Rails application whose HAML templates frequently make use of a routine called sanitize. I have deduced from context that this routine sanitizes user-controlled HTML. Example:
# views/feed_items/_about.html.haml
%h3 Summary:
.description
= sanitize #feed_item.description
I want to make this routine add 'rel=nofollow' to all outbound links, in addition to what it's already doing. What is the most straightforward way to do that?
N.B. I am not having any luck finding the definition of this method, or the official configuration knobs for it. The vendor directory has two different HTML sanitizer gems in it and I can't even figure out which one is being used. This is a large, complicated web application that I did not write, and I barely understand Ruby, let alone all of Rails' extensions to it. Please assume I do not know any of the things that you think are obvious.
The sanitizer will strip out the rel tags if they exist.
I ran into a similar issue and added an additional helper method - clean_links to the ApplicationHelper module, and called it after sanitizing the content.
# application_helper.rb
def clean_links html
html.gsub!(/\\2')
html.html_safe
end
This method looks for all <a> tags, and adds rel="nofollow". The html_safe method is necessary or else the HTML will be displayed as a string (it's already been sanitized).
This solution treats all links equally, so if you only want this for links pointing outside the domain, you'll have to update the REGEX accordingly.
In your view: <%= clean_links sanitize(#something) %>
So, first the content is sanitized, then you add the rel="nofollow" tag before displaying the link.
Actually there's a built-in way:
sanitize "your input", scrubber: Loofah::Scrubbers::NoFollow.new

Rendering Pagedown Markdown in Rails 4

I have a properly working pagedown editor in a rails 4 application, but I believe I'm missing something simple to render the pagedown data at a later point in my application.
Here's the gem (and initialization) I'm using: https://github.com/hughevans/pagedown-bootstrap-rails
Any ideas on how to render that data with the already-used gems?
EDIT: I think the root of my problem is it's not storing the data as the HTML version, so it doesn't render it when I display the data again. It's missing the converter step when the form gets saved, but I don't see any specific instruction on how to do that, so I assumed it was default part of these gems.
EDIT2: I've since taken the approach to convert the Markdown on each page load, with the following code:
Unfortunately, it doesn't use all the Pagedown Markdown, but it's at least handling new lines properly:
Any ideas?
Thanks!
So the answer to this question is two fold. Either you can convert the MD to HTML and store it in the database, or leave it as MD in the DB and convert it to HTML every time you want to render it. Note that you'll need to convert it back to MD (I'm not sure if this is entirely easy or not) if you want that field to be editable in the original MD.
Since this app doesn't care about performance, I decided to store it as MD and render it.
The results I was getting above stemmed from HAML's whitespace rendering that it does, so I had to use a little HAML filters to work around that.
The HAML ended up looking like:
.wmd-output><
:preserve
#{#object.attribute}
The second challenge was actually pretty straightforward, just not explicitly stated anywhere in the Markdown documentation. So I just wrote some javascript that automatically converts any .wmd-output class into it's proper Markdown on page load:
$(function() {
$('.wmd-output').each(function(i) {
var converter = new Markdown.Converter();
var content = $(this).html();
$(this).html(converter.makeHtml(content));
});
});
I hope this helps other people in the future.
This line of haml referenced should be what you need to render it:
= f.input :description, :as => :pagedown, :input_html => { :preview => true }

Are there any better alternatives to Sanitize for a Ruby app?

I love Sanitize. It's an amazing utility. The only issue I have w/ it is the fact that it takes forever to prepare a development environment w/ it because it uses Nokogiri, which is a pain for compile time. Are there any programs that do what Sanitize does (if nothing else than mildly what it does) w/out using Nokogiri? This would help exponentially!
Rails has its own SanitizeHelper.
According to http://api.rubyonrails.org/classes/ActionView/Helpers/SanitizeHelper.html, it will
This sanitize helper will html encode all tags and strip all attributes that aren’t specifically allowed.
It also strips href/src tags with invalid protocols, like javascript: especially. It does its best to counter any tricks that hackers may use, like throwing in unicode/ascii/hex values to get past the javascript: filters. Check out the extensive test suite.
You can use it in a view like so
<%= sanitize #article.body %>
You can visit the link to see more customizing options like:
Custom Use (only the mentioned tags and attributes are allowed, nothing else)
<%= sanitize #article.body, tags: %w(table tr td), attributes: %w(id class style) %>

mixing clientside javascript template with rails logic for backbone.js

I've worked a while now with Backbone.js, and one of the things I nowadays run into is; Sometimes you need to have serverside logic into a .eco.jst template
For example
an i18n translation (currently look at i18n.js gem for this)
a path a route without hardcoding it (somemodel_path(somemodel))
authorisation (for example, show a delete button if the user can destroy this model). Atm I solve this by passing in some rights object in the json that gets filled in.
Rendering a html helper like simple_form or S3_file_uploader (atm I solve this with rendering it serverside, and put the display on none)
As you know, .eco get parsed by node.js, so I can't call ruby in the eco files. Most of these problems I solve by basicly creating a "data" object in the head. Similar to this:
window.data = {
some_translation = "<%= t('cool') %>",
<%= "can_destoy_model = true," if can?('destroy', Model) %>
post_edit_link = "<%= post_path(#post) %>
}
Besides this being bulky (this is just an example, normally this would be more ordened or I add a html5 data attribute to some dom element), It's time consuming, sometimes you have to recreate complete business logic which otherwise would be a oneliner in rails (take for example the S3_file_uploader, which requires encoded amazon policyfile and a token)
What are your thoughts about this? Should I perhaps not use .eco (although I like templates in seperate files instead of poluting the view). Would I able to use serverside logic if I for instance used mustache or handlebars and which gem would you recommend if so?
My experience with Backbone.js is kind of limited, but I've managed to setup an environment with logic-less templates using the following gems:
handlebars_assets
haml_assets
And a bunch of other stuff, even a mini-framework I'm currently working on (you can find it here)
I picked this approach for building Single Page Applications using Backbone.
Basically, the haml_assets gem provides sprockets with the ability to parse .haml files, this is not needed but I love HAML syntax. The handlebars_assets gem provides means to parse Handlebars templates, both on the server-side and the client-side. You can use Ruby code inside the templates and you would solve both the i18n and the path methods problems you mentioned.
I've found these tools to be excellent to help DRY an application's templates, and it can really save you from adding logic inside templates. If you use Backbone Views to take, for example, decisions on whether to show a delete button or not, you can keep the logic inside the Backbone View, and use that logic to render the proper Handlebars template (or partial).
Using your example:
Coffeescript:
class ProjectShowView extends Backbone.View
template: (context) -> HandlebarsTemplates['projects/show'](context)
deleteButtonTemplate: (context) -> HandlebarsTemplates['projects/shared/delete_button'](context)
render: (canDelete = false) ->
#$el.html(#template(#model.toJSON()))
#$('.delete_button_container').append(#deleteButtonTemplate()) if canDelete
#
The example is quite primitive and basic, but can hopefully point in the right direction. I hope it helps!

Ancillary files for view templates in a Rails application

A very basic question on Rails. I would like to create a Rails application that, besides the "regular" html view, it can generate a set of XML files. I am aware that I can tailor the templating using the "respond_to" command and using ERB with the templates ???.xml.erb. My question is: suppose that the final document consists in several of these XML files (some are template and must be autoedited by the application but some others are "static" and do not need to be changed). In this scenario, which would be the best location in the application folder to put these ancillary files of the templates?
Thanks a lot in advance
Miquel
I hope I'm understanding your question...
If your responses are most static you should continue using the .xml.erb templates, but if you need something more dynamic maybe you could consider builder templates, which have a .builder or .rxml extension.
This is an example of what a builder template might look like:
xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
xml.channel do
xml.title(#feed_title)
xml.link(#url)
xml.description "Basecamp: Recent items"
xml.language "en-us"
xml.ttl "40"
for item in #recent_items
xml.item do
xml.title(item_title(item))
xml.description(item_description(item)) if item_description(item)
xml.pubDate(item_pubDate(item))
xml.guid(#person.firm.account.url + #recent_items.url(item))
xml.link(#person.firm.account.url + #recent_items.url(item))
xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
end
end
end
end

Resources