clickonchris-facebooker2 0.0.7.x1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,50 @@
1
+ # Facebooker2
2
+ require "mogli"
3
+ module Facebooker2
4
+ class NotConfigured < Exception; end
5
+ class << self
6
+ attr_accessor :api_key, :secret, :app_id
7
+ end
8
+
9
+ def self.secret
10
+ @secret || raise_unconfigured_exception
11
+ end
12
+
13
+ def self.app_id
14
+ @app_id || raise_unconfigured_exception
15
+ end
16
+
17
+ def self.raise_unconfigured_exception
18
+ raise NotConfigured.new("No configuration provided for Facebooker2. Either set the app_id and secret or call Facebooker2.load_facebooker_yaml in an initializer")
19
+ end
20
+
21
+ def self.configuration=(hash)
22
+ self.api_key = hash[:api_key]
23
+ self.secret = hash[:secret]
24
+ self.app_id = hash[:app_id]
25
+ end
26
+
27
+ def self.load_facebooker_yaml
28
+ config = YAML.load(ERB.new(File.read(File.join(::Rails.root,"config","facebooker.yml"))).result)[::Rails.env]
29
+ raise NotConfigured.new("Unable to load configuration for #{::Rails.env} from facebooker.yml. Is it set up?") if config.nil?
30
+ self.configuration = config.with_indifferent_access
31
+ end
32
+
33
+ def self.cast_to_facebook_id(object)
34
+ if object.kind_of?(Mogli::Profile)
35
+ object.id
36
+ elsif object.respond_to?(:facebook_id)
37
+ object.facebook_id
38
+ else
39
+ object
40
+ end
41
+ end
42
+ end
43
+
44
+
45
+ require "facebooker2/rails/controller"
46
+ require "facebooker2/rails/helpers/facebook_connect"
47
+ require "facebooker2/rails/helpers/javascript"
48
+ require "facebooker2/rails/helpers/request_forms"
49
+ require "facebooker2/rails/helpers/user"
50
+ require "facebooker2/rails/helpers"
@@ -0,0 +1,4 @@
1
+ module Facebooker2
2
+ module Rails
3
+ end
4
+ end
@@ -0,0 +1,180 @@
1
+ require "digest/md5"
2
+ require "hmac-sha2"
3
+ module Facebooker2
4
+ module Rails
5
+ module Controller
6
+
7
+ def self.included(controller)
8
+ controller.helper Facebooker2::Rails::Helpers
9
+ controller.helper_method :current_facebook_user
10
+ controller.helper_method :current_facebook_client
11
+ controller.helper_method :facebook_params
12
+ end
13
+
14
+ def current_facebook_user
15
+ fetch_client_and_user
16
+ @_current_facebook_user
17
+ end
18
+
19
+ def current_facebook_client
20
+ fetch_client_and_user
21
+ @_current_facebook_client
22
+ end
23
+
24
+ # This mimics the getSession logic from the php facebook SDK
25
+ # https://github.com/facebook/php-sdk/blob/master/src/facebook.php#L333
26
+ #
27
+ def fetch_client_and_user
28
+ return if @_fb_user_fetched
29
+ # Try to authenticate from the signed request first
30
+ sig = fetch_client_and_user_from_signed_request
31
+ sig = fetch_client_and_user_from_cookie unless @_current_facebook_client
32
+
33
+ #write the authentication params to a new cookie
34
+ if !@_current_facebook_client.nil?
35
+ #we may have generated the signature based on the params in @facebook_params, and the expiration here is different
36
+
37
+ set_fb_cookie(@_current_facebook_client.access_token, @_current_facebook_client.expiration, @_current_facebook_user.id, sig)
38
+ else
39
+ # if we do not have a client, delete the cookie
40
+ set_fb_cookie(nil,nil,nil,nil)
41
+ end
42
+
43
+ @_fb_user_fetched = true
44
+ end
45
+
46
+ def fetch_client_and_user_from_cookie
47
+ if (hash_data = fb_cookie_hash) and
48
+ fb_cookie_signature_correct?(fb_cookie_hash,Facebooker2.secret)
49
+ fb_create_user_and_client(hash_data["access_token"],hash_data["expires"],hash_data["uid"])
50
+ return fb_cookie_hash["sig"]
51
+ end
52
+ end
53
+
54
+ def fb_create_user_and_client(token,expires,userid)
55
+ client = Mogli::Client.new(token,expires.to_i)
56
+ user = Mogli::User.new(:id=>userid)
57
+ fb_sign_in_user_and_client(user,client)
58
+ end
59
+
60
+ def fb_sign_in_user_and_client(user,client)
61
+ user.client = client
62
+ @_current_facebook_user = user
63
+ @_current_facebook_client = client
64
+ @_fb_user_fetched = true
65
+ end
66
+
67
+ def fb_cookie_hash
68
+ return nil unless fb_cookie?
69
+ hash={}
70
+ data = fb_cookie.gsub(/"/,"")
71
+ data.split("&").each do |str|
72
+ parts = str.split("=")
73
+ hash[parts.first] = parts.last
74
+ end
75
+ hash
76
+ end
77
+
78
+ def fb_cookie?
79
+ !fb_cookie.nil?
80
+ end
81
+
82
+ def fb_cookie
83
+ cookies[fb_cookie_name]
84
+ end
85
+
86
+ def fb_cookie_name
87
+ return "fbs_#{Facebooker2.app_id}"
88
+ end
89
+
90
+ # check if the expected signature matches the one from facebook
91
+ def fb_cookie_signature_correct?(hash,secret)
92
+ generateSignature(hash,secret) == hash["sig"]
93
+ end
94
+
95
+ # compute the md5 sig based on access_token,expires,uid, and the app secret
96
+ def generateSignature(hash,secret)
97
+ sorted_keys = hash.keys.reject {|k| k=="sig"}.sort
98
+ test_string = ""
99
+ sorted_keys.each do |key|
100
+ test_string += "#{key}=#{hash[key]}"
101
+ end
102
+ test_string += secret
103
+ sig = Digest::MD5.hexdigest(test_string)
104
+ return sig
105
+ end
106
+
107
+ def fb_signed_request_json(encoded)
108
+ chars_to_add = 4-(encoded.size % 4)
109
+ encoded += ("=" * chars_to_add)
110
+ Base64.decode64(encoded)
111
+ end
112
+
113
+ def facebook_params
114
+ @facebook_param ||= fb_load_facebook_params
115
+ end
116
+
117
+ def fb_load_facebook_params
118
+ return {} if params[:signed_request].blank?
119
+ sig,encoded_json = params[:signed_request].split(".")
120
+ return {} unless fb_signed_request_sig_valid?(sig,encoded_json)
121
+ ActiveSupport::JSON.decode(fb_signed_request_json(encoded_json)).with_indifferent_access
122
+ end
123
+
124
+ def fb_signed_request_sig_valid?(sig,encoded)
125
+ base64 = Base64.encode64(HMAC::SHA256.digest(Facebooker2.secret,encoded))
126
+ #now make the url changes that facebook makes
127
+ url_escaped_base64 = base64.gsub(/=*\n?$/,"").tr("+/","-_")
128
+ sig == url_escaped_base64
129
+ end
130
+
131
+ def fetch_client_and_user_from_signed_request
132
+ if facebook_params[:oauth_token]
133
+ fb_create_user_and_client(facebook_params[:oauth_token],facebook_params[:expires],facebook_params[:user_id])
134
+
135
+ if @_current_facebook_client
136
+ #compute a signature so we can store it in the cookie
137
+ sig_hash = Hash["uid"=>facebook_params[:user_id],"access_token"=>facebook_params[:oauth_token],"expires"=>facebook_params[:expires]]
138
+ return generateSignature(sig_hash, Facebooker2.secret)
139
+ end
140
+ end
141
+ end
142
+
143
+
144
+ # /**
145
+ # This method was shamelessly stolen from the php facebook SDK:
146
+ # https://github.com/facebook/php-sdk/blob/master/src/facebook.php
147
+ #
148
+ # Set a JS Cookie based on the _passed in_ session. It does not use the
149
+ # currently stored session -- you need to explicitly pass it in.
150
+ #
151
+ # If a nil access_token is passed in this method will actually delete the fbs_ cookie
152
+ #
153
+ # */
154
+ def set_fb_cookie(access_token,expires,uid,sig)
155
+
156
+ #default values for the cookie
157
+ value = 'deleted'
158
+ expires = Time.now.utc - 3600 unless expires != nil
159
+
160
+ if access_token
161
+ value = '"uid=' + uid + '&' +
162
+ 'access_token=' + access_token + '&' +
163
+ 'expires=' + expires.to_i.to_s + '&' +
164
+ 'sig=' + sig + '"'
165
+ end
166
+
167
+ # if an existing cookie is not set, we dont need to delete it
168
+ if (value == 'deleted' && cookies[fb_cookie_name] == "" )
169
+ return;
170
+ end
171
+
172
+ # in php they have to check if headers have already been sent before setting the cookie
173
+ # maybe rails we don't have this problem?
174
+
175
+ #My browser doesn't seem to save the cookie if I set expires
176
+ cookies[fb_cookie_name] = { :value=>value }#, :expires=>expires}
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,33 @@
1
+ module Facebooker2
2
+ module Rails
3
+ module Helpers
4
+ include FacebookConnect
5
+ include Javascript
6
+ include RequestForms
7
+ include User
8
+
9
+ def fb_stringify_vals(hash)
10
+ result={}
11
+ hash.each do |key,value|
12
+ result[key]=value.to_s
13
+ end
14
+ result
15
+ end
16
+ def fb_transform_keys(options,transformation_hash)
17
+ new_hash = {}
18
+ options.each do |key,value|
19
+ new_key = transformation_hash[key]||key
20
+ new_hash[new_key]=value
21
+ end
22
+ new_hash
23
+ end
24
+ FB_ALWAYS_VALID_OPTION_KEYS = [:class, :style]
25
+
26
+ def fb_assert_valid_keys(options,*valid_keys)
27
+ unknown_keys = options.keys - [valid_keys + FB_ALWAYS_VALID_OPTION_KEYS].flatten
28
+ raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,52 @@
1
+ module Facebooker2
2
+ module Rails
3
+ module Helpers
4
+ module FacebookConnect
5
+ #
6
+ # Render an <fb:login-button> element, similar to
7
+ # fb_login_button. Adds a js redirect to the onlogin event via rjs.
8
+ #
9
+ # ==== Examples
10
+ #
11
+ # fb_login_and_redirect '/other_page'
12
+ # => <fb:login-button onlogin="window.location.href = &quot;/other_page&quot;;"></fb:login-button>
13
+ #
14
+ # Like #fb_login_button, this also supports the :text option
15
+ #
16
+ # fb_login_and_redirect '/other_page', :text => "Login with Facebook", :v => '2'
17
+ # => <fb:login-button onlogin="window.location.href = &quot;/other_page&quot;;" v="2">Login with Facebook</fb:login-button>
18
+ #
19
+ def fb_login_and_redirect(url, options = {})
20
+ js = update_page do |page|
21
+ page.redirect_to url
22
+ end
23
+
24
+ text = options.delete(:text)
25
+
26
+ #rails 3 only escapes non-html_safe strings, so get the raw string instead of the SafeBuffer
27
+ content_tag("fb:login-button",text,options.merge(:onlogin=>js.to_str))
28
+ end
29
+
30
+ def fb_login(options = {},&proc)
31
+ js = capture(&proc)
32
+ text = options.delete(:text)
33
+ concat(content_tag("fb:login-button",text,options.merge(:onlogin=>js.to_str)))
34
+ end
35
+
36
+ #
37
+ # Logs the user out of facebook and redirects to the given URL
38
+ # args are passed to the call to link_to_function
39
+ def fb_logout_link(text,url,*args)
40
+ function= "FB.logout(function() {window.location.href = '#{url}';})"
41
+ link_to_function text, function.to_str, *args
42
+ end
43
+
44
+ def fb_server_fbml(style=nil,&proc)
45
+ style_string=" style=\"#{style}\"" if style
46
+ content = capture(&proc)
47
+ concat("<fb:serverFbml#{style_string}><script type='text/fbml'>#{content}</script></fb:serverFbml>")
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,56 @@
1
+ module Facebooker2
2
+ module Rails
3
+ module Helpers
4
+ module Javascript
5
+
6
+ def fb_html_safe(str)
7
+ if str.respond_to?(:html_safe)
8
+ str.html_safe
9
+ else
10
+ str
11
+ end
12
+ end
13
+
14
+ def fb_connect_async_js(app_id=Facebooker2.app_id,options={},&proc)
15
+ opts = Hash.new(true).merge!(options)
16
+ cookie = opts[:cookie]
17
+ status = opts[:status]
18
+ xfbml = opts[:xfbml]
19
+ extra_js = capture(&proc) if block_given?
20
+ js = <<-JAVASCRIPT
21
+ <script>
22
+ window.fbAsyncInit = function() {
23
+ FB.init({
24
+ appId : '#{app_id}',
25
+ status : #{status}, // check login status
26
+ cookie : #{cookie}, // enable cookies to allow the server to access the session
27
+ xfbml : #{xfbml} // parse XFBML
28
+ });
29
+ #{extra_js}
30
+ };
31
+
32
+ (function() {
33
+ var s = document.createElement('div');
34
+ s.setAttribute('id','fb-root');
35
+ document.documentElement.getElementsByTagName("body")[0].appendChild(s);
36
+ var e = document.createElement('script');
37
+ e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
38
+ e.async = true;
39
+ s.appendChild(e);
40
+ }());
41
+ </script>
42
+ JAVASCRIPT
43
+ escaped_js = fb_html_safe(js)
44
+ if block_given?
45
+ concat(escaped_js)
46
+ #return the empty string, since concat returns the buffer and we don't want double output
47
+ # from klochner
48
+ ""
49
+ else
50
+ escaped_js
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,33 @@
1
+ module Facebooker2
2
+ module Rails
3
+ module Helpers
4
+ module RequestForms
5
+ def fb_req_choice(label,url)
6
+ tag "fb:req-choice",:label=>label,:url=>url
7
+ end
8
+
9
+ def fb_multi_friend_selector(message,options={},&block)
10
+ options = fb_stringify_vals({:showborder=>false,:actiontext=>message,:max=>20}.merge(options.dup))
11
+ tag("fb:multi-friend-selector",options)
12
+ end
13
+
14
+ def fb_request_form(type,url,message,options={},&block)
15
+ content = capture(&block)
16
+ concat(content_tag("fb:request-form", content.to_s + fb_forgery_protection_token_tag,
17
+ {:action=>url,:method=>"post",:invite=>true,:type=>type,:content=>message}.merge(options)))
18
+ end
19
+
20
+
21
+ def fb_forgery_protection_token_tag
22
+ unless protect_against_forgery?
23
+ ''
24
+ else
25
+ tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
26
+ end
27
+ end
28
+
29
+
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,49 @@
1
+ module Facebooker2
2
+ module Rails
3
+ module Helpers
4
+ module User
5
+ # Render an fb:name tag for the given user
6
+ # This renders the name of the user specified. You can use this tag as both subject and object of
7
+ # a sentence. <em> See </em> http://wiki.developers.facebook.com/index.php/Fb:name for full description.
8
+ # Use this tag on FBML pages instead of retrieving the user's info and rendering the name explicitly.
9
+ #
10
+ def fb_name(user, options={})
11
+ options = fb_transform_keys(options,FB_NAME_OPTION_KEYS_TO_TRANSFORM)
12
+ fb_assert_valid_keys(options, FB_NAME_VALID_OPTION_KEYS)
13
+ options.merge!(:uid => Facebooker2.cast_to_facebook_id(user))
14
+ content_tag("fb:name",nil, fb_stringify_vals(options))
15
+ end
16
+
17
+ FB_NAME_OPTION_KEYS_TO_TRANSFORM = {:first_name_only => :firstnameonly,
18
+ :last_name_only => :lastnameonly,
19
+ :show_network => :shownetwork,
20
+ :use_you => :useyou,
21
+ :if_cant_see => :ifcantsee,
22
+ :subject_id => :subjectid}
23
+ FB_NAME_VALID_OPTION_KEYS = [:firstnameonly, :linked, :lastnameonly, :possessive, :reflexive,
24
+ :shownetwork, :useyou, :ifcantsee, :capitalize, :subjectid]
25
+
26
+
27
+ def fb_profile_pic(user, options={})
28
+ options = options.dup
29
+ validate_fb_profile_pic_size(options)
30
+ options = fb_transform_keys(options,FB_PROFILE_PIC_OPTION_KEYS_TO_TRANSFORM)
31
+ fb_assert_valid_keys(options,FB_PROFILE_PIC_VALID_OPTION_KEYS)
32
+ options.merge!(:uid => Facebooker2.cast_to_facebook_id(user))
33
+ content_tag("fb:profile-pic", nil,fb_stringify_vals(options))
34
+ end
35
+
36
+ FB_PROFILE_PIC_OPTION_KEYS_TO_TRANSFORM = {:facebook_logo => 'facebook-logo'}
37
+ FB_PROFILE_PIC_VALID_OPTION_KEYS = [:size, :linked, 'facebook-logo', :width, :height]
38
+ VALID_FB_PROFILE_PIC_SIZES = [:thumb, :small, :normal, :square]
39
+ def validate_fb_profile_pic_size(options)
40
+ if options.has_key?(:size) && !VALID_FB_PROFILE_PIC_SIZES.include?(options[:size].to_sym)
41
+ raise(ArgumentError, "Unknown value for size: #{options[:size]}")
42
+ end
43
+ end
44
+
45
+
46
+ end
47
+ end
48
+ end
49
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: clickonchris-facebooker2
3
+ version: !ruby/object:Gem::Version
4
+ hash: 113
5
+ prerelease: 6
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 7
10
+ - x
11
+ - 1
12
+ version: 0.0.7.x1
13
+ platform: ruby
14
+ authors:
15
+ - Mike Mangino (patch by Chris Johnson)
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2011-01-27 00:00:00 -06:00
21
+ default_executable:
22
+ dependencies:
23
+ - !ruby/object:Gem::Dependency
24
+ name: mogli
25
+ prerelease: false
26
+ requirement: &id001 !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ hash: 7
32
+ segments:
33
+ - 0
34
+ - 0
35
+ - 12
36
+ version: 0.0.12
37
+ type: :runtime
38
+ version_requirements: *id001
39
+ - !ruby/object:Gem::Dependency
40
+ name: ruby-hmac
41
+ prerelease: false
42
+ requirement: &id002 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ hash: 3
48
+ segments:
49
+ - 0
50
+ version: "0"
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ description: Facebook Connect integration library for ruby and rails
54
+ email: chris@clickonchris.com
55
+ executables: []
56
+
57
+ extensions: []
58
+
59
+ extra_rdoc_files: []
60
+
61
+ files:
62
+ - lib/facebooker2/rails/controller.rb
63
+ - lib/facebooker2/rails/helpers/facebook_connect.rb
64
+ - lib/facebooker2/rails/helpers/javascript.rb
65
+ - lib/facebooker2/rails/helpers/request_forms.rb
66
+ - lib/facebooker2/rails/helpers/user.rb
67
+ - lib/facebooker2/rails/helpers.rb
68
+ - lib/facebooker2/rails.rb
69
+ - lib/facebooker2.rb
70
+ has_rdoc: true
71
+ homepage: http://developers.facebook.com/docs/api
72
+ licenses: []
73
+
74
+ post_install_message:
75
+ rdoc_options: []
76
+
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ hash: 3
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ">"
92
+ - !ruby/object:Gem::Version
93
+ hash: 25
94
+ segments:
95
+ - 1
96
+ - 3
97
+ - 1
98
+ version: 1.3.1
99
+ requirements: []
100
+
101
+ rubyforge_project:
102
+ rubygems_version: 1.4.2
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: Facebook Connect integration library for ruby and rails
106
+ test_files: []
107
+