Rewrite of channel api.

This commit is contained in:
Martin Edenhofer 2015-08-28 02:53:14 +02:00
parent 89546a09a2
commit 6a733396f9
38 changed files with 1570 additions and 613 deletions

View file

@ -7,19 +7,9 @@ class App.ChannelEmail extends App.ControllerTabs
@tabs = [
{
name: 'Inbound',
target: 'c-inbound',
controller: App.ChannelEmailInbound,
},
{
name: 'Outbound',
target: 'c-outbound',
controller: App.ChannelEmailOutbound,
},
{
name: 'Adresses',
target: 'c-address',
controller: App.ChannelEmailAddress,
name: 'Email Accounts',
target: 'c-channel',
controller: App.ChannelEmailAccountOverview,
},
{
name: 'Signatures',
@ -132,100 +122,6 @@ class App.ChannelEmailFilterEdit extends App.ControllerModal
@hide()
)
class App.ChannelEmailAddress extends App.Controller
events:
'click [data-type=new]': 'new'
constructor: ->
super
App.EmailAddress.subscribe( @render, initFetch: true )
render: =>
data = App.EmailAddress.search( sortBy: 'realname' )
template = $( '<div><div class="overview"></div><a data-type="new" class="btn btn--success">' + App.i18n.translateContent('New') + '</a></div>' )
new App.ControllerTable(
el: template.find('.overview')
model: App.EmailAddress
objects: data
bindRow:
events:
'click': @edit
)
@html template
new: (e) =>
e.preventDefault()
new App.ChannelEmailAddressEdit(
container: @el.closest('.content')
)
edit: (id, e) =>
e.preventDefault()
item = App.EmailAddress.find(id)
new App.ChannelEmailAddressEdit(
object: item
container: @el.closest('.content')
)
class App.ChannelEmailAddressEdit extends App.ControllerModal
constructor: ->
super
@head = 'Email-Address'
@button = true
@close = true
@cancel = true
if @object
@form = new App.ControllerForm(
model: App.EmailAddress
params: @object
autofocus: true
)
else
@form = new App.ControllerForm(
model: App.EmailAddress,
autofocus: true,
)
@content = @form.form
@show()
onSubmit: (e) =>
e.preventDefault()
# get params
params = @formParam(e.target)
object = @object || new App.EmailAddress
object.load(params)
# validate form
errors = @form.validate( params )
# show errors in form
if errors
@log 'error', errors
@formValidate( form: e.target, errors: errors )
return false
# disable form
@formDisable(e)
# save object
object.save(
done: =>
@hide()
fail: =>
@hide()
)
class App.ChannelEmailSignature extends App.Controller
events:
'click [data-type=new]': 'new'
@ -317,193 +213,575 @@ class App.ChannelEmailSignatureEdit extends App.ControllerModal
@hide()
)
class App.ChannelEmailInbound extends App.Controller
class App.ChannelEmailAccountOverview extends App.Controller
events:
'click [data-type=new]': 'new'
'click [data-type="new"]': 'wizard'
'click [data-type="delete"]': 'delete'
'click [data-type="edit-inbound"]': 'edit_inbound'
'click [data-type="edit-outbound"]': 'edit_outbound'
'click [data-type="email-address-new"]': 'email_address_new'
'click [data-type="email-address-edit"]': 'email_address_edit'
'click [data-type="edit-notification-outbound"]': 'edit_notification_outbound'
constructor: ->
super
App.Channel.subscribe( @render, initFetch: true )
@interval(@load, 20000)
render: =>
channels = App.Channel.search( filter: { area: 'Email::Inbound' } )
load: =>
@ajax(
id: 'email_index'
type: 'GET'
url: @apiPath + '/channels/email_index'
processData: true
success: (data, status, xhr) =>
template = $( '<div><div class="overview"></div><a data-type="new" class="btn btn--success">' + App.i18n.translateContent('New') + '</a></div>' )
# load assets
App.Collection.loadAssets( data.assets )
new App.ControllerTable(
el: template.find('.overview')
model: App.Channel
objects: channels
bindRow:
events:
'click': @edit
@render()
)
@html template
new: (e) =>
e.preventDefault()
new App.ChannelEmailInboundEdit(
container: @el.closest('.content')
)
edit: (id, e) =>
e.preventDefault()
item = App.Channel.find(id)
new App.ChannelEmailInboundEdit(
object: item
container: @el.closest('.content')
)
class App.ChannelEmailInboundEdit extends App.ControllerModal
constructor: ->
super
@head = 'Email Channel'
@button = true
@close = true
@cancel = true
if @object
@form = new App.ControllerForm(
model: App.Channel
params: @object
autofocus: true
)
else
@form = new App.ControllerForm(
model: App.Channel
autofocus: true
)
@content = @form.form
@show()
onSubmit: (e) =>
e.preventDefault()
# get params
params = @formParam(e.target)
params['area'] = 'Email::Inbound'
object = @object || new App.Channel
object.load(params)
# validate form
errors = @form.validate( params )
# show errors in form
if errors
@log 'error', errors
@formValidate( form: e.target, errors: errors )
return false
# disable form
@formDisable(e)
# save object
object.save(
done: =>
@hide()
fail: =>
@hide()
)
class App.ChannelEmailOutbound extends App.Controller
events:
'change [name="adapter"]': 'toggle'
'submit #mail_adapter': 'update'
constructor: ->
super
App.Channel.subscribe( @render, initFetch: true )
render: =>
@html App.view('channel/email_outbound')()
# get current Email::Outbound channel
channels = App.Channel.all()
adapters = {}
adapter_used = undefined
channel_used = undefined
# get channels
channels = App.Channel.search( filter: { area: 'Email::Account' } )
for channel in channels
if channel.area is 'Email::Outbound'
email_addresses = App.EmailAddress.search( filter: { channel_id: channel.id } )
channel.email_addresses = email_addresses
adapters[channel.adapter] = channel.adapter
if @adapter_used
if @adapter_used is channel.adapter
adapter_used = channel.adapter
channel_used = channel
else if channel.active is true
adapter_used = channel.adapter
channel_used = channel
# get all unlinked email addresses
email_addresses_all = App.EmailAddress.all()
email_addresses_not_used = []
for email_address in email_addresses_all
if !email_address.channel_id || !App.Channel.exists(email_address.channel_id)
email_addresses_not_used.push email_address
configure_attributes = [
{ name: 'adapter', display: 'Send Mails via', tag: 'select', multiple: false, null: false, options: adapters , default: adapter_used },
# get channels
channel = App.Channel.search( filter: { area: 'Email::Notification', active: true } )[0]
@html App.view('channel/email_account_overview')(
channels: channels
email_addresses_not_used: email_addresses_not_used
channel: channel
)
wizard: (e) =>
e.preventDefault()
new App.ChannelEmailAccountWizard(
container: @el.closest('.content')
callback: @load
)
edit_inbound: (e) =>
e.preventDefault()
id = $(e.target).closest('tr').data('id')
channel = App.Channel.find(id)
slide = 'js-inbound'
new App.ChannelEmailAccountWizard(
container: @el.closest('.content')
slide: slide
channel: channel
callback: @load
)
edit_outbound: (e) =>
e.preventDefault()
id = $(e.target).closest('tr').data('id')
channel = App.Channel.find(id)
slide = 'js-outbound'
new App.ChannelEmailAccountWizard(
container: @el.closest('.content')
slide: slide
channel: channel
callback: @load
)
delete: (e) =>
e.preventDefault()
id = $(e.target).closest('tr').data('id')
item = App.Channel.find(id)
new App.ControllerGenericDestroyConfirm(
item: item
container: @el.closest('.content')
callback: @load
)
email_address_new: (e) =>
e.preventDefault()
channel_id = $(e.target).closest('tr').data('id')
new App.ControllerGenericNew(
pageData:
object: 'Email Address'
genericObject: 'EmailAddress'
container: @el.closest('.content')
item:
channel_id: channel_id
callback: @load
)
email_address_edit: (e) =>
e.preventDefault()
id = $(e.target).closest('li').data('id')
new App.ControllerGenericEdit(
pageData:
object: 'Email Address'
genericObject: 'EmailAddress'
container: @el.closest('.content')
id: id
callback: @load
)
edit_notification_outbound: (e) =>
e.preventDefault()
id = $(e.target).closest('tr').data('id')
channel = App.Channel.find(id)
slide = 'js-outbound'
new App.ChannelEmailNotificationWizard(
container: @el.closest('.content')
channel: channel
callback: @load
)
class App.ChannelEmailAccountWizard extends App.Controller
elements:
'.modal-body': 'body'
className: 'modal fade'
events:
'submit .js-intro': 'probeBasedOnIntro'
'submit .js-inbound': 'probeInbound'
'change .js-outbound [name=adapter]': 'toggleOutboundAdapter'
'submit .js-outbound': 'probleOutbound'
'click .js-back': 'goToSlide'
constructor: ->
super
# store account settings
@account =
inbound:
adapter: undefined
options: undefined
outbound:
adapter: undefined
options: undefined
meta: {}
if @channel
@account =
inbound: @channel.options.inbound
outbound: @channel.options.outbound
meta: {}
if @container
@el.addClass('modal--local')
@render()
@el.modal
keyboard: true
show: true
backdrop: true
container: @container
.on
'show.bs.modal': @onShow
'shown.bs.modal': @onComplete
'hidden.bs.modal': =>
if @callback
@callback()
$('.modal').remove()
if @slide
@showSlide(@slide)
render: =>
@html App.view('channel/email_account_wizard')()
@showSlide('js-intro')
# outbound
adapters =
sendmail: 'Local MTA (Sendmail/Postfix/Exim/...) - use server setup'
smtp: 'SMTP - configure your own outgoing SMTP settings'
configureAttributesOutbound = [
{ name: 'adapter', display: 'Send Mails via', tag: 'select', multiple: false, null: false, options: adapters },
]
new App.ControllerForm(
el: @el.find('#form-email-adapter'),
model: { configure_attributes: configure_attributes, className: '' },
autofocus: true,
el: @$('.base-outbound-type')
model:
configure_attributes: configureAttributesOutbound
className: ''
params:
adapter: @account.outbound.adapter || 'sendmail'
)
@toggleOutboundAdapter()
# inbound
configureAttributesInbound = [
{ name: 'adapter', display: 'Type', tag: 'select', multiple: false, null: false, options: { imap: 'IMAP', pop3: 'POP3' } },
{ name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false },
{ name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false },
{ name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: false, autocapitalize: false, single: true },
]
new App.ControllerForm(
el: @$('.base-inbound-settings'),
model:
configure_attributes: configureAttributesInbound
className: ''
params: @account.inbound
)
# if adapter_used is 'Sendmail'
# # some form
toggleOutboundAdapter: =>
if adapter_used is 'SMTP'
configure_attributes = [
{ name: 'host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, default: (channel_used['options']&&channel_used['options']['host']) },
{ name: 'user', display: 'User', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, default: (channel_used['options']&&channel_used['options']['user']) },
{ name: 'password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false, default: (channel_used['options']&&channel_used['options']['password']) },
{ name: 'ssl', display: 'SSL', tag: 'select', multiple: false, null: false, options: { true: 'yes', false: 'no' } , translate: true, default: (channel_used['options']&&channel_used['options']['ssl']) },
{ name: 'port', display: 'Port', tag: 'input', type: 'text', limit: 5, null: false, class: 'span1', autocapitalize: false, default: ((channel_used['options']&&channel_used['options']['port']) || 25) },
# fill user / password based on intro info
channel_used = { options: {} }
if @account['meta']
channel_used['options']['user'] = @account['meta']['email']
channel_used['options']['password'] = @account['meta']['password']
# show used backend
@el.find('.base-outbound-settings').html('')
adapter = @$('.js-outbound [name=adapter]').val()
if adapter is 'smtp'
configureAttributesOutbound = [
{ name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autofocus: true },
{ name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false },
{ name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false, single: true },
]
@form = new App.ControllerForm(
el: @el.find('#form-email-adapter-settings'),
model: { configure_attributes: configure_attributes, className: '' },
autofocus: true,
el: @$('.base-outbound-settings')
model:
configure_attributes: configureAttributesOutbound
className: ''
params: @account.outbound
)
toggle: (e) =>
probeBasedOnIntro: (e) =>
e.preventDefault()
params = @formParam(e.target)
# remember account settings
@account.meta = params
# let backend know about the channel
if @channel
params.channel_id = @channel.id
@disable(e)
@$('.js-probe .js-email').text( params.email )
@showSlide('js-probe')
@ajax(
id: 'email_probe'
type: 'POST'
url: @apiPath + '/channels/email_probe'
data: JSON.stringify( params )
processData: true
success: (data, status, xhr) =>
if data.result is 'ok'
if data.setting
for key, value of data.setting
@account[key] = value
@verify(@account)
else if data.result is 'duplicate'
@showSlide('js-intro')
@showAlert('js-intro', 'Account already exists!' )
else
@showSlide('js-inbound')
@showAlert('js-inbound', 'Unable to detect your server settings. Manual configuration needed.' )
@$('.js-inbound [name="options::user"]').val( @account['meta']['email'] )
@$('.js-inbound [name="options::password"]').val( @account['meta']['password'] )
@enable(e)
fail: =>
@enable(e)
@showSlide('js-intro')
)
probeInbound: (e) =>
e.preventDefault()
# get params
params = @formParam(e.target)
# render page with new selected adapter
if @adapter_used isnt params['adapter']
# let backend know about the channel
params.channel_id = @channel.id
# set selected adapter
@adapter_used = params['adapter']
@disable(e)
@render()
@showSlide('js-test')
update: (e) =>
@ajax(
id: 'email_inbound'
type: 'POST'
url: @apiPath + '/channels/email_inbound'
data: JSON.stringify( params )
processData: true
success: (data, status, xhr) =>
if data.result is 'ok'
# remember account settings
@account.inbound = params
@showSlide('js-outbound')
if !@channel
@$('.js-outbound [name="options::user"]').val( @account['meta']['email'] )
@$('.js-outbound [name="options::password"]').val( @account['meta']['password'] )
else
@showSlide('js-inbound')
@showAlert('js-inbound', data.message_human || data.message )
@enable(e)
fail: =>
@showSlide('js-inbound')
@showAlert('js-inbound', data.message_human || data.message )
@enable(e)
)
probleOutbound: (e) =>
e.preventDefault()
params = @formParam(e.target)
# errors = @form.validate( params )
# get params
params = @formParam(e.target)
params['email'] = @account['meta']['email']
# update Email::Outbound adapter
channels = App.Channel.all()
for channel in channels
if channel.area is 'Email::Outbound' && channel.adapter is params['adapter']
channel.updateAttributes(
options: {
host: params['host'],
user: params['user'],
password: params['password'],
ssl: params['ssl'],
port: params['port'],
},
active: true,
)
if !params['email'] && @channel
email_addresses = App.EmailAddress.search( filter: { channel_id: @channel.id } )
if email_addresses && email_addresses[0]
params['email'] = email_addresses[0].email
# set all other Email::Outbound adapters to inactive
channels = App.Channel.all()
for channel in channels
if channel.area is 'Email::Outbound' && channel.adapter isnt params['adapter']
channel.updateAttributes( active: false )
# let backend know about the channel
params.channel_id = @channel.id
@disable(e)
@showSlide('js-test')
@ajax(
id: 'email_outbound'
type: 'POST'
url: @apiPath + '/channels/email_outbound'
data: JSON.stringify( params )
processData: true
success: (data, status, xhr) =>
if data.result is 'ok'
# remember account settings
@account.outbound = params
@verify(@account)
else
@showSlide('js-outbound')
@showAlert('js-outbound', data.message_human || data.message )
@enable(e)
fail: =>
@showSlide('js-outbound')
@showAlert('js-outbound', data.message_human || data.message )
@enable(e)
)
verify: (account, count = 0) =>
@showSlide('js-verify')
# let backend know about the channel
if @channel
account.channel_id = @channel.id
if !account.email && @channel
email_addresses = App.EmailAddress.search( filter: { channel_id: @channel.id } )
if email_addresses && email_addresses[0]
account.email = email_addresses[0].email
@ajax(
id: 'email_verify'
type: 'POST'
url: @apiPath + '/channels/email_verify'
data: JSON.stringify( account )
processData: true
success: (data, status, xhr) =>
if data.result is 'ok'
@el.remove()
else
if count is 2
@showAlert('js-verify', data.message_human || data.message )
@delay(
=>
@showSlide('js-intro')
@showAlert('js-intro', 'Unable to verify sending and receiving. Please check your settings.' )
2300
)
else
if data.subject && @account
@account.subject = data.subject
@verify( @account, count + 1 )
fail: =>
@showSlide('js-intro')
@showAlert('js-intro', 'Unable to verify sending and receiving. Please check your settings.' )
)
goToSlide: (e) =>
e.preventDefault()
slide = $(e.target).data('slide')
@showSlide(slide)
showSlide: (name) =>
@hideAlert(name)
@$('.setup.wizard').addClass('hide')
@$(".setup.wizard.#{name}").removeClass('hide')
@$(".setup.wizard.#{name} input, .setup.wizard.#{name} select").first().focus()
showAlert: (screen, message) =>
@$(".#{screen}").find('.alert').removeClass('hide').text( App.i18n.translateInline( message ) )
hideAlert: (screen) =>
@$(".#{screen}").find('.alert').addClass('hide')
disable: (e) =>
@formDisable(e)
@$('.wizard-controls .btn').attr('disabled', true)
enable: (e) =>
@formEnable(e)
@$('.wizard-controls .btn').attr('disabled', false)
class App.ChannelEmailNotificationWizard extends App.Controller
elements:
'.modal-body': 'body'
className: 'modal fade'
events:
'change .js-outbound [name=adapter]': 'toggleOutboundAdapter'
'submit .js-outbound': 'probleOutbound'
constructor: ->
super
# store account settings
@account =
inbound:
adapter: undefined
options: undefined
outbound:
adapter: undefined
options: undefined
meta: {}
if @channel
@account =
inbound: @channel.options.inbound
outbound: @channel.options.outbound
if @container
@el.addClass('modal--local')
@render()
@el.modal
keyboard: true
show: true
backdrop: true
container: @container
.on
'show.bs.modal': @onShow
'shown.bs.modal': @onComplete
'hidden.bs.modal': =>
if @callback
@callback()
$('.modal').remove()
if @slide
@showSlide(@slide)
render: =>
@html App.view('channel/email_notification_wizard')()
@showSlide('js-outbound')
# outbound
adapters =
sendmail: 'Local MTA (Sendmail/Postfix/Exim/...) - use server setup'
smtp: 'SMTP - configure your own outgoing SMTP settings'
configureAttributesOutbound = [
{ name: 'adapter', display: 'Send Mails via', tag: 'select', multiple: false, null: false, options: adapters },
]
new App.ControllerForm(
el: @$('.base-outbound-type')
model:
configure_attributes: configureAttributesOutbound
className: ''
params:
adapter: @account.outbound.adapter || 'sendmail'
)
@toggleOutboundAdapter()
toggleOutboundAdapter: =>
# show used backend
@el.find('.base-outbound-settings').html('')
adapter = @$('.js-outbound [name=adapter]').val()
if adapter is 'smtp'
configureAttributesOutbound = [
{ name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autofocus: true },
{ name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false },
{ name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false, single: true },
]
@form = new App.ControllerForm(
el: @$('.base-outbound-settings')
model:
configure_attributes: configureAttributesOutbound
className: ''
params: @account.outbound
)
probleOutbound: (e) =>
e.preventDefault()
# get params
params = @formParam(e.target)
# let backend know about the channel
params.channel_id = @channel.id
@disable(e)
@showSlide('js-test')
@ajax(
id: 'email_outbound'
type: 'POST'
url: @apiPath + '/channels/email_notification'
data: JSON.stringify( params )
processData: true
success: (data, status, xhr) =>
if data.result is 'ok'
@el.remove()
else
@showSlide('js-outbound')
@showAlert('js-outbound', data.message_human || data.message )
@enable(e)
fail: =>
@showSlide('js-outbound')
@showAlert('js-outbound', data.message_human || data.message )
@enable(e)
)
showSlide: (name) =>
@hideAlert(name)
@$('.setup.wizard').addClass('hide')
@$(".setup.wizard.#{name}").removeClass('hide')
@$(".setup.wizard.#{name} input, .setup.wizard.#{name} select").first().focus()
showAlert: (screen, message) =>
@$(".#{screen}").find('.alert').removeClass('hide').text( App.i18n.translateInline( message ) )
hideAlert: (screen) =>
@$(".#{screen}").find('.alert').addClass('hide')
disable: (e) =>
@formDisable(e)
@$('.wizard-controls .btn').attr('disabled', true)
enable: (e) =>
@formEnable(e)
@$('.wizard-controls .btn').attr('disabled', false)

View file

@ -364,7 +364,7 @@ class Base extends App.ControllerContent
if App.Config.get('system_online_service')
@navigate 'getting_started/channel/email_pre_configured'
else
@navigate 'getting_started/channel'
@navigate 'getting_started/email_notification'
else
for key, value of data.messages
@showAlert( key, value )
@ -394,6 +394,151 @@ class Base extends App.ControllerContent
App.Config.set( 'getting_started/base', Base, 'Routes' )
class EmailNotification extends App.ControllerContent
className: 'getstarted fit'
events:
'change .js-outbound [name=adapter]': 'toggleOutboundAdapter'
'submit .js-outbound': 'submit'
constructor: ->
super
# redirect if we are not admin
if !@authenticate(true)
@navigate '#'
return
# set title
@title 'Email Notifications'
@adapters = [
{
name: 'Email'
class: 'email'
link: '#getting_started/channel/email'
},
]
@fetch()
release: =>
@el.removeClass('fit getstarted')
fetch: ->
# get data
@ajax(
id: 'getting_started',
type: 'GET',
url: @apiPath + '/getting_started',
processData: true,
success: (data, status, xhr) =>
# check if import is active
if data.import_mode == true
@navigate '#import/' + data.import_backend
return
# render page
@render()
)
render: ->
@html App.view('getting_started/email_notification')()
adapters =
sendmail: 'Local MTA (Sendmail/Postfix/Exim/...) - use server setup'
smtp: 'SMTP - configure your own outgoing SMTP settings'
adapter_used = 'sendmail'
configureAttributesOutbound = [
{ name: 'adapter', display: 'Send Mails via', tag: 'select', multiple: false, null: false, options: adapters , default: adapter_used },
]
new App.ControllerForm(
el: @$('.base-outbound-type'),
model: { configure_attributes: configureAttributesOutbound, className: '' },
)
@toggleOutboundAdapter()
toggleOutboundAdapter: =>
# show used backend
channel_used = { options: {} }
adapter = @$('.js-outbound [name=adapter]').val()
if adapter is 'smtp'
configureAttributesOutbound = [
{ name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autofocus: true, default: (channel_used['options']&&channel_used['options']['host']) },
{ name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, default: (channel_used['options']&&channel_used['options']['user']) },
{ name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false, single: true, default: (channel_used['options']&&channel_used['options']['password']) },
]
@form = new App.ControllerForm(
el: @$('.base-outbound-settings')
model: { configure_attributes: configureAttributesOutbound, className: '' }
)
else
@el.find('.base-outbound-settings').html('')
submit: (e) =>
e.preventDefault()
# get params
params = @formParam(e.target)
params['email'] = 'me@localhost'
@disable(e)
@showSlide('js-test')
@ajax(
id: 'email_notification'
type: 'POST'
url: @apiPath + '/channels/email_notification'
data: JSON.stringify( params )
processData: true
success: (data, status, xhr) =>
if data.result is 'ok'
for key, value of data.settings
App.Config.set( key, value )
if App.Config.get('system_online_service')
@navigate 'getting_started/channel/email_pre_configured'
else
@navigate 'getting_started/channel'
else
@showSlide('js-outbound')
@showAlert('js-outbound', data.message_human || data.message )
@enable(e)
fail: =>
@showSlide('js-outbound')
@showAlert('js-outbound', data.message_human || data.message )
@enable(e)
)
goToSlide: (e) =>
e.preventDefault()
slide = $(e.target).data('slide')
@showSlide(slide)
showSlide: (name) =>
@hideAlert(name)
@$('.setup.wizard').addClass('hide')
@$(".setup.wizard.#{name}").removeClass('hide')
@$(".setup.wizard.#{name} input, .setup.wizard.#{name} select").first().focus()
showAlert: (screen, message) =>
@$(".#{screen}").find('.alert').removeClass('hide').text( App.i18n.translateInline( message ) )
hideAlert: (screen) =>
@$(".#{screen}").find('.alert').addClass('hide')
disable: (e) =>
@formDisable(e)
@$('.wizard-controls .btn').attr('disabled', true)
enable: (e) =>
@formEnable(e)
@$('.wizard-controls .btn').attr('disabled', false)
App.Config.set( 'getting_started/email_notification', EmailNotification, 'Routes' )
class Channel extends App.ControllerContent
className: 'getstarted fit'
@ -610,7 +755,7 @@ class ChannelEmail extends App.ControllerContent
@ajax(
id: 'email_probe'
type: 'POST'
url: @apiPath + '/getting_started/email_probe'
url: @apiPath + '/channels/email_probe'
data: JSON.stringify( params )
processData: true
success: (data, status, xhr) =>
@ -643,7 +788,7 @@ class ChannelEmail extends App.ControllerContent
@ajax(
id: 'email_inbound'
type: 'POST'
url: @apiPath + '/getting_started/email_inbound'
url: @apiPath + '/channels/email_inbound'
data: JSON.stringify( params )
processData: true
success: (data, status, xhr) =>
@ -679,7 +824,7 @@ class ChannelEmail extends App.ControllerContent
@ajax(
id: 'email_outbound'
type: 'POST'
url: @apiPath + '/getting_started/email_outbound'
url: @apiPath + '/channels/email_outbound'
data: JSON.stringify( params )
processData: true
success: (data, status, xhr) =>
@ -705,7 +850,7 @@ class ChannelEmail extends App.ControllerContent
@ajax(
id: 'email_verify'
type: 'POST'
url: @apiPath + '/getting_started/email_verify'
url: @apiPath + '/channels/email_verify'
data: JSON.stringify( account )
processData: true
success: (data, status, xhr) =>

View file

@ -2,18 +2,20 @@ class App.Channel extends App.Model
@configure 'Channel', 'adapter', 'area', 'options', 'group_id', 'active', 'updated_at'
@extend Spine.Model.Ajax
@url: @apiPath + '/channels'
@configure_delete = true
@configure_attributes = [
{ name: 'adapter', display: 'Type', tag: 'select', multiple: false, null: false, options: { IMAP: 'IMAP', POP3: 'POP3' } },
{ name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false },
{ name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false },
{ name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false },
{ name: 'options::ssl', display: 'SSL', tag: 'select', multiple: false, null: true, options: { true: 'yes', false: 'no' }, translate: true, default: true},
{ name: 'options::folder', display: 'Folder', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false },
{ name: 'group_id', display: 'Group', tag: 'select', multiple: false, null: false, nulloption: true, relation: 'Group' },
{ name: 'active', display: 'Active', tag: 'select', multiple: false, null: false, options: { true: 'yes', false: 'no' }, translate: true, default: true },
]
@configure_overview = [
'adapter', 'options::host', 'options::user', 'group'
]
displayName: ->
name = ''
if @options
if @options.inbound
name += "#{@options.inbound.options.user}@#{@options.inbound.options.host} (#{@options.inbound.adapter})"
if @options.outbound
if @options.outbound
if name != ''
name += ' / '
if @options.outbound.options
name += "#{@options.outbound.options.host} (#{@options.outbound.adapter})"
else
name += " (#{@options.outbound.adapter})"
if name == ''
name = '???'
name

View file

@ -1,11 +1,20 @@
class App.EmailAddress extends App.Model
@configure 'EmailAddress', 'realname', 'email', 'note', 'active', 'updated_at'
@configure 'EmailAddress', 'realname', 'email', 'channel_id', 'note', 'active', 'updated_at'
@extend Spine.Model.Ajax
@url: @apiPath + '/email_addresses'
@filterChannel: (options, type) =>
return options if type isnt 'collection'
_.filter(
options
(channel) ->
return channel if channel && channel.area is 'Email::Account'
)
@configure_attributes = [
{ name: 'realname', display: 'Realname', tag: 'input', type: 'text', limit: 250, null: false },
{ name: 'email', display: 'Email', tag: 'input', type: 'text', limit: 250, null: false },
{ name: 'email', display: 'Email', tag: 'input', type: 'email', limit: 250, null: false },
{ name: 'channel_id', display: 'Channel', tag: 'select', multiple: false, null: false, relation: 'Channel', nulloption: true, filter: @filterChannel },
{ name: 'note', display: 'Note', tag: 'textarea', note: 'Notes are visible to agents only, never to customers.', limit: 250, null: true },
{ name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 },
{ name: 'active', display: 'Active', tag: 'active', default: true },

View file

@ -0,0 +1,99 @@
<h1><%- @T('Email Accounts') %></h1>
<% if !_.isEmpty(@email_addresses_not_used): %>
<h2><%- @T('Not linked email addresses') %></h2>
<ul>
<% for email_address in @email_addresses_not_used: %>
<li data-id="<%= email_address.id %>"><a href="" data-type="email-address-edit"><%= email_address.email %></a>
<% end %>
</ul>
<% end %>
<table class="table table-hover user-list">
<thead>
<tr>
<th><%- @T('Inbound') %></th>
<th><%- @T('Outbound') %></th>
<th><%- @T('Email Adresses') %></th>
<th><%- @T('Action') %></th>
</tr>
</thead>
<tbody>
<% for channel in @channels: %>
<tr data-id="<%- channel.id %>">
<td class="<% if channel.status_in is 'ok': %>success<% else if channel.status_in is 'error': %>danger<% else: %>warning<% end %>">
<%- @T('State') %>: <%- @T(channel.status_in || 'unknown') %><br>
<%= channel.options.inbound.options.user %><br>
<a href="#" data-type="edit-inbound"><%= channel.options.inbound.options.host %> (<%= channel.options.inbound.adapter %>)</a>
</td>
<td class="<% if channel.status_out is 'ok': %>success<% else if channel.status_out is 'error': %>danger<% else: %>warning<% end %>">
<%- @T('State') %>: <%- @T(channel.status_out || 'unknown') %><br>
<% if channel.options.outbound && channel.options.outbound.options: %>
<%= channel.options.outbound.options.user %><br>
<a href="#" data-type="edit-outbound"><%= channel.options.outbound.options.host %>
<% end %>
(<%= channel.options.outbound.adapter %>)</a>
</td>
<td>
<ul>
<% if !_.isEmpty(channel.email_addresses): %>
<% for email_address in channel.email_addresses: %>
<li data-id="<%= email_address.id %>"><a href="" data-type="email-address-edit"><%= email_address.email %></a>
<% end %>
<% else: %>
<li><%- @T('none') %></li>
<% end %>
</ul>
<a href="#" data-type="email-address-new" title="<%- @Ti('New Email Address') %>"><svg class="icon-trash"><use xlink:href="#icon-plus"></use></svg></a>
</td>
<td>
<a href="#" data-type="delete" title="<%- @Ti('Delete') %>"><svg class="icon-trash"><use xlink:href="#icon-trash"></use></svg></a>
</td>
</tr>
<% if channel.status_in is 'error': %>
<tr>
<td colspan="4"><%= channel.last_log_in %></td>
</tr>
<% end %>
<% if channel.status_out is 'error': %>
<tr>
<td colspan="4"><%= channel.last_log_out %></td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
<a data-type="new" class="btn btn--success"><%- @T('New') %></a>
<h1><%- @T('Notification Account') %></h1>
<table class="table table-hover user-list">
<thead>
<tr>
<th><%- @T('Outbound') %></th>
</tr>
</thead>
<tbody>
<tr data-id="<%- @channel.id %>">
<td class="<% if @channel.status_out is 'ok': %>success<% else if @channel.status_out is 'error': %>danger<% else: %>warning<% end %>">
<%- @T('State') %>: <%- @T(@channel.status_out || 'unknown') %><br>
<% if @channel.options.outbound && @channel.options.outbound.options: %>
<%= @channel.options.outbound.options.user %><br>
<a href="#" data-type="edit-notification-outbound"><%= @channel.options.outbound.options.host %>
<% end %>
(<%= @channel.options.outbound.adapter %>)</a>
</td>
</tr>
<% if @channel.status_in is 'error': %>
<tr>
<td colspan="1"><%= @channel.last_log_in %></td>
</tr>
<% end %>
<% if @channel.status_out is 'error': %>
<tr>
<td colspan="1"><%= @channel.last_log_out %></td>
</tr>
<% end %>
</tbody>
</table>

View file

@ -0,0 +1,92 @@
<div class="modal-dialog">
<form class="setup wizard js-intro">
<div class="wizard-slide">
<h2><%- @T('Email Account') %></h2>
<div class="wizard-body vertical justified">
<div class="alert alert--danger hide" role="alert"></div>
<fieldset>
<div class="form-group">
<label><%- @T('Full Name') %></label>
<input type="text" class="form-control" value="" name="realname" placeholder="<%- @Ti('Organization Support') %>" required>
</div>
<div class="form-group">
<label><%- @T('Email') %></label>
<input type="email" class="form-control" value="" name="email" placeholder="<%- @Ti('support@example.com') %>" required>
</div>
<div class="form-group">
<label><%- @T('Password') %></label>
<input type="password" class="form-control" name="password" value="" required>
</div>
</fieldset>
</div>
<div class="wizard-controls center">
<button class="btn btn--primary align-right"><%- @T('Connect') %></button>
</div>
</div>
</form>
<form class="setup wizard hide js-probe">
<div class="wizard-slide">
<h2><%- @T('Email Account') %></h2>
<div class="wizard-body vertical justified">
<p class="wizard-loadingText">
<span class="loading icon"></span> <%- @T('Testing') %> <span class="js-email"></span>
</p>
</div>
</div>
</form>
<form class="setup wizard hide js-test">
<div class="wizard-slide">
<h2><%- @T('Email Account') %></h2>
<div class="wizard-body vertical justified">
<p class="wizard-loadingText">
<span class="loading icon"></span> <%- @T('Verifying...') %>
</p>
</div>
</div>
</form>
<form class="setup wizard hide js-verify">
<div class="wizard-slide">
<h2><%- @T('Email Account') %></h2>
<div class="wizard-body vertical justified">
<div class="alert alert--danger hide" role="alert"></div>
<p class="wizard-loadingText">
<span class="loading icon"></span> <%- @T('Verify sending and receiving') %>
</p>
</div>
</div>
</form>
<form class="setup wizard hide js-inbound">
<div class="wizard-slide">
<h2><%- @T('Email Inbound') %></h2>
<div class="wizard-body vertical justified">
<div class="alert alert--danger hide" role="alert"></div>
<div class="base-inbound-settings"></div>
</div>
<div class="wizard-controls center">
<a class="btn btn--text btn--secondary js-back" data-slide="js-intro"><%- @T('Go Back') %></a>
<button class="btn btn--primary align-right"><%- @T( 'Continue' ) %></button>
</div>
</div>
</form>
<form class="setup wizard hide js-outbound">
<div class="wizard-slide">
<h2><%- @T('Email Outbound') %></h2>
<div class="wizard-body vertical justified">
<div class="alert alert--danger hide" role="alert"></div>
<div class="base-outbound-type"></div>
<div class="base-outbound-settings"></div>
</div>
<div class="wizard-controls center">
<a class="btn btn--text btn--secondary js-back" data-slide="js-inbound"><%- @T('Go Back') %></a>
<button class="btn btn--primary align-right"><%- @T( 'Continue' ) %></button>
</div>
</div>
</form>
</div>

View file

@ -0,0 +1,39 @@
<div class="modal-dialog">
<form class="setup wizard hide js-probe">
<div class="wizard-slide">
<h2><%- @T('Email Account') %></h2>
<div class="wizard-body vertical justified">
<p class="wizard-loadingText">
<span class="loading icon"></span> <%- @T('Testing') %> <span class="js-email"></span>
</p>
</div>
</div>
</form>
<form class="setup wizard hide js-test">
<div class="wizard-slide">
<h2><%- @T('Email Account') %></h2>
<div class="wizard-body vertical justified">
<p class="wizard-loadingText">
<span class="loading icon"></span> <%- @T('Verifying...') %>
</p>
</div>
</div>
</form>
<form class="setup wizard js-outbound">
<div class="wizard-slide">
<h2><%- @T('Email Outbound') %></h2>
<div class="wizard-body vertical justified">
<div class="alert alert--danger hide" role="alert"></div>
<div class="base-outbound-type"></div>
<div class="base-outbound-settings"></div>
</div>
<div class="wizard-controls center">
<button class="btn btn--primary align-right"><%- @T( 'Continue' ) %></button>
</div>
</div>
</form>
</div>

View file

@ -1,5 +0,0 @@
<form id="mail_adapter">
<div id="form-email-adapter"></div>
<div id="form-email-adapter-settings"></div>
<button data-type="" type="submit" class="btn"><%- @T( 'Submit' ) %></botton>
</form>

View file

@ -17,7 +17,7 @@
</div>
</div>
<div class="wizard-controls center">
<a class="btn btn--text btn--secondary" href="#getting_started/base"><%- @T('Go Back') %></a>
<a class="btn btn--text btn--secondary" href="#getting_started/email_notification"><%- @T('Go Back') %></a>
<a class="btn align-right" href="#getting_started/finish"><%- @T( 'Skip' ) %></a>
</div>
</div>

View file

@ -64,7 +64,7 @@
<form class="setup wizard hide js-inbound">
<div class="wizard-slide">
<h2><%- @T('E-Mail Inbound') %></h2>
<h2><%- @T('Email Inbound') %></h2>
<div class="wizard-body vertical justified">
<div class="alert alert--danger hide" role="alert"></div>
<div class="base-inbound-settings"></div>
@ -78,7 +78,7 @@
<form class="setup wizard hide js-outbound">
<div class="wizard-slide">
<h2><%- @T('E-Mail Outbound') %></h2>
<h2><%- @T('Email Outbound') %></h2>
<div class="wizard-body vertical justified">
<div class="alert alert--danger hide" role="alert"></div>
<div class="base-outbound-type"></div>

View file

@ -0,0 +1,30 @@
<div class="main flex vertical centered darkBackground">
<svg class="wizard-logo icon-full-logo"><use xlink:href="#icon-full-logo" /></svg>
<form class="setup wizard js-outbound">
<div class="wizard-slide">
<h2><%- @T('Email Notification') %></h2>
<div class="wizard-body vertical justified">
<div class="alert alert--danger hide" role="alert"></div>
<div class="base-outbound-type"></div>
<div class="base-outbound-settings"></div>
</div>
<div class="wizard-controls center">
<a class="btn btn--text btn--secondary" href="#getting_started/base"><%- @T('Go Back') %></a>
<button class="btn btn--primary align-right"><%- @T( 'Continue' ) %></button>
</div>
</div>
</form>
<form class="setup wizard hide js-test">
<div class="wizard-slide">
<h2><%- @T('Email Notification') %></h2>
<div class="wizard-body vertical justified">
<p class="wizard-loadingText">
<span class="loading icon"></span> <%- @T('Verifying...') %>
</p>
</div>
</div>
</form>
</div>

View file

@ -11,14 +11,25 @@ JSON
Example:
{
"id":1,
"area":"Email::Inbound",
"adapter":"IMAP",
"area":"Email::Account",
"group_id:": 1,
"options":{
"host":"mail.example.com",
"user":"some_user",
"password":"some_password",
"ssl":true
"inbound": {
"adapter":"IMAP",
"options": {
"host":"mail.example.com",
"user":"some_user",
"password":"some_password",
"ssl":true
},
"outbound":{
"adapter":"SMTP",
"options": {
"host":"mail.example.com",
"user":"some_user",
"password":"some_password",
"start_tls":true
}
},
"active":true,
"updated_at":"2012-09-14T17:51:53Z",
@ -29,10 +40,10 @@ Example:
{
"id":1,
"area":"Twitter::Inbound",
"adapter":"Twitter",
"area":"Twitter::Account",
"group_id:": 1,
"options":{
"adapter":"Twitter",
"auth": {
"consumer_key":"PJ4c3dYYRtSZZZdOKo8ow",
"consumer_secret":"ggAdnJE2Al1Vv0cwwvX5bdvKOieFs0vjCIh5M8Dxk",
@ -84,14 +95,12 @@ Response:
[
{
"id": 1,
"area":"Email::Inbound",
"adapter":"IMAP",
"area":"Email::Account",
...
},
{
"id": 2,
"area":"Email::Inbound",
"adapter":"IMAP",
"area":"Email::Account",
...
}
]
@ -114,8 +123,7 @@ GET /api/v1/channels/#{id}.json
Response:
{
"id": 1,
"area":"Email::Inbound",
"adapter":"IMAP",
"area":"Email::Account",
...
}
@ -136,22 +144,33 @@ POST /api/v1/channels.json
Payload:
{
"area":"Email::Inbound",
"adapter":"IMAP",
"area":"Email::Account",
"group_id:": 1,
"options":{
"host":"mail.example.com",
"user":"some_user",
"password":"some_password",
"ssl":true
"inbound":
"adapter":"IMAP",
"options":{
"host":"mail.example.com",
"user":"some_user",
"password":"some_password",
"ssl":true
},
},
"outbound":{
"adapter":"SMTP",
"options": {
"host":"mail.example.com",
"user":"some_user",
"password":"some_password",
"start_tls":true
}
},
"active":true,
}
Response:
{
"area":"Email::Inbound",
"adapter":"IMAP",
"area":"Email::Account",
...
}
@ -173,14 +192,26 @@ PUT /api/v1/channels/{id}.json
Payload:
{
"id":1,
"area":"Email::Inbound",
"adapter":"IMAP",
"area":"Email::Account",
"group_id:": 1,
"options":{
"host":"mail.example.com",
"user":"some_user",
"password":"some_password",
"ssl":true
"inbound":
"adapter":"IMAP",
"options":{
"host":"mail.example.com",
"user":"some_user",
"password":"some_password",
"ssl":true
},
},
"outbound":{
"adapter":"SMTP",
"options": {
"host":"mail.example.com",
"user":"some_user",
"password":"some_password",
"start_tls":true
}
},
"active":true,
}
@ -219,4 +250,207 @@ curl http://localhost/api/v1/channels.json -v -u #{login}:#{password} -H "Conten
return if deny_if_not_role(Z_ROLENAME_ADMIN)
model_destory_render(Channel, params)
end
def email_index
return if deny_if_not_role(Z_ROLENAME_ADMIN)
assets = {}
Channel.all.each {|channel|
assets = channel.assets(assets)
}
EmailAddress.all.each {|email_address|
assets = email_address.assets(assets)
}
render json: {
assets: assets
}
end
def email_probe
# check admin permissions
return if deny_if_not_role(Z_ROLENAME_ADMIN)
# probe settings based on email and password
result = EmailHelper::Probe.full(
email: params[:email],
password: params[:password],
)
# verify if user+host already exists
if result[:result] == 'ok'
return if email_account_duplicate?(result)
end
render json: result
end
def email_outbound
# check admin permissions
return if deny_if_not_role(Z_ROLENAME_ADMIN)
# connection test
render json: EmailHelper::Probe.outbound(params, params[:email])
end
def email_inbound
# check admin permissions
return if deny_if_not_role(Z_ROLENAME_ADMIN)
# connection test
result = EmailHelper::Probe.inbound(params)
# check account duplicate
return if email_account_duplicate?({ setting: { inbound: params } }, params[:channel_id])
render json: result
end
def email_verify
# check admin permissions
return if deny_if_not_role(Z_ROLENAME_ADMIN)
email = params[:email] || params[:meta][:email]
email = email.downcase
channel_id = params[:channel_id]
# check account duplicate
return if email_account_duplicate?({ setting: { inbound: params[:inbound] } }, channel_id)
# check delivery for 30 sek.
result = EmailHelper::Verify.email(
outbound: params[:outbound],
inbound: params[:inbound],
sender: email,
subject: params[:subject],
)
if result[:result] != 'ok'
render json: result
return
end
# update account
if channel_id
channel = Channel.find(channel_id)
channel.update_attributes(
options: {
inbound: params[:inbound],
outbound: params[:outbound],
},
last_log_in: '',
last_log_out: '',
status_in: nil,
status_out: nil,
)
render json: {
result: 'ok',
}
return
end
# create new account
channel = Channel.create(
area: 'Email::Account',
options: {
inbound: params[:inbound],
outbound: params[:outbound],
},
active: true,
group_id: Group.first.id,
)
# remember address && set channel for email address
address = EmailAddress.find_by(email: email)
# if we are on initial setup, use already exisiting dummy email address
if Channel.count == 1
address = EmailAddress.first
end
if address
address.update_attributes(
realname: params[:meta][:realname],
email: email,
active: true,
channel_id: channel.id,
)
else
address = EmailAddress.create(
realname: params[:meta][:realname],
email: email,
active: true,
channel_id: channel.id,
)
end
render json: {
result: 'ok',
}
end
def email_notification
# check admin permissions
return if deny_if_not_role(Z_ROLENAME_ADMIN)
adapter = params[:adapter].downcase
email = Setting.get('notification_sender')
# connection test
result = EmailHelper::Probe.outbound(params, email)
# save settings
if result[:result] == 'ok'
# validate adapter
if adapter !~ /^(smtp|sendmail)$/
render json: {
result: 'failed',
message: "Unknown adapter '#{adapter}'",
}
return
end
Channel.where(area: 'Email::Notification').each {|channel|
active = false
if adapter =~ /^#{channel.options[:outbound][:adapter]}$/i
active = true
channel.options = {
outbound: {
adapter: adapter,
options: params[:options],
},
}
end
channel.active = active
channel.save
}
end
render json: result
end
private
def email_account_duplicate?(result, channel_id = nil)
Channel.where(area: 'Email::Account').each {|channel|
next if !channel.options
next if !channel.options[:inbound]
next if !channel.options[:inbound][:adapter]
next if channel.options[:inbound][:adapter] != result[:setting][:inbound][:adapter]
next if channel.options[:inbound][:options][:host] != result[:setting][:inbound][:options][:host]
next if channel.options[:inbound][:options][:user] != result[:setting][:inbound][:options][:user]
next if channel.id.to_s == channel_id.to_s
render json: {
result: 'duplicate',
message: 'Account already exists!',
}
return true
}
false
end
end

View file

@ -188,119 +188,6 @@ curl http://localhost/api/v1/getting_started -v -u #{login}:#{password}
}
end
def email_probe
# check admin permissions
return if deny_if_not_role(Z_ROLENAME_ADMIN)
# probe settings based on email and password
render json: EmailHelper::Probe.full(
email: params[:email],
password: params[:password],
)
end
def email_outbound
# check admin permissions
return if deny_if_not_role(Z_ROLENAME_ADMIN)
# connection test
render json: EmailHelper::Probe.outbound(params, params[:email])
end
def email_inbound
# check admin permissions
return if deny_if_not_role(Z_ROLENAME_ADMIN)
# connection test
render json: EmailHelper::Probe.inbound(params)
end
def email_verify
# check admin permissions
return if deny_if_not_role(Z_ROLENAME_ADMIN)
# send verify email to inbox
if !params[:subject]
subject = '#' + rand(99_999_999_999).to_s
else
subject = params[:subject]
end
result = EmailHelper::Verify.email(
outbound: params[:outbound],
inbound: params[:inbound],
sender: params[:meta][:email],
subject: subject,
)
# check delivery for 30 sek.
if result[:result] != 'ok'
render json: result
return
end
# remember address
address = EmailAddress.where( email: params[:meta][:email] ).first
if !address
address = EmailAddress.first
end
if address
address.update_attributes(
realname: params[:meta][:realname],
email: params[:meta][:email],
active: 1,
updated_by_id: 1,
created_by_id: 1,
)
else
EmailAddress.create(
realname: params[:meta][:realname],
email: params[:meta][:email],
active: 1,
updated_by_id: 1,
created_by_id: 1,
)
end
# store mailbox
Channel.create(
area: 'Email::Inbound',
adapter: params[:inbound][:adapter],
options: params[:inbound][:options],
group_id: 1,
active: 1,
updated_by_id: 1,
created_by_id: 1,
)
# save settings
if params[:outbound][:adapter] =~ /^smtp$/i
smtp = Channel.where( adapter: 'SMTP', area: 'Email::Outbound' ).first
smtp.options = params[:outbound][:options]
smtp.active = true
smtp.save!
sendmail = Channel.where( adapter: 'Sendmail' ).first
sendmail.active = false
sendmail.save!
else
sendmail = Channel.where( adapter: 'Sendmail', area: 'Email::Outbound' ).first
sendmail.options = {}
sendmail.active = true
sendmail.save!
smtp = Channel.where( adapter: 'SMTP' ).first
smtp.active = false
smtp.save
end
render json: {
result: 'ok',
}
end
private
def auto_wizard_enabled_response

View file

@ -3,24 +3,107 @@
class Channel < ApplicationModel
store :options
def self.fetch
channels = Channel.where( 'active = ? AND area LIKE ?', true, '%::Inbound' )
channels.each { |channel|
begin
# we need to require each channel backend individually otherwise we get a
# 'warning: toplevel constant Twitter referenced by Channel::Twitter' error e.g.
# so we have to convert the channel name to the filename via Rails String.underscore
# http://stem.ps/rails/2015/01/25/ruby-gotcha-toplevel-constant-referenced-by.html
require "channel/#{channel[:adapter].to_filename}"
=begin
channel_object = Object.const_get("Channel::#{channel[:adapter].to_classname}")
channel_instance = channel_object.new
channel_instance.fetch(channel)
rescue => e
logger.error "Can't use Channel::#{channel[:adapter].to_classname}"
logger.error e.inspect
logger.error e.backtrace
end
}
fetch all accounts
Channel.fetch
=end
def self.fetch
channels = Channel.where('active = ? AND area LIKE ?', true, '%::Account')
channels.each(&:fetch)
end
=begin
fetch one account
channel = Channel.where(area: 'Email::Account').first
channel.fetch
=end
def fetch
adapter = options[:adapter]
adapter_options = options
if options[:options]
adapter_options = options[:options]
elsif options[:inbound] && options[:inbound][:adapter]
adapter = options[:inbound][:adapter]
adapter_options = options[:inbound][:options]
end
begin
# we need to require each channel backend individually otherwise we get a
# 'warning: toplevel constant Twitter referenced by Channel::Driver::Twitter' error e.g.
# so we have to convert the channel name to the filename via Rails String.underscore
# http://stem.ps/rails/2015/01/25/ruby-gotcha-toplevel-constant-referenced-by.html
require "channel/driver/#{adapter.to_filename}"
driver_class = Object.const_get("Channel::Driver::#{adapter.to_classname}")
driver_instance = driver_class.new
driver_instance.fetch(adapter_options, self)
self.status_in = 'ok'
self.last_log_in = ''
save
rescue => e
error = "Can't use Channel::Driver::#{adapter.to_classname}: #{e.inspect}"
logger.error error
logger.error e.backtrace
self.status_in = 'error'
self.last_log_in = error
save
end
end
=begin
send via account
channel = Channel.where(area: 'Email::Account').first
channel.deliver(mail_params, notification)
=end
def deliver(mail_params, notification = false)
adapter = options[:adapter]
adapter_options = options
if options[:options]
adapter_options = options[:options]
elsif options[:outbound] && options[:outbound][:adapter]
adapter = options[:outbound][:adapter]
adapter_options = options[:outbound][:options]
end
begin
# we need to require each channel backend individually otherwise we get a
# 'warning: toplevel constant Twitter referenced by Channel::Driver::Twitter' error e.g.
# so we have to convert the channel name to the filename via Rails String.underscore
# http://stem.ps/rails/2015/01/25/ruby-gotcha-toplevel-constant-referenced-by.html
require "channel/driver/#{adapter.to_filename}"
driver_class = Object.const_get("Channel::Driver::#{adapter.to_classname}")
driver_instance = driver_class.new
driver_instance.send(adapter_options, mail_params, notification)
self.status_out = 'ok'
self.last_log_out = ''
save
rescue => e
error = "Can't use Channel::Driver::#{adapter.to_classname}: #{e.inspect}"
logger.error error
logger.error e.backtrace
self.status_out = 'error'
self.last_log_out = error
save
end
end
end

View file

@ -0,0 +1,4 @@
class Channel
class Driver
end
end

View file

@ -2,9 +2,9 @@
require 'facebook'
class Channel::Facebook
class Channel::Driver::Facebook
def fetch (channel)
def fetch (adapter_options, channel)
@channel = channel
@facebook = Facebook.new( @channel[:options] )

View file

@ -2,17 +2,17 @@
require 'net/imap'
class Channel::Imap < Channel::EmailParser
class Channel::Driver::Imap < Channel::EmailParser
def fetch (channel, check_type = '', verify_string = '')
def fetch (options, channel, check_type = '', verify_string = '')
ssl = true
port = 993
if channel[:options].key?(:ssl) && channel[:options][:ssl].to_s == 'false'
if options.key?(:ssl) && options[:ssl].to_s == 'false'
ssl = false
port = 143
end
Rails.logger.info "fetching imap (#{channel[:options][:host]}/#{channel[:options][:user]} port=#{port},ssl=#{ssl})"
Rails.logger.info "fetching imap (#{options[:host]}/#{options[:user]} port=#{port},ssl=#{ssl})"
# on check, reduce open_timeout to have faster probing
timeout = 12
@ -22,24 +22,24 @@ class Channel::Imap < Channel::EmailParser
Timeout.timeout(timeout) do
@imap = Net::IMAP.new( channel[:options][:host], port, ssl, nil, false )
@imap = Net::IMAP.new( options[:host], port, ssl, nil, false )
end
# try LOGIN, if not - try plain
begin
@imap.authenticate( 'LOGIN', channel[:options][:user], channel[:options][:password] )
@imap.authenticate( 'LOGIN', options[:user], options[:password] )
rescue => e
if e.to_s !~ /(unsupported\s(authenticate|authentication)\smechanism|not\ssupported)/i
raise e
end
@imap.login( channel[:options][:user], channel[:options][:password] )
@imap.login( options[:user], options[:password] )
end
if !channel[:options][:folder] || channel[:options][:folder].empty?
if !options[:folder] || options[:folder].empty?
@imap.select('INBOX')
else
@imap.select( channel[:options][:folder] )
@imap.select( options[:folder] )
end
if check_type == 'check'
Rails.logger.info 'check only mode, fetch no emails'

View file

@ -1,6 +1,6 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
class Channel::MailStdin < Channel::EmailParser
class Channel::Driver::MailStdin < Channel::EmailParser
def initialize
Rails.logger.info 'read main from STDIN'

View file

@ -2,19 +2,19 @@
require 'net/pop'
class Channel::Pop3 < Channel::EmailParser
class Channel::Driver::Pop3 < Channel::EmailParser
def fetch (channel, check_type = '', verify_string = '')
def fetch (options, channel, check_type = '', verify_string = '')
ssl = true
port = 995
if channel[:options].key?(:ssl) && channel[:options][:ssl].to_s == 'false'
if options.key?(:ssl) && options[:ssl].to_s == 'false'
ssl = false
port = 110
end
Rails.logger.info "fetching pop3 (#{channel[:options][:host]}/#{channel[:options][:user]} port=#{port},ssl=#{ssl})"
Rails.logger.info "fetching pop3 (#{options[:host]}/#{options[:user]} port=#{port},ssl=#{ssl})"
@pop = Net::POP3.new( channel[:options][:host], port )
@pop = Net::POP3.new( options[:host], port )
# on check, reduce open_timeout to have faster probing
if check_type == 'check'
@ -25,7 +25,7 @@ class Channel::Pop3 < Channel::EmailParser
if ssl
@pop.enable_ssl(OpenSSL::SSL::VERIFY_NONE)
end
@pop.start( channel[:options][:user], channel[:options][:password] )
@pop.start( options[:user], options[:password] )
if check_type == 'check'
Rails.logger.info 'check only mode, fetch no emails'
disconnect

View file

@ -1,7 +1,7 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
class Channel::Sendmail
def send(attr, _channel, notification = false)
class Channel::Driver::Sendmail
def send(_options, attr, notification = false)
# return if we run import mode
return if Setting.get('import_mode')

View file

@ -1,7 +1,7 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
class Channel::SMTP
def send(attr, channel, notification = false)
class Channel::Driver::Smtp
def send(options, attr, notification = false)
# return if we run import mode
return if Setting.get('import_mode')
@ -9,11 +9,11 @@ class Channel::SMTP
mail = Channel::EmailBuild.build(attr, notification)
mail.delivery_method :smtp, {
openssl_verify_mode: 'none',
address: channel[:options][:host],
port: channel[:options][:port] || 25,
domain: channel[:options][:host],
user_name: channel[:options][:user],
password: channel[:options][:password],
address: options[:host],
port: options[:port] || 25,
domain: options[:host],
user_name: options[:user],
password: options[:password],
enable_starttls_auto: true,
}
mail.deliver

View file

@ -1,8 +1,8 @@
# Copyright (C) 2012-2015 Zammad Foundation, http://zammad-foundation.org/
class Channel::Twitter
class Channel::Driver::Twitter
def fetch (channel)
def fetch (adapter_options, channel)
@channel = channel
@tweet = Tweet.new( @channel[:options][:auth] )
@ -21,7 +21,7 @@ class Channel::Twitter
def send(article, _notification = false)
@channel = Channel.find_by( area: 'Twitter::Inbound', active: true )
@channel = Channel.find_by( area: 'Twitter::Account', active: true )
@tweet = Tweet.new( @channel[:options][:auth] )
tweet = @tweet.from_article(article)

View file

@ -1,28 +0,0 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
require 'net/imap'
module Channel::EmailSend
def self.send(article, notification = false)
channel = Channel.find_by( area: 'Email::Outbound', active: true )
begin
# we need to require the channel backend individually otherwise we get a
# 'warning: toplevel constant Twitter referenced by Channel::Twitter' error e.g.
# so we have to convert the channel name to the filename via Rails String.underscore
# http://stem.ps/rails/2015/01/25/ruby-gotcha-toplevel-constant-referenced-by.html
require "channel/#{channel[:adapter].underscore}"
channel_object = Object.const_get("Channel::#{channel[:adapter]}")
channel_instance = channel_object.new
result = channel_instance.send(article, channel, notification)
channel_instance.disconnect
rescue => e
Rails.logger.error "Can't use Channel::#{channel[:adapter]}"
Rails.logger.error e.inspect
Rails.logger.error e.backtrace
end
result
end
end

View file

@ -2,6 +2,7 @@
class EmailAddress < ApplicationModel
has_many :groups, after_add: :cache_update, after_remove: :cache_update
belongs_to :channel
validates :realname, presence: true
validates :email, presence: true

View file

@ -11,7 +11,15 @@ class Observer::Ticket::Article::CommunicateEmail::BackgroundJob
subject = ticket.subject_build( record.subject )
# send email
message = Channel::EmailSend.send(
if !ticket.group.email_address_id
fail "Can't send email, no email address definde for group id '#{ticket.group.id}'"
elsif !ticket.group.email_address.channel_id
fail "Can't send email, no channel definde for email_address id '#{ticket.group.email_address_id}'"
end
channel = ticket.group.email_address.channel
# get linked channel and send
message = channel.deliver(
{
message_id: record.message_id,
in_reply_to: record.in_reply_to,

View file

@ -1,6 +1,6 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
require 'channel/facebook'
require 'channel/driver/facebook'
class Observer::Ticket::Article::CommunicateFacebook < ActiveRecord::Observer
observe 'ticket::_article'
@ -19,7 +19,7 @@ class Observer::Ticket::Article::CommunicateFacebook < ActiveRecord::Observer
type = Ticket::Article::Type.lookup( id: record.type_id )
return if type['name'] !~ /\Afacebook/
facebook = Channel::Facebook.new
facebook = Channel::Driver::Facebook.new
post = facebook.send({
type: type['name'],
to: record.to,

View file

@ -1,7 +1,7 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
# http://stem.ps/rails/2015/01/25/ruby-gotcha-toplevel-constant-referenced-by.html
require 'channel/twitter'
require 'channel/driver/twitter'
class Observer::Ticket::Article::CommunicateTwitter < ActiveRecord::Observer
observe 'ticket::_article'
@ -20,7 +20,7 @@ class Observer::Ticket::Article::CommunicateTwitter < ActiveRecord::Observer
type = Ticket::Article::Type.lookup( id: record.type_id )
return if type['name'] !~ /\Atwitter/
twitter = Channel::Twitter.new
twitter = Channel::Driver::Twitter.new
tweet = twitter.send({
type: type['name'],
to: record.to,

View file

@ -1,6 +1,14 @@
Zammad::Application.routes.draw do
api_path = Rails.configuration.api_path
# email helper
match api_path + '/channels/email_index', to: 'channels#email_index', via: :get
match api_path + '/channels/email_probe', to: 'channels#email_probe', via: :post
match api_path + '/channels/email_outbound', to: 'channels#email_outbound', via: :post
match api_path + '/channels/email_inbound', to: 'channels#email_inbound', 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
# channels
match api_path + '/channels', to: 'channels#index', via: :get
match api_path + '/channels/:id', to: 'channels#show', via: :get

View file

@ -2,13 +2,9 @@ Zammad::Application.routes.draw do
api_path = Rails.configuration.api_path
# getting_started
match api_path + '/getting_started', to: 'getting_started#index', via: :get
match api_path + '/getting_started/auto_wizard/:token', to: 'getting_started#auto_wizard_admin', via: :get
match api_path + '/getting_started/auto_wizard', to: 'getting_started#auto_wizard_admin', via: :get
match api_path + '/getting_started/base', to: 'getting_started#base', via: :post
match api_path + '/getting_started/email_probe', to: 'getting_started#email_probe', via: :post
match api_path + '/getting_started/email_outbound', to: 'getting_started#email_outbound', via: :post
match api_path + '/getting_started/email_inbound', to: 'getting_started#email_inbound', via: :post
match api_path + '/getting_started/email_verify', to: 'getting_started#email_verify', via: :post
match api_path + '/getting_started', to: 'getting_started#index', via: :get
match api_path + '/getting_started/auto_wizard/:token', to: 'getting_started#auto_wizard_admin', via: :get
match api_path + '/getting_started/auto_wizard', to: 'getting_started#auto_wizard_admin', via: :get
match api_path + '/getting_started/base', to: 'getting_started#base', via: :post
end

View file

@ -0,0 +1,57 @@
class UpdateChannel < ActiveRecord::Migration
def up
add_column :email_addresses, :channel_id, :integer, null: true
EmailAddress.reset_column_information
channel = Channel.find_by(area: 'Email::Inbound')
EmailAddress.all.each {|email_address|
email_address.channel_id = channel.id
email_address.save
}
add_column :channels, :last_log_in, :text, limit: 500.kilobytes + 1, null: true
add_column :channels, :last_log_out, :text, limit: 500.kilobytes + 1, null: true
add_column :channels, :status_in, :string, limit: 100, null: true
add_column :channels, :status_out, :string, limit: 100, null: true
Channel.reset_column_information
channel_outbound = Channel.find_by(area: 'Email::Outbound', active: true)
Channel.all.each {|channel|
if channel.area == 'Email::Inbound'
channel.area = 'Email::Account'
options = {
inbound: {
adapter: channel.adapter.downcase,
options: channel.options,
},
outbound: {
adapter: channel_outbound.adapter.downcase,
options: channel_outbound.options,
},
}
channel.options = options
channel.save
elsif channel.area == 'Email::Outbound'
channel.area = 'Email::Notification'
options = {
outbound: {
adapter: channel.adapter,
options: channel.options,
},
}
channel.options = options
channel.save
elsif channel.area == 'Twitter::Inbound'
channel.area = 'Twitter::Account'
channel.options[:adapter] = channel.adapter
channel.save
end
}
remove_column :channels, :adapter
Channel.reset_column_information
end
end

View file

@ -1743,21 +1743,28 @@ Overview.create_if_not_exists(
)
Channel.create_if_not_exists(
adapter: 'SMTP',
area: 'Email::Outbound',
area: 'Email::Notification',
options: {
host: 'host.example.com',
user: '',
password: '',
ssl: true,
outbound: {
adapter: 'smtp',
options: {
host: 'host.example.com',
user: '',
password: '',
ssl: true,
},
},
},
group_id: 1,
active: false,
)
Channel.create_if_not_exists(
adapter: 'Sendmail',
area: 'Email::Outbound',
options: {},
area: 'Email::Notification',
options: {
outbound: {
adapter: 'sendmail',
},
},
active: true,
)

View file

@ -14,26 +14,28 @@ returns on success
{
result: 'ok',
inbound: {
adapter: 'imap',
options: {
host: 'imap.gmail.com',
port: 993,
ssl: true,
user: 'some@example.com',
password: 'password',
settings: {
inbound: {
adapter: 'imap',
options: {
host: 'imap.gmail.com',
port: 993,
ssl: true,
user: 'some@example.com',
password: 'password',
},
},
},
outbound: {
adapter: 'smtp',
options: {
host: 'smtp.gmail.com',
port: 25,
ssl: true,
user: 'some@example.com',
password: 'password',
outbound: {
adapter: 'smtp',
options: {
host: 'smtp.gmail.com',
port: 25,
ssl: true,
user: 'some@example.com',
password: 'password',
},
},
},
}
}
returns on fail
@ -150,7 +152,7 @@ get result of inbound probe
result = EmailHelper::Probe.inbound(
adapter: 'imap',
options: {
settings: {
host: 'imap.gmail.com',
port: 993,
ssl: true,
@ -184,24 +186,22 @@ returns on fail
def self.inbound(params)
# validate params
if !params[:adapter]
result = {
result: 'invalid',
message: 'Invalid, need adapter!',
}
return result
end
adapter = params[:adapter].downcase
# connection test
begin
if params[:adapter] =~ /^imap$/i
Channel::Imap.new.fetch( { options: params[:options] }, 'check' )
elsif params[:adapter] =~ /^pop3$/i
Channel::Pop3.new.fetch( { options: params[:options] }, 'check' )
else
fail "Invalid adapter '#{params[:adapter]}'"
# validate adapter
if adapter !~ /^(imap|pop3)$/
fail "Unknown adapter '#{adapter}'"
end
require "channel/driver/#{adapter.to_filename}"
driver_class = Object.const_get("Channel::Driver::#{adapter.to_classname}")
driver_instance = driver_class.new
driver_instance.fetch(params[:options], nil, 'check')
rescue => e
message_human = ''
translations.each {|key, message|
@ -238,7 +238,8 @@ get result of outbound probe
password: 'password',
}
},
'sender@example.com',
'sender_and_recipient_of_test_email@example.com',
'subject of probe email',
)
returns on success
@ -266,15 +267,9 @@ returns on fail
def self.outbound(params, email, subject = nil)
# validate params
if !params[:adapter]
result = {
result: 'invalid',
message: 'Invalid, need adapter!',
}
return result
end
adapter = params[:adapter].downcase
# prepare test email
if subject
mail = {
:from => email,
@ -295,29 +290,29 @@ returns on fail
# test connection
begin
if params[:adapter] =~ /^smtp$/i
# validate adapter
if adapter !~ /^(smtp|sendmail)$/
fail "Unknown adapter '#{adapter}'"
end
# in case, fill missing params
# set smtp defaults
if adapter =~ /^smtp$/
if !params[:options].key?(:port)
params[:options][:port] = 25
end
if !params[:options].key?(:ssl)
params[:options][:ssl] = true
end
Channel::SMTP.new.send(
mail,
{
options: params[:options]
}
)
elsif params[:adapter] =~ /^sendmail$/i
Channel::Sendmail.new.send(
mail,
nil
)
else
fail "Invalid adapter '#{params[:adapter]}'"
end
require "channel/driver/#{adapter.to_filename}"
driver_class = Object.const_get("Channel::Driver::#{adapter.to_classname}")
driver_instance = driver_class.new
driver_instance.send(
params[:options],
mail,
)
rescue => e
# check if sending email was ok, but mailserver rejected

View file

@ -26,7 +26,7 @@ get result of inbound probe
password: 'password',
},
},
sender: 'sender@example.com',
sender: 'sender_and_recipient_of_verify_email@example.com',
)
returns on success
@ -56,12 +56,13 @@ or
def self.email(params)
# send verify email
if !params[:subject]
if !params[:subject] || params[:subject].empty?
subject = '#' + rand(99_999_999_999).to_s
else
subject = params[:subject]
end
result = EmailHelper::Probe.outbound( params[:outbound], params[:sender], subject )
puts "VERIFY #{subject.inspect}/#{params[:sender]}"
result = EmailHelper::Probe.outbound(params[:outbound], params[:sender], subject)
# looking for verify email
(1..5).each {
@ -72,9 +73,9 @@ or
begin
if params[:inbound][:adapter] =~ /^imap$/i
found = Channel::Imap.new.fetch( { options: params[:inbound][:options] }, 'verify', subject )
found = Channel::Driver::Imap.new.fetch(params[:inbound][:options], self, 'verify', subject)
else
found = Channel::Pop3.new.fetch( { options: params[:inbound][:options] }, 'verify', subject )
found = Channel::Driver::Pop3.new.fetch(params[:inbound][:options], self, 'verify', subject)
end
rescue => e
result = {

View file

@ -111,7 +111,9 @@ module NotificationFactory
content_type = data[:content_type]
end
Channel::EmailSend.send(
# get active Email::Outbound Channel and send
channel = Channel.find_by(area: 'Email::Notification', active: true)
channel.deliver(
{
# in_reply_to: in_reply_to,
from: sender,

View file

@ -370,6 +370,19 @@ test( "table test 2", function() {
created_at: '2014-06-10T11:17:34.000Z',
},
])
App.Channel.configure_delete = true
App.Channel.configure_attributes = [
{ name: 'adapter', display: 'Type', tag: 'select', multiple: false, null: false, options: { IMAP: 'IMAP', POP3: 'POP3' } },
{ name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false },
{ name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false },
{ name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false },
{ name: 'options::ssl', display: 'SSL', tag: 'select', multiple: false, null: true, options: { true: 'yes', false: 'no' }, translate: true, default: true},
{ name: 'options::folder', display: 'Folder', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false },
{ name: 'group_id', display: 'Group', tag: 'select', multiple: false, null: false, nulloption: true, relation: 'Group' },
{ name: 'active', display: 'Active', tag: 'select', multiple: false, null: false, options: { true: 'yes', false: 'no' }, translate: true, default: true },
]
App.Channel.refresh( [
{
id: 1,

View file

@ -44,13 +44,9 @@ class AaaGettingStartedTest < TestCase
value: 'test1234äöüß',
)
click( css: '.js-admin .btn--success' )
watch_for(
css: '.js-base h2',
value: 'Organization',
)
# getting started - base
match(
watch_for(
css: '.js-base h2',
value: 'Organization',
)
@ -76,19 +72,27 @@ class AaaGettingStartedTest < TestCase
click(
css: '.js-base .btn--primary',
)
# getting started - email notification
watch_for(
css: 'body',
value: 'channel',
css: '.js-outbound h2',
value: 'Email Notification',
)
location_check(
url: '#getting_started/channel',
url: '#getting_started/email_notification',
)
click(
css: '.js-outbound .btn--primary',
)
# getting started - create email account
match(
watch_for(
css: '.js-channel h2',
value: 'Connect Channels',
)
location_check(
url: '#getting_started/channel',
)
click(
css: '.js-channel .email .provider_name',
)
@ -117,14 +121,10 @@ class AaaGettingStartedTest < TestCase
value: 'invite',
timeout: 100,
)
location_check(
url: '#getting_started/agents',
)
# invite agent1
match(
css: 'body',
value: 'Invite',
location_check(
url: '#getting_started/agents',
)
set(
css: '.js-agent input[name="firstname"]',

View file

@ -20,6 +20,7 @@ class FacebookTest < ActiveSupport::TestCase
provider_page_name = 'Hansi Merkurs Hutfabrik'
provider_options = {
adapter: 'facebook',
auth: {
access_token: provider_key
},
@ -34,8 +35,7 @@ class FacebookTest < ActiveSupport::TestCase
current = Channel.where( adapter: 'Facebook' )
current.each(&:destroy)
Channel.create(
adapter: 'Facebook',
area: 'Facebook::Inbound',
area: 'Facebook::Account',
options: provider_options,
active: true,
created_by_id: 1,

View file

@ -28,12 +28,12 @@ class TwitterTest < ActiveSupport::TestCase
me_bauer_token_secret = 'T8ph5afeSDjGDA9X1ZBlzEvoSiXfN266ZZUMj5UaY'
# add channel
current = Channel.where( adapter: 'Twitter' )
current = Channel.where(area: 'Twitter::Account')
current.each(&:destroy)
Channel.create(
adapter: 'Twitter',
area: 'Twitter::Inbound',
area: 'Twitter::Account',
options: {
adapter: 'twitter',
auth: {
consumer_key: consumer_key,
consumer_secret: consumer_secret,