facebooker 0.9.5
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/CHANGELOG.txt +0 -0
- data/COPYING +19 -0
- data/History.txt +3 -0
- data/Manifest.txt +60 -0
- data/README +44 -0
- data/README.txt +79 -0
- data/Rakefile +38 -0
- data/TODO.txt +10 -0
- data/facebooker.yml.tpl +36 -0
- data/init.rb +52 -0
- data/install.rb +5 -0
- data/lib/facebooker.rb +63 -0
- data/lib/facebooker/affiliation.rb +10 -0
- data/lib/facebooker/album.rb +11 -0
- data/lib/facebooker/cookie.rb +10 -0
- data/lib/facebooker/data.rb +38 -0
- data/lib/facebooker/education_info.rb +11 -0
- data/lib/facebooker/event.rb +26 -0
- data/lib/facebooker/feed.rb +65 -0
- data/lib/facebooker/group.rb +35 -0
- data/lib/facebooker/location.rb +8 -0
- data/lib/facebooker/model.rb +118 -0
- data/lib/facebooker/notifications.rb +17 -0
- data/lib/facebooker/parser.rb +386 -0
- data/lib/facebooker/photo.rb +9 -0
- data/lib/facebooker/rails/controller.rb +174 -0
- data/lib/facebooker/rails/facebook_asset_path.rb +18 -0
- data/lib/facebooker/rails/facebook_form_builder.rb +112 -0
- data/lib/facebooker/rails/facebook_request_fix.rb +14 -0
- data/lib/facebooker/rails/facebook_session_handling.rb +58 -0
- data/lib/facebooker/rails/facebook_url_rewriting.rb +31 -0
- data/lib/facebooker/rails/helpers.rb +535 -0
- data/lib/facebooker/rails/routing.rb +49 -0
- data/lib/facebooker/rails/test_helpers.rb +11 -0
- data/lib/facebooker/rails/utilities.rb +22 -0
- data/lib/facebooker/server_cache.rb +24 -0
- data/lib/facebooker/service.rb +25 -0
- data/lib/facebooker/session.rb +385 -0
- data/lib/facebooker/tag.rb +12 -0
- data/lib/facebooker/user.rb +200 -0
- data/lib/facebooker/version.rb +9 -0
- data/lib/facebooker/work_info.rb +9 -0
- data/lib/net/http_multipart_post.rb +123 -0
- data/lib/tasks/facebooker.rake +16 -0
- data/lib/tasks/tunnel.rake +39 -0
- data/setup.rb +1585 -0
- data/test/event_test.rb +15 -0
- data/test/facebook_cache_test.rb +43 -0
- data/test/facebook_data_test.rb +50 -0
- data/test/facebooker_test.rb +766 -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/http_multipart_post_test.rb +54 -0
- data/test/model_test.rb +79 -0
- data/test/rails_integration_test.rb +732 -0
- data/test/session_test.rb +396 -0
- data/test/test_helper.rb +54 -0
- data/test/user_test.rb +101 -0
- metadata +130 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
module Facebooker
|
2
|
+
module Rails
|
3
|
+
module Routing
|
4
|
+
module RouteSetExtensions
|
5
|
+
def self.included(base)
|
6
|
+
base.alias_method_chain :extract_request_environment, :facebooker
|
7
|
+
end
|
8
|
+
|
9
|
+
def extract_request_environment_with_facebooker(request)
|
10
|
+
env = extract_request_environment_without_facebooker(request)
|
11
|
+
env.merge :canvas => (request.parameters[:fb_sig_in_canvas]=="1")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
module MapperExtensions
|
15
|
+
|
16
|
+
# Generates pseudo-resource routes. Since everything is a POST, routes can't be identified
|
17
|
+
# using HTTP verbs. Therefore, the action is appended to the beginning of each named route,
|
18
|
+
# except for index.
|
19
|
+
#
|
20
|
+
# Example:
|
21
|
+
# map.facebook_resources :profiles
|
22
|
+
#
|
23
|
+
# Generates the following routes:
|
24
|
+
#
|
25
|
+
# new_profile POST /profiles/new {:controller=>"profiles", :action=>"new"}
|
26
|
+
# profiles POST /profiles/index {:controller=>"profiles", :action=>"index"}
|
27
|
+
# show_profile POST /profiles/:id/show {:controller=>"profiles", :action=>"show"}
|
28
|
+
# create_profile POST /profiles/create {:controller=>"profiles", :action=>"create"}
|
29
|
+
# edit_profile POST /profiles/:id/edit {:controller=>"profiles", :action=>"edit"}
|
30
|
+
# update_profile POST /profiles/:id/update {:controller=>"profiles", :action=>"update"}
|
31
|
+
# destroy_profile POST /profiles/:id/destroy {:controller=>"profiles", :action=>"destroy"}
|
32
|
+
#
|
33
|
+
def facebook_resources(name_sym)
|
34
|
+
name = name_sym.to_s
|
35
|
+
|
36
|
+
with_options :controller => name, :conditions => { :method => :post } do |map|
|
37
|
+
map.named_route("new_#{name.singularize}", "#{name}/new", :action => 'new')
|
38
|
+
map.named_route(name, "#{name}/index", :action => 'index')
|
39
|
+
map.named_route("show_#{name.singularize}", "#{name}/:id/show", :action => 'show', :id => /\d+/)
|
40
|
+
map.named_route("create_#{name.singularize}", "#{name}/create", :action => 'create')
|
41
|
+
map.named_route("edit_#{name.singularize}", "#{name}/:id/edit", :action => 'edit', :id => /\d+/)
|
42
|
+
map.named_route("update_#{name.singularize}", "#{name}/:id/update", :action => 'update', :id => /\d+/)
|
43
|
+
map.named_route("destroy_#{name.singularize}", "#{name}/:id/destroy", :action => 'destroy', :id => /\d+/)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Facebooker
|
2
|
+
module Rails
|
3
|
+
module TestHelpers
|
4
|
+
def assert_fb_redirect_to(url)
|
5
|
+
assert_equal "fb:redirect", response_from_page_or_rjs.children.first.name
|
6
|
+
assert_equal url, response_from_page_or_rjs.children.first.attributes['url']
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Facebooker
|
2
|
+
module Rails
|
3
|
+
class Utilities
|
4
|
+
class << self
|
5
|
+
def refresh_all_images(session)
|
6
|
+
Dir.glob(File.join(RAILS_ROOT,"public","images","*.{png,jpg,gif}")).each do |img|
|
7
|
+
refresh_image(session,img)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def refresh_image(session,full_path)
|
12
|
+
basename=File.basename(full_path)
|
13
|
+
base_path=ActionController::Base.asset_host
|
14
|
+
base_path += "/" unless base_path.ends_with?("/")
|
15
|
+
image_path=base_path+"images/#{basename}"
|
16
|
+
puts "refreshing: #{image_path}"
|
17
|
+
session.server_cache.refresh_img_src(image_path)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Facebooker
|
2
|
+
class ServerCache
|
3
|
+
def initialize(session)
|
4
|
+
@session = session
|
5
|
+
end
|
6
|
+
|
7
|
+
#
|
8
|
+
# Stores an FBML reference on the server for use
|
9
|
+
# across multiple users in FBML
|
10
|
+
def set_ref_handle(handle_name, fbml_source)
|
11
|
+
(@session.post 'facebook.fbml.setRefHandle', :handle => handle_name, :fbml => fbml_source) == '1'
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Fetches and re-caches the content stored at the given URL, for use in a fb:ref FBML tag.
|
16
|
+
def refresh_ref_url(url)
|
17
|
+
(@session.post 'facebook.fbml.refreshRefUrl', :url => url) == '1'
|
18
|
+
end
|
19
|
+
|
20
|
+
def refresh_img_src(url)
|
21
|
+
(@session.post 'facebook.fbml.refreshImgSrc', :url => url) == '1'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'facebooker/parser'
|
3
|
+
module Facebooker
|
4
|
+
class Service
|
5
|
+
def initialize(api_base, api_path, api_key)
|
6
|
+
@api_base = api_base
|
7
|
+
@api_path = api_path
|
8
|
+
@api_key = api_key
|
9
|
+
end
|
10
|
+
|
11
|
+
# TODO: support ssl
|
12
|
+
def post(params)
|
13
|
+
Parser.parse(params[:method], Net::HTTP.post_form(url, params))
|
14
|
+
end
|
15
|
+
|
16
|
+
def post_file(params)
|
17
|
+
Parser.parse(params[:method], Net::HTTP.post_multipart_form(url, params))
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def url
|
22
|
+
URI.parse('http://'+ @api_base + @api_path)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,385 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
module Facebooker
|
5
|
+
#
|
6
|
+
# Raised when trying to perform an operation on a user
|
7
|
+
# other than the logged in user (if that's unallowed)
|
8
|
+
class NonSessionUser < StandardError; end
|
9
|
+
class Session
|
10
|
+
class SessionExpired < StandardError; end
|
11
|
+
class UnknownError < StandardError; end
|
12
|
+
class ServiceUnavailable < StandardError; end
|
13
|
+
class MaxRequestsDepleted < StandardError; end
|
14
|
+
class HostNotAllowed < StandardError; end
|
15
|
+
class MissingOrInvalidParameter < StandardError; end
|
16
|
+
class InvalidAPIKey < StandardError; end
|
17
|
+
class SessionExpired < StandardError; end
|
18
|
+
class CallOutOfOrder < StandardError; end
|
19
|
+
class IncorrectSignature < StandardError; end
|
20
|
+
class SignatureTooOld < StandardError; end
|
21
|
+
class TooManyUserCalls < StandardError; end
|
22
|
+
class TooManyUserActionCalls < StandardError; end
|
23
|
+
class InvalidFeedTitleLink < StandardError; end
|
24
|
+
class InvalidFeedTitleLength < StandardError; end
|
25
|
+
class InvalidFeedTitleName < StandardError; end
|
26
|
+
class BlankFeedTitle < StandardError; end
|
27
|
+
class FeedBodyLengthTooLong < StandardError; end
|
28
|
+
class InvalidFeedPhotoSource < StandardError; end
|
29
|
+
class InvalidFeedPhotoLink < StandardError; end
|
30
|
+
class FeedMarkupInvalid < StandardError; end
|
31
|
+
class FeedTitleDataInvalid < StandardError; end
|
32
|
+
class FeedTitleTemplateInvalid < StandardError; end
|
33
|
+
class FeedBodyDataInvalid < StandardError; end
|
34
|
+
class FeedBodyTemplateInvalid < StandardError; end
|
35
|
+
class FeedPhotosNotRetrieved < StandardError; end
|
36
|
+
class FeedTargetIdsInvalid < StandardError; end
|
37
|
+
class ConfigurationMissing < StandardError; end
|
38
|
+
class FQLParseError < StandardError; end
|
39
|
+
class FQLFieldDoesNotExist < StandardError; end
|
40
|
+
class FQLTableDoesNotExist < StandardError; end
|
41
|
+
class FQLStatementNotIndexable < StandardError; end
|
42
|
+
class FQLFunctionDoesNotExist < StandardError; end
|
43
|
+
class FQLWrongNumberArgumentsPassedToFunction < StandardError; end
|
44
|
+
class InvalidAlbumId < StandardError; end
|
45
|
+
class AlbumIsFull < StandardError; end
|
46
|
+
class MissingOrInvalidImageFile < StandardError; end
|
47
|
+
class TooManyUnapprovedPhotosPending < StandardError; end
|
48
|
+
|
49
|
+
API_SERVER_BASE_URL = "api.facebook.com"
|
50
|
+
API_PATH_REST = "/restserver.php"
|
51
|
+
WWW_SERVER_BASE_URL = "www.facebook.com"
|
52
|
+
WWW_PATH_LOGIN = "/login.php"
|
53
|
+
WWW_PATH_ADD = "/add.php"
|
54
|
+
WWW_PATH_INSTALL = "/install.php"
|
55
|
+
|
56
|
+
attr_writer :auth_token
|
57
|
+
attr_reader :session_key
|
58
|
+
|
59
|
+
def self.create(api_key=nil, secret_key=nil)
|
60
|
+
api_key ||= self.api_key
|
61
|
+
secret_key ||= self.secret_key
|
62
|
+
raise ArgumentError unless !api_key.nil? && !secret_key.nil?
|
63
|
+
new(api_key, secret_key)
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.api_key
|
67
|
+
extract_key_from_environment(:api) || extract_key_from_configuration_file(:api) rescue report_inability_to_find_key(:api)
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.secret_key
|
71
|
+
extract_key_from_environment(:secret) || extract_key_from_configuration_file(:secret) rescue report_inability_to_find_key(:secret)
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.current
|
75
|
+
@current_session
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.current=(session)
|
79
|
+
@current_session=session
|
80
|
+
end
|
81
|
+
|
82
|
+
def login_url(options={})
|
83
|
+
options = default_login_url_options.merge(options)
|
84
|
+
"http://www.facebook.com/login.php?api_key=#{@api_key}&v=1.0#{login_url_optional_parameters(options)}"
|
85
|
+
end
|
86
|
+
|
87
|
+
def install_url(options={})
|
88
|
+
"http://www.facebook.com/install.php?api_key=#{@api_key}&v=1.0#{install_url_optional_parameters(options)}"
|
89
|
+
end
|
90
|
+
|
91
|
+
def install_url_optional_parameters(options)
|
92
|
+
optional_parameters = []
|
93
|
+
optional_parameters << "&next=#{CGI.escape(options[:next])}" if options[:next]
|
94
|
+
optional_parameters.join
|
95
|
+
end
|
96
|
+
|
97
|
+
def login_url_optional_parameters(options)
|
98
|
+
# It is important that unused options are omitted as stuff like &canvas=false will still display the canvas.
|
99
|
+
optional_parameters = []
|
100
|
+
optional_parameters << "&next=#{CGI.escape(options[:next])}" if options[:next]
|
101
|
+
optional_parameters << "&skipcookie=true" if options[:skip_cookie]
|
102
|
+
optional_parameters << "&hide_checkbox=true" if options[:hide_checkbox]
|
103
|
+
optional_parameters << "&canvas=true" if options[:canvas]
|
104
|
+
optional_parameters.join
|
105
|
+
end
|
106
|
+
|
107
|
+
def default_login_url_options
|
108
|
+
{}
|
109
|
+
end
|
110
|
+
|
111
|
+
def initialize(api_key, secret_key)
|
112
|
+
@api_key = api_key
|
113
|
+
@secret_key = secret_key
|
114
|
+
end
|
115
|
+
|
116
|
+
def secret_for_method(method_name)
|
117
|
+
@secret_key
|
118
|
+
end
|
119
|
+
|
120
|
+
def auth_token
|
121
|
+
@auth_token ||= post 'facebook.auth.createToken'
|
122
|
+
end
|
123
|
+
|
124
|
+
def infinite?
|
125
|
+
@expires == 0
|
126
|
+
end
|
127
|
+
|
128
|
+
def expired?
|
129
|
+
@expires.nil? || (!infinite? && Time.at(@expires) <= Time.now)
|
130
|
+
end
|
131
|
+
|
132
|
+
def secured?
|
133
|
+
!@session_key.nil? && !expired?
|
134
|
+
end
|
135
|
+
|
136
|
+
def secure!
|
137
|
+
response = post 'facebook.auth.getSession', :auth_token => auth_token
|
138
|
+
secure_with!(response['session_key'], response['uid'], response['expires'], response['secret'])
|
139
|
+
end
|
140
|
+
|
141
|
+
def secure_with!(session_key, uid, expires, secret_from_session = nil)
|
142
|
+
@session_key = session_key
|
143
|
+
@uid = Integer(uid)
|
144
|
+
@expires = Integer(expires)
|
145
|
+
@secret_from_session = secret_from_session
|
146
|
+
end
|
147
|
+
|
148
|
+
def fql_query(query, format = 'XML')
|
149
|
+
response = post('facebook.fql.query', :query => query, :format => format)
|
150
|
+
type = response.shift
|
151
|
+
response.shift.map do |hash|
|
152
|
+
case type
|
153
|
+
when 'user'
|
154
|
+
user = User.new
|
155
|
+
user.session = self
|
156
|
+
user.populate_from_hash!(hash)
|
157
|
+
user
|
158
|
+
when 'photo'
|
159
|
+
Photo.from_hash(hash)
|
160
|
+
when 'event_member'
|
161
|
+
Event::Attendance.from_hash(hash)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def user
|
167
|
+
@user ||= User.new(uid, self)
|
168
|
+
end
|
169
|
+
|
170
|
+
#
|
171
|
+
# This one has so many parameters, a Hash seemed cleaner than a long param list. Options can be:
|
172
|
+
# :uid => Filter by events associated with a user with this uid
|
173
|
+
# :eids => Filter by this list of event ids. This is a comma-separated list of eids.
|
174
|
+
# :start_time => Filter with this UTC as lower bound. A missing or zero parameter indicates no lower bound. (Time or Integer)
|
175
|
+
# :end_time => Filter with this UTC as upper bound. A missing or zero parameter indicates no upper bound. (Time or Integer)
|
176
|
+
# :rsvp_status => Filter by this RSVP status.
|
177
|
+
def events(options = {})
|
178
|
+
@events ||= post('facebook.events.get', options).map do |hash|
|
179
|
+
Event.from_hash(hash)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def event_members(eid)
|
184
|
+
@members ||= post('facebook.events.getMembers', :eid => eid).map do |attendee_hash|
|
185
|
+
Event::Attendance.from_hash(attendee_hash)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
#
|
191
|
+
# Returns a proxy object for handling calls to Facebook cached items
|
192
|
+
# such as images and FBML ref handles
|
193
|
+
def server_cache
|
194
|
+
Facebooker::ServerCache.new(self)
|
195
|
+
end
|
196
|
+
|
197
|
+
#
|
198
|
+
# Returns a proxy object for handling calls to the Facebook Data API
|
199
|
+
def data
|
200
|
+
Facebooker::Data.new(self)
|
201
|
+
end
|
202
|
+
|
203
|
+
#
|
204
|
+
# Given an array like:
|
205
|
+
# [[userid, otheruserid], [yetanotherid, andanotherid]]
|
206
|
+
# returns a Hash indicating friendship of those pairs:
|
207
|
+
# {[userid, otheruserid] => true, [yetanotherid, andanotherid] => false}
|
208
|
+
# if one of the Hash values is nil, it means the facebook platform's answer is "I don't know"
|
209
|
+
def check_friendship(array_of_pairs_of_users)
|
210
|
+
uids1 = []
|
211
|
+
uids2 = []
|
212
|
+
array_of_pairs_of_users.each do |pair|
|
213
|
+
uids1 = pair.first
|
214
|
+
uids2 = pair.last
|
215
|
+
end
|
216
|
+
post('facebook.friends.areFriends', :uids1 => uids1, :uids2 => uids2)
|
217
|
+
end
|
218
|
+
|
219
|
+
def get_photos(pids = nil, subj_id = nil, aid = nil)
|
220
|
+
if [subj_id, pids, aid].all? {|arg| arg.nil?}
|
221
|
+
raise ArgumentError, "Can't get a photo without a picture, album or subject ID"
|
222
|
+
end
|
223
|
+
@photos = post('facebook.photos.get', :subj_id => subj_id, :pids => pids, :aid => aid ).map do |hash|
|
224
|
+
Photo.from_hash(hash)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def get_albums(aids)
|
229
|
+
@albums = post('facebook.photos.getAlbums', :aids => aids).map do |hash|
|
230
|
+
Album.from_hash(hash)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def get_tags(pids)
|
235
|
+
@tags = post('facebook.photos.getTags', :pids => pids).map do |hash|
|
236
|
+
Tag.from_hash(hash)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def add_tags(pid, x, y, tag_uid = nil, tag_text = nil )
|
241
|
+
if [tag_uid, tag_text].all? {|arg| arg.nil?}
|
242
|
+
raise ArgumentError, "Must enter a name or string for this tag"
|
243
|
+
end
|
244
|
+
@tags = post('facebook.photos.addTag', :pid => pid, :tag_uid => tag_uid, :tag_text => tag_text, :x => x, :y => y )
|
245
|
+
end
|
246
|
+
|
247
|
+
def send_notification(user_ids, fbml, email_fbml = nil)
|
248
|
+
params = {:notification => fbml, :to_ids => user_ids.map{ |id| User.cast_to_facebook_id(id)}.join(',')}
|
249
|
+
if email_fbml
|
250
|
+
params[:email] = email_fbml
|
251
|
+
end
|
252
|
+
post 'facebook.notifications.send', params
|
253
|
+
end
|
254
|
+
|
255
|
+
##
|
256
|
+
# Send email to as many as 100 users at a time
|
257
|
+
def send_email(user_ids, subject, text, fbml = nil)
|
258
|
+
user_ids = Array(user_ids)
|
259
|
+
params = {:fbml => fbml, :recipients => user_ids.map{ |id| User.cast_to_facebook_id(id)}.join(','), :text => text, :subject => subject}
|
260
|
+
post 'facebook.notifications.sendEmail', params
|
261
|
+
end
|
262
|
+
|
263
|
+
# Only serialize the bare minimum to recreate the session.
|
264
|
+
def marshal_load(variables)#:nodoc:
|
265
|
+
fields_to_serialize.each_with_index{|field, index| instance_variable_set_value(field, variables[index])}
|
266
|
+
end
|
267
|
+
|
268
|
+
# Only serialize the bare minimum to recreate the session.
|
269
|
+
def marshal_dump#:nodoc:
|
270
|
+
fields_to_serialize.map{|field| instance_variable_value(field)}
|
271
|
+
end
|
272
|
+
|
273
|
+
# Only serialize the bare minimum to recreate the session.
|
274
|
+
def to_yaml( opts = {} )
|
275
|
+
YAML::quick_emit(self.object_id, opts) do |out|
|
276
|
+
out.map(taguri) do |map|
|
277
|
+
fields_to_serialize.each do |field|
|
278
|
+
map.add(field, instance_variable_value(field))
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def instance_variable_set_value(field, value)
|
285
|
+
self.instance_variable_set("@#{field}", value)
|
286
|
+
end
|
287
|
+
|
288
|
+
def instance_variable_value(field)
|
289
|
+
self.instance_variable_get("@#{field}")
|
290
|
+
end
|
291
|
+
|
292
|
+
def fields_to_serialize
|
293
|
+
%w(session_key uid expires secret_from_session auth_token api_key secret_key)
|
294
|
+
end
|
295
|
+
|
296
|
+
class Desktop < Session
|
297
|
+
def login_url
|
298
|
+
super + "&auth_token=#{auth_token}"
|
299
|
+
end
|
300
|
+
|
301
|
+
def secret_for_method(method_name)
|
302
|
+
secret = auth_request_methods.include?(method_name) ? super : @secret_from_session
|
303
|
+
secret
|
304
|
+
end
|
305
|
+
|
306
|
+
def post(method, params = {})
|
307
|
+
if method == 'facebook.profile.getFBML' || method == 'facebook.profile.setFBML'
|
308
|
+
raise NonSessionUser.new("User #{@uid} is not the logged in user.") unless @uid == params[:uid]
|
309
|
+
end
|
310
|
+
super
|
311
|
+
end
|
312
|
+
private
|
313
|
+
def auth_request_methods
|
314
|
+
['facebook.auth.getSession', 'facebook.auth.createToken']
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
def post(method, params = {})
|
319
|
+
add_facebook_params(params, method)
|
320
|
+
@session_key && params[:session_key] ||= @session_key
|
321
|
+
service.post(params.merge(:sig => signature_for(params)))
|
322
|
+
end
|
323
|
+
|
324
|
+
def post_file(method, params = {})
|
325
|
+
add_facebook_params(params, method)
|
326
|
+
@session_key && params[:session_key] ||= @session_key
|
327
|
+
service.post_file(params.merge(:sig => signature_for(params.reject{|key, value| key.nil?})))
|
328
|
+
end
|
329
|
+
|
330
|
+
|
331
|
+
def self.configuration_file_path
|
332
|
+
@configuration_file_path || File.expand_path("~/.facebookerrc")
|
333
|
+
end
|
334
|
+
|
335
|
+
def self.configuration_file_path=(path)
|
336
|
+
@configuration_file_path = path
|
337
|
+
end
|
338
|
+
|
339
|
+
private
|
340
|
+
def add_facebook_params(hash, method)
|
341
|
+
hash[:method] = method
|
342
|
+
hash[:api_key] = @api_key
|
343
|
+
hash[:call_id] = Time.now.to_f.to_s unless method == 'facebook.auth.getSession'
|
344
|
+
hash[:v] = "1.0"
|
345
|
+
end
|
346
|
+
|
347
|
+
def self.extract_key_from_environment(key_name)
|
348
|
+
val = ENV["FACEBOOK_" + key_name.to_s.upcase + "_KEY"]
|
349
|
+
end
|
350
|
+
|
351
|
+
def self.extract_key_from_configuration_file(key_name)
|
352
|
+
read_configuration_file[key_name]
|
353
|
+
end
|
354
|
+
|
355
|
+
def self.report_inability_to_find_key(key_name)
|
356
|
+
raise ConfigurationMissing, "Could not find configuration information for #{key_name}"
|
357
|
+
end
|
358
|
+
|
359
|
+
def self.read_configuration_file
|
360
|
+
eval(File.read(configuration_file_path))
|
361
|
+
end
|
362
|
+
|
363
|
+
def service
|
364
|
+
@service ||= Service.new(API_SERVER_BASE_URL, API_PATH_REST, @api_key)
|
365
|
+
end
|
366
|
+
|
367
|
+
def uid
|
368
|
+
@uid || (secure!; @uid)
|
369
|
+
end
|
370
|
+
|
371
|
+
def signature_for(params)
|
372
|
+
raw_string = params.inject([]) do |collection, pair|
|
373
|
+
collection << pair.join("=")
|
374
|
+
collection
|
375
|
+
end.sort.join
|
376
|
+
Digest::MD5.hexdigest([raw_string, secret_for_method(params[:method])].join)
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
class CanvasSession < Session
|
381
|
+
def default_login_url_options
|
382
|
+
{:canvas => true}
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|