fs-facebooker 1.0.37

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 (135) hide show
  1. data/.autotest +15 -0
  2. data/CHANGELOG.rdoc +24 -0
  3. data/COPYING.rdoc +19 -0
  4. data/Manifest.txt +133 -0
  5. data/README.rdoc +104 -0
  6. data/Rakefile +85 -0
  7. data/TODO.rdoc +4 -0
  8. data/examples/desktop_login.rb +14 -0
  9. data/facebooker.gemspec +38 -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 +83 -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/xd_receiver_generator.rb +10 -0
  43. data/init.rb +25 -0
  44. data/install.rb +12 -0
  45. data/lib/facebooker.rb +179 -0
  46. data/lib/facebooker/adapters/adapter_base.rb +91 -0
  47. data/lib/facebooker/adapters/bebo_adapter.rb +77 -0
  48. data/lib/facebooker/adapters/facebook_adapter.rb +52 -0
  49. data/lib/facebooker/admin.rb +42 -0
  50. data/lib/facebooker/batch_request.rb +45 -0
  51. data/lib/facebooker/data.rb +57 -0
  52. data/lib/facebooker/feed.rb +78 -0
  53. data/lib/facebooker/logging.rb +44 -0
  54. data/lib/facebooker/mobile.rb +20 -0
  55. data/lib/facebooker/mock/service.rb +50 -0
  56. data/lib/facebooker/mock/session.rb +18 -0
  57. data/lib/facebooker/model.rb +139 -0
  58. data/lib/facebooker/models/affiliation.rb +10 -0
  59. data/lib/facebooker/models/album.rb +11 -0
  60. data/lib/facebooker/models/applicationproperties.rb +39 -0
  61. data/lib/facebooker/models/applicationrestrictions.rb +10 -0
  62. data/lib/facebooker/models/cookie.rb +10 -0
  63. data/lib/facebooker/models/education_info.rb +11 -0
  64. data/lib/facebooker/models/event.rb +28 -0
  65. data/lib/facebooker/models/friend_list.rb +16 -0
  66. data/lib/facebooker/models/group.rb +36 -0
  67. data/lib/facebooker/models/info_item.rb +10 -0
  68. data/lib/facebooker/models/info_section.rb +10 -0
  69. data/lib/facebooker/models/location.rb +8 -0
  70. data/lib/facebooker/models/notifications.rb +17 -0
  71. data/lib/facebooker/models/page.rb +28 -0
  72. data/lib/facebooker/models/photo.rb +19 -0
  73. data/lib/facebooker/models/tag.rb +12 -0
  74. data/lib/facebooker/models/user.rb +497 -0
  75. data/lib/facebooker/models/video.rb +9 -0
  76. data/lib/facebooker/models/work_info.rb +10 -0
  77. data/lib/facebooker/parser.rb +642 -0
  78. data/lib/facebooker/rails/backwards_compatible_param_checks.rb +31 -0
  79. data/lib/facebooker/rails/controller.rb +344 -0
  80. data/lib/facebooker/rails/cucumber.rb +28 -0
  81. data/lib/facebooker/rails/cucumber/world.rb +46 -0
  82. data/lib/facebooker/rails/extensions/action_controller.rb +48 -0
  83. data/lib/facebooker/rails/extensions/rack_setup.rb +6 -0
  84. data/lib/facebooker/rails/extensions/routing.rb +15 -0
  85. data/lib/facebooker/rails/facebook_form_builder.rb +112 -0
  86. data/lib/facebooker/rails/facebook_pretty_errors.rb +22 -0
  87. data/lib/facebooker/rails/facebook_request_fix.rb +30 -0
  88. data/lib/facebooker/rails/facebook_request_fix_2-3.rb +31 -0
  89. data/lib/facebooker/rails/facebook_session_handling.rb +68 -0
  90. data/lib/facebooker/rails/facebook_url_helper.rb +192 -0
  91. data/lib/facebooker/rails/facebook_url_rewriting.rb +60 -0
  92. data/lib/facebooker/rails/helpers.rb +794 -0
  93. data/lib/facebooker/rails/helpers/fb_connect.rb +118 -0
  94. data/lib/facebooker/rails/integration_session.rb +38 -0
  95. data/lib/facebooker/rails/profile_publisher_extensions.rb +42 -0
  96. data/lib/facebooker/rails/publisher.rb +550 -0
  97. data/lib/facebooker/rails/routing.rb +49 -0
  98. data/lib/facebooker/rails/test_helpers.rb +68 -0
  99. data/lib/facebooker/rails/utilities.rb +22 -0
  100. data/lib/facebooker/server_cache.rb +24 -0
  101. data/lib/facebooker/service.rb +102 -0
  102. data/lib/facebooker/session.rb +606 -0
  103. data/lib/facebooker/version.rb +9 -0
  104. data/lib/net/http_multipart_post.rb +123 -0
  105. data/lib/rack/facebook.rb +77 -0
  106. data/lib/tasks/facebooker.rake +18 -0
  107. data/lib/tasks/tunnel.rake +46 -0
  108. data/rails/init.rb +1 -0
  109. data/setup.rb +1585 -0
  110. data/templates/layout.erb +24 -0
  111. data/test/facebooker/adapters_test.rb +96 -0
  112. data/test/facebooker/admin_test.rb +102 -0
  113. data/test/facebooker/batch_request_test.rb +83 -0
  114. data/test/facebooker/data_test.rb +86 -0
  115. data/test/facebooker/logging_test.rb +43 -0
  116. data/test/facebooker/mobile_test.rb +45 -0
  117. data/test/facebooker/model_test.rb +133 -0
  118. data/test/facebooker/models/event_test.rb +15 -0
  119. data/test/facebooker/models/photo_test.rb +16 -0
  120. data/test/facebooker/models/user_test.rb +343 -0
  121. data/test/facebooker/rails/facebook_request_fix_2-3_test.rb +24 -0
  122. data/test/facebooker/rails/facebook_url_rewriting_test.rb +39 -0
  123. data/test/facebooker/rails/publisher_test.rb +481 -0
  124. data/test/facebooker/rails_integration_test.rb +1398 -0
  125. data/test/facebooker/server_cache_test.rb +44 -0
  126. data/test/facebooker/session_test.rb +614 -0
  127. data/test/facebooker_test.rb +951 -0
  128. data/test/fixtures/multipart_post_body_with_only_parameters.txt +33 -0
  129. data/test/fixtures/multipart_post_body_with_single_file.txt +38 -0
  130. data/test/fixtures/multipart_post_body_with_single_file_that_has_nil_key.txt +38 -0
  131. data/test/net/http_multipart_post_test.rb +52 -0
  132. data/test/rack/facebook_test.rb +61 -0
  133. data/test/rails_test_helper.rb +27 -0
  134. data/test/test_helper.rb +74 -0
  135. metadata +232 -0
@@ -0,0 +1,118 @@
1
+ module Facebooker
2
+ module Rails
3
+ module Helpers
4
+ module FbConnect
5
+
6
+ def fb_connect_javascript_tag
7
+ if request.ssl?
8
+ javascript_include_tag "https://www.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php"
9
+ else
10
+ javascript_include_tag "http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php"
11
+ end
12
+ end
13
+
14
+ #
15
+ # For information on the :app_settings argument see http://wiki.developers.facebook.com/index.php/JS_API_M_FB.Facebook.Init_2
16
+ # 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
17
+ # and Rails' Hash#to_json always quotes strings so there is no way to indicate when the value should be a javascript function.
18
+ # For this reason :app_settings needs to be a string that is valid JSON (including the {}'s).
19
+ #
20
+ def init_fb_connect(*required_features,&proc)
21
+ additions = ""
22
+ if block_given?
23
+ additions = capture(&proc)
24
+ end
25
+
26
+ # Yes, app_settings is set to a string of an empty JSON element. That's intentional.
27
+ options = {:js => :prototype, :app_settings => '{}'}
28
+
29
+ if required_features.last.is_a?(Hash)
30
+ options.merge!(required_features.pop.symbolize_keys)
31
+ end
32
+
33
+ if request.ssl?
34
+ init_string = "FB.Facebook.init('#{Facebooker.api_key}','/xd_receiver_ssl.html', #{options[:app_settings]});"
35
+ else
36
+ init_string = "FB.Facebook.init('#{Facebooker.api_key}','/xd_receiver.html', #{options[:app_settings]});"
37
+ end
38
+ unless required_features.blank?
39
+ init_string = <<-FBML
40
+ #{case options[:js]
41
+ when :jquery then "$(document).ready("
42
+ when :dojo then "dojo.addOnLoad("
43
+ else "Element.observe(window,'load',"
44
+ end} function() {
45
+ FB_RequireFeatures(#{required_features.to_json}, function() {
46
+ #{init_string}
47
+ #{additions}
48
+ });
49
+ });
50
+ FBML
51
+ end
52
+
53
+ # block_is_within_action_view? is rails 2.1.x and has been
54
+ # deprecated. rails >= 2.2.x uses block_called_from_erb?
55
+ block_tester = respond_to?(:block_is_within_action_view?) ?
56
+ :block_is_within_action_view? : :block_called_from_erb?
57
+
58
+ if block_given? && send(block_tester, proc)
59
+ concat(javascript_tag(init_string))
60
+ else
61
+ javascript_tag init_string
62
+ end
63
+ end
64
+
65
+ # Render an <fb:login-button> element
66
+ #
67
+ # ==== Examples
68
+ #
69
+ # <%= fb_login_button%>
70
+ # => <fb:login-button></fb:login-button>
71
+ #
72
+ # Specifying a javascript callback
73
+ #
74
+ # <%= fb_login_button 'update_something();'%>
75
+ # => <fb:login-button onlogin='update_something();'></fb:login-button>
76
+ #
77
+ # Adding options <em>See:</em> http://wiki.developers.facebook.com/index.php/Fb:login-button
78
+ #
79
+ # <%= fb_login_button 'update_something();', :size => :small, :background => :dark%>
80
+ # => <fb:login-button background='dark' onlogin='update_something();' size='small'></fb:login-button>
81
+ #
82
+ def fb_login_button(*args)
83
+
84
+ callback = args.first
85
+ options = args[1] || {}
86
+ options.merge!(:onlogin=>callback)if callback
87
+
88
+ content_tag("fb:login-button",nil, options)
89
+ end
90
+
91
+ def fb_login_and_redirect(url, options = {})
92
+ js = update_page do |page|
93
+ page.redirect_to url
94
+ end
95
+ content_tag("fb:login-button",nil,options.merge(:onlogin=>js))
96
+ end
97
+
98
+ def fb_unconnected_friends_count
99
+ content_tag "fb:unconnected-friends-count",nil
100
+ end
101
+
102
+ def fb_logout_link(text,url,*args)
103
+ js = update_page do |page|
104
+ page.call "FB.Connect.logoutAndRedirect",url
105
+ end
106
+ link_to_function text, js, *args
107
+ end
108
+
109
+ def fb_user_action(action, user_message = nil, prompt = "", callback = nil)
110
+ update_page do |page|
111
+ page.call("FB.Connect.showFeedDialog",action.template_id,action.data,action.target_ids,action.body_general,nil,page.literal("FB.RequireConnect.promptConnect"),callback,prompt,user_message.nil? ? nil : {:value=>user_message})
112
+ end
113
+ end
114
+
115
+ end
116
+ end
117
+ end
118
+ 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'}.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,550 @@
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
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
+ #
82
+ # To send a message, use ActionMailer like semantics
83
+ # TestPublisher.deliver_action(@user)
84
+ #
85
+ # For testing, you may want to create an instance of the underlying message without sending it
86
+ # TestPublisher.create_action(@user)
87
+ # will create and return an instance of Facebooker::Feeds::Action
88
+ #
89
+ # Publisher makes many helpers available, including the linking and asset helpers
90
+ class Publisher
91
+
92
+ #story sizes from the Facebooker API
93
+ ONE_LINE=1
94
+ SHORT=2
95
+ FULL=4
96
+
97
+ def initialize
98
+ @from = nil
99
+ @full_story_template = nil
100
+ @recipients = nil
101
+ @controller = PublisherController.new(self)
102
+ end
103
+
104
+ def default_url_options
105
+ {:host => Facebooker.canvas_server_base + Facebooker.facebook_path_prefix}
106
+ end
107
+
108
+ # use facebook options everywhere
109
+ def request_comes_from_facebook?
110
+ true
111
+ end
112
+
113
+ class FacebookTemplate < ::ActiveRecord::Base
114
+ cattr_accessor :template_cache
115
+ self.template_cache = {}
116
+
117
+ def self.inspect(*args)
118
+ "FacebookTemplate"
119
+ end
120
+
121
+ def template_changed?(hash)
122
+ if respond_to?(:content_hash)
123
+ content_hash != hash
124
+ else
125
+ false
126
+ end
127
+ end
128
+
129
+
130
+
131
+ class << self
132
+
133
+ def register(klass,method)
134
+ publisher = setup_publisher(klass,method)
135
+ template_id = Facebooker::Session.create.register_template_bundle(publisher.one_line_story_templates,publisher.short_story_templates,publisher.full_story_template,publisher.action_links)
136
+ template = find_or_initialize_by_template_name(template_name(klass,method))
137
+ template.bundle_id = template_id
138
+ template.content_hash = hashed_content(klass,method) if template.respond_to?(:content_hash)
139
+ template.save!
140
+ cache(klass,method,template)
141
+ template
142
+ end
143
+
144
+ def for_class_and_method(klass,method)
145
+ find_cached(klass,method)
146
+ end
147
+ def bundle_id_for_class_and_method(klass,method)
148
+ for_class_and_method(klass,method).bundle_id
149
+ end
150
+
151
+ def cache(klass,method,template)
152
+ template_cache[template_name(klass,method)] = template
153
+ end
154
+
155
+ def clear_cache!
156
+ self.template_cache = {}
157
+ end
158
+
159
+ def find_cached(klass,method)
160
+ template_cache[template_name(klass,method)] || find_in_db(klass,method)
161
+ end
162
+
163
+ def find_in_db(klass,method)
164
+ template = find_by_template_name(template_name(klass,method))
165
+ if template and template.template_changed?(hashed_content(klass,method))
166
+ template.destroy
167
+ template = nil
168
+ end
169
+
170
+ if template.nil?
171
+ template = register(klass,method)
172
+ end
173
+ template
174
+ end
175
+
176
+ def setup_publisher(klass,method)
177
+ publisher = klass.new
178
+ publisher.send method + '_template'
179
+ publisher
180
+ end
181
+
182
+ def hashed_content(klass, method)
183
+ publisher = setup_publisher(klass,method)
184
+ # sort the Hash elements (in the short_story and full_story) before generating MD5
185
+ Digest::MD5.hexdigest [publisher.one_line_story_templates,
186
+ (publisher.short_story_templates and publisher.short_story_templates.collect{|ss| ss.to_a.sort_by{|e| e[0].to_s}}),
187
+ (publisher.full_story_template and publisher.full_story_template.to_a.sort_by{|e| e[0].to_s})
188
+ ].to_json
189
+ end
190
+
191
+ def template_name(klass,method)
192
+ "#{klass.name}::#{method}"
193
+ end
194
+ end
195
+ end
196
+
197
+ class_inheritable_accessor :master_helper_module
198
+ attr_accessor :one_line_story_templates, :short_story_templates
199
+ attr_writer :action_links
200
+
201
+ cattr_accessor :skip_registry
202
+ self.skip_registry = false
203
+
204
+
205
+ class InvalidSender < StandardError; end
206
+ class UnknownBodyType < StandardError; end
207
+ class UnspecifiedBodyType < StandardError; end
208
+ class Email
209
+ attr_accessor :title
210
+ attr_accessor :text
211
+ attr_accessor :fbml
212
+ end
213
+
214
+ class Notification
215
+ attr_accessor :fbml
216
+ end
217
+
218
+ class Profile
219
+ attr_accessor :profile
220
+ attr_accessor :profile_action
221
+ attr_accessor :mobile_profile
222
+ attr_accessor :profile_main
223
+ end
224
+ class Ref
225
+ attr_accessor :handle
226
+ attr_accessor :fbml
227
+ end
228
+ class UserAction
229
+ attr_accessor :data
230
+ attr_reader :target_ids
231
+ attr_accessor :body_general
232
+ attr_accessor :template_id
233
+ attr_accessor :template_name
234
+ attr_accessor :story_size
235
+
236
+ def target_ids=(val)
237
+ @target_ids = val.is_a?(Array) ? val.join(",") : val
238
+ end
239
+
240
+ def data_hash
241
+ data||{}
242
+ end
243
+ end
244
+
245
+ cattr_accessor :ignore_errors
246
+ attr_accessor :_body
247
+
248
+ def recipients(*args)
249
+ if args.size==0
250
+ @recipients
251
+ else
252
+ @recipients=args.first
253
+ end
254
+ end
255
+
256
+ def from(*args)
257
+ if args.size==0
258
+ @from
259
+ else
260
+ @from=args.first
261
+ end
262
+ end
263
+
264
+
265
+ def send_as(option)
266
+ self._body=case option
267
+ when :action
268
+ Facebooker::Feed::Action.new
269
+ when :story
270
+ Facebooker::Feed::Story.new
271
+ when :templatized_action
272
+ Facebooker::Feed::TemplatizedAction.new
273
+ when :notification
274
+ Notification.new
275
+ when :email
276
+ Email.new
277
+ when :profile
278
+ Profile.new
279
+ when :ref
280
+ Ref.new
281
+ when :user_action
282
+ UserAction.new
283
+ else
284
+ raise UnknownBodyType.new("Unknown type to publish")
285
+ end
286
+ end
287
+
288
+ def full_story_template(title=nil,body=nil,params={})
289
+ if title.nil?
290
+ @full_story_template
291
+ else
292
+ @full_story_template=params.merge(:template_title=>title, :template_body=>body)
293
+ end
294
+ end
295
+
296
+ def one_line_story_template(str)
297
+ @one_line_story_templates ||= []
298
+ @one_line_story_templates << str
299
+ end
300
+
301
+ def short_story_template(title,body,params={})
302
+ @short_story_templates ||= []
303
+ @short_story_templates << params.merge(:template_title=>title, :template_body=>body)
304
+ end
305
+
306
+ def action_links(*links)
307
+ if links.blank?
308
+ @action_links
309
+ else
310
+ @action_links = links
311
+ end
312
+ end
313
+
314
+ def method_missing(name,*args)
315
+ if args.size==1 and self._body.respond_to?("#{name}=")
316
+ self._body.send("#{name}=",*args)
317
+ elsif self._body.respond_to?(name)
318
+ self._body.send(name,*args)
319
+ else
320
+ super
321
+ end
322
+ end
323
+
324
+ # work around the fact that facebook cares about the order of the keys in the hash
325
+ class ImageHolder
326
+ attr_accessor :src,:href
327
+ def initialize(src,href)
328
+ self.src=src
329
+ self.href=href
330
+ end
331
+
332
+ def ==(other)
333
+ self.src == other.src && self.href == other.href
334
+ end
335
+
336
+ def to_json(*args)
337
+ "{\"src\":#{src.to_json}, \"href\":#{href.to_json}}"
338
+ end
339
+ end
340
+
341
+ def image(src,target)
342
+ ImageHolder.new(image_path(src),target.respond_to?(:to_str) ? target : url_for(target))
343
+ end
344
+
345
+ def action_link(text,target)
346
+ {:text=>text, :href=>target}
347
+ end
348
+
349
+ def requires_from_user?(from,body)
350
+ ! (announcement_notification?(from,body) or ref_update?(body) or profile_update?(body))
351
+ end
352
+
353
+ def profile_update?(body)
354
+ body.is_a?(Profile)
355
+ end
356
+
357
+ def ref_update?(body)
358
+ body.is_a?(Ref)
359
+ end
360
+
361
+ def announcement_notification?(from,body)
362
+ from.nil? and body.is_a?(Notification)
363
+ end
364
+
365
+ def send_message(method)
366
+ @recipients = @recipients.is_a?(Array) ? @recipients : [@recipients]
367
+ if from.nil? and @recipients.size==1 and requires_from_user?(from,_body)
368
+ @from = @recipients.first
369
+ end
370
+ # notifications can
371
+ # omit the from address
372
+ raise InvalidSender.new("Sender must be a Facebooker::User") unless from.is_a?(Facebooker::User) || !requires_from_user?(from,_body)
373
+ case _body
374
+ when Facebooker::Feed::TemplatizedAction,Facebooker::Feed::Action
375
+ from.publish_action(_body)
376
+ when Facebooker::Feed::Story
377
+ @recipients.each {|r| r.publish_story(_body)}
378
+ when Notification
379
+ (from.nil? ? Facebooker::Session.create : from.session).send_notification(@recipients,_body.fbml)
380
+ when Email
381
+ from.session.send_email(@recipients,
382
+ _body.title,
383
+ _body.text,
384
+ _body.fbml)
385
+ when Profile
386
+ # If recipient and from aren't the same person, create a new user object using the
387
+ # userid from recipient and the session from from
388
+ @from = Facebooker::User.new(Facebooker::User.cast_to_facebook_id(@recipients.first),Facebooker::Session.create)
389
+ @from.set_profile_fbml(_body.profile, _body.mobile_profile, _body.profile_action, _body.profile_main)
390
+ when Ref
391
+ Facebooker::Session.create.server_cache.set_ref_handle(_body.handle,_body.fbml)
392
+ when UserAction
393
+ @from.session.publish_user_action(_body.template_id,_body.data_hash,_body.target_ids,_body.body_general,_body.story_size)
394
+ else
395
+ raise UnspecifiedBodyType.new("You must specify a valid send_as")
396
+ end
397
+ end
398
+
399
+ # nodoc
400
+ # needed for actionview
401
+ def logger
402
+ RAILS_DEFAULT_LOGGER
403
+ end
404
+
405
+ # nodoc
406
+ # delegate to action view. Set up assigns and render
407
+ def render(opts)
408
+ opts = opts.dup
409
+ body = opts.delete(:assigns) || {}
410
+ initialize_template_class(body.dup.merge(:controller=>self)).render(opts)
411
+ end
412
+
413
+
414
+ def initialize_template_class(assigns)
415
+ template_root = "#{RAILS_ROOT}/app/views"
416
+ controller_root = File.join(template_root,self.class.controller_path)
417
+ #only do this on Rails 2.1
418
+ if ActionController::Base.respond_to?(:append_view_path)
419
+ # only add the view path once
420
+ unless ActionController::Base.view_paths.include?(controller_root)
421
+ ActionController::Base.append_view_path(controller_root)
422
+ ActionController::Base.append_view_path(controller_root+"/..")
423
+ end
424
+ view_paths = ActionController::Base.view_paths
425
+ else
426
+ view_paths = [template_root, controller_root]
427
+ end
428
+ returning ActionView::Base.new(view_paths, assigns, self) do |template|
429
+ template.controller=self
430
+ template.extend(self.class.master_helper_module)
431
+ def template.request_comes_from_facebook?
432
+ true
433
+ end
434
+ end
435
+ end
436
+
437
+
438
+ self.master_helper_module = Module.new
439
+ self.master_helper_module.module_eval do
440
+ # url_helper delegates to @controller,
441
+ # so we need to define that in the template
442
+ # we make it point to the publisher
443
+ include ActionView::Helpers::UrlHelper
444
+ include ActionView::Helpers::TextHelper
445
+ include ActionView::Helpers::TagHelper
446
+ include ActionView::Helpers::FormHelper
447
+ include ActionView::Helpers::FormTagHelper
448
+ include ActionView::Helpers::AssetTagHelper
449
+ include ActionView::Helpers::NumberHelper
450
+ include Facebooker::Rails::Helpers
451
+
452
+ #define this for the publisher views
453
+ def protect_against_forgery?
454
+ @paf ||= ActionController::Base.new.send(:protect_against_forgery?)
455
+ end
456
+
457
+ # url_for calls in publishers tend to want full paths
458
+ def url_for(options = {})
459
+ super(options.kind_of?(Hash) ? {:only_path => false}.update(options) : options)
460
+ end
461
+ end
462
+ ActionController::Routing::Routes.named_routes.install(self.master_helper_module)
463
+ include self.master_helper_module
464
+ class <<self
465
+
466
+ def register_all_templates
467
+ all_templates = instance_methods.grep(/_template$/) - %w(short_story_template full_story_template one_line_story_template)
468
+ all_templates.each do |template|
469
+ template_name=template.sub(/_template$/,"")
470
+ puts "Registering #{template_name}"
471
+ send("register_"+template_name)
472
+ end
473
+ end
474
+
475
+ def method_missing(name,*args)
476
+ should_send = false
477
+ method = ''
478
+ if md = /^create_(.*)$/.match(name.to_s)
479
+ method = md[1]
480
+ elsif md = /^deliver_(.*)$/.match(name.to_s)
481
+ method = md[1]
482
+ should_send = true
483
+ elsif md = /^register_(.*)$/.match(name.to_s)
484
+ return FacebookTemplate.register(self, md[1])
485
+ else
486
+ super
487
+ end
488
+
489
+ #now create the item
490
+ (publisher=new).send(method,*args)
491
+ case publisher._body
492
+ when UserAction
493
+ publisher._body.template_name = method
494
+ publisher._body.template_id ||= FacebookTemplate.bundle_id_for_class_and_method(self,method)
495
+ end
496
+
497
+ should_send ? publisher.send_message(method) : publisher._body
498
+ end
499
+
500
+ def controller_path
501
+ self.to_s.underscore
502
+ end
503
+
504
+ def helper(*args)
505
+ args.each do |arg|
506
+ case arg
507
+ when Symbol,String
508
+ add_template_helper("#{arg.to_s.classify}Helper".constantize)
509
+ when Module
510
+ add_template_helper(arg)
511
+ end
512
+ end
513
+ end
514
+
515
+ def add_template_helper(helper_module) #:nodoc:
516
+ master_helper_module.send :include,helper_module
517
+ include master_helper_module
518
+ end
519
+
520
+
521
+ def inherited(child)
522
+ super
523
+ child.master_helper_module=Module.new
524
+ child.master_helper_module.__send__(:include,self.master_helper_module)
525
+ child.send(:include, child.master_helper_module)
526
+ FacebookTemplate.clear_cache!
527
+ end
528
+
529
+ end
530
+ class PublisherController
531
+ include Facebooker::Rails::Publisher.master_helper_module
532
+ include ActionController::UrlWriter
533
+
534
+ def initialize(source)
535
+ self.class.url_option_source = source
536
+ end
537
+
538
+ class << self
539
+ attr_accessor :url_option_source
540
+ alias :old_default_url_options :default_url_options
541
+ def default_url_options(*args)
542
+ url_option_source.default_url_options(*args)
543
+ end
544
+ end
545
+
546
+ end
547
+
548
+ end
549
+ end
550
+ end