Rails escapes string inside label tag - ruby-on-rails

I have a label tag, whose content is loaded from a en.yml file.
html.erb
<%=label_tag(:name, t(:name, scope:[:helpers, :form], name: person_name(person))).html_safe%>
person_name is a helper and outputs a string
persons_helper.rb
def person_name(person)
content_tag(:span,
formatted_name(person.name) || t("helpers.persons.default_name"),
class: 'name').html_safe
end
output string from the helper is passed on t method and concatenated as following
en.yml
name: "Person Name: (%{name})"
I want the output to be like
<label for="person">
Person Name:
<span class='name> John Doe </span>
</label>
but Instead I get
<label for="person">
Person Name:(<span class="name">John Doe</span>)
</label>
I understand that it got to do with html_safe, raw and escaping strings but I just could not get it to work!
Thanks!

Call .html_safe on the method call inside the label_tag. E.g:
<%=label_tag(:name, t(:name, scope:[:helpers, :form], name: person_name(person).html_safe))%>

It appears that the I18n.t method does not return a SafeBuffer (i.e. an html_safe string). So you should call .html_safe on the output from this method.
<%= label_tag(:name, t(:name, scope:[:helpers, :form], name: person_name(person)).html_safe) %>
Note the .html_safe call has been moved in one parenthesis from where you had it. This can also be made marginally easier to see by using the block form of the label_tag helper.
<%= label_tag(:name) { t("helpers.form.name", name: person_name(person)).html_safe } %>
Note: I also switched to the "helpers.form.name" method of selecting the I18n translation in this example to further increase readability (but this may be just a personal preference -- so use your original style if you prefer!).
Finally, for security purposes -- so that a user's name doesn't come through unescaped -- you should remove the .html_safe from your person_name helper and add a strict html_escape (or sanitize) so that it looks like this:
def person_name(person)
content_tag(:span,
h(formatted_name(person.name)) || t("helpers.persons.default_name"),
class: 'name')
end
In this form, the content_tag will make sure everything is html_safe except for the content. Meaning that the person.name will come through as is and be escaped as needed. However, this is not needed if the formatted_name method returns an already escaped or html_safe name. Basically the point is that you don't want to blindly mark strings as html_safe when they come from user inputted values because you don't know if they contain script tags or what. Hopefully this didn't confuse. :) In general, only mark strings as html_safe when you are 100% sure that they are actually always going to be safe (i.e. they come from within your system and not from user input of any sort).

Rails translations can be automatically marked as html_safe but using a naming convention. If a translation is suffixed with _html then the string is marked as html_safe, likewise keys named html are also marked as html_safe.
# config/locales/en.yml
en:
welcome: <b>welcome!</b>
hello_html: <b>hello!</b>
title:
html: <b>title!</b>
In the above t('hello_html') and t('title.html') will be html_safe strings and will not require a call to raw or .html_safe where as t('welcome') will not be html_safe and will require calling raw or .html_safe to avoid the html in the string being escaped.
See http://guides.rubyonrails.org/i18n.html#using-safe-html-translations

Related

How to pre-populate a text-type input field in html via erb/ruby on rails?

I'm trying to get the following line to work in my Ruby on Rails project's erb.html file:
Student City:<input type="text" name="student[city]" value = <%= #student.city %>>
(This whole thing is inside of a form tag)
Everything works fine unless I give value a multi-word value, then it'll cut off everything after the space in the string.
To be specific, if student.city == "San Francisco", my page loads a text field with "San". I've tried changing it to different data types and manipulating the string and it still does the same thing. (Interestingly, trying to print the result after splitting the string into an array will display
["San",
Is this a known bug? Any ideas for a work around? If this is an actual bug with erb, how do I submit a bug report?
I wonder why you don't use Ruby on Rails' form helpers?
Something like:
<%= form_for #student do |f| %>
Student city: <%= f.text_field :city %>
# ...
<% end %>
Read more about form helpers in the Rails Guides.
You need to enclose them with double quotes:
Student City:<input type="text" name="student[city]" value="<%= #student.city %>" >

Does Rails' #link_to only accept one parameter in its block when used outside of ERB?

When I did this in ERB, it works as expected, giving me an a tag that wraps around an image and some text
<%= link_to(...) do %>
<img src="..." />
text
<% end %>
But when I tried to put this in a method, the a tag only wraps the last argument, which in this case, is the text.
def build_link
link_to(...) do
image_tag(...)
text
end
end
Looking at the docs, they only gave an example of using link_to in ERB, so is it smart to assume that using it in a method doesn't work as well and can't accept two parameters?
Following up to my comment:
The reason is behavior happens is because of how Ruby handles blocks, and how Rails handles the output for ActionController.
The trick here is to use handy-dandy concat.
def build_link
link_to("#") do
concat image_tag("http://placehold.it/300x300")
concat "hello world"
end
end
Pretend the block you pass to link_to is just another method, and it gets returned some object/value. In this case, your text object gets returned.
But because you want to output both image_tag and text, you need to pass that together to the output.

Apply a class inside paragraph with HAML

I have got a problem with Ruby on Rails template using HAML.
I have no idea how to apply a specific class inside a tag, for example in:
%p My name is John Doe
I want to add a .highlight class to "name" word. How to do this? I only know this way:
%p
%span My
%span.highlight name
%span is John Doe
but of course it is not the best way to do this, I hope so. Any ideas?
No matter what, you have to put the highlighted text in its own element, so your code is pretty close. You don't need to wrap the rest of the text in elements, though:
%p
My
%span.highlight name
is John Doe
This will produce:
<p>
My
<span class="highlight">name</span>
is John Doe
</p>
Edit:
In the case of question marks, you can use Haml's succeed helper method:
%p
= succeed "?" do
What's your
%span.highlight name
This will render:
<p>
What's your
<span class='highlight'>name</span>?
</p>
There are also precede and surround methods, which you can read about here: http://haml.info/docs/yardoc/file.REFERENCE.html#helper-methods
If you'd rather use string interpolation, it's probably be easiest to define your own helper method.
def hl(str)
capture_haml {
haml_tag 'span.highlight', escape_once(str)
}.strip!.html_safe
end
Since this is kind of elaborate, let me break it down from the inside out. First, we need to escape the string with escape_once. This is only necessary if str comes from user input (and so could lead to an XSS attack), but it's good to have in any event. Then we use haml_tag to render the string inside a span.highlight. Then we have to capture this output as a string with capture_haml, since we're using it in interpolation (ordinarily haml_tag writes to the Haml buffer instead of returning a string). Then we use strip! because Haml will render a newline after this, and you said you don't want that. Finally, we use html_safe to tell Rails not to escape the output, since it would break our <span> and we already escaped the input so we know it's safe.
Now you can use the hl method in your interpolated Ruby:
%p
What's your #{hl "name"}?
This will render:
<p>
What's your <span class='highlight'>name</span>?
</p>

Rails: unable to escape after using content_for with raw

In a helper method I have this:
content_for(:title, raw(page_title))
And in my view I have this after calling the helper method:
<%= h(content_for(:title)) %>
However when I do this, the h() does not HTML escape the content? I've also tried content_for(:title).html_safe and html_escape(content_for(:title)) with no success.
I save the content as raw as I want to access the raw (unescaped) content in a separate view.
I'm on Rails 3.0.17.
After some investigation, here's my conclusions: It's all about html_safe.
Let me explain with some code:
page_title.html_safe? #false
raw(page_title).html_safe? #true - that's all that raw does
content_for(:title, raw(page_title)) #associates :title with page_title, where page_title.html_safe? returns true
Now when the view calls the helper method here's what happens:
content_for(:title) #no escaping. Since page_title was stored and html_safe is true, conetnt_for will not escape
h(content_for(:title)) #no escaping. Since the last line returned a string where html_safe? returns true, this will also not escape.
<%= h(content_for(:title)) %> #no escaping. Same reason as above
In short, raw simply sets the html_safe attribute on the string/SafeBuffer. Escaping is performed only on strings where string.html_safe? returns false. Since the string returns true at each opportunity for escaping, the string is never escaped.
Resolution:
Create a new string through interpolation or concatenation - this will set html_safe to false again and the string will be escaped.
For more, check out this guide on SafeBuffers and read the docs.

converting plain text to html

My rails 3 app receives emails. Some of them are plain text. When the app displays them to the user I want them to be properly formatted. In other word I want to encode plain text into html. For example: "Hello\n\nHello" => HelloHello (or something like it).
Of course I can write my own 4 lines of code but I am sure those 4 lines have already be written, tested and wrapped in some nice method call.
I know I'm a little late, but I actually think the proper solution to this, at least within Rails, is to leverage the simple_format helper method provided from ActionView::Helpers::TextHelper.
Wrap your text in a Pre tag:
<%= content_tag('pre', "Hello\n\nHello") %>
Using #html_safe, let me explain with an example:
If in your controller the variable is:
#str = "<h1>Hi</h1>"
Then in the view:
<%= #str.html_safe %>
#Batkins has the right answer, which should be accepted.
if someone who is still looking,
Converting plain text to HTML
<%= simple_format("plain text") %>
Converting HTML to proper plain text
text.html_safe
The simple_format is TextHelper module so you if you want to use simple_format method in controller
include ActionView::Helpers::TextHelper
in your controller
render :text => "bla bla bla"
it be useful
http://apidock.com/rails/ActionView/Rendering/render

Resources