Commit 1e0cc2a4 authored by Bráulio Bhavamitra's avatar Bráulio Bhavamitra
Browse files

Merge branch 'fb_app' into 'master'

fb_app: plugin to create FB page tabs and share to FB timeline

Depends on !512

See merge request !513
parents c5776ba3 69b83fad
gem 'slim'
# for backwards compatibility of serialized objects
gem 'fb_graph'
gem 'fb_graph2'
gem 'facebook-signed-request'
page_tab:
use_test_app: false
timeline:
use_test_app: true
test_users:
- identifier1
- identifier1
app:
id: xxx
secret: xxx
domain: domainconfigured.net
open_graph:
namespace: app_name
objects:
blog_post: article
community: community
enterprise: sse_initiative
favorite_enterprise: sse_initiative
forum: discussion
event: event
friend: friend
gallery_image: picture
person: user
product: sse_product
uploaded_file: document
actions:
add: add
comment: comment
create: create
favorite: favorite
like: like
make_friendship: make_friendship
upload: upload
update: update
start: start
announce_creation: announce_creation
announce_new: announce_new
announce_update: announce_update
announce_news: announce_news
test_app:
test_id: xxx
test_secret: xxx
class FbAppPluginMyprofileController < OpenGraphPlugin::MyprofileController
no_design_blocks
before_filter :load_provider
before_filter :load_auth
def index
if params[:tabs_added]
@page_tabs = FbAppPlugin::PageTab.create_from_tabs_added params[:tabs_added], params[:page_tab]
@page_tab = @page_tabs.first
redirect_to @page_tab.facebook_url
end
end
def show_login
@status = params[:auth].delete :status
@logged_auth = FbAppPlugin::Auth.new params[:auth]
@logged_auth.fetch_user
if @auth.connected?
render partial: 'identity', locals: {auth: @logged_auth}
else
render nothing: true
end
end
def save_auth
@status = params[:auth].delete :status rescue FbAppPlugin::Auth::Status::Unknown
if @status == FbAppPlugin::Auth::Status::Connected
@auth.attributes = params[:auth]
@auth.save! if @auth.changed?
else
@auth.destroy if @auth and @auth.persisted?
@auth = new_auth
end
render partial: 'settings'
end
protected
def load_provider
@provider = FbAppPlugin.oauth_provider_for environment
end
def load_auth
@auth = FbAppPlugin::Auth.where(profile_id: profile.id, provider_id: @provider.id).first
@auth ||= new_auth
end
def new_auth
FbAppPlugin::Auth.new profile: profile, provider: @provider
end
def context
:fb_app
end
end
class FbAppPluginController < PublicController
no_design_blocks
def index
end
def myprofile_config
if logged_in?
redirect_to controller: :fb_app_plugin_myprofile, profile: user.identifier
else
redirect_to controller: :account, action: :login, return_to: url_for(controller: :fb_app_plugin, action: :myprofile_config)
end
end
protected
# prevent session reset because X-CSRF not being passed by FB
# see also https://gist.github.com/toretore/911886
def handle_unverified_request
end
end
class FbAppPluginPageTabController < FbAppPluginController
no_design_blocks
before_filter :change_theme
before_filter :disable_cache
include CatalogHelper
helper ManageProductsHelper
helper FbAppPlugin::DisplayHelper
def index
return unless load_page_tabs
if params[:tabs_added]
@page_tabs = FbAppPlugin::PageTab.create_from_tabs_added params[:tabs_added]
@page_tab = @page_tabs.first
redirect_to @page_tab.facebook_url
elsif @signed_request or @page_id
if @page_tab.present?
if product_id = params[:product_id]
@product = environment.products.find product_id
@profile = @product.profile
@inputs = @product.inputs
@allowed_user = false
load_catalog
render action: 'product'
elsif @page_tab.config_type.in? [:profile, :own_profile]
@profile = @page_tab.value
load_catalog
render action: 'catalog' unless performed?
else
# fake profile for catalog controller
@profile = environment.enterprise_default_template
@profile.shopping_cart_settings.enabled = true
base_query = @page_tab.value
params[:base_query] = base_query
params[:scope] = 'all'
load_catalog
render action: 'catalog' unless performed?
end
else
render action: 'first_load'
end
else
# render template
render action: 'index'
end
end
def search_autocomplete
load_page_tabs
load_search_autocomplete
respond_to do |format|
format.json{ render 'catalog/search_autocomplete' }
end
end
def admin
return redirect_to '/plugin/fb_app/myprofile_config' if params[:page_id].blank? and params[:signed_request].blank?
return unless load_page_tabs
if request.put? and @page_id.present?
create_page_tabs if @page_tab.nil?
@page_tab.update_attributes! params[:page_tab]
respond_to do |format|
format.js{ render action: 'admin' }
end
end
end
def destroy
@page_tab = FbAppPlugin::PageTab.find params[:id]
return render_access_denied unless user.present? and (user.is_admin?(environment) or user.is_admin? @page_tab.profile)
@page_tab.destroy
render nothing: true
end
def uninstall
render text: params.to_yaml
end
def enterprise_search
scope = environment.enterprises.enabled.public
@query = params[:query]
@profiles = scope.limit(10).order('name ASC').
where(['name ILIKE ? OR name ILIKE ? OR identifier LIKE ?', "#{@query}%", "% #{@query}%", "#{@query}%"])
render partial: 'open_graph_plugin/myprofile/profile_search', locals: {profiles: @profiles}
end
# unfortunetely, this needs to be public
def profile
@profile
end
protected
def default_url_options
{profile: @profile.identifier} if @profile
end
def load_page_tabs
@signed_requests = read_param params[:signed_request]
if @signed_requests.present?
@datas = []
@page_ids = @signed_requests.map do |signed_request|
@data = FbAppPlugin::Auth.parse_signed_request signed_request
@datas << @data
page_id = @data[:page][:id] rescue nil
if page_id.blank?
render_not_found
return false
end
page_id
end
else
@page_ids = read_param params[:page_id]
end
@page_tabs = FbAppPlugin::PageTab.where page_id: @page_ids
@signed_request = @signed_requests.first
@page_id = @page_ids.first
@page_tab = @page_tabs.first
@new_request = @page_tab.blank?
true
end
def create_page_tabs
@page_tabs = FbAppPlugin::PageTab.create_from_page_ids @page_ids
@page_tab ||= @page_tabs.first
end
def change_theme
# move to config
unless theme_responsive?
@current_theme = 'ees'
@theme_responsive = true
end
@without_pure_chat = true
end
def get_layout
return nil if request.format == :js or request.xhr?
return 'application-responsive'
end
def disable_cache
@disable_cache_theme_navigation = true
end
def load_catalog options = {}
@use_show_more = true
catalog_load_index options
end
def read_param param
if param.is_a? Hash
param.values
else
Array(param).select{ |p| p.present? }
end
end
end
class CreateFbAppPageTabConfig < ActiveRecord::Migration
def change
create_table :fb_app_plugin_page_tab_configs do |t|
t.string :page_id
t.text :config, default: {}.to_yaml
t.integer :profile_id
t.timestamps
end
add_index :fb_app_plugin_page_tab_configs, [:profile_id]
add_index :fb_app_plugin_page_tab_configs, [:page_id]
add_index :fb_app_plugin_page_tab_configs, [:page_id, :profile_id]
end
end
system "script/noosfero-plugins -q enable oauth_client"
system "script/noosfero-plugins -q enable open_graph"
require_dependency 'profile'
# hate to wrte this, but without Noosfero::Plugin::Settings is loaded instead
require 'fb_app_plugin/settings'
# attr_accessible must be defined on subclasses
Profile.descendants.each do |subclass|
subclass.class_eval do
attr_accessible :fb_app_settings
end
end
class Profile
def fb_app_settings attrs = {}
@fb_app_settings ||= FbAppPlugin::Settings.new self, attrs
attrs.each{ |a, v| @fb_app_settings.send "#{a}=", v }
@fb_app_settings
end
alias_method :fb_app_settings=, :fb_app_settings
has_many :fb_app_page_tabs, class_name: 'FbAppPlugin::PageTab'
def fb_app_auth
provider = FbAppPlugin.oauth_provider_for self.environment
self.oauth_auths.where(provider_id: provider.id).first
end
end
module FbAppPlugin
extend Noosfero::Plugin::ParentMethods
def self.plugin_name
I18n.t 'fb_app_plugin.lib.plugin.name'
end
def self.plugin_description
I18n.t 'fb_app_plugin.lib.plugin.description'
end
def self.config
@config ||= HashWithIndifferentAccess.new(YAML.load File.read("#{File.dirname __FILE__}/../config.yml")) rescue {}
end
def self.test_users
@test_users ||= self.config[:test_users]
end
def self.test_user? user
user and (self.test_users.blank? or self.test_users.include? user.identifier)
end
def self.debug? actor=nil
self.test_user? actor
end
def self.scope user
if self.test_user? user then 'publish_actions' else '' end
end
def self.oauth_provider_for environment
return unless self.config.present?
@oauth_providers ||= {}
@oauth_providers[environment] ||= begin
app_id = self.timeline_app_credentials[:id].to_s
app_secret = self.timeline_app_credentials[:secret].to_s
client = environment.oauth_providers.where(client_id: app_id).first
# attributes that may be changed by the user
client ||= OauthClientPlugin::Provider.new strategy: 'facebook',
name: 'FB App', site: 'https://facebook.com'
# attributes that should not change
client.attributes = {
client_id: app_id, client_secret: app_secret,
environment_id: environment.id,
}
client.save! if client.changed?
client
end
end
def self.open_graph_config
return unless self.config.present?
@open_graph_config ||= begin
key = if self.config[:timeline][:use_test_app] then :test_app else :app end
self.config[key][:open_graph]
end
end
def self.credentials app = :app
return unless self.config.present?
{id: self.config[app][:id], secret: self.config[app][:secret]}
end
def self.timeline_app_credentials
return unless self.config.present?
@timeline_app_credentials ||= begin
key = if self.config[:timeline][:use_test_app] then :test_app else :app end
self.credentials key
end
end
def self.page_tab_app_credentials
return unless self.config.present?
@page_tab_app_credentials ||= begin
key = if self.config[:page_tab][:use_test_app] then :test_app else :app end
self.credentials key
end
end
end
class FbAppPlugin::Base < Noosfero::Plugin
def stylesheet?
true
end
def js_files
['fb_app.js'].map{ |j| "javascripts/#{j}" }
end
def head_ending
return unless FbAppPlugin.config.present?
lambda do
tag 'meta', property: 'fb:app_id', content: FbAppPlugin.config[:app][:id]
end
end
def control_panel_buttons
return unless FbAppPlugin.config.present?
{ title: FbAppPlugin.plugin_name, icon: 'fb-app', url: {host: FbAppPlugin.config[:app][:domain], profile: profile.identifier, controller: :fb_app_plugin_myprofile} }
end
end
ActiveSupport.on_load :open_graph_plugin do
OpenGraphPlugin::Stories.register_publisher FbAppPlugin::Publisher.default
end
ActiveSupport.on_load :metadata_plugin do
MetadataPlugin::Controllers.class_eval do
def fb_app_plugin_page_tab
:@product
end
end
end
module FbAppPlugin::DisplayHelper
extend CatalogHelper
def fb_url_options options
options.merge! page_id: @page_ids, signed_request: @signed_requests, id: nil
end
def url_for options = {}
return super unless options.is_a? Hash
if options[:controller] == :catalog
options[:controller] = :fb_app_plugin_page_tab
options = fb_url_options options
end
super
end
protected
def product_url_options product, options = {}
options = options.merge! product.url
options = options.merge! controller: :fb_app_plugin_page_tab, product_id: product.id, action: :index
options = fb_url_options options
unless Rails.env.development?
domain = FbAppPlugin.config[:app][:domain]
options[:host] = domain if domain.present?
options[:protocol] = '//'
end
options
end
def product_path product, options = {}
url = url_for product_url_options(product, options = {})
url
end
def link_to_product product, opts = {}
url_opts = opts.delete(:url_options) || {}
url_opts = product_url_options product, url_opts
url = params.merge url_opts
link_to content_tag('span', product.name), url,
opts.merge(target: '')
end
def link_to name = nil, options = nil, html_options = nil, &block
html_options ||= {}
options[:protocol] = '//' if options.is_a? Hash
html_options[:target] ||= '_parent'
super
end
end
# add target attribute to links
class FbAppPlugin::LinkRenderer < WillPaginate::ActionView::LinkRenderer
def prepare collection, options, template
super
end
protected
def default_url_params
{target: ''}
end