From 6f9ba8cfa09e999872857884acc7216dd9191171 Mon Sep 17 00:00:00 2001 From: esb Date: Fri, 11 Jan 2013 13:49:55 +1100 Subject: [PATCH 1/5] update_page fragments are marked as html_safe even though they have not been html_escaped. When a fragment is passed as an html tag option, the standard html_escape is bypassed because of the html_safe flag. This leaves quotes (") unescaped, causing HTML errors. --- lib/action_view/helpers/prototype_helper.rb | 4 ++-- test/template/prototype_helper_test.rb | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/action_view/helpers/prototype_helper.rb b/lib/action_view/helpers/prototype_helper.rb index 9f776e2..296ef02 100644 --- a/lib/action_view/helpers/prototype_helper.rb +++ b/lib/action_view/helpers/prototype_helper.rb @@ -547,7 +547,7 @@ def render(*options) def with_formats(*args) return yield unless @context - + lookup = @context.lookup_context begin old_formats, lookup.formats = lookup.formats, args @@ -587,7 +587,7 @@ def method_missing(method, *arguments) # page.hide 'spinner' # end def update_page(&block) - JavaScriptGenerator.new(self, &block).to_s.html_safe + JavaScriptGenerator.new(self, &block).to_s end # Works like update_page but wraps the generated JavaScript in a diff --git a/test/template/prototype_helper_test.rb b/test/template/prototype_helper_test.rb index a6aa848..1991051 100644 --- a/test/template/prototype_helper_test.rb +++ b/test/template/prototype_helper_test.rb @@ -104,6 +104,18 @@ def test_update_page_tag_with_html_options assert_equal javascript_tag(create_generator(&block).to_s, {:defer => 'true'}), update_page_tag({:defer => 'true'}, &block) end + def test_update_page_html_tag + block = Proc.new { |page| page.hide('cancel-commit') } + input_tag = submit_tag('Save', :onclick => 'Element.hide("cancel-commit");') + assert_equal input_tag, submit_tag('Save', :onclick => update_page(&block)) + end + + def test_update_page_link_to_function + block = Proc.new { |page| page.hide('cancel-commit') } + link_tag = link_to_function('Save', 'Element.hide("cancel-commit");') + assert_equal link_tag, link_to_function('Save', update_page(&block)) + end + def test_remote_function res = remote_function(:url => authors_path, :with => "'author[name]='+$F('author_name')+'&author[dob]='+$F('author_dob')") assert_equal "new Ajax.Request('/authors', {asynchronous:true, evalScripts:true, parameters:'author[name]='+$F('author_name')+'&author[dob]='+$F('author_dob')})", res From 2d5fbc13d2478c778c9c687b80a82d5e3e1e953a Mon Sep 17 00:00:00 2001 From: esb Date: Sun, 13 Jan 2013 10:47:58 +1100 Subject: [PATCH 2/5] include the X-CSRF-Token header with every Ajax request --- vendor/assets/javascripts/prototype_ujs.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/vendor/assets/javascripts/prototype_ujs.js b/vendor/assets/javascripts/prototype_ujs.js index 2cd1220..dfab254 100644 --- a/vendor/assets/javascripts/prototype_ujs.js +++ b/vendor/assets/javascripts/prototype_ujs.js @@ -1,4 +1,14 @@ (function() { + Ajax.Responders.register({ + onCreate: function(request) { + var token = $$('meta[name=csrf-token]')[0]; + if (token) { + if (!request.options.requestHeaders) request.options.requestHeaders = {}; + request.options.requestHeaders['X-CSRF-Token'] = token.readAttribute('content'); + } + } + }); + // Technique from Juriy Zaytsev // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ function isEventSupported(eventName) { From e2097dc98feb63d41492a1887febf0f4dbf8fcf3 Mon Sep 17 00:00:00 2001 From: esb Date: Sun, 13 Jan 2013 10:53:34 +1100 Subject: [PATCH 3/5] Reapply the actionpack 2.2 fix for disable-with, the original submit button's name and value need to be submitted as a hidden input. --- vendor/assets/javascripts/prototype_ujs.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/vendor/assets/javascripts/prototype_ujs.js b/vendor/assets/javascripts/prototype_ujs.js index dfab254..03ee271 100644 --- a/vendor/assets/javascripts/prototype_ujs.js +++ b/vendor/assets/javascripts/prototype_ujs.js @@ -140,6 +140,19 @@ function disableFormElements(form) { form.select('input[type=submit][data-disable-with]').each(function(input) { + if (input.name == form.retrieve('rails:submit-button')) { + if (window.hiddenCommit) { + window.hiddenCommit.setAttribute('name', input.name); + window.hiddenCommit.setAttribute('value', input.value); + } else { + hiddenCommit = document.createElement('input'); + hiddenCommit.type = 'hidden'; + hiddenCommit.value = input.value; + hiddenCommit.name = input.name; + form.appendChild(hiddenCommit); + } + } + input.store('rails:original-value', input.getValue()); input.setValue(input.readAttribute('data-disable-with')).disable(); }); From b57e9f315a32689666e09396cea5b89eafb75740 Mon Sep 17 00:00:00 2001 From: esb Date: Sun, 13 Jan 2013 11:01:03 +1100 Subject: [PATCH 4/5] Make click event handler on a elements only respond to left clicks --- vendor/assets/javascripts/prototype_ujs.js | 26 ++++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/vendor/assets/javascripts/prototype_ujs.js b/vendor/assets/javascripts/prototype_ujs.js index 03ee271..ce0b553 100644 --- a/vendor/assets/javascripts/prototype_ujs.js +++ b/vendor/assets/javascripts/prototype_ujs.js @@ -157,7 +157,7 @@ input.setValue(input.readAttribute('data-disable-with')).disable(); }); } - + function enableFormElements(form) { form.select('input[type=submit][data-disable-with]').each(function(input) { input.setValue(input.retrieve('rails:original-value')).enable(); @@ -170,17 +170,19 @@ } document.on('click', 'a[data-confirm], a[data-remote], a[data-method]', function(event, link) { - if (!allowAction(link)) { - event.stop(); - return false; - } + if (Event.isLeftClick(event)) { + if (!allowAction(link)) { + event.stop(); + return false; + } - if (link.readAttribute('data-remote')) { - handleRemote(link); - event.stop(); - } else if (link.readAttribute('data-method')) { - handleMethod(link); - event.stop(); + if (link.readAttribute('data-remote')) { + handleRemote(link); + event.stop(); + } else if (link.readAttribute('data-method')) { + handleMethod(link); + event.stop(); + } } }); @@ -208,7 +210,7 @@ document.on('ajax:create', 'form', function(event, form) { if (form == event.findElement()) disableFormElements(form); }); - + document.on('ajax:complete', 'form', function(event, form) { if (form == event.findElement()) enableFormElements(form); }); From f20170fefb42958e3f8f9c71a073b6316bbd7d3e Mon Sep 17 00:00:00 2001 From: esb Date: Tue, 15 Jan 2013 18:28:16 +1100 Subject: [PATCH 5/5] Ensure onclick handlers are escaped only once --- lib/prototype-rails/javascript_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/prototype-rails/javascript_helper.rb b/lib/prototype-rails/javascript_helper.rb index 39806d9..e99ef19 100644 --- a/lib/prototype-rails/javascript_helper.rb +++ b/lib/prototype-rails/javascript_helper.rb @@ -33,7 +33,7 @@ def button_to_function(name, *args, &block) function = block_given? ? update_page(&block) : args[0] || '' onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};" - tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick)) + tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => escape_once(onclick)), false, false) end # link_to_function("Show me more", nil, :id => "more_link") do |page| @@ -60,6 +60,6 @@ def link_to_function(name, *args, &block) onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;" href = html_options[:href] || '#' - content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick)) + content_tag(:a, name, html_options.merge(:href => href, :onclick => escape_once(onclick)), false) end end