facebooker-fbg1 1.0.61.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (154) hide show
  1. data/.autotest +15 -0
  2. data/CHANGELOG.rdoc +24 -0
  3. data/COPYING.rdoc +19 -0
  4. data/Manifest.txt +150 -0
  5. data/README.rdoc +119 -0
  6. data/Rakefile +94 -0
  7. data/TODO.rdoc +4 -0
  8. data/examples/desktop_login.rb +14 -0
  9. data/facebooker.gemspec +42 -0
  10. data/generators/facebook/facebook_generator.rb +14 -0
  11. data/generators/facebook/templates/config/facebooker.yml +49 -0
  12. data/generators/facebook/templates/public/javascripts/facebooker.js +332 -0
  13. data/generators/facebook_controller/USAGE +33 -0
  14. data/generators/facebook_controller/facebook_controller_generator.rb +40 -0
  15. data/generators/facebook_controller/templates/controller.rb +7 -0
  16. data/generators/facebook_controller/templates/functional_test.rb +11 -0
  17. data/generators/facebook_controller/templates/helper.rb +2 -0
  18. data/generators/facebook_controller/templates/view.fbml.erb +2 -0
  19. data/generators/facebook_controller/templates/view.html.erb +2 -0
  20. data/generators/facebook_publisher/facebook_publisher_generator.rb +14 -0
  21. data/generators/facebook_publisher/templates/create_facebook_templates.rb +15 -0
  22. data/generators/facebook_publisher/templates/publisher.rb +3 -0
  23. data/generators/facebook_scaffold/USAGE +27 -0
  24. data/generators/facebook_scaffold/facebook_scaffold_generator.rb +118 -0
  25. data/generators/facebook_scaffold/templates/controller.rb +93 -0
  26. data/generators/facebook_scaffold/templates/facebook_style.css +2579 -0
  27. data/generators/facebook_scaffold/templates/functional_test.rb +89 -0
  28. data/generators/facebook_scaffold/templates/helper.rb +2 -0
  29. data/generators/facebook_scaffold/templates/layout.fbml.erb +6 -0
  30. data/generators/facebook_scaffold/templates/layout.html.erb +17 -0
  31. data/generators/facebook_scaffold/templates/style.css +74 -0
  32. data/generators/facebook_scaffold/templates/view_edit.fbml.erb +13 -0
  33. data/generators/facebook_scaffold/templates/view_edit.html.erb +18 -0
  34. data/generators/facebook_scaffold/templates/view_index.fbml.erb +24 -0
  35. data/generators/facebook_scaffold/templates/view_index.html.erb +24 -0
  36. data/generators/facebook_scaffold/templates/view_new.fbml.erb +12 -0
  37. data/generators/facebook_scaffold/templates/view_new.html.erb +17 -0
  38. data/generators/facebook_scaffold/templates/view_show.fbml.erb +10 -0
  39. data/generators/facebook_scaffold/templates/view_show.html.erb +10 -0
  40. data/generators/publisher/publisher_generator.rb +14 -0
  41. data/generators/xd_receiver/templates/xd_receiver.html +10 -0
  42. data/generators/xd_receiver/templates/xd_receiver_ssl.html +10 -0
  43. data/generators/xd_receiver/xd_receiver_generator.rb +10 -0
  44. data/init.rb +26 -0
  45. data/install.rb +12 -0
  46. data/lib/facebooker.rb +254 -0
  47. data/lib/facebooker/adapters/adapter_base.rb +91 -0
  48. data/lib/facebooker/adapters/bebo_adapter.rb +77 -0
  49. data/lib/facebooker/adapters/facebook_adapter.rb +60 -0
  50. data/lib/facebooker/admin.rb +42 -0
  51. data/lib/facebooker/application.rb +13 -0
  52. data/lib/facebooker/attachment.rb +51 -0
  53. data/lib/facebooker/batch_request.rb +45 -0
  54. data/lib/facebooker/data.rb +57 -0
  55. data/lib/facebooker/feed.rb +78 -0
  56. data/lib/facebooker/logging.rb +44 -0
  57. data/lib/facebooker/mobile.rb +20 -0
  58. data/lib/facebooker/mock/service.rb +50 -0
  59. data/lib/facebooker/mock/session.rb +18 -0
  60. data/lib/facebooker/model.rb +139 -0
  61. data/lib/facebooker/models/affiliation.rb +10 -0
  62. data/lib/facebooker/models/album.rb +11 -0
  63. data/lib/facebooker/models/applicationproperties.rb +39 -0
  64. data/lib/facebooker/models/applicationrestrictions.rb +10 -0
  65. data/lib/facebooker/models/cookie.rb +10 -0
  66. data/lib/facebooker/models/education_info.rb +11 -0
  67. data/lib/facebooker/models/event.rb +28 -0
  68. data/lib/facebooker/models/family_relative_info.rb +7 -0
  69. data/lib/facebooker/models/friend_list.rb +16 -0
  70. data/lib/facebooker/models/group.rb +36 -0
  71. data/lib/facebooker/models/info_item.rb +10 -0
  72. data/lib/facebooker/models/info_section.rb +10 -0
  73. data/lib/facebooker/models/location.rb +8 -0
  74. data/lib/facebooker/models/notifications.rb +17 -0
  75. data/lib/facebooker/models/page.rb +44 -0
  76. data/lib/facebooker/models/photo.rb +19 -0
  77. data/lib/facebooker/models/tag.rb +12 -0
  78. data/lib/facebooker/models/user.rb +575 -0
  79. data/lib/facebooker/models/video.rb +9 -0
  80. data/lib/facebooker/models/work_info.rb +10 -0
  81. data/lib/facebooker/parser.rb +724 -0
  82. data/lib/facebooker/rails/backwards_compatible_param_checks.rb +31 -0
  83. data/lib/facebooker/rails/controller.rb +372 -0
  84. data/lib/facebooker/rails/cucumber.rb +28 -0
  85. data/lib/facebooker/rails/cucumber/world.rb +40 -0
  86. data/lib/facebooker/rails/extensions/action_controller.rb +48 -0
  87. data/lib/facebooker/rails/extensions/rack_setup.rb +14 -0
  88. data/lib/facebooker/rails/extensions/routing.rb +15 -0
  89. data/lib/facebooker/rails/facebook_form_builder.rb +141 -0
  90. data/lib/facebooker/rails/facebook_pretty_errors.rb +22 -0
  91. data/lib/facebooker/rails/facebook_request_fix.rb +28 -0
  92. data/lib/facebooker/rails/facebook_request_fix_2-3.rb +31 -0
  93. data/lib/facebooker/rails/facebook_session_handling.rb +68 -0
  94. data/lib/facebooker/rails/facebook_url_helper.rb +192 -0
  95. data/lib/facebooker/rails/facebook_url_rewriting.rb +60 -0
  96. data/lib/facebooker/rails/helpers.rb +834 -0
  97. data/lib/facebooker/rails/helpers/fb_connect.rb +133 -0
  98. data/lib/facebooker/rails/helpers/stream_publish.rb +22 -0
  99. data/lib/facebooker/rails/integration_session.rb +38 -0
  100. data/lib/facebooker/rails/profile_publisher_extensions.rb +42 -0
  101. data/lib/facebooker/rails/publisher.rb +599 -0
  102. data/lib/facebooker/rails/routing.rb +49 -0
  103. data/lib/facebooker/rails/test_helpers.rb +68 -0
  104. data/lib/facebooker/rails/utilities.rb +22 -0
  105. data/lib/facebooker/server_cache.rb +24 -0
  106. data/lib/facebooker/service.rb +102 -0
  107. data/lib/facebooker/service/base_service.rb +19 -0
  108. data/lib/facebooker/service/curl_service.rb +44 -0
  109. data/lib/facebooker/service/net_http_service.rb +12 -0
  110. data/lib/facebooker/service/typhoeus_multi_service.rb +27 -0
  111. data/lib/facebooker/service/typhoeus_service.rb +17 -0
  112. data/lib/facebooker/session.rb +732 -0
  113. data/lib/facebooker/stream_post.rb +19 -0
  114. data/lib/facebooker/version.rb +9 -0
  115. data/lib/net/http_multipart_post.rb +123 -0
  116. data/lib/rack/facebook.rb +89 -0
  117. data/lib/rack/facebook_session.rb +21 -0
  118. data/lib/tasks/facebooker.rake +18 -0
  119. data/lib/tasks/facebooker.rb +2 -0
  120. data/lib/tasks/tunnel.rake +46 -0
  121. data/rails/init.rb +1 -0
  122. data/setup.rb +1585 -0
  123. data/templates/layout.erb +24 -0
  124. data/test/facebooker/adapters_test.rb +191 -0
  125. data/test/facebooker/admin_test.rb +102 -0
  126. data/test/facebooker/application_test.rb +41 -0
  127. data/test/facebooker/attachment_test.rb +72 -0
  128. data/test/facebooker/batch_request_test.rb +83 -0
  129. data/test/facebooker/data_test.rb +86 -0
  130. data/test/facebooker/logging_test.rb +43 -0
  131. data/test/facebooker/mobile_test.rb +45 -0
  132. data/test/facebooker/model_test.rb +133 -0
  133. data/test/facebooker/models/event_test.rb +15 -0
  134. data/test/facebooker/models/page_test.rb +51 -0
  135. data/test/facebooker/models/photo_test.rb +16 -0
  136. data/test/facebooker/models/user_test.rb +430 -0
  137. data/test/facebooker/rails/facebook_request_fix_2-3_test.rb +25 -0
  138. data/test/facebooker/rails/facebook_url_rewriting_test.rb +76 -0
  139. data/test/facebooker/rails/integration_session_test.rb +13 -0
  140. data/test/facebooker/rails/publisher_test.rb +532 -0
  141. data/test/facebooker/rails_integration_test.rb +1534 -0
  142. data/test/facebooker/server_cache_test.rb +44 -0
  143. data/test/facebooker/service_test.rb +58 -0
  144. data/test/facebooker/session_test.rb +883 -0
  145. data/test/facebooker_test.rb +951 -0
  146. data/test/fixtures/multipart_post_body_with_only_parameters.txt +33 -0
  147. data/test/fixtures/multipart_post_body_with_single_file.txt +38 -0
  148. data/test/fixtures/multipart_post_body_with_single_file_that_has_nil_key.txt +38 -0
  149. data/test/net/http_multipart_post_test.rb +52 -0
  150. data/test/rack/facebook_session_test.rb +34 -0
  151. data/test/rack/facebook_test.rb +73 -0
  152. data/test/rails_test_helper.rb +36 -0
  153. data/test/test_helper.rb +74 -0
  154. metadata +272 -0
@@ -0,0 +1,133 @@
1
+ module Facebooker
2
+ module Rails
3
+ module Helpers
4
+ module FbConnect
5
+ include Facebooker::Rails::Helpers::StreamPublish
6
+ def fb_connect_javascript_tag(options = {})
7
+ # accept both Rails and Facebook locale formatting, i.e. "en-US" and "en_US".
8
+ lang = "/#{options[:lang].to_s.gsub('-', '_')}" if options[:lang]
9
+ # dont use the javascript_include_tag helper since it adds a .js at the end
10
+ if request.ssl?
11
+ "<script src=\"https://www.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php#{lang}\" type=\"text/javascript\"></script>"
12
+ else
13
+ "<script src=\"http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php#{lang}\" type=\"text/javascript\"></script>"
14
+ end
15
+ end
16
+
17
+ #
18
+ # For information on the :app_settings argument see http://wiki.developers.facebook.com/index.php/JS_API_M_FB.Facebook.Init_2
19
+ # While it would be nice to treat :app_settings as a hash, some of the arguments do different things if they are a string vs a javascript function
20
+ # and Rails' Hash#to_json always quotes strings so there is no way to indicate when the value should be a javascript function.
21
+ # For this reason :app_settings needs to be a string that is valid JSON (including the {}'s).
22
+ #
23
+ def init_fb_connect(*required_features,&proc)
24
+ additions = ""
25
+ if block_given?
26
+ additions = capture(&proc)
27
+ end
28
+
29
+ # Yes, app_settings is set to a string of an empty JSON element. That's intentional.
30
+ options = {:js => :prototype, :app_settings => '{}'}
31
+
32
+ if required_features.last.is_a?(Hash)
33
+ options.merge!(required_features.pop.symbolize_keys)
34
+ end
35
+
36
+ if request.ssl?
37
+ init_string = "FB.init('#{Facebooker.api_key}','/xd_receiver_ssl.html', #{options[:app_settings]});"
38
+ else
39
+ init_string = "FB.init('#{Facebooker.api_key}','/xd_receiver.html', #{options[:app_settings]});"
40
+ end
41
+ unless required_features.blank?
42
+ init_string = <<-FBML
43
+ #{case options[:js]
44
+ when :jquery then "jQuery(document).ready("
45
+ when :dojo then "dojo.addOnLoad("
46
+ else "Element.observe(window,'load',"
47
+ end} function() {
48
+ FB_RequireFeatures(#{required_features.to_json}, function() {
49
+ #{init_string}
50
+ #{additions}
51
+ });
52
+ });
53
+ FBML
54
+ end
55
+
56
+ # block_is_within_action_view? is rails 2.1.x and has been
57
+ # deprecated. rails >= 2.2.x uses block_called_from_erb?
58
+ block_tester = respond_to?(:block_is_within_action_view?) ?
59
+ :block_is_within_action_view? : :block_called_from_erb?
60
+
61
+ if block_given? && send(block_tester, proc)
62
+ versioned_concat(javascript_tag(init_string),proc.binding)
63
+ else
64
+ javascript_tag init_string
65
+ end
66
+ end
67
+
68
+ # Render an <fb:login-button> element
69
+ #
70
+ # ==== Examples
71
+ #
72
+ # <%= fb_login_button%>
73
+ # => <fb:login-button></fb:login-button>
74
+ #
75
+ # Specifying a javascript callback
76
+ #
77
+ # <%= fb_login_button 'update_something();'%>
78
+ # => <fb:login-button onlogin='update_something();'></fb:login-button>
79
+ #
80
+ # Adding options <em>See:</em> http://wiki.developers.facebook.com/index.php/Fb:login-button
81
+ #
82
+ # <%= fb_login_button 'update_something();', :size => :small, :background => :dark%>
83
+ # => <fb:login-button background='dark' onlogin='update_something();' size='small'></fb:login-button>
84
+ #
85
+ def fb_login_button(*args)
86
+
87
+ callback = args.first
88
+ options = args[1] || {}
89
+ options.merge!(:onlogin=>callback)if callback
90
+
91
+ content_tag("fb:login-button",nil, options)
92
+ end
93
+
94
+ def fb_login_and_redirect(url, options = {})
95
+ js = update_page do |page|
96
+ page.redirect_to url
97
+ end
98
+ content_tag("fb:login-button",nil,options.merge(:onlogin=>js))
99
+ end
100
+
101
+ def fb_unconnected_friends_count
102
+ content_tag "fb:unconnected-friends-count",nil
103
+ end
104
+
105
+ def fb_logout_link(text,url,*args)
106
+ js = update_page do |page|
107
+ page.call "FB.Connect.logoutAndRedirect",url
108
+ end
109
+ link_to_function text, js, *args
110
+ end
111
+
112
+ def fb_bookmark_link(text,url,*args)
113
+ js = update_page do |page|
114
+ page.call "FB.Connect.showBookmarkDialog",url
115
+ end
116
+ link_to_function text, js, *args
117
+ end
118
+
119
+ def fb_user_action(action, user_message = nil, prompt = "", callback = nil)
120
+ defaulted_callback = callback || "null"
121
+ update_page do |page|
122
+ page.call("FB.Connect.showFeedDialog",action.template_id,action.data,action.target_ids,action.body_general,nil,page.literal("FB.RequireConnect.promptConnect"),page.literal(defaulted_callback),prompt,user_message.nil? ? nil : {:value=>user_message})
123
+ end
124
+ end
125
+
126
+ def fb_connect_stream_publish(stream_post,user_message_prompt=nil,callback=nil,auto_publish=false,actor=nil)
127
+ stream_publish("FB.Connect.streamPublish",stream_post,user_message_prompt,callback,auto_publish,actor)
128
+ end
129
+
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,22 @@
1
+ module Facebooker
2
+ module Rails
3
+ module Helpers
4
+ module StreamPublish
5
+ def stream_publish(js_method,stream_post,user_message_prompt=nil,callback=nil,auto_publish=false,actor=nil)
6
+ defaulted_callback = callback || "null"
7
+ update_page do |page|
8
+ page.call(js_method,
9
+ stream_post.user_message,
10
+ stream_post.attachment.to_hash,
11
+ stream_post.action_links,
12
+ Facebooker::User.cast_to_facebook_id(stream_post.target),
13
+ user_message_prompt,
14
+ page.literal(defaulted_callback),
15
+ auto_publish,
16
+ Facebooker::User.cast_to_facebook_id(actor))
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,38 @@
1
+ require 'action_controller/integration'
2
+
3
+ class Facebooker::Rails::IntegrationSession < ActionController::Integration::Session
4
+ include Facebooker::Rails::TestHelpers
5
+ attr_accessor :default_request_params, :canvas
6
+
7
+ def process(method, path, parameters = nil, headers = nil)
8
+ if canvas
9
+ parameters = facebook_params(@default_request_params.merge(parameters || {}))
10
+ end
11
+ super method, path, parameters, headers
12
+ end
13
+
14
+ def reset!
15
+ self.default_request_params = {:fb_sig_in_canvas => '1', :fb_sig_api_key => Facebooker::Session.api_key}.with_indifferent_access
16
+ self.canvas = true
17
+ super
18
+ end
19
+
20
+ def get(path, parameters = nil, headers = nil)
21
+ if canvas
22
+ post path, (parameters || {}).merge('fb_sig_request_method' => 'GET'), headers
23
+ else
24
+ super path, parameters, headers
25
+ end
26
+ end
27
+
28
+ %w(put delete).each do |method|
29
+ define_method method do |*args|
30
+ if canvas
31
+ path, parameters, headers = *args
32
+ post path, (parameters || {}).merge('_method' => method.upcase), headers
33
+ else
34
+ super *args
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,42 @@
1
+ module Facebooker
2
+ module Rails
3
+ module ProfilePublisherExtensions
4
+
5
+ ##
6
+ # returns true if Facebook is requesting the interface for a profile publisher
7
+ def wants_interface?
8
+ params[:method] == "publisher_getInterface"
9
+ end
10
+
11
+ ##
12
+ # render the interface for a publisher.
13
+ # fbml is the content in string form. Use render_to_string to get the content from a template
14
+ # publish_enabled controlls whether the post form is active by default. If it isn't, you'll need to use fbjs to activate it
15
+ # comment_enabled controls whether to include a comment box
16
+ def render_publisher_interface(fbml,publish_enabled=true,comment_enabled=false)
17
+ render :json=>{:content=>{:fbml=>fbml,:publishEnabled=>publish_enabled,:commentEnabled=>comment_enabled},
18
+ :method=>"publisher_getInterface"}
19
+ end
20
+
21
+ # render an error while publishing the template
22
+ # This can be used for validation errors
23
+ def render_publisher_error(title,body)
24
+ render :json=>{:errorCode=>1,:errorTitle=>title,:errorMessage=>body}.to_json
25
+ end
26
+
27
+ # render the response for a feed. This takes a user_action object like those returned from the Rails Publisher
28
+ # For instance, AttackPublisher.create_attack(@attack)
29
+ # The template must have been registered previously
30
+ def render_publisher_response(user_action)
31
+ render :json=>{:content=> {
32
+ :feed=>{
33
+ :template_id=>user_action.template_id,
34
+ :template_data=>user_action.data
35
+ }
36
+ },
37
+ :method=>"publisher_getFeedStory"
38
+ }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,599 @@
1
+ module Facebooker
2
+ module Rails
3
+ # ActionMailer like module for publishing Facbook messages
4
+ #
5
+ # To use, create a subclass and define methods
6
+ # Each method should start by calling send_as to specify the type of message
7
+ # Valid options are :email and :notification, :user_action, :profile, :ref, :publish_stream
8
+ #
9
+ #
10
+ # Below is an example of each type
11
+ #
12
+ # class TestPublisher < Facebooker::Rails::Publisher
13
+ # # The new message templates are supported as well
14
+ # # First, create a method that contains your templates:
15
+ # # You may include multiple one line story templates and short story templates
16
+ # # but only one full story template
17
+ # # Your most specific template should be first
18
+ # #
19
+ # # Before using, you must register your template by calling register. For this example
20
+ # # You would call TestPublisher.register_publish_action
21
+ # # Registering the template will store the template id returned from Facebook in the
22
+ # # facebook_templates table that is created when you create your first publisher
23
+ # def publish_action_template
24
+ # one_line_story_template "{*actor*} did stuff with {*friend*}"
25
+ # one_line_story_template "{*actor*} did stuff"
26
+ # short_story_template "{*actor*} has a title {*friend*}", render(:partial=>"short_body")
27
+ # short_story_template "{*actor*} has a title", render(:partial=>"short_body")
28
+ # full_story_template "{*actor*} has a title {*friend*}", render(:partial=>"full_body")
29
+ # action_links action_link("My text {*template_var*}","{*link_url*}")
30
+ # end
31
+ #
32
+ # # To send a registered template, you need to create a method to set the data
33
+ # # The publisher will look up the template id from the facebook_templates table
34
+ # def publish_action(f)
35
+ # send_as :user_action
36
+ # from f
37
+ # story_size SHORT # or ONE_LINE or FULL
38
+ # data :friend=>"Mike"
39
+ # end
40
+ #
41
+ #
42
+ # # Provide a from user to send a general notification
43
+ # # if from is nil, this will send an announcement
44
+ # def notification(to,f)
45
+ # send_as :notification
46
+ # recipients to
47
+ # from f
48
+ # fbml "Not"
49
+ # end
50
+ #
51
+ # def email(to,f)
52
+ # send_as :email
53
+ # recipients to
54
+ # from f
55
+ # title "Email"
56
+ # fbml 'text'
57
+ # text fbml
58
+ # end
59
+ # # This will render the profile in /users/profile.fbml.erb
60
+ # # it will set @user to user_to_update in the template
61
+ # # The mobile profile will be rendered from the app/views/test_publisher/_mobile.erb
62
+ # # template
63
+ # def profile_update(user_to_update,user_with_session_to_use)
64
+ # send_as :profile
65
+ # from user_with_session_to_use
66
+ # recipients user_to_update
67
+ # profile render(:file=>"users/profile.fbml.erb",:assigns=>{:user=>user_to_update})
68
+ # profile_action "A string"
69
+ # mobile_profile render(:partial=>"mobile",:assigns=>{:user=>user_to_update})
70
+ # end
71
+ #
72
+ # # Update the given handle ref with the content from a
73
+ # # template
74
+ # def ref_update(user)
75
+ # send_as :ref
76
+ # from user
77
+ # fbml render(:file=>"users/profile",:assigns=>{:user=>user_to_update})
78
+ # handle "a_ref_handle"
79
+ # end
80
+ #
81
+ # # Publish a post into the stream on the user's Wall and News Feed.
82
+ # def publish_stream(user_with_session_to_use, user_to_update, params)
83
+ # send_as :publish_stream
84
+ # from user_with_session_to_use
85
+ # target user_to_update
86
+ # attachment params[:attachment]
87
+ # message params[:message]
88
+ # action_links params[:action_links]
89
+ # end
90
+ #
91
+ #
92
+ # To send a message, use ActionMailer like semantics
93
+ # TestPublisher.deliver_action(@user)
94
+ #
95
+ # For testing, you may want to create an instance of the underlying message without sending it
96
+ # TestPublisher.create_action(@user)
97
+ # will create and return an instance of Facebooker::Feeds::Action
98
+ #
99
+ # Publisher makes many helpers available, including the linking and asset helpers
100
+ class Publisher
101
+
102
+ #story sizes from the Facebooker API
103
+ ONE_LINE=1
104
+ SHORT=2
105
+ FULL=4
106
+
107
+ def initialize
108
+ @from = nil
109
+ @full_story_template = nil
110
+ @recipients = nil
111
+ @controller = PublisherController.new(self)
112
+ @action_links = nil
113
+ end
114
+
115
+ def self.default_url_options
116
+ {:host => Facebooker.canvas_server_base + Facebooker.facebook_path_prefix}
117
+ end
118
+
119
+ def default_url_options
120
+ self.class.default_url_options
121
+ end
122
+
123
+ # use facebook options everywhere
124
+ def request_comes_from_facebook?
125
+ true
126
+ end
127
+
128
+ class FacebookTemplate < ::ActiveRecord::Base
129
+ cattr_accessor :template_cache
130
+ self.template_cache = {}
131
+
132
+ def self.inspect(*args)
133
+ "FacebookTemplate"
134
+ end
135
+
136
+ def template_changed?(hash)
137
+ if respond_to?(:content_hash)
138
+ content_hash != hash
139
+ else
140
+ false
141
+ end
142
+ end
143
+
144
+ def deactivate
145
+ Facebooker::Session.create.deactivate_template_bundle_by_id(self.bundle_id)
146
+ return true
147
+ rescue Facebooker::Session::TemplateBundleInvalid => e
148
+ return false
149
+ end
150
+
151
+
152
+
153
+ class << self
154
+
155
+ def register(klass,method)
156
+ publisher = setup_publisher(klass,method)
157
+ template_id = Facebooker::Session.create.register_template_bundle(publisher.one_line_story_templates,publisher.short_story_templates,publisher.full_story_template,publisher.action_links)
158
+ template = find_or_initialize_by_template_name(template_name(klass,method))
159
+ template.deactivate if template.bundle_id # deactivate old templates to avoid exceeding templates/app limit
160
+ template.bundle_id = template_id
161
+ template.content_hash = hashed_content(klass,method) if template.respond_to?(:content_hash)
162
+ template.save!
163
+ cache(klass,method,template)
164
+ template
165
+ end
166
+
167
+ def for_class_and_method(klass,method)
168
+ find_cached(klass,method)
169
+ end
170
+ def bundle_id_for_class_and_method(klass,method)
171
+ for_class_and_method(klass,method).bundle_id
172
+ end
173
+
174
+ def cache(klass,method,template)
175
+ template_cache[template_name(klass,method)] = template
176
+ end
177
+
178
+ def clear_cache!
179
+ self.template_cache = {}
180
+ end
181
+
182
+ def find_cached(klass,method)
183
+ template_cache[template_name(klass,method)] || find_in_db(klass,method)
184
+ end
185
+
186
+ def find_in_db(klass,method)
187
+ template = find_by_template_name(template_name(klass,method))
188
+
189
+ if template.nil? || template.template_changed?(hashed_content(klass, method))
190
+ template = register(klass,method)
191
+ end
192
+ template
193
+ end
194
+
195
+ def setup_publisher(klass,method)
196
+ publisher = klass.new
197
+ publisher.send method + '_template'
198
+ publisher
199
+ end
200
+
201
+ def hashed_content(klass, method)
202
+ publisher = setup_publisher(klass,method)
203
+ # sort the Hash elements (in the short_story and full_story) before generating MD5
204
+ Digest::MD5.hexdigest [publisher.one_line_story_templates,
205
+ (publisher.short_story_templates and publisher.short_story_templates.collect{|ss| ss.to_a.sort_by{|e| e[0].to_s}}),
206
+ (publisher.full_story_template and publisher.full_story_template.to_a.sort_by{|e| e[0].to_s})
207
+ ].to_json
208
+ end
209
+
210
+ def template_name(klass,method)
211
+ "#{Facebooker.api_key}: #{klass.name}::#{method}"
212
+ end
213
+ end
214
+ end
215
+
216
+ class_inheritable_accessor :master_helper_module
217
+ attr_accessor :one_line_story_templates, :short_story_templates
218
+ attr_writer :action_links
219
+
220
+ cattr_accessor :skip_registry
221
+ self.skip_registry = false
222
+
223
+
224
+ class InvalidSender < StandardError; end
225
+ class UnknownBodyType < StandardError; end
226
+ class UnspecifiedBodyType < StandardError; end
227
+ class Email
228
+ attr_accessor :title
229
+ attr_accessor :text
230
+ attr_accessor :fbml
231
+ end
232
+
233
+ class Notification
234
+ attr_accessor :fbml
235
+ end
236
+
237
+ class Profile
238
+ attr_accessor :profile
239
+ attr_accessor :profile_action
240
+ attr_accessor :mobile_profile
241
+ attr_accessor :profile_main
242
+ end
243
+ class Ref
244
+ attr_accessor :handle
245
+ attr_accessor :fbml
246
+ end
247
+ class UserAction
248
+ attr_accessor :data
249
+ attr_reader :target_ids
250
+ attr_accessor :body_general
251
+ attr_accessor :template_id
252
+ attr_accessor :template_name
253
+ attr_accessor :story_size
254
+
255
+ def target_ids=(val)
256
+ @target_ids = val.is_a?(Array) ? val.join(",") : val
257
+ end
258
+
259
+ def data_hash
260
+ data||{}
261
+ end
262
+ end
263
+
264
+ class PublishStream
265
+ attr_accessor :target
266
+ attr_accessor :attachment
267
+ attr_accessor :action_links
268
+ attr_accessor :message
269
+ end
270
+
271
+ cattr_accessor :ignore_errors
272
+ attr_accessor :_body
273
+
274
+ def recipients(*args)
275
+ if args.size==0
276
+ @recipients
277
+ else
278
+ @recipients=args.first
279
+ end
280
+ end
281
+
282
+ def from(*args)
283
+ if args.size==0
284
+ @from
285
+ else
286
+ @from=args.first
287
+ end
288
+ end
289
+
290
+
291
+ def send_as(option)
292
+ self._body=case option
293
+ when :action
294
+ Facebooker::Feed::Action.new
295
+ when :story
296
+ Facebooker::Feed::Story.new
297
+ when :templatized_action
298
+ Facebooker::Feed::TemplatizedAction.new
299
+ when :notification
300
+ Notification.new
301
+ when :email
302
+ Email.new
303
+ when :profile
304
+ Profile.new
305
+ when :ref
306
+ Ref.new
307
+ when :user_action
308
+ UserAction.new
309
+ when :publish_stream
310
+ StreamPost.new
311
+ else
312
+ raise UnknownBodyType.new("Unknown type to publish")
313
+ end
314
+ end
315
+
316
+ def full_story_template(title=nil,body=nil,params={})
317
+ if title.nil?
318
+ @full_story_template
319
+ else
320
+ @full_story_template=params.merge(:template_title=>title, :template_body=>body)
321
+ end
322
+ end
323
+
324
+ def one_line_story_template(str)
325
+ @one_line_story_templates ||= []
326
+ @one_line_story_templates << str
327
+ end
328
+
329
+ def short_story_template(title,body,params={})
330
+ @short_story_templates ||= []
331
+ @short_story_templates << params.merge(:template_title=>title, :template_body=>body)
332
+ end
333
+
334
+ def action_links(*links)
335
+ if self._body and self._body.respond_to?(:action_links)
336
+ self._body.send(:action_links,*links)
337
+ end
338
+ if links.blank?
339
+ @action_links
340
+ else
341
+ @action_links = links
342
+ end
343
+ end
344
+
345
+ def method_missing(name,*args)
346
+ if args.size==1 and self._body.respond_to?("#{name}=")
347
+ self._body.send("#{name}=",*args)
348
+ elsif self._body.respond_to?(name)
349
+ self._body.send(name,*args)
350
+ else
351
+ super
352
+ end
353
+ end
354
+
355
+ # work around the fact that facebook cares about the order of the keys in the hash
356
+ class ImageHolder
357
+ attr_accessor :src,:href
358
+ def initialize(src,href)
359
+ self.src=src
360
+ self.href=href
361
+ end
362
+
363
+ def ==(other)
364
+ self.src == other.src && self.href == other.href
365
+ end
366
+
367
+ def to_json(*args)
368
+ "{\"src\":#{src.to_json}, \"href\":#{href.to_json}}"
369
+ end
370
+ end
371
+
372
+ def image(src,target)
373
+ ImageHolder.new(image_path(src),target.respond_to?(:to_str) ? target : url_for(target))
374
+ end
375
+
376
+ def action_link(text,target)
377
+ {:text=>text, :href=>target}
378
+ end
379
+
380
+ def requires_from_user?(from,body)
381
+ ! (announcement_notification?(from,body) or ref_update?(body) or profile_update?(body))
382
+ end
383
+
384
+ def profile_update?(body)
385
+ body.is_a?(Profile)
386
+ end
387
+
388
+ def ref_update?(body)
389
+ body.is_a?(Ref)
390
+ end
391
+
392
+ def announcement_notification?(from,body)
393
+ from.nil? and body.is_a?(Notification)
394
+ end
395
+
396
+ def send_message(method)
397
+ @recipients = @recipients.is_a?(Array) ? @recipients : [@recipients]
398
+ if from.nil? and @recipients.size==1 and requires_from_user?(from,_body)
399
+ @from = @recipients.first
400
+ end
401
+ # notifications can
402
+ # omit the from address
403
+ raise InvalidSender.new("Sender must be a Facebooker::User") unless from.is_a?(Facebooker::User) || !requires_from_user?(from,_body)
404
+ case _body
405
+ when Facebooker::Feed::TemplatizedAction,Facebooker::Feed::Action
406
+ from.publish_action(_body)
407
+ when Facebooker::Feed::Story
408
+ @recipients.each {|r| r.publish_story(_body)}
409
+ when Notification
410
+ (from.nil? ? Facebooker::Session.create : from.session).send_notification(@recipients,_body.fbml)
411
+ when Email
412
+ from.session.send_email(@recipients,
413
+ _body.title,
414
+ _body.text,
415
+ _body.fbml)
416
+ when Profile
417
+ # If recipient and from aren't the same person, create a new user object using the
418
+ # userid from recipient and the session from from
419
+ @from = Facebooker::User.new(Facebooker::User.cast_to_facebook_id(@recipients.first),Facebooker::Session.create)
420
+ @from.set_profile_fbml(_body.profile, _body.mobile_profile, _body.profile_action, _body.profile_main)
421
+ when Ref
422
+ Facebooker::Session.create.server_cache.set_ref_handle(_body.handle,_body.fbml)
423
+ when UserAction
424
+ @from.session.publish_user_action(_body.template_id,_body.data_hash,_body.target_ids,_body.body_general,_body.story_size)
425
+ when Facebooker::StreamPost
426
+ @from.publish_to(_body.target, {:attachment => _body.attachment, :action_links => @action_links, :message => _body.message })
427
+ else
428
+ raise UnspecifiedBodyType.new("You must specify a valid send_as")
429
+ end
430
+ end
431
+
432
+ # nodoc
433
+ # needed for actionview
434
+ def logger
435
+ RAILS_DEFAULT_LOGGER
436
+ end
437
+
438
+ # nodoc
439
+ # delegate to action view. Set up assigns and render
440
+ def render(opts)
441
+ opts = opts.dup
442
+ body = opts.delete(:assigns) || {}
443
+ initialize_template_class(body.dup.merge(:controller=>self)).render(opts)
444
+ end
445
+
446
+
447
+ def initialize_template_class(assigns)
448
+ template_root = "#{RAILS_ROOT}/app/views"
449
+ controller_root = File.join(template_root,self.class.controller_path)
450
+ #only do this on Rails 2.1
451
+ if ActionController::Base.respond_to?(:append_view_path)
452
+ # only add the view path once
453
+ unless ActionController::Base.view_paths.include?(controller_root)
454
+ ActionController::Base.append_view_path(controller_root)
455
+ ActionController::Base.append_view_path(controller_root+"/..")
456
+ end
457
+ view_paths = ActionController::Base.view_paths
458
+ else
459
+ view_paths = [template_root, controller_root]
460
+ end
461
+ returning ActionView::Base.new(view_paths, assigns, self) do |template|
462
+ template.controller=self
463
+ template.extend(self.class.master_helper_module)
464
+ def template.request_comes_from_facebook?
465
+ true
466
+ end
467
+ end
468
+ end
469
+
470
+
471
+ self.master_helper_module = Module.new
472
+ self.master_helper_module.module_eval do
473
+ # url_helper delegates to @controller,
474
+ # so we need to define that in the template
475
+ # we make it point to the publisher
476
+ include ActionView::Helpers::UrlHelper
477
+ include ActionView::Helpers::TextHelper
478
+ include ActionView::Helpers::TagHelper
479
+ include ActionView::Helpers::FormHelper
480
+ include ActionView::Helpers::FormTagHelper
481
+ include ActionView::Helpers::AssetTagHelper
482
+ include ActionView::Helpers::NumberHelper
483
+ include Facebooker::Rails::Helpers
484
+
485
+ #define this for the publisher views
486
+ def protect_against_forgery?
487
+ @paf ||= ActionController::Base.new.send(:protect_against_forgery?)
488
+ end
489
+
490
+ # url_for calls in publishers tend to want full paths
491
+ def url_for(options = {})
492
+ super(options.kind_of?(Hash) ? {:only_path => false}.update(options) : options)
493
+ end
494
+ end
495
+ ActionController::Routing::Routes.named_routes.install(self.master_helper_module)
496
+ include self.master_helper_module
497
+ class <<self
498
+
499
+ def register_all_templates_on_all_applications
500
+ Facebooker.with_all_applications do
501
+ puts "Registering templates for #{Facebooker.api_key}"
502
+ register_all_templates
503
+ end
504
+ end
505
+
506
+ def register_all_templates
507
+ all_templates = instance_methods.grep(/_template$/) - %w(short_story_template full_story_template one_line_story_template)
508
+ all_templates.each do |template|
509
+ template_name=template.sub(/_template$/,"")
510
+ puts "Registering #{template_name}"
511
+ send("register_"+template_name)
512
+ end
513
+ end
514
+
515
+ def unregister_inactive_templates
516
+ session = Facebooker::Session.create
517
+ active_template_ids = FacebookTemplate.all.map(&:bundle_id)
518
+ all_template_ids = session.active_template_bundles.map {|t| t["template_bundle_id"]}
519
+ (all_template_ids - active_template_ids).each do |template_bundle_id|
520
+ session.deactivate_template_bundle_by_id(template_bundle_id)
521
+ end
522
+ end
523
+
524
+ def method_missing(name,*args)
525
+ should_send = false
526
+ method = ''
527
+ if md = /^create_(.*)$/.match(name.to_s)
528
+ method = md[1]
529
+ elsif md = /^deliver_(.*)$/.match(name.to_s)
530
+ method = md[1]
531
+ should_send = true
532
+ elsif md = /^register_(.*)$/.match(name.to_s)
533
+ return FacebookTemplate.register(self, md[1])
534
+ else
535
+ super
536
+ end
537
+
538
+ #now create the item
539
+ (publisher=new).send(method,*args)
540
+ case publisher._body
541
+ when UserAction
542
+ publisher._body.template_name = method
543
+ publisher._body.template_id ||= FacebookTemplate.bundle_id_for_class_and_method(self,method)
544
+ end
545
+
546
+ should_send ? publisher.send_message(method) : publisher._body
547
+ end
548
+
549
+ def controller_path
550
+ self.to_s.underscore
551
+ end
552
+
553
+ def helper(*args)
554
+ args.each do |arg|
555
+ case arg
556
+ when Symbol,String
557
+ add_template_helper("#{arg.to_s.camelcase}Helper".constantize)
558
+ when Module
559
+ add_template_helper(arg)
560
+ end
561
+ end
562
+ end
563
+
564
+ def add_template_helper(helper_module) #:nodoc:
565
+ master_helper_module.send :include,helper_module
566
+ include master_helper_module
567
+ end
568
+
569
+
570
+ def inherited(child)
571
+ super
572
+ child.master_helper_module=Module.new
573
+ child.master_helper_module.__send__(:include,self.master_helper_module)
574
+ child.send(:include, child.master_helper_module)
575
+ FacebookTemplate.clear_cache!
576
+ end
577
+
578
+ end
579
+ class PublisherController
580
+ include Facebooker::Rails::Publisher.master_helper_module
581
+ include ActionController::UrlWriter
582
+
583
+ def initialize(source)
584
+ self.class.url_option_source = source
585
+ end
586
+
587
+ class << self
588
+ attr_accessor :url_option_source
589
+ alias :old_default_url_options :default_url_options
590
+ def default_url_options(*args)
591
+ url_option_source.default_url_options(*args)
592
+ end
593
+ end
594
+
595
+ end
596
+
597
+ end
598
+ end
599
+ end