From 49172439a75089746757a41d58413c188c7fd8d5 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Wed, 30 Dec 2015 14:24:13 +0100 Subject: [PATCH] Init version of twitter and facebook channel connector. --- .gitlab-ci.yml | 50 ++++- .../_application_controller.coffee | 7 +- .../app/controllers/_channel/facebook.coffee | 190 +++++++++++++++++- .../app/controllers/_channel/twitter.coffee | 17 +- .../_profile/calendar_subscriptions.coffee | 4 - .../app/views/facebook/account_edit.jst.eco | 20 ++ .../app/views/facebook/app_config.jst.eco | 29 +++ .../app/views/facebook/index.jst.eco | 14 ++ .../app/views/facebook/list.jst.eco | 41 ++++ .../app/views/twitter/app_config.jst.eco | 8 +- app/controllers/channels_controller.rb | 24 +++ .../external_credentials_controller.rb | 7 +- app/models/external_credential.rb | 12 +- config/routes/channel.rb | 4 + lib/external_credential/facebook.rb | 87 ++++++++ lib/external_credential/twitter.rb | 30 ++- test/integration/facebook_browser_test.rb | 170 ++++++++++++++++ test/integration/twitter_browser_test.rb | 163 +++++++++++++++ 18 files changed, 831 insertions(+), 46 deletions(-) create mode 100644 app/assets/javascripts/app/views/facebook/account_edit.jst.eco create mode 100644 app/assets/javascripts/app/views/facebook/app_config.jst.eco create mode 100644 app/assets/javascripts/app/views/facebook/index.jst.eco create mode 100644 app/assets/javascripts/app/views/facebook/list.jst.eco create mode 100644 lib/external_credential/facebook.rb create mode 100644 test/integration/facebook_browser_test.rb create mode 100644 test/integration/twitter_browser_test.rb diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3bd3cdf86..13c219e45 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -187,6 +187,40 @@ job_integration_otrs_31: - rake db:migrate - ruby -I test/ test/integration/otrs_import_test.rb +job_integration_twitter_ff: + stage: browser + tags: + - browser-ff + - twitter + script: + - export BROWSER_PORT=3041 + - export WS_PORT=3042 + - export BROWSER_URL=http://$IP:$BROWSER_PORT + - RAILS_ENV=test rake db:create + - script/bootstrap.sh + - rake assets:precompile + - script/build/test_startup.sh $RAILS_ENV $BROWSER_PORT $WS_PORT + - ruby -I test/ test/integration/twitter_browser_test.rb || script/build/test_shutdown.sh $RAILS_ENV $BROWSER_PORT $WS_PORT 1 + - script/build/test_shutdown.sh $RAILS_ENV $BROWSER_PORT $WS_PORT +http://192.168.122.75:3041/api/v1/external_credentials/twitter/callback + +job_integration_facebook_ff: + stage: browser + tags: + - browser-ff + - facebook + script: + - export BROWSER_PORT=3051 + - export WS_PORT=3052 + - export BROWSER_URL=http://$IP:$BROWSER_PORT + - RAILS_ENV=test rake db:create + - script/bootstrap.sh + - rake assets:precompile + - script/build/test_startup.sh $RAILS_ENV $BROWSER_PORT $WS_PORT + - ruby -I test/ test/integration/facebook_browser_test.rb || script/build/test_shutdown.sh $RAILS_ENV $BROWSER_PORT $WS_PORT 1 + - script/build/test_shutdown.sh $RAILS_ENV $BROWSER_PORT $WS_PORT +http://192.168.122.75:3051/api/v1/external_credentials/facebook/callback + job_integration_autowizard_ff: stage: browser tags: @@ -264,8 +298,8 @@ job_integration_autowizard_chrome: tags: - browser-chrome script: - - export BROWSER_PORT=3071 - - export WS_PORT=3072 + - export BROWSER_PORT=4001 + - export WS_PORT=4002 - export BROWSER_URL=http://$IP:$BROWSER_PORT - RAILS_ENV=test rake db:create - cp contrib/auto_wizard_example.json auto_wizard.json @@ -280,8 +314,8 @@ job_integration_browser_chrome_1: tags: - browser-chrome script: - - export BROWSER_PORT=3041 - - export WS_PORT=3042 + - export BROWSER_PORT=4011 + - export WS_PORT=4012 - export BROWSER_URL=http://$IP:$BROWSER_PORT - unset MAILBOX_AUTO1 - unset MAILBOX_AUTO2 @@ -300,8 +334,8 @@ job_integration_browser_chrome_2: tags: - browser-chrome script: - - export BROWSER_PORT=3051 - - export WS_PORT=3052 + - export BROWSER_PORT=4021 + - export WS_PORT=4022 - export BROWSER_URL=http://$IP:$BROWSER_PORT - unset MAILBOX_AUTO1 - unset MAILBOX_AUTO2 @@ -320,8 +354,8 @@ job_integration_browser_chrome_3: tags: - browser-chrome script: - - export BROWSER_PORT=3061 - - export WS_PORT=3062 + - export BROWSER_PORT=4031 + - export WS_PORT=4032 - export BROWSER_URL=http://$IP:$BROWSER_PORT - unset MAILBOX_AUTO1 - unset MAILBOX_AUTO2 diff --git a/app/assets/javascripts/app/controllers/_application_controller.coffee b/app/assets/javascripts/app/controllers/_application_controller.coffee index ed717fc64..044669b66 100644 --- a/app/assets/javascripts/app/controllers/_application_controller.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller.coffee @@ -486,9 +486,6 @@ class App.Controller extends Spine.Controller item.created_by = App.User.find( item.created_by_id ) items - ws_send: (data) -> - App.Event.trigger( 'ws:send', JSON.stringify(data) ) - # central method, is getting called on every ticket form change ticketFormChanges: (params, attribute, attributes, classname, form, ui) => if @formMeta.dependencies && @formMeta.dependencies[attribute.name] @@ -573,6 +570,10 @@ class App.Controller extends Spine.Controller logoUrl: -> "#{@Config.get('image_path')}/#{@Config.get('product_logo')}" + selectAll: (e) -> + e.currentTarget.focus() + e.currentTarget.select() + class App.ControllerPermanent extends App.Controller constructor: -> super diff --git a/app/assets/javascripts/app/controllers/_channel/facebook.coffee b/app/assets/javascripts/app/controllers/_channel/facebook.coffee index 8ec5d5c9b..045c025ed 100644 --- a/app/assets/javascripts/app/controllers/_channel/facebook.coffee +++ b/app/assets/javascripts/app/controllers/_channel/facebook.coffee @@ -1,16 +1,186 @@ -class App.ChannelFacebook extends App.Controller +class Index extends App.ControllerContent + events: + 'click .js-new': 'new' + 'click .js-edit': 'edit' + 'click .js-delete': 'delete' + 'click .js-configApp': 'configApp' + constructor: -> super + return if !@authenticate() - @title 'Facebook' + #@interval(@load, 60000) + @load() - # render page - @render() - - render: -> - - @html App.view('channel/facebook')( - head: 'some header' + load: => + @startLoading() + @ajax( + id: 'facebook_index' + type: 'GET' + url: "#{@apiPath}/channels/facebook_index" + processData: true + success: (data, status, xhr) => + @stopLoading() + App.Collection.loadAssets(data.assets) + @callbackUrl = data.callback_url + @render(data) ) -App.Config.set( 'Facebook', { prio: 6000, name: 'Facebook', parent: '#channels', target: '#channels/facebook', controller: App.ChannelFacebook, role: ['Admin'] }, 'NavBarAdmin' ) + render: (data) => + + # if no facebook app is registered, show intro + if !App.ExternalCredential.findByAttribute(name: 'facebook') + @html App.view('facebook/index')() + return + + channels = [] + for channel_id in data.channel_ids + channel = App.Channel.find(channel_id) + if channel && channel.options && channel.options.sync + displayName = '-' + if channel.options.sync.wall.group_id + group = App.Group.find(channel.options.sync.wall.group_id) + displayName = group.displayName() + channel.options.sync.wall.groupName = displayName + for page in channel.options.pages + displayName = '-' + for page_id, pageParams of channel.options.sync.pages + if page.id is page_id + if pageParams.group_id + group = App.Group.find(pageParams.group_id) + displayName = group.displayName() + page.groupName = displayName + channels.push channel + @html App.view('facebook/list')( + channels: channels + ) + # accounts: accounts + # showDescription: showDescription + # description: description + + if @channel_id + @edit(undefined, @channel_id) + + configApp: => + external_credential = App.ExternalCredential.findByAttribute('name', 'facebook') + contentInline = $(App.view('facebook/app_config')( + external_credential: external_credential + callbackUrl: @callbackUrl + )) + contentInline.find('.js-select').on('click', (e) => + @selectAll(e) + ) + modal = new App.ControllerModal( + head: 'Connect Facebook App' + container: @el.parents('.content') + contentInline: contentInline + shown: true + button: 'Connect' + cancel: true + small: true + onSubmit: (e) => + @formDisable(e) + + # verify app credentals + @ajax( + id: 'facebook_app_verify' + type: 'POST' + url: "#{@apiPath}/external_credentials/facebook/app_verify" + data: JSON.stringify(modal.formParams()) + processData: true + success: (data, status, xhr) => + if data.attributes + if !external_credential + external_credential = new App.ExternalCredential + external_credential.load(name: 'facebook', credentials: modal.formParams()) + external_credential.save( + done: => + @load() + modal.close() + fail: -> + modal.element().find('.alert').removeClass('hidden').text('Unable to create entry.') + ) + return + @formEnable(e) + modal.element().find('.alert').removeClass('hidden').text(data.error || 'Unable to verify App.') + ) + ) + + new: (e) -> + window.location.href = "#{@apiPath}/external_credentials/facebook/link_account" + + edit: (e, id) => + if e + e.preventDefault() + id = $(e.target).closest('.action').data('id') + channel = App.Channel.find(id) + if !channel.options.sync + channel.options.sync = {} + if !channel.options.sync.wall + channel.options.sync.wall = {} + if !channel.options.sync.pages + channel.options.sync.pages = {} + content = $( App.view('facebook/account_edit')(channel: channel) ) + + groupSelection = (selected_id, el, prefix) -> + selection = App.UiElement.select.render( + name: "#{prefix}::group_id" + multiple: false + limit: 100 + null: false + relation: 'Group' + nulloption: true + default: selected_id + ) + el.html(selection) + + groupSelection(channel.options.sync.wall.group_id, content.find('.js-wall .js-groups'), 'wall') + for page in channel.options.pages + pageConfigured = false + for page_id, pageParams of channel.options.sync.pages + if page.id is page_id + pageConfigured = true + groupSelection(pageParams.group_id, content.find(".js-groups[data-page-id=#{page.id}]"), "pages::#{page.id}") + if !pageConfigured + groupSelection('', content.find(".js-groups[data-page-id=#{page.id}]"), "pages::#{page.id}") + + modal = new App.ControllerModal( + head: 'Facebook Account' + container: @el.parents('.content') + contentInline: content + shown: true + cancel: true + onSubmit: (e) => + @formDisable(e) + channel.options.sync = modal.formParams() + @ajax( + id: 'channel_facebook_update' + type: 'POST' + url: "#{@apiPath}/channels/facebook_verify/#{channel.id}" + data: JSON.stringify(channel.attributes()) + processData: true + success: (data, status, xhr) => + @load() + modal.close() + fail: => + @formEnable(e) + ) + ) + + delete: (e) => + e.preventDefault() + id = $(e.target).closest('.action').data('id') + item = App.Channel.find(id) + new App.ControllerGenericDestroyConfirm( + item: item + container: @el.closest('.content') + callback: @load + ) + + description: (e) => + new App.ControllerGenericDescription( + description: App.Twitter.description + container: @el.closest('.content') + ) + +App.Config.set('Facebook', { prio: 5100, name: 'Facebook', parent: '#channels', target: '#channels/facebook', controller: Index, role: ['Admin'] }, 'NavBarAdmin') diff --git a/app/assets/javascripts/app/controllers/_channel/twitter.coffee b/app/assets/javascripts/app/controllers/_channel/twitter.coffee index d45412153..33478f7c1 100644 --- a/app/assets/javascripts/app/controllers/_channel/twitter.coffee +++ b/app/assets/javascripts/app/controllers/_channel/twitter.coffee @@ -4,7 +4,6 @@ class Index extends App.ControllerContent 'click .js-edit': 'edit' 'click .js-delete': 'delete' 'click .js-configApp': 'configApp' - 'click .js-configApp': 'configApp' constructor: -> super @@ -23,12 +22,13 @@ class Index extends App.ControllerContent success: (data, status, xhr) => @stopLoading() App.Collection.loadAssets(data.assets) + @callbackUrl = data.callback_url @render(data) ) render: (data) => - # if no twitter app is registered, show into + # if no twitter app is registered, show intro if !App.ExternalCredential.findByAttribute(name: 'twitter') @html App.view('twitter/index')() return @@ -66,12 +66,19 @@ class Index extends App.ControllerContent if @channel_id @edit(undefined, @channel_id) - configApp: -> - external_credential = App.ExternalCredential.findByAttribute(name: 'twitter') + configApp: => + external_credential = App.ExternalCredential.findByAttribute('name', 'twitter') + contentInline = $(App.view('twitter/app_config')( + external_credential: external_credential + callbackUrl: @callbackUrl + )) + contentInline.find('.js-select').on('click', (e) => + @selectAll(e) + ) modal = new App.ControllerModal( head: 'Connect Twitter App' container: @el.parents('.content') - contentInline: App.view('twitter/app_config')(external_credential: external_credential) + contentInline: contentInline shown: true button: 'Connect' cancel: true diff --git a/app/assets/javascripts/app/controllers/_profile/calendar_subscriptions.coffee b/app/assets/javascripts/app/controllers/_profile/calendar_subscriptions.coffee index ef0c3e101..1009cc0db 100644 --- a/app/assets/javascripts/app/controllers/_profile/calendar_subscriptions.coffee +++ b/app/assets/javascripts/app/controllers/_profile/calendar_subscriptions.coffee @@ -37,10 +37,6 @@ class CalendarSubscriptions extends App.Controller $(e.currentTarget).next().removeClass('is-hidden') $(e.currentTarget).remove() - selectAll: (e) -> - e.currentTarget.focus() - e.currentTarget.select() - onOptionsChange: => @setAllPreferencesToFalse() diff --git a/app/assets/javascripts/app/views/facebook/account_edit.jst.eco b/app/assets/javascripts/app/views/facebook/account_edit.jst.eco new file mode 100644 index 000000000..80e464ea0 --- /dev/null +++ b/app/assets/javascripts/app/views/facebook/account_edit.jst.eco @@ -0,0 +1,20 @@ + +
+ + <%- @T('Wall') %> +
+ <%= @channel.options.user.name %> ->
+
+ +
+ + <%- @T('Pages') %> +
+ <% if @channel.options.pages: %> + <% for page in @channel.options.pages: %> + <%= page.name %> ->

+ <% end %> + <% end %> +
+ +
\ No newline at end of file diff --git a/app/assets/javascripts/app/views/facebook/app_config.jst.eco b/app/assets/javascripts/app/views/facebook/app_config.jst.eco new file mode 100644 index 000000000..1bbb27c70 --- /dev/null +++ b/app/assets/javascripts/app/views/facebook/app_config.jst.eco @@ -0,0 +1,29 @@ + +

+ The tutorial on how to create a Facebook App is hosted on zammad.org/facebook-app-tutorial +

+
+

<%- @T('Enter your %s App Keys', 'Facebook') %>

+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+

<%- @T('Your callback URL') %>

+
+
+ +
+
+
\ No newline at end of file diff --git a/app/assets/javascripts/app/views/facebook/index.jst.eco b/app/assets/javascripts/app/views/facebook/index.jst.eco new file mode 100644 index 000000000..9ca4976b3 --- /dev/null +++ b/app/assets/javascripts/app/views/facebook/index.jst.eco @@ -0,0 +1,14 @@ + + +
+
+

+ Lorem ipsum Consequat ex dolore ullamco dolor ut eu eiusmod voluptate. Lorem ipsum Non aliquip Ut veniam cupidatat velit deserunt. Lorem ipsum Id reprehenderit deserunt esse eiusmod exercitation. Lorem ipsum Voluptate mollit sed Ut nulla consequat enim. Lorem ipsum Adipisicing ullamco dolor elit officia pariatur ex ea laboris Ut exercitation proident sed. Lorem ipsum In officia reprehenderit sed nulla incididunt aute incididunt ad quis tempor. Lorem ipsum Dolore est id minim dolore et labore incididunt commodo. Lorem ipsum Excepteur non consectetur anim ut nostrud amet et. Lorem ipsum Sunt nostrud nulla officia aute laborum enim in pariatur sit enim et. +

+
<%- @T('Connect Facebook App') %>
+
+
\ No newline at end of file diff --git a/app/assets/javascripts/app/views/facebook/list.jst.eco b/app/assets/javascripts/app/views/facebook/list.jst.eco new file mode 100644 index 000000000..657a79b70 --- /dev/null +++ b/app/assets/javascripts/app/views/facebook/list.jst.eco @@ -0,0 +1,41 @@ + + +
+<% for channel in @channels: %> +
+
+

<%- @Icon('status', 'supergood-color inline') %> <%= channel.options.user.name %> <%= channel.options.user.id %>

+
+
+
+

<%- @T('Wall') %>

+ <%= channel.options.user.name %> -> <%= channel.options.sync.wall.groupName %> +
+
+
+
+

<%- @T('Pages') %>

+ <% if channel.options.pages: %> + <% for page in channel.options.pages: %> + <%= page.name %> -> <%= page.groupName %>
+ (<%= page.perms %>)
+ <% end %> + <% end %> +
+
+
+
<%- @T('Delete') %>
+
<%- @T('Edit') %>
+
+
+<% end %> +
\ No newline at end of file diff --git a/app/assets/javascripts/app/views/twitter/app_config.jst.eco b/app/assets/javascripts/app/views/twitter/app_config.jst.eco index 145393c16..933967e60 100644 --- a/app/assets/javascripts/app/views/twitter/app_config.jst.eco +++ b/app/assets/javascripts/app/views/twitter/app_config.jst.eco @@ -3,7 +3,7 @@ The tutorial on how to create a Twitter App is hosted on zammad.org/twitter-app-tutorial

-

Enter your Twitter App Keys

+

<%- @T('Enter your %s App Keys', 'Twitter') %>

@@ -20,4 +20,10 @@
+

<%- @T('Your callback URL') %>

+
+
+ +
+
\ No newline at end of file diff --git a/app/controllers/channels_controller.rb b/app/controllers/channels_controller.rb index bad56e2b7..e64be0c33 100644 --- a/app/controllers/channels_controller.rb +++ b/app/controllers/channels_controller.rb @@ -59,6 +59,7 @@ curl http://localhost/api/v1/channels.json -v -u #{login}:#{password} -H "Conten render json: { assets: assets, channel_ids: channel_ids, + callback_url: ExternalCredential.callback_url('twitter'), } end @@ -67,6 +68,29 @@ curl http://localhost/api/v1/channels.json -v -u #{login}:#{password} -H "Conten model_update_render(Channel, params) end + def facebook_index + assets = {} + ExternalCredential.where(name: 'facebook').each {|external_credential| + assets = external_credential.assets(assets) + } + channel_ids = [] + Channel.order(:id).each {|channel| + next if channel.area != 'Facebook::Account' + assets = channel.assets(assets) + channel_ids.push channel.id + } + render json: { + assets: assets, + channel_ids: channel_ids, + callback_url: ExternalCredential.callback_url('facebook'), + } + end + + def facebook_verify + return if deny_if_not_role(Z_ROLENAME_ADMIN) + model_update_render(Channel, params) + end + def email_index return if deny_if_not_role(Z_ROLENAME_ADMIN) system_online_service = Setting.get('system_online_service') diff --git a/app/controllers/external_credentials_controller.rb b/app/controllers/external_credentials_controller.rb index eeb28d36d..8ca86bc0f 100644 --- a/app/controllers/external_credentials_controller.rb +++ b/app/controllers/external_credentials_controller.rb @@ -34,13 +34,12 @@ class ExternalCredentialsController < ApplicationController return rescue => e render json: { error: e.message }, status: :ok - end def link_account return if deny_if_not_role(Z_ROLENAME_ADMIN) provider = params[:provider].downcase - attributes = ExternalCredential.request_account_to_link(provider, callback_url(provider)) + attributes = ExternalCredential.request_account_to_link(provider) session[:request_token] = attributes[:request_token] redirect_to attributes[:authorize_url] end @@ -56,11 +55,11 @@ class ExternalCredentialsController < ApplicationController private def callback_url(provider) - "#{Setting.get('http_type')}://#{Setting.get('fqdn')}#{Rails.configuration.api_path}/external_credentials/#{provider}/callback" + ExternalCredential.callback_url(provider) end def app_url(provider, channel_id) - "#{Setting.get('http_type')}://#{Setting.get('fqdn')}/#channels/#{provider}/#{channel_id}" + ExternalCredential.app_url(provider, channel_id) end end diff --git a/app/models/external_credential.rb b/app/models/external_credential.rb index 830316238..9d5c5c429 100644 --- a/app/models/external_credential.rb +++ b/app/models/external_credential.rb @@ -9,9 +9,9 @@ class ExternalCredential < ApplicationModel backend.app_verify(params) end - def self.request_account_to_link(provider, callback) + def self.request_account_to_link(provider) backend = load_backend(provider) - backend.request_account_to_link(callback) + backend.request_account_to_link end def self.link_account(provider, request_token, params) @@ -19,6 +19,14 @@ class ExternalCredential < ApplicationModel backend.link_account(request_token, params) end + def self.callback_url(provider) + "#{Setting.get('http_type')}://#{Setting.get('fqdn')}#{Rails.configuration.api_path}/external_credentials/#{provider}/callback" + end + + def self.app_url(provider, channel_id) + "#{Setting.get('http_type')}://#{Setting.get('fqdn')}/#channels/#{provider}/#{channel_id}" + end + def self.load_backend(provider) adapter = "ExternalCredential::#{provider.camelcase}" require "#{adapter.to_filename}" diff --git a/config/routes/channel.rb b/config/routes/channel.rb index bd3286140..0bc9e246a 100644 --- a/config/routes/channel.rb +++ b/config/routes/channel.rb @@ -13,6 +13,10 @@ Zammad::Application.routes.draw do match api_path + '/channels/twitter_index', to: 'channels#twitter_index', via: :get match api_path + '/channels/twitter_verify/:id', to: 'channels#twitter_verify', via: :post + # facebook helper + match api_path + '/channels/facebook_index', to: 'channels#facebook_index', via: :get + match api_path + '/channels/facebook_verify/:id', to: 'channels#facebook_verify', via: :post + # channels match api_path + '/channels/group/:id', to: 'channels#group_update', via: :post match api_path + '/channels/:id', to: 'channels#destroy', via: :delete diff --git a/lib/external_credential/facebook.rb b/lib/external_credential/facebook.rb new file mode 100644 index 000000000..689007320 --- /dev/null +++ b/lib/external_credential/facebook.rb @@ -0,0 +1,87 @@ +class ExternalCredential::Facebook + + def self.app_verify(params) + request_account_to_link(params) + params + end + + def self.request_account_to_link(credentials = {}) + external_credential = ExternalCredential.find_by(name: 'facebook') + if !credentials[:application_id] + credentials[:application_id] = external_credential.credentials['application_id'] + end + if !credentials[:application_secret] + credentials[:application_secret] = external_credential.credentials['application_secret'] + end + + oauth = Koala::Facebook::OAuth.new( + credentials[:application_id], + credentials[:application_secret], + ExternalCredential.callback_url('facebook'), + ) + oauth.get_app_access_token.inspect + state = rand(999_999_999_999).to_s + { + request_token: state, + authorize_url: oauth.url_for_oauth_code(permissions: 'publish_pages, manage_pages', state: state), + } + end + + def self.link_account(_request_token, params) + # fail if request_token.params[:oauth_token] != params[:state] + external_credential = ExternalCredential.find_by(name: 'facebook') + fail 'No such account' if !external_credential + oauth = Koala::Facebook::OAuth.new( + external_credential.credentials['application_id'], + external_credential.credentials['application_secret'], + ExternalCredential.callback_url('facebook'), + ) + + access_token = oauth.get_access_token(params[:code]) + client = Koala::Facebook::API.new(access_token) + user = client.get_object('me') + #p client.get_connections('me', 'accounts').inspect + pages = [] + client.get_connections('me', 'accounts').each { |page| + pages.push( + id: page['id'], + name: page['name'], + access_token: page['access_token'], + perms: page['perms'], + ) + } + + # check if account already exists + Channel.where(area: 'Facebook::Account').each {|channel| + next if !channel.options + next if !channel.options['user'] + next if !channel.options['user']['id'] + next if channel.options['user']['id'] != user['id'] + channel.options['auth']['access_token'] = access_token + channel.options['pages'] = pages + channel.save + return channel + } + + # create channel + Channel.create( + area: 'Facebook::Account', + options: { + adapter: 'facebook', + auth: { + access_token: access_token + }, + user: user, + pages: pages, + sync: { + wall: {}, + pages: [], + } + }, + active: true, + created_by_id: 1, + updated_by_id: 1, + ) + end + +end diff --git a/lib/external_credential/twitter.rb b/lib/external_credential/twitter.rb index 71b4ee5f9..4f9968e8d 100644 --- a/lib/external_credential/twitter.rb +++ b/lib/external_credential/twitter.rb @@ -1,15 +1,11 @@ class ExternalCredential::Twitter def self.app_verify(params) - attributes = { - consumer_key: params[:consumer_key], - consumer_secret: params[:consumer_secret], - } - request_account_to_link('', attributes) - attributes + request_account_to_link(params) + params end - def self.request_account_to_link(callback_url, credentials = {}) + def self.request_account_to_link(credentials = {}) external_credential = ExternalCredential.find_by(name: 'twitter') if !credentials[:consumer_key] credentials[:consumer_key] = external_credential.credentials['consumer_key'] @@ -21,8 +17,9 @@ class ExternalCredential::Twitter credentials[:consumer_key], credentials[:consumer_secret], { site: 'https://api.twitter.com' - }) - request_token = consumer.get_request_token(oauth_callback: callback_url) + } + ) + request_token = consumer.get_request_token(oauth_callback: ExternalCredential.callback_url('twitter')) { request_token: request_token, authorize_url: request_token.authorize_url, @@ -41,6 +38,21 @@ class ExternalCredential::Twitter ) user = client.user + # check if account already exists + Channel.where(area: 'Twitter::Account').each {|channel| + next if !channel.options + next if !channel.options['user'] + next if !channel.options['user']['id'] + next if channel.options['user']['id'] != user['id'] + + # update access_token + channel.options['auth']['external_credential_id'] = external_credential.id + channel.options['auth']['oauth_token'] = access_token.token + channel.options['auth']['oauth_token_secret'] = access_token.secret + channel.save + return channel + } + # create channel Channel.create( area: 'Twitter::Account', diff --git a/test/integration/facebook_browser_test.rb b/test/integration/facebook_browser_test.rb new file mode 100644 index 000000000..e8e275e4f --- /dev/null +++ b/test/integration/facebook_browser_test.rb @@ -0,0 +1,170 @@ +# encoding: utf-8 +require 'browser_test_helper' + +class FacebookBrowserTest < TestCase + def test_add_config + + # app config + if !ENV['FACEBOOK_APP_ID'] + fail "ERROR: Need FACEBOOK_APP_ID - hint FACEBOOK_APP_ID='1234'" + end + app_id = ENV['FACEBOOK_APP_ID'] + if !ENV['FACEBOOK_APP_SECRET'] + fail "ERROR: Need FACEBOOK_APP_SECRET - hint FACEBOOK_APP_SECRET='1234'" + end + app_secret = ENV['FACEBOOK_APP_SECRET'] + if !ENV['FACEBOOK_USER_LOGIN'] + fail "ERROR: Need FACEBOOK_USER_LOGIN - hint FACEBOOK_USER_LOGIN='1234'" + end + user_login = ENV['FACEBOOK_USER_LOGIN'] + if !ENV['FACEBOOK_USER_PW'] + fail "ERROR: Need FACEBOOK_USER_PW - hint FACEBOOK_USER_PW='1234'" + end + user_pw = ENV['FACEBOOK_USER_PW'] + + @browser = browser_instance + login( + username: 'master@example.com', + password: 'test', + url: browser_url, + ) + tasks_close_all() + + click(css: 'a[href="#manage"]') + click(css: 'a[href="#channels/facebook"]') + + click(css: '#content .js-configApp') + sleep 2 + set( + css: '#content .modal [name=application_id]', + value: app_id, + ) + set( + css: '#content .modal [name=application_secret]', + value: 'wrong', + ) + click(css: '#content .modal .js-submit') + + watch_for( + css: '#content .modal .alert', + value: 'Error', + ) + + set( + css: '#content .modal [name=application_secret]', + value: app_secret, + ) + click(css: '#content .modal .js-submit') + + watch_for_disappear( + css: '#content .modal .alert', + value: 'Error', + ) + + watch_for( + css: '#content .js-new', + value: 'add account', + ) + + click(css: '#content .js-configApp') + + set( + css: '#content .modal [name=application_secret]', + value: 'wrong', + ) + click(css: '#content .modal .js-submit') + + watch_for( + css: '#content .modal .alert', + value: 'Error', + ) + + set( + css: '#content .modal [name=application_secret]', + value: app_secret, + ) + click(css: '#content .modal .js-submit') + + watch_for_disappear( + css: '#content .modal .alert', + value: 'Error', + ) + + watch_for( + css: '#content .js-new', + value: 'add account', + ) + + click(css: '#content .js-new') + + watch_for( + css: 'body', + value: 'Facebook Login', + ) + + set( + css: '#email', + value: user_login, + ) + set( + css: '#pass', + value: user_pw, + ) + click(css: '#login_button_inline') + + #sleep 10 + #click(css: 'div[role="dialog"] button[type="submit"][name="__CONFIRM__"]') + #sleep 10 + #click(css: 'div[role="dialog"] button[type="submit"][name="__CONFIRM__"]') + #sleep 10 + + #watch_for( + # css: '#content .modal', + # value: '', + #) + + watch_for( + css: '#navigation', + value: 'Dashboard', + ) + + #click(css: '#content .modal .js-close') + + watch_for( + css: '#content', + value: 'Hansi Merkur', + ) + exists( + css: '#content .main .action:nth-child(1)' + ) + exists_not( + css: '#content .main .action:nth-child(2)' + ) + + click(css: '#content .js-new') + + sleep 10 + + #click(css: '#login_button_inline') + + #watch_for( + # css: '#content .modal', + # value: 'Search Terms', + #) + + #click(css: '#content .modal .js-close') + + watch_for( + css: '#content', + value: 'Hansi Merkur', + ) + exists( + css: '#content .main .action:nth-child(1)' + ) + exists_not( + css: '#content .main .action:nth-child(2)' + ) + + end + +end diff --git a/test/integration/twitter_browser_test.rb b/test/integration/twitter_browser_test.rb new file mode 100644 index 000000000..a36d72852 --- /dev/null +++ b/test/integration/twitter_browser_test.rb @@ -0,0 +1,163 @@ +# encoding: utf-8 +require 'browser_test_helper' + +class TwitterBrowserTest < TestCase + def test_add_config + + # app config + if !ENV['TWITTER_CONSUMER_KEY'] + fail "ERROR: Need TWITTER_CONSUMER_KEY - hint TWITTER_CONSUMER_KEY='1234'" + end + consumer_key = ENV['TWITTER_CONSUMER_KEY'] + if !ENV['TWITTER_CONSUMER_SECRET'] + fail "ERROR: Need TWITTER_CONSUMER_SECRET - hint TWITTER_CONSUMER_SECRET='1234'" + end + consumer_secret = ENV['TWITTER_CONSUMER_SECRET'] + + if !ENV['TWITTER_USER_LOGIN'] + fail "ERROR: Need TWITTER_USER_LOGIN - hint TWITTER_USER_LOGIN='1234'" + end + twitter_user_loign = ENV['TWITTER_USER_LOGIN'] + + if !ENV['TWITTER_USER_PW'] + fail "ERROR: Need TWITTER_USER_PW - hint TWITTER_USER_PW='1234'" + end + twitter_pw = ENV['TWITTER_USER_PW'] + + @browser = browser_instance + login( + username: 'master@example.com', + password: 'test', + url: browser_url, + ) + tasks_close_all() + + click(css: 'a[href="#manage"]') + click(css: 'a[href="#channels/twitter"]') + click(css: '#content .js-configApp') + sleep 2 + set( + css: '#content .modal [name=consumer_key]', + value: consumer_key, + ) + set( + css: '#content .modal [name=consumer_secret]', + value: 'wrong', + ) + click(css: '#content .modal .js-submit') + + watch_for( + css: '#content .modal .alert', + value: 'Authorization Required', + ) + + set( + css: '#content .modal [name=consumer_secret]', + value: consumer_secret, + ) + click(css: '#content .modal .js-submit') + + watch_for_disappear( + css: '#content .modal .alert', + value: 'Authorization Required', + ) + + watch_for( + css: '#content .js-new', + value: 'add account', + ) + + click(css: '#content .js-configApp') + + set( + css: '#content .modal [name=consumer_secret]', + value: 'wrong', + ) + click(css: '#content .modal .js-submit') + + watch_for( + css: '#content .modal .alert', + value: 'Authorization Required', + ) + + set( + css: '#content .modal [name=consumer_secret]', + value: consumer_secret, + ) + click(css: '#content .modal .js-submit') + + watch_for_disappear( + css: '#content .modal .alert', + value: 'Authorization Required', + ) + + watch_for( + css: '#content .js-new', + value: 'add account', + ) + + click(css: '#content .js-new') + + sleep 10 + + set( + css: '#username_or_email', + value: twitter_user_loign, + ) + set( + css: '#password', + value: twitter_pw, + ) + click(css: '#allow') + + #watch_for( + # css: '.notice.callback', + # value: 'Redirecting you back to the application', + #) + + watch_for( + css: '#content .modal', + value: 'Search Terms', + ) + + click(css: '#content .modal .js-close') + + watch_for( + css: '#content', + value: 'Armin Theo', + ) + exists( + css: '#content .main .action:nth-child(1)' + ) + exists_not( + css: '#content .main .action:nth-child(2)' + ) + + # add account again + click(css: '#content .js-new') + + sleep 10 + + click(css: '#allow') + + watch_for( + css: '#content .modal', + value: 'Search Terms', + ) + + click(css: '#content .modal .js-close') + + watch_for( + css: '#content', + value: 'Armin Theo', + ) + exists( + css: '#content .main .action:nth-child(1)' + ) + exists_not( + css: '#content .main .action:nth-child(2)' + ) + + end + +end