In my RoR application, user selects an option from a popup and the selected value is passed to hidden fields found in my parent form.
The problem is when passing values containing html characters (e.g. <a href= ""> ) to the parent form.
In my popup, i have a link as follows which passes a value to the main form:
popup code:
<% #users.each do |user| %>
<%= link_to_function "PassValue", "sendValue('"+ user.location+ "')" %>
<% end %>
application.js:
function sendValue(location){
window.opener.document.getElementById('submission_user_attributes_location').value = location;
}
The location value retrieved form the database can contain html chars like '', and this is where my sendValue function is not working.
Please can someone help me on this.
Many many thanks in advance for your help :)
Please do not use obstrusive javascript. Try rewriting this code using non obstrusive javascript and it will prevent you from running into more problems in the future. See this railscast for more info: http://railscasts.com/episodes/205-unobtrusive-javascript
This being said, you could fix your problem by encoding your user.location with URI.encode, or escape quotes manualy or use escape_javascript.
My favorite solution is escape_javascript. From the documentation:
escape_javascript - Escape carrier
returns and single and double quotes
for JavaScript segments.
# File actionpack/lib/action_view/helpers/javascript_helper.rb, line 50
def escape_javascript(javascript)
if javascript
javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) { JS_ESCAPE_MAP[$1] }
else
''
end
end
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.
I have a question in using luquid. My question is like this,
I have a model called 'Page' (with is an ActiveRecord::Base
inherited) , and it has a column called 'content' which will store
the html page content.
I have a code to display it as follows
<%#template = Liquid::Template.parse(page_content) %>
<%= #template.render('page_content' => yield) %>
where 'page_content' has implemented in application helper as follows
def current_site_layout
Page.find(1). content
end
but my problem is if I have content as follows
<h1>This is a test</h1>
It will display in the page as
<h1>This is a test</h1> (with <h1></ h1> tags)
where as I want it to print like This is a test (formatting
applied as h1)
what am I missing here , and I think I will have to use liquid_methods
or something like that. But since I'm new to liquid I'm not sure which
method to use.. can someone help me
I'm on rails3 and using gem 'liquid 2.2.2', from 'github.com/GnomesLab/
liquid.git'
thanks in advance
cheers
sameera
In rails 3, strings are escaped by default. To display unescaped strings, you need to call raw method explicitly.
<%#template = Liquid::Template.parse(page_content) %>
<%= raw #template.render('page_content' => yield) %>
I need some help on how to implement a jquery-ui autocomplete in my Rails app.
I want to add autocompletion to a text field where the user can enter in a customer name. As there can be hundreds of customers, I will need to pull the suggested auto-completion values 'remotely', as in, from a table (at least this is what I understand).
The main point I am failing to understand is how to provide the suggested values to the autocompletion textbox. I have read the jquery-ui docs, but I seem to be a bit dense on this matter.
So what I am really after is an example of how I can get this to work in a Rails app, not necessarily a full description of how the javascript is built (that's what the jquery-ui team has done for me =) ).
For example, how do I prepare the data for the autocompletion, and how do I attach the autocompletion functionality to a textbox.
Well I never got an answer to my question above so I ended up having to figure it out for myself. I thought I should post the solution I came up with in case there are any other guys out there who are wondering the same thing.
First thing you should know is that this is my first experience with javascript, and I am just getting the hang of Rails. So by all means, feel free to edit, comment anywhere you feel I have gone wrong with this. Right or wrong at least I know that it functions the way I wanted it to.
I think the best way to show this is by example. So the following is how I got the autocomplete widget to work in my app. You can go ahead and put the following code in your app even if you don't understand what is happening, then we can go over how each part is working by example. After this you should have a grasp on how to modify it for your use or refractor it.
**INCLUDE JQUERY UI IN YOUR RAILS APP.**
Download a copy of the [jQuery UI][ui] and place jquery-ui-1.8.2.custom.min.js inside your /public/javascript directory. Also make sure you have a copy of jQuery itself and that this is also in the same folder.
Include the jQuery UI file and the jQuery file in your application.html.erb file like this.(you can name the files as you please as long as they match)
<%= javascript_include_tag 'jquery.min', 'jquery-ui-1.8.2.custom.min.js' %>
In your download of jQuery UI, you will have a folder that contains all of your CSS data. The name will vary based on the theme you chose, for example I chose the theme 'cupertino'. Place the entire folder containing your CSS data into '/public/stylesheets/'. Then include the CSS file in your application.html.erb like this.
<%= stylesheet_link_tag 'cupertino/jquery-ui-1.8.2.custom' %>
**EXAMPLE AUTOCOMPLETE JAVASCRIPT**
Now take the following chunk of code and place it in one of your 'new' views. You can use this in any view, but realize that I have literally taken it from an existing view belonging to a controller called 'links_controller', and it is pulling data from a 'people_controller'. Hopefully you know enough about Rails to work out what you need to change so this works for you.
-- Begin big chunk of code --
<script type="text/javascript">
$(function() {
// Below is the name of the textfield that will be autocomplete
$('#select_origin').autocomplete({
// This shows the min length of charcters that must be typed before the autocomplete looks for a match.
minLength: 2,
// This is the source of the auocomplete suggestions. In this case a list of names from the people controller, in JSON format.
source: '<%= people_path(:json) %>',
// This updates the textfield when you move the updown the suggestions list, with your keyboard. In our case it will reflect the same value that you see in the suggestions which is the person.given_name.
focus: function(event, ui) {
$('#select_origin').val(ui.item.person.given_name);
return false;
},
// Once a value in the drop down list is selected, do the following:
select: function(event, ui) {
// place the person.given_name value into the textfield called 'select_origin'...
$('#select_origin').val(ui.item.person.given_name);
// and place the person.id into the hidden textfield called 'link_origin_id'.
$('#link_origin_id').val(ui.item.person.id);
return false;
}
})
// The below code is straight from the jQuery example. It formats what data is displayed in the dropdown box, and can be customized.
.data( "autocomplete" )._renderItem = function( ul, item ) {
return $( "<li></li>" )
.data( "item.autocomplete", item )
// For now which just want to show the person.given_name in the list.
.append( "<a>" + item.person.given_name + "</a>" )
.appendTo( ul );
};
});
</script>
<h1>New link</h1>
<% form_for(#link) do |f| %>
<%= f.error_messages %>
<!-- Place the following text fields in your form, the names are not important. What is important is that they match the names in your javascript above -->
<p>
Select which person you want to link:<br />
<!-- This is the textfield that will autocomplete. What is displayed here is for the user to see but the data will not go anywhere -->
<input id="select_origin"/>
<!-- This is the hidden textfield that will be given the Persons ID based on who is selected. This value will be sent as a parameter -->
<input id="link_origin_id" name="link[origin_id]" type="hidden"/>
</p>
<!-- end of notes -->
<p>
<%= f.label :rcvd_id %><br />
<%= f.text_field :rcvd_id %>
</p>
<p>
<%= f.label :link_type %><br />
<%= f.text_field :link_type %>
</p>
<p>
<%= f.label :summary %><br />
<%= f.text_area :summary %>
</p>
<p>
<%= f.label :active %><br />
<%= f.check_box :active %>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
-- End Big Chunk of Code --
Okay now to connect the dots.
**PROVIDE DATA FOR AUTOCOMPLETE TO USE AS SUGGESTIONS**
Lets start by connecting up some data that the autocomplete textfield can display in the drop down suggestions. The format we will be using is JSON, but don't worry if you are not familiar with it ... neither am I =). It is good enough to know that it is a way to format text so that other parts of yours/other applications can use it.
The data the textfield will need for the autocomplete is specified in the 'source:' option. Because we want to send a list of peoples names and their ID to the autocomplete we will put the following as the source.
source: '<%= people_path(:json) %>'
The rails helper above will translate to a string "/people.json". You do not need to create a page at "/people.json". What you do need to do is tell your people_controller what to do when it receives a request for /people with the .json format. Put the following into your people_controller:
def index
# I will explain this part in a moment.
if params[:term]
#people = Person.find(:all,:conditions => ['given_name LIKE ?', "#{params[:term]}%"])
else
#people = Person.all
end
respond_to do |format|
format.html # index.html.erb
# Here is where you can specify how to handle the request for "/people.json"
format.json { render :json => #people.to_json }
end
end
Now we have all the people in #people being sent to the autocomplete textfield. This brings up the very next point.
**FILTER DATA USED FOR AUTOCOMPLETE SUGGESTION, BASED ON INPUT**
How does the autocomplete textfield know how to filter the results based on what you type?
The autocomplete widget assigned to the textfield will send whatever you type into the textfield as a parameter to your source:. The parameter being sent is "term". So if you were to type "Joe" into the textfield, we would be doing the following:
/people.json?term=joe
That is why we have the following in the controller:
# If the autocomplete is used, it will send a parameter 'term', so we catch that here
if params[:term]
# Then we limit the number of records assigned to #people, by using the term value as a filter.
#people = Person.find(:all,:conditions => ['given_name LIKE ?', "#{params[:term]}%"])
# In my example, I still need to access all records when I first render the page, so for normal use I assign all. This has nothing to do with the autocomplete, just showing you how I used it in my situation.
else
#people = Person.all
end
Now that we have limited the number of records assigned to #people based on what is typed into the autocomplete textfield, we can now turn that into JSON format for the autocomplete suggestions.
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #people.to_json }
end
Now, just review the comments inside the "Big Chunk of Code" which should explain the rest of how this ties together.
At the end you should have a textfield on your page that acts as the autocomplete and a hidden field that will send the ID in a parameter to your controller.
**CUSTOMIZE YOUR OWN AUTOCOMPLETE**
Once you understand the above and you want to modify it for your use, you should know that the format JSON returned from your controller looks like this:
[{"person":{"id":1,"given_name":"joe","middle_name":"smith","family_name":"jones","nationality":"australian"}}]
The way to access the different values from the JSON string in your javascript in this case would be:
ui.item.person.name_of_some_attribute_such_as_given_name
Pretty, simple. A lot like accessing an ActiveRecord attribute in Rails.
One last note. I spent a lot of time looking for a different way to supply the hidden value, as I thought this function should have been built into the jquery widget. However, this is not the case. It is clearly shown in the official jQuery example that the way to send a different value then selected as a parameter, is to use a hidden field.
Dale
[ui]:http://jqueryui.com/download
jQuery 1.9/1.10 removed the key autocomplete and added uiAutocomplete
.data("uiAutocomplete") instead of .data("autocomplete")
After modifying to above,it worked for me.
Dale's Answer is quite the tutorial. One thing to note is that using your first query, the datasource will only return matches beginning with the string you type. If you want search anywhere in the word, you need to change:
#people = Person.find(:all,:conditions =>
['given_name LIKE ?', "#{params[:term]}%"])
to
#people = Person.find(:all,:conditions =>
['given_name LIKE ?', "%#{params[:term]}%"])
(added an extra % to the query)
I basically followed Dale's advice below but my controller and js files were slightly diff- his version was giving me issues for some reason (maybe bc of jquery updates)
Context: I'm trying to autocomplete names of DJs typed in by users - also a newb
DJs Controller
class DjsController < ApplicationController
def index
if params[:term]
#djs = Dj.is_dj.where('lower(name) LIKE ?', "%#{params[:term].downcase}%")
respond_to do |format|
format.html
format.json { render :json => #djs.map(&:name) }
end
end
end
end
html.erb file
<script type="text/javascript">
$(function() {
$('#select_origin').autocomplete({
source: '<%= djs_path(:json) %>'
})
$('.submit-comment').click(function(){
var dj_name = $('#select_origin').val();
$('#link_origin_id').val(dj_name);
})
})
</script>
This is a great help.
In addition to it in case if you need to fetch url of image of user, it might not be possible with to_json. For that add the following code in model.
def avatar_url
avatar.url(:thumb)
end
And then in controller instead of to_json use as_json
respond_to do |format|
format.json {render :json => #users.as_json(:only => [:id,:name,:username], :methods => [:avatar_url]) }
end
It's important to note that if your 'source' is relatively small, for example 50 elements, the implementation should be different (and a lot simpler). It is mentioned in the fourth paragraph of the official doc:
https://api.jqueryui.com/autocomplete/
When using local data all you need to do is obtain the data and pass it to the autocomplete method, and it will do the filtering for you. You don't need to go back and forth to the server every time a term es entered.
function filterByTags(tags) {
$("#stories-filter").autocomplete({
source: tags,
autoFocus: true
});
}
$("#stories-filter").click(function() {
$.ajax({
dataType: 'json',
method: 'GET',
url: 'tags/index',
data: $(this).data('project-id'),
success: function (response) {
if(response.success) {
var tags = response.data.tags;
filterByTags(tags);
}
},
error: function (response) {
if(response.status === 422) {
var $errors = 'There are no tags in this project',
$errorsContainer = $('.error-container');
$errorsContainer.append($errors);
$errorsContainer.show();
}
}
});
});
Since this is old, but google still comes here, a small note about the main answer, which is essentially good, but some things have changed:
see answer about jquery having changed .data("uiAutocomplete") to .data("autocomplete")
Also i would recommend a separate route on the resource collection that just handles json
use rabl to create smaller json (or pluck for larger models)
ilike , not like, for case insensitive
the % in front, so the search is not just start_with.
valiable traversal in the methods, like item.person.name are just item.name (so drop the .person)
use coffee (in haml)
use a limit, and where as in: Person.where('given_name ilike ?', "%#{params[:term]}%").limit(20)
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
I've got the following problem: I have rhtml (html minced together with ruby inside <% %> and <%= %> tags) stored in a database which I want to render. The information is acquired through a query. I need to be able to evaluate the information I get from the database as though as it was normal content inside the .erb-file. What I currently have:
<% #mymods.each do |mod| %>
<%= render_text(mod["html"])%>
<% end %>
Where mod["html"] is the variable containing the rhtml-code and #mymods an array of objects from the query. I have currently no idea what function I should use (render_text does, of course, not work).
Help is greatly appreciated.
/TZer0
You can use the ERB object to render text without the text being in a file.
Just pass the text with the <%= %> tags. You could put something like the following as an application_helper function.
def render_erb_text(text, args={})
b = binding
template = ERB.new(text, 0, "%<>")
template.result(b)
end
And then in your template
<%= render_erb_text("<%= %w(hi how are you).join(' - ') %>")%>
You might also consider rendering the text in your controller as you can handle any render errors better there than during view evaluation.
Take a look at the ERB documentation for more information regarding variable binding etc.
I'm not familiar with the details of how this works under the covers, but there could be some serious risk in running this code on bad or malicious database data. Evaluating ruby code from user input or any un-vetted source should be done very carefully, if at all.