I end up with strange bug (feature?):
Here is how test.html.erb file looks like:
<textarea><%= "a\nb\nc" %></textarea>
and in rendered textarea I expect something like that:
a
b
c
But I got:
Where these excess spaces came from?
Rails 5.0.0.beta2
UDP: I should mention it before, but I have the same problem while using f.text_area inside of form_for block. It looks like:
.form
=form_for article do |f|
.form-group
=field_label f, :text, true
=f.text_area :text, class: %w(form-control), rows: 20, placeholder: t('placeholder.article_text')
(HAML)
I end up with it, and just simplified the exploit to simple erb file with one string
UDP2:
Here how it looks with simple_format: <textarea><%= simple_format("a\nb\nc") %></textarea>
I should clearify some thing: this textarea is used to edit article text. And then it (text) will be processed with markdown processor (RDiscount) before appears in html page. I have no any idea, why I should use simple_format to display raw text in textarea and why this
should became this
after save?
In HAML, use
~ f.text_area :text
instead of
= f.text_area :text
The ~ operator suppresses "pretty" newlines in HAML, which is useful with TEXTAREA and PRE tags. See Whitespace preservation in HAML for more information.
<textarea><%= "a\r\nb\r\nc" %></textarea>
\n is new line it will take to next line at same position where you was in previous line \r is carriage return mean move to start of line.
But I wont recommend this approach You should use <br/> tag instead
so use
<textarea><%= WhatEverTheTextIs.gsub(/\n/, '<br/>').html_safe %></textarea>
This will replace all new line characters to <br> tag
One more easy solution is to use simple_format i-e:
<textarea><%= simple_format(YOUR_TEXT_HERE) %></textarea>
UPDATE
Here is the code to use for form helper
.form
=form_for article do |f|
.form-group
=field_label f, :text, true
=f.text_area :text,:value=>simple_format(article.text), class: %w(form-control), rows: 20, placeholder: t('placeholder.article_text')
I just ran into the exact same problem. It all comes down to whitespace in your templates and layouts. In my case my main application is using HAML and has an application.html.haml with a section like so
#content.container{tabindex: "-1"}
.row
- if content_for?(:left_nav)
.col-md-3.sidebar
= yield(:left_nav)
.col-md-9
= flash_helper
= yield
%footer
= render :partial => 'shared/footer'
That's at least 10 spaces before the "yield" of the main content.
Then I was using a view from a gem that was in ERB, not HAML. But, since my main application layout is HAML, the view rendering/handling goes through the HAML renderers. HAML has dealt with whitespace and texareas already, but when the final and complete view is rendered, all of the extra whitespace from the main layout is included in the html and causes the content of the textarea to be indented.
Why is the first row not indented? Because the HAML "preserve" function cleans up the whitespace for the first row in the text area. See https://github.com/haml/haml/issues/516.
For me, the easy fix is to configure HAML to use "ugly" mode. This gets rid of all the leading whitespace when rendering the views. There is more about "ugly" mode and newlines in textareas in the HAML FAQ. To configure HAML to use ugly mode:
To improve performance, Haml defaults to {Haml::Options#ugly "ugly" mode} in Rails apps running in production. Ugly mode is when whitespace is stripped out, and this can cause issues occassionally.
If you are using Rails, you can change the default behaviour by creating a config/initializers/haml.rb file and adding in the following line.
Haml::Template.options[:ugly] = true
Links I found helpful while researching this:
https://github.com/haml/haml/issues/643
ERB view embedded in a Haml layout: what to do about whitespace now? (although the proposed solution didn't work for me b/c the ERB view would always be picked up before the HAML view)
https://github.com/haml/haml/blob/master/lib/haml/buffer.rb (fix_textareas)
https://github.com/haml/haml/blob/master/lib/haml/helpers.rb (specifically the preserve method)
My versions are Rails 4.2.6 and HAML 4.0.7
Related
I'm trying to create a situation where one user makes message templates and another one can plug in values. I'm using the best_in_place gem, which will allow a user to edit the message on the show page.
The problem is this. When I call the message, with the required erb to make the gem work, it treats all of this as a regular string, not as ruby.
This is unclear, I'm sorry.
Here's the code.
#announcement.content = "The <%= best_in_place #announcement, :train %> is arriving in five minutes."
/show.html.erb
<%= #announcement.content %>
I want it to put "The click to set train is arriving in five minutes." and if the user clicks where it says "click to set train," a text field will open for them to edit (this is something the best-in-place gem does).
Instead, it puts "The <%= best_in_place #announcement, :train %> is arriving in five minutes."
I understand why it is doing this, but I don't know how to make it instead interpret the ruby I'm trying to pass in.
Ideas?
Use regular old string interpolation:
#announcement.content = "The #{best_in_place #announcement, :train} is arriving in five minutes."
You can use ERB to render any ERB template string. In this case something like:
<%= ERB.new(#announcement.content).result %>
Although you likely won't have access to all your Rails helpers, etc.
The Rails way to do this:
#announcement.content_type = :arriving
Later:
<%= render(partial: #announcement.content_type)
In _arriving.erb:
The <%= best_in_place #announcement, :train %> is arriving in five minutes.
TL;DR: ERB is not Ruby, and Rails uses both at different times.
You want simple Ruby string interpolation here:
#announcement.content = "The #{best_in_place #announcement, :train} is arriving in five minutes."
This is unclear, I'm sorry.
Not to worry, the Rails framework throws so many different new concepts at you it can be frustrating for newcomers.
Start from this: the Ruby framework builds the answer to the user's browser from a collection of resources Each file is evaluated by an interpreter for its own language. The trick is: look at the extension.
Files ending in .coffee will be compiled into javascript, files ending in .scss will become CSS, and in the same way files ending in .erb will yield HTML.
ERB is a language composed of mostly HTML already, plus a tag that allows you to interpolate Ruby. ERB stands for Embedded Ruby.
What about files ending in .rb, like the file in which you (surely) are evaluating #announcement.content = "The <%= best_in_place[...]" (a controller, I guess)?
Well, that's just pure Ruby :) that's why the ERB interpolation syntax <%= ... > is not recognized.
What you want to do in the controller, is (as you're trying to do) preparing the data for the view. The ruby in the <%= ... > tag in ERB will have access to the controller's instance variables, i.e. the variables with an # in front defined in the controller. But to define those, inside the controller, you should rely on Ruby alone.
Take-home message:
Be aware of which language you are writing in at each moment. For example:
# show.html.erb
<p>Here is ERB, which will be interpreted straight into HTML</p>
<% "Inside the '<% ...' tag is Ruby, but results won't show up in the HTML because there's no '<%='."%>
<% which_language = "Ruby" # Even variable assignments, and comments, do work %>
<%= "Inside the '<%=' tag, you're writing and interpolating #{which_language} :)" %>
I think the fact that I wasn't clear made it hard to answer this question.
What I'm doing is transforming user-inputted text (using a method in the model, called by the controller) to replace certain keywords with erb tags that call the best_in_place plugin. In my view, when presenting this content to another user, I wanted to call this content, which is saved as an attribute in the database, in such a way that it would render correctly for the other user to have the best_in_place functionality active.
Here's what I ended up doing. It is working, but if you have better ideas, please let me know.
In the announcements#create view, the user creates an announcement with certain pre-defined blocks of bracketed text as well as free-input text. For example, they might write "[train] is leaving from [platform] in [time] minutes."
When they hit save, the controller's create action calls the construct_message method from the model. It looks like this:
def construct_message(msg)
msg.gsub! '[train]', '<%= best_in_place #announcement, :train_id, :as => :select, collection: Train::list_trains, place_holder: "Click here to set train." %>' #note: list_trains and list_platforms are methods on the model, not really important...
msg.gsub! '[platform]', '<%= best_in_place #announcement, :platform_id, :as => select, collection: Platform::list_platforms, placeholder: "Click here to set platform." %>'
msg.gsub! '[time]', '<%= best_in_place #announcement, :number_of_minutes, placeholder: "Click here to set." %>'
end
Then, when I want to show that attribute in my view, I'm using render :inline, like this.
on announcements/:id
<p id="notice"><%= notice %></p>
<p>
<strong>Content:</strong>
<% announcement = #announcement %>
<%= render :inline => announcement.content, locals: { :announcement => announcement } %>
</p>
This allows the erb call that I wrote into the attribute to be functional.
Also note that I'm choosing to use a local rather than instance variable here; this is because in announcements#index, I also render this text and the table there uses local variables.
A simple test case:
<% content = "<pre>a\nb</pre>" %>
<%= raw content %>
Browser Screenshot, with inserted spaces on second line:
Here's where it gets interesting. In rails:
raw(content).size # => 14 (correct)
But, in JS:
$("pre").html().length // => 13 (should be 3)
Any chance you are using HAML for your layout? If so, that would explain it as HAML will indent things for you. You can use HAML's ~ to get around this.
See: http://haml.info/docs/yardoc/file.REFERENCE.html#tilde
I'm trying to show the contents of a field from the database in a <p> element. In the html.erb template the code looks like:
<p><%= front.gsub(/(\r)?\n/, "<br>") %></p> ...
The issue I'm having is that to escape the breaks, I have to apply the .html_safe method at the end of the above gsub, but doing so opens the whole application to XSS attacks. How can I only allow the breaks to be escaped?
You can use the simple_formatmethod.
<%= simple_format(front) %>
More here => http://api.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html#method-i-simple_format
This is based on the simple_format helper. We can use sanitize to remove bad tags that allow XSS attacks.
<%= sanitize(front).gsub(/(\r)?\n/, "<br/>").html_safe %>
You can also use strip_tags if you want to remove all HTML tags before replacing new lines with <br>.
<%= strip_tags(front).gsub(/(\r)?\n/, "<br/>").html_safe %>
Have you considered wrapping the text into <pre>-tags instead? That will keep the basic formatting (newlines, spaces, ...).
I have Person.description with the following stored in the database:
jnkl
fdsfdsf
fdsf
fsdfdsfs fds fd sf sdf ds
How do I display this with the line-breaks in the view? It is currently displaying all on one line and I don't understand why.
you should use the simple_format helper:
<%= simple_format #person.description %>
http://api.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html#method-i-simple_format
Source
# File actionview/lib/action_view/helpers/text_helper.rb, line 301
def simple_format(text, html_options = {}, options = {})
wrapper_tag = options.fetch(:wrapper_tag, :p)
text = sanitize(text) if options.fetch(:sanitize, true)
paragraphs = split_paragraphs(text)
if paragraphs.empty?
content_tag(wrapper_tag, nil, html_options)
else
paragraphs.map! { |paragraph|
content_tag(wrapper_tag, raw(paragraph), html_options)
}.join("\n\n").html_safe
end
end
The reason why, is that in plain HTML, outside of containing tags such as 'xmp', line breaks aren't rendered as linebreaks, for the most part they are ignored. For them to show up, you need to replace them with 'br' tags, or something else that has a style or structure associated with it, like p tags, or even divs, depending on the content is.
This should do what you ask:
#person.description.gsub(/\n/, '<br />')
The built in Rails helper simple_format will also work, using p tags
http://api.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html#method-i-simple_format
I also use
#person.description.gsub(/\n/, '<br/>').html_safe
to display them in the view
Instead of replacing \n to <br> tags, which would consume a lot of processing if its a huge text, use css white-space: pre to break lines on \n.
Source: An html tag other than a textarea where \n is correctly interpreted
simple_format did not suit my needs as I want to be able to show multiple line breaks. simple_format makes 2 or more line breaks a paragraph.
So, I used css white-space: break-spaces; instead. The only current drawback is the browser support is 89.5% now.
I need to display user comments, omitting HTML to prevent attacks (when custom styled elements can be posted as comments)
The only thing, i would like to keep by displaying - is tag
I displaying the comment in this way:
<p class="content"><%=h comment.content.gsub(/\n/,"<br/>") %></p>
Comment is suppossed to be saved in database without any markup
Line ending are converted to "br" tags
But, sure, they are gone, because of =h output mode.
Is there a way to kill all html, except "br" tags ?
You could either use sanitize which keeps only specified HTML tags:
<%= sanitize comment.content.gsub(/\n/,"<br/>"), :tags => ['br'] %>
or (in your case preferably) change the order of both and do the html_escape yourself:
<%= html_escape(comment.content).gsub(/\n/,"<br/>") %>
I'd recommend to use white_list plugin. It's safety for XSS attacts and you will be able to control list of allowed tags