simianarmy-facebooker 1.0.40

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 (135) hide show
  1. data/.autotest +15 -0
  2. data/CHANGELOG.rdoc +24 -0
  3. data/COPYING.rdoc +19 -0
  4. data/Manifest.txt +133 -0
  5. data/README.rdoc +113 -0
  6. data/Rakefile +85 -0
  7. data/TODO.rdoc +4 -0
  8. data/examples/desktop_login.rb +14 -0
  9. data/facebooker.gemspec +38 -0
  10. data/generators/facebook/facebook_generator.rb +14 -0
  11. data/generators/facebook/templates/config/facebooker.yml +49 -0
  12. data/generators/facebook/templates/public/javascripts/facebooker.js +93 -0
  13. data/generators/facebook_controller/USAGE +33 -0
  14. data/generators/facebook_controller/facebook_controller_generator.rb +40 -0
  15. data/generators/facebook_controller/templates/controller.rb +7 -0
  16. data/generators/facebook_controller/templates/functional_test.rb +11 -0
  17. data/generators/facebook_controller/templates/helper.rb +2 -0
  18. data/generators/facebook_controller/templates/view.fbml.erb +2 -0
  19. data/generators/facebook_controller/templates/view.html.erb +2 -0
  20. data/generators/facebook_publisher/facebook_publisher_generator.rb +14 -0
  21. data/generators/facebook_publisher/templates/create_facebook_templates.rb +15 -0
  22. data/generators/facebook_publisher/templates/publisher.rb +3 -0
  23. data/generators/facebook_scaffold/USAGE +27 -0
  24. data/generators/facebook_scaffold/facebook_scaffold_generator.rb +118 -0
  25. data/generators/facebook_scaffold/templates/controller.rb +93 -0
  26. data/generators/facebook_scaffold/templates/facebook_style.css +2579 -0
  27. data/generators/facebook_scaffold/templates/functional_test.rb +89 -0
  28. data/generators/facebook_scaffold/templates/helper.rb +2 -0
  29. data/generators/facebook_scaffold/templates/layout.fbml.erb +6 -0
  30. data/generators/facebook_scaffold/templates/layout.html.erb +17 -0
  31. data/generators/facebook_scaffold/templates/style.css +74 -0
  32. data/generators/facebook_scaffold/templates/view_edit.fbml.erb +13 -0
  33. data/generators/facebook_scaffold/templates/view_edit.html.erb +18 -0
  34. data/generators/facebook_scaffold/templates/view_index.fbml.erb +24 -0
  35. data/generators/facebook_scaffold/templates/view_index.html.erb +24 -0
  36. data/generators/facebook_scaffold/templates/view_new.fbml.erb +12 -0
  37. data/generators/facebook_scaffold/templates/view_new.html.erb +17 -0
  38. data/generators/facebook_scaffold/templates/view_show.fbml.erb +10 -0
  39. data/generators/facebook_scaffold/templates/view_show.html.erb +10 -0
  40. data/generators/publisher/publisher_generator.rb +14 -0
  41. data/generators/xd_receiver/templates/xd_receiver.html +10 -0
  42. data/generators/xd_receiver/xd_receiver_generator.rb +10 -0
  43. data/init.rb +26 -0
  44. data/install.rb +12 -0
  45. data/lib/facebooker/adapters/adapter_base.rb +91 -0
  46. data/lib/facebooker/adapters/bebo_adapter.rb +77 -0
  47. data/lib/facebooker/adapters/facebook_adapter.rb +52 -0
  48. data/lib/facebooker/admin.rb +42 -0
  49. data/lib/facebooker/batch_request.rb +45 -0
  50. data/lib/facebooker/data.rb +57 -0
  51. data/lib/facebooker/feed.rb +78 -0
  52. data/lib/facebooker/logging.rb +44 -0
  53. data/lib/facebooker/mobile.rb +20 -0
  54. data/lib/facebooker/mock/service.rb +50 -0
  55. data/lib/facebooker/mock/session.rb +18 -0
  56. data/lib/facebooker/model.rb +139 -0
  57. data/lib/facebooker/models/affiliation.rb +10 -0
  58. data/lib/facebooker/models/album.rb +11 -0
  59. data/lib/facebooker/models/applicationproperties.rb +39 -0
  60. data/lib/facebooker/models/applicationrestrictions.rb +10 -0
  61. data/lib/facebooker/models/cookie.rb +10 -0
  62. data/lib/facebooker/models/education_info.rb +11 -0
  63. data/lib/facebooker/models/event.rb +28 -0
  64. data/lib/facebooker/models/friend_list.rb +16 -0
  65. data/lib/facebooker/models/group.rb +36 -0
  66. data/lib/facebooker/models/info_item.rb +10 -0
  67. data/lib/facebooker/models/info_section.rb +10 -0
  68. data/lib/facebooker/models/location.rb +8 -0
  69. data/lib/facebooker/models/notifications.rb +17 -0
  70. data/lib/facebooker/models/page.rb +28 -0
  71. data/lib/facebooker/models/photo.rb +19 -0
  72. data/lib/facebooker/models/tag.rb +12 -0
  73. data/lib/facebooker/models/user.rb +469 -0
  74. data/lib/facebooker/models/video.rb +9 -0
  75. data/lib/facebooker/models/work_info.rb +10 -0
  76. data/lib/facebooker/parser.rb +650 -0
  77. data/lib/facebooker/rails/backwards_compatible_param_checks.rb +31 -0
  78. data/lib/facebooker/rails/controller.rb +356 -0
  79. data/lib/facebooker/rails/cucumber/world.rb +46 -0
  80. data/lib/facebooker/rails/cucumber.rb +28 -0
  81. data/lib/facebooker/rails/extensions/action_controller.rb +48 -0
  82. data/lib/facebooker/rails/extensions/rack_setup.rb +6 -0
  83. data/lib/facebooker/rails/extensions/routing.rb +15 -0
  84. data/lib/facebooker/rails/facebook_form_builder.rb +112 -0
  85. data/lib/facebooker/rails/facebook_pretty_errors.rb +22 -0
  86. data/lib/facebooker/rails/facebook_request_fix.rb +28 -0
  87. data/lib/facebooker/rails/facebook_request_fix_2-3.rb +31 -0
  88. data/lib/facebooker/rails/facebook_session_handling.rb +68 -0
  89. data/lib/facebooker/rails/facebook_url_helper.rb +192 -0
  90. data/lib/facebooker/rails/facebook_url_rewriting.rb +60 -0
  91. data/lib/facebooker/rails/helpers/fb_connect.rb +118 -0
  92. data/lib/facebooker/rails/helpers.rb +804 -0
  93. data/lib/facebooker/rails/integration_session.rb +38 -0
  94. data/lib/facebooker/rails/profile_publisher_extensions.rb +42 -0
  95. data/lib/facebooker/rails/publisher.rb +550 -0
  96. data/lib/facebooker/rails/routing.rb +49 -0
  97. data/lib/facebooker/rails/test_helpers.rb +68 -0
  98. data/lib/facebooker/rails/utilities.rb +22 -0
  99. data/lib/facebooker/server_cache.rb +24 -0
  100. data/lib/facebooker/service.rb +102 -0
  101. data/lib/facebooker/session.rb +635 -0
  102. data/lib/facebooker/version.rb +9 -0
  103. data/lib/facebooker.rb +179 -0
  104. data/lib/net/http_multipart_post.rb +123 -0
  105. data/lib/rack/facebook.rb +82 -0
  106. data/lib/tasks/facebooker.rake +18 -0
  107. data/lib/tasks/tunnel.rake +46 -0
  108. data/rails/init.rb +1 -0
  109. data/setup.rb +1585 -0
  110. data/templates/layout.erb +24 -0
  111. data/test/facebooker/adapters_test.rb +174 -0
  112. data/test/facebooker/admin_test.rb +102 -0
  113. data/test/facebooker/batch_request_test.rb +83 -0
  114. data/test/facebooker/data_test.rb +86 -0
  115. data/test/facebooker/logging_test.rb +43 -0
  116. data/test/facebooker/mobile_test.rb +45 -0
  117. data/test/facebooker/model_test.rb +133 -0
  118. data/test/facebooker/models/event_test.rb +15 -0
  119. data/test/facebooker/models/photo_test.rb +16 -0
  120. data/test/facebooker/models/user_test.rb +361 -0
  121. data/test/facebooker/rails/facebook_request_fix_2-3_test.rb +25 -0
  122. data/test/facebooker/rails/facebook_url_rewriting_test.rb +39 -0
  123. data/test/facebooker/rails/publisher_test.rb +481 -0
  124. data/test/facebooker/rails_integration_test.rb +1434 -0
  125. data/test/facebooker/server_cache_test.rb +44 -0
  126. data/test/facebooker/session_test.rb +652 -0
  127. data/test/facebooker_test.rb +951 -0
  128. data/test/fixtures/multipart_post_body_with_only_parameters.txt +33 -0
  129. data/test/fixtures/multipart_post_body_with_single_file.txt +38 -0
  130. data/test/fixtures/multipart_post_body_with_single_file_that_has_nil_key.txt +38 -0
  131. data/test/net/http_multipart_post_test.rb +52 -0
  132. data/test/rack/facebook_test.rb +61 -0
  133. data/test/rails_test_helper.rb +27 -0
  134. data/test/test_helper.rb +74 -0
  135. metadata +222 -0
@@ -0,0 +1,804 @@
1
+ require 'action_pack'
2
+
3
+ module Facebooker
4
+ module Rails
5
+
6
+ # Facebook specific helpers for creating FBML
7
+ #
8
+ # All helpers that take a user as a parameter will get the Facebook UID from the facebook_id attribute if it exists.
9
+ # It will use to_s if the facebook_id attribute is not present.
10
+ #
11
+ module Helpers
12
+
13
+ include Facebooker::Rails::Helpers::FbConnect
14
+
15
+ # Create an fb:dialog
16
+ # id must be a unique name e.g. "my_dialog"
17
+ # cancel_button is true or false
18
+ def fb_dialog( id, cancel_button, &block )
19
+ content = capture(&block)
20
+ if ignore_binding?
21
+ concat( content_tag("fb:dialog", content, {:id => id, :cancel_button => cancel_button}) )
22
+ else
23
+ concat( content_tag("fb:dialog", content, {:id => id, :cancel_button => cancel_button}), block.binding )
24
+ end
25
+ end
26
+
27
+ def fb_iframe(src, options = {})
28
+ content_tag "fb:iframe", '', options.merge({ :src => src })
29
+ end
30
+
31
+ def fb_swf(src, options = {})
32
+ tag "fb:swf", options.merge(:swfsrc => src)
33
+ end
34
+
35
+ def fb_dialog_title( title )
36
+ content_tag "fb:dialog-title", title
37
+ end
38
+
39
+ def fb_dialog_content( &block )
40
+ content = capture(&block)
41
+ if ignore_binding?
42
+ concat( content_tag("fb:dialog-content", content) )
43
+ else
44
+ concat( content_tag("fb:dialog-content", content), block.binding )
45
+ end
46
+ end
47
+
48
+ def fb_dialog_button( type, value, options={} )
49
+ options.assert_valid_keys FB_DIALOG_BUTTON_VALID_OPTION_KEYS
50
+ options.merge! :type => type, :value => value
51
+ tag "fb:dialog-button", options
52
+ end
53
+
54
+ FB_DIALOG_BUTTON_VALID_OPTION_KEYS = [:close_dialog, :href, :form_id, :clickrewriteurl, :clickrewriteid, :clickrewriteform]
55
+
56
+ def fb_show_feed_dialog(action, user_message = "", prompt = "", callback = nil)
57
+ update_page do |page|
58
+ page.call "Facebook.showFeedDialog",action.template_id,action.data,action.body_general,action.target_ids,callback,prompt,user_message
59
+ end
60
+ end
61
+
62
+
63
+ # Create an fb:request-form without a selector
64
+ #
65
+ # The block passed to this tag is used as the content of the form
66
+ #
67
+ # The message param is the name sent to content_for that specifies the body of the message
68
+ #
69
+ # For example,
70
+ #
71
+ # <% content_for("invite_message") do %>
72
+ # This gets sent in the invite. <%= fb_req_choice("with a button!",new_poke_path) %>
73
+ # <% end %>
74
+ # <% fb_request_form("Poke","invite_message",create_poke_path) do %>
75
+ # Send a poke to: <%= fb_friend_selector %> <br />
76
+ # <%= fb_request_form_submit %>
77
+ # <% end %>
78
+ def fb_request_form(type,message_param,url,options={},&block)
79
+ content = capture(&block)
80
+ message = @template.instance_variable_get("@content_for_#{message_param}")
81
+ if ignore_binding?
82
+ concat(content_tag("fb:request-form", content + token_tag,
83
+ {:action=>url,:method=>"post",:invite=>true,:type=>type,:content=>message}.merge(options)))
84
+ else
85
+ concat(content_tag("fb:request-form", content + token_tag,
86
+ {:action=>url,:method=>"post",:invite=>true,:type=>type,:content=>message}.merge(options)),
87
+ block.binding)
88
+ end
89
+ end
90
+
91
+ # Create a submit button for an <fb:request-form>
92
+ # If the request is for an individual user you can optionally
93
+ # Provide the user and a label for the request button.
94
+ # For example
95
+ # <% content_for("invite_user") do %>
96
+ # This gets sent in the invite. <%= fb_req_choice("Come join us!",new_invite_path) %>
97
+ # <% end %>
98
+ # <% fb_request_form("Invite","invite_user",create_invite_path) do %>
99
+ # Invite <%= fb_name(@facebook_user.friends.first.id)%> to the party <br />
100
+ # <%= fb_request_form_submit(:uid => @facebook_user.friends.first.id, :label => "Invite %n") %>
101
+ # <% end %>
102
+ # <em>See:</em> http://wiki.developers.facebook.com/index.php/Fb:request-form-submit for options
103
+ def fb_request_form_submit(options={})
104
+ tag("fb:request-form-submit",stringify_vals(options))
105
+ end
106
+
107
+
108
+ # Create an fb:request-form with an fb_multi_friend_selector inside
109
+ #
110
+ # The content of the block are used as the message on the form,
111
+ #
112
+ # For example:
113
+ # <% fb_multi_friend_request("Poke","Choose some friends to Poke",create_poke_path) do %>
114
+ # If you select some friends, they will see this message.
115
+ # <%= fb_req_choice("They will get this button, too",new_poke_path) %>
116
+ # <% end %>
117
+ def fb_multi_friend_request(type,friend_selector_message,url,&block)
118
+ content = capture(&block)
119
+ if ignore_binding?
120
+ concat(content_tag("fb:request-form",
121
+ fb_multi_friend_selector(friend_selector_message) + token_tag,
122
+ {:action=>url,:method=>"post",:invite=>true,:type=>type,:content=>content}
123
+ ))
124
+ else
125
+ concat(content_tag("fb:request-form",
126
+ fb_multi_friend_selector(friend_selector_message) + token_tag,
127
+ {:action=>url,:method=>"post",:invite=>true,:type=>type,:content=>content}
128
+ ),
129
+ block.binding)
130
+ end
131
+ end
132
+
133
+ # Render an <fb:friend-selector> element
134
+ # <em>See:</em> http://wiki.developers.facebook.com/index.php/Fb:friend-selector for options
135
+ #
136
+ def fb_friend_selector(options={})
137
+ tag("fb:friend-selector",stringify_vals(options))
138
+ end
139
+
140
+ # Render an <fb:multi-friend-input> element
141
+ # <em> See: </em> http://wiki.developers.facebook.com/index.php/Fb:multi-friend-input for options
142
+ def fb_multi_friend_input(options={})
143
+ tag "fb:multi-friend-input",stringify_vals(options)
144
+ end
145
+
146
+ # Render an <fb:multi-friend-selector> with the passed in welcome message
147
+ # Full version shows all profile pics for friends.
148
+ # <em> See: </em> http://wiki.developers.facebook.com/index.php/Fb:multi-friend-selector for options
149
+ # <em> Note: </em> I don't think the block is used here.
150
+ def fb_multi_friend_selector(message,options={},&block)
151
+ options = options.dup
152
+ tag("fb:multi-friend-selector",stringify_vals({:showborder=>false,:actiontext=>message,:max=>20}.merge(options)))
153
+ end
154
+
155
+ # Render a condensed <fb:multi-friend-selector> with the passed in welcome message
156
+ # Condensed version show checkboxes for each friend.
157
+ # <em> See: </em> http://wiki.developers.facebook.com/index.php/Fb:multi-friend-selector_%28condensed%29 for options
158
+ # <em> Note: </em> I don't think the block is used here.
159
+ def fb_multi_friend_selector_condensed(options={},&block)
160
+ options = options.dup
161
+ tag("fb:multi-friend-selector",stringify_vals(options.merge(:condensed=>true)))
162
+ end
163
+
164
+ # Render a button in a request using the <fb:req-choice> tag
165
+ # url must be an absolute url
166
+ # This should be used inside the block of an fb_multi_friend_request
167
+ def fb_req_choice(label,url)
168
+ tag "fb:req-choice",:label=>label,:url=>url
169
+ end
170
+
171
+ # Create a facebook form using <fb:editor>
172
+ #
173
+ # It yields a form builder that will convert the standard rails form helpers
174
+ # into the facebook specific version.
175
+ #
176
+ # Example:
177
+ # <% facebook_form_for(:poke,@poke,:url => create_poke_path) do |f| %>
178
+ # <%= f.text_field :message, :label=>"message" %>
179
+ # <%= f.buttons "Save Poke" %>
180
+ # <% end %>
181
+ #
182
+ # will generate
183
+ #
184
+ # <fb:editor action="/pokes/create">
185
+ # <fb:editor-text name="poke[message]" id="poke_message" value="" label="message" />
186
+ # <fb:editor-buttonset>
187
+ # <fb:editor-button label="Save Poke"
188
+ # </fb:editor-buttonset>
189
+ # </fb:editor>
190
+ def facebook_form_for( record_or_name_or_array,*args, &proc)
191
+
192
+ raise ArgumentError, "Missing block" unless block_given?
193
+ options = args.last.is_a?(Hash) ? args.pop : {}
194
+
195
+ case record_or_name_or_array
196
+ when String, Symbol
197
+ object_name = record_or_name_or_array
198
+ when Array
199
+ object = record_or_name_or_array.last
200
+ object_name = ActionController::RecordIdentifier.singular_class_name(object)
201
+ apply_form_for_options!(record_or_name_or_array, options)
202
+ args.unshift object
203
+ else
204
+ object = record_or_name_or_array
205
+ object_name = ActionController::RecordIdentifier.singular_class_name(object)
206
+ apply_form_for_options!([object], options)
207
+ args.unshift object
208
+ end
209
+ method = (options[:html]||{})[:method]
210
+ options[:builder] ||= Facebooker::Rails::FacebookFormBuilder
211
+ editor_options={}
212
+
213
+ action=options.delete(:url)
214
+ editor_options[:action]= action unless action.blank?
215
+ width=options.delete(:width)
216
+ editor_options[:width]=width unless width.blank?
217
+ width=options.delete(:labelwidth)
218
+ editor_options[:labelwidth]=width unless width.blank?
219
+
220
+ if ignore_binding?
221
+ concat(tag("fb:editor",editor_options,true))
222
+ concat(tag(:input,{:type=>"hidden",:name=>:_method, :value=>method},false)) unless method.blank?
223
+ concat(token_tag)
224
+ fields_for( object_name,*(args << options), &proc)
225
+ concat("</fb:editor>")
226
+ else
227
+ concat(tag("fb:editor",editor_options,true) , proc.binding)
228
+ concat(tag(:input,{:type=>"hidden",:name=>:_method, :value=>method},false), proc.binding) unless method.blank?
229
+ concat(token_tag, proc.binding)
230
+ fields_for( object_name,*(args << options), &proc)
231
+ concat("</fb:editor>",proc.binding)
232
+ end
233
+ end
234
+
235
+ # Render an fb:name tag for the given user
236
+ # This renders the name of the user specified. You can use this tag as both subject and object of
237
+ # a sentence. <em> See </em> http://wiki.developers.facebook.com/index.php/Fb:name for full description.
238
+ # Use this tag on FBML pages instead of retrieving the user's info and rendering the name explicitly.
239
+ #
240
+ def fb_name(user, options={})
241
+ options = options.dup
242
+ options.transform_keys!(FB_NAME_OPTION_KEYS_TO_TRANSFORM)
243
+ options.assert_valid_keys(FB_NAME_VALID_OPTION_KEYS)
244
+ options.merge!(:uid => cast_to_facebook_id(user))
245
+ content_tag("fb:name",nil, stringify_vals(options))
246
+ end
247
+
248
+ FB_NAME_OPTION_KEYS_TO_TRANSFORM = {:first_name_only => :firstnameonly,
249
+ :last_name_only => :lastnameonly,
250
+ :show_network => :shownetwork,
251
+ :use_you => :useyou,
252
+ :if_cant_see => :ifcantsee,
253
+ :subject_id => :subjectid}
254
+ FB_NAME_VALID_OPTION_KEYS = [:firstnameonly, :linked, :lastnameonly, :possessive, :reflexive,
255
+ :shownetwork, :useyou, :ifcantsee, :capitalize, :subjectid]
256
+
257
+ # Render an <fb:pronoun> tag for the specified user
258
+ # Options give flexibility for placing in any part of a sentence.
259
+ # <em> See </em> http://wiki.developers.facebook.com/index.php/Fb:pronoun for complete list of options.
260
+ #
261
+ def fb_pronoun(user, options={})
262
+ options = options.dup
263
+ options.transform_keys!(FB_PRONOUN_OPTION_KEYS_TO_TRANSFORM)
264
+ options.assert_valid_keys(FB_PRONOUN_VALID_OPTION_KEYS)
265
+ options.merge!(:uid => cast_to_facebook_id(user))
266
+ content_tag("fb:pronoun",nil, stringify_vals(options))
267
+ end
268
+
269
+ FB_PRONOUN_OPTION_KEYS_TO_TRANSFORM = {:use_you => :useyou, :use_they => :usethey}
270
+ FB_PRONOUN_VALID_OPTION_KEYS = [:useyou, :possessive, :reflexive, :objective,
271
+ :usethey, :capitalize]
272
+
273
+ # Render an fb:ref tag.
274
+ # Options must contain either url or handle.
275
+ # * <em> url </em> The URL from which to fetch the FBML. You may need to call fbml.refreshRefUrl to refresh cache
276
+ # * <em> handle </em> The string previously set by fbml.setRefHandle that identifies the FBML
277
+ # <em> See </em> http://wiki.developers.facebook.com/index.php/Fb:ref for complete description
278
+ def fb_ref(options)
279
+ options.assert_valid_keys(FB_REF_VALID_OPTION_KEYS)
280
+ validate_fb_ref_has_either_url_or_handle(options)
281
+ validate_fb_ref_does_not_have_both_url_and_handle(options)
282
+ tag("fb:ref", stringify_vals(options))
283
+ end
284
+
285
+ def validate_fb_ref_has_either_url_or_handle(options)
286
+ unless options.has_key?(:url) || options.has_key?(:handle)
287
+ raise ArgumentError, "fb_ref requires :url or :handle"
288
+ end
289
+ end
290
+
291
+ def validate_fb_ref_does_not_have_both_url_and_handle(options)
292
+ if options.has_key?(:url) && options.has_key?(:handle)
293
+ raise ArgumentError, "fb_ref may not have both :url and :handle"
294
+ end
295
+ end
296
+
297
+ FB_REF_VALID_OPTION_KEYS = [:url, :handle]
298
+
299
+ # Render an <fb:profile-pic> for the specified user.
300
+ #
301
+ # You can optionally specify the size using the :size=> option. Valid
302
+ # sizes are :thumb, :small, :normal and :square. Or, you can specify
303
+ # width and height settings instead, just like an img tag.
304
+ #
305
+ # You can optionally specify whether or not to include the facebook icon
306
+ # overlay using the :facebook_logo => true option. Default is false.
307
+ #
308
+ def fb_profile_pic(user, options={})
309
+ options = options.dup
310
+ validate_fb_profile_pic_size(options)
311
+ options.transform_keys!(FB_PROFILE_PIC_OPTION_KEYS_TO_TRANSFORM)
312
+ options.assert_valid_keys(FB_PROFILE_PIC_VALID_OPTION_KEYS)
313
+ options.merge!(:uid => cast_to_facebook_id(user))
314
+ content_tag("fb:profile-pic", nil,stringify_vals(options))
315
+ end
316
+
317
+ FB_PROFILE_PIC_OPTION_KEYS_TO_TRANSFORM = {:facebook_logo => 'facebook-logo'}
318
+ FB_PROFILE_PIC_VALID_OPTION_KEYS = [:size, :linked, 'facebook-logo', :width, :height]
319
+
320
+
321
+ # Render an fb:photo tag.
322
+ # photo is either a Facebooker::Photo or an id of a Facebook photo or an object that responds to photo_id.
323
+ # <em> See: </em> http://wiki.developers.facebook.com/index.php/Fb:photo for complete list of options.
324
+ def fb_photo(photo, options={})
325
+ options = options.dup
326
+ options.assert_valid_keys(FB_PHOTO_VALID_OPTION_KEYS)
327
+ options.merge!(:pid => cast_to_photo_id(photo))
328
+ validate_fb_photo_size(options)
329
+ validate_fb_photo_align_value(options)
330
+ content_tag("fb:photo",nil, stringify_vals(options))
331
+ end
332
+
333
+ FB_PHOTO_VALID_OPTION_KEYS = [:uid, :size, :align]
334
+
335
+ def cast_to_photo_id(object)
336
+ object.respond_to?(:photo_id) ? object.photo_id : object
337
+ end
338
+
339
+ VALID_FB_SHARED_PHOTO_SIZES = [:thumb, :small, :normal, :square]
340
+ VALID_FB_PHOTO_SIZES = VALID_FB_SHARED_PHOTO_SIZES
341
+ VALID_FB_PROFILE_PIC_SIZES = VALID_FB_SHARED_PHOTO_SIZES
342
+ VALID_PERMISSIONS=[:email, :offline_access, :status_update, :photo_upload, :create_listing, :create_event, :rsvp_event, :sms, :video_upload,
343
+ :publish_stream, :read_stream]
344
+
345
+ # Render an fb:tabs tag containing some number of fb:tab_item tags.
346
+ # Example:
347
+ # <% fb_tabs do %>
348
+ # <%= fb_tab_item("Home", "home") %>
349
+ # <%= fb_tab_item("Office", "office") %>
350
+ # <% end %>
351
+ def fb_tabs(&block)
352
+ content = capture(&block)
353
+ if ignore_binding?
354
+ concat(content_tag("fb:tabs", content))
355
+ else
356
+ concat(content_tag("fb:tabs", content), block.binding)
357
+ end
358
+ end
359
+
360
+ # Render an fb:tab_item tag.
361
+ # Use this in conjunction with fb_tabs
362
+ # Options can contains :selected => true to indicate that a tab is the current tab.
363
+ # <em> See: </em> http://wiki.developers.facebook.com/index.php/Fb:tab-item for complete list of options
364
+ def fb_tab_item(title, url, options={})
365
+ options= options.dup
366
+ options.assert_valid_keys(FB_TAB_ITEM_VALID_OPTION_KEYS)
367
+ options.merge!(:title => title, :href => url)
368
+ validate_fb_tab_item_align_value(options)
369
+ tag("fb:tab-item", stringify_vals(options))
370
+ end
371
+
372
+ FB_TAB_ITEM_VALID_OPTION_KEYS = [:align, :selected]
373
+
374
+ def validate_fb_tab_item_align_value(options)
375
+ if options.has_key?(:align) && !VALID_FB_TAB_ITEM_ALIGN_VALUES.include?(options[:align].to_sym)
376
+ raise(ArgumentError, "Unknown value for align: #{options[:align]}")
377
+ end
378
+ end
379
+
380
+ def validate_fb_photo_align_value(options)
381
+ if options.has_key?(:align) && !VALID_FB_PHOTO_ALIGN_VALUES.include?(options[:align].to_sym)
382
+ raise(ArgumentError, "Unknown value for align: #{options[:align]}")
383
+ end
384
+ end
385
+
386
+ VALID_FB_SHARED_ALIGN_VALUES = [:left, :right]
387
+ VALID_FB_PHOTO_ALIGN_VALUES = VALID_FB_SHARED_ALIGN_VALUES
388
+ VALID_FB_TAB_ITEM_ALIGN_VALUES = VALID_FB_SHARED_ALIGN_VALUES
389
+
390
+
391
+ # Create a Facebook wall. It can contain fb_wall_posts
392
+ #
393
+ # For Example:
394
+ # <% fb_wall do %>
395
+ # <%= fb_wall_post(@user,"This is my message") %>
396
+ # <%= fb_wall_post(@otheruser,"This is another message") %>
397
+ # <% end %>
398
+ def fb_wall(&proc)
399
+ content = capture(&proc)
400
+ if ignore_binding?
401
+ concat(content_tag("fb:wall",content,{}))
402
+ else
403
+ concat(content_tag("fb:wall",content,{}),proc.binding)
404
+ end
405
+ end
406
+
407
+ # Render an <fb:wallpost> tag
408
+ # TODO: Optionally takes a time parameter t = int The current time, which is displayed in epoch seconds.
409
+ def fb_wallpost(user,message)
410
+ content_tag("fb:wallpost",message,:uid=>cast_to_facebook_id(user))
411
+ end
412
+ alias_method :fb_wall_post, :fb_wallpost
413
+
414
+ # Render an <fb:error> tag
415
+ # If message and text are present then this will render fb:error and fb:message tag
416
+ # TODO: Optionally takes a decoration tag with value of 'no_padding' or 'shorten'
417
+ def fb_error(message, text=nil)
418
+ fb_status_msg("error", message, text)
419
+ end
420
+
421
+ # Render an <fb:explanation> tag
422
+ # If message and text are present then this will render fb:error and fb:message tag
423
+ # TODO: Optionally takes a decoration tag with value of 'no_padding' or 'shorten'
424
+ def fb_explanation(message, text=nil)
425
+ fb_status_msg("explanation", message, text)
426
+ end
427
+
428
+ # Render an <fb:success> tag
429
+ # If message and text are present then this will render fb:error and fb:message tag
430
+ # TODO: Optionally takes a decoration tag with value of 'no_padding' or 'shorten'
431
+ def fb_success(message, text=nil)
432
+ fb_status_msg("success", message, text)
433
+ end
434
+
435
+ # Render flash values as <fb:message> and <fb:error> tags
436
+ #
437
+ # values in flash[:notice] will be rendered as an <fb:message>
438
+ #
439
+ # values in flash[:error] will be rendered as an <fb:error>
440
+ # TODO: Allow flash[:info] to render fb_explanation
441
+ def facebook_messages
442
+ message=""
443
+ unless flash[:notice].blank?
444
+ message += fb_success(flash[:notice])
445
+ end
446
+ unless flash[:error].blank?
447
+ message += fb_error(flash[:error])
448
+ end
449
+ message
450
+ end
451
+
452
+ # Create a dashboard. It can contain fb_action, fb_help, and fb_create_button
453
+ #
454
+ # For Example:
455
+ # <% fb_dashboard do %>
456
+ # <%= APP_NAME %>
457
+ # <%= fb_action 'My Matches', search_path %>
458
+ # <%= fb_help 'Feedback', "http://www.facebook.com/apps/application.php?id=6236036681" %>
459
+ # <%= fb_create_button 'Invite Friends', main_path %>
460
+ # <% end %>
461
+ def fb_dashboard(&proc)
462
+ if block_given?
463
+ content = capture(&proc)
464
+ if ignore_binding?
465
+ concat(content_tag("fb:dashboard",content,{}))
466
+ else
467
+ concat(content_tag("fb:dashboard",content,{}),proc.binding)
468
+ end
469
+ else
470
+ content_tag("fb:dashboard",content,{})
471
+ end
472
+ end
473
+
474
+ # Content for the wide profile box goes in this tag
475
+ def fb_wide(&proc)
476
+ content = capture(&proc)
477
+ if ignore_binding?
478
+ concat(content_tag("fb:wide", content, {}))
479
+ else
480
+ concat(content_tag("fb:wide", content, {}), proc.binding)
481
+ end
482
+ end
483
+
484
+ # Content for the narrow profile box goes in this tag
485
+ def fb_narrow(&proc)
486
+ content = capture(&proc)
487
+ if ignore_binding?
488
+ concat(content_tag("fb:narrow", content, {}))
489
+ else
490
+ concat(content_tag("fb:narrow", content, {}), proc.binding)
491
+ end
492
+ end
493
+
494
+ # Renders an action using the <fb:action> tag
495
+ def fb_action(name, url)
496
+ "<fb:action href=\"#{url_for(url)}\">#{name}</fb:action>"
497
+ end
498
+
499
+ # Render a <fb:help> tag
500
+ # For use inside <fb:dashboard>
501
+ def fb_help(name, url)
502
+ "<fb:help href=\"#{url_for(url)}\">#{name}</fb:help>"
503
+ end
504
+
505
+ # Render a <fb:create-button> tag
506
+ # For use inside <fb:dashboard>
507
+ def fb_create_button(name, url)
508
+ "<fb:create-button href=\"#{url_for(url)}\">#{name}</fb:create-button>"
509
+ end
510
+
511
+ # Create a comment area
512
+ # All the data for this content area is stored on the facebook servers.
513
+ # <em>See:</em> http://wiki.developers.facebook.com/index.php/Fb:comments for full details
514
+ def fb_comments(xid,canpost=true,candelete=false,numposts=5,options={})
515
+ options = options.dup
516
+ title = (title = options.delete(:title)) ? fb_title(title) : nil
517
+ content_tag "fb:comments",title,stringify_vals(options.merge(:xid=>xid,:canpost=>canpost.to_s,:candelete=>candelete.to_s,:numposts=>numposts))
518
+ end
519
+
520
+ # Adds a title to the title bar
521
+ #
522
+ # Facebook | App Name | This is the canvas page window title
523
+ #
524
+ # +title+: This is the canvas page window
525
+ def fb_title(title)
526
+ "<fb:title>#{title}</fb:title>"
527
+ end
528
+
529
+ # Create a Google Analytics tag
530
+ #
531
+ # +uacct+: Your Urchin/Google Analytics account ID.
532
+ def fb_google_analytics(uacct, options={})
533
+ options = options.dup
534
+ tag "fb:google-analytics", stringify_vals(options.merge(:uacct => uacct))
535
+ end
536
+
537
+ # Render if-is-app-user tag
538
+ # This tag renders the enclosing content only if the user specified has accepted the terms of service for the application.
539
+ # Use fb_if_user_has_added_app to determine wether the user has added the app.
540
+ # Example:
541
+ # <% fb_if_is_app_user(@facebook_user) do %>
542
+ # Thanks for accepting our terms of service!
543
+ # <% fb_else do %>
544
+ # Hey you haven't agreed to our terms. <%= link_to("Please accept our terms of service.", :action => "terms_of_service") %>
545
+ # <% end %>
546
+ #<% end %>
547
+ def fb_if_is_app_user(user=nil,options={},&proc)
548
+ content = capture(&proc)
549
+ options = options.dup
550
+ options.merge!(:uid=>cast_to_facebook_id(user)) if user
551
+ if ignore_binding?
552
+ concat(content_tag("fb:if-is-app-user",content,stringify_vals(options)))
553
+ else
554
+ concat(content_tag("fb:if-is-app-user",content,stringify_vals(options)),proc.binding)
555
+ end
556
+ end
557
+
558
+ # Render if-user-has-added-app tag
559
+ # This tag renders the enclosing content only if the user specified has installed the application
560
+ #
561
+ # Example:
562
+ # <% fb_if_user_has_added_app(@facebook_user) do %>
563
+ # Hey you are an app user!
564
+ # <% fb_else do %>
565
+ # Hey you aren't an app user. <%= link_to("Add App and see the other side.", :action => "added_app") %>
566
+ # <% end %>
567
+ #<% end %>
568
+ def fb_if_user_has_added_app(user,options={},&proc)
569
+ content = capture(&proc)
570
+ options = options.dup
571
+ if ignore_binding?
572
+ concat(content_tag("fb:if-user-has-added-app", content, stringify_vals(options.merge(:uid=>cast_to_facebook_id(user)))))
573
+ else
574
+ concat(content_tag("fb:if-user-has-added-app", content, stringify_vals(options.merge(:uid=>cast_to_facebook_id(user)))),proc.binding)
575
+ end
576
+ end
577
+
578
+ # Render fb:if-is-user tag
579
+ # This tag only renders enclosing content if the user is one of those specified
580
+ # user can be a single user or an Array of users
581
+ # Example:
582
+ # <% fb_if_is_user(@check_user) do %>
583
+ # <%= fb_name(@facebook_user) %> are one of the users. <%= link_to("Check the other side", :action => "friend") %>
584
+ # <% fb_else do %>
585
+ # <%= fb_name(@facebook_user) %> are not one of the users <%= fb_name(@check_user) %>
586
+ # <%= link_to("Check the other side", :action => "you") %>
587
+ # <% end %>
588
+ # <% end %>
589
+ def fb_if_is_user(user,&proc)
590
+ content = capture(&proc)
591
+ user = [user] unless user.is_a? Array
592
+ user_list=user.map{|u| cast_to_facebook_id(u)}.join(",")
593
+ if ignore_binding?
594
+ concat(content_tag("fb:if-is-user",content,{:uid=>user_list}))
595
+ else
596
+ concat(content_tag("fb:if-is-user",content,{:uid=>user_list}),proc.binding)
597
+ end
598
+ end
599
+
600
+ # Render fb:else tag
601
+ # 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
602
+ def fb_else(&proc)
603
+ content = capture(&proc)
604
+ if ignore_binding?
605
+ concat(content_tag("fb:else",content))
606
+ else
607
+ concat(content_tag("fb:else",content),proc.binding)
608
+ end
609
+ end
610
+
611
+ #
612
+ # Return the URL for the about page of the application
613
+ def fb_about_url
614
+ "http://#{Facebooker.www_server_base_url}/apps/application.php?api_key=#{Facebooker.api_key}"
615
+ end
616
+
617
+ #
618
+ # Embed a discussion board named xid on the current page
619
+ # <em>See</em http://wiki.developers.facebook.com/index.php/Fb:board for more details
620
+ # Options are:
621
+ # * canpost
622
+ # * candelete
623
+ # * canmark
624
+ # * cancreatet
625
+ # * numtopics
626
+ # * callbackurl
627
+ # * returnurl
628
+ #
629
+ def fb_board(xid,options={})
630
+ options = options.dup
631
+ title = (title = options.delete(:title)) ? fb_title(title) : nil
632
+ content_tag("fb:board", title, stringify_vals(options.merge(:xid=>xid)))
633
+ end
634
+
635
+ # Renders an 'Add to Profile' button
636
+ # The button allows a user to add condensed profile box to the main profile
637
+ def fb_add_profile_section
638
+ tag "fb:add-section-button",:section=>"profile"
639
+ end
640
+
641
+ # Renders an 'Add to Info' button
642
+ # The button allows a user to add an application info section to her Info tab
643
+ def fb_add_info_section
644
+ tag "fb:add-section-button",:section=>"info"
645
+ end
646
+
647
+ # Renders a link that, when clicked, initiates a dialog requesting the specified extended permission from the user.
648
+ #
649
+ # You can prompt a user with the following permissions:
650
+ # * email
651
+ # * read_stream
652
+ # * publish_stream
653
+ # * offline_access
654
+ # * status_update
655
+ # * photo_upload
656
+ # * create_event
657
+ # * rsvp_event
658
+ # * sms
659
+ # * video_upload
660
+ # * create_note
661
+ # * share_item
662
+ # Example:
663
+ # <%= fb_prompt_permission('email', "Would you like to receive email from our application?" ) %>
664
+ #
665
+ # See http://wiki.developers.facebook.com/index.php/Fb:prompt-permission for
666
+ # more details. Correct as of 7th June 2009.
667
+ #
668
+ def fb_prompt_permission(permission,message,callback=nil)
669
+ raise(ArgumentError, "Unknown value for permission: #{permission}") unless VALID_PERMISSIONS.include?(permission.to_sym)
670
+ args={:perms=>permission}
671
+ args[:next_fbjs]=callback unless callback.nil?
672
+ content_tag("fb:prompt-permission",message,args)
673
+ end
674
+
675
+ # Renders a link to prompt for multiple permissions at once.
676
+ #
677
+ # Example:
678
+ # <%= fb_prompt_permissions(['email','offline_access','status_update'], 'Would you like to grant some permissions?')
679
+ def fb_prompt_permissions(permissions,message,callback=nil)
680
+ permissions.each do |p|
681
+ raise(ArgumentError, "Unknown value for permission: #{p}") unless VALID_PERMISSIONS.include?(p.to_sym)
682
+ end
683
+ args={:perms=>permissions*','}
684
+ args[:next_fbjs]=callback unless callback.nil?
685
+ content_tag("fb:prompt-permission",message,args)
686
+ end
687
+
688
+ # Renders an <fb:eventlink /> tag that displays the event name and links to the event's page.
689
+ def fb_eventlink(eid)
690
+ content_tag "fb:eventlink",nil,:eid=>eid
691
+ end
692
+
693
+ # Renders an <fb:grouplink /> tag that displays the group name and links to the group's page.
694
+ def fb_grouplink(gid)
695
+ content_tag "fb:grouplink",nil,:gid=>gid
696
+ end
697
+
698
+ # Returns the status of the user
699
+ def fb_user_status(user,linked=true)
700
+ content_tag "fb:user-status",nil,stringify_vals(:uid=>cast_to_facebook_id(user), :linked=>linked)
701
+ end
702
+
703
+ # Renders a standard 'Share' button for the specified URL.
704
+ def fb_share_button(url)
705
+ content_tag "fb:share-button",nil,:class=>"url",:href=>url
706
+ end
707
+
708
+ # Renders the FBML on a Facebook server inside an iframe.
709
+ #
710
+ # Meant to be used for a Facebook Connect site or an iframe application
711
+ def fb_serverfbml(options={},&proc)
712
+ inner = capture(&proc)
713
+ concat(content_tag("fb:serverfbml",inner,options),&proc.binding)
714
+ end
715
+
716
+ def fb_container(options={},&proc)
717
+ inner = capture(&proc)
718
+ concat(content_tag("fb:container",inner,options),&proc.binding)
719
+ end
720
+
721
+ # Renders an fb:time element
722
+ #
723
+ # Example:
724
+ # <%= fb_time(Time.now, :tz => 'America/New York', :preposition => true) %>
725
+ #
726
+ # See http://wiki.developers.facebook.com/index.php/Fb:time for
727
+ # more details
728
+ def fb_time(time, options={})
729
+ tag "fb:time",stringify_vals({:t => time.to_i}.merge(options))
730
+ end
731
+
732
+ protected
733
+
734
+ def cast_to_facebook_id(object)
735
+ Facebooker::User.cast_to_facebook_id(object)
736
+ end
737
+
738
+ def validate_fb_profile_pic_size(options)
739
+ if options.has_key?(:size) && !VALID_FB_PROFILE_PIC_SIZES.include?(options[:size].to_sym)
740
+ raise(ArgumentError, "Unknown value for size: #{options[:size]}")
741
+ end
742
+ end
743
+
744
+ def validate_fb_photo_size(options)
745
+ if options.has_key?(:size) && !VALID_FB_PHOTO_SIZES.include?(options[:size].to_sym)
746
+ raise(ArgumentError, "Unknown value for size: #{options[:size]}")
747
+ end
748
+ end
749
+
750
+ private
751
+ def stringify_vals(hash)
752
+ result={}
753
+ hash.each do |key,value|
754
+ result[key]=value.to_s
755
+ end
756
+ result
757
+ end
758
+
759
+ def fb_status_msg(type, message, text)
760
+ if text.blank?
761
+ tag("fb:#{type}", :message => message)
762
+ else
763
+ content_tag("fb:#{type}", content_tag("fb:message", message) + text)
764
+ end
765
+ end
766
+
767
+ def token_tag
768
+ unless protect_against_forgery?
769
+ ''
770
+ else
771
+ tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
772
+ end
773
+ end
774
+
775
+ def ignore_binding?
776
+ ActionPack::VERSION::MAJOR >= 2 && ActionPack::VERSION::MINOR >= 2
777
+ end
778
+ end
779
+ end
780
+ end
781
+
782
+ class Hash
783
+ def transform_keys!(transformation_hash)
784
+ transformation_hash.each_pair{|key, value| transform_key!(key, value)}
785
+ end
786
+
787
+
788
+ def transform_key!(old_key, new_key)
789
+ swapkey!(new_key, old_key)
790
+ end
791
+
792
+ ### This method is lifted from Ruby Facets core
793
+ def swapkey!( newkey, oldkey )
794
+ self[newkey] = self.delete(oldkey) if self.has_key?(oldkey)
795
+ self
796
+ end
797
+
798
+ # We can allow css attributes.
799
+ FB_ALWAYS_VALID_OPTION_KEYS = [:class, :style]
800
+ def assert_valid_keys(*valid_keys)
801
+ unknown_keys = keys - [valid_keys + FB_ALWAYS_VALID_OPTION_KEYS].flatten
802
+ raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
803
+ end
804
+ end