I have a pretty standard Rails form:
<div>
<h1>Create a New Listing</h1>
<%- form_for #listing, :html => {:multipart => true} do |f| -%>
<div><%= f.label :title, "Title:"%> <%= f.text_field :title %></div>
<div>
<%= f.label :image, "Image:" %> <%= f.file_field :image
</div>
<div>
<%= f.label :sound, "Sound Clip:"%> <%= f.file_field :sound %><br />
</div>
<div class="submit"><%= f.submit 'Post Listing' %></div>
<%- end -%>
</div>
When a user chooses a file, but the form fails for validation purposes, he must always re-select the file. It is not sticky. Any suggestion on how to fix this?
Thanks!
You can't make the file field sticky, I think. Even if Rails provides the initial value, most browsers will just ignore it (or otherwise, some smart-aleck could set the default file to /etc/passwd, and if you don't pay attention, next thing you know your box is rooted.
The best you can do that I can think of is set a flag that says a file has already been uploaded, so if the user does not select another one, use the one already sent in the last request.
UPDATE: You'd be surprised how many people have no security skills whatsoever. I've known people to use a browser as root. However, "why" is not exactly an issue - the important point I was trying to make is just that it's not Rails's fault, the problem most likely lies in the browser behaviour.
You can read an article that says it better than I can...
UPDATE 2: "Your box is rooted" should say "the user's box is rooted". The scenario I describe is this: User submits a file innocent.txt and a CAPTCHA. Malicious server responds CAPTCHA is wrong, enter it again, and covertly changes the file from innocent.txt to ~/.ssh/id_rsa. User does not look at the file field (he already put in the correct value there), so just redoes the CAPTCHA and pushes submit. Now the server has the user's private SSH key.
Related
I have a simple rails form looks something like this
<%= form_for :phrase, url: phrases_path do |f| %>
<p>
<%= f.label :"Enter Your Text" %><br>
<%= f.text_area :text %>
</p>
<%= f.fields_for :order do |builder| %>
<p>
<%= builder.label :"ваш email" %><br />
<%= builder.text_field :email %>
</p>
<% end %>
This form goes to the appropriate controller and in the controller depending on the value(text) of the text area performs some calculations to create a price and then save the order.
And it works just fine. But I have another requirement. Before submitting this application the user can click on a button(inserted somewhere between the form) to know the cost. As I said above the cost is calculated using text of the text_area and some logic present inside some method. I would like to implement the button. On clicking the button an ajax request is started, the value(text) of the text area is retrieved and the cost is calculated depending on some method. This calculated value is then shown on the page.
I know to start an ajax request and update the page. What I dont know is how to get the value of the text_area for the Ajax request. I cannot apply everything in javascript since the computation of cost requires some method which is ruby/rails dependent
I am starting use Ruby on Rails and I am having a little problem. I have a form with 3 fields, this is the code:
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.text_field :name, autofocus: true, placeholder: "Name" %>
</div>
<div class="field">
<%= f.email_field :email, autofocus: true, placeholder: "Email" %>
</div>
<div class="field">
<%= f.number_field :age, autofocus: true, placeholder: "Age" %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
In the email field when you write something that is not an email and try to submit, the browser (chrome or firefox ) display an error saying that the field must content an #. The same happen with the age field, if a letter is entered the browser show an error saying that the field only accept numbers.
I wanna know how to make that the browser show a message when any field is empty when you try to submit. I know how to do it in cakephp so I guess it can be done here in ruby too. I already validate the fields in the model, setting the presence in true but that only works for show a message after you submit and the page reload again.
When you use something like:
f.email_field
It is generating an HTML5 input element that tells the browser it has to be a valid email. HTML 5 also has a required='required' option that can be used to prevent blank fields.
You can add it like this:
<div class="field">
<%= f.email_field :email, autofocus: true, placeholder: "Email", :required => 'required' %>
</div>
This will add required='required' to your form element. Note that in HTML5 you only need the word required in your form element, but the only way I know to add it in Rails is to use the option form I'm showing you here.
This will prevent submitting the form without that field. This works for current versions of Firefox, Chrome, Opera, and IE11. Safari will prevent the submission but doesn't indicate why. It just does nothing.
I would check this out: http://blueashes.com/2013/web-development/html5-form-validation-fallback/
You can set the HTML required attribute to true. Just add required: true to each field.
Here's what your new form will look like:
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.text_field :name, required: true, autofocus: true, placeholder: "Name" %>
</div>
<div class="field">
<%= f.email_field :email, required: true, autofocus: true, placeholder: "Email" %>
</div>
<div class="field">
<%= f.number_field :age, required: true, autofocus: true, placeholder: "Age" %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
Your case is pretty custom, that's why it looks pretty easy, but what you're really trying to achieve here is called 'client-side validation'.
To be really portable and user-friendly it has to be done in JavaScript. Basically this will be a script that validates the fields and outputs the corresponding error messages to the user, preventing form submission at the same time. This is almost the same that Rails does on the server side when you submit the form.
Once the problem is defined, you can approach it in one of the following ways:
Stay with Rails. Rails is initially designed to handle form validation on the server side. You can just accept the way it is, and it will yield the cleanest, shortest and the most semantic code possible. For it to be more seamless you can easily pull in some AJAX for it, which should be easy (http://guides.rubyonrails.org/working_with_javascript_in_rails.html). To user it'll look like nothing ever got submitted.
Write some custom JS yourself to handle those validations. Either on your own, or with the aid of libraries like http://jqueryvalidation.org/. This is going to be a mess, since you'll basically have to duplicate Rails server-side validation code on the client-side in a different language. And keep it in sync.
Use one of the helper libraries for Rails. E.g. https://github.com/joecorcoran/judge looks promising, but there are others to be Googled. These guys exercise the same idea: you've got server-side validations and they should be easily usable on the client-side. Certain libraries generate JavaScript automatically, others just send the form to be validated to the server behind the scenes.
If I were you, I would choose the 1st way + AJAX. Other ways would make simple matters unnecessarily complex, and instead of writing useful stuff you'll most certainly have to dive into debugging obscure JS and cryptic meta-programmed Ruby/Rails libraries.
Hope that helps!
HTML 5 has required=true option that can be used to prevent form submission with empty fields. In rails form helpers, you can use it like
<%= f.text_field :first_name, required: true %>
<%= f.email_field :email, required: true %>
I am useing a form_for helper to collect data on the client side of my application. However something weird is happening. I am not collecting the :name and :description and they are both returning as nil. this is my code:
<%= form_for #type do |f| %>
....
<%= f.text_field :name, :class => "col-xs-4" %>
<%= f.text_field :description, :class => "col-xs-4" %>
<%= f.submit %>
....
Do I need to make a fields_for under the form_for to get this working? It is a bit tricky because I am using #type which in this case is set up to tell the view which kind of attr. they are looking at. For example, this line:
<%= f.label #type %> <label> Description</label>
depending on what view you are on shows ether:
Group Description
or
Tag Description
and because they are both technically the same, I am using the same index for both. I hope I am clear with my issue and thank anyone who understands my problem and solution.
The param name will depend on the object you pass.
If #type contains an instance of Group, then you will get the params under params[:group], and if it is an instance of Tag, the you will get them on params[:tag]
<%= form_for #type do |f| %>
<%= f.label :name, "#{#type.model_name} Description" %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
Note the label definition. The way you are defining it will create 2 labels and the second one will not be linked to any field.
fields_for is normally used when you are creating several objects within the same form, for instance a Project and several tasks associated to it.
Hope this helps.
update:
If #type is a string or symbol it should work too. The tradeoffs using this approach will be that if there are any validation errors when creating the object, those will not be displayed and the fields will not be prefilled with the input that the user gave before submitting the form, forcing the user to enter all the information again and guessing which was the validation error (you can initialize it from the received params, but that complicates the code readability)
The unique thing different in your view would be the label definition.
<%= f.label :name, "#{#type} Description" %>
I am working on a web application and what is bothering me is the fact that users can use firebug to manipulate the code.
<%= form_for([#journal, #news]) do |f| %>
<div class="field">
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<%= f.hidden_field :journal %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
routes.rb
resources :journal do
resources: news
end
The url appears like so mysite.com/journal/1/news/3. Since the journal id is in the url how can I prevent a user from changing journal id value of 1 to something like 2 or 3.
<input id="news_journal_id" type="hidden" value="1" name="news[journal_id]">
You can't. NEVER trust input from anywhere. Implement a server-side validation/role-model/access-limitation of any kind.
You can't prevent users from sending you modified input. You can (and should) check that input on the server instead of blindly accepting it.
If a user can't read/write this journal, redirect him to a corresponding page, etc.
You should use current_user associations to fetch any record, to make your application secure.
For example:
journal = currect_user.journals.find(params[:news][:journal_id])
journal.news.create(params[:news].except(:journal_id))
Ok so currently I have a form
<div class="field">
<%= f.label :title %><br/>
<%= f.text_field :title %><br/>
<%= f.label :itunesurl %><br />
<%= f.text_field :itunesurl %><br />
<%= f.hidden_field :user_id, :value => current_user.id %>
</div>
<div class="actions">
<%= f.submit %>
</div>
Which passes the current_user.id into the create method of my "app" model which creates it like this before saving it:
#app = App.new(params[:app])
However I have associations of (pseudocode)
user has_many apps
apps belongs_to user
Question: is it safer (so the form doesn't get modified) to do something like this within the create method?
#user = current_user
#app = #user.apps.create(params[:app])
If so... how exactly would I go about actually implementing the code above (its not syntactically correct.. just pseudo)?
Thanks!
Yes using the second way that you have suggested is the best approach
#user = current_user
#app = #user.apps.create(params[:app])
Also make sure you protect yourself from mass assignment, take a read of this http://stephensclafani.com/2010/01/04/ruby-on-rails-secure-mass-assignment/
It's absolutely safer to do it the second way. If you do it the first way, you're trusting the client to state who they are. Anyone could easily modify the form (with firebug, or they could manually submit a POST request with many tools) and end up submitting a form with the current_user of another person.
Make sure you apply this thinking everywhere throughout your app. Do not trust anything the client submits, ever.
The second code snippet is more "RESTful" than the first. By more RESTful, I mean, if an App is a resource that is logically accessed through a User, then by all means use it.
The way you set that up through routes:
resources :users do
resources :apps
end
This will give you paths like user_app_path and new_user_app_path, to which you pass a user ID and an app ID or a new app.
Hope this helps