I'm building an app on Rails 3 RC. I understand the point behind the _snowman param (http://railssnowman.info/)...however, I have a search form which makes a GET request to the index. Therefore, submitting the form is creating the following query string:
?_snowman=☃&search=Box
I don't know that supporting UTF encoding is as important as a clean query string for this particular form. (Perhaps I'm just too much of a perfectionist...hehe) Is there some way to remove the _snowman param for just this form? I'd rather not convert the form to a POST request to hide the snowman, but I'd also prefer it not be in my query string. Any thoughts?
You can avoid the snowman (now a checkmark) in Rails 3 by.... not using Rails for the search form. Instead of using form_tag, write your own as outlined in:
Rails 3 UTF-8 query string showing up in URL?
Rails helpers are great unless they're not helping. Do-it-yourself is good as long as you understand the consequences, and are willing to maintain it in the future.
I believe the snowman has to be sent over the wire to ensure your data is being encoded properly, which means you can't really remove the snowman input from forms. Since, it's being sent in your GET request, it will have to be appended to the URL.
I suppose you could write some javascript to clean up the URL once the search page loads, or you could setup a redirect to the equivalent URL minus the snowman. Both options don't really feel right to me.
Also, it doesn't seem there is any way to configure Rails to not output it. If you really wanted to get rid of it, you could comment out those lines in Rails' source (the committed patches at the bottom of railssnowman.info should lead you to the files and line numbers). This adds some maintenance chores for you when you upgrade Rails. Perhaps you can submit a patch to be able to turn this off?
EDIT: Looks like they just switched it to what looks like a checkmark instead of a snowman.
EDIT: Oops, back to a snowman.
In Rails 4.1 you can use the option :enforce_utf8 => false to disable utf8 input tag.
However I want to use this in Rails 3, so I monkey-patched my Rails. I put the following in the config/initializers directory.
# allow removing utf8 using enforce_utf8, remove after Rails 4.1
module ActionView
module Helpers
module FormTagHelper
def extra_tags_for_form(html_options)
authenticity_token = html_options.delete("authenticity_token")
method = html_options.delete("method").to_s
method_tag = case method
when /^get$/i # must be case-insensitive, but can't use downcase as might be nil
html_options["method"] = "get"
''
when /^post$/i, "", nil
html_options["method"] = "post"
token_tag(authenticity_token)
else
html_options["method"] = "post"
tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag(authenticity_token)
end
enforce_utf8 = html_options.delete("enforce_utf8") { true }
tags = (enforce_utf8 ? utf8_enforcer_tag : ''.html_safe) << method_tag
content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline')
end
end
end
end
Related
I have an application that collect user input and store to DB and show back to user.
One user entered "alert(1)" into the name field and saved it into DB.
Whenever the name is displayed, the page will be broken.
I know how to fix that input only with validation for input, and h() for output.
However, I have so many input fields and so many outputs that accept users' text.
Is there any simple way to prevent this happening(i.e. overriding params method, etc)?
I also want to know how you expert guys are dealing with this problem?
As of Rails 3, my understanding was that embedded ruby code was html escaped by default. You don't need to use h() to make it that way. That is, if you use <%= "<script>a=1/0;</script>" %> in a view, the string is going to be made html safe, and so the script doesn't execute. You would have to specifically use raw() or something similar to avoid it - which you should naturally not do unless you're really confident about the contents.
For output, Rails 3 automatically html-encode all text unless I use raw() method.
For input, How about making a common validator and apply to all fields that are text or string? Is it desirable?
http://api.rubyonrails.org/classes/ActiveModel/Validator.html
class MyValidator < ActiveModel::Validator
def validate(record)
record.class.columns.each do |c|
if c.type==:text || c.type == :string
record.errors.add c.type, "script tag is not allowed" if c[/<script[^>]*>/]
end
end
end
end
here my view code:
- #categories.each do |c|
%li.menu-drop
= check_box_tag "categories[]", c.id
.tipo-infor
= c.title
when i submit this form, it generate this url:
http://0.0.0.0:3000/buscar-projetos-com?utf8=%E2%9C%93&categories%5B%5D=3&categories%5B%5D=4&commit=Pesquisar
my question is how to better display this url just like:
http://0.0.0.0:3000/buscar-projetos-com?categories=2,3,4,5
Those %E2's and such are the ASCII representation of those URLs and it is much safer to use those when sending data over the internet. Furthermore commas (,) are not legal in URLs, so they should never be used as neither browsers nor standard libraries will like them.
If you are concerned about "ugly" URL strings, you could submit your form using the POST method vs GET, which will hide all params from the user.
I followed Ryan Bates screencast of how to use permalinks in a Rails application. Unfortunately I am stuck with an issue when some of my permalinks contain slashes. Is there anything that I can do in the controller to encode those on the fly, or do they need to be encoded in the database?
You can use Rack::Utils.escape to return a clean, friendly URI. For instance:
Rack::Utils.escape("This/is/not/a/good/url")
will return
"This%2Fis%2Fnot%2Fa%2Fgood%2Furl"
and
Rack::Utils.unescape("This%2Fis%2Fnot%2Fa%2Fgood%2Furl")
converts it back to the original string:
"This%2Fis%2Fnot%2Fa%2Fgood%2Furl"
You'll have to wire those methods into the find methods in the controller, but should work out for you.
To generate permalinks that are safe, use something like this. It will create a 4 character long, url safe permalink and check to make sure there are no duplicates.
def create_permalink
loop do
self.permalink = SecureRandom.urlsafe_base64(4).downcase
break permalink unless ModelName.find_by_permalink(permalink)
end
end
I'm probably missing something obvious, but I've got a logo I'd like to include in all of the emails I send from my app. I have a master layout I'm using for all of those Mailers. I assume there's a way to do keep it DRY and not have to add the line of code to attach the file in every mailer method. Can someone point me in the right direction or correct my line of thought.
Thanks!
Callbacks using before_filter and after_filter will be supported in a future Rails release:
http://github.com/rails/rails/commit/4f28c4fc9a51bbab76d5dcde033c47aa6711339b
Since they will be implemented using AbstractController::Callbacks, you can do the following to mimic the functionality that will be present in ActionMailer::Base once Rails 4 is released:
class YourMailer < ActionMailer::Base
if self.included_modules.include?(AbstractController::Callbacks)
raise "You've already included AbstractController::Callbacks, remove this line."
else
include AbstractController::Callbacks
end
before_filter :add_inline_attachments!
private
def add_inline_attachments!
attachments.inline["footer.jpg"] = File.read('/path/to/filename.jpg')
end
end
This includes the module that will be used in a future rails version, so the callback hooks available to you will be the same to ensure future compatibility. The code will raise when you try to upgrade to a Rails version that already includes AbstractController::Callbacks, so you will be reminded to remove the conditional logic.
I hacked a little something, it's not ideal, but it works.
If you use
default "SOMEHEADER", Proc.new { set_layout }
And then define set_layout
def set_layout
attachments.inline["logo.png"] = File.read("logopath.png")
attachments.inline["footer.jpg"] = File.read("footerpath.png")
"SOME HEADER VALUE"
end
Then because set_layout gets called to set the header, it also adds the inline attachments. It basically creates a callback for adding attachments.
An actual callback system in ActionMailer would be preferable, but this works too.
Thought I would share since I was looking for this answer on this question earlier today.
in the layout file that your mailer uses u can add the following
<%= image_tag('logo.png') %>
I am assuming that the mail being sent out is html or multipart.
Also you will need to make changes in the environment files. ActionMailer does not get a default base_url. For e.g in environments/development.rb I added the following
config.action_mailer.default_url_options = { :host => "localhost:3000" }
If you want to do it in a dRY manner (and as an attachment) maybe you could do something like
class MyMailer < ActionMailer::Base
default :attachment => File.read(File.join(Rails.root,'public','images','logo.png'))
end
I know you've asked about attaching inline images, but here's a different approach that achieves the same thing with less complexity..
Using inline base64 encoded images in the html layout - no attachments required!
Basically just change the src="..." of your logo image to the format:
<img alt="example logo"
width="32px"
height="32px"
src="data:image/png;base64,iVBORw0KGgoAAA....."/>
I use the online base64 encoder / decoder tool at http://www.base64-image.net for generating the complete <img /> tag
This approach has a few benefits:
- no attachment code, which makes the backend server code cleaner and easier to read
- no increase in email size - inline image attachments are converted to base64 anyway so this approach doesn't make the email payload any larger
- it's widely supported - if the receiving email client is showing html, it's pretty likely it also supports this method
Background: I've been doing RoR for about a year now, and am fairly comfortable with it, however, I know next to nothing about Javascript.
I've been playing around with some jquery autocomplete stuff in my rails app. I pretty much had a version working, but needed some tokenized fields too for a one to many relationship.
Right on cue - good old Ryan Bates does a railscast on it. So I start following the instructions.
Got a little bit nervous when I had to start installing 'jquery-rails' gem (I'd already installed jrails to get the other stuff working).
As suspected, it broke some stuff but I managed to get that working again.
Anyway, I got most of the way through the tutorial, and everything was going fine - I've got the tokenizer script to find the correct input field and it acts as expected. I've tested the json link too - that sends back all the right stuff.
However when I start typing in the text field - nothing happens, and when I view the console window it comes back with:
Uncaught TypeError: Cannot call method 'replace' of undefined
jQuery.jQuery.extend._Deferred.deferred.resolveWith
done
jQuery.ajaxTransport.send.callback
I can make guesses as to why this is going wrong - but any expert advice would be greatly appreciated.
(I should also add - I'm using formtastic too)
Thanks in advance.
Ok, finally figured it out.
It turns out that my author's name column is not name but rather author. So I needed to make a change inside the js.coffee script to override that default search of name.
The line you need to use is:
propertyToSearch: "author"
My whole book.js.coffee file now looks like this:
jQuery ->
$('#book_author_tokens').tokenInput '/authors.json'
theme: 'mac'
prePopulate: $('#book_author_tokens').data('load')
propertyToSearch: "author"
This actually fixed the error Uncaught TypeError: Cannot call method 'replace' of undefined
Of course, if you do use a different column name you will want to also edit the functions in the author.rb file to reflect that:
def self.tokens(query)
authors = where("author like ?", "%#{query}%")
if authors.empty?
[{id: "<<<#{query}>>>", author: "New: \"#{query}\""}]
else
authors
end
end
def self.ids_from_tokens(tokens)
tokens.gsub!(/<<<(.+?)>>>/) { create!(author: $1).id }
tokens.split(',')
end
Edit
Another thing I had to do for the fieldsto be prepopulated with the existing authors was change this:
= f.input :author_tokens, :data => { :load => #book.author }
To this:
= f.input :author_tokens, :input_html => { :data => { :load => #book.author } }
And then they would show up.
Hope this helps you.