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.
- data/.autotest +15 -0
- data/CHANGELOG.rdoc +24 -0
- data/COPYING.rdoc +19 -0
- data/Manifest.txt +133 -0
- data/README.rdoc +104 -0
- data/Rakefile +85 -0
- data/TODO.rdoc +4 -0
- data/examples/desktop_login.rb +14 -0
- data/facebooker.gemspec +38 -0
- data/generators/facebook/facebook_generator.rb +14 -0
- data/generators/facebook/templates/config/facebooker.yml +49 -0
- data/generators/facebook/templates/public/javascripts/facebooker.js +83 -0
- data/generators/facebook_controller/USAGE +33 -0
- data/generators/facebook_controller/facebook_controller_generator.rb +40 -0
- data/generators/facebook_controller/templates/controller.rb +7 -0
- data/generators/facebook_controller/templates/functional_test.rb +11 -0
- data/generators/facebook_controller/templates/helper.rb +2 -0
- data/generators/facebook_controller/templates/view.fbml.erb +2 -0
- data/generators/facebook_controller/templates/view.html.erb +2 -0
- data/generators/facebook_publisher/facebook_publisher_generator.rb +14 -0
- data/generators/facebook_publisher/templates/create_facebook_templates.rb +15 -0
- data/generators/facebook_publisher/templates/publisher.rb +3 -0
- data/generators/facebook_scaffold/USAGE +27 -0
- data/generators/facebook_scaffold/facebook_scaffold_generator.rb +118 -0
- data/generators/facebook_scaffold/templates/controller.rb +93 -0
- data/generators/facebook_scaffold/templates/facebook_style.css +2579 -0
- data/generators/facebook_scaffold/templates/functional_test.rb +89 -0
- data/generators/facebook_scaffold/templates/helper.rb +2 -0
- data/generators/facebook_scaffold/templates/layout.fbml.erb +6 -0
- data/generators/facebook_scaffold/templates/layout.html.erb +17 -0
- data/generators/facebook_scaffold/templates/style.css +74 -0
- data/generators/facebook_scaffold/templates/view_edit.fbml.erb +13 -0
- data/generators/facebook_scaffold/templates/view_edit.html.erb +18 -0
- data/generators/facebook_scaffold/templates/view_index.fbml.erb +24 -0
- data/generators/facebook_scaffold/templates/view_index.html.erb +24 -0
- data/generators/facebook_scaffold/templates/view_new.fbml.erb +12 -0
- data/generators/facebook_scaffold/templates/view_new.html.erb +17 -0
- data/generators/facebook_scaffold/templates/view_show.fbml.erb +10 -0
- data/generators/facebook_scaffold/templates/view_show.html.erb +10 -0
- data/generators/publisher/publisher_generator.rb +14 -0
- data/generators/xd_receiver/templates/xd_receiver.html +10 -0
- data/generators/xd_receiver/xd_receiver_generator.rb +10 -0
- data/init.rb +25 -0
- data/install.rb +12 -0
- data/lib/facebooker/adapters/adapter_base.rb +91 -0
- data/lib/facebooker/adapters/bebo_adapter.rb +77 -0
- data/lib/facebooker/adapters/facebook_adapter.rb +52 -0
- data/lib/facebooker/admin.rb +42 -0
- data/lib/facebooker/batch_request.rb +45 -0
- data/lib/facebooker/data.rb +57 -0
- data/lib/facebooker/feed.rb +78 -0
- data/lib/facebooker/logging.rb +44 -0
- data/lib/facebooker/mobile.rb +20 -0
- data/lib/facebooker/mock/service.rb +50 -0
- data/lib/facebooker/mock/session.rb +18 -0
- data/lib/facebooker/model.rb +139 -0
- data/lib/facebooker/models/affiliation.rb +10 -0
- data/lib/facebooker/models/album.rb +11 -0
- data/lib/facebooker/models/applicationproperties.rb +39 -0
- data/lib/facebooker/models/applicationrestrictions.rb +10 -0
- data/lib/facebooker/models/cookie.rb +10 -0
- data/lib/facebooker/models/education_info.rb +11 -0
- data/lib/facebooker/models/event.rb +28 -0
- data/lib/facebooker/models/friend_list.rb +16 -0
- data/lib/facebooker/models/group.rb +36 -0
- data/lib/facebooker/models/info_item.rb +10 -0
- data/lib/facebooker/models/info_section.rb +10 -0
- data/lib/facebooker/models/location.rb +8 -0
- data/lib/facebooker/models/notifications.rb +17 -0
- data/lib/facebooker/models/page.rb +28 -0
- data/lib/facebooker/models/photo.rb +19 -0
- data/lib/facebooker/models/tag.rb +12 -0
- data/lib/facebooker/models/user.rb +497 -0
- data/lib/facebooker/models/video.rb +9 -0
- data/lib/facebooker/models/work_info.rb +10 -0
- data/lib/facebooker/parser.rb +650 -0
- data/lib/facebooker/rails/backwards_compatible_param_checks.rb +31 -0
- data/lib/facebooker/rails/controller.rb +337 -0
- data/lib/facebooker/rails/cucumber/world.rb +46 -0
- data/lib/facebooker/rails/cucumber.rb +28 -0
- data/lib/facebooker/rails/extensions/action_controller.rb +48 -0
- data/lib/facebooker/rails/extensions/rack_setup.rb +6 -0
- data/lib/facebooker/rails/extensions/routing.rb +15 -0
- data/lib/facebooker/rails/facebook_form_builder.rb +112 -0
- data/lib/facebooker/rails/facebook_pretty_errors.rb +22 -0
- data/lib/facebooker/rails/facebook_request_fix.rb +30 -0
- data/lib/facebooker/rails/facebook_request_fix_2-3.rb +31 -0
- data/lib/facebooker/rails/facebook_session_handling.rb +68 -0
- data/lib/facebooker/rails/facebook_url_helper.rb +192 -0
- data/lib/facebooker/rails/facebook_url_rewriting.rb +60 -0
- data/lib/facebooker/rails/helpers/fb_connect.rb +118 -0
- data/lib/facebooker/rails/helpers.rb +780 -0
- data/lib/facebooker/rails/integration_session.rb +38 -0
- data/lib/facebooker/rails/profile_publisher_extensions.rb +42 -0
- data/lib/facebooker/rails/publisher.rb +554 -0
- data/lib/facebooker/rails/routing.rb +49 -0
- data/lib/facebooker/rails/test_helpers.rb +68 -0
- data/lib/facebooker/rails/utilities.rb +22 -0
- data/lib/facebooker/server_cache.rb +24 -0
- data/lib/facebooker/service.rb +102 -0
- data/lib/facebooker/session.rb +606 -0
- data/lib/facebooker/version.rb +9 -0
- data/lib/facebooker.rb +180 -0
- data/lib/net/http_multipart_post.rb +123 -0
- data/lib/rack/facebook.rb +77 -0
- data/lib/tasks/facebooker.rake +18 -0
- data/lib/tasks/tunnel.rake +46 -0
- data/rails/init.rb +1 -0
- data/setup.rb +1585 -0
- data/templates/layout.erb +24 -0
- data/test/facebooker/adapters_test.rb +96 -0
- data/test/facebooker/admin_test.rb +102 -0
- data/test/facebooker/batch_request_test.rb +83 -0
- data/test/facebooker/data_test.rb +86 -0
- data/test/facebooker/logging_test.rb +43 -0
- data/test/facebooker/mobile_test.rb +45 -0
- data/test/facebooker/model_test.rb +133 -0
- data/test/facebooker/models/event_test.rb +15 -0
- data/test/facebooker/models/photo_test.rb +16 -0
- data/test/facebooker/models/user_test.rb +343 -0
- data/test/facebooker/rails/facebook_request_fix_2-3_test.rb +24 -0
- data/test/facebooker/rails/facebook_url_rewriting_test.rb +39 -0
- data/test/facebooker/rails/publisher_test.rb +481 -0
- data/test/facebooker/rails_integration_test.rb +1398 -0
- data/test/facebooker/server_cache_test.rb +44 -0
- data/test/facebooker/session_test.rb +614 -0
- data/test/facebooker_test.rb +951 -0
- data/test/fixtures/multipart_post_body_with_only_parameters.txt +33 -0
- data/test/fixtures/multipart_post_body_with_single_file.txt +38 -0
- data/test/fixtures/multipart_post_body_with_single_file_that_has_nil_key.txt +38 -0
- data/test/net/http_multipart_post_test.rb +52 -0
- data/test/rack/facebook_test.rb +61 -0
- data/test/rails_test_helper.rb +27 -0
- data/test/test_helper.rb +74 -0
- 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
|