facebooker 1.0.13 → 1.0.18

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 (51) hide show
  1. data/Manifest.txt +29 -16
  2. data/Rakefile +9 -2
  3. data/generators/facebook/templates/public/javascripts/facebooker.js +1 -8
  4. data/init.rb +3 -1
  5. data/lib/facebooker.rb +4 -2
  6. data/lib/facebooker/adapters/facebook_adapter.rb +4 -0
  7. data/lib/facebooker/admin.rb +2 -2
  8. data/lib/facebooker/mobile.rb +20 -0
  9. data/lib/facebooker/mock/service.rb +50 -0
  10. data/lib/facebooker/mock/session.rb +18 -0
  11. data/lib/facebooker/model.rb +3 -3
  12. data/lib/facebooker/models/applicationrestrictions.rb +10 -0
  13. data/lib/facebooker/models/user.rb +35 -3
  14. data/lib/facebooker/models/user.rb.orig +396 -0
  15. data/lib/facebooker/models/user.rb.rej +17 -0
  16. data/lib/facebooker/models/video.rb +9 -0
  17. data/lib/facebooker/parser.rb +22 -1
  18. data/lib/facebooker/rails/controller.rb +34 -12
  19. data/lib/facebooker/rails/cucumber.rb +28 -0
  20. data/lib/facebooker/rails/cucumber/world.rb +46 -0
  21. data/lib/facebooker/rails/facebook_pretty_errors.rb +19 -11
  22. data/lib/facebooker/rails/helpers.rb +203 -122
  23. data/lib/facebooker/rails/helpers/fb_connect.rb +56 -5
  24. data/lib/facebooker/rails/integration_session.rb +38 -0
  25. data/lib/facebooker/rails/publisher.rb +15 -2
  26. data/lib/facebooker/rails/test_helpers.rb +11 -26
  27. data/lib/facebooker/service.rb +6 -3
  28. data/lib/facebooker/session.rb +24 -2
  29. data/lib/facebooker/session.rb.orig +564 -0
  30. data/lib/facebooker/session.rb.rej +29 -0
  31. data/lib/facebooker/version.rb +1 -1
  32. data/lib/tasks/tunnel.rake +2 -2
  33. data/test/{adapters_test.rb → facebooker/adapters_test.rb} +2 -4
  34. data/test/{facebook_admin_test.rb → facebooker/admin_test.rb} +2 -2
  35. data/test/{batch_request_test.rb → facebooker/batch_request_test.rb} +3 -2
  36. data/test/{facebook_data_test.rb → facebooker/data_test.rb} +2 -2
  37. data/test/{logging_test.rb → facebooker/logging_test.rb} +3 -3
  38. data/test/facebooker/mobile_test.rb +45 -0
  39. data/test/{model_test.rb → facebooker/model_test.rb} +36 -4
  40. data/test/{event_test.rb → facebooker/models/event_test.rb} +2 -2
  41. data/test/{user_test.rb → facebooker/models/user_test.rb} +10 -5
  42. data/test/{publisher_test.rb → facebooker/rails/publisher_test.rb} +12 -18
  43. data/test/{rails_integration_test.rb → facebooker/rails_integration_test.rb} +347 -272
  44. data/test/{facebook_cache_test.rb → facebooker/server_cache_test.rb} +1 -1
  45. data/test/{session_test.rb → facebooker/session_test.rb} +3 -2
  46. data/test/facebooker_test.rb +19 -1
  47. data/test/{http_multipart_post_test.rb → net/http_multipart_post_test.rb} +2 -4
  48. data/test/rails_test_helper.rb +11 -0
  49. data/test/test_helper.rb +8 -2
  50. metadata +45 -32
  51. data/lib/facebooker/rails/facebook_asset_path.rb +0 -18
@@ -41,7 +41,6 @@ generators/xd_receiver/xd_receiver_generator.rb
41
41
  generators/xd_receiver/templates/xd_receiver.html
42
42
  init.rb
43
43
  install.rb
44
- lib/facebooker.rb
45
44
  lib/facebooker/adapters/adapter_base.rb
46
45
  lib/facebooker/adapters/bebo_adapter.rb
47
46
  lib/facebooker/adapters/facebook_adapter.rb
@@ -50,10 +49,14 @@ lib/facebooker/batch_request.rb
50
49
  lib/facebooker/data.rb
51
50
  lib/facebooker/feed.rb
52
51
  lib/facebooker/logging.rb
52
+ lib/facebooker/mock/service.rb
53
+ lib/facebooker/mock/session.rb
54
+ lib/facebooker/mobile.rb
53
55
  lib/facebooker/model.rb
54
56
  lib/facebooker/models/affiliation.rb
55
57
  lib/facebooker/models/album.rb
56
58
  lib/facebooker/models/applicationproperties.rb
59
+ lib/facebooker/models/applicationrestrictions.rb
57
60
  lib/facebooker/models/cookie.rb
58
61
  lib/facebooker/models/education_info.rb
59
62
  lib/facebooker/models/event.rb
@@ -67,18 +70,23 @@ lib/facebooker/models/page.rb
67
70
  lib/facebooker/models/photo.rb
68
71
  lib/facebooker/models/tag.rb
69
72
  lib/facebooker/models/user.rb
73
+ lib/facebooker/models/user.rb.orig
74
+ lib/facebooker/models/user.rb.rej
75
+ lib/facebooker/models/video.rb
70
76
  lib/facebooker/models/work_info.rb
71
77
  lib/facebooker/parser.rb
72
78
  lib/facebooker/rails/controller.rb
73
- lib/facebooker/rails/facebook_asset_path.rb
79
+ lib/facebooker/rails/cucumber/world.rb
80
+ lib/facebooker/rails/cucumber.rb
74
81
  lib/facebooker/rails/facebook_form_builder.rb
75
82
  lib/facebooker/rails/facebook_pretty_errors.rb
76
83
  lib/facebooker/rails/facebook_request_fix.rb
77
84
  lib/facebooker/rails/facebook_session_handling.rb
78
85
  lib/facebooker/rails/facebook_url_helper.rb
79
86
  lib/facebooker/rails/facebook_url_rewriting.rb
80
- lib/facebooker/rails/helpers.rb
81
87
  lib/facebooker/rails/helpers/fb_connect.rb
88
+ lib/facebooker/rails/helpers.rb
89
+ lib/facebooker/rails/integration_session.rb
82
90
  lib/facebooker/rails/profile_publisher_extensions.rb
83
91
  lib/facebooker/rails/publisher.rb
84
92
  lib/facebooker/rails/routing.rb
@@ -87,28 +95,33 @@ lib/facebooker/rails/utilities.rb
87
95
  lib/facebooker/server_cache.rb
88
96
  lib/facebooker/service.rb
89
97
  lib/facebooker/session.rb
98
+ lib/facebooker/session.rb.orig
99
+ lib/facebooker/session.rb.rej
90
100
  lib/facebooker/version.rb
101
+ lib/facebooker.rb
91
102
  lib/net/http_multipart_post.rb
92
103
  lib/tasks/facebooker.rake
93
104
  lib/tasks/tunnel.rake
94
105
  rails/init.rb
95
106
  setup.rb
96
107
  templates/layout.erb
97
- test/adapters_test.rb
98
- test/batch_request_test.rb
99
- test/event_test.rb
100
- test/facebook_admin_test.rb
101
- test/facebook_cache_test.rb
102
- test/facebook_data_test.rb
108
+ test/facebooker/adapters_test.rb
109
+ test/facebooker/admin_test.rb
110
+ test/facebooker/batch_request_test.rb
111
+ test/facebooker/data_test.rb
112
+ test/facebooker/logging_test.rb
113
+ test/facebooker/model_test.rb
114
+ test/facebooker/models/event_test.rb
115
+ test/facebooker/models/user_test.rb
116
+ test/facebooker/rails/publisher_test.rb
117
+ test/facebooker/rails_integration_test.rb
118
+ test/facebooker/server_cache_test.rb
119
+ test/facebooker/session_test.rb
103
120
  test/facebooker_test.rb
104
121
  test/fixtures/multipart_post_body_with_only_parameters.txt
105
122
  test/fixtures/multipart_post_body_with_single_file.txt
106
123
  test/fixtures/multipart_post_body_with_single_file_that_has_nil_key.txt
107
- test/http_multipart_post_test.rb
108
- test/logging_test.rb
109
- test/model_test.rb
110
- test/publisher_test.rb
111
- test/rails_integration_test.rb
112
- test/session_test.rb
124
+ test/net/http_multipart_post_test.rb
125
+ test/rails_test_helper.rb
113
126
  test/test_helper.rb
114
- test/user_test.rb
127
+ test/facebooker/mobile_test.rb
data/Rakefile CHANGED
@@ -3,6 +3,12 @@
3
3
  require 'rubygems'
4
4
  ENV['RUBY_FLAGS']="-I#{%w(lib ext bin test).join(File::PATH_SEPARATOR)}"
5
5
  require 'hoe'
6
+ begin
7
+ require 'load_multi_rails_rake_tasks'
8
+ rescue LoadError
9
+ $stderr.puts "Install the multi_rails gem to run tests against multiple versions of Rails"
10
+ end
11
+
6
12
  $: << File.dirname(__FILE__) + '/lib'
7
13
  require './lib/facebooker.rb'
8
14
 
@@ -15,7 +21,7 @@ Hoe.new('facebooker', Facebooker::VERSION::STRING) do |p|
15
21
  p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
16
22
  p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
17
23
  p.remote_rdoc_dir = '' # Release to root
18
- p.test_globs = 'test/*.rb'
24
+ p.test_globs = 'test/**/*_test.rb'
19
25
  p.extra_deps << ['json', '>= 1.0.0']
20
26
  end
21
27
 
@@ -29,9 +35,10 @@ namespace :test do
29
35
  desc 'Aggregate code coverage for unit, functional and integration tests'
30
36
  Rcov::RcovTask.new(:coverage) do |t|
31
37
  t.libs << "test"
32
- t.test_files = FileList["test/*.rb"]
38
+ t.test_files = FileList["test/**/*_test.rb"]
33
39
  t.output_dir = "coverage/"
34
40
  t.verbose = true
41
+ t.rcov_opts = ['--exclude', 'test,/usr/lib/ruby,/Library/Ruby,/System/Library', '--sort', 'coverage']
35
42
  end
36
43
  end
37
44
 
@@ -48,14 +48,7 @@ function encodeURIComponent(str) {
48
48
 
49
49
  var Form = {};
50
50
  Form.serialize = function(form_element) {
51
- elements=$(form_element).serialize();
52
- param_string="";
53
- for (var name in elements) {
54
- if (param_string)
55
- param_string += "&";
56
- param_string += encodeURIComponent(name)+"="+encodeURIComponent(elements[name]);
57
- }
58
- return param_string;
51
+ return $(form_element).serialize();
59
52
  };
60
53
 
61
54
  Ajax.Updater = function (container,url,options) {
data/init.rb CHANGED
@@ -12,7 +12,6 @@ require 'net/http_multipart_post'
12
12
  require 'facebooker/rails/controller'
13
13
  require 'facebooker/rails/facebook_url_rewriting'
14
14
  require 'facebooker/rails/facebook_session_handling'
15
- require 'facebooker/rails/facebook_asset_path'
16
15
  require 'facebooker/rails/facebook_request_fix'
17
16
  require 'facebooker/rails/routing'
18
17
  require 'facebooker/rails/facebook_pretty_errors' rescue nil
@@ -68,3 +67,6 @@ end
68
67
  # pull :canvas=> into env in routing to allow for conditions
69
68
  ActionController::Routing::RouteSet.send :include, Facebooker::Rails::Routing::RouteSetExtensions
70
69
  ActionController::Routing::RouteSet::Mapper.send :include, Facebooker::Rails::Routing::MapperExtensions
70
+
71
+ Mime::Type.register_alias "text/html", :fbml
72
+ Mime::Type.register_alias "text/javascript", :fbjs
@@ -100,7 +100,7 @@ module Facebooker
100
100
  @timeout
101
101
  end
102
102
 
103
- [:api_key,:secret_key, :www_server_base_url,:login_url_base,:install_url_base,:api_rest_path,:api_server_base,:api_server_base_url,:canvas_server_base].each do |delegated_method|
103
+ [:api_key,:secret_key, :www_server_base_url,:login_url_base,:install_url_base,:api_rest_path,:api_server_base,:api_server_base_url,:canvas_server_base, :video_server_base].each do |delegated_method|
104
104
  define_method(delegated_method){ return current_adapter.send(delegated_method)}
105
105
  end
106
106
 
@@ -147,6 +147,7 @@ require 'facebooker/service'
147
147
  require 'facebooker/server_cache'
148
148
  require 'facebooker/data'
149
149
  require 'facebooker/admin'
150
+ require 'facebooker/mobile'
150
151
  require 'facebooker/session'
151
152
  require 'facebooker/version'
152
153
  require 'facebooker/models/location'
@@ -166,7 +167,8 @@ require 'facebooker/models/tag'
166
167
  require 'facebooker/models/user'
167
168
  require 'facebooker/models/info_item'
168
169
  require 'facebooker/models/info_section'
170
+ require 'facebooker/models/friend_list'
171
+ require 'facebooker/models/video'
169
172
  require 'facebooker/adapters/adapter_base'
170
173
  require 'facebooker/adapters/facebook_adapter'
171
174
  require 'facebooker/adapters/bebo_adapter'
172
- require 'facebooker/models/friend_list'
@@ -11,6 +11,10 @@ module Facebooker
11
11
  "api.facebook.com"
12
12
  end
13
13
 
14
+ def video_server_base
15
+ "api-video.facebook.com"
16
+ end
17
+
14
18
  def www_server_base_url
15
19
  "www.facebook.com"
16
20
  end
@@ -15,7 +15,7 @@ module Facebooker
15
15
  # +properties+: Hash of properties you want to view.
16
16
  def get_app_properties(*properties)
17
17
  json = @session.post('facebook.admin.getAppProperties', :properties => properties.to_json)
18
- hash = JSON.parse(CGI.unescapeHTML(json))
18
+ hash = Facebooker.json_decode(CGI.unescapeHTML(json))
19
19
  @properties = ApplicationProperties.from_hash(hash)
20
20
  end
21
21
 
@@ -29,7 +29,7 @@ module Facebooker
29
29
  # ** BETA ***
30
30
  def get_restriction_info(*restrictions)
31
31
  json = @session.post('facebook.admin.getRestrictionInfo')
32
- hash = JSON.parse(CGI.unescapeHTML(json))
32
+ hash = Facebooker.json_decode(CGI.unescapeHTML(json))
33
33
  @restrictions = ApplicationRestrictions.from_hash(hash)
34
34
  end
35
35
 
@@ -0,0 +1,20 @@
1
+ module Facebooker
2
+ class Mobile
3
+ def initialize(session)
4
+ @session = session
5
+ end
6
+
7
+ # Used to determine whether the user identified by "uid" has enabled SMS for this application.
8
+ def can_send(user)
9
+ @session.post('facebook.sms.canSend', :uid => User.cast_to_facebook_id(user))
10
+ end
11
+
12
+ # Send the given message to the user.
13
+ # See http://wiki.developers.facebook.com/index.php/Mobile
14
+ def send(user, message)
15
+ @session.post('facebook.sms.send',
16
+ {:uid => User.cast_to_facebook_id(user),
17
+ :message => message}, false)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,50 @@
1
+ require 'digest/md5'
2
+ require 'facebooker/service'
3
+
4
+ module Facebooker
5
+ # A mock service that reads the Facebook response from fixtures
6
+ # Adapted from http://gist.github.com/44344
7
+ #
8
+ # Facebooker::MockService.fixture_path = 'path/to/dir'
9
+ # Facebooker::Session.current = Facebooker::MockSession.create
10
+ #
11
+ class MockService < Service
12
+ class << self
13
+ attr_accessor :fixture_path
14
+ end
15
+
16
+ def read_fixture(method, filename, original = nil)
17
+ path = fixture_path(method, filename)
18
+ File.read path
19
+ rescue Errno::ENAMETOOLONG
20
+ read_fixture(method, hash_fixture_name(filename), filename)
21
+ rescue Errno::ENOENT => e
22
+ if File.exists?(fixture_path(method, 'default'))
23
+ File.read fixture_path(method, 'default')
24
+ else
25
+ e.message << "\n(Non-hashed path is #{original})" if original
26
+ e.message << "\nFacebook API Reference: http://wiki.developers.facebook.com/index.php/#{method.sub(/^facebook\./, '')}#Example_Return_XML"
27
+ raise e
28
+ end
29
+ end
30
+
31
+ def post(params)
32
+ method = params.delete(:method)
33
+ params.delete_if {|k,_| [:v, :api_key, :call_id, :sig].include?(k) }
34
+ Parser.parse(method, read_fixture(method, fixture_name(params)))
35
+ end
36
+
37
+ private
38
+ def fixture_path(method, filename)
39
+ File.join(self.class.fixture_path, method, "#{filename}.xml")
40
+ end
41
+
42
+ def hash_fixture_name(filename)
43
+ Digest::MD5.hexdigest(filename)
44
+ end
45
+
46
+ def fixture_name(params)
47
+ params.map {|*args| args.join('=') }.sort.join('&')
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,18 @@
1
+ require 'facebooker/session'
2
+
3
+ module Facebooker
4
+ class MockSession < Session
5
+ def secured?
6
+ true
7
+ end
8
+
9
+ def secure!
10
+ @uid = 1
11
+ true
12
+ end
13
+
14
+ def service
15
+ @service ||= MockService.new(Facebooker.api_server_base, Facebooker.api_rest_path, @api_key)
16
+ end
17
+ end
18
+ end
@@ -119,10 +119,10 @@ module Facebooker
119
119
  ##
120
120
  # Set model's attributes via Hash. Keys should map directly to the model's attribute names.
121
121
  def populate_from_hash!(hash)
122
- unless hash.empty?
122
+ unless hash.nil? || hash.empty?
123
123
  hash.each do |key, value|
124
124
  set_attr_method = "#{key}="
125
- if respond_to?(set_attr_method)
125
+ if !value.nil? && respond_to?(set_attr_method)
126
126
  self.__send__(set_attr_method, value)
127
127
  else
128
128
  Facebooker::Logging.log_info("**Warning**, Attempt to set non-attribute: #{key}",hash)
@@ -132,4 +132,4 @@ module Facebooker
132
132
  end
133
133
  end
134
134
  end
135
- end
135
+ end
@@ -0,0 +1,10 @@
1
+ module Facebooker
2
+
3
+ class ApplicationRestrictions
4
+ include Model
5
+ FIELDS = [ :age, :location, :age_distribution, :type ]
6
+
7
+ attr_accessor *FIELDS
8
+
9
+ end
10
+ end
@@ -10,8 +10,8 @@ module Facebooker
10
10
  include Model
11
11
  attr_accessor :message, :time, :status_id
12
12
  end
13
- FIELDS = [:status, :political, :pic_small, :name, :quotes, :is_app_user, :tv, :profile_update_time, :meeting_sex, :hs_info, :timezone, :relationship_status, :hometown_location, :about_me, :wall_count, :significant_other_id, :pic_big, :music, :uid, :work_history, :sex, :religion, :notes_count, :activities, :pic_square, :movies, :has_added_app, :education_history, :birthday, :first_name, :meeting_for, :last_name, :interests, :current_location, :pic, :books, :affiliations, :locale, :profile_url, :proxied_email,:email_hashes]
14
- STANDARD_FIELDS = [:uid, :first_name, :last_name, :name, :timezone, :birthday, :sex, :affiliations, :locale, :profile_url]
13
+ FIELDS = [:status, :political, :pic_small, :name, :quotes, :is_app_user, :tv, :profile_update_time, :meeting_sex, :hs_info, :timezone, :relationship_status, :hometown_location, :about_me, :wall_count, :significant_other_id, :pic_big, :music, :uid, :work_history, :sex, :religion, :notes_count, :activities, :pic_square, :movies, :has_added_app, :education_history, :birthday, :first_name, :meeting_for, :last_name, :interests, :current_location, :pic, :books, :affiliations, :locale, :profile_url, :proxied_email, :email_hashes, :allowed_restrictions]
14
+ STANDARD_FIELDS = [:uid, :first_name, :last_name, :name, :timezone, :birthday, :sex, :affiliations, :locale, :profile_url, :pic_square]
15
15
  populating_attr_accessor *FIELDS
16
16
  attr_reader :affiliations
17
17
  populating_hash_settable_accessor :current_location, Location
@@ -204,6 +204,38 @@ module Facebooker
204
204
  options.merge(nil => multipart_post_file)))
205
205
  end
206
206
 
207
+ # Upload a video to the user's profile.
208
+ #
209
+ # In your view, create a multipart form that posts directly to your application (not through canvas):
210
+ #
211
+ # <% form_tag videos_url(:canvas => false), :html => {:multipart => true, :promptpermission => 'video_upload'} do %>
212
+ # Video: <%= file_field_tag 'video' %>
213
+ # Title: <%= text_area_tag 'title' %>
214
+ # Description: <%= text_area_tag 'description' %>
215
+ # <%= submit_tag 'Upload Video', :class => 'inputsubmit' %>
216
+ # <% end %>
217
+ #
218
+ # And in your controller:
219
+ #
220
+ # class VideosController < ApplicationController
221
+ # def create
222
+ # file = Net::HTTP::MultipartPostFile.new(
223
+ # params[:photo].original_filename,
224
+ # params[:photo].content_type,
225
+ # params[:photo].read
226
+ # )
227
+ #
228
+ # @video = facebook_session.user.upload_video(file, :description => params[:description])
229
+ # redirect_to videos_url(:canvas => true)
230
+ # end
231
+ # end
232
+ #
233
+ # Options correspond to http://wiki.developers.facebook.com/index.php/Video.upload
234
+ def upload_video(multipart_post_file, options = {})
235
+ Video.from_hash(session.post_file('facebook.video.upload',
236
+ options.merge(nil => multipart_post_file, :base => Facebooker.video_server_base)))
237
+ end
238
+
207
239
  def profile_fbml
208
240
  session.post('facebook.profile.getFBML', :uid => id)
209
241
  end
@@ -314,7 +346,7 @@ module Facebooker
314
346
  ##
315
347
  # Two Facebooker::User objects should be considered equal if their Facebook ids are equal
316
348
  def ==(other_user)
317
- id == other_user.id
349
+ other_user.is_a?(User) && id == other_user.id
318
350
  end
319
351
 
320
352
 
@@ -0,0 +1,396 @@
1
+ require 'facebooker/model'
2
+ require 'facebooker/models/affiliation'
3
+ require 'facebooker/models/work_info'
4
+ module Facebooker
5
+ #
6
+ # Holds attributes and behavior for a Facebook User
7
+ class User
8
+ include Model
9
+ class Status
10
+ include Model
11
+ attr_accessor :message, :time, :status_id
12
+ end
13
+ FIELDS = [:status, :political, :pic_small, :name, :quotes, :is_app_user, :tv, :profile_update_time, :meeting_sex, :hs_info, :timezone, :relationship_status, :hometown_location, :about_me, :wall_count, :significant_other_id, :pic_big, :music, :uid, :work_history, :sex, :religion, :notes_count, :activities, :pic_square, :movies, :has_added_app, :education_history, :birthday, :first_name, :meeting_for, :last_name, :interests, :current_location, :pic, :books, :affiliations, :locale, :profile_url, :proxied_email]
14
+ STANDARD_FIELDS = [:uid, :first_name, :last_name, :name, :timezone, :birthday, :sex, :affiliations, :locale, :profile_url]
15
+ attr_accessor :id, :session
16
+ populating_attr_accessor *FIELDS
17
+ attr_reader :affiliations
18
+ populating_hash_settable_accessor :current_location, Location
19
+ populating_hash_settable_accessor :hometown_location, Location
20
+ populating_hash_settable_accessor :hs_info, EducationInfo::HighschoolInfo
21
+ populating_hash_settable_accessor :status, Status
22
+ populating_hash_settable_list_accessor :affiliations, Affiliation
23
+ populating_hash_settable_list_accessor :education_history, EducationInfo
24
+ populating_hash_settable_list_accessor :work_history, WorkInfo
25
+
26
+ # Can pass in these two forms:
27
+ # id, session, (optional) attribute_hash
28
+ # attribute_hash
29
+ def initialize(*args)
30
+ if (args.first.kind_of?(String) || args.first.kind_of?(Integer)) && args.size==1
31
+ @id=Integer(args.shift)
32
+ @session = Session.current
33
+ elsif (args.first.kind_of?(String) || args.first.kind_of?(Integer)) && args[1].kind_of?(Session)
34
+ @id = Integer(args.shift)
35
+ @session = args.shift
36
+ end
37
+ if args.last.kind_of?(Hash)
38
+ populate_from_hash!(args.pop)
39
+ end
40
+ end
41
+
42
+ # Returns a user's events, params correspond to API call parameters (except UID):
43
+ # http://wiki.developers.facebook.com/index.php/Events.get
44
+ # E.g:
45
+ # @user.events(:start_time => Time.now, :end_time => 1.month.from_now)
46
+ # # => Returns events betwen now and a month from now
47
+ def events(params={})
48
+ @events ||= {}
49
+ [:start_time,:end_time].compact.each do |key|
50
+ params[key] = params[key].to_i
51
+ end
52
+ # puts @events[params.to_s].nil?
53
+ @events[params.to_s] ||= @session.post('facebook.events.get', {:uid => self.id}.merge(params)).map do |event|
54
+ Event.from_hash(event)
55
+ end
56
+ end
57
+
58
+ #
59
+ # Set the list of friends, given an array of User objects. If the list has been retrieved previously, will not set
60
+ def friends=(list_of_friends,flid=nil)
61
+ @friends_hash ||= {}
62
+ flid=cast_to_friend_list_id(flid)
63
+ #use __blank instead of nil so that this is cached
64
+ cache_key = flid||"__blank"
65
+
66
+ @friends_hash[cache_key] ||= list_of_friends
67
+ end
68
+
69
+ def cast_to_friend_list_id(flid)
70
+ case flid
71
+ when String
72
+ list=friend_lists.detect {|f| f.name==flid}
73
+ raise Facebooker::Session::InvalidFriendList unless list
74
+ list.flid
75
+ when FriendList
76
+ flid.flid
77
+ else
78
+ flid
79
+ end
80
+ end
81
+ ##
82
+ # Retrieve friends
83
+ def friends(flid = nil)
84
+ @friends_hash ||= {}
85
+ flid=cast_to_friend_list_id(flid)
86
+
87
+ #use __blank instead of nil so that this is cached
88
+ cache_key = flid||"__blank"
89
+ options = {:uid=>@id}
90
+ options[:flid] = flid unless flid.nil?
91
+ @friends_hash[cache_key] ||= @session.post('facebook.friends.get', options,false).map do |uid|
92
+ User.new(uid, @session)
93
+ end
94
+ @friends_hash[cache_key]
95
+ end
96
+
97
+ def friend_lists
98
+ @friend_lists ||= @session.post('facebook.friends.getLists').map do |hash|
99
+ friend_list = FriendList.from_hash(hash)
100
+ friend_list.session = session
101
+ friend_list
102
+ end
103
+ end
104
+ ###
105
+ # Retrieve friends with user info populated
106
+ # Subsequent calls will be retrieved from memory.
107
+ # Optional: list of fields to retrieve as symbols
108
+ def friends!(*fields)
109
+ @friends ||= session.post('facebook.users.getInfo', :fields => collect(fields), :uids => friends.map{|f| f.id}.join(',')).map do |hash|
110
+ User.new(hash['uid'], session, hash)
111
+ end
112
+ end
113
+
114
+ ###
115
+ # Retrieve profile data for logged in user
116
+ # Optional: list of fields to retrieve as symbols
117
+ def populate(*fields)
118
+ session.post('facebook.users.getInfo', :fields => collect(fields), :uids => id) do |response|
119
+ populate_from_hash!(response.first)
120
+ end
121
+ end
122
+
123
+ def friends_with?(user_or_id)
124
+ friends.map{|f| f.to_i}.include?(user_or_id.to_i)
125
+ end
126
+
127
+ def friends_with_this_app
128
+ @friends_with_this_app ||= session.post('facebook.friends.getAppUsers').map do |uid|
129
+ User.new(uid, session)
130
+ end
131
+ end
132
+
133
+ def groups(gids = [])
134
+ args = gids.empty? ? {} : {:gids => gids}
135
+ @groups ||= session.post('facebook.groups.get', args).map do |hash|
136
+ group = Group.from_hash(hash)
137
+ group.session = session
138
+ group
139
+ end
140
+ end
141
+
142
+ def notifications
143
+ @notifications ||= Notifications.from_hash(session.post('facebook.notifications.get'))
144
+ end
145
+
146
+ def publish_story(story)
147
+ publish(story)
148
+ end
149
+
150
+ def publish_action(action)
151
+ publish(action)
152
+ end
153
+
154
+ def publish_templatized_action(action)
155
+ publish(action)
156
+ end
157
+
158
+ def albums
159
+ @albums ||= session.post('facebook.photos.getAlbums', :uid => self.id) do |response|
160
+ response.map do |hash|
161
+ Album.from_hash(hash)
162
+ end
163
+ end
164
+ end
165
+
166
+ def create_album(params)
167
+ @album = session.post('facebook.photos.createAlbum', params) {|response| Album.from_hash(response)}
168
+ end
169
+
170
+ def profile_photos
171
+ session.get_photos(nil, nil, profile_pic_album_id)
172
+ end
173
+
174
+ # Upload a photo to the user's profile.
175
+ #
176
+ # In your view, create a multipart form that posts directly to your application (not through canvas):
177
+ #
178
+ # <% form_tag photos_url(:canvas => false), :html => {:multipart => true, :promptpermission => 'photo_upload'} do %>
179
+ # Photo: <%= file_field_tag 'photo' %>
180
+ # Caption: <%= text_area_tag 'caption' %>
181
+ # <%= submit_tag 'Upload Photo', :class => 'inputsubmit' %>
182
+ # <% end %>
183
+ #
184
+ # And in your controller:
185
+ #
186
+ # class PhotosController < ApplicationController
187
+ # def create
188
+ # file = Net::HTTP::MultipartPostFile.new(
189
+ # params[:photo].original_filename,
190
+ # params[:photo].content_type,
191
+ # params[:photo].read
192
+ # )
193
+ #
194
+ # @photo = facebook_session.user.upload_photo(file, :caption => params[:caption])
195
+ # redirect_to photos_url(:canvas => true)
196
+ # end
197
+ # end
198
+ #
199
+ # Options correspond to http://wiki.developers.facebook.com/index.php/Photos.upload
200
+ def upload_photo(multipart_post_file, options = {})
201
+ Photo.from_hash(session.post_file('facebook.photos.upload',
202
+ options.merge(nil => multipart_post_file)))
203
+ end
204
+
205
+ def profile_fbml
206
+ session.post('facebook.profile.getFBML', :uid => @id)
207
+ end
208
+
209
+ ##
210
+ # Set the profile FBML for this user
211
+ #
212
+ # This does not set profile actions, that should be done with profile_action=
213
+ def profile_fbml=(markup)
214
+ set_profile_fbml(markup, nil, nil)
215
+ end
216
+
217
+ ##
218
+ # Set the mobile profile FBML
219
+ def mobile_fbml=(markup)
220
+ set_profile_fbml(nil, markup, nil)
221
+ end
222
+
223
+ def profile_action=(markup)
224
+ set_profile_fbml(nil, nil, markup)
225
+ end
226
+
227
+ def profile_main=(markup)
228
+ set_profile_fbml(nil,nil,nil,markup)
229
+ end
230
+
231
+ def set_profile_fbml(profile_fbml, mobile_fbml, profile_action_fbml, profile_main = nil)
232
+ parameters = {:uid => @id}
233
+ parameters[:profile] = profile_fbml if profile_fbml
234
+ parameters[:profile_action] = profile_action_fbml if profile_action_fbml
235
+ parameters[:mobile_profile] = mobile_fbml if mobile_fbml
236
+ parameters[:profile_main] = profile_main if profile_main
237
+ session.post('facebook.profile.setFBML', parameters,false)
238
+ end
239
+
240
+ ## ** NEW PROFILE DESIGN ***
241
+ # Set a info section for this user
242
+ #
243
+ # Note: using set_profile_info as I feel using user.set_info could be confused with the user.getInfo facebook method.
244
+ # Also, I feel it fits in line with user.set_profile_fbml.
245
+ def set_profile_info(title, info_fields, format = :text)
246
+ session.post('facebook.profile.setInfo', :title => title, :uid => @id,
247
+ :type => format.to_s == "text" ? 1 : 5, :info_fields => info_fields.to_json)
248
+ end
249
+
250
+ def get_profile_info
251
+ session.post('facebook.profile.getInfo', :uid => @id)
252
+ end
253
+
254
+ ##
255
+ # This DOES NOT set the status of a user on Facebook
256
+ # Use the set_status method instead
257
+ def status=(message)
258
+ case message
259
+ when String,Status
260
+ @status = message
261
+ when Hash
262
+ @status = Status.from_hash(message)
263
+ end
264
+ end
265
+
266
+ ##
267
+ # Set the status for a user
268
+ # DOES NOT prepend "is" to the message
269
+ #
270
+ # requires extended permission.
271
+ def set_status(message)
272
+ self.status=message
273
+ session.post('facebook.users.setStatus',:status=>message,:status_includes_verb=>1) do |ret|
274
+ ret
275
+ end
276
+ end
277
+
278
+ ##
279
+ # Checks to see if the user has enabled the given extended permission
280
+ def has_permission?(ext_perm) # ext_perm = email, offline_access, status_update, photo_upload, create_listing, create_event, rsvp_event, sms
281
+ session.post('facebook.users.hasAppPermission',:ext_perm=>ext_perm) == "1"
282
+ end
283
+
284
+ ##
285
+ # Convenience method to send email to the current user
286
+ def send_email(subject, text=nil, fbml=nil)
287
+ session.send_email([@id], subject, text, fbml)
288
+ end
289
+
290
+ ##
291
+ # Convenience method to set cookie for the current user
292
+ def set_cookie(name, value, expires=nil, path=nil)
293
+ session.data.set_cookie(@id, name, value, expires, path)
294
+ end
295
+
296
+ ##
297
+ # Convenience method to get cookies for the current user
298
+ def get_cookies(name=nil)
299
+ session.data.get_cookies(@id, name)
300
+ end
301
+
302
+ ##
303
+ # Returns the user's id as an integer
304
+ def to_i
305
+ id
306
+ end
307
+
308
+ def to_s
309
+ id.to_s
310
+ end
311
+
312
+ ##
313
+ # Two Facebooker::User objects should be considered equal if their Facebook ids are equal
314
+ def ==(other_user)
315
+ id == other_user.id
316
+ end
317
+
318
+
319
+ # register a user with Facebook
320
+ # users should be a hast with at least an :email field
321
+ # you can optionally provide an :account_id field as well
322
+
323
+ def self.register(users)
324
+ user_map={}
325
+ users=users.map do |h|
326
+ returning h.dup do |d|
327
+ if email=d.delete(:email)
328
+ hash = hash_email(email)
329
+ user_map[hash]=h
330
+ d[:email_hash]=hash
331
+ end
332
+ end
333
+ end
334
+ Facebooker::Session.create.post("facebook.connect.registerUsers",:accounts=>users.to_json) do |ret|
335
+ ret.each do |hash|
336
+ user_map.delete(hash)
337
+ end
338
+ unless user_map.empty?
339
+ e=Facebooker::Session::UserRegistrationFailed.new
340
+ e.failed_users = user_map.values
341
+ raise e
342
+ end
343
+ ret
344
+ end
345
+ end
346
+
347
+ def self.hash_email(email)
348
+ email = email.downcase.strip
349
+ crc=Zlib.crc32(email)
350
+ md5=Digest::MD5.hexdigest(email)
351
+ "#{crc}_#{md5}"
352
+ end
353
+
354
+ def self.cast_to_facebook_id(object)
355
+ if object.respond_to?(:facebook_id)
356
+ object.facebook_id
357
+ else
358
+ object
359
+ end
360
+ end
361
+
362
+ def facebook_id
363
+ @id
364
+ end
365
+
366
+ def self.user_fields(fields = [])
367
+ valid_fields(fields)
368
+ end
369
+
370
+ def self.standard_fields(fields = [])
371
+ valid_fields(fields,STANDARD_FIELDS)
372
+ end
373
+
374
+ private
375
+ def publish(feed_story_or_action)
376
+ session.post(Facebooker::Feed::METHODS[feed_story_or_action.class.name.split(/::/).last], feed_story_or_action.to_params) == "1" ? true : false
377
+ end
378
+
379
+ def self.valid_fields(fields, allowable=FIELDS)
380
+ allowable.reject{|field_name| !fields.empty? && !fields.include?(field_name)}.join(',')
381
+ end
382
+
383
+ def collect(fields, allowable=FIELDS)
384
+ allowable.reject{|field_name| !fields.empty? && !fields.include?(field_name)}.join(',')
385
+ end
386
+
387
+ def profile_pic_album_id
388
+ merge_aid(-3, @id)
389
+ end
390
+
391
+ def merge_aid(aid, uid)
392
+ (uid << 32) + (aid & 0xFFFFFFFF)
393
+ end
394
+
395
+ end
396
+ end