Init version of twitter admin.

This commit is contained in:
Martin Edenhofer 2015-12-21 01:48:49 +01:00
parent 272a569b10
commit b990fa6daf
19 changed files with 342 additions and 152 deletions

View file

@ -654,6 +654,9 @@ class App.ControllerModal extends App.Controller
post: -> post: ->
# nothing # nothing
element: =>
@el
render: => render: =>
if @buttonSubmit is true if @buttonSubmit is true
@buttonSubmit = 'Submit' @buttonSubmit = 'Submit'
@ -688,6 +691,11 @@ class App.ControllerModal extends App.Controller
e.preventDefault() e.preventDefault()
@el.modal('hide') @el.modal('hide')
formParams: =>
if @container
return @formParam(@container.find('.modal form'))
return @formParam(@$('.modal form'))
onShow: -> onShow: ->
# do nothing # do nothing

View file

@ -268,6 +268,7 @@ class App.ControllerGenericDestroyConfirm extends App.ControllerModal
buttonSubmit: 'yes' buttonSubmit: 'yes'
buttonClass: 'btn--danger' buttonClass: 'btn--danger'
head: 'Confirm' head: 'Confirm'
small: true
content: -> content: ->
App.i18n.translateContent('Sure to delete this object?') App.i18n.translateContent('Sure to delete this object?')
@ -275,9 +276,9 @@ class App.ControllerGenericDestroyConfirm extends App.ControllerModal
onSubmit: => onSubmit: =>
@item.destroy( @item.destroy(
done: => done: =>
@close()
if @callback if @callback
@callback() @callback()
@close()
fail: => fail: =>
@log 'errors' @log 'errors'
@close() @close()
@ -348,6 +349,8 @@ class App.ControllerNavSidbar extends App.ControllerContent
constructor: (params) -> constructor: (params) ->
super super
@params = params
# get groups # get groups
groups = App.Config.get(@configKey) groups = App.Config.get(@configKey)
groupsUnsorted = [] groupsUnsorted = []
@ -385,6 +388,9 @@ class App.ControllerNavSidbar extends App.ControllerContent
else if @target && item.target is window.location.hash else if @target && item.target is window.location.hash
item.active = true item.active = true
selectedItem = item selectedItem = item
else if @target && window.location.hash.match(item.target)
item.active = true
selectedItem = item
else else
item.active = false item.active = false
@ -400,7 +406,7 @@ class App.ControllerNavSidbar extends App.ControllerContent
renderContainer: => renderContainer: =>
return if $( ".#{@configKey}" )[0] return if $( ".#{@configKey}" )[0]
@html App.view('generic/navbar_level2/index')( @html App.view('generic/navbar_level2/index')(
className: @configKey className: @configKey
) )
renderNavBar: (selectedItem) => renderNavBar: (selectedItem) =>
@ -427,9 +433,8 @@ class App.ControllerNavSidbar extends App.ControllerContent
@activeController.render() @activeController.render()
return return
@activeController = new selectedItem.controller( @params.el = @$('.main')
el: @$('.main') @activeController = new selectedItem.controller(@params)
)
class App.GenericHistory extends App.ControllerModal class App.GenericHistory extends App.ControllerModal
buttonClose: true buttonClose: true

View file

@ -1,27 +1,24 @@
class Index extends App.ControllerContent class Index extends App.ControllerContent
events: events:
'click .js-new': 'new' 'click .js-new': 'new'
'click .js-edit': 'edit' 'click .js-edit': 'edit'
'click .js-delete': 'delete' 'click .js-delete': 'delete'
'click .js-create-app': 'createApp' 'click .js-configApp': 'configApp'
'click .js-configApp': 'configApp'
constructor: -> constructor: ->
super super
# check authentication
return if !@authenticate() return if !@authenticate()
@render()
#@interval(@load, 60000) #@interval(@load, 60000)
#@load() @load()
load: -> load: =>
@startLoading() @startLoading()
@ajax( @ajax(
id: 'twitter_index' id: 'twitter_index'
type: 'GET' type: 'GET'
url: @apiPath + '/channels/twitter_index' url: "#{@apiPath}/channels/twitter_index"
processData: true processData: true
success: (data, status, xhr) => success: (data, status, xhr) =>
@stopLoading() @stopLoading()
@ -29,73 +26,173 @@ class Index extends App.ControllerContent
@render(data) @render(data)
) )
render: => render: (data) =>
# accounts = App.Twitter.search(
# sortBy: 'name'
# )
# # show description button, only if content exists # if no twitter app is registered, show into
# showDescription = false if !App.ExternalCredential.findByAttribute(name: 'twitter')
# if App.Twitter.description @html App.view('twitter/index')()
# if !_.isEmpty(accounts) return
# showDescription = true
# else
# description = marked(App.Twitter.description)
@html App.view('twitter/index')() channels = []
for channel_id in data.channel_ids
channel = App.Channel.find(channel_id)
if channel && channel.options && channel.options.sync && channel.options.sync.search
for search in channel.options.sync.search
displayName = '-'
if search.group_id
group = App.Group.find(search.group_id)
displayName = group.displayName()
search.groupName = displayName
if channel && channel.options && channel.options.sync && channel.options.sync.mentions
displayName = '-'
if channel.options.sync.mentions.group_id
group = App.Group.find(channel.options.sync.mentions.group_id)
displayName = group.displayName()
channel.options.sync.mentions.groupName = displayName
if channel && channel.options && channel.options.sync && channel.options.sync.direct_messages
displayName = '-'
if channel.options.sync.direct_messages.group_id
group = App.Group.find(channel.options.sync.direct_messages.group_id)
displayName = group.displayName()
channel.options.sync.direct_messages.groupName = displayName
channels.push channel
@html App.view('twitter/list')(
channels: channels
)
# accounts: accounts # accounts: accounts
# showDescription: showDescription # showDescription: showDescription
# description: description # description: description
createApp: -> if @channel_id
modal = new App.ControllerModal @edit(undefined, @channel_id)
configApp: ->
external_credential = App.ExternalCredential.findByAttribute(name: 'twitter')
modal = new App.ControllerModal(
head: 'Connect Twitter App' head: 'Connect Twitter App'
container: @el.parents('.content') container: @el.parents('.content')
content: App.view('twitter/app_create') contentInline: App.view('twitter/app_config')(external_credential: external_credential)
shown: true shown: true
button: 'Connect' button: 'Connect'
cancel: true cancel: true
onSubmit: => small: true
@html App.view('twitter/list')() onSubmit: (e) =>
modal.close() @formDisable(e)
# verify app credentals
@ajax(
id: 'twitter_app_verify'
type: 'POST'
url: "#{@apiPath}/external_credentials/twitter/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: 'twitter', 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) -> new: (e) ->
# e.preventDefault() window.location.href = "#{@apiPath}/external_credentials/twitter/link_account"
# new App.ControllerGenericNew(
# pageData:
# title: 'SLAs'
# object: 'Sla'
# objects: 'SLAs'
# genericObject: 'Sla'
# container: @el.closest('.content')
# callback: @load
# large: true
# )
edit: (e) -> edit: (e, id) =>
# e.preventDefault() if e
# id = $(e.target).closest('.action').data('id') e.preventDefault()
# new App.ControllerGenericEdit( id = $(e.target).closest('.action').data('id')
# id: id channel = App.Channel.find(id)
# pageData: content = $( App.view('twitter/account_edit')(channel: channel) )
# title: 'SLAs'
# object: 'Sla'
# objects: 'SLAs'
# genericObject: 'Sla'
# callback: @load
# container: @el.closest('.content')
# large: true
# )
delete: (e) -> groupSelection = (selected_id, el, prefix) ->
# e.preventDefault() selection = App.UiElement.select.render(
# id = $(e.target).closest('.action').data('id') name: "#{prefix}::group_id"
# item = App.Twitter.find(id) multiple: false
# new App.ControllerGenericDestroyConfirm( limit: 100
# item: item null: false
# container: @el.closest('.content') relation: 'Group'
# callback: @load nulloption: true
# ) default: selected_id
)
el.find('.js-groups').html(selection)
placeholderAdd = (value = '', group_id) ->
placeholder = content.find('.js-searchTermPlaceholder').clone()
placeholder.removeClass('hidden').removeClass('js-searchTermPlaceholder')
placeholder.find('input').val(value)
placeholder.find('input').attr('name', 'search::term')
groupSelection(group_id, placeholder, 'search')
content.find('.js-searchTermList').append(placeholder)
for item in channel.options.sync.search
placeholderAdd(item.term, item.group_id, 'search')
content.find('.js-searchTermAdd').on('click', ->
placeholderAdd('', '')
)
content.find('.js-searchTerm').on('click', '.js-searchTermRemove',(e) ->
$(e.target).closest('.js-searchTermItem').remove()
)
groupSelection(channel.options.sync.mentions.group_id, content.find('.js-mention'), 'mentions')
groupSelection(channel.options.sync.direct_messages.group_id, content.find('.js-directMessage'), 'direct_messages')
modal = new App.ControllerModal(
head: 'Twitter Account'
container: @el.parents('.content')
contentInline: content
shown: true
cancel: true
onSubmit: (e) =>
@formDisable(e)
params = modal.formParams()
search = []
position = 0
if params.search
if _.isArray(params.search.term)
for key in params.search.term
item =
term: params.search.term[position]
group_id: params.search.group_id[position]
search.push item
position += 1
else
search.push params.search
params.search = search
channel.options.sync = params
@ajax(
id: 'channel_twitter_update'
type: 'POST'
url: "#{@apiPath}/channels/twitter_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) => description: (e) =>
new App.ControllerGenericDescription( new App.ControllerGenericDescription(
@ -103,4 +200,4 @@ class Index extends App.ControllerContent
container: @el.closest('.content') container: @el.closest('.content')
) )
App.Config.set( 'Twitter', { prio: 5000, name: 'Twitter', parent: '#channels', target: '#channels/twitter', controller: Index, role: ['Admin'] }, 'NavBarAdmin' ) App.Config.set('Twitter', { prio: 5000, name: 'Twitter', parent: '#channels', target: '#channels/twitter', controller: Index, role: ['Admin'] }, 'NavBarAdmin')

View file

@ -1,13 +1,14 @@
class IndexRouter extends App.ControllerNavSidbar class IndexRouter extends App.ControllerNavSidbar
configKey: 'NavBarAdmin' configKey: 'NavBarAdmin'
App.Config.set( 'manage', IndexRouter, 'Routes' ) App.Config.set('manage', IndexRouter, 'Routes')
App.Config.set( 'manage/:target', IndexRouter, 'Routes' ) App.Config.set('manage/:target', IndexRouter, 'Routes')
App.Config.set( 'settings/:target', IndexRouter, 'Routes' ) App.Config.set('settings/:target', IndexRouter, 'Routes')
App.Config.set( 'channels/:target', IndexRouter, 'Routes' ) App.Config.set('channels/:target', IndexRouter, 'Routes')
App.Config.set( 'system/:target', IndexRouter, 'Routes' ) App.Config.set('channels/:target/:channel_id', IndexRouter, 'Routes')
App.Config.set('system/:target', IndexRouter, 'Routes')
App.Config.set( 'Manage', { prio: 1000, name: 'Manage', target: '#manage', role: ['Admin'] }, 'NavBarAdmin' ) App.Config.set('Manage', { prio: 1000, name: 'Manage', target: '#manage', role: ['Admin'] }, 'NavBarAdmin')
App.Config.set( 'Channels', { prio: 2500, name: 'Channels', target: '#channels', role: ['Admin'] }, 'NavBarAdmin' ) App.Config.set('Channels', { prio: 2500, name: 'Channels', target: '#channels', role: ['Admin'] }, 'NavBarAdmin')
App.Config.set( 'Settings', { prio: 7000, name: 'Settings', target: '#settings', role: ['Admin'] }, 'NavBarAdmin' ) App.Config.set('Settings', { prio: 7000, name: 'Settings', target: '#settings', role: ['Admin'] }, 'NavBarAdmin')
App.Config.set( 'System', { prio: 8000, name: 'System', target: '#system', role: ['Admin'] }, 'NavBarAdmin' ) App.Config.set('System', { prio: 8000, name: 'System', target: '#system', role: ['Admin'] }, 'NavBarAdmin')

View file

@ -206,4 +206,4 @@ class Verify extends App.ControllerContent
removeAll: true removeAll: true
@formEnable( @$('form') ) @formEnable( @$('form') )
App.Config.set( 'password_reset_verify/:token', Verify, 'Routes' ) App.Config.set('password_reset_verify/:token', Verify, 'Routes')

View file

@ -77,6 +77,7 @@ class _ajaxSingleton
head: 'StatusCode: ' + status head: 'StatusCode: ' + status
contentInline: '<pre>' + App.Utils.htmlEscape(detail) + '</pre>' contentInline: '<pre>' + App.Utils.htmlEscape(detail) + '</pre>'
buttonClose: true buttonClose: true
buttonSubmit: false
) )
) )

View file

@ -115,7 +115,6 @@ class App.Auth
# store user data # store user data
sessionUser = App.User.fullLocal(data.session.id) sessionUser = App.User.fullLocal(data.session.id)
console.log('set', sessionUser)
App.Session.set(sessionUser) App.Session.set(sessionUser)
# trigger auth ok with new session data # trigger auth ok with new session data

View file

@ -0,0 +1,4 @@
class App.ExternalCredential extends App.Model
@configure 'ExternalCredential', 'name', 'credentials'
@extend Spine.Model.Ajax
@url: @apiPath + '/external_credentials'

View file

@ -0,0 +1,31 @@
<div class="alert alert--danger hidden" role="alert"></div>
<fieldset>
<%- @T('Search Terms') %>
<div class="js-searchTerm">
<div class="js-searchTermItem js-searchTermPlaceholder hidden">
<input name="" value=""> -> <div class="js-groups"></div>
<div class="btn btn--text js-searchTermRemove">
<%- @Icon('trash') %> <%- @T('Remove') %>
</div>
<hr>
</div>
<div class="js-searchTermList"></div>
<div class="btn btn--text js-searchTermAdd">
<%- @Icon('plus-small') %> <%- @T('Add') %>
</div>
</div>
<hr>
<div class="js-mention">
<%- @T('Mentions') %> -> <div class="js-groups"></div>
</div>
<hr>
<div class="js-directMessage">
<%- @T('Direct Messages') %> -> <div class="js-groups"></div>
</div>
</fieldset>

View file

@ -0,0 +1,23 @@
<div class="alert alert--danger hidden" role="alert"></div>
<p>
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>
<div class="input form-group">
<div class="formGroup-label">
<label for="consumer_key">Twitter API Key <span>*</span></label>
</div>
<div class="controls">
<input id="consumer_key" type="text" name="consumer_key" value="<% if @external_credential && @external_credential.credentials: %><%= @external_credential.credentials.consumer_key %><% end %>" class="form-control" required autocomplete="off" >
</div>
</div>
<div class="input form-group">
<div class="formGroup-label">
<label for="consumer_secret">Twitter API Secret <span>*</span></label>
</div>
<div class="controls">
<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>
</fieldset>

View file

@ -1,32 +0,0 @@
<h2>Create Twitter App</h2>
<p>
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>
<div class="input form-group">
<div class="formGroup-label">
<label for="CallbackURL">Callback URL</label>
</div>
<div class="controls">
<input id="CallbackURL" type="text" name="name" class="form-control" value="http://example.com/twitter-app" readonly>
<span class="help-block">You need this callback URL for the Twitter App setup.</span>
</div>
</div>
<h2>Enter your Twitter App Keys</h2>
<div class="input form-group">
<div class="formGroup-label">
<label for="TwitterApiKey">Twitter API Key <span>*</span></label>
</div>
<div class="controls">
<input id="TwitterApiKey" type="text" name="name" class="form-control">
</div>
</div>
<div class="input form-group">
<div class="formGroup-label">
<label for="TwitterApiKeySecret">Twitter API Secret <span>*</span></label>
</div>
<div class="controls">
<input id="TwitterApiKeySecret" type="text" name="name" class="form-control">
</div>
</div>
</fieldset>

View file

@ -9,6 +9,6 @@
<p> <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. 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> </p>
<div class="btn btn--success js-create-app"><%- @T('Connect Twitter App') %></div> <div class="btn btn--success js-configApp"><%- @T('Connect Twitter App') %></div>
</div> </div>
</div> </div>

View file

@ -4,47 +4,60 @@
</div> </div>
<div class="page-header-meta"> <div class="page-header-meta">
<a class="btn js-config"><%- @T('Configure App') %></a> <a class="btn js-configApp"><%- @T('Configure App') %></a>
<a class="btn btn--success js-new"><%- @T('Add Account') %></a> <a class="btn btn--success js-new"><%- @T('Add Account') %></a>
</div> </div>
</div> </div>
<div class="page-content"> <div class="page-content">
<div class="action"> <% for channel in @channels: %>
<div class="action" data-id="<%= channel.id %>">
<div class="action-block action-row"> <div class="action-block action-row">
<h2><%- @Icon('status', 'supergood-color inline') %> Zammad Community <span class="text-muted">@zammad_community</span></h2> <h2><%- @Icon('status', 'supergood-color inline') %> <%= channel.options.user.name %> <span class="text-muted">@<%= channel.options.user.screen_name %></span></h2>
</div> </div>
<div class="action-flow action-flow--row"> <div class="action-flow action-flow--row">
<div class="action-block"> <div class="action-block">
<h3><%- @T('Search Terms') %></h3> <h3><%- @T('Search Terms') %></h3>
zammad <% if channel.options.sync.search: %>
<% for search in channel.options.sync.search: %>
<%= search.term %><br>
<% end %>
<% end %>
</div> </div>
<%- @Icon('arrow-right', 'action-flow-icon') %> <%- @Icon('arrow-right', 'action-flow-icon') %>
<div class="action-block"> <div class="action-block">
<h3><%- @T('Group') %></h3> <h3><%- @T('Group') %></h3>
social network <% if channel.options.sync.search: %>
<% for search in channel.options.sync.search: %>
<%= search.groupName %><br>
<% end %>
<% end %>
</div> </div>
</div> </div>
<div class="action-flow action-flow--row"> <div class="action-flow action-flow--row">
<div class="action-block"> <div class="action-block">
<h3><%- @T('Mentions') %></h3> <h3><%- @T('Mentions') %></h3>
@zammad_community @<%= channel.options.user.screen_name %>
</div> </div>
<%- @Icon('arrow-right', 'action-flow-icon') %> <%- @Icon('arrow-right', 'action-flow-icon') %>
<div class="action-block"> <div class="action-block">
<h3><%- @T('Group') %></h3> <h3><%- @T('Group') %></h3>
social network <% if channel.options.sync.mentions: %>
<%= channel.options.sync.mentions.groupName %>
<% end %>
</div> </div>
</div> </div>
<div class="action-flow action-flow--row"> <div class="action-flow action-flow--row">
<div class="action-block"> <div class="action-block">
<h3><%- @T('Direct Messages') %></h3> <h3><%- @T('Direct Messages') %></h3>
@zammad_community @<%= channel.options.user.screen_name %>
</div> </div>
<%- @Icon('arrow-right', 'action-flow-icon') %> <%- @Icon('arrow-right', 'action-flow-icon') %>
<div class="action-block"> <div class="action-block">
<h3><%- @T('Group') %></h3> <h3><%- @T('Group') %></h3>
social network <% if channel.options.sync.direct_messages: %>
<%= channel.options.sync.direct_messages.groupName %>
<% end %>
</div> </div>
</div> </div>
<div class="action-controls"> <div class="action-controls">
@ -52,4 +65,5 @@
<div class="sla-edit btn js-edit"><%- @T('Edit') %></div> <div class="sla-edit btn js-edit"><%- @T('Edit') %></div>
</div> </div>
</div> </div>
<% end %>
</div> </div>

View file

@ -45,6 +45,28 @@ curl http://localhost/api/v1/channels.json -v -u #{login}:#{password} -H "Conten
model_destory_render(Channel, params) model_destory_render(Channel, params)
end end
def twitter_index
assets = {}
ExternalCredential.where(name: 'twitter').each {|external_credential|
assets = external_credential.assets(assets)
}
channel_ids = []
Channel.order(:id).each {|channel|
next if channel.area != 'Twitter::Account'
assets = channel.assets(assets)
channel_ids.push channel.id
}
render json: {
assets: assets,
channel_ids: channel_ids,
}
end
def twitter_verify
return if deny_if_not_role(Z_ROLENAME_ADMIN)
model_update_render(Channel, params)
end
def email_index def email_index
return if deny_if_not_role(Z_ROLENAME_ADMIN) return if deny_if_not_role(Z_ROLENAME_ADMIN)
system_online_service = Setting.get('system_online_service') system_online_service = Setting.get('system_online_service')

View file

@ -15,28 +15,12 @@ class ExternalCredentialsController < ApplicationController
def create def create
return if deny_if_not_role(Z_ROLENAME_ADMIN) return if deny_if_not_role(Z_ROLENAME_ADMIN)
model_create_render(ExternalCredential, params)
# try access
begin
attributes = ExternalCredential.app_verify(params)
model_create_render(ExternalCredential, { name: params[:provider].downcase, credentials: attributes })
return
rescue => e
render json: { error: e.message }, status: :unprocessable_entity
end
end end
def update def update
return if deny_if_not_role(Z_ROLENAME_ADMIN) return if deny_if_not_role(Z_ROLENAME_ADMIN)
model_update_render(ExternalCredential, params)
# try access
begin
attributes = ExternalCredential.app_verify(params)
model_update_render(ExternalCredential, { name: params[:provider].downcase, credentials: attributes })
return
rescue => e
render json: { error: e.message }, status: :unprocessable_entity
end
end end
def destroy def destroy
@ -44,26 +28,29 @@ class ExternalCredentialsController < ApplicationController
model_destory_render(ExternalCredential, params) model_destory_render(ExternalCredential, params)
end end
def app_verify
attributes = ExternalCredential.app_verify(params)
render json: { attributes: attributes }, status: :ok
return
rescue => e
render json: { error: e.message }, status: :ok
end
def link_account def link_account
return if deny_if_not_role(Z_ROLENAME_ADMIN) return if deny_if_not_role(Z_ROLENAME_ADMIN)
provider = params[:provider].downcase provider = params[:provider].downcase
attributes = ExternalCredential.request_account_to_link(provider, callback_url(provider)) attributes = ExternalCredential.request_account_to_link(provider, callback_url(provider))
session[:request_token] = attributes[:request_token] session[:request_token] = attributes[:request_token]
redirect_to attributes[:authorize_url] redirect_to attributes[:authorize_url]
end end
def callback def callback
return if deny_if_not_role(Z_ROLENAME_ADMIN) return if deny_if_not_role(Z_ROLENAME_ADMIN)
provider = params[:provider].downcase provider = params[:provider].downcase
channel = ExternalCredential.link_account(provider, session[:request_token], params) channel = ExternalCredential.link_account(provider, session[:request_token], params)
session[:request_token] = nil session[:request_token] = nil
redirect_to app_url(provider, channel.id)
render json: channel
end end
private private
@ -72,4 +59,8 @@ class ExternalCredentialsController < ApplicationController
"#{Setting.get('http_type')}://#{Setting.get('fqdn')}#{Rails.configuration.api_path}/external_credentials/#{provider}/callback" "#{Setting.get('http_type')}://#{Setting.get('fqdn')}#{Rails.configuration.api_path}/external_credentials/#{provider}/callback"
end end
def app_url(provider, channel_id)
"#{Setting.get('http_type')}://#{Setting.get('fqdn')}/#channels/#{provider}/#{channel_id}"
end
end end

View file

@ -47,6 +47,8 @@ class Channel::Driver::Twitter
def fetch (options, channel) def fetch (options, channel)
options = check_external_credential(options)
@tweet = Tweet.new(options[:auth]) @tweet = Tweet.new(options[:auth])
@sync = options[:sync] @sync = options[:sync]
@channel = channel @channel = channel
@ -90,6 +92,8 @@ class Channel::Driver::Twitter
# return if we run import mode # return if we run import mode
return if Setting.get('import_mode') return if Setting.get('import_mode')
options = check_external_credential(options)
@tweet = Tweet.new(options[:auth]) @tweet = Tweet.new(options[:auth])
tweet = @tweet.from_article(article) tweet = @tweet.from_article(article)
disconnect disconnect
@ -164,4 +168,15 @@ class Channel::Driver::Twitter
counter += 1 counter += 1
} }
end end
def check_external_credential(options)
if options[:auth] && options[:auth][:external_credential_id]
external_credential = ExternalCredential.find_by(id: options[:auth][:external_credential_id])
fail "No such ExternalCredential.find(#{options[:auth][:external_credential_id]})" if !external_credential
options[:auth][:consumer_key] = external_credential.credentials['consumer_key']
options[:auth][:consumer_secret] = external_credential.credentials['consumer_secret']
end
options
end
end end

View file

@ -9,8 +9,12 @@ Zammad::Application.routes.draw do
match api_path + '/channels/email_verify', to: 'channels#email_verify', via: :post match api_path + '/channels/email_verify', to: 'channels#email_verify', via: :post
match api_path + '/channels/email_notification', to: 'channels#email_notification', via: :post match api_path + '/channels/email_notification', to: 'channels#email_notification', via: :post
# twitter helper
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
# channels # channels
match api_path + '/channels/group/:id', to: 'channels#group_update', via: :post match api_path + '/channels/group/:id', to: 'channels#group_update', via: :post
match api_path + '/channels/:id', to: 'channels#destroy', via: :delete match api_path + '/channels/:id', to: 'channels#destroy', via: :delete
end end

View file

@ -9,6 +9,7 @@ Zammad::Application.routes.draw do
match api_path + '/external_credentials/:id', to: 'external_credentials#destroy', via: :delete match api_path + '/external_credentials/:id', to: 'external_credentials#destroy', via: :delete
# callback URL # callback URL
match api_path + '/external_credentials/:provider/app_verify', to: 'external_credentials#app_verify', via: :post
match api_path + '/external_credentials/:provider/link_account', to: 'external_credentials#link_account', via: :get match api_path + '/external_credentials/:provider/link_account', to: 'external_credentials#link_account', via: :get
match api_path + '/external_credentials/:provider/callback', to: 'external_credentials#callback', via: :get match api_path + '/external_credentials/:provider/callback', to: 'external_credentials#callback', via: :get

View file

@ -11,9 +11,15 @@ class ExternalCredential::Twitter
def self.request_account_to_link(callback_url, credentials = {}) def self.request_account_to_link(callback_url, credentials = {})
external_credential = ExternalCredential.find_by(name: 'twitter') external_credential = ExternalCredential.find_by(name: 'twitter')
if !credentials[:consumer_key]
credentials[:consumer_key] = external_credential.credentials['consumer_key']
end
if !credentials[:consumer_secret]
credentials[:consumer_secret] = external_credential.credentials['consumer_secret']
end
consumer = OAuth::Consumer.new( consumer = OAuth::Consumer.new(
credentials[:consumer_key] || external_credential.credentials[:consumer_key], credentials[:consumer_key],
credentials[:consumer_secret] || external_credential.credentials[:consumer_secret], { credentials[:consumer_secret], {
site: 'https://api.twitter.com' site: 'https://api.twitter.com'
}) })
request_token = consumer.get_request_token(oauth_callback: callback_url) request_token = consumer.get_request_token(oauth_callback: callback_url)
@ -25,7 +31,6 @@ class ExternalCredential::Twitter
def self.link_account(request_token, params) def self.link_account(request_token, params)
fail if request_token.params[:oauth_token] != params[:oauth_token] fail if request_token.params[:oauth_token] != params[:oauth_token]
external_credential = ExternalCredential.find_by(name: 'twitter') external_credential = ExternalCredential.find_by(name: 'twitter')
access_token = request_token.get_access_token(oauth_verifier: params[:oauth_verifier]) access_token = request_token.get_access_token(oauth_verifier: params[:oauth_verifier])
client = Twitter::REST::Client.new( client = Twitter::REST::Client.new(
@ -44,6 +49,7 @@ class ExternalCredential::Twitter
user: { user: {
id: user.id, id: user.id,
screen_name: user.screen_name, screen_name: user.screen_name,
name: user.name,
}, },
auth: { auth: {
external_credential_id: external_credential.id, external_credential_id: external_credential.id,