facebooker 1.0.13 → 1.0.18

Sign up to get free protection for your applications and to get access to all the features.
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