I have a requirement where I need to make links clickable in text while keeping any other html tags as text (not html_safe). This means I cannot make the entire text html_safe as that will render the other html tags and I cannot sanitize the text and remove the other html tags. I've seen other websites handle this by making the html_safe links and other text on their own lines. It looks like the following when inspecting the html.
<span>
"This is an "
https://example.com/
"other <b>HTML</b>"
</span>
What would be the best way to do this in Rails 4?
When you call .html_safe on a string your actually getting an object that behaves like a string but is a ActiveSupport::SafeBuffer. When you append a string to a ActiveSupport::SafeBuffer its automatically escaped. Lets say you want to construct a span where the text is user input:
'<span>'.html_safe + text +'</span>'.html_safe
In this case we are safe against an XXS attack as the user originated text is automatically escaped:
irb(main):004:0> "<span>".html_safe + "<script>alert('You have been haxxored!')</script>" + "</span>".html_safe
=> "<span><script>alert('You have been haxxored')</script></span>"
That's what happens automatically when you output a variable in your views as the view is constructed as a ActiveSupport::SafeBuffer. Whenever you output a regular string it will be automatically escaped thus its secure by default.
Of course there is always going to be a huge number of programmers that just give proceed to give themselves a XSS vulnerability out of ignorance:
# WAAAH! Rails is escaping my tags! Bad rails!
'<span>'+ text +'</span>'.html_safe
Another way to approach the problem is to use the tag helpers, partials or Nokogiri instead of using string concatenation to construct HTML which in itself is tedious, borderline unreadible and error prone.
I was able to get this working using the following.
#module ApplicationHelper
def url_regexp
#url_regexp ||= %r{
(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)
(?:\([-A-Z0-9+&##\/%=~_|$?!:,.]*\)|
[-A-Z0-9+&##\/%=~_|$?!:,.])*
(?:\([-A-Z0-9+&##\/%=~_|$?!:,.]*\)|
[A-Z0-9+&##\/%=~_|$])
}ix
end
#in the view
<%- "This is a test https://example.com".partition(url_regexp).each do |text| %>
<%- if text =~ url_regexp %>
<%= "<a href='#{text}' target='_blank'>#{text}</a>".html_safe %>
<%- else %>
<%= text %>
<% end %>
I like my view-source code to be nice an clean but any time I use <%= => tags the whitespace before that tag is ignored.
.html.erb:
View-Source:
Notice the Yield, crfs_meta, and stylesheet link tags ignore the whitespace before the tag. I am using standard 2 spaces as my "tab". For the Yield and when I render partials I can just compensate for the removed whitespace and put it in the partial, but I can't do that for the script and style sheet tags. Is there a way to make is keep my whitespace???
I just realized that the render tag for a layout and yield keeps the whitespace of the previous line (4 tabs, render is on 6 tabs) but inside that layout I have to put the first line at 0 tab, then the next line at 4 tabs, to get it inline at 6 tabs...
The problem isn't the <%= as your edit suggests. It's because the methods you call in your <%= ... %> block are generating more lines of output and those subsequent lines don't respect your indenting.
There's no really neat way to solve it as far as I can tell. You could replace any newlines in the output of those methods with the correct indenting but you are then trading of readability in your code for readability in your output.
For example, something like:
<%= csrf_meta_tags.gsub("\n", "\n ").html_safe %>
Or you could write a helper method (in application_helper.rb) to do it:
def indent_output(output, indentation = " ")
output.gsub("\n", "\n" + indentation).html_safe
end
And then your relevant view code is:
<%= indent_output(csrf_meta_tags, " ") %>
<%= indent_output(stylesheet_link_tag("bootstrap.min", "main"), " ") %>
I have the inherited the following string (I can do nothing about the format):
<iframe \n class=\"some_class\"\n type=\"text/html\" \n src=\"/embed/iframe_content.html?id=tsqA5D7_z10\" \n width=\"960\" \n height=\"593\" \n marginwidth=\"0\" \n marginheight=\"0\" \n frameborder=\"0\">\n</iframe>
I am rendering it in an erb template like this:
<%= the_string %>
At the moment it renders as text like this:
<iframe class="some_class" type="text/html" src="/embed/iframe_content.html?id=tsqA5D7_z10" width="960" height="593" marginwidth="0" marginheight="0" frameborder="0"></iframe>
I need to render it as HTML.
I have tried the following:
<%= the_string.html_safe %> # Renders the string unchanged
<%= CGI.unescapeHTML(the_string) %> # Errors with a Type Error 'can't dup NilClass'
<%= CGI.unescapeHTML(the_string).html_safe %> # Errors with a Type Error 'can't dup NilClass'
<%= raw the_string %> # Renders the string unchanged
How can I render this string as HTML?
As you seem to have noticed, there are two things you need to take care of:
Unescaping the HTML entities
Printing the raw html in your view
For number 2 <%= raw ... %> should work fine.
For number 1 CGI.unescapeHTML was the right idea, but I don't think it recognizes all HTML entities so I would recommend taking a look at the HTML Entites gem
You can also try and use the simple_format helper method, but I think you are going to have to pass it some options for it to allow the <iframe> tag
also I would strongly suggest moving your unescaping logic into a helper method.
what you are unescaping must not be a string and thats why you are getting Errors with a Type Error can't dup NilClass
Try doing
s = String.new your_obj.to_s
Now do
CGI.unescapeHTML(s)
In the end I had to use the HTMLEntities Gem suggested by Matthew;
Installed the gem with RVM and added it to my Gemfile
Required it in my application.rb
The following was the only way I could get it to render correctly. Note the extra single quotes wrapped around the_string. Without them the angle brackets don't render, though everything else does.
coder = HTMLEntities.new
raw coder.decode("'"+the_string+"'")
You can try this:
<%= raw the_string %>
Version 3 sounds valuable. Any reason why you are not using the_string?
<%= raw CGI.unescapeHTML(the_string) %>
Assuming a user inputs the following...
This is a dummy text that may
have some random
whitespaces wherever in the body
How do I accept this in a rails form and output this from an instance variable?
EDIT: Stackoverflow's Markdown did not render the text I typed in properly either. The originsl text - assuming underscores are spaces will be input as such:
This_is_a_dummy____text_that_may
_
_
have_some____________________random
_
_
_
whitespaces_wherever__in_the_body
I tried the usual rails scaffold methods: = f.text_area :message in the form and = #user.message to show it.
Use a textarea tag for that. In rails it would translate to something like:
<%= f.text_area :notes %>
where f is the form handle.
If you want to store it in mysql, use a text datatype.
I got a rails app where I can input a few paragraphs of text into my model. The problem is I dont know how to input any line breaks.
I've tried to add " {ln}{/ln} ; { } and {br}{/br}" but that only displays the html as text and no break.
Is there anyway I can set it so the text area control will use any of the html I place within the model entry?
Is there any thing I can type so rails will recognize, hey put a line here?
Line breaks in textareas are produced as `\n'. However, the problem is that if you simply dump it into your view, it will just be line breaks in your HTML source.
You can try using the Rails simple_format helper to take care of some of this for you: http://api.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html#M002285
It will auto-convert line breaks to HTML tags. You can use it with something like <%= simple_format(my_text_field) %>.
The problem isn't so much editing the value as it is rendering it later. To add newline characters to your value while editing it in a textarea, just hit the return key. When you re-edit that value later, the whitespace should still be there.
Rendering the whitespace is the tricky part. In HTML, whitespace is generally insignificant. A renderer like the one your browser uses will display a single space for any continuous string of whitespace. So merely dumping the value onto the page won't be enough:
<%= obj.description %>
Even though your value may be "One \t \n \n Two", it will show up on the screen as "One Two".
To get those new line characters to actually separate the lines when displayed, you'll need to convert them to HTML before rendering:
<%= obj.description.gsub(/\n/, '<br/>') %>
Of course, if users are entering data that will be included in your HTML, you should be escaping the values to protect against XSS. If new lines are the only thing you need to support, it should be as simple as this:
<%= h(obj.description).gsub(/\n/, '<br/>') %>
If you want to allow more complex formatting, look into Markdown and Textile (both of which Rails provides helper view methods for). Just be sure to investigate what if any support they provide for XSS prevention.
Keep user input unmodified and add this to your css:
white-space: pre-line;
It will display \r or \n (enter) in user input as a new line.
Here is another way to display the line breaks in a string while still escaping the rest of the text:
<%= safe_join(#object.textarea_input.split("\r\n"), "<br />".html_safe) %>
See here http://code.byteblues.com/2012/03/23/preloading-a-text-input-area-text_area-with-data-that-contains-a-line-break/
<%=raw text_area_tag :keywords, keywords, :rows => 8 %>
the problem with simple_format is that it's also adding other tags like <b><i><hr><h1>...
if you just want line breaks without other tags i suggest you build a partial (lets call it line_break):
<% text.split("\n").each do |t| %>
<%= t %><br>
<% end %>
then, just call it from your view:
<%= render partial: 'line_break', locals: {text: some_text} %>
What version of rails are you using?? Because the way to handle this, is different in rails 2 and 3.
Let's say the value of the record is "foo<br />bar"
In rails 3, if you want to evaluate the html, you could do <%=raw "foo<br />bar" %>, if you do so, you'll get a line break when you will see the view.
In rails 2 you don't have to do that, just do <%= "foo<br />bar" %>
Also, HTML doesn't get evaluated in a textarea anyway.
\n if memory serves (it hasn't been doing so well today... try at your own risk lol)
Edit: making the assumption you were talking about a textarea, if it is simple output, just use <br>
The answers above were good:
the gsub (#Ian) worked well
the simple_format (#Karl) was a bit over the top as #Aaron pointed out, wrapping everything in <p>
So I tweaked as follows:
simple_format(value, {}, wrapper_tag: 'div')
The other answers are wrong. Text area does not render as line breaks, because the innerHTML value of the TEXTAREA element does not render HTML..
You need to add the Line feed HTML entity:
EXAMPLE:
<textarea><%= "LINE 1
LINE 2
LINE 3".html_safe %></textarea>
See also New line in text area - Stack Overflow
If you are simply displaying your string in the view. then try it with
< p >This is my text< / p >< br />