corey-facebooker 1.0.28.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. data/CHANGELOG.rdoc +17 -0
  2. data/COPYING.rdoc +19 -0
  3. data/README.rdoc +104 -0
  4. data/Rakefile +86 -0
  5. data/TODO.rdoc +4 -0
  6. data/generators/facebook/facebook_generator.rb +14 -0
  7. data/generators/facebook/templates/config/facebooker.yml +49 -0
  8. data/generators/facebook/templates/public/javascripts/facebooker.js +99 -0
  9. data/generators/facebook_controller/USAGE +33 -0
  10. data/generators/facebook_controller/facebook_controller_generator.rb +40 -0
  11. data/generators/facebook_controller/templates/controller.rb +7 -0
  12. data/generators/facebook_controller/templates/functional_test.rb +11 -0
  13. data/generators/facebook_controller/templates/helper.rb +2 -0
  14. data/generators/facebook_controller/templates/view.fbml.erb +2 -0
  15. data/generators/facebook_controller/templates/view.html.erb +2 -0
  16. data/generators/facebook_publisher/facebook_publisher_generator.rb +14 -0
  17. data/generators/facebook_publisher/templates/create_facebook_templates.rb +15 -0
  18. data/generators/facebook_publisher/templates/publisher.rb +3 -0
  19. data/generators/facebook_scaffold/USAGE +27 -0
  20. data/generators/facebook_scaffold/facebook_scaffold_generator.rb +118 -0
  21. data/generators/facebook_scaffold/templates/controller.rb +93 -0
  22. data/generators/facebook_scaffold/templates/facebook_style.css +2579 -0
  23. data/generators/facebook_scaffold/templates/functional_test.rb +89 -0
  24. data/generators/facebook_scaffold/templates/helper.rb +2 -0
  25. data/generators/facebook_scaffold/templates/layout.fbml.erb +6 -0
  26. data/generators/facebook_scaffold/templates/layout.html.erb +17 -0
  27. data/generators/facebook_scaffold/templates/style.css +74 -0
  28. data/generators/facebook_scaffold/templates/view_edit.fbml.erb +13 -0
  29. data/generators/facebook_scaffold/templates/view_edit.html.erb +18 -0
  30. data/generators/facebook_scaffold/templates/view_index.fbml.erb +24 -0
  31. data/generators/facebook_scaffold/templates/view_index.html.erb +24 -0
  32. data/generators/facebook_scaffold/templates/view_new.fbml.erb +12 -0
  33. data/generators/facebook_scaffold/templates/view_new.html.erb +17 -0
  34. data/generators/facebook_scaffold/templates/view_show.fbml.erb +10 -0
  35. data/generators/facebook_scaffold/templates/view_show.html.erb +10 -0
  36. data/generators/publisher/publisher_generator.rb +14 -0
  37. data/generators/xd_receiver/templates/xd_receiver.html +10 -0
  38. data/generators/xd_receiver/xd_receiver_generator.rb +9 -0
  39. data/init.rb +23 -0
  40. data/install.rb +12 -0
  41. data/lib/facebooker.rb +181 -0
  42. data/lib/facebooker/adapters/adapter_base.rb +90 -0
  43. data/lib/facebooker/adapters/bebo_adapter.rb +75 -0
  44. data/lib/facebooker/adapters/facebook_adapter.rb +52 -0
  45. data/lib/facebooker/admin.rb +42 -0
  46. data/lib/facebooker/batch_request.rb +44 -0
  47. data/lib/facebooker/data.rb +57 -0
  48. data/lib/facebooker/feed.rb +78 -0
  49. data/lib/facebooker/logging.rb +51 -0
  50. data/lib/facebooker/mobile.rb +20 -0
  51. data/lib/facebooker/mock/service.rb +50 -0
  52. data/lib/facebooker/mock/session.rb +18 -0
  53. data/lib/facebooker/model.rb +137 -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/applicationrestrictions.rb +10 -0
  58. data/lib/facebooker/models/cookie.rb +10 -0
  59. data/lib/facebooker/models/education_info.rb +11 -0
  60. data/lib/facebooker/models/event.rb +28 -0
  61. data/lib/facebooker/models/friend_list.rb +16 -0
  62. data/lib/facebooker/models/group.rb +36 -0
  63. data/lib/facebooker/models/info_item.rb +10 -0
  64. data/lib/facebooker/models/info_section.rb +10 -0
  65. data/lib/facebooker/models/location.rb +8 -0
  66. data/lib/facebooker/models/notifications.rb +17 -0
  67. data/lib/facebooker/models/page.rb +27 -0
  68. data/lib/facebooker/models/photo.rb +12 -0
  69. data/lib/facebooker/models/tag.rb +12 -0
  70. data/lib/facebooker/models/user.rb +471 -0
  71. data/lib/facebooker/models/video.rb +9 -0
  72. data/lib/facebooker/models/work_info.rb +9 -0
  73. data/lib/facebooker/parser.rb +610 -0
  74. data/lib/facebooker/rails/controller.rb +307 -0
  75. data/lib/facebooker/rails/cucumber.rb +28 -0
  76. data/lib/facebooker/rails/cucumber/world.rb +46 -0
  77. data/lib/facebooker/rails/extensions/action_controller.rb +48 -0
  78. data/lib/facebooker/rails/extensions/rack_setup.rb +2 -0
  79. data/lib/facebooker/rails/extensions/routing.rb +15 -0
  80. data/lib/facebooker/rails/facebook_form_builder.rb +112 -0
  81. data/lib/facebooker/rails/facebook_pretty_errors.rb +22 -0
  82. data/lib/facebooker/rails/facebook_request_fix.rb +24 -0
  83. data/lib/facebooker/rails/facebook_session_handling.rb +69 -0
  84. data/lib/facebooker/rails/facebook_url_helper.rb +192 -0
  85. data/lib/facebooker/rails/facebook_url_rewriting.rb +52 -0
  86. data/lib/facebooker/rails/helpers.rb +772 -0
  87. data/lib/facebooker/rails/helpers/fb_connect.rb +99 -0
  88. data/lib/facebooker/rails/integration_session.rb +38 -0
  89. data/lib/facebooker/rails/profile_publisher_extensions.rb +42 -0
  90. data/lib/facebooker/rails/publisher.rb +530 -0
  91. data/lib/facebooker/rails/routing.rb +49 -0
  92. data/lib/facebooker/rails/test_helpers.rb +68 -0
  93. data/lib/facebooker/rails/utilities.rb +22 -0
  94. data/lib/facebooker/server_cache.rb +24 -0
  95. data/lib/facebooker/service.rb +94 -0
  96. data/lib/facebooker/session.rb +596 -0
  97. data/lib/facebooker/version.rb +9 -0
  98. data/lib/net/http_multipart_post.rb +123 -0
  99. data/lib/rack/facebook.rb +77 -0
  100. data/lib/tasks/facebooker.rake +18 -0
  101. data/lib/tasks/tunnel.rake +46 -0
  102. data/rails/init.rb +1 -0
  103. data/setup.rb +1585 -0
  104. data/templates/layout.erb +24 -0
  105. data/test/facebooker/adapters_test.rb +96 -0
  106. data/test/facebooker/admin_test.rb +102 -0
  107. data/test/facebooker/batch_request_test.rb +83 -0
  108. data/test/facebooker/data_test.rb +86 -0
  109. data/test/facebooker/logging_test.rb +43 -0
  110. data/test/facebooker/mobile_test.rb +45 -0
  111. data/test/facebooker/model_test.rb +123 -0
  112. data/test/facebooker/models/event_test.rb +15 -0
  113. data/test/facebooker/models/user_test.rb +331 -0
  114. data/test/facebooker/rails/publisher_test.rb +468 -0
  115. data/test/facebooker/rails_integration_test.rb +1349 -0
  116. data/test/facebooker/server_cache_test.rb +44 -0
  117. data/test/facebooker/session_test.rb +614 -0
  118. data/test/facebooker_test.rb +925 -0
  119. data/test/fixtures/multipart_post_body_with_only_parameters.txt +33 -0
  120. data/test/fixtures/multipart_post_body_with_single_file.txt +38 -0
  121. data/test/fixtures/multipart_post_body_with_single_file_that_has_nil_key.txt +38 -0
  122. data/test/net/http_multipart_post_test.rb +52 -0
  123. data/test/rack/facebook_test.rb +62 -0
  124. data/test/rails_test_helper.rb +13 -0
  125. data/test/test_helper.rb +66 -0
  126. metadata +219 -0
@@ -0,0 +1,772 @@
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. Valid
286
+ # sizes are :thumb, :small, :normal and :square. Or, you can specify
287
+ # width and height settings instead, just like an img tag.
288
+ #
289
+ # You can optionally specify whether or not to include the facebook icon
290
+ # overlay using the :facebook_logo => true option. Default is false.
291
+ #
292
+ def fb_profile_pic(user, options={})
293
+ options = options.dup
294
+ validate_fb_profile_pic_size(options)
295
+ options.transform_keys!(FB_PROFILE_PIC_OPTION_KEYS_TO_TRANSFORM)
296
+ options.assert_valid_keys(FB_PROFILE_PIC_VALID_OPTION_KEYS)
297
+ options.merge!(:uid => cast_to_facebook_id(user))
298
+ content_tag("fb:profile-pic", nil,stringify_vals(options))
299
+ end
300
+
301
+ FB_PROFILE_PIC_OPTION_KEYS_TO_TRANSFORM = {:facebook_logo => 'facebook-logo'}
302
+ FB_PROFILE_PIC_VALID_OPTION_KEYS = [:size, :linked, 'facebook-logo', :width, :height]
303
+
304
+
305
+ # Render an fb:photo tag.
306
+ # photo is either a Facebooker::Photo or an id of a Facebook photo or an object that responds to photo_id.
307
+ # <em> See: </em> http://wiki.developers.facebook.com/index.php/Fb:photo for complete list of options.
308
+ def fb_photo(photo, options={})
309
+ options = options.dup
310
+ options.assert_valid_keys(FB_PHOTO_VALID_OPTION_KEYS)
311
+ options.merge!(:pid => cast_to_photo_id(photo))
312
+ validate_fb_photo_size(options)
313
+ validate_fb_photo_align_value(options)
314
+ content_tag("fb:photo",nil, stringify_vals(options))
315
+ end
316
+
317
+ FB_PHOTO_VALID_OPTION_KEYS = [:uid, :size, :align]
318
+
319
+ def cast_to_photo_id(object)
320
+ object.respond_to?(:photo_id) ? object.photo_id : object
321
+ end
322
+
323
+ VALID_FB_SHARED_PHOTO_SIZES = [:thumb, :small, :normal, :square]
324
+ VALID_FB_PHOTO_SIZES = VALID_FB_SHARED_PHOTO_SIZES
325
+ VALID_FB_PROFILE_PIC_SIZES = VALID_FB_SHARED_PHOTO_SIZES
326
+ VALID_PERMISSIONS=[:email, :offline_access, :status_update, :photo_upload, :create_listing, :create_event, :rsvp_event, :sms, :video_upload]
327
+
328
+ # Render an fb:tabs tag containing some number of fb:tab_item tags.
329
+ # Example:
330
+ # <% fb_tabs do %>
331
+ # <%= fb_tab_item("Home", "home") %>
332
+ # <%= fb_tab_item("Office", "office") %>
333
+ # <% end %>
334
+ def fb_tabs(&block)
335
+ content = capture(&block)
336
+ if ignore_binding?
337
+ concat(content_tag("fb:tabs", content))
338
+ else
339
+ concat(content_tag("fb:tabs", content), block.binding)
340
+ end
341
+ end
342
+
343
+ # Render an fb:tab_item tag.
344
+ # Use this in conjunction with fb_tabs
345
+ # Options can contains :selected => true to indicate that a tab is the current tab.
346
+ # <em> See: </em> http://wiki.developers.facebook.com/index.php/Fb:tab-item for complete list of options
347
+ def fb_tab_item(title, url, options={})
348
+ options= options.dup
349
+ options.assert_valid_keys(FB_TAB_ITEM_VALID_OPTION_KEYS)
350
+ options.merge!(:title => title, :href => url)
351
+ validate_fb_tab_item_align_value(options)
352
+ tag("fb:tab-item", stringify_vals(options))
353
+ end
354
+
355
+ FB_TAB_ITEM_VALID_OPTION_KEYS = [:align, :selected]
356
+
357
+ def validate_fb_tab_item_align_value(options)
358
+ if options.has_key?(:align) && !VALID_FB_TAB_ITEM_ALIGN_VALUES.include?(options[:align].to_sym)
359
+ raise(ArgumentError, "Unknown value for align: #{options[:align]}")
360
+ end
361
+ end
362
+
363
+ def validate_fb_photo_align_value(options)
364
+ if options.has_key?(:align) && !VALID_FB_PHOTO_ALIGN_VALUES.include?(options[:align].to_sym)
365
+ raise(ArgumentError, "Unknown value for align: #{options[:align]}")
366
+ end
367
+ end
368
+
369
+ VALID_FB_SHARED_ALIGN_VALUES = [:left, :right]
370
+ VALID_FB_PHOTO_ALIGN_VALUES = VALID_FB_SHARED_ALIGN_VALUES
371
+ VALID_FB_TAB_ITEM_ALIGN_VALUES = VALID_FB_SHARED_ALIGN_VALUES
372
+
373
+
374
+ # Create a Facebook wall. It can contain fb_wall_posts
375
+ #
376
+ # For Example:
377
+ # <% fb_wall do %>
378
+ # <%= fb_wall_post(@user,"This is my message") %>
379
+ # <%= fb_wall_post(@otheruser,"This is another message") %>
380
+ # <% end %>
381
+ def fb_wall(&proc)
382
+ content = capture(&proc)
383
+ if ignore_binding?
384
+ concat(content_tag("fb:wall",content,{}))
385
+ else
386
+ concat(content_tag("fb:wall",content,{}),proc.binding)
387
+ end
388
+ end
389
+
390
+ # Render an <fb:wallpost> tag
391
+ # TODO: Optionally takes a time parameter t = int The current time, which is displayed in epoch seconds.
392
+ def fb_wallpost(user,message)
393
+ content_tag("fb:wallpost",message,:uid=>cast_to_facebook_id(user))
394
+ end
395
+ alias_method :fb_wall_post, :fb_wallpost
396
+
397
+ # Render an <fb:error> tag
398
+ # If message and text are present then this will render fb:error and fb:message tag
399
+ # TODO: Optionally takes a decoration tag with value of 'no_padding' or 'shorten'
400
+ def fb_error(message, text=nil)
401
+ fb_status_msg("error", message, text)
402
+ end
403
+
404
+ # Render an <fb:explanation> tag
405
+ # If message and text are present then this will render fb:error and fb:message tag
406
+ # TODO: Optionally takes a decoration tag with value of 'no_padding' or 'shorten'
407
+ def fb_explanation(message, text=nil)
408
+ fb_status_msg("explanation", message, text)
409
+ end
410
+
411
+ # Render an <fb:success> tag
412
+ # If message and text are present then this will render fb:error and fb:message tag
413
+ # TODO: Optionally takes a decoration tag with value of 'no_padding' or 'shorten'
414
+ def fb_success(message, text=nil)
415
+ fb_status_msg("success", message, text)
416
+ end
417
+
418
+ # Render flash values as <fb:message> and <fb:error> tags
419
+ #
420
+ # values in flash[:notice] will be rendered as an <fb:message>
421
+ #
422
+ # values in flash[:error] will be rendered as an <fb:error>
423
+ # TODO: Allow flash[:info] to render fb_explanation
424
+ def facebook_messages
425
+ message=""
426
+ unless flash[:notice].blank?
427
+ message += fb_success(flash[:notice])
428
+ end
429
+ unless flash[:error].blank?
430
+ message += fb_error(flash[:error])
431
+ end
432
+ message
433
+ end
434
+
435
+ # Create a dashboard. It can contain fb_action, fb_help, and fb_create_button
436
+ #
437
+ # For Example:
438
+ # <% fb_dashboard do %>
439
+ # <%= APP_NAME %>
440
+ # <%= fb_action 'My Matches', search_path %>
441
+ # <%= fb_help 'Feedback', "http://www.facebook.com/apps/application.php?id=6236036681" %>
442
+ # <%= fb_create_button 'Invite Friends', main_path %>
443
+ # <% end %>
444
+ def fb_dashboard(&proc)
445
+ if block_given?
446
+ content = capture(&proc)
447
+ if ignore_binding?
448
+ concat(content_tag("fb:dashboard",content,{}))
449
+ else
450
+ concat(content_tag("fb:dashboard",content,{}),proc.binding)
451
+ end
452
+ else
453
+ content_tag("fb:dashboard",content,{})
454
+ end
455
+ end
456
+
457
+ # Content for the wide profile box goes in this tag
458
+ def fb_wide(&proc)
459
+ content = capture(&proc)
460
+ if ignore_binding?
461
+ concat(content_tag("fb:wide", content, {}))
462
+ else
463
+ concat(content_tag("fb:wide", content, {}), proc.binding)
464
+ end
465
+ end
466
+
467
+ # Content for the narrow profile box goes in this tag
468
+ def fb_narrow(&proc)
469
+ content = capture(&proc)
470
+ if ignore_binding?
471
+ concat(content_tag("fb:narrow", content, {}))
472
+ else
473
+ concat(content_tag("fb:narrow", content, {}), proc.binding)
474
+ end
475
+ end
476
+
477
+ # Renders an action using the <fb:action> tag
478
+ def fb_action(name, url)
479
+ "<fb:action href=\"#{url_for(url)}\">#{name}</fb:action>"
480
+ end
481
+
482
+ # Render a <fb:help> tag
483
+ # For use inside <fb:dashboard>
484
+ def fb_help(name, url)
485
+ "<fb:help href=\"#{url_for(url)}\">#{name}</fb:help>"
486
+ end
487
+
488
+ # Render a <fb:create-button> tag
489
+ # For use inside <fb:dashboard>
490
+ def fb_create_button(name, url)
491
+ "<fb:create-button href=\"#{url_for(url)}\">#{name}</fb:create-button>"
492
+ end
493
+
494
+ # Create a comment area
495
+ # All the data for this content area is stored on the facebook servers.
496
+ # <em>See:</em> http://wiki.developers.facebook.com/index.php/Fb:comments for full details
497
+ def fb_comments(xid,canpost=true,candelete=false,numposts=5,options={})
498
+ options = options.dup
499
+ title = (title = options.delete(:title)) ? fb_title(title) : nil
500
+ content_tag "fb:comments",title,stringify_vals(options.merge(:xid=>xid,:canpost=>canpost.to_s,:candelete=>candelete.to_s,:numposts=>numposts))
501
+ end
502
+
503
+ # Adds a title to the title bar
504
+ #
505
+ # Facebook | App Name | This is the canvas page window title
506
+ #
507
+ # +title+: This is the canvas page window
508
+ def fb_title(title)
509
+ "<fb:title>#{title}</fb:title>"
510
+ end
511
+
512
+ # Create a Google Analytics tag
513
+ #
514
+ # +uacct+: Your Urchin/Google Analytics account ID.
515
+ def fb_google_analytics(uacct, options={})
516
+ options = options.dup
517
+ tag "fb:google-analytics", stringify_vals(options.merge(:uacct => uacct))
518
+ end
519
+
520
+ # Render if-is-app-user tag
521
+ # This tag renders the enclosing content only if the user specified has accepted the terms of service for the application.
522
+ # Use fb_if_user_has_added_app to determine wether the user has added the app.
523
+ # Example:
524
+ # <% fb_if_is_app_user(@facebook_user) do %>
525
+ # Thanks for accepting our terms of service!
526
+ # <% fb_else do %>
527
+ # Hey you haven't agreed to our terms. <%= link_to("Please accept our terms of service.", :action => "terms_of_service") %>
528
+ # <% end %>
529
+ #<% end %>
530
+ def fb_if_is_app_user(user=nil,options={},&proc)
531
+ content = capture(&proc)
532
+ options = options.dup
533
+ options.merge!(:uid=>cast_to_facebook_id(user)) if user
534
+ if ignore_binding?
535
+ concat(content_tag("fb:if-is-app-user",content,stringify_vals(options)))
536
+ else
537
+ concat(content_tag("fb:if-is-app-user",content,stringify_vals(options)),proc.binding)
538
+ end
539
+ end
540
+
541
+ # Render if-user-has-added-app tag
542
+ # This tag renders the enclosing content only if the user specified has installed the application
543
+ #
544
+ # Example:
545
+ # <% fb_if_user_has_added_app(@facebook_user) do %>
546
+ # Hey you are an app user!
547
+ # <% fb_else do %>
548
+ # Hey you aren't an app user. <%= link_to("Add App and see the other side.", :action => "added_app") %>
549
+ # <% end %>
550
+ #<% end %>
551
+ def fb_if_user_has_added_app(user,options={},&proc)
552
+ content = capture(&proc)
553
+ options = options.dup
554
+ if ignore_binding?
555
+ concat(content_tag("fb:if-user-has-added-app", content, stringify_vals(options.merge(:uid=>cast_to_facebook_id(user)))))
556
+ else
557
+ concat(content_tag("fb:if-user-has-added-app", content, stringify_vals(options.merge(:uid=>cast_to_facebook_id(user)))),proc.binding)
558
+ end
559
+ end
560
+
561
+ # Render fb:if-is-user tag
562
+ # This tag only renders enclosing content if the user is one of those specified
563
+ # user can be a single user or an Array of users
564
+ # Example:
565
+ # <% fb_if_is_user(@check_user) do %>
566
+ # <%= fb_name(@facebook_user) %> are one of the users. <%= link_to("Check the other side", :action => "friend") %>
567
+ # <% fb_else do %>
568
+ # <%= fb_name(@facebook_user) %> are not one of the users <%= fb_name(@check_user) %>
569
+ # <%= link_to("Check the other side", :action => "you") %>
570
+ # <% end %>
571
+ # <% end %>
572
+ def fb_if_is_user(user,&proc)
573
+ content = capture(&proc)
574
+ user = [user] unless user.is_a? Array
575
+ user_list=user.map{|u| cast_to_facebook_id(u)}.join(",")
576
+ if ignore_binding?
577
+ concat(content_tag("fb:if-is-user",content,{:uid=>user_list}))
578
+ else
579
+ concat(content_tag("fb:if-is-user",content,{:uid=>user_list}),proc.binding)
580
+ end
581
+ end
582
+
583
+ # Render fb:else tag
584
+ # 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
585
+ def fb_else(&proc)
586
+ content = capture(&proc)
587
+ if ignore_binding?
588
+ concat(content_tag("fb:else",content))
589
+ else
590
+ concat(content_tag("fb:else",content),proc.binding)
591
+ end
592
+ end
593
+
594
+ #
595
+ # Return the URL for the about page of the application
596
+ def fb_about_url
597
+ "http://#{Facebooker.www_server_base_url}/apps/application.php?api_key=#{Facebooker.api_key}"
598
+ end
599
+
600
+ #
601
+ # Embed a discussion board named xid on the current page
602
+ # <em>See</em http://wiki.developers.facebook.com/index.php/Fb:board for more details
603
+ # Options are:
604
+ # * canpost
605
+ # * candelete
606
+ # * canmark
607
+ # * cancreatet
608
+ # * numtopics
609
+ # * callbackurl
610
+ # * returnurl
611
+ #
612
+ def fb_board(xid,options={})
613
+ options = options.dup
614
+ title = (title = options.delete(:title)) ? fb_title(title) : nil
615
+ content_tag("fb:board", title, stringify_vals(options.merge(:xid=>xid)))
616
+ end
617
+
618
+ # Renders an 'Add to Profile' button
619
+ # The button allows a user to add condensed profile box to the main profile
620
+ def fb_add_profile_section
621
+ tag "fb:add-section-button",:section=>"profile"
622
+ end
623
+
624
+ # Renders an 'Add to Info' button
625
+ # The button allows a user to add an application info section to her Info tab
626
+ def fb_add_info_section
627
+ tag "fb:add-section-button",:section=>"info"
628
+ end
629
+
630
+ # Renders a link that, when clicked, initiates a dialog requesting the specified extended permission from the user.
631
+ #
632
+ # You can prompt a user with the following permissions:
633
+ # * email
634
+ # * offline_access
635
+ # * status_update
636
+ # * photo_upload
637
+ # * video_upload
638
+ # * create_listing
639
+ # * create_event
640
+ # * rsvp_event
641
+ # * sms
642
+ #
643
+ # Example:
644
+ # <%= fb_prompt_permission('email', "Would you like to receive email from our application?" ) %>
645
+ #
646
+ # See http://wiki.developers.facebook.com/index.php/Fb:prompt-permission for
647
+ # more details
648
+ #
649
+ def fb_prompt_permission(permission,message,callback=nil)
650
+ raise(ArgumentError, "Unknown value for permission: #{permission}") unless VALID_PERMISSIONS.include?(permission.to_sym)
651
+ args={:perms=>permission}
652
+ args[:next_fbjs]=callback unless callback.nil?
653
+ content_tag("fb:prompt-permission",message,args)
654
+ end
655
+
656
+ # Renders an <fb:eventlink /> tag that displays the event name and links to the event's page.
657
+ def fb_eventlink(eid)
658
+ content_tag "fb:eventlink",nil,:eid=>eid
659
+ end
660
+
661
+ # Renders an <fb:grouplink /> tag that displays the group name and links to the group's page.
662
+ def fb_grouplink(gid)
663
+ content_tag "fb:grouplink",nil,:gid=>gid
664
+ end
665
+
666
+ # Returns the status of the user
667
+ def fb_user_status(user,linked=true)
668
+ content_tag "fb:user-status",nil,stringify_vals(:uid=>cast_to_facebook_id(user), :linked=>linked)
669
+ end
670
+
671
+ # Renders a standard 'Share' button for the specified URL.
672
+ def fb_share_button(url)
673
+ content_tag "fb:share-button",nil,:class=>"url",:href=>url
674
+ end
675
+
676
+ # Renders the FBML on a Facebook server inside an iframe.
677
+ #
678
+ # Meant to be used for a Facebook Connect site or an iframe application
679
+ def fb_serverfbml(options={},&proc)
680
+ inner = capture(&proc)
681
+ concat(content_tag("fb:serverfbml",inner,options),&proc.binding)
682
+ end
683
+
684
+ def fb_container(options={},&proc)
685
+ inner = capture(&proc)
686
+ concat(content_tag("fb:container",inner,options),&proc.binding)
687
+ end
688
+
689
+ # Renders an fb:time element
690
+ #
691
+ # Example:
692
+ # <%= fb_time(Time.now, :tz => 'America/New York', :preposition => true) %>
693
+ #
694
+ # See http://wiki.developers.facebook.com/index.php/Fb:time for
695
+ # more details
696
+ def fb_time(time, options={})
697
+ tag "fb:time",stringify_vals({:t => time.to_i}.merge(options))
698
+ end
699
+
700
+ protected
701
+
702
+ def cast_to_facebook_id(object)
703
+ Facebooker::User.cast_to_facebook_id(object)
704
+ end
705
+
706
+ def validate_fb_profile_pic_size(options)
707
+ if options.has_key?(:size) && !VALID_FB_PROFILE_PIC_SIZES.include?(options[:size].to_sym)
708
+ raise(ArgumentError, "Unknown value for size: #{options[:size]}")
709
+ end
710
+ end
711
+
712
+ def validate_fb_photo_size(options)
713
+ if options.has_key?(:size) && !VALID_FB_PHOTO_SIZES.include?(options[:size].to_sym)
714
+ raise(ArgumentError, "Unknown value for size: #{options[:size]}")
715
+ end
716
+ end
717
+
718
+ private
719
+ def stringify_vals(hash)
720
+ result={}
721
+ hash.each do |key,value|
722
+ result[key]=value.to_s
723
+ end
724
+ result
725
+ end
726
+
727
+ def fb_status_msg(type, message, text)
728
+ if text.blank?
729
+ tag("fb:#{type}", :message => message)
730
+ else
731
+ content_tag("fb:#{type}", content_tag("fb:message", message) + text)
732
+ end
733
+ end
734
+
735
+ def token_tag
736
+ unless protect_against_forgery?
737
+ ''
738
+ else
739
+ tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
740
+ end
741
+ end
742
+
743
+ def ignore_binding?
744
+ ActionPack::VERSION::MAJOR >= 2 && ActionPack::VERSION::MINOR >= 2
745
+ end
746
+ end
747
+ end
748
+ end
749
+
750
+ class Hash
751
+ def transform_keys!(transformation_hash)
752
+ transformation_hash.each_pair{|key, value| transform_key!(key, value)}
753
+ end
754
+
755
+
756
+ def transform_key!(old_key, new_key)
757
+ swapkey!(new_key, old_key)
758
+ end
759
+
760
+ ### This method is lifted from Ruby Facets core
761
+ def swapkey!( newkey, oldkey )
762
+ self[newkey] = self.delete(oldkey) if self.has_key?(oldkey)
763
+ self
764
+ end
765
+
766
+ # We can allow css attributes.
767
+ FB_ALWAYS_VALID_OPTION_KEYS = [:class, :style]
768
+ def assert_valid_keys(*valid_keys)
769
+ unknown_keys = keys - [valid_keys + FB_ALWAYS_VALID_OPTION_KEYS].flatten
770
+ raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
771
+ end
772
+ end