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 %>
The question says it all. If I put HTML directly into the (JSON-formatted) translation file, like this:
"test_html" : "click <a href='http://stackoverflow.com/'>here</a>",
I get this in my HTML:
click <a href='http://stackoverflow.com/'>here</a>
I also tried combining this in my translation file:
"test_html_placeholder" : "click %shere%s",
With this in my HTML:
<%= __('test_html_placeholder', '', '') %>
But got similar results.
The only thing I can get to work is this clumsiness:
"test_html_pre" : "click ",
"test_html_link" : "here",
"test_html_post" : ".",
with this:
<%= __('test_html_pre') %><%= __('test_html_link') %><%= __('test_html_post') %>
But it's so cumbersome as to be almost not worth doing, and moreover the word order in some languages would force me to put some empty strings in my translation files, which i18n-node doesn't seem to like as it spits out the key (attribute) name when it
encounters an empty string.
I also tried using "\" as an escape character in front of the symbols, but I got an invalid JSON error when I lifted sails (restarted the server).
Any ideas, workarounds? I'm using sails.js, it wasn't my decision but I'm stuck with it and it comes with i18n-node. It's kind of late in the day on this project to consider using another library, but not completely out of the question.
beside of any upcoming discussion whether to include (html-)code in language files or not:
try to use
<%- __('click') %>
instead of
<%= __('click') %>
in ejs (the sails default template engine) a '<%=' will escape any html tags while '<%-' puts output as is without touching it. I am pretty sure you'll find unescaped html in your .json files. i18n doesn't do any transformation other than JSON.stringify() but almost all template engines do escape strings by default to prevent xssi.
For those using pug/jade, you can use
!{ __('key_for_your_text') }
Another option for pug is using
p!= __('key_for_your_text')
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"), " ") %>
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 />