rfacebook 0.5.1 → 0.6.0

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/README CHANGED
@@ -1,3 +1,78 @@
1
+ ============================== FACEBOOK RAILS CONTROLLER EXTENSIONS NOTES ==============================
2
+
3
+ Rails Applications:
4
+
5
+ First, set up your Facebook Developer account properly. By properly,
6
+ I mean that you need to set your callback URL to be the root of your
7
+ Rails application. For example:
8
+
9
+ Callback URL >>> http://www.mycrazywebsite.com/ ***NOTE: trailing slash is necessary
10
+ Canvas Page URL >>> http://apps.facebook.com/mycrazywebsite
11
+
12
+ Then, whenever a user visits
13
+
14
+ http://apps.facebook.com/mycrazywebsite/someRailsController/someAction
15
+
16
+ ...the Facebook API will call:
17
+
18
+ http://www.mycrazywebsite.com/someRailsController/someAction
19
+
20
+
21
+ You can get this behavior by installing the new RFacebook gem. After
22
+ installing the gem, make your ApplicationController look like this:
23
+
24
+ >>>>>>>>>>>> require "facebook_rails_controller_extensions"
25
+ >>>>>>>>>>>>
26
+ >>>>>>>>>>>> class ApplicationController < ActionController::Base
27
+ >>>>>>>>>>>>
28
+ >>>>>>>>>>>> # additions to integrate Facebook into controllers
29
+ >>>>>>>>>>>> include RFacebook::RailsControllerExtensions
30
+ >>>>>>>>>>>>
31
+ >>>>>>>>>>>> def facebook_api_key
32
+ >>>>>>>>>>>> return "<YOUR API KEY HERE>"
33
+ >>>>>>>>>>>> end
34
+ >>>>>>>>>>>>
35
+ >>>>>>>>>>>> def facebook_api_secret
36
+ >>>>>>>>>>>> return "<YOUR API SECRET HERE>"
37
+ >>>>>>>>>>>> end
38
+ >>>>>>>>>>>>
39
+ >>>>>>>>>>>> end
40
+
41
+ You will have access to 2 new items in your controllers:
42
+
43
+ (1) "fbsession"
44
+ Contains a FacebookWebSession. Will return false for "is_valid?" if you haven't forced the user to log in.
45
+
46
+
47
+ (2) "fbparams"
48
+ Contains all the params that are prefixed with "fb_sig_". You shouldn't ever *really* need this, but its there just in case.
49
+
50
+
51
+ And, you have a few new filters to try out:
52
+
53
+ (1) require_facebook_login
54
+ (2) require_facebook_install
55
+
56
+ For example, one of my Rails controllers looks like this:
57
+
58
+ >>>>>>>>>>>> class FacebookController < ApplicationController
59
+ >>>>>>>>>>>>
60
+ >>>>>>>>>>>> before_filter :require_facebook_install # require users to install the application (less intrusive is to require_facebook_login)
61
+ >>>>>>>>>>>>
62
+ >>>>>>>>>>>> def index
63
+ >>>>>>>>>>>> xml = fbsession.users_getInfo(:uids => [fbsession.session_user_id], :fields => ["first_name", "last_name"])
64
+ >>>>>>>>>>>> @firstName = xml.at("first_name").inner_html
65
+ >>>>>>>>>>>> @lastName = xml.at("last_name").inner_html
66
+ >>>>>>>>>>>> end
67
+ >>>>>>>>>>>>
68
+ >>>>>>>>>>>>
69
+ >>>>>>>>>>>> end
70
+
71
+
72
+
73
+ ============================== FACEBOOK PLATFORM NOTES ==============================
74
+
75
+
1
76
  ============================== IMPORTANT NOTES ==============================
2
77
 
3
78
 
@@ -64,7 +64,7 @@ class FacebookDesktopSession < FacebookSession
64
64
  optionalHideCheckbox = (hidecheckbox == true) ? "&hide_checkbox=true" : ""
65
65
 
66
66
  # build and return URL
67
- return "http://#{LOGIN_SERVER_BASE_URL}#{LOGIN_SERVER_PATH}?v=1.0&api_key=#{@api_key}&auth_token=#{@desktop_auth_token}#{optionalPopup}#{optionalNext}#{optionalSkipCookie}#{optionalHideCheckbox}"
67
+ return "http://#{WWW_SERVER_BASE_URL}#{WWW_PATH_LOGIN}?v=1.0&api_key=#{@api_key}&auth_token=#{@desktop_auth_token}#{optionalPopup}#{optionalNext}#{optionalSkipCookie}#{optionalHideCheckbox}"
68
68
  end
69
69
 
70
70
 
@@ -97,13 +97,21 @@ class FacebookDesktopSession < FacebookSession
97
97
 
98
98
  # determine the current user's id
99
99
  result = call_method("users.getLoggedInUser")
100
- @session_uid = result.at("users_getLoggedInUser_response").inner_html
100
+ @session_user_id = result.at("users_getLoggedInUser_response").inner_html
101
101
  end
102
102
 
103
103
  def is_valid?
104
104
  return (is_activated? and !session_expired?)
105
105
  end
106
106
 
107
+ def session_user_id
108
+ return @session_user_id
109
+ end
110
+
111
+ def session_key
112
+ return @session_key
113
+ end
114
+
107
115
  protected
108
116
 
109
117
  def is_activated?
@@ -118,7 +126,7 @@ class FacebookDesktopSession < FacebookSession
118
126
  def activate_with_token(auth_token)
119
127
  result = call_method("auth.getSession", {:auth_token => auth_token}, true)
120
128
  if result != nil
121
- @session_uid = result.at("uid").inner_html
129
+ @session_user_id = result.at("uid").inner_html
122
130
  @session_key = result.at("session_key").inner_html
123
131
  @session_secret = result.at("secret").inner_html
124
132
  end
@@ -0,0 +1,120 @@
1
+ require "facebook_web_session"
2
+
3
+ module RFacebook
4
+
5
+ module RailsControllerExtensions
6
+
7
+
8
+ # SECTION: Exceptions
9
+
10
+ class APIKeyNeededException < Exception; end
11
+ class APISecretNeededException < Exception; end
12
+
13
+ # SECTION: Template Methods (must be implemented by concrete subclass)
14
+
15
+ def facebook_api_key
16
+ raise APIKeyNeededException
17
+ end
18
+
19
+ def facebook_api_secret
20
+ raise APISecretNeededException
21
+ end
22
+
23
+
24
+
25
+ # SECTION: Required Methods
26
+
27
+ def fbparams
28
+
29
+ @fbparams ||= {};
30
+
31
+ # try to get fbparams from the params hash
32
+ if (@fbparams.length <= 0)
33
+ @fbparams = fbsession.get_fb_sig_params(params)
34
+ end
35
+
36
+ # else, try to get fbparams from the cookies hash
37
+ # TODO: we don't write anything into the cookie, so this is kind of pointless right now
38
+ if (@fbparams.length <= 0)
39
+ @fbparams = fbsession.get_fb_sig_params(cookies)
40
+ end
41
+
42
+ return @fbparams
43
+
44
+ end
45
+
46
+ def fbsession
47
+
48
+ if !@fbsession
49
+
50
+ # create a Facebook session that can be used by the controller
51
+ @fbsession = FacebookWebSession.new(facebook_api_key, facebook_api_secret)
52
+
53
+ # now we need to activate the session somehow. If the signature parameters are bad, then we don't make the session
54
+ if fbparams
55
+ # these might be nil
56
+ facebookUid = fbparams["user"]
57
+ facebookSessionKey = fbparams["session_key"]
58
+ expirationTime = fbparams["expires"]
59
+
60
+ # Method 1: user logged in and was redirected to our site (iframe/external)
61
+ if ( params["auth_token"] )
62
+ @fbsession.activate_with_token(params["auth_token"])
63
+ # Method 2: we have the user id and key from the fb_sig_ params
64
+ elsif (facebookUid and facebookSessionKey and expirationTime)
65
+ @fbsession.activate_with_previous_session(facebookSessionKey, facebookUid, expirationTime)
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+ return @fbsession
72
+
73
+ end
74
+
75
+ # SECTION: Helpful Methods
76
+
77
+ def facebook_redirect_to(url)
78
+
79
+ if (in_facebook_canvas? and !in_facebook_iframe?)
80
+ render :text => "<fb:redirect url=\"#{url}\" />"
81
+
82
+ elsif url =~ /^https?:\/\/([^\/]*\.)?facebook\.com(:\d+)?/i # TODO: why doesn't this just check for iframe?
83
+ render :text => "<script type=\"text/javascript\">\ntop.location.href = \"#{url}\";\n</script>"
84
+
85
+ else
86
+ redirect_to url
87
+
88
+ end
89
+
90
+ end
91
+
92
+ def in_facebook_canvas?
93
+ return (fbparams["in_fbframe"] != nil)
94
+ end
95
+
96
+ def in_facebook_iframe?
97
+ return (fbparams["in_iframe"] != nil)
98
+ end
99
+
100
+ def require_facebook_login
101
+ sess = fbsession
102
+ if (sess and !sess.is_valid?)
103
+ if in_facebook_canvas?
104
+ render :text => "<fb:redirect url=\"#{sess.get_login_url(:canvas=>true)}\" />"
105
+ else
106
+ redirect_to sess.get_login_url
107
+ end
108
+ end
109
+ end
110
+
111
+ def require_facebook_install
112
+ sess = fbsession
113
+ if (sess and !sess.is_valid? and in_facebook_canvas?)
114
+ render :text => "<fb:redirect url=\"#{sess.get_install_url(:canvas=>true)}\" />"
115
+ end
116
+ end
117
+
118
+ end
119
+
120
+ end
@@ -39,14 +39,17 @@ require "hpricot"
39
39
 
40
40
  module RFacebook
41
41
 
42
- API_SERVER_BASE_URL = "api.facebook.com"
43
- LOGIN_SERVER_BASE_URL = "www.facebook.com"
44
- API_SERVER_PATH = "/restserver.php"
45
- LOGIN_SERVER_PATH = "/login.php"
42
+ API_SERVER_BASE_URL = "api.f8.facebook.com"
43
+ API_PATH_REST = "/restserver.php"
44
+
45
+ WWW_SERVER_BASE_URL = "www.f8.facebook.com"
46
+ WWW_PATH_LOGIN = "/login.php"
47
+ WWW_PATH_ADD = "/add.php"
48
+ WWW_PATH_INSTALL = "/install.php"
46
49
 
47
50
  class FacebookSession
48
51
 
49
- attr_reader :session_uid, :session_key
52
+ # error handling accessors
50
53
  attr_reader :last_call_was_successful, :last_error
51
54
  attr_writer :suppress_exceptions
52
55
 
@@ -73,7 +76,7 @@ class FacebookSession
73
76
 
74
77
  # calculated parameters
75
78
  @api_server_base_url = API_SERVER_BASE_URL
76
- @api_server_path = API_SERVER_PATH
79
+ @api_server_path = API_PATH_REST
77
80
 
78
81
  # optional parameters
79
82
  @suppress_exceptions = suppress_exceptions
@@ -83,37 +86,42 @@ class FacebookSession
83
86
  @last_error = nil
84
87
  @session_expired = false
85
88
 
86
- # virtual members (subclasses will set these)
87
- @session_uid = nil
88
- @session_key = nil
89
-
90
89
  end
91
90
 
92
91
  def session_expired?
93
92
  return (@session_expired == true)
94
93
  end
95
-
96
- protected
97
-
98
- # SECTION: Protected Abstract Interface
94
+
95
+ # SECTION: Public Abstract Interface
96
+
97
+ def is_valid?
98
+ raise Exception
99
+ end
99
100
 
100
- def get_secret(params)
101
+ def session_key
101
102
  raise Exception
102
103
  end
103
104
 
104
- def init_with_token(auth_token)
105
+ def session_user_id
105
106
  raise Exception
106
107
  end
107
108
 
108
- def session_key=(key)
109
+ def session_expires
109
110
  raise Exception
110
111
  end
111
112
 
112
- def is_activated?
113
+ def session_uid # deprecated
114
+ return session_user_id
115
+ end
116
+
117
+ # SECTION: Protected Abstract Interface
118
+ protected
119
+
120
+ def get_secret(params)
113
121
  raise Exception
114
122
  end
115
123
 
116
- def is_valid?
124
+ def is_activated?
117
125
  raise Exception
118
126
  end
119
127
 
@@ -152,7 +160,7 @@ class FacebookSession
152
160
  params[:v] = "1.0"
153
161
 
154
162
  if (method != "auth.getSession" and method != "auth.createToken")
155
- params[:session_key] = @session_key
163
+ params[:session_key] = session_key
156
164
  params[:call_id] = Time.now.to_f.to_s
157
165
  end
158
166
 
@@ -175,7 +183,7 @@ class FacebookSession
175
183
  @last_call_was_successful = false
176
184
  code = xml.at("error_code").inner_html
177
185
  msg = xml.at("error_msg").inner_html
178
- @last_error = "ERROR #{code}: #{msg} (#{method}, #{params})"
186
+ @last_error = "ERROR #{code}: #{msg} (#{method.inspect}, #{params.inspect})"
179
187
  @last_error_code = code
180
188
 
181
189
  # check to see if this error was an expired session error
@@ -227,16 +235,19 @@ class FacebookSession
227
235
  # Function: param_signature
228
236
  # Generates a param_signature for a call to the API, per the spec on Facebook
229
237
  # see: <http://developers.facebook.com/documentation.php?v=1.0&doc=auth>
230
- def param_signature(params)
238
+ def param_signature(params)
239
+ return generate_signature(params, get_secret(params));
240
+ end
241
+
242
+ def generate_signature(hash, secret)
231
243
 
232
244
  args = []
233
- params.each do |k,v|
245
+ hash.each do |k,v|
234
246
  args << "#{k}=#{v}"
235
247
  end
236
- sorted_array = args.sort
237
- request_str = sorted_array.join("")
238
- param_signature = Digest::MD5.hexdigest("#{request_str}#{get_secret(params)}") # uses Template method get_secret
239
- return param_signature
248
+ sortedArray = args.sort
249
+ requestStr = sortedArray.join("")
250
+ return Digest::MD5.hexdigest("#{requestStr}#{secret}")
240
251
 
241
252
  end
242
253
 
@@ -38,77 +38,169 @@ module RFacebook
38
38
 
39
39
  class FacebookWebSession < FacebookSession
40
40
 
41
- # Function: get_login_url
42
- # Gets the authentication URL
43
- #
44
- # Parameters:
45
- # options.next - the page to redirect to after login
46
- # options.popup - boolean, whether or not to use the popup style (defaults to true)
47
- # options.skipcookie - boolean, whether to force new Facebook login (defaults to false)
48
- # options.hidecheckbox - boolean, whether to show the "infinite session" option checkbox
49
- def get_login_url(options={})
50
- # options
51
- path_next = options[:next] ||= nil
52
- popup = (options[:popup] == nil) ? true : false
53
- skipcookie = (options[:skipcookie] == nil) ? false : true
54
- hidecheckbox = (options[:hidecheckbox] == nil) ? false : true
41
+ # SECTION: Properties
55
42
 
56
- # get some extra portions of the URL
57
- optionalNext = (path_next == nil) ? "" : "&next=#{CGI.escape(path_next.to_s)}"
58
- optionalPopup = (popup == true) ? "&popup=true" : ""
59
- optionalSkipCookie = (skipcookie == true) ? "&skipcookie=true" : ""
60
- optionalHideCheckbox = (hidecheckbox == true) ? "&hide_checkbox=true" : ""
61
-
62
- # build and return URL
63
- return "http://#{LOGIN_SERVER_BASE_URL}#{LOGIN_SERVER_PATH}?v=1.0&api_key=#{@api_key}#{optionalPopup}#{optionalNext}#{optionalSkipCookie}#{optionalHideCheckbox}"
64
- end
43
+ def session_key
44
+ return @session_key
45
+ end
65
46
 
66
- # Function: activate_with_token
67
- # Gets the session information available after current user logs in.
68
- #
69
- # Parameters:
70
- # auth_token - string token passed back by the callback URL
71
- def activate_with_token(auth_token)
72
- result = call_method("auth.getSession", {:auth_token => auth_token})
73
- if result != nil
74
- @session_uid = result.at("uid").inner_html
75
- @session_key = result.at("session_key").inner_html
47
+ def session_user_id
48
+ return @session_uid
76
49
  end
77
- end
78
50
 
79
- # Function: activate_with_previous_session
80
- # Sets the session key directly (for example, if you have an infinite session key)
81
- #
82
- # Parameters:
83
- # key - the session key to use
84
- def activate_with_previous_session(key)
85
- # set the session key
86
- @session_key = key
51
+ def session_expires
52
+ return @session_expires
53
+ end
87
54
 
88
- # determine the current user's id
89
- result = call_method("users.getLoggedInUser")
90
- @session_uid = result.at("users_getLoggedInUser_response").inner_html
91
- end
55
+ # SECTION: URL Getters
56
+
57
+ # Function: get_login_url
58
+ # Gets the authentication URL
59
+ #
60
+ # Parameters:
61
+ # options.next - the page to redirect to after login
62
+ # options.popup - boolean, whether or not to use the popup style (defaults to true)
63
+ # options.skipcookie - boolean, whether to force new Facebook login (defaults to false)
64
+ # options.hidecheckbox - boolean, whether to show the "infinite session" option checkbox
65
+ def get_login_url(options={})
66
+
67
+ # handle options
68
+ nextPage = options[:next] ||= nil
69
+ popup = (options[:popup] == nil) ? false : true
70
+ skipcookie = (options[:skipcookie] == nil) ? false : true
71
+ hidecheckbox = (options[:hidecheckbox] == nil) ? false : true
72
+ frame = (options[:frame] == nil) ? false : true
73
+ canvas = (options[:canvas] == nil) ? false : true
74
+
75
+ # url pieces
76
+ optionalNext = (nextPage == nil) ? "" : "&next=#{CGI.escape(nextPage.to_s)}"
77
+ optionalPopup = (popup == true) ? "&popup=true" : ""
78
+ optionalSkipCookie = (skipcookie == true) ? "&skipcookie=true" : ""
79
+ optionalHideCheckbox = (hidecheckbox == true) ? "&hide_checkbox=true" : ""
80
+ optionalFrame = (frame == true) ? "&fbframe=true" : ""
81
+ optionalCanvas = (canvas == true) ? "&canvas=true" : ""
82
+
83
+ # build and return URL
84
+ return "http://#{WWW_SERVER_BASE_URL}#{WWW_PATH_LOGIN}?v=1.0&api_key=#{@api_key}#{optionalPopup}#{optionalNext}#{optionalSkipCookie}#{optionalHideCheckbox}#{optionalFrame}#{optionalCanvas}"
85
+
86
+ end
87
+
88
+ def get_install_url(options={})
89
+
90
+ # handle options
91
+ nextPage = options[:next] ||= nil
92
+
93
+ # url pieces
94
+ optionalNext = (nextPage == nil) ? "" : "&next=#{CGI.escape(nextPage.to_s)}"
95
+
96
+ # build and return URL
97
+ return "http:/#{WWW_SERVER_BASE_URL}#{WWW_PATH_INSTALL}?api_key=#{@api_key}#{optionalNext}"
98
+
99
+ end
92
100
 
93
- def is_valid?
94
- return (is_activated? and !session_expired?)
95
- end
96
101
 
97
- protected
102
+ # SECTION: Callback Verification Helpers
103
+
104
+ def get_fb_sig_params(originalParams)
105
+
106
+ # setup
107
+ timeout = 48*3600
108
+ namespace = "fb_sig"
109
+ prefix = "#{namespace}_"
110
+
111
+ # get the params prefixed by "fb_sig_" (and remove the prefix)
112
+ sigParams = {}
113
+ originalParams.each do |k,v|
114
+ oldLen = k.length
115
+ newK = k.sub(prefix, "")
116
+ if oldLen != newK.length
117
+ sigParams[newK] = v
118
+ end
119
+ end
120
+
121
+ # handle invalidation
122
+ if (timeout and (sigParams["time"].nil? or (Time.now.to_i - sigParams["time"].to_i > timeout.to_i)))
123
+ # invalidate if the timeout has been reached
124
+ sigParams = {}
125
+ end
126
+
127
+ if !sig_params_valid?(sigParams, originalParams[namespace])
128
+ # invalidate if the signatures don't match
129
+ sigParams = {}
130
+ end
131
+
132
+ return sigParams
133
+
134
+ end
98
135
 
99
- def is_activated?
100
- return (@session_key != nil)
101
- end
102
136
 
103
- # Function: get_secret
104
- # Template method, used by super::signature to generate a signature
105
- def get_secret(params)
137
+
138
+ # SECTION: Session Activation
139
+
140
+ # Function: activate_with_token
141
+ # Gets the session information available after current user logs in.
142
+ #
143
+ # Parameters:
144
+ # auth_token - string token passed back by the callback URL
145
+ def activate_with_token(auth_token)
146
+ result = call_method("auth.getSession", {:auth_token => auth_token})
147
+ if result != nil
148
+ @session_uid = result.at("uid").inner_html
149
+ @session_key = result.at("session_key").inner_html
150
+ @session_expires = result.at("expires").inner_html
151
+ end
152
+ end
153
+
154
+ # Function: activate_with_previous_session
155
+ # Sets the session key directly (for example, if you have an infinite session key)
156
+ #
157
+ # Parameters:
158
+ # key - the session key to use
159
+ def activate_with_previous_session(key, uid=nil, expires=nil)
160
+
161
+ # TODO: what is a good way to handle expiration?
162
+ # low priority since the API will give an error code for me...
163
+
164
+ # set the session key
165
+ @session_key = key
106
166
 
107
- return @api_secret
167
+ # determine the current user's id
168
+ if uid
169
+ @session_uid = uid
170
+ else
171
+ result = call_method("users.getLoggedInUser")
172
+ @session_uid = result.at("users_getLoggedInUser_response").inner_html
173
+ end
174
+
175
+ end
176
+
177
+ def is_valid?
178
+ return (is_activated? and !session_expired?)
179
+ end
180
+
181
+
182
+
183
+
184
+
185
+
186
+ # SECTION: Protected methods
187
+ protected
188
+
189
+ def is_activated?
190
+ return (@session_key != nil)
191
+ end
192
+
193
+ # Function: get_secret
194
+ # Template method, used by super::signature to generate a signature
195
+ def get_secret(params)
196
+ return @api_secret
197
+ end
108
198
 
109
- end
199
+ def sig_params_valid?(sigParams, expectedSig)
200
+ return (sigParams and expectedSig and generate_signature(sigParams, @api_secret) == expectedSig)
201
+ end
110
202
 
111
- end
203
+ end
112
204
 
113
205
 
114
206
 
metadata CHANGED
@@ -1,11 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.0
2
+ rubygems_version: 0.9.1
3
3
  specification_version: 1
4
4
  name: rfacebook
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.5.1
7
- date: 2007-05-08 00:00:00 -04:00
8
- summary: A Ruby interface to the Facebook API v1.0+
6
+ version: 0.6.0
7
+ date: 2007-05-29 00:00:00 -07:00
8
+ summary: A Ruby interface to the Facebook API v1.0+. Supports the new features from F8.
9
9
  require_paths:
10
10
  - lib
11
11
  email: matt@livelearncode.com
@@ -30,6 +30,7 @@ authors:
30
30
  - Matt Pizzimenti
31
31
  files:
32
32
  - lib/facebook_desktop_session.rb
33
+ - lib/facebook_rails_controller_extensions.rb
33
34
  - lib/facebook_session.rb
34
35
  - lib/facebook_web_session.rb
35
36
  - README