cwninja-facebooker 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. data/CHANGELOG.txt +0 -0
  2. data/COPYING +19 -0
  3. data/History.txt +16 -0
  4. data/Manifest.txt +114 -0
  5. data/README +46 -0
  6. data/README.txt +81 -0
  7. data/Rakefile +76 -0
  8. data/TODO.txt +10 -0
  9. data/generators/facebook/facebook_generator.rb +14 -0
  10. data/generators/facebook/templates/config/facebooker.yml +46 -0
  11. data/generators/facebook/templates/public/javascripts/facebooker.js +106 -0
  12. data/generators/facebook_controller/USAGE +33 -0
  13. data/generators/facebook_controller/facebook_controller_generator.rb +40 -0
  14. data/generators/facebook_controller/templates/controller.rb +7 -0
  15. data/generators/facebook_controller/templates/functional_test.rb +11 -0
  16. data/generators/facebook_controller/templates/helper.rb +2 -0
  17. data/generators/facebook_controller/templates/view.fbml.erb +2 -0
  18. data/generators/facebook_controller/templates/view.html.erb +2 -0
  19. data/generators/facebook_publisher/facebook_publisher_generator.rb +14 -0
  20. data/generators/facebook_publisher/templates/create_facebook_templates.rb +15 -0
  21. data/generators/facebook_publisher/templates/publisher.rb +3 -0
  22. data/generators/facebook_scaffold/USAGE +27 -0
  23. data/generators/facebook_scaffold/facebook_scaffold_generator.rb +118 -0
  24. data/generators/facebook_scaffold/templates/controller.rb +93 -0
  25. data/generators/facebook_scaffold/templates/facebook_style.css +2579 -0
  26. data/generators/facebook_scaffold/templates/functional_test.rb +89 -0
  27. data/generators/facebook_scaffold/templates/helper.rb +2 -0
  28. data/generators/facebook_scaffold/templates/layout.fbml.erb +6 -0
  29. data/generators/facebook_scaffold/templates/layout.html.erb +17 -0
  30. data/generators/facebook_scaffold/templates/style.css +74 -0
  31. data/generators/facebook_scaffold/templates/view_edit.fbml.erb +13 -0
  32. data/generators/facebook_scaffold/templates/view_edit.html.erb +18 -0
  33. data/generators/facebook_scaffold/templates/view_index.fbml.erb +24 -0
  34. data/generators/facebook_scaffold/templates/view_index.html.erb +24 -0
  35. data/generators/facebook_scaffold/templates/view_new.fbml.erb +12 -0
  36. data/generators/facebook_scaffold/templates/view_new.html.erb +17 -0
  37. data/generators/facebook_scaffold/templates/view_show.fbml.erb +10 -0
  38. data/generators/facebook_scaffold/templates/view_show.html.erb +10 -0
  39. data/generators/publisher/publisher_generator.rb +14 -0
  40. data/generators/xd_receiver/templates/xd_receiver.html +10 -0
  41. data/generators/xd_receiver/xd_receiver_generator.rb +9 -0
  42. data/init.rb +70 -0
  43. data/install.rb +12 -0
  44. data/lib/facebooker.rb +160 -0
  45. data/lib/facebooker/adapters/adapter_base.rb +87 -0
  46. data/lib/facebooker/adapters/bebo_adapter.rb +75 -0
  47. data/lib/facebooker/adapters/facebook_adapter.rb +48 -0
  48. data/lib/facebooker/admin.rb +28 -0
  49. data/lib/facebooker/batch_request.rb +44 -0
  50. data/lib/facebooker/data.rb +57 -0
  51. data/lib/facebooker/feed.rb +78 -0
  52. data/lib/facebooker/logging.rb +51 -0
  53. data/lib/facebooker/model.rb +123 -0
  54. data/lib/facebooker/models/affiliation.rb +10 -0
  55. data/lib/facebooker/models/album.rb +11 -0
  56. data/lib/facebooker/models/applicationproperties.rb +39 -0
  57. data/lib/facebooker/models/cookie.rb +10 -0
  58. data/lib/facebooker/models/education_info.rb +11 -0
  59. data/lib/facebooker/models/event.rb +26 -0
  60. data/lib/facebooker/models/friend_list.rb +14 -0
  61. data/lib/facebooker/models/group.rb +35 -0
  62. data/lib/facebooker/models/info_item.rb +10 -0
  63. data/lib/facebooker/models/info_section.rb +10 -0
  64. data/lib/facebooker/models/location.rb +8 -0
  65. data/lib/facebooker/models/notifications.rb +17 -0
  66. data/lib/facebooker/models/page.rb +27 -0
  67. data/lib/facebooker/models/photo.rb +10 -0
  68. data/lib/facebooker/models/tag.rb +12 -0
  69. data/lib/facebooker/models/user.rb +369 -0
  70. data/lib/facebooker/models/work_info.rb +9 -0
  71. data/lib/facebooker/parser.rb +547 -0
  72. data/lib/facebooker/rails/controller.rb +280 -0
  73. data/lib/facebooker/rails/facebook_asset_path.rb +18 -0
  74. data/lib/facebooker/rails/facebook_form_builder.rb +112 -0
  75. data/lib/facebooker/rails/facebook_pretty_errors.rb +14 -0
  76. data/lib/facebooker/rails/facebook_request_fix.rb +24 -0
  77. data/lib/facebooker/rails/facebook_session_handling.rb +69 -0
  78. data/lib/facebooker/rails/facebook_url_helper.rb +191 -0
  79. data/lib/facebooker/rails/facebook_url_rewriting.rb +39 -0
  80. data/lib/facebooker/rails/helpers.rb +642 -0
  81. data/lib/facebooker/rails/helpers/fb_connect.rb +34 -0
  82. data/lib/facebooker/rails/profile_publisher_extensions.rb +42 -0
  83. data/lib/facebooker/rails/publisher.rb +511 -0
  84. data/lib/facebooker/rails/routing.rb +49 -0
  85. data/lib/facebooker/rails/test_helpers.rb +88 -0
  86. data/lib/facebooker/rails/utilities.rb +22 -0
  87. data/lib/facebooker/server_cache.rb +24 -0
  88. data/lib/facebooker/service.rb +83 -0
  89. data/lib/facebooker/session.rb +564 -0
  90. data/lib/facebooker/version.rb +9 -0
  91. data/lib/net/http_multipart_post.rb +123 -0
  92. data/lib/tasks/facebooker.rake +18 -0
  93. data/lib/tasks/tunnel.rake +46 -0
  94. data/rails/init.rb +1 -0
  95. data/setup.rb +1585 -0
  96. data/templates/layout.erb +24 -0
  97. data/test/adapters_test.rb +98 -0
  98. data/test/batch_request_test.rb +82 -0
  99. data/test/event_test.rb +15 -0
  100. data/test/facebook_admin_test.rb +76 -0
  101. data/test/facebook_cache_test.rb +44 -0
  102. data/test/facebook_data_test.rb +86 -0
  103. data/test/facebooker_test.rb +855 -0
  104. data/test/fixtures/multipart_post_body_with_only_parameters.txt +33 -0
  105. data/test/fixtures/multipart_post_body_with_single_file.txt +38 -0
  106. data/test/fixtures/multipart_post_body_with_single_file_that_has_nil_key.txt +38 -0
  107. data/test/http_multipart_post_test.rb +54 -0
  108. data/test/logging_test.rb +43 -0
  109. data/test/model_test.rb +91 -0
  110. data/test/publisher_test.rb +458 -0
  111. data/test/rails_integration_test.rb +1221 -0
  112. data/test/session_test.rb +613 -0
  113. data/test/test_helper.rb +60 -0
  114. data/test/user_test.rb +270 -0
  115. metadata +185 -0
@@ -0,0 +1,69 @@
1
+ module ActionController
2
+ class CgiRequest
3
+ alias :initialize_aliased_by_facebooker :initialize
4
+
5
+ def initialize(cgi, session_options = {})
6
+ initialize_aliased_by_facebooker(cgi, session_options)
7
+ @cgi.instance_variable_set("@request_params", request_parameters.merge(query_parameters))
8
+ end
9
+
10
+ DEFAULT_SESSION_OPTIONS[:cookie_only] = false
11
+ end
12
+ end
13
+
14
+ module ActionController
15
+ class RackRequest < AbstractRequest #:nodoc:
16
+ alias :initialize_aliased_by_facebooker :initialize
17
+
18
+ def initialize(cgi, session_options = {})
19
+ initialize_aliased_by_facebooker(cgi, session_options)
20
+ @cgi.instance_variable_set("@request_params", request_parameters.merge(query_parameters))
21
+ end
22
+ end
23
+ end
24
+
25
+ class CGI
26
+ class Session
27
+ private
28
+ alias :initialize_aliased_by_facebooker :initialize
29
+ attr_reader :request, :initialization_options
30
+
31
+ def initialize(request, option={})
32
+ @request = request
33
+ @initialization_options = option
34
+ option['session_id'] = set_session_id
35
+ initialize_aliased_by_facebooker(request, option)
36
+ end
37
+
38
+ def set_session_id
39
+ if session_key_should_be_set_with_facebook_session_key?
40
+ request_parameters[facebook_session_key]
41
+ else
42
+ request_parameters[session_key]
43
+ end
44
+ end
45
+
46
+ def request_parameters
47
+ request.instance_variable_get("@request_params")
48
+ end
49
+
50
+ def session_key_should_be_set_with_facebook_session_key?
51
+ request_parameters[session_key].blank? && !request_parameters[facebook_session_key].blank?
52
+ end
53
+
54
+ def session_key
55
+ initialization_options['session_key'] || '_session_id'
56
+ end
57
+
58
+ def facebook_session_key
59
+ 'fb_sig_session_key'
60
+ end
61
+
62
+ alias :create_new_id_aliased_by_facebooker :create_new_id
63
+
64
+ def create_new_id
65
+ @new_session = true
66
+ @session_id || create_new_id_aliased_by_facebooker
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,191 @@
1
+ # Extends the ActionView::Helpers::UrlHelper module. See it for details on
2
+ # the usual url helper methods: url_for, link_to, button_to, etc.
3
+ #
4
+ # Mostly, the changes sanitize javascript into facebook javascript.
5
+ # It sanitizes link_to solely by altering the private methods:
6
+ # convert_options_to_javascript!, confirm_javascript_function, and
7
+ # method_javascript_function. For button_to, it alters button_to
8
+ # itself, as well as confirm_javascript_function. No other methods
9
+ # need to be changed because of Facebook javascript.
10
+ #
11
+ # For button_to and link_to, adds alternate confirm options for facebook.
12
+ # ==== Options
13
+ # * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm
14
+ # prompt with the question specified.
15
+ #
16
+ # Example:
17
+ # # Generates: <a href="http://rubyforge.org/projects/facebooker" onclick="
18
+ # # var dlg = new Dialog().showChoice('Please Confirm', 'Go to Facebooker?').setStyle();
19
+ # # var a=this;dlg.onconfirm = function() {
20
+ # # document.setLocation(a.getHref());
21
+ # # }; return false;">Facebooker</a>
22
+ # link_to("Facebooker", "http://rubyforge.org/projects/facebooker", :confirm=>"Go to Facebooker?")
23
+ #
24
+ # Alternatively, options[:confirm] may be specified.
25
+ # See the Facebook page http://wiki.developers.facebook.com/index.php/FBJS.
26
+ # These options are:
27
+ # <tt>:title</tt>:: Specifies the title of the Facebook dialog. Default is "Please Confirm".
28
+ # <tt>:content</tt>:: Specifies the title of the Facebook dialog. Default is "Are you sure?".
29
+ #
30
+ # Example:
31
+ # # Generates: <a href="http://rubyforge.org/projects/facebooker" onclick="
32
+ # # var dlg = new Dialog().showChoice('the page says:', 'Go to Facebooker?').setStyle();
33
+ # # var a=this;dlg.onconfirm = function() {
34
+ # # document.setLocation(a.getHref());
35
+ # # }; return false;">Facebooker</a>
36
+ # link_to("Facebooker", "http://rubyforge.org/projects/facebooker", :confirm=>{:title=>"the page says:", :content=>"Go to Facebooker?"})
37
+ #
38
+ # Any other options passed are assumed to be css styles.
39
+ # Again, see the Facebook page http://wiki.developers.facebook.com/index.php/FBJS.
40
+ #
41
+ # Example:
42
+ # # Generates: <a href="http://rubyforge.org/projects/facebooker" onclick="
43
+ # # var dlg = new Dialog().showChoice('the page says:', 'Are you sure?').setStyle({color: 'pink', width: '200px'});
44
+ # # var a=this;dlg.onconfirm = function() {
45
+ # # document.setLocation(a.getHref());
46
+ # # }; return false;">Facebooker</a>
47
+ # link_to("Facebooker", "http://rubyforge.org/projects/facebooker", :confirm=>{:title=>"the page says:, :color=>"pink", :width=>"200px"})
48
+ module ActionView
49
+ module Helpers
50
+ module UrlHelper
51
+ # Alters one and only one line of the Rails button_to. See below.
52
+ def button_to_with_facebooker(name, options={}, html_options = {})
53
+ if !request_comes_from_facebook?
54
+ button_to_without_facebooker(name,options,html_options)
55
+ else
56
+ html_options = html_options.stringify_keys
57
+ convert_boolean_attributes!(html_options, %w( disabled ))
58
+
59
+ method_tag = ''
60
+ if (method = html_options.delete('method')) && %w{put delete}.include?(method.to_s)
61
+ method_tag = tag('input', :type => 'hidden', :name => '_method', :value => method.to_s)
62
+ end
63
+
64
+ form_method = method.to_s == 'get' ? 'get' : 'post'
65
+
66
+ request_token_tag = ''
67
+ if form_method == 'post' && protect_against_forgery?
68
+ request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
69
+ end
70
+
71
+ if confirm = html_options.delete("confirm")
72
+ # this line is the only change => html_options["onclick"] = "return #{confirm_javascript_function(confirm)}"
73
+ html_options["onclick"] = "#{confirm_javascript_function(confirm, 'a.getForm().submit();')}return false;"
74
+ end
75
+
76
+ url = options.is_a?(String) ? options : self.url_for(options)
77
+ name ||= url
78
+
79
+ html_options.merge!("type" => "submit", "value" => name)
80
+
81
+ "<form method=\"#{form_method}\" action=\"#{escape_once url}\" class=\"button-to\"><div>" +
82
+ method_tag + tag("input", html_options) + request_token_tag + "</div></form>"
83
+ end
84
+ end
85
+
86
+ alias_method_chain :button_to, :facebooker
87
+
88
+ private
89
+
90
+ # Altered to throw an error on :popup and sanitize the javascript
91
+ # for Facebook.
92
+ def convert_options_to_javascript_with_facebooker!(html_options, url ='')
93
+ if !request_comes_from_facebook?
94
+ convert_options_to_javascript_without_facebooker!(html_options,url)
95
+ else
96
+ confirm, popup = html_options.delete("confirm"), html_options.delete("popup")
97
+
98
+ method, href = html_options.delete("method"), html_options['href']
99
+
100
+ html_options["onclick"] = case
101
+ when popup
102
+ raise ActionView::ActionViewError, "You can't use :popup"
103
+ when method # or maybe (confirm and method)
104
+ "#{method_javascript_function(method, url, href, confirm)}return false;"
105
+ when confirm # and only confirm
106
+ "#{confirm_javascript_function(confirm)}return false;"
107
+ else
108
+ html_options["onclick"]
109
+ end
110
+ end
111
+ end
112
+
113
+ alias_method_chain :convert_options_to_javascript!, :facebooker
114
+
115
+
116
+ # Overrides a private method that link_to calls via convert_options_to_javascript! and
117
+ # also, button_to calls directly. For Facebook, confirm can be a hash of options to
118
+ # stylize the Facebook dialog. Takes :title, :content, :style options. See
119
+ # the Facebook page http://wiki.developers.facebook.com/index.php/FBJS for valid
120
+ # style formats like "color: 'black', background: 'white'" or like "'color','black'".
121
+ #
122
+ # == Examples ==
123
+ #
124
+ # link_to("Facebooker", "http://rubyforge.org/projects/facebooker", :confirm=>"Go to Facebooker?")
125
+ # link_to("Facebooker", "http://rubyforge.org/projects/facebooker", :confirm=>{:title=>"the page says:, :content=>"Go to Facebooker?"})
126
+ # link_to("Facebooker", "http://rubyforge.org/projects/facebooker", :confirm=>{:title=>"the page says:, :content=>"Go to Facebooker?", :color=>"pink"})
127
+ def confirm_javascript_function_with_facebooker(confirm, fun = nil)
128
+ if !request_comes_from_facebook?
129
+ confirm_javascript_function_without_facebooker(confirm)
130
+ else
131
+ if(confirm.is_a?(Hash))
132
+ confirm_options = confirm.stringify_keys
133
+ title = confirm_options.delete("title") || "Please Confirm"
134
+ content = confirm_options.delete("content") || "Are you sure?"
135
+ style = confirm_options.empty? ? "" : convert_options_to_css(confirm_options)
136
+ else
137
+ title,content,style = 'Please Confirm', confirm, ""
138
+ end
139
+
140
+ "var dlg = new Dialog().showChoice('#{escape_javascript(title.to_s)}','#{escape_javascript(content.to_s)}').setStyle(#{style});"+
141
+ "var a=this;dlg.onconfirm = function() { #{fun ? fun : 'document.setLocation(a.getHref());'} };"
142
+ end
143
+ end
144
+
145
+ alias_method_chain :confirm_javascript_function, :facebooker
146
+
147
+ def convert_options_to_css(options)
148
+ key_pair = options.shift
149
+ style = "{#{key_pair[0]}: '#{key_pair[1]}'"
150
+ for key in options.keys
151
+ style << ", #{key}: '#{options[key]}'"
152
+ end
153
+ style << "}"
154
+ end
155
+
156
+ # Dynamically creates a form for link_to with method. Calls confirm_javascript_function if and
157
+ # only if (confirm && method) for link_to
158
+ def method_javascript_function_with_facebooker(method, url = '', href = nil, confirm = nil)
159
+ if !request_comes_from_facebook?
160
+ method_javascript_function_without_facebooker(method,url,href)
161
+ else
162
+ action = (href && url.size > 0) ? "'#{url}'" : 'a.getHref()'
163
+ submit_function =
164
+ "var f = document.createElement('form'); f.setStyle('display','none'); " +
165
+ "a.getParentNode().appendChild(f); f.setMethod('POST'); f.setAction(#{action});"
166
+
167
+ unless method == :post
168
+ submit_function << "var m = document.createElement('input'); m.setType('hidden'); "
169
+ submit_function << "m.setName('_method'); m.setValue('#{method}'); f.appendChild(m);"
170
+ end
171
+
172
+ if protect_against_forgery?
173
+ submit_function << "var s = document.createElement('input'); s.setType('hidden'); "
174
+ submit_function << "s.setName('#{request_forgery_protection_token}'); s.setValue('#{escape_javascript form_authenticity_token}'); f.appendChild(s);"
175
+ end
176
+ submit_function << "f.submit();"
177
+
178
+ if(confirm)
179
+ confirm_javascript_function(confirm, submit_function)
180
+ else
181
+ "var a=this;" + submit_function
182
+ end
183
+ end
184
+ end
185
+
186
+ alias_method_chain :method_javascript_function, :facebooker
187
+
188
+ end
189
+ end
190
+ end
191
+
@@ -0,0 +1,39 @@
1
+ module ::ActionController
2
+ class AbstractRequest
3
+ def relative_url_root
4
+ Facebooker.path_prefix
5
+ end
6
+ end
7
+
8
+ class Base
9
+ def self.relative_url_root
10
+ Facebooker.path_prefix
11
+ end
12
+ end
13
+
14
+ class UrlRewriter
15
+ RESERVED_OPTIONS << :canvas
16
+ def link_to_new_canvas?
17
+ @request.parameters["fb_sig_in_new_facebook"] == "1"
18
+ end
19
+ def link_to_canvas?(params, options)
20
+ option_override = options[:canvas]
21
+ return false if option_override == false # important to check for false. nil should use default behavior
22
+ option_override || @request.parameters["fb_sig_in_canvas"] == "1" || @request.parameters[:fb_sig_in_canvas] == "1"
23
+ end
24
+
25
+ def rewrite_url_with_facebooker(*args)
26
+ options = args.first.is_a?(Hash) ? args.first : args.last
27
+ is_link_to_canvas = link_to_canvas?(@request.request_parameters, options)
28
+ if is_link_to_canvas && !options.has_key?(:host)
29
+ options[:host] = Facebooker.canvas_server_base
30
+ end
31
+ options.delete(:canvas)
32
+ Facebooker.request_for_canvas(is_link_to_canvas) do
33
+ rewrite_url_without_facebooker(*args)
34
+ end
35
+ end
36
+
37
+ alias_method_chain :rewrite_url, :facebooker
38
+ end
39
+ end
@@ -0,0 +1,642 @@
1
+ module Facebooker
2
+ module Rails
3
+
4
+ # Facebook specific helpers for creating FBML
5
+ #
6
+ # All helpers that take a user as a parameter will get the Facebook UID from the facebook_id attribute if it exists.
7
+ # It will use to_s if the facebook_id attribute is not present.
8
+ #
9
+ module Helpers
10
+
11
+ include Facebooker::Rails::Helpers::FbConnect
12
+
13
+ # Create an fb:dialog
14
+ # id must be a unique name e.g. "my_dialog"
15
+ # cancel_button is true or false
16
+ def fb_dialog( id, cancel_button, &block )
17
+ content = capture(&block)
18
+ concat( content_tag("fb:dialog", content, {:id => id, :cancel_button => cancel_button}), block.binding )
19
+ end
20
+
21
+ def fb_dialog_title( title )
22
+ content_tag "fb:dialog-title", title
23
+ end
24
+
25
+ def fb_dialog_content( &block )
26
+ content = capture(&block)
27
+ concat( content_tag("fb:dialog-content", content), block.binding )
28
+ end
29
+
30
+ def fb_dialog_button( type, value, options={} )
31
+ options.assert_valid_keys FB_DIALOG_BUTTON_VALID_OPTION_KEYS
32
+ options.merge! :type => type, :value => value
33
+ tag "fb:dialog-button", options
34
+ end
35
+
36
+ FB_DIALOG_BUTTON_VALID_OPTION_KEYS = [:close_dialog, :href, :form_id, :clickrewriteurl, :clickrewriteid, :clickrewriteform]
37
+
38
+ # Create an fb:request-form without a selector
39
+ #
40
+ # The block passed to this tag is used as the content of the form
41
+ #
42
+ # The message param is the name sent to content_for that specifies the body of the message
43
+ #
44
+ # For example,
45
+ #
46
+ # <% content_for("invite_message") do %>
47
+ # This gets sent in the invite. <%= fb_req_choice("with a button!",new_poke_path) %>
48
+ # <% end %>
49
+ # <% fb_request_form("Poke","invite_message",create_poke_path) do %>
50
+ # Send a poke to: <%= fb_friend_selector %> <br />
51
+ # <%= fb_request_form_submit %>
52
+ # <% end %>
53
+ def fb_request_form(type,message_param,url,options={},&block)
54
+ content = capture(&block)
55
+ message = @template.instance_variable_get("@content_for_#{message_param}")
56
+ concat(content_tag("fb:request-form", content + token_tag,
57
+ {:action=>url,:method=>"post",:invite=>true,:type=>type,:content=>message}.merge(options)),
58
+ block.binding)
59
+ end
60
+
61
+ # Create a submit button for an <fb:request-form>
62
+ # If the request is for an individual user you can optionally
63
+ # Provide the user and a label for the request button.
64
+ # For example
65
+ # <% content_for("invite_user") do %>
66
+ # This gets sent in the invite. <%= fb_req_choice("Come join us!",new_invite_path) %>
67
+ # <% end %>
68
+ # <% fb_request_form("Invite","invite_user",create_invite_path) do %>
69
+ # Invite <%= fb_name(@facebook_user.friends.first.id)%> to the party <br />
70
+ # <%= fb_request_form_submit(@facebook_user.friends.first.id,"Invite %n") %>
71
+ # <% end %>
72
+ # <em>See:</em> http://wiki.developers.facebook.com/index.php/Fb:request-form-submit for options
73
+ def fb_request_form_submit(options={})
74
+ tag("fb:request-form-submit",stringify_vals(options))
75
+ end
76
+
77
+
78
+ # Create an fb:request-form with an fb_multi_friend_selector inside
79
+ #
80
+ # The content of the block are used as the message on the form,
81
+ #
82
+ # For example:
83
+ # <% fb_multi_friend_request("Poke","Choose some friends to Poke",create_poke_path) do %>
84
+ # If you select some friends, they will see this message.
85
+ # <%= fb_req_choice("They will get this button, too",new_poke_path) %>
86
+ # <% end %>
87
+ def fb_multi_friend_request(type,friend_selector_message,url,&block)
88
+ content = capture(&block)
89
+ concat(content_tag("fb:request-form",
90
+ fb_multi_friend_selector(friend_selector_message) + token_tag,
91
+ {:action=>url,:method=>"post",:invite=>true,:type=>type,:content=>content}
92
+ ),
93
+ block.binding)
94
+ end
95
+
96
+ # Render an <fb:friend-selector> element
97
+ # <em>See:</em> http://wiki.developers.facebook.com/index.php/Fb:friend-selector for options
98
+ #
99
+ def fb_friend_selector(options={})
100
+ tag("fb:friend-selector",stringify_vals(options))
101
+ end
102
+
103
+ # Render an <fb:multi-friend-input> element
104
+ # <em> See: </em> http://wiki.developers.facebook.com/index.php/Fb:multi-friend-input for options
105
+ def fb_multi_friend_input(options={})
106
+ tag "fb:multi-friend-input",stringify_vals(options)
107
+ end
108
+
109
+ # Render an <fb:multi-friend-selector> with the passed in welcome message
110
+ # Full version shows all profile pics for friends.
111
+ # <em> See: </em> http://wiki.developers.facebook.com/index.php/Fb:multi-friend-selector for options
112
+ # <em> Note: </em> I don't think the block is used here.
113
+ def fb_multi_friend_selector(message,options={},&block)
114
+ options = options.dup
115
+ tag("fb:multi-friend-selector",stringify_vals({:showborder=>false,:actiontext=>message,:max=>20}.merge(options)))
116
+ end
117
+
118
+ # Render a condensed <fb:multi-friend-selector> with the passed in welcome message
119
+ # Condensed version show checkboxes for each friend.
120
+ # <em> See: </em> http://wiki.developers.facebook.com/index.php/Fb:multi-friend-selector_%28condensed%29 for options
121
+ # <em> Note: </em> I don't think the block is used here.
122
+ def fb_multi_friend_selector_condensed(options={},&block)
123
+ options = options.dup
124
+ tag("fb:multi-friend-selector",stringify_vals(options.merge(:condensed=>true)))
125
+ end
126
+
127
+ # Render a button in a request using the <fb:req-choice> tag
128
+ # url must be an absolute url
129
+ # This should be used inside the block of an fb_multi_friend_request
130
+ def fb_req_choice(label,url)
131
+ tag "fb:req-choice",:label=>label,:url=>url
132
+ end
133
+
134
+ # Create a facebook form using <fb:editor>
135
+ #
136
+ # It yields a form builder that will convert the standard rails form helpers
137
+ # into the facebook specific version.
138
+ #
139
+ # Example:
140
+ # <% facebook_form_for(:poke,@poke,:url => create_poke_path) do |f| %>
141
+ # <%= f.text_field :message, :label=>"message" %>
142
+ # <%= f.buttons "Save Poke" %>
143
+ # <% end %>
144
+ #
145
+ # will generate
146
+ #
147
+ # <fb:editor action="/pokes/create">
148
+ # <fb:editor-text name="poke[message]" id="poke_message" value="" label="message" />
149
+ # <fb:editor-buttonset>
150
+ # <fb:editor-button label="Save Poke"
151
+ # </fb:editor-buttonset>
152
+ # </fb:editor>
153
+ def facebook_form_for( record_or_name_or_array,*args, &proc)
154
+
155
+ raise ArgumentError, "Missing block" unless block_given?
156
+ options = args.last.is_a?(Hash) ? args.pop : {}
157
+
158
+ case record_or_name_or_array
159
+ when String, Symbol
160
+ object_name = record_or_name_or_array
161
+ when Array
162
+ object = record_or_name_or_array.last
163
+ object_name = ActionController::RecordIdentifier.singular_class_name(object)
164
+ apply_form_for_options!(record_or_name_or_array, options)
165
+ args.unshift object
166
+ else
167
+ object = record_or_name_or_array
168
+ object_name = ActionController::RecordIdentifier.singular_class_name(object)
169
+ apply_form_for_options!([object], options)
170
+ args.unshift object
171
+ end
172
+ method = (options[:html]||{})[:method]
173
+ options[:builder] ||= Facebooker::Rails::FacebookFormBuilder
174
+ editor_options={}
175
+
176
+ action=options.delete(:url)
177
+ editor_options[:action]= action unless action.blank?
178
+ width=options.delete(:width)
179
+ editor_options[:width]=width unless width.blank?
180
+ width=options.delete(:labelwidth)
181
+ editor_options[:labelwidth]=width unless width.blank?
182
+
183
+ concat(tag("fb:editor",editor_options,true) , proc.binding)
184
+ concat(tag(:input,{:type=>"hidden",:name=>:_method, :value=>method},false), proc.binding) unless method.blank?
185
+ concat(token_tag, proc.binding)
186
+ fields_for( object_name,*(args << options), &proc)
187
+ concat("</fb:editor>",proc.binding)
188
+ end
189
+
190
+ # Render an fb:name tag for the given user
191
+ # This renders the name of the user specified. You can use this tag as both subject and object of
192
+ # a sentence. <em> See </em> http://wiki.developers.facebook.com/index.php/Fb:name for full description.
193
+ # Use this tag on FBML pages instead of retrieving the user's info and rendering the name explicitly.
194
+ #
195
+ def fb_name(user, options={})
196
+ options = options.dup
197
+ options.transform_keys!(FB_NAME_OPTION_KEYS_TO_TRANSFORM)
198
+ options.assert_valid_keys(FB_NAME_VALID_OPTION_KEYS)
199
+ options.merge!(:uid => cast_to_facebook_id(user))
200
+ content_tag("fb:name",nil, stringify_vals(options))
201
+ end
202
+
203
+ FB_NAME_OPTION_KEYS_TO_TRANSFORM = {:first_name_only => :firstnameonly,
204
+ :last_name_only => :lastnameonly,
205
+ :show_network => :shownetwork,
206
+ :use_you => :useyou,
207
+ :if_cant_see => :ifcantsee,
208
+ :subject_id => :subjectid}
209
+ FB_NAME_VALID_OPTION_KEYS = [:firstnameonly, :linked, :lastnameonly, :possessive, :reflexive,
210
+ :shownetwork, :useyou, :ifcantsee, :capitalize, :subjectid]
211
+
212
+ # Render an <fb:pronoun> tag for the specified user
213
+ # Options give flexibility for placing in any part of a sentence.
214
+ # <em> See </em> http://wiki.developers.facebook.com/index.php/Fb:pronoun for complete list of options.
215
+ #
216
+ def fb_pronoun(user, options={})
217
+ options = options.dup
218
+ options.transform_keys!(FB_PRONOUN_OPTION_KEYS_TO_TRANSFORM)
219
+ options.assert_valid_keys(FB_PRONOUN_VALID_OPTION_KEYS)
220
+ options.merge!(:uid => cast_to_facebook_id(user))
221
+ content_tag("fb:pronoun",nil, stringify_vals(options))
222
+ end
223
+
224
+ FB_PRONOUN_OPTION_KEYS_TO_TRANSFORM = {:use_you => :useyou, :use_they => :usethey}
225
+ FB_PRONOUN_VALID_OPTION_KEYS = [:useyou, :possessive, :reflexive, :objective,
226
+ :usethey, :capitalize]
227
+
228
+ # Render an fb:ref tag.
229
+ # Options must contain either url or handle.
230
+ # * <em> url </em> The URL from which to fetch the FBML. You may need to call fbml.refreshRefUrl to refresh cache
231
+ # * <em> handle </em> The string previously set by fbml.setRefHandle that identifies the FBML
232
+ # <em> See </em> http://wiki.developers.facebook.com/index.php/Fb:ref for complete description
233
+ def fb_ref(options)
234
+ options.assert_valid_keys(FB_REF_VALID_OPTION_KEYS)
235
+ validate_fb_ref_has_either_url_or_handle(options)
236
+ validate_fb_ref_does_not_have_both_url_and_handle(options)
237
+ tag("fb:ref", stringify_vals(options))
238
+ end
239
+
240
+ def validate_fb_ref_has_either_url_or_handle(options)
241
+ unless options.has_key?(:url) || options.has_key?(:handle)
242
+ raise ArgumentError, "fb_ref requires :url or :handle"
243
+ end
244
+ end
245
+
246
+ def validate_fb_ref_does_not_have_both_url_and_handle(options)
247
+ if options.has_key?(:url) && options.has_key?(:handle)
248
+ raise ArgumentError, "fb_ref may not have both :url and :handle"
249
+ end
250
+ end
251
+
252
+ FB_REF_VALID_OPTION_KEYS = [:url, :handle]
253
+
254
+ # Render an <fb:profile-pic> for the specified user.
255
+ #
256
+ # You can optionally specify the size using the :size=> option.
257
+ #
258
+ # Valid sizes are :thumb, :small, :normal and :square
259
+ def fb_profile_pic(user, options={})
260
+ options = options.dup
261
+ validate_fb_profile_pic_size(options)
262
+ options.merge!(:uid => cast_to_facebook_id(user))
263
+ content_tag("fb:profile-pic", nil,stringify_vals(options))
264
+ end
265
+
266
+ # Render an fb:photo tag.
267
+ # photo is either a Facebooker::Photo or an id of a Facebook photo or an object that responds to photo_id.
268
+ # <em> See: </em> http://wiki.developers.facebook.com/index.php/Fb:photo for complete list of options.
269
+ def fb_photo(photo, options={})
270
+ options = options.dup
271
+ options.assert_valid_keys(FB_PHOTO_VALID_OPTION_KEYS)
272
+ options.merge!(:pid => cast_to_photo_id(photo))
273
+ validate_fb_photo_size(options)
274
+ validate_fb_photo_align_value(options)
275
+ content_tag("fb:photo",nil, stringify_vals(options))
276
+ end
277
+
278
+ FB_PHOTO_VALID_OPTION_KEYS = [:uid, :size, :align]
279
+
280
+ def cast_to_photo_id(object)
281
+ object.respond_to?(:photo_id) ? object.photo_id : object
282
+ end
283
+
284
+ VALID_FB_SHARED_PHOTO_SIZES = [:thumb, :small, :normal, :square]
285
+ VALID_FB_PHOTO_SIZES = VALID_FB_SHARED_PHOTO_SIZES
286
+ VALID_FB_PROFILE_PIC_SIZES = VALID_FB_SHARED_PHOTO_SIZES
287
+ VALID_PERMISSIONS=[:email, :offline_access, :status_update, :photo_upload, :create_listing, :create_event, :rsvp_event, :sms]
288
+
289
+ # Render an fb:tabs tag containing some number of fb:tab_item tags.
290
+ # Example:
291
+ # <% fb_tabs do %>
292
+ # <%= fb_tab_item("Home", "home") %>
293
+ # <%= fb_tab_item("Office", "office") %>
294
+ # <% end %>
295
+ def fb_tabs(&block)
296
+ content = capture(&block)
297
+ concat(content_tag("fb:tabs", content), block.binding)
298
+ end
299
+
300
+ # Render an fb:tab_item tag.
301
+ # Use this in conjunction with fb_tabs
302
+ # Options can contains :selected => true to indicate that a tab is the current tab.
303
+ # <em> See: </em> http://wiki.developers.facebook.com/index.php/Fb:tab-item for complete list of options
304
+ def fb_tab_item(title, url, options={})
305
+ options= options.dup
306
+ options.assert_valid_keys(FB_TAB_ITEM_VALID_OPTION_KEYS)
307
+ options.merge!(:title => title, :href => url)
308
+ validate_fb_tab_item_align_value(options)
309
+ tag("fb:tab-item", stringify_vals(options))
310
+ end
311
+
312
+ FB_TAB_ITEM_VALID_OPTION_KEYS = [:align, :selected]
313
+
314
+ def validate_fb_tab_item_align_value(options)
315
+ if options.has_key?(:align) && !VALID_FB_TAB_ITEM_ALIGN_VALUES.include?(options[:align].to_sym)
316
+ raise(ArgumentError, "Unkown value for align: #{options[:align]}")
317
+ end
318
+ end
319
+
320
+ def validate_fb_photo_align_value(options)
321
+ if options.has_key?(:align) && !VALID_FB_PHOTO_ALIGN_VALUES.include?(options[:align].to_sym)
322
+ raise(ArgumentError, "Unkown value for align: #{options[:align]}")
323
+ end
324
+ end
325
+
326
+ VALID_FB_SHARED_ALIGN_VALUES = [:left, :right]
327
+ VALID_FB_PHOTO_ALIGN_VALUES = VALID_FB_SHARED_ALIGN_VALUES
328
+ VALID_FB_TAB_ITEM_ALIGN_VALUES = VALID_FB_SHARED_ALIGN_VALUES
329
+
330
+
331
+ # Create a Facebook wall. It can contain fb_wall_posts
332
+ #
333
+ # For Example:
334
+ # <% fb_wall do %>
335
+ # <%= fb_wall_post(@user,"This is my message") %>
336
+ # <%= fb_wall_post(@otheruser,"This is another message") %>
337
+ # <% end %>
338
+ def fb_wall(&proc)
339
+ content = capture(&proc)
340
+ concat(content_tag("fb:wall",content,{}),proc.binding)
341
+ end
342
+
343
+ # Render an <fb:wallpost> tag
344
+ # TODO: Optionally takes a time parameter t = int The current time, which is displayed in epoch seconds.
345
+ def fb_wallpost(user,message)
346
+ content_tag("fb:wallpost",message,:uid=>cast_to_facebook_id(user))
347
+ end
348
+ alias_method :fb_wall_post, :fb_wallpost
349
+
350
+ # Render an <fb:error> tag
351
+ # If message and text are present then this will render fb:error and fb:message tag
352
+ # TODO: Optionally takes a decoration tag with value of 'no_padding' or 'shorten'
353
+ def fb_error(message, text=nil)
354
+ fb_status_msg("error", message, text)
355
+ end
356
+
357
+ # Render an <fb:explanation> tag
358
+ # If message and text are present then this will render fb:error and fb:message tag
359
+ # TODO: Optionally takes a decoration tag with value of 'no_padding' or 'shorten'
360
+ def fb_explanation(message, text=nil)
361
+ fb_status_msg("explanation", message, text)
362
+ end
363
+
364
+ # Render an <fb:success> tag
365
+ # If message and text are present then this will render fb:error and fb:message tag
366
+ # TODO: Optionally takes a decoration tag with value of 'no_padding' or 'shorten'
367
+ def fb_success(message, text=nil)
368
+ fb_status_msg("success", message, text)
369
+ end
370
+
371
+ # Render flash values as <fb:message> and <fb:error> tags
372
+ #
373
+ # values in flash[:notice] will be rendered as an <fb:message>
374
+ #
375
+ # values in flash[:error] will be rendered as an <fb:error>
376
+ # TODO: Allow flash[:info] to render fb_explanation
377
+ def facebook_messages
378
+ message=""
379
+ unless flash[:notice].blank?
380
+ message += fb_success(flash[:notice])
381
+ end
382
+ unless flash[:error].blank?
383
+ message += fb_error(flash[:error])
384
+ end
385
+ message
386
+ end
387
+
388
+ # Create a dashboard. It can contain fb_action, fb_help, and fb_create_button
389
+ #
390
+ # For Example:
391
+ # <% fb_dashboard do %>
392
+ # <%= APP_NAME %>
393
+ # <%= fb_action 'My Matches', search_path %>
394
+ # <%= fb_help 'Feedback', "http://www.facebook.com/apps/application.php?id=6236036681" %>
395
+ # <%= fb_create_button 'Invite Friends', main_path %>
396
+ # <% end %>
397
+ def fb_dashboard(&proc)
398
+ if block_given?
399
+ content = capture(&proc)
400
+ concat(content_tag("fb:dashboard",content,{}),proc.binding)
401
+ else
402
+ content_tag("fb:dashboard",content,{})
403
+ end
404
+ end
405
+
406
+ # Content for the wide profile box goes in this tag
407
+ def fb_wide(&proc)
408
+ content = capture(&proc)
409
+ concat(content_tag("fb:wide", content, {}), proc.binding)
410
+ end
411
+
412
+ # Content for the narrow profile box goes in this tag
413
+ def fb_narrow(&proc)
414
+ content = capture(&proc)
415
+ concat(content_tag("fb:narrow", content, {}), proc.binding)
416
+ end
417
+
418
+ # Renders an action using the <fb:action> tag
419
+ def fb_action(name, url)
420
+ "<fb:action href=\"#{url_for(url)}\">#{name}</fb:action>"
421
+ end
422
+
423
+ # Render a <fb:help> tag
424
+ # For use inside <fb:dashboard>
425
+ def fb_help(name, url)
426
+ "<fb:help href=\"#{url_for(url)}\">#{name}</fb:help>"
427
+ end
428
+
429
+ # Render a <fb:create-button> tag
430
+ # For use inside <fb:dashboard>
431
+ def fb_create_button(name, url)
432
+ "<fb:create-button href=\"#{url_for(url)}\">#{name}</fb:create-button>"
433
+ end
434
+
435
+ # Create a comment area
436
+ # All the data for this content area is stored on the facebook servers.
437
+ # <em>See:</em> http://wiki.developers.facebook.com/index.php/Fb:comments for full details
438
+ def fb_comments(xid,canpost=true,candelete=false,numposts=5,options={})
439
+ options = options.dup
440
+ title = (title = options.delete(:title)) ? fb_title(title) : nil
441
+ content_tag "fb:comments",title,stringify_vals(options.merge(:xid=>xid,:canpost=>canpost.to_s,:candelete=>candelete.to_s,:numposts=>numposts))
442
+ end
443
+
444
+ # Adds a title to the title bar
445
+ #
446
+ # Facebook | App Name | This is the canvas page window title
447
+ #
448
+ # +title+: This is the canvas page window
449
+ def fb_title(title)
450
+ "<fb:title>#{title}</fb:title>"
451
+ end
452
+
453
+ # Create a Google Analytics tag
454
+ #
455
+ # +uacct+: Your Urchin/Google Analytics account ID.
456
+ def fb_google_analytics(uacct, options={})
457
+ options = options.dup
458
+ tag "fb:google-analytics", stringify_vals(options.merge(:uacct => uacct))
459
+ end
460
+
461
+ # Render if-is-app-user tag
462
+ # This tag renders the enclosing content only if the user specified has accepted the terms of service for the application.
463
+ # Use fb_if_user_has_added_app to determine wether the user has added the app.
464
+ # Example:
465
+ # <% fb_if_is_app_user(@facebook_user) do %>
466
+ # Thanks for accepting our terms of service!
467
+ # <% fb_else do %>
468
+ # Hey you haven't agreed to our terms. <%= link_to("Please accept our terms of service.", :action => "terms_of_service") %>
469
+ # <% end %>
470
+ #<% end %>
471
+ def fb_if_is_app_user(user=nil,options={},&proc)
472
+ content = capture(&proc)
473
+ options = options.dup
474
+ options.merge!(:uid=>cast_to_facebook_id(user)) if user
475
+ concat(content_tag("fb:if-is-app-user",content,stringify_vals(options)),proc.binding)
476
+ end
477
+
478
+ # Render if-user-has-added-app tag
479
+ # This tag renders the enclosing content only if the user specified has installed the application
480
+ #
481
+ # Example:
482
+ # <% fb_if_user_has_added_app(@facebook_user) do %>
483
+ # Hey you are an app user!
484
+ # <% fb_else do %>
485
+ # Hey you aren't an app user. <%= link_to("Add App and see the other side.", :action => "added_app") %>
486
+ # <% end %>
487
+ #<% end %>
488
+ def fb_if_user_has_added_app(user,options={},&proc)
489
+ content = capture(&proc)
490
+ options = options.dup
491
+ concat(content_tag("fb:if-user-has-added-app",content,stringify_vals(options.merge(:uid=>cast_to_facebook_id(user)))),proc.binding)
492
+ end
493
+
494
+ # Render fb:if-is-user tag
495
+ # This tag only renders enclosing content if the user is one of those specified
496
+ # user can be a single user or an Array of users
497
+ # Example:
498
+ # <% fb_if_is_user(@check_user) do %>
499
+ # <%= fb_name(@facebook_user) %> are one of the users. <%= link_to("Check the other side", :action => "friend") %>
500
+ # <% fb_else do %>
501
+ # <%= fb_name(@facebook_user) %> are not one of the users <%= fb_name(@check_user) %>
502
+ # <%= link_to("Check the other side", :action => "you") %>
503
+ # <% end %>
504
+ # <% end %>
505
+ def fb_if_is_user(user,&proc)
506
+ content = capture(&proc)
507
+ user = [user] unless user.is_a? Array
508
+ user_list=user.map{|u| cast_to_facebook_id(u)}.join(",")
509
+ concat(content_tag("fb:if-is-user",content,{:uid=>user_list}),proc.binding)
510
+ end
511
+
512
+ # Render fb:else tag
513
+ # Must be used within if block such as fb_if_is_user or fb_if_is_app_user . See example in fb_if_is_app_user
514
+ def fb_else(&proc)
515
+ content = capture(&proc)
516
+ concat(content_tag("fb:else",content),proc.binding)
517
+ end
518
+
519
+ #
520
+ # Return the URL for the about page of the application
521
+ def fb_about_url
522
+ "http://#{Facebooker.www_server_base_url}/apps/application.php?api_key=#{Facebooker.api_key}"
523
+ end
524
+
525
+ #
526
+ # Embed a discussion board named xid on the current page
527
+ #
528
+ def fb_board(xid,options={})
529
+ options = options.dup
530
+ tag("fb:board",stringify_vals(options.merge(:xid=>xid)))
531
+ end
532
+
533
+ def fb_add_profile_section
534
+ tag "fb:add-section-button",:section=>"profile"
535
+ end
536
+
537
+ def fb_add_info_section
538
+ tag "fb:add-section-button",:section=>"info"
539
+ end
540
+
541
+ def fb_prompt_permission(permission,message,callback=nil)
542
+ raise(ArgumentError, "Unknown value for permission: #{permission}") unless VALID_PERMISSIONS.include?(permission.to_sym)
543
+ args={:perms=>permission}
544
+ args[:next_fbjs]=callback unless callback.nil?
545
+ content_tag("fb:prompt-permission",message,args)
546
+ end
547
+
548
+ def fb_eventlink(eid)
549
+ content_tag "fb:eventlink",nil,:eid=>eid
550
+ end
551
+
552
+ def fb_grouplink(gid)
553
+ content_tag "fb:grouplink",nil,:gid=>gid
554
+ end
555
+
556
+ def fb_user_status(user,linked=true)
557
+ content_tag "fb:user-status",nil,stringify_vals(:uid=>cast_to_facebook_id(user), :linked=>linked)
558
+ end
559
+
560
+ def fb_share_button(url)
561
+ content_tag "fb:share-button",nil,:class=>"url",:href=>url
562
+ end
563
+
564
+ def fb_serverfbml(options={},&proc)
565
+ inner = capture(&proc)
566
+ concat(content_tag("fb:serverfbml",inner,options),&proc.binding)
567
+ end
568
+
569
+ def fb_container(options={},&proc)
570
+ inner = capture(&proc)
571
+ concat(content_tag("fb:container",inner,options),&proc.binding)
572
+ end
573
+
574
+ protected
575
+
576
+ def cast_to_facebook_id(object)
577
+ Facebooker::User.cast_to_facebook_id(object)
578
+ end
579
+
580
+ def validate_fb_profile_pic_size(options)
581
+ if options.has_key?(:size) && !VALID_FB_PROFILE_PIC_SIZES.include?(options[:size].to_sym)
582
+ raise(ArgumentError, "Unkown value for size: #{options[:size]}")
583
+ end
584
+ end
585
+
586
+ def validate_fb_photo_size(options)
587
+ if options.has_key?(:size) && !VALID_FB_PHOTO_SIZES.include?(options[:size].to_sym)
588
+ raise(ArgumentError, "Unkown value for size: #{options[:size]}")
589
+ end
590
+ end
591
+
592
+ private
593
+ def stringify_vals(hash)
594
+ result={}
595
+ hash.each do |key,value|
596
+ result[key]=value.to_s
597
+ end
598
+ result
599
+ end
600
+
601
+ def fb_status_msg(type, message, text)
602
+ if text.blank?
603
+ tag("fb:#{type}", :message => message)
604
+ else
605
+ content_tag("fb:#{type}", content_tag("fb:message", message) + text)
606
+ end
607
+ end
608
+
609
+ def token_tag
610
+ unless protect_against_forgery?
611
+ ''
612
+ else
613
+ tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
614
+ end
615
+ end
616
+ end
617
+ end
618
+ end
619
+
620
+ class Hash
621
+ def transform_keys!(transformation_hash)
622
+ transformation_hash.each_pair{|key, value| transform_key!(key, value)}
623
+ end
624
+
625
+
626
+ def transform_key!(old_key, new_key)
627
+ swapkey!(new_key, old_key)
628
+ end
629
+
630
+ ### This method is lifted from Ruby Facets core
631
+ def swapkey!( newkey, oldkey )
632
+ self[newkey] = self.delete(oldkey) if self.has_key?(oldkey)
633
+ self
634
+ end
635
+
636
+ # We can allow css attributes.
637
+ FB_ALWAYS_VALID_OPTION_KEYS = [:class, :style]
638
+ def assert_valid_keys(*valid_keys)
639
+ unknown_keys = keys - [valid_keys + FB_ALWAYS_VALID_OPTION_KEYS].flatten
640
+ raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
641
+ end
642
+ end