taweili-facebooker 1.0.37

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 +104 -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 +83 -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 +25 -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 +497 -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 +337 -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 +30 -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 +780 -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 +554 -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 +606 -0
  102. data/lib/facebooker/version.rb +9 -0
  103. data/lib/facebooker.rb +180 -0
  104. data/lib/net/http_multipart_post.rb +123 -0
  105. data/lib/rack/facebook.rb +77 -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 +96 -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 +343 -0
  121. data/test/facebooker/rails/facebook_request_fix_2-3_test.rb +24 -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 +1398 -0
  125. data/test/facebooker/server_cache_test.rb +44 -0
  126. data/test/facebooker/session_test.rb +614 -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 +232 -0
@@ -0,0 +1,606 @@
1
+ require 'cgi'
2
+
3
+ module Facebooker
4
+ #
5
+ # Raised when trying to perform an operation on a user
6
+ # other than the logged in user (if that's unallowed)
7
+ class NonSessionUser < StandardError; end
8
+ class Session
9
+
10
+ #
11
+ # Raised when a facebook session has expired. This
12
+ # happens when the timeout is reached, or when the
13
+ # user logs out of facebook
14
+ # can be handled with:
15
+ # rescue_from Facebooker::Session::SessionExpired, :with => :some_method_name
16
+ class SessionExpired < StandardError; end
17
+
18
+ class UnknownError < StandardError; end
19
+ class ServiceUnavailable < StandardError; end
20
+ class MaxRequestsDepleted < StandardError; end
21
+ class HostNotAllowed < StandardError; end
22
+ class MissingOrInvalidParameter < StandardError; end
23
+ class InvalidAPIKey < StandardError; end
24
+ class SessionExpired < StandardError; end
25
+ class CallOutOfOrder < StandardError; end
26
+ class IncorrectSignature < StandardError; end
27
+ class SignatureTooOld < StandardError; end
28
+ class TooManyUserCalls < StandardError; end
29
+ class TooManyUserActionCalls < StandardError; end
30
+ class InvalidFeedTitleLink < StandardError; end
31
+ class InvalidFeedTitleLength < StandardError; end
32
+ class InvalidFeedTitleName < StandardError; end
33
+ class BlankFeedTitle < StandardError; end
34
+ class FeedBodyLengthTooLong < StandardError; end
35
+ class InvalidFeedPhotoSource < StandardError; end
36
+ class InvalidFeedPhotoLink < StandardError; end
37
+ class TemplateDataMissingRequiredTokens < StandardError; end
38
+ class FeedMarkupInvalid < StandardError; end
39
+ class FeedTitleDataInvalid < StandardError; end
40
+ class FeedTitleTemplateInvalid < StandardError; end
41
+ class FeedBodyDataInvalid < StandardError; end
42
+ class FeedBodyTemplateInvalid < StandardError; end
43
+ class FeedPhotosNotRetrieved < StandardError; end
44
+ class FeedTargetIdsInvalid < StandardError; end
45
+ class TemplateBundleInvalid < StandardError; end
46
+ class ConfigurationMissing < StandardError; end
47
+ class FQLParseError < StandardError; end
48
+ class FQLFieldDoesNotExist < StandardError; end
49
+ class FQLTableDoesNotExist < StandardError; end
50
+ class FQLStatementNotIndexable < StandardError; end
51
+ class FQLFunctionDoesNotExist < StandardError; end
52
+ class FQLWrongNumberArgumentsPassedToFunction < StandardError; end
53
+ class InvalidAlbumId < StandardError; end
54
+ class AlbumIsFull < StandardError; end
55
+ class MissingOrInvalidImageFile < StandardError; end
56
+ class TooManyUnapprovedPhotosPending < StandardError; end
57
+ class ExtendedPermissionRequired < StandardError; end
58
+ class InvalidFriendList < StandardError; end
59
+ class UserUnRegistrationFailed < StandardError
60
+ attr_accessor :failed_users
61
+ end
62
+ class UserRegistrationFailed < StandardError
63
+ attr_accessor :failed_users
64
+ end
65
+
66
+ API_SERVER_BASE_URL = ENV["FACEBOOKER_API"] == "new" ? "api.new.facebook.com" : "api.facebook.com"
67
+ API_PATH_REST = "/restserver.php"
68
+ WWW_SERVER_BASE_URL = ENV["FACEBOOKER_API"] == "new" ? "www.new.facebook.com" : "www.facebook.com"
69
+ WWW_PATH_LOGIN = "/login.php"
70
+ WWW_PATH_ADD = "/add.php"
71
+ WWW_PATH_INSTALL = "/install.php"
72
+
73
+ attr_writer :auth_token
74
+ attr_reader :session_key
75
+
76
+ def self.create(api_key=nil, secret_key=nil)
77
+ api_key ||= self.api_key
78
+ secret_key ||= self.secret_key
79
+ raise ArgumentError unless !api_key.nil? && !secret_key.nil?
80
+ new(api_key, secret_key)
81
+ end
82
+
83
+ def self.api_key
84
+ extract_key_from_environment(:api) || extract_key_from_configuration_file(:api) rescue report_inability_to_find_key(:api)
85
+ end
86
+
87
+ def self.secret_key
88
+ extract_key_from_environment(:secret) || extract_key_from_configuration_file(:secret) rescue report_inability_to_find_key(:secret)
89
+ end
90
+
91
+ def self.current
92
+ Thread.current['facebook_session']
93
+ end
94
+
95
+ def self.current=(session)
96
+ Thread.current['facebook_session'] = session
97
+ end
98
+
99
+ def login_url(options={})
100
+ options = default_login_url_options.merge(options)
101
+ "#{Facebooker.login_url_base(@api_key)}#{login_url_optional_parameters(options)}"
102
+ end
103
+
104
+ def install_url(options={})
105
+ "#{Facebooker.install_url_base(@api_key)}#{install_url_optional_parameters(options)}"
106
+ end
107
+
108
+ # The url to get user to approve extended permissions
109
+ # http://wiki.developers.facebook.com/index.php/Extended_permission
110
+ #
111
+ # permissions:
112
+ # * email
113
+ # * offline_access
114
+ # * status_update
115
+ # * photo_upload
116
+ # * video_upload
117
+ # * create_listing
118
+ # * create_event
119
+ # * rsvp_event
120
+ # * sms
121
+ def permission_url(permission,options={})
122
+ options = default_login_url_options.merge(options)
123
+ "http://#{Facebooker.www_server_base_url}/authorize.php?api_key=#{@api_key}&v=1.0&ext_perm=#{permission}#{install_url_optional_parameters(options)}"
124
+ end
125
+
126
+ def install_url_optional_parameters(options)
127
+ optional_parameters = []
128
+ optional_parameters += add_next_parameters(options)
129
+ optional_parameters.join
130
+ end
131
+
132
+ def add_next_parameters(options)
133
+ opts = []
134
+ opts << "&next=#{CGI.escape(options[:next])}" if options[:next]
135
+ opts << "&next_cancel=#{CGI.escape(options[:next_cancel])}" if options[:next_cancel]
136
+ opts
137
+ end
138
+
139
+ def login_url_optional_parameters(options)
140
+ # It is important that unused options are omitted as stuff like &canvas=false will still display the canvas.
141
+ optional_parameters = []
142
+ optional_parameters += add_next_parameters(options)
143
+ optional_parameters << "&skipcookie=true" if options[:skip_cookie]
144
+ optional_parameters << "&hide_checkbox=true" if options[:hide_checkbox]
145
+ optional_parameters << "&canvas=true" if options[:canvas]
146
+ optional_parameters.join
147
+ end
148
+
149
+ def default_login_url_options
150
+ {}
151
+ end
152
+
153
+ def initialize(api_key, secret_key)
154
+ @api_key = api_key
155
+ @secret_key = secret_key
156
+ @batch_request = nil
157
+ @session_key = nil
158
+ @uid = nil
159
+ @auth_token = nil
160
+ @secret_from_session = nil
161
+ @expires = nil
162
+ end
163
+
164
+ def secret_for_method(method_name)
165
+ @secret_key
166
+ end
167
+
168
+ def auth_token
169
+ @auth_token ||= post 'facebook.auth.createToken'
170
+ end
171
+
172
+ def infinite?
173
+ @expires == 0
174
+ end
175
+
176
+ def expired?
177
+ @expires.nil? || (!infinite? && Time.at(@expires) <= Time.now)
178
+ end
179
+
180
+ def secured?
181
+ !@session_key.nil? && !expired?
182
+ end
183
+
184
+ def secure!
185
+ response = post 'facebook.auth.getSession', :auth_token => auth_token
186
+ secure_with!(response['session_key'], response['uid'], response['expires'], response['secret'])
187
+ end
188
+
189
+ def secure_with!(session_key, uid = nil, expires = nil, secret_from_session = nil)
190
+ @session_key = session_key
191
+ @uid = uid ? Integer(uid) : post('facebook.users.getLoggedInUser', :session_key => session_key)
192
+ @expires = Integer(expires)
193
+ @secret_from_session = secret_from_session
194
+ end
195
+
196
+ def fql_query(query, format = 'XML')
197
+ post('facebook.fql.query', :query => query, :format => format) do |response|
198
+ type = response.shift
199
+ return [] if type.nil?
200
+ response.shift.map do |hash|
201
+ case type
202
+ when 'user'
203
+ user = User.new
204
+ user.session = self
205
+ user.populate_from_hash!(hash)
206
+ user
207
+ when 'photo'
208
+ Photo.from_hash(hash)
209
+ when 'page'
210
+ Page.from_hash(hash)
211
+ when 'page_admin'
212
+ Page.from_hash(hash)
213
+ when 'event_member'
214
+ Event::Attendance.from_hash(hash)
215
+ else
216
+ hash
217
+ end
218
+ end
219
+ end
220
+ end
221
+
222
+ def user
223
+ @user ||= User.new(uid, self)
224
+ end
225
+
226
+ #
227
+ # This one has so many parameters, a Hash seemed cleaner than a long param list. Options can be:
228
+ # :uid => Filter by events associated with a user with this uid
229
+ # :eids => Filter by this list of event ids. This is a comma-separated list of eids.
230
+ # :start_time => Filter with this UTC as lower bound. A missing or zero parameter indicates no lower bound. (Time or Integer)
231
+ # :end_time => Filter with this UTC as upper bound. A missing or zero parameter indicates no upper bound. (Time or Integer)
232
+ # :rsvp_status => Filter by this RSVP status.
233
+ def events(options = {})
234
+ @events = post('facebook.events.get', options) do |response|
235
+ response.map do |hash|
236
+ Event.from_hash(hash)
237
+ end
238
+ end
239
+ end
240
+
241
+ def event_members(eid)
242
+ @members = post('facebook.events.getMembers', :eid => eid) do |response|
243
+ response.map do |attendee_hash|
244
+ Event::Attendance.from_hash(attendee_hash)
245
+ end
246
+ end
247
+ end
248
+
249
+ def users_standard(user_ids, fields=[])
250
+ post("facebook.users.getStandardInfo",:uids=>user_ids.join(","),:fields=>User.standard_fields(fields)) do |users|
251
+ users.map { |u| User.new(u)}
252
+ end
253
+ end
254
+
255
+ def users(user_ids, fields=[])
256
+ post("facebook.users.getInfo",:uids=>user_ids.join(","),:fields=>User.user_fields(fields)) do |users|
257
+ users.map { |u| User.new(u)}
258
+ end
259
+ end
260
+
261
+ def pages(options = {})
262
+ raise ArgumentError, 'fields option is mandatory' unless options.has_key?(:fields)
263
+ @pages ||= {}
264
+ @pages[options] ||= post('facebook.pages.getInfo', options) do |response|
265
+ response.map do |hash|
266
+ Page.from_hash(hash)
267
+ end
268
+ end
269
+ end
270
+
271
+ # Takes page_id and uid, returns true if uid is a fan of the page_id
272
+ def is_fan(page_id, uid)
273
+ post('facebook.pages.isFan', :page_id=>page_id, :uid=>uid)
274
+ end
275
+
276
+ #
277
+ # Returns a proxy object for handling calls to Facebook cached items
278
+ # such as images and FBML ref handles
279
+ def server_cache
280
+ Facebooker::ServerCache.new(self)
281
+ end
282
+
283
+ #
284
+ # Returns a proxy object for handling calls to the Facebook Data API
285
+ def data
286
+ Facebooker::Data.new(self)
287
+ end
288
+
289
+ def admin
290
+ Facebooker::Admin.new(self)
291
+ end
292
+
293
+ def mobile
294
+ Facebooker::Mobile.new(self)
295
+ end
296
+
297
+ #
298
+ # Given an array like:
299
+ # [[userid, otheruserid], [yetanotherid, andanotherid]]
300
+ # returns a Hash indicating friendship of those pairs:
301
+ # {[userid, otheruserid] => true, [yetanotherid, andanotherid] => false}
302
+ # if one of the Hash values is nil, it means the facebook platform's answer is "I don't know"
303
+ def check_friendship(array_of_pairs_of_users)
304
+ uids1 = []
305
+ uids2 = []
306
+ array_of_pairs_of_users.each do |pair|
307
+ uids1 << pair.first
308
+ uids2 << pair.last
309
+ end
310
+ post('facebook.friends.areFriends', :uids1 => uids1.join(','), :uids2 => uids2.join(','))
311
+ end
312
+
313
+ def get_photos(pids = nil, subj_id = nil, aid = nil)
314
+ if [subj_id, pids, aid].all? {|arg| arg.nil?}
315
+ raise ArgumentError, "Can't get a photo without a picture, album or subject ID"
316
+ end
317
+ @photos = post('facebook.photos.get', :subj_id => subj_id, :pids => pids, :aid => aid ) do |response|
318
+ response.map do |hash|
319
+ Photo.from_hash(hash)
320
+ end
321
+ end
322
+ end
323
+
324
+ def get_albums(aids)
325
+ @albums = post('facebook.photos.getAlbums', :aids => aids) do |response|
326
+ response.map do |hash|
327
+ Album.from_hash(hash)
328
+ end
329
+ end
330
+ end
331
+
332
+ def get_tags(pids)
333
+ @tags = post('facebook.photos.getTags', :pids => pids) do |response|
334
+ response.map do |hash|
335
+ Tag.from_hash(hash)
336
+ end
337
+ end
338
+ end
339
+
340
+ def add_tags(pid, x, y, tag_uid = nil, tag_text = nil )
341
+ if [tag_uid, tag_text].all? {|arg| arg.nil?}
342
+ raise ArgumentError, "Must enter a name or string for this tag"
343
+ end
344
+ @tags = post('facebook.photos.addTag', :pid => pid, :tag_uid => tag_uid, :tag_text => tag_text, :x => x, :y => y )
345
+ end
346
+
347
+ def send_notification(user_ids, fbml, email_fbml = nil)
348
+ params = {:notification => fbml, :to_ids => user_ids.map{ |id| User.cast_to_facebook_id(id)}.join(',')}
349
+ if email_fbml
350
+ params[:email] = email_fbml
351
+ end
352
+ params[:type]="user_to_user"
353
+ # if there is no uid, this is an announcement
354
+ unless uid?
355
+ params[:type]="app_to_user"
356
+ end
357
+
358
+ post 'facebook.notifications.send', params,uid?
359
+ end
360
+
361
+ ##
362
+ # Register a template bundle with Facebook.
363
+ # returns the template id to use to send using this template
364
+ def register_template_bundle(one_line_story_templates,short_story_templates=nil,full_story_template=nil, action_links=nil)
365
+ parameters = {:one_line_story_templates => Array(one_line_story_templates).to_json}
366
+
367
+ parameters[:action_links] = action_links.to_json unless action_links.blank?
368
+
369
+ parameters[:short_story_templates] = Array(short_story_templates).to_json unless short_story_templates.blank?
370
+
371
+ parameters[:full_story_template] = full_story_template.to_json unless full_story_template.blank?
372
+
373
+ post("facebook.feed.registerTemplateBundle", parameters, false)
374
+ end
375
+
376
+ ##
377
+ # publish a previously rendered template bundle
378
+ # see http://wiki.developers.facebook.com/index.php/Feed.publishUserAction
379
+ #
380
+ def publish_user_action(bundle_id,data={},target_ids=nil,body_general=nil,story_size=nil)
381
+ parameters={:template_bundle_id=>bundle_id,:template_data=>data.to_json}
382
+ parameters[:target_ids] = target_ids unless target_ids.blank?
383
+ parameters[:body_general] = body_general unless body_general.blank?
384
+ parameters[:story_size] = story_size unless story_size.nil?
385
+ post("facebook.feed.publishUserAction", parameters)
386
+ end
387
+
388
+
389
+ ##
390
+ # Send email to as many as 100 users at a time
391
+ def send_email(user_ids, subject, text, fbml = nil)
392
+ user_ids = Array(user_ids)
393
+ params = {:fbml => fbml, :recipients => user_ids.map{ |id| User.cast_to_facebook_id(id)}.join(','), :text => text, :subject => subject}
394
+ post 'facebook.notifications.sendEmail', params
395
+ end
396
+
397
+ # Only serialize the bare minimum to recreate the session.
398
+ def marshal_load(variables)#:nodoc:
399
+ fields_to_serialize.each_with_index{|field, index| instance_variable_set_value(field, variables[index])}
400
+ end
401
+
402
+ # Only serialize the bare minimum to recreate the session.
403
+ def marshal_dump#:nodoc:
404
+ fields_to_serialize.map{|field| instance_variable_value(field)}
405
+ end
406
+
407
+ # Only serialize the bare minimum to recreate the session.
408
+ def to_yaml( opts = {} )
409
+ YAML::quick_emit(self.object_id, opts) do |out|
410
+ out.map(taguri) do |map|
411
+ fields_to_serialize.each do |field|
412
+ map.add(field, instance_variable_value(field))
413
+ end
414
+ end
415
+ end
416
+ end
417
+
418
+ def instance_variable_set_value(field, value)
419
+ self.instance_variable_set("@#{field}", value)
420
+ end
421
+
422
+ def instance_variable_value(field)
423
+ self.instance_variable_get("@#{field}")
424
+ end
425
+
426
+ def fields_to_serialize
427
+ %w(session_key uid expires secret_from_session auth_token api_key secret_key)
428
+ end
429
+
430
+ class Desktop < Session
431
+ def login_url
432
+ super + "&auth_token=#{auth_token}"
433
+ end
434
+
435
+ def secret_for_method(method_name)
436
+ secret = auth_request_methods.include?(method_name) ? super : @secret_from_session
437
+ secret
438
+ end
439
+
440
+ def post(method, params = {},use_session=false)
441
+ if method == 'facebook.profile.getFBML' || method == 'facebook.profile.setFBML'
442
+ raise NonSessionUser.new("User #{@uid} is not the logged in user.") unless @uid == params[:uid]
443
+ end
444
+ super
445
+ end
446
+ private
447
+ def auth_request_methods
448
+ ['facebook.auth.getSession', 'facebook.auth.createToken']
449
+ end
450
+ end
451
+
452
+ def batch_request?
453
+ @batch_request
454
+ end
455
+
456
+ def add_to_batch(req,&proc)
457
+ batch_request = BatchRequest.new(req,proc)
458
+ Thread.current[:facebooker_current_batch_queue] << batch_request
459
+ batch_request
460
+ end
461
+
462
+ # Submit the enclosed requests for this session inside a batch
463
+ #
464
+ # All requests will be sent to Facebook at the end of the block
465
+ # each method inside the block will return a proxy object
466
+ # attempting to access the proxy before the end of the block will yield an exception
467
+ #
468
+ # For Example:
469
+ #
470
+ # facebook_session.batch do
471
+ # @send_result = facebook_session.send_notification([12451752],"Woohoo")
472
+ # @albums = facebook_session.user.albums
473
+ # end
474
+ # puts @albums.first.inspect
475
+ #
476
+ # is valid, however
477
+ #
478
+ # facebook_session.batch do
479
+ # @send_result = facebook_session.send_notification([12451752],"Woohoo")
480
+ # @albums = facebook_session.user.albums
481
+ # puts @albums.first.inspect
482
+ # end
483
+ #
484
+ # will raise Facebooker::BatchRequest::UnexecutedRequest
485
+ #
486
+ # If an exception is raised while processing the result, that exception will be
487
+ # re-raised on the next access to that object or when exception_raised? is called
488
+ #
489
+ # for example, if the send_notification resulted in TooManyUserCalls being raised,
490
+ # calling
491
+ # @send_result.exception_raised?
492
+ # would re-raise that exception
493
+ # if there was an error retrieving the albums, it would be re-raised when
494
+ # @albums.first
495
+ # is called
496
+ #
497
+ def batch(serial_only=false)
498
+ @batch_request=true
499
+ Thread.current[:facebooker_current_batch_queue]=[]
500
+ yield
501
+ # Set the batch request to false so that post will execute the batch job
502
+ @batch_request=false
503
+ BatchRun.current_batch=Thread.current[:facebooker_current_batch_queue]
504
+ post("facebook.batch.run",:method_feed=>BatchRun.current_batch.map{|q| q.uri}.to_json,:serial_only=>serial_only.to_s)
505
+ ensure
506
+ @batch_request=false
507
+ BatchRun.current_batch=nil
508
+ end
509
+
510
+ def post_without_logging(method, params = {}, use_session_key = true, &proc)
511
+ add_facebook_params(params, method)
512
+ use_session_key && @session_key && params[:session_key] ||= @session_key
513
+ final_params=params.merge(:sig => signature_for(params))
514
+ if batch_request?
515
+ add_to_batch(final_params,&proc)
516
+ else
517
+ result = service.post(final_params)
518
+ result = yield result if block_given?
519
+ result
520
+ end
521
+ end
522
+
523
+ def post(method, params = {}, use_session_key = true, &proc)
524
+ if batch_request?
525
+ post_without_logging(method, params, use_session_key, &proc)
526
+ else
527
+ Logging.log_fb_api(method, params) do
528
+ post_without_logging(method, params, use_session_key, &proc)
529
+ end
530
+ end
531
+ end
532
+
533
+ def post_file(method, params = {})
534
+ base = params.delete(:base)
535
+ Logging.log_fb_api(method, params) do
536
+ add_facebook_params(params, method)
537
+ @session_key && params[:session_key] ||= @session_key unless params[:uid]
538
+ service.post_file(params.merge(:base => base, :sig => signature_for(params.reject{|key, value| key.nil?})))
539
+ end
540
+ end
541
+
542
+
543
+ @configuration_file_path = nil
544
+
545
+ def self.configuration_file_path
546
+ @configuration_file_path || File.expand_path("~/.facebookerrc")
547
+ end
548
+
549
+ def self.configuration_file_path=(path)
550
+ @configuration_file_path = path
551
+ end
552
+
553
+ private
554
+ def add_facebook_params(hash, method)
555
+ hash[:method] = method
556
+ hash[:api_key] = @api_key
557
+ hash[:call_id] = Time.now.to_f.to_s unless method == 'facebook.auth.getSession'
558
+ hash[:v] = "1.0"
559
+ end
560
+
561
+ # This ultimately delgates to the adapter
562
+ def self.extract_key_from_environment(key_name)
563
+ Facebooker.send(key_name.to_s + "_key") rescue nil
564
+ end
565
+
566
+ def self.extract_key_from_configuration_file(key_name)
567
+ read_configuration_file[key_name]
568
+ end
569
+
570
+ def self.report_inability_to_find_key(key_name)
571
+ raise ConfigurationMissing, "Could not find configuration information for #{key_name}"
572
+ end
573
+
574
+ def self.read_configuration_file
575
+ eval(File.read(configuration_file_path))
576
+ end
577
+
578
+ def service
579
+ @service ||= Service.new(Facebooker.api_server_base, Facebooker.api_rest_path, @api_key)
580
+ end
581
+
582
+ def uid
583
+ @uid || (secure!; @uid)
584
+ end
585
+
586
+ def uid?
587
+ !! @uid
588
+ end
589
+
590
+ def signature_for(params)
591
+ raw_string = params.inject([]) do |collection, pair|
592
+ collection << pair.map { |x|
593
+ Array === x ? Facebooker.json_encode(x) : x
594
+ }.join("=")
595
+ collection
596
+ end.sort.join
597
+ Digest::MD5.hexdigest([raw_string, secret_for_method(params[:method])].join)
598
+ end
599
+ end
600
+
601
+ class CanvasSession < Session
602
+ def default_login_url_options
603
+ {:canvas => true}
604
+ end
605
+ end
606
+ end
@@ -0,0 +1,9 @@
1
+ module Facebooker #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 1
4
+ MINOR = 0
5
+ TINY = 37
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end