Init version of twitter and facebook channel connector.
This commit is contained in:
parent
3878c40e70
commit
49172439a7
18 changed files with 831 additions and 46 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<div class="alert alert--danger hidden" role="alert"></div>
|
||||
<fieldset>
|
||||
|
||||
<%- @T('Wall') %>
|
||||
<div class="js-wall">
|
||||
<%= @channel.options.user.name %> -> <div data-page-id="<%= @channel.options.user.id %>" class="js-groups"></div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<%- @T('Pages') %>
|
||||
<div class="js-pages">
|
||||
<% if @channel.options.pages: %>
|
||||
<% for page in @channel.options.pages: %>
|
||||
<%= page.name %> -> <div data-page-id="<%= page.id %>" class="js-groups"></div><br>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
29
app/assets/javascripts/app/views/facebook/app_config.jst.eco
Normal file
29
app/assets/javascripts/app/views/facebook/app_config.jst.eco
Normal file
|
@ -0,0 +1,29 @@
|
|||
<div class="alert alert--danger hidden" role="alert"></div>
|
||||
<p>
|
||||
The tutorial on how to create a Facebook App is hosted on <a href="http://zammad.org/twitter-app-tutorial" target="_blank">zammad.org/facebook-app-tutorial</a>
|
||||
</p>
|
||||
<fieldset>
|
||||
<h2><%- @T('Enter your %s App Keys', 'Facebook') %></h2>
|
||||
<div class="input form-group">
|
||||
<div class="formGroup-label">
|
||||
<label for="application_id">Facebook APP ID <span>*</span></label>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<input id="application_id" type="text" name="application_id" value="<% if @external_credential && @external_credential.credentials: %><%= @external_credential.credentials.application_id %><% end %>" class="form-control" required autocomplete="off" >
|
||||
</div>
|
||||
</div>
|
||||
<div class="input form-group">
|
||||
<div class="formGroup-label">
|
||||
<label for="application_secret">Facebook App Secret <span>*</span></label>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<input id="application_secret" type="text" name="application_secret" value="<% if @external_credential && @external_credential.credentials: %><%= @external_credential.credentials.application_secret %><% end %>" class="form-control" required autocomplete="off" >
|
||||
</div>
|
||||
</div>
|
||||
<h2><%- @T('Your callback URL') %></h2>
|
||||
<div class="input form-group">
|
||||
<div class="controls">
|
||||
<input class="form-control js-select" readonly value="<%= @callbackUrl %>">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
14
app/assets/javascripts/app/views/facebook/index.jst.eco
Normal file
14
app/assets/javascripts/app/views/facebook/index.jst.eco
Normal file
|
@ -0,0 +1,14 @@
|
|||
<div class="page-header">
|
||||
<div class="page-header-title">
|
||||
<h1><%- @T('Facebook') %> <small><%- @T('Accounts') %></small></h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-content">
|
||||
<div class="page-description">
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<div class="btn btn--success js-configApp"><%- @T('Connect Facebook App') %></div>
|
||||
</div>
|
||||
</div>
|
41
app/assets/javascripts/app/views/facebook/list.jst.eco
Normal file
41
app/assets/javascripts/app/views/facebook/list.jst.eco
Normal file
|
@ -0,0 +1,41 @@
|
|||
<div class="page-header">
|
||||
<div class="page-header-title">
|
||||
<h1><%- @T('Facebook') %> <small><%- @T('Accounts') %></small></h1>
|
||||
</div>
|
||||
|
||||
<div class="page-header-meta">
|
||||
<a class="btn js-configApp"><%- @T('Configure App') %></a>
|
||||
<a class="btn btn--success js-new"><%- @T('Add Account') %></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-content">
|
||||
<% for channel in @channels: %>
|
||||
<div class="action" data-id="<%= channel.id %>">
|
||||
<div class="action-block action-row">
|
||||
<h2><%- @Icon('status', 'supergood-color inline') %> <%= channel.options.user.name %> <span class="text-muted"><%= channel.options.user.id %></span></h2>
|
||||
</div>
|
||||
<div class="action-flow action-flow--row">
|
||||
<div class="action-block">
|
||||
<h3><%- @T('Wall') %></h3>
|
||||
<%= channel.options.user.name %> -> <%= channel.options.sync.wall.groupName %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-flow action-flow--row">
|
||||
<div class="action-block">
|
||||
<h3><%- @T('Pages') %></h3>
|
||||
<% if channel.options.pages: %>
|
||||
<% for page in channel.options.pages: %>
|
||||
<%= page.name %> -> <%= page.groupName %><br>
|
||||
(<%= page.perms %>)<br>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-controls">
|
||||
<div class="sla-toggle btn btn--danger btn--secondary js-delete"><%- @T('Delete') %></div>
|
||||
<div class="sla-edit btn js-edit"><%- @T('Edit') %></div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
|
@ -3,7 +3,7 @@
|
|||
The tutorial on how to create a Twitter App is hosted on <a href="http://zammad.org/twitter-app-tutorial" target="_blank">zammad.org/twitter-app-tutorial</a>
|
||||
</p>
|
||||
<fieldset>
|
||||
<h2>Enter your Twitter App Keys</h2>
|
||||
<h2><%- @T('Enter your %s App Keys', 'Twitter') %></h2>
|
||||
<div class="input form-group">
|
||||
<div class="formGroup-label">
|
||||
<label for="consumer_key">Twitter API Key <span>*</span></label>
|
||||
|
@ -20,4 +20,10 @@
|
|||
<input id="consumer_secret" type="text" name="consumer_secret" value="<% if @external_credential && @external_credential.credentials: %><%= @external_credential.credentials.consumer_secret %><% end %>" class="form-control" required autocomplete="off" >
|
||||
</div>
|
||||
</div>
|
||||
<h2><%- @T('Your callback URL') %></h2>
|
||||
<div class="input form-group">
|
||||
<div class="controls">
|
||||
<input class="form-control js-select" readonly value="<%= @callbackUrl %>">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}"
|
||||
|
|
|
@ -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
|
||||
|
|
87
lib/external_credential/facebook.rb
Normal file
87
lib/external_credential/facebook.rb
Normal file
|
@ -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
|
|
@ -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',
|
||||
|
|
170
test/integration/facebook_browser_test.rb
Normal file
170
test/integration/facebook_browser_test.rb
Normal file
|
@ -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
|
163
test/integration/twitter_browser_test.rb
Normal file
163
test/integration/twitter_browser_test.rb
Normal file
|
@ -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
|
Loading…
Reference in a new issue