tenderlove-facebooker 1.0.16.20090319151701

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