Merge branch 'develop' of git.znuny.com:zammad/zammad into develop
This commit is contained in:
commit
80676678fc
57 changed files with 1361 additions and 346 deletions
1
Gemfile
1
Gemfile
|
@ -28,6 +28,7 @@ end
|
||||||
|
|
||||||
gem 'autoprefixer-rails'
|
gem 'autoprefixer-rails'
|
||||||
|
|
||||||
|
gem 'doorkeeper'
|
||||||
gem 'oauth2'
|
gem 'oauth2'
|
||||||
|
|
||||||
gem 'omniauth'
|
gem 'omniauth'
|
||||||
|
|
|
@ -82,6 +82,8 @@ GEM
|
||||||
docile (1.1.5)
|
docile (1.1.5)
|
||||||
domain_name (0.5.20160826)
|
domain_name (0.5.20160826)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
|
doorkeeper (4.2.0)
|
||||||
|
railties (>= 4.2)
|
||||||
eco (1.0.0)
|
eco (1.0.0)
|
||||||
coffee-script
|
coffee-script
|
||||||
eco-source
|
eco-source
|
||||||
|
@ -366,6 +368,7 @@ DEPENDENCIES
|
||||||
daemons
|
daemons
|
||||||
delayed_job_active_record
|
delayed_job_active_record
|
||||||
diffy
|
diffy
|
||||||
|
doorkeeper
|
||||||
eco
|
eco
|
||||||
em-websocket
|
em-websocket
|
||||||
email_verifier
|
email_verifier
|
||||||
|
|
|
@ -78,14 +78,15 @@ class App.ControllerTable extends App.Controller
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
console.log('checkboxClick', e.target)
|
console.log('checkboxClick', e.target)
|
||||||
|
|
||||||
callbackHeader = (header) ->
|
callbackHeader = (headers) ->
|
||||||
console.log('current header is', header)
|
console.log('current header is', headers)
|
||||||
# add new header item
|
# add new header item
|
||||||
attribute =
|
attribute =
|
||||||
name: 'some name'
|
name: 'some name'
|
||||||
display: 'Some Name'
|
display: 'Some Name'
|
||||||
header.push attribute
|
headers.push attribute
|
||||||
console.log('new header is', header)
|
console.log('new header is', headers)
|
||||||
|
headers
|
||||||
|
|
||||||
callbackAttributes = (value, object, attribute, header, refObject) ->
|
callbackAttributes = (value, object, attribute, header, refObject) ->
|
||||||
console.log('data of item col', value, object, attribute, header, refObject)
|
console.log('data of item col', value, object, attribute, header, refObject)
|
||||||
|
|
|
@ -34,20 +34,47 @@ class Index extends App.ControllerSubContent
|
||||||
App.Setting.unsubscribe(@subscribeApplicationId)
|
App.Setting.unsubscribe(@subscribeApplicationId)
|
||||||
|
|
||||||
table = =>
|
table = =>
|
||||||
|
|
||||||
|
callbackHeader = (headers) ->
|
||||||
|
attribute =
|
||||||
|
name: 'view'
|
||||||
|
display: 'View'
|
||||||
|
headers.splice(3, 0, attribute)
|
||||||
|
attribute =
|
||||||
|
name: 'token'
|
||||||
|
display: 'Token'
|
||||||
|
headers.splice(4, 0, attribute)
|
||||||
|
headers
|
||||||
|
|
||||||
|
callbackViewAttributes = (value, object, attribute, header, refObject) ->
|
||||||
|
value = 'X'
|
||||||
|
value
|
||||||
|
|
||||||
|
callbackTokenAttributes = (value, object, attribute, header, refObject) ->
|
||||||
|
value = 'X'
|
||||||
|
value
|
||||||
|
|
||||||
new App.ControllerTable(
|
new App.ControllerTable(
|
||||||
el: @$('.js-appList')
|
el: @$('.js-appList')
|
||||||
model: App.Application
|
model: App.Application
|
||||||
tableId: 'applications'
|
tableId: 'applications'
|
||||||
objects: App.Application.all()
|
objects: App.Application.all()
|
||||||
bindRow:
|
bindRow:
|
||||||
events:
|
events:
|
||||||
'click': @appEdit
|
'click': @appEdit
|
||||||
|
bindCol:
|
||||||
|
view:
|
||||||
|
events:
|
||||||
|
'click': @appView
|
||||||
|
token:
|
||||||
|
events:
|
||||||
|
'click': @appToken
|
||||||
|
callbackHeader: [callbackHeader]
|
||||||
|
callbackAttributes:
|
||||||
|
view: [callbackViewAttributes]
|
||||||
|
token: [callbackTokenAttributes]
|
||||||
)
|
)
|
||||||
table()
|
table()
|
||||||
#App.Application.fetchFull(
|
|
||||||
# table
|
|
||||||
# clear: true
|
|
||||||
#)
|
|
||||||
@subscribeApplicationId = App.Application.subscribe(table, initFetch: true, clear: true)
|
@subscribeApplicationId = App.Application.subscribe(table, initFetch: true, clear: true)
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,6 +109,18 @@ class Index extends App.ControllerSubContent
|
||||||
value = @PasswordAccess.prop('checked')
|
value = @PasswordAccess.prop('checked')
|
||||||
App.Setting.set('api_password_access', value)
|
App.Setting.set('api_password_access', value)
|
||||||
|
|
||||||
|
appToken: (id, e) ->
|
||||||
|
e.preventDefault()
|
||||||
|
new ViewAppTokenModal(
|
||||||
|
app: App.Application.find(id)
|
||||||
|
)
|
||||||
|
|
||||||
|
appView: (id, e) ->
|
||||||
|
e.preventDefault()
|
||||||
|
new ViewAppModal(
|
||||||
|
app: App.Application.find(id)
|
||||||
|
)
|
||||||
|
|
||||||
appNew: (e) ->
|
appNew: (e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
new App.ControllerGenericNew(
|
new App.ControllerGenericNew(
|
||||||
|
@ -107,4 +146,51 @@ class Index extends App.ControllerSubContent
|
||||||
container: @el.closest('.content')
|
container: @el.closest('.content')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class ViewAppModal extends App.ControllerModal
|
||||||
|
headPrefix: 'App'
|
||||||
|
buttonSubmit: false
|
||||||
|
buttonCancel: true
|
||||||
|
shown: true
|
||||||
|
small: true
|
||||||
|
events:
|
||||||
|
'click .js-select': 'selectAll'
|
||||||
|
|
||||||
|
constructor: (params) ->
|
||||||
|
@head = params.app.name
|
||||||
|
super
|
||||||
|
|
||||||
|
content: ->
|
||||||
|
"AppID: <input class=\"js-select\" type=\"text\" value=\"#{@app.uid}\">
|
||||||
|
<br>
|
||||||
|
Secret: <input class=\"js-select\" type=\"text\" value=\"#{@app.secret}\">"
|
||||||
|
|
||||||
|
class ViewAppTokenModal extends App.ControllerModal
|
||||||
|
headPrefix: 'Generate Token'
|
||||||
|
buttonSubmit: 'Generate Token'
|
||||||
|
buttonCancel: true
|
||||||
|
shown: true
|
||||||
|
small: true
|
||||||
|
events:
|
||||||
|
'click .js-select': 'selectAll'
|
||||||
|
|
||||||
|
constructor: (params) ->
|
||||||
|
@head = params.app.name
|
||||||
|
super
|
||||||
|
|
||||||
|
content: ->
|
||||||
|
"#{App.i18n.translateContent('Generate Access Token for |%s|', App.Session.get().displayNameLong())}"
|
||||||
|
|
||||||
|
onSubmit: =>
|
||||||
|
@ajax(
|
||||||
|
id: 'application_token'
|
||||||
|
type: 'POST'
|
||||||
|
url: "#{@apiPath}/applications/token"
|
||||||
|
processData: true
|
||||||
|
data: JSON.stringify(id: @app.id)
|
||||||
|
success: (data, status, xhr) =>
|
||||||
|
@contentInline = "#{App.i18n.translateContent('New Access Token is')}: <input class=\"js-select\" type=\"text\" value=\"#{data.token}\">"
|
||||||
|
@update()
|
||||||
|
@$('.js-submit').remove()
|
||||||
|
)
|
||||||
|
|
||||||
App.Config.set('API', { prio: 1200, name: 'API', parent: '#system', target: '#system/api', controller: Index, permission: ['admin.api'] }, 'NavBarAdmin')
|
App.Config.set('API', { prio: 1200, name: 'API', parent: '#system', target: '#system/api', controller: Index, permission: ['admin.api'] }, 'NavBarAdmin')
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
class App.Application extends App.Model
|
class App.Application extends App.Model
|
||||||
@configure 'Application', 'name', 'scopes', 'redirect_uri'
|
@configure 'Application', 'name', 'redirect_uri'
|
||||||
@extend Spine.Model.Ajax
|
@extend Spine.Model.Ajax
|
||||||
@url: @apiPath + '/applications'
|
@url: @apiPath + '/applications'
|
||||||
|
|
||||||
@configure_attributes = [
|
@configure_attributes = [
|
||||||
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false },
|
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false },
|
||||||
{ name: 'redirect_uri', display: 'Redirect URI', tag: 'textarea', limit: 250, null: false, note: 'Use one line per URI' },
|
{ name: 'redirect_uri', display: 'Callback URL', tag: 'textarea', limit: 250, null: false, note: 'Use one line per URI' },
|
||||||
{ name: 'scopes', display: 'Scopes', tag: 'input', note: 'Scopes define the access for' },
|
{ name: 'clients', display: 'Clients', tag: 'input', readonly: 1 },
|
||||||
{ name: 'clients', display: 'Clients', tag: 'input', readonly: 1 },
|
|
||||||
{ name: 'created_at', display: 'Created', tag: 'datetime', readonly: 1 },
|
{ name: 'created_at', display: 'Created', tag: 'datetime', readonly: 1 },
|
||||||
{ name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 },
|
{ name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 },
|
||||||
]
|
]
|
||||||
@configure_overview = [
|
@configure_overview = [
|
||||||
'name', 'scopes', 'clients'
|
'name', 'redirect_uri', 'clients'
|
||||||
]
|
]
|
||||||
@configure_delete = true
|
@configure_delete = true
|
||||||
|
|
|
@ -51,6 +51,7 @@ curl -u <%= @S('email') %>:some_password <%= @C('http_type') %>://<%= @C('fqdn')
|
||||||
|
|
||||||
<button class="btn js-appNew"><%- @T('New Application') %></button>
|
<button class="btn js-appNew"><%- @T('New Application') %></button>
|
||||||
|
|
||||||
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
@ -59,7 +60,7 @@ OAuth URLs are:
|
||||||
<table class="settings-list" style="width: 100%;">
|
<table class="settings-list" style="width: 100%;">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="40%"><%- @T('Title') %>
|
<th width="40%"><%- @T('Action') %>
|
||||||
<th width="60%"><%- @T('URL') %>
|
<th width="60%"><%- @T('URL') %>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -69,9 +70,6 @@ OAuth URLs are:
|
||||||
<tr>
|
<tr>
|
||||||
<td><%- @T('Getting an Access Token') %>
|
<td><%- @T('Getting an Access Token') %>
|
||||||
<td><%= @C('http_type') %>://<%= @C('fqdn') %>/oauth/token
|
<td><%= @C('http_type') %>://<%= @C('fqdn') %>/oauth/token
|
||||||
<tr>
|
|
||||||
<td><%- @T('Revoking Access') %>
|
|
||||||
<td><%= @C('http_type') %>://<%= @C('fqdn') %>/oauth/applications
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -301,7 +301,6 @@ class ApplicationController < ActionController::Base
|
||||||
return authentication_check_prerequesits(user, 'token_auth', auth_param) if user
|
return authentication_check_prerequesits(user, 'token_auth', auth_param) if user
|
||||||
end
|
end
|
||||||
|
|
||||||
=begin
|
|
||||||
# check oauth2 token based authentication
|
# check oauth2 token based authentication
|
||||||
token = Doorkeeper::OAuth::Token.from_bearer_authorization(request)
|
token = Doorkeeper::OAuth::Token.from_bearer_authorization(request)
|
||||||
if token
|
if token
|
||||||
|
@ -309,19 +308,23 @@ class ApplicationController < ActionController::Base
|
||||||
logger.debug "oauth2 token auth check '#{token}'"
|
logger.debug "oauth2 token auth check '#{token}'"
|
||||||
access_token = Doorkeeper::AccessToken.by_token(token)
|
access_token = Doorkeeper::AccessToken.by_token(token)
|
||||||
|
|
||||||
|
if !access_token
|
||||||
|
raise Exceptions::NotAuthorized, 'Invalid token!'
|
||||||
|
end
|
||||||
|
|
||||||
# check expire
|
# check expire
|
||||||
if access_token.expires_in && (access_token.created_at + access_token.expires_in) < Time.zone.now
|
if access_token.expires_in && (access_token.created_at + access_token.expires_in) < Time.zone.now
|
||||||
raise Exceptions::NotAuthorized, 'OAuth2 token is expired!'
|
raise Exceptions::NotAuthorized, 'OAuth2 token is expired!'
|
||||||
end
|
end
|
||||||
|
|
||||||
if access_token.scopes.empty?
|
# if access_token.scopes.empty?
|
||||||
raise Exceptions::NotAuthorized, 'OAuth2 scope missing for token!'
|
# raise Exceptions::NotAuthorized, 'OAuth2 scope missing for token!'
|
||||||
end
|
# end
|
||||||
|
|
||||||
user = User.find(access_token.resource_owner_id)
|
user = User.find(access_token.resource_owner_id)
|
||||||
return authentication_check_prerequesits(user, 'token_auth', auth_param) if user
|
return authentication_check_prerequesits(user, 'token_auth', auth_param) if user
|
||||||
end
|
end
|
||||||
=end
|
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
72
app/controllers/applications_controller.rb
Normal file
72
app/controllers/applications_controller.rb
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
class ApplicationsController < ApplicationController
|
||||||
|
before_action { authentication_check(permission: 'admin.api') }
|
||||||
|
|
||||||
|
def index
|
||||||
|
all = Doorkeeper::Application.all
|
||||||
|
if params[:full]
|
||||||
|
assets = {}
|
||||||
|
item_ids = []
|
||||||
|
all.each { |item|
|
||||||
|
item_ids.push item.id
|
||||||
|
if !assets[:Application]
|
||||||
|
assets[:Application] = {}
|
||||||
|
end
|
||||||
|
application = item.attributes
|
||||||
|
application[:clients] = Doorkeeper::AccessToken.where(application_id: item.id).count
|
||||||
|
assets[:Application][item.id] = application
|
||||||
|
}
|
||||||
|
render json: {
|
||||||
|
record_ids: item_ids,
|
||||||
|
assets: assets,
|
||||||
|
}, status: :ok
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
render json: all, status: :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
def token
|
||||||
|
access_token = Doorkeeper::AccessToken.create!(application_id: params[:id], resource_owner_id: current_user.id)
|
||||||
|
render json: { token: access_token.token }, status: :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
application = Doorkeeper::Application.find(params[:id])
|
||||||
|
render json: application, status: :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
application = Doorkeeper::Application.new(clean_params)
|
||||||
|
application.save!
|
||||||
|
render json: application, status: :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
application = Doorkeeper::Application.find(params[:id])
|
||||||
|
application.update_attributes!(clean_params)
|
||||||
|
render json: application, status: :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
application = Doorkeeper::Application.find(params[:id])
|
||||||
|
application.destroy!
|
||||||
|
render json: {}, status: :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def clean_params
|
||||||
|
params_data = params.permit! #.to_h
|
||||||
|
params_data.delete('application')
|
||||||
|
params_data.delete('action')
|
||||||
|
params_data.delete('controller')
|
||||||
|
params_data.delete('id')
|
||||||
|
params_data.delete('uid')
|
||||||
|
params_data.delete('secret')
|
||||||
|
params_data.delete('created_at')
|
||||||
|
params_data.delete('updated_at')
|
||||||
|
params_data
|
||||||
|
end
|
||||||
|
end
|
|
@ -13,6 +13,9 @@ class Organization < ApplicationModel
|
||||||
has_many :members, class_name: 'User'
|
has_many :members, class_name: 'User'
|
||||||
validates :name, presence: true
|
validates :name, presence: true
|
||||||
|
|
||||||
|
before_create :domain_cleanup
|
||||||
|
before_update :domain_cleanup
|
||||||
|
|
||||||
activity_stream_support permission: 'admin.role'
|
activity_stream_support permission: 'admin.role'
|
||||||
history_support
|
history_support
|
||||||
search_index_support
|
search_index_support
|
||||||
|
@ -21,6 +24,15 @@ class Organization < ApplicationModel
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def domain_cleanup
|
||||||
|
return if !domain
|
||||||
|
return if domain.empty?
|
||||||
|
domain.gsub!(/@/, '')
|
||||||
|
domain.gsub!(/\s*/, '')
|
||||||
|
domain.strip!
|
||||||
|
domain.downcase!
|
||||||
|
end
|
||||||
|
|
||||||
def cache_delete
|
def cache_delete
|
||||||
super
|
super
|
||||||
|
|
||||||
|
|
|
@ -738,22 +738,21 @@ perform changes on ticket
|
||||||
}
|
}
|
||||||
|
|
||||||
# get subject
|
# get subject
|
||||||
value['subject'].gsub!(/\#\{config\.(.+?)\}/, '<%= c "\\1", false %>')
|
|
||||||
value['subject'].gsub!(/\#\{(.+?)\}/, '<%= d "\\1", false %>')
|
|
||||||
subject = NotificationFactory::Mailer.template(
|
subject = NotificationFactory::Mailer.template(
|
||||||
templateInline: value['subject'],
|
templateInline: value['subject'],
|
||||||
locale: 'en-en',
|
locale: 'en-en',
|
||||||
objects: objects,
|
objects: objects,
|
||||||
|
quote: false,
|
||||||
)
|
)
|
||||||
subject = subject_build(subject)
|
subject = subject_build(subject)
|
||||||
|
|
||||||
value['body'].gsub!(/\#\{config\.(.+?)\}/, '<%= c "\\1", true %>')
|
|
||||||
value['body'].gsub!(/\#\{(.+?)\}/, '<%= d "\\1", true %>')
|
|
||||||
body = NotificationFactory::Mailer.template(
|
body = NotificationFactory::Mailer.template(
|
||||||
templateInline: value['body'],
|
templateInline: value['body'],
|
||||||
locale: 'en-en',
|
locale: 'en-en',
|
||||||
objects: objects,
|
objects: objects,
|
||||||
|
quote: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
Ticket::Article.create(
|
Ticket::Article.create(
|
||||||
ticket_id: id,
|
ticket_id: id,
|
||||||
to: recipient_string,
|
to: recipient_string,
|
||||||
|
|
|
@ -121,6 +121,49 @@ returns
|
||||||
Ticket::Article.where('ticket_id = ? AND sender_id NOT IN (?)', ticket_id, sender.id).order('created_at DESC').first
|
Ticket::Article.where('ticket_id = ? AND sender_id NOT IN (?)', ticket_id, sender.id).order('created_at DESC').first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
get body as html
|
||||||
|
|
||||||
|
article = Ticket::Article.find(123)
|
||||||
|
article.body_as_html
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def body_as_html
|
||||||
|
return '' if !body
|
||||||
|
return body if content_type && content_type =~ %r{text/html}i
|
||||||
|
body.text2html
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
get body as text
|
||||||
|
|
||||||
|
article = Ticket::Article.find(123)
|
||||||
|
article.body_as_text
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def body_as_text
|
||||||
|
return '' if !body
|
||||||
|
return body if !content_type || content_type.empty? || content_type =~ %r{text/plain}i
|
||||||
|
body.html2text
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
get body as text with quote sign "> " at the beginning of each line
|
||||||
|
|
||||||
|
article = Ticket::Article.find(123)
|
||||||
|
article.body_as_text
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def body_as_text_with_quote
|
||||||
|
body_as_text.word_wrap.message_quote
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# strip not wanted chars
|
# strip not wanted chars
|
||||||
|
|
|
@ -33,7 +33,7 @@ class User < ApplicationModel
|
||||||
include User::SearchIndex
|
include User::SearchIndex
|
||||||
|
|
||||||
before_validation :check_name, :check_email, :check_login, :check_password
|
before_validation :check_name, :check_email, :check_login, :check_password
|
||||||
before_create :check_preferences_default, :validate_roles
|
before_create :check_preferences_default, :validate_roles, :domain_based_assignment
|
||||||
before_update :check_preferences_default, :validate_roles
|
before_update :check_preferences_default, :validate_roles
|
||||||
after_create :avatar_for_email_check
|
after_create :avatar_for_email_check
|
||||||
after_update :avatar_for_email_check
|
after_update :avatar_for_email_check
|
||||||
|
@ -856,6 +856,20 @@ returns
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def domain_based_assignment
|
||||||
|
return if !email
|
||||||
|
return if organization_id
|
||||||
|
begin
|
||||||
|
domain = Mail::Address.new(email).domain
|
||||||
|
return if !domain
|
||||||
|
organization = Organization.find_by(domain: domain.downcase, domain_assignment: true)
|
||||||
|
return if !organization
|
||||||
|
self.organization_id = organization.id
|
||||||
|
rescue
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def avatar_for_email_check
|
def avatar_for_email_check
|
||||||
return if !email
|
return if !email
|
||||||
return if email.empty?
|
return if email.empty?
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
Dein <%= c 'product_name' %> Passwort wurde geändert
|
Dein #{config.product_name} Passwort wurde geändert
|
||||||
|
|
||||||
<div>Hallo <%= d 'user.firstname' %>,</div>
|
<div>Hallo #{user.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>das Passwort für Dein <%= c 'product_name' %> Account <b><%= d 'user.login' %></b> wurde kürzlich geändert.</div>
|
<div>das Passwort für Dein #{config.product_name} Account <b>#{user.login}</b> wurde kürzlich geändert.</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Diese Aktivität ist Dir nicht bekannt? In diesen Fall kontaktiere Deinen System-Administrator.</div>
|
<div>Diese Aktivität ist Dir nicht bekannt? In diesen Fall kontaktiere Deinen System-Administrator.</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Dein <%= c 'product_name' %> Team</div>
|
<div>Dein #{config.product_name} Team</div>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
Your <%= c 'product_name' %> password has been changed
|
Your #{product_name} password has been changed
|
||||||
|
|
||||||
<p>Hi <%= d 'user.firstname' %>,</p>
|
<p>Hi #{user.firstname},</p>
|
||||||
<br>
|
<br>
|
||||||
<p>The password for your <%= c 'product_name' %> account <b><%= d 'user.login' %></b> has been changed recently.</p>
|
<p>The password for your #{product_name} account <b>#{user.login}</b> has been changed recently.</p>
|
||||||
<br>
|
<br>
|
||||||
<p>This activity is not known to you? If not, contact your system administrator.</p>
|
<p>This activity is not known to you? If not, contact your system administrator.</p>
|
||||||
<br>
|
<br>
|
||||||
<p>Your <%= c 'product_name' %> Team</p>
|
<p>Your #{product_name} Team</p>
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
Zurücksetzen Deines <%= c 'product_name' %> Passworts
|
Zurücksetzen Deines #{config.product_name} Passworts
|
||||||
|
|
||||||
<div>Hallo <%= d 'user.firstname' %>,</div>
|
<div>Hallo #{user.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>wir haben eine Anfrage zum Zurücksetzen des Passworts für <%= c 'product_name' %> Account <b><%= d 'user.login' %></b> erhalten.</div>
|
<div>wir haben eine Anfrage zum Zurücksetzen des Passworts für #{config.product_name} Account <b>#{user.login}</b> erhalten.</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Wenn Sie Ihr Passwort zurückzusetzen wollen, klicken Sie auf den unten stehenden Link (oder kopieren Sie die URL in den Browser einfügen):</div>
|
<div>Wenn Sie Ihr Passwort zurückzusetzen wollen, klicken Sie auf den unten stehenden Link (oder kopieren Sie die URL in den Browser einfügen):</div>
|
||||||
<br>
|
<br>
|
||||||
<div><a href="<%= c 'http_type' %>://<%= c 'fqdn' %>/#password_reset_verify/<%= d 'token.name' %>"><%= c 'http_type' %>://<%= c 'fqdn' %>/#password_reset_verify/<%= d 'token.name' %></a></div>
|
<div><a href="#{config.http_type}://#{config.fqdn}/#password_reset_verify/#{token.name}">#{config.http_type}://#{config.fqdn}/#password_reset_verify/#{token.name}</a></div>
|
||||||
<br>
|
<br>
|
||||||
<div>Dieser Link führt Sie zu einer Seite, auf der Sie Ihr Passwort ändern können.</div>
|
<div>Dieser Link führt Sie zu einer Seite, auf der Sie Ihr Passwort ändern können.</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Wenn Sie Ihr Passwort nicht zurücksetzen wollen, ignorieren Sie diese Meldung. Das Passwort bleibt unverändert.</div>
|
<div>Wenn Sie Ihr Passwort nicht zurücksetzen wollen, ignorieren Sie diese Meldung. Das Passwort bleibt unverändert.</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Dein <%= c 'product_name' %> Team</div>
|
<div>Dein #{config.product_name} Team</div>
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
Reset your <%= c 'product_name' %> password
|
Reset your #{config.product_name} password
|
||||||
|
|
||||||
<div>Hi <%= d 'user.firstname' %>,</div>
|
<div>Hi #{user.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>We received a request to reset the password for your <%= c 'product_name' %> account <b><%= d 'user.login' %></b>.</div>
|
<div>We received a request to reset the password for your #{config.product_name} account <b>#{user.login}</b>.</div>
|
||||||
<br>
|
<br>
|
||||||
<div>If you want to reset your password, click on the link below (or copy and paste the URL into your browser):</div>
|
<div>If you want to reset your password, click on the link below (or copy and paste the URL into your browser):</div>
|
||||||
<br>
|
<br>
|
||||||
<div><a href="<%= c 'http_type' %>://<%= c 'fqdn' %>/#password_reset_verify/<%= d 'token.name' %>"><%= c 'http_type' %>://<%= c 'fqdn' %>/#password_reset_verify/<%= d 'token.name' %></a></div>
|
<div><a href="#{config.http_type}://#{config.fqdn}/#password_reset_verify/#{token.name}">#{config.http_type}://#{config.fqdn}/#password_reset_verify/#{token.name}</a></div>
|
||||||
<br>
|
<br>
|
||||||
<div>This link takes you to a page where you can change your password.</div>
|
<div>This link takes you to a page where you can change your password.</div>
|
||||||
<br>
|
<br>
|
||||||
<div>If you don't want to reset your password, please ignore this message. Your password will not be reseted.</div>
|
<div>If you don't want to reset your password, please ignore this message. Your password will not be reseted.</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Your <%= c 'product_name' %> Team</div>
|
<div>Your #{config.product_name} Team</div>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
Bestätigung des <%= c 'product_name' %> Accounts, <%= d 'user.firstname' %> <%= d 'user.lastname' %>
|
Bestätigung des #{config.product_name} Accounts, #{user.firstname} #{user.lastname}
|
||||||
|
|
||||||
<div>Hallo <%= d 'user.firstname' %>,</div>
|
<div>Hallo #{user.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>bestätige Deine E-Mail-Adresse um Deine Registrierung bei <%= c 'product_name' %> abzuschließen. Es ist einfach - klick einfach auf den Link unten.</div>
|
<div>bestätige Deine E-Mail-Adresse um Deine Registrierung bei #{config.product_name} abzuschließen. Es ist einfach - klick einfach auf den Link unten.</div>
|
||||||
<br>
|
<br>
|
||||||
<div><a href="<%= c 'http_type' %>://<%= c 'fqdn' %>/#email_verify/<%= d 'token.name' %>"><%= c 'http_type' %>://<%= c 'fqdn' %>/#email_verify/<%= d 'token.name' %></a></div>
|
<div><a href="#{config.http_type}://#{config.fqdn}/#email_verify/#{token.name}">#{config.http_type}://#{config.fqdn}/#email_verify/#{token.name}</a></div>
|
||||||
<br>
|
<br>
|
||||||
<div>Dein <%= c 'product_name' %> Team</div>
|
<div>Dein #{config.product_name} Team</div>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
Confirm your <%= c 'product_name' %> account, <%= d 'user.firstname' %> <%= d 'user.lastname' %>
|
Confirm your #{config.product_name} account, #{user.firstname} #{user.lastname}
|
||||||
|
|
||||||
<div>Hi <%= d 'user.firstname' %>,</div>
|
<div>Hi #{user.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Confirm your email address to complete your <%= c 'product_name' %> account. It's easy, just click the link below.</div>
|
<div>Confirm your email address to complete your #{config.product_name} account. It's easy, just click the link below.</div>
|
||||||
<br>
|
<br>
|
||||||
<div><a href="<%= c 'http_type' %>://<%= c 'fqdn' %>/#email_verify/<%= d 'token.name' %>"><%= c 'http_type' %>://<%= c 'fqdn' %>/#email_verify/<%= d 'token.name' %></a></div>
|
<div><a href="#{config.http_type}://#{config.fqdn}/#email_verify/#{token.name}">#{config.http_type}://#{config.fqdn}/#email_verify/#{token.name}</a></div>
|
||||||
<br>
|
<br>
|
||||||
<div>Your <%= c 'product_name' %> Team</div>
|
<div>Your #{config.product_name} Team</div>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
Test Ticket!
|
Test Ticket!
|
||||||
|
|
||||||
<div>Hallo <%= d 'agent.firstname' %>,</div>
|
<div>Hallo #{agent.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>dies ist ein <b>Test Ticket</b>. Ich bin ein Kunde und benötige Hilfe! :)</div>
|
<div>dies ist ein <b>Test Ticket</b>. Ich bin ein Kunde und benötige Hilfe! :)</div>
|
||||||
<br>
|
<br>
|
||||||
<div><%= d 'customer.fullname' %></div>
|
<div>#{customer.fullname}</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Das Zammad Projekt</div>
|
<div>Das Zammad Projekt</div>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
Test Ticket!
|
Test Ticket!
|
||||||
|
|
||||||
<div>Dear <%= d 'agent.firstname' %>,</div>
|
<div>Dear #{agent.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>This is a <b>test ticket</b>. I'm a customer and I need some help! :)</div>
|
<div>This is a <b>test ticket</b>. I'm a customer and I need some help! :)</div>
|
||||||
<br>
|
<br>
|
||||||
<div><%= d 'customer.fullname' %></div>
|
<div>#{customer.fullname}</div>
|
||||||
<br>
|
<br>
|
||||||
<div>The Zammad Project</div>
|
<div>The Zammad Project</div>
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
Neues Ticket (<%= d 'ticket.title' %>)
|
Neues Ticket (#{ticket.title})
|
||||||
|
|
||||||
<div>Hallo <%= d 'recipient.firstname' %>,</div>
|
<div>Hallo #{recipient.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>es wurde ein neues Ticket (<%= d 'ticket.title' %>) von "<b><%= d 'current_user.longname' %></b>" erstellt.</div>
|
<div>es wurde ein neues Ticket (#{ticket.title}) von "<b>#{current_user.longname}</b>" erstellt.</div>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
<%= t 'Group' %>: <%= d 'ticket.group.name' %><br>
|
#{t('Group')}: #{ticket.group.name}<br>
|
||||||
<%= t 'Owner' %>: <%= d 'ticket.owner.fullname' %><br>
|
#{t('Owner')}: #{ticket.owner.fullname}<br>
|
||||||
<%= t 'State' %>: <%= t d 'ticket.state.name' %><br>
|
#{t('State')}: #{t(ticket.state.name)}<br>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<% if @objects[:article] %>
|
<% if @objects[:article] %>
|
||||||
<div>
|
<div>
|
||||||
<%= t 'Information' %>:
|
#{t('Information')}:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a_html 'article' %>
|
#{article.body_as_html}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
<a href="<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>" target="zammad_app"><%= t 'View this in Zammad' %></a>
|
<a href="#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}" target="zammad_app">#{t('View this in Zammad')}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
New Ticket (<%= d 'ticket.title' %>)
|
New Ticket (#{ticket.title})
|
||||||
|
|
||||||
<div>Hi <%= d 'recipient.firstname' %>,</div>
|
<div>Hi #{recipient.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>A new ticket (<%= d 'ticket.title' %>) has been created by "<b><%= d 'current_user.longname' %></b>".</div>
|
<div>A new ticket (#{ticket.title}) has been created by "<b>#{current_user.longname}</b>".</div>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
<%= t 'Group' %>: <%= d 'ticket.group.name' %><br>
|
#{t('Group')}: #{ticket.group.name}<br>
|
||||||
<%= t 'Owner' %>: <%= d 'ticket.owner.fullname' %><br>
|
#{t('Owner')}: #{ticket.owner.fullname}<br>
|
||||||
<%= t 'State' %>: <%= t d 'ticket.state.name' %><br>
|
#{t('State')}: #{t(ticket.state.name)}<br>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<% if @objects[:article] %>
|
<% if @objects[:article] %>
|
||||||
<div>
|
<div>
|
||||||
<%= t 'Information' %>:
|
#{t('Information')}:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a_html 'article' %>
|
#{article.body_as_html}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
<a href="<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>" target="zammad_app"><%= t 'View this in Zammad' %></a>
|
<a href="#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}" target="zammad_app">#{t('View this in Zammad')}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
Ticket ist eskaliert (<%= d 'ticket.title' %>)
|
Ticket ist eskaliert (#{ticket.title})
|
||||||
|
|
||||||
<div>Hallo <%= d 'recipient.firstname' %>,</div>
|
<div>Hallo #{recipient.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Ticket (<%= d 'ticket.title' %>) von "<b><%= d 'ticket.customer.longname' %></b>" ist seit "<%= d 'ticket.escalation_at' %>" eskaliert!</div>
|
<div>Ticket (#{ticket.title}) von "<b>#{ticket.customer.longname}</b>" ist seit "#{ticket.escalation_at}" eskaliert!</div>
|
||||||
<br>
|
<br>
|
||||||
<% if @objects[:article] %>
|
<% if @objects[:article] %>
|
||||||
<div>
|
<div>
|
||||||
<%= t 'Information' %>:
|
#{t('Information')}:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a_html 'article' %>
|
#{article.body_as_html}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
<a href="<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>" target="zammad_app"><%= t 'View this in Zammad' %></a>
|
<a href="#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}" target="zammad_app">#{t('View this in Zammad')}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
Ticket is escalated (<%= d 'ticket.title' %>)
|
Ticket is escalated (#{ticket.title})
|
||||||
|
|
||||||
<div>Hi <%= d 'recipient.firstname' %>,</div>
|
<div>Hi #{recipient.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>A ticket (<%= d 'ticket.title' %>) from "<b><%= d 'ticket.customer.longname' %></b>" is escalated since "<%= d 'ticket.escalation_at' %>"!</div>
|
<div>A ticket (#{ticket.title}) from "<b>#{ticket.customer.longname}</b>" is escalated since "#{ticket.escalation_at}"!</div>
|
||||||
<br>
|
<br>
|
||||||
<% if @objects[:article] %>
|
<% if @objects[:article] %>
|
||||||
<div>
|
<div>
|
||||||
<%= t 'Information' %>:
|
#{t('Information')}:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a_html 'article' %>
|
#{article.body_as_html}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
<a href="<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>" target="zammad_app"><%= t 'View this in Zammad' %></a>
|
<a href="#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}" target="zammad_app">#{t('View this in Zammad')}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
Ticket wird eskalieren (<%= d 'ticket.title' %>)
|
Ticket wird eskalieren (#{ticket.title})
|
||||||
|
|
||||||
<div>Hallo <%= d 'recipient.firstname' %>,</div>
|
<div>Hallo #{recipient.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Ticket (<%= d 'ticket.title' %>) von "<b><%= d 'ticket.customer.longname' %></b>" wird um "<%= d 'ticket.escalation_at' %>" eskalieren!</div>
|
<div>Ticket (#{ticket.title}) von "<b>#{ticket.customer.longname}</b>" wird um "#{ticket.escalation_at}" eskalieren!</div>
|
||||||
<br>
|
<br>
|
||||||
<% if @objects[:article] %>
|
<% if @objects[:article] %>
|
||||||
<div>
|
<div>
|
||||||
<%= t 'Information' %>:
|
#{t('Information')}:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a_html 'article' %>
|
#{article.body_as_html}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
<a href="<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>" target="zammad_app"><%= t 'View this in Zammad' %></a>
|
<a href="#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}" target="zammad_app">#{t('View this in Zammad')}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
Ticket will escalate (<%= d 'ticket.title' %>)
|
Ticket will escalate (#{ticket.title})
|
||||||
|
|
||||||
<div>Hi <%= d 'recipient.firstname' %>,</div>
|
<div>Hi #{recipient.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>A ticket (<%= d 'ticket.title' %>) from "<b><%= d 'ticket.customer.longname' %></b>" will escalate at "<%= d 'ticket.escalation_at' %>"!</div>
|
<div>A ticket (#{ticket.title}) from "<b>#{ticket.customer.longname}</b>" will escalate at "#{ticket.escalation_at}"!</div>
|
||||||
<br>
|
<br>
|
||||||
<% if @objects[:article] %>
|
<% if @objects[:article] %>
|
||||||
<div>
|
<div>
|
||||||
<%= t 'Information' %>:
|
#{t('Information')}:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a_html 'article' %>
|
#{article.body_as_html}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
<a href="<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>" target="zammad_app"><%= t 'View this in Zammad' %></a>
|
<a href="#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}" target="zammad_app">#{t('View this in Zammad')}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
Warten auf Erinnerung erreicht! (<%= d 'ticket.title' %>)
|
Warten auf Erinnerung erreicht! (#{ticket.title})
|
||||||
|
|
||||||
<div>Hallo <%= d 'recipient.firstname' %>,</div>
|
<div>Hallo #{recipient.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>dieses Ticket benötigt Deine Aufmerksamkeit, warten auf Erinnerung für (<%= d 'ticket.title' %>) mit dem Kunden "<b><%= d 'ticket.customer.longname' %></b>" ist erreicht.</div>
|
<div>dieses Ticket benötigt Deine Aufmerksamkeit, warten auf Erinnerung für (#{ticket.title}) mit dem Kunden "<b>#{ticket.customer.longname}</b>" ist erreicht.</div>
|
||||||
<br>
|
<br>
|
||||||
<% if @objects[:article] %>
|
<% if @objects[:article] %>
|
||||||
<div>
|
<div>
|
||||||
<%= t 'Information' %>:
|
#{t('Information')}:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a_html 'article' %>
|
#{article.body_as_html}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
<a href="<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>" target="zammad_app"><%= t 'View this in Zammad' %></a>
|
<a href="#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}" target="zammad_app">#{t('View this in Zammad')}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
Reminder reached (<%= d 'ticket.title' %>)
|
Reminder reached (#{ticket.title})
|
||||||
|
|
||||||
<div>Hi <%= d 'recipient.firstname' %>,</div>
|
<div>Hi #{recipient.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>A ticket needs attention, reminder reached for (<%= d 'ticket.title' %>) with customer "<b><%= d 'ticket.customer.longname' %></b>".</div>
|
<div>A ticket needs attention, reminder reached for (#{ticket.title}) with customer "<b>#{ticket.customer.longname}</b>".</div>
|
||||||
<br>
|
<br>
|
||||||
<% if @objects[:article] %>
|
<% if @objects[:article] %>
|
||||||
<div>
|
<div>
|
||||||
<%= t 'Information' %>:
|
#{t('Information')}:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a_html 'article' %>
|
#{article.body_as_html}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
<a href="<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>" target="zammad_app"><%= t 'View this in Zammad' %></a>
|
<a href="#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}" target="zammad_app">#{t('View this in Zammad')}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
Ticket aktualisiert (<%= d 'ticket.title' %>)
|
Ticket aktualisiert (#{ticket.title})
|
||||||
|
|
||||||
<div>Hi <%= d 'recipient.firstname' %>,</div>
|
<div>Hi #{recipient.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
Ticket (<%= d 'ticket.title' %>) wurde von "<b><%= d 'current_user.longname' %></b>" aktualisiert.
|
Ticket (#{ticket.title}) wurde von "<b>#{current_user.longname}</b>" aktualisiert.
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<% if @objects[:changes] && !@objects[:changes].empty? %>
|
<% if @objects[:changes] && !@objects[:changes].empty? %>
|
||||||
<div>
|
<div>
|
||||||
<%= t 'Changes' %>:<br>
|
#{t('Changes')}:<br>
|
||||||
<% @objects[:changes].each do |key, value| %>
|
<% @objects[:changes].each do |key, value| %>
|
||||||
<%= t key %>: <%= h value[0] %> -> <%= h value[1] %><br>
|
<%= t key %>: <%= h value[0] %> -> <%= h value[1] %><br>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -17,13 +17,13 @@ Ticket (<%= d 'ticket.title' %>) wurde von "<b><%= d 'current_user.longname' %><
|
||||||
<br>
|
<br>
|
||||||
<% if @objects[:article] %>
|
<% if @objects[:article] %>
|
||||||
<div>
|
<div>
|
||||||
<%= t 'Information' %>:
|
#{t('Information')}:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a_html 'article' %>
|
#{article.body_as_html}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
<a href="<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>" target="zammad_app"><%= t 'View this in Zammad' %></a>
|
<a href="#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}" target="zammad_app">#{t('View this in Zammad')}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
Updated Ticket (<%= d 'ticket.title' %>)
|
Updated Ticket (#{ticket.title})
|
||||||
|
|
||||||
<div>Hi <%= d 'recipient.firstname' %>,</div>
|
<div>Hi #{recipient.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
Ticket (<%= d 'ticket.title' %>) has been updated by "<b><%= d 'current_user.longname' %></b>".
|
Ticket (#{ticket.title}) has been updated by "<b>#{current_user.longname}</b>".
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<% if @objects[:changes] && !@objects[:changes].empty? %>
|
<% if @objects[:changes] && !@objects[:changes].empty? %>
|
||||||
<div>
|
<div>
|
||||||
<%= t 'Changes' %>:<br>
|
#{t('Changes')}:<br>
|
||||||
<% @objects[:changes].each do |key, value| %>
|
<% @objects[:changes].each do |key, value| %>
|
||||||
<%= t key %>: <%= h value[0] %> -> <%= h value[1] %><br>
|
<%= t key %>: <%= h value[0] %> -> <%= h value[1] %><br>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -17,13 +17,13 @@ Ticket (<%= d 'ticket.title' %>) has been updated by "<b><%= d 'current_user.lon
|
||||||
<br>
|
<br>
|
||||||
<% if @objects[:article] %>
|
<% if @objects[:article] %>
|
||||||
<div>
|
<div>
|
||||||
<%= t 'Information' %>:
|
#{t('Information')}:
|
||||||
<blockquote type="cite">
|
<blockquote type="cite">
|
||||||
<%= a_html 'article' %>
|
#{article.body_as_html}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
<a href="<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>" target="zammad_app"><%= t 'View this in Zammad' %></a>
|
<a href="#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}" target="zammad_app">#{t('View this in Zammad')}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
<%= c 'product_name' %>-Anmeldung erfasst von einem neuen Gerät
|
#{config.product_name}-Anmeldung erfasst von einem neuen Gerät
|
||||||
|
|
||||||
<div>Hallo <%= d 'user.firstname' %>,</div>
|
<div>Hallo #{user.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>es sieht aus, als ob Du Dich mit <b>einem neuen Gerät</b> um "<%= d 'user_device.created_at' %>" angemeldet hast:</div>
|
<div>es sieht aus, als ob Du Dich mit <b>einem neuen Gerät</b> um "#{user_device.created_at}" angemeldet hast:</div>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
Dein Gerät: <%= d 'user_device.name' %><br>
|
Dein Gerät: #{user_device.name}<br>
|
||||||
Deine Lokation (relativ): <%= d 'user_device.location' %><br>
|
Deine Lokation (relativ): #{user_device.location}<br>
|
||||||
Deine IP: <%= d 'user_device.ip' %><br>
|
Deine IP: #{user_device.ip}<br>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Das Gerät wurde in die Liste der bekannten Geräte hinzugefügt, diese Liste kannst Du hier einsehen:</div>
|
<div>Das Gerät wurde in die Liste der bekannten Geräte hinzugefügt, diese Liste kannst Du hier einsehen:</div>
|
||||||
<br>
|
<br>
|
||||||
<div><%= c 'http_type' %>://<%= c 'fqdn' %>/#profile/devices</div>
|
<div>#{config.http_type}://#{config.fqdn}/#profile/devices</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Wenn dies nicht Du warst, entferne das Gerät aus der Liste, ändere Dein Account-Passwort und kontaktieren Deinen Administrator. Jemand könnte unberechtigten Zugriff auf Dein Konto bekommen haben.</div>
|
<div>Wenn dies nicht Du warst, entferne das Gerät aus der Liste, ändere Dein Account-Passwort und kontaktieren Deinen Administrator. Jemand könnte unberechtigten Zugriff auf Dein Konto bekommen haben.</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Dein <%= c 'product_name' %> Team</div>
|
<div>Dein #{config.product_name} Team</div>
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
<%= c 'product_name' %> signin detected from a new device
|
#{config.product_name} signin detected from a new device
|
||||||
|
|
||||||
<div>Hi <%= d 'user.firstname' %>,</div>
|
<div>Hi #{user.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>It looks like you signed into your account <b>using a new device</b> on "<%= d 'user_device.created_at' %>":</div>
|
<div>It looks like you signed into your account <b>using a new device</b> on "#{user_device.created_at}":</div>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
Your device: <%= d 'user_device.name' %><br>
|
Your device: #{user_device.name}<br>
|
||||||
Your location (relative): <%= d 'user_device.location' %><br>
|
Your location (relative): #{user_device.location}<br>
|
||||||
Your IP: <%= d 'user_device.ip' %><br>
|
Your IP: #{user_device.ip}<br>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Your device has been added to your list of known devices, which you can view here:</div>
|
<div>Your device has been added to your list of known devices, which you can view here:</div>
|
||||||
<br>
|
<br>
|
||||||
<div><%= c 'http_type' %>://<%= c 'fqdn' %>/#profile/devices</div>
|
<div>#{config.http_type}://#{config.fqdn}/#profile/devices</div>
|
||||||
<br>
|
<br>
|
||||||
<div>If this wasn't you, remove the device, changing your account password, and contacting your administrator. Somebody might have gained unauthorized access to your account.</div>
|
<div>If this wasn't you, remove the device, changing your account password, and contacting your administrator. Somebody might have gained unauthorized access to your account.</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Your <%= c 'product_name' %> Team</div>
|
<div>Your #{config.product_name} Team</div>
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
<%= c 'product_name' %>-Anmeldung von einem anderen Land erfasst
|
#{config.product_name}-Anmeldung von einem anderen Land erfasst
|
||||||
|
|
||||||
<div>Hallo <%= d 'user.firstname' %>,</div>
|
<div>Hallo #{user.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>es sieht aus, als ob Du Dich um "<%= d 'user_device.created_at' %>" von einem <b>bekannten Gerät aus einem anderen Land angemeldet hast</b>:</div>
|
<div>es sieht aus, als ob Du Dich um "#{user_device.created_at}" von einem <b>bekannten Gerät aus einem anderen Land angemeldet hast</b>:</div>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
Dein Gerät: <%= d 'user_device.name' %><br>
|
Dein Gerät: #{user_device.name}<br>
|
||||||
Deine Lokation (relativ): <%= d 'user_device.location' %><br>
|
Deine Lokation (relativ): #{user_device.location}<br>
|
||||||
Deine IP: <%= d 'user_device.ip' %><br>
|
Deine IP: #{user_device.ip}<br>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Das neue Land wurde in die Liste der bekannten Geräte hinzugefügt, diese Liste kannst Du hier einsehen:</div>
|
<div>Das neue Land wurde in die Liste der bekannten Geräte hinzugefügt, diese Liste kannst Du hier einsehen:</div>
|
||||||
<br>
|
<br>
|
||||||
<div><%= c 'http_type' %>://<%= c 'fqdn' %>/#profile/devices</div>
|
<div>#{config.http_type}://#{config.fqdn}/#profile/devices</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Wenn dies nicht Du warst, entferne die neue Lokation aus der Liste, ändere Dein Account-Passwort und kontaktieren Deinen Administrator. Jemand könnte unberechtigten Zugriff auf Dein Konto bekommen haben.</div>
|
<div>Wenn dies nicht Du warst, entferne die neue Lokation aus der Liste, ändere Dein Account-Passwort und kontaktieren Deinen Administrator. Jemand könnte unberechtigten Zugriff auf Dein Konto bekommen haben.</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Dein <%= c 'product_name' %> Team</div>
|
<div>Dein #{config.product_name} Team</div>
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
<%= c 'product_name' %> signin detected from a new country
|
#{config.product_name} signin detected from a new country
|
||||||
|
|
||||||
<div>Hi <%= d 'user.firstname' %>,</div>
|
<div>Hi #{user.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>It looks like you used your account with an <b>known device but from a new country</b> on "<%= d 'user_device.created_at' %>":</div>
|
<div>It looks like you used your account with an <b>known device but from a new country</b> on "#{user_device.created_at}":</div>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
Your device: <%= d 'user_device.name' %><br>
|
Your device: #{user_device.name}<br>
|
||||||
Your location (relative): <%= d 'user_device.location' %><br>
|
Your location (relative): #{user_device.location}<br>
|
||||||
Your IP: <%= d 'user_device.ip' %><br>
|
Your IP: #{user_device.ip}<br>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div>The country has been added to your list of known devices, which you can view here:</div>
|
<div>The country has been added to your list of known devices, which you can view here:</div>
|
||||||
<br>
|
<br>
|
||||||
<div><%= c 'http_type' %>://<%= c 'fqdn' %>/#profile/devices</div>
|
<div>#{config.http_type}://#{config.fqdn}/#profile/devices</div>
|
||||||
<br>
|
<br>
|
||||||
<div>If this wasn't you, remove the device, changing your account password, and contacting your administrator. Somebody might have gained unauthorized access to your account.</div>
|
<div>If this wasn't you, remove the device, changing your account password, and contacting your administrator. Somebody might have gained unauthorized access to your account.</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Your <%= c 'product_name' %> Team</div>
|
<div>Your #{config.product_name} Team</div>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
Einladung zu <%= c 'product_name' %> über <%= c 'fqdn' %>
|
Einladung zu #{config.product_name} über #{config.fqdn}
|
||||||
|
|
||||||
<div>Hallo <%= d 'user.firstname' %>,</div>
|
<div>Hallo #{user.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Ich (<%= d 'current_user.firstname' %> <%= d 'current_user.lastname' %>) möchte Dich zu <%= c 'product_name' %> einladen - unsere Kundensupport / Ticket System Platform.</div>
|
<div>Ich (#{current_user.firstname} #{current_user.lastname}) möchte Dich zu #{config.product_name} einladen - unsere Kundensupport / Ticket System Platform.</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Um sich anzumelden kann <a href="<%= c 'http_type' %>://<%= c 'fqdn' %>/#password_reset_verify/<%= d 'token.name' %>">hier</a> das Password gesetzt werden.</div>
|
<div>Um sich anzumelden kann <a href="#{config.http_type}://#{config.fqdn}/#password_reset_verify/#{token.name}">hier</a> das Password gesetzt werden.</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Enjoy,</div>
|
<div>Enjoy,</div>
|
||||||
<br>
|
<br>
|
||||||
<div><%= d 'current_user.firstname' %> <%= d 'current_user.lastname' %></div>
|
<div>#{current_user.firstname} #{current_user.lastname}</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Dein <%= c 'product_name' %> Team</div>
|
<div>Dein #{config.product_name} Team</div>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
Invitation to <%= c 'product_name' %> at <%= c 'fqdn' %>
|
Invitation to #{config.product_name} at #{config.fqdn}
|
||||||
|
|
||||||
<div>Hi <%= d 'user.firstname' %>,</div>
|
<div>Hi #{user.firstname},</div>
|
||||||
<br>
|
<br>
|
||||||
<div>I (<%= d 'current_user.firstname' %> <%= d 'current_user.lastname' %>) invite you to <%= c 'product_name' %> - our customer support / ticket system platform.</div>
|
<div>I (#{current_user.firstname} #{current_user.lastname}) invite you to #{config.product_name} - our customer support / ticket system platform.</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Click <a href="<%= c 'http_type' %>://<%= c 'fqdn' %>/#password_reset_verify/<%= d 'token.name' %>">here</a> and set your password.</div>
|
<div>Click <a href="#{config.http_type}://#{config.fqdn}/#password_reset_verify/#{token.name}">here</a> and set your password.</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Enjoy,</div>
|
<div>Enjoy,</div>
|
||||||
<br>
|
<br>
|
||||||
<div><%= d 'current_user.firstname' %> <%= d 'current_user.lastname' %></div>
|
<div>#{current_user.firstname} #{current_user.lastname}</div>
|
||||||
<br>
|
<br>
|
||||||
<div>Your <%= c 'product_name' %> Team</div>
|
<div>Your #{config.product_name} Team</div>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
# <%= d 'ticket.title' %>
|
# #{ticket.title}
|
||||||
_<<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>|Ticket#<%= d 'ticket.number' %>>: Created by <%= d 'current_user.longname' %> at <%= d 'ticket.updated_at' %>_
|
_<#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}|Ticket##{ticket.number}>: Created by #{current_user.longname} at #{ticket.updated_at}_
|
||||||
* <%= t 'Group' %>: <%= d 'ticket.group.name' %>
|
* #{t('Group')}: #{ticket.group.name}
|
||||||
* <%= t 'Owner' %>: <%= d 'ticket.owner.fullname' %>
|
* #{t('Owner')}: #{ticket.owner.fullname}
|
||||||
* <%= t 'State' %>: <%= t d 'ticket.state.name' %>
|
* #{t('State')}: #{t(ticket.state.name)}
|
||||||
|
|
||||||
<% if @objects[:article] %>
|
<% if @objects[:article] %>
|
||||||
<%= a_text 'article' %>
|
#{article.body_as_text}
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# <%= d 'ticket.title' %>
|
# #{ticket.title}
|
||||||
_<<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>|Ticket#<%= d 'ticket.number' %>>: Escalated at <%= d 'ticket.escalation_at' %>_
|
_<#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}|Ticket##{ticket.number}>: Escalated at #{ticket.escalation_at}_
|
||||||
A ticket (<%= d 'ticket.title' %>) from "<%= d 'ticket.customer.longname' %>" is escalated since "<%= d 'ticket.escalation_at' %>"!
|
A ticket (#{ticket.title}) from "#{ticket.customer.longname}" is escalated since "#{ticket.escalation_at}"!
|
||||||
|
|
||||||
<% if @objects[:article] %>
|
<% if @objects[:article] %>
|
||||||
<%= a_text 'article' %>
|
#{article.body_as_text}
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# <%= d 'ticket.title' %>
|
# #{ticket.title}
|
||||||
_<<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>|Ticket#<%= d 'ticket.number' %>>: Will escalate at <%= d 'ticket.escalation_at' %>_
|
_<#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}|Ticket##{ticket.number}>: Will escalate at #{ticket.escalation_at}_
|
||||||
A ticket (<%= d 'ticket.title' %>) from "<%= d 'ticket.customer.longname' %>" will escalate at "<%= d 'ticket.escalation_at' %>"!
|
A ticket (#{ticket.title}) from "#{ticket.customer.longname}" will escalate at "#{ticket.escalation_at}"!
|
||||||
|
|
||||||
<% if @objects[:article] %>
|
<% if @objects[:article] %>
|
||||||
<%= a_text 'article' %>
|
#{article.body_as_text}
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# <%= d 'ticket.title' %>
|
# #{ticket.title}
|
||||||
_<<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>|Ticket#<%= d 'ticket.number' %>>: Reminder reached!_
|
_<#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}|Ticket##{ticket.number}>: Reminder reached!_
|
||||||
A ticket needs attention, reminder reached for (<%= d 'ticket.title' %>) with customer "*<%= d 'ticket.customer.longname' %>*".
|
A ticket needs attention, reminder reached for (#{ticket.title}) with customer "*#{ticket.customer.longname}*".
|
||||||
|
|
||||||
<% if @objects[:article] %>
|
<% if @objects[:article] %>
|
||||||
<%= a_text 'article' %>
|
#{article.body_as_text}
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# <%= d 'ticket.title' %>
|
# #{ticket.title}
|
||||||
_<<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>|Ticket#<%= d 'ticket.number' %>>: Updated by <%= d 'current_user.longname' %> at <%= d 'ticket.updated_at' %>_
|
_<#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}|Ticket##{ticket.number}>: Updated by #{current_user.longname} at #{ticket.updated_at}_
|
||||||
<% if @objects[:changes] && !@objects[:changes].empty? %>
|
<% if @objects[:changes] && !@objects[:changes].empty? %>
|
||||||
<% @objects[:changes].each do |key, value| %>
|
<% @objects[:changes].each do |key, value| %>
|
||||||
* <%= t key %>: <%= h value[0] %> -> <%= h value[1] %>
|
* <%= t key %>: <%= h value[0] %> -> <%= h value[1] %>
|
||||||
|
@ -7,5 +7,5 @@ _<<%= c 'http_type' %>://<%= c 'fqdn' %>/#ticket/zoom/<%= d 'ticket.id' %>|Ticke
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if @objects[:article] %>
|
<% if @objects[:article] %>
|
||||||
<%= a_text 'article' %>
|
#{article.body_as_text}
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
112
config/initializers/doorkeeper.rb
Normal file
112
config/initializers/doorkeeper.rb
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
Doorkeeper.configure do
|
||||||
|
# Change the ORM that doorkeeper will use (needs plugins)
|
||||||
|
orm :active_record
|
||||||
|
|
||||||
|
# This block will be called to check whether the resource owner is authenticated or not.
|
||||||
|
resource_owner_authenticator do
|
||||||
|
# fail "Please configure doorkeeper resource_owner_authenticator block located in #{__FILE__}"
|
||||||
|
# Put your resource owner authentication logic here.
|
||||||
|
# Example implementation:
|
||||||
|
User.find_by_id(session[:user_id]) || redirect_to(new_user_session_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
# If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.
|
||||||
|
# admin_authenticator do
|
||||||
|
# # Put your admin authentication logic here.
|
||||||
|
# # Example implementation:
|
||||||
|
# Admin.find_by_id(session[:admin_id]) || redirect_to(new_admin_session_url)
|
||||||
|
# end
|
||||||
|
|
||||||
|
# Authorization Code expiration time (default 10 minutes).
|
||||||
|
# authorization_code_expires_in 10.minutes
|
||||||
|
|
||||||
|
# Access token expiration time (default 2 hours).
|
||||||
|
# If you want to disable expiration, set this to nil.
|
||||||
|
# access_token_expires_in 2.hours
|
||||||
|
|
||||||
|
# Assign a custom TTL for implicit grants.
|
||||||
|
# custom_access_token_expires_in do |oauth_client|
|
||||||
|
# oauth_client.application.additional_settings.implicit_oauth_expiration
|
||||||
|
# end
|
||||||
|
|
||||||
|
# Use a custom class for generating the access token.
|
||||||
|
# https://github.com/doorkeeper-gem/doorkeeper#custom-access-token-generator
|
||||||
|
# access_token_generator '::Doorkeeper::JWT'
|
||||||
|
|
||||||
|
# The controller Doorkeeper::ApplicationController inherits from.
|
||||||
|
# Defaults to ActionController::Base.
|
||||||
|
# https://github.com/doorkeeper-gem/doorkeeper#custom-base-controller
|
||||||
|
# base_controller 'ApplicationController'
|
||||||
|
|
||||||
|
# Reuse access token for the same resource owner within an application (disabled by default)
|
||||||
|
# Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383
|
||||||
|
# reuse_access_token
|
||||||
|
|
||||||
|
# Issue access tokens with refresh token (disabled by default)
|
||||||
|
# use_refresh_token
|
||||||
|
|
||||||
|
# Provide support for an owner to be assigned to each registered application (disabled by default)
|
||||||
|
# Optional parameter confirmation: true (default false) if you want to enforce ownership of
|
||||||
|
# a registered application
|
||||||
|
# Note: you must also run the rails g doorkeeper:application_owner generator to provide the necessary support
|
||||||
|
# enable_application_owner confirmation: false
|
||||||
|
|
||||||
|
# Define access token scopes for your provider
|
||||||
|
# For more information go to
|
||||||
|
# https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
|
||||||
|
# default_scopes :public
|
||||||
|
# optional_scopes :write, :update
|
||||||
|
|
||||||
|
# Change the way client credentials are retrieved from the request object.
|
||||||
|
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
|
||||||
|
# falls back to the `:client_id` and `:client_secret` params from the `params` object.
|
||||||
|
# Check out the wiki for more information on customization
|
||||||
|
# client_credentials :from_basic, :from_params
|
||||||
|
|
||||||
|
# Change the way access token is authenticated from the request object.
|
||||||
|
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
|
||||||
|
# falls back to the `:access_token` or `:bearer_token` params from the `params` object.
|
||||||
|
# Check out the wiki for more information on customization
|
||||||
|
# access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param
|
||||||
|
|
||||||
|
# Change the native redirect uri for client apps
|
||||||
|
# When clients register with the following redirect uri, they won't be redirected to any server and the authorization code will be displayed within the provider
|
||||||
|
# The value can be any string. Use nil to disable this feature. When disabled, clients must provide a valid URL
|
||||||
|
# (Similar behaviour: https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi)
|
||||||
|
#
|
||||||
|
# native_redirect_uri 'urn:ietf:wg:oauth:2.0:oob'
|
||||||
|
|
||||||
|
# Forces the usage of the HTTPS protocol in non-native redirect uris (enabled
|
||||||
|
# by default in non-development environments). OAuth2 delegates security in
|
||||||
|
# communication to the HTTPS protocol so it is wise to keep this enabled.
|
||||||
|
#
|
||||||
|
# force_ssl_in_redirect_uri !Rails.env.development?
|
||||||
|
|
||||||
|
# Specify what grant flows are enabled in array of Strings. The valid
|
||||||
|
# strings and the flows they enable are:
|
||||||
|
#
|
||||||
|
# "authorization_code" => Authorization Code Grant Flow
|
||||||
|
# "implicit" => Implicit Grant Flow
|
||||||
|
# "password" => Resource Owner Password Credentials Grant Flow
|
||||||
|
# "client_credentials" => Client Credentials Grant Flow
|
||||||
|
#
|
||||||
|
# If not specified, Doorkeeper enables authorization_code and
|
||||||
|
# client_credentials.
|
||||||
|
#
|
||||||
|
# implicit and password grant flows have risks that you should understand
|
||||||
|
# before enabling:
|
||||||
|
# http://tools.ietf.org/html/rfc6819#section-4.4.2
|
||||||
|
# http://tools.ietf.org/html/rfc6819#section-4.4.3
|
||||||
|
#
|
||||||
|
# grant_flows %w(authorization_code client_credentials)
|
||||||
|
|
||||||
|
# Under some circumstances you might want to have applications auto-approved,
|
||||||
|
# so that the user skips the authorization step.
|
||||||
|
# For example if dealing with a trusted application.
|
||||||
|
# skip_authorization do |resource_owner, client|
|
||||||
|
# client.superapp? or resource_owner.admin?
|
||||||
|
# end
|
||||||
|
|
||||||
|
# WWW-Authenticate Realm (default "Doorkeeper").
|
||||||
|
# realm "Doorkeeper"
|
||||||
|
end
|
124
config/locales/doorkeeper.en.yml
Normal file
124
config/locales/doorkeeper.en.yml
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
en:
|
||||||
|
activerecord:
|
||||||
|
attributes:
|
||||||
|
doorkeeper/application:
|
||||||
|
name: 'Name'
|
||||||
|
redirect_uri: 'Redirect URI'
|
||||||
|
errors:
|
||||||
|
models:
|
||||||
|
doorkeeper/application:
|
||||||
|
attributes:
|
||||||
|
redirect_uri:
|
||||||
|
fragment_present: 'cannot contain a fragment.'
|
||||||
|
invalid_uri: 'must be a valid URI.'
|
||||||
|
relative_uri: 'must be an absolute URI.'
|
||||||
|
secured_uri: 'must be an HTTPS/SSL URI.'
|
||||||
|
|
||||||
|
doorkeeper:
|
||||||
|
applications:
|
||||||
|
confirmations:
|
||||||
|
destroy: 'Are you sure?'
|
||||||
|
buttons:
|
||||||
|
edit: 'Edit'
|
||||||
|
destroy: 'Destroy'
|
||||||
|
submit: 'Submit'
|
||||||
|
cancel: 'Cancel'
|
||||||
|
authorize: 'Authorize'
|
||||||
|
form:
|
||||||
|
error: 'Whoops! Check your form for possible errors'
|
||||||
|
help:
|
||||||
|
redirect_uri: 'Use one line per URI'
|
||||||
|
native_redirect_uri: 'Use %{native_redirect_uri} for local tests'
|
||||||
|
scopes: 'Separate scopes with spaces. Leave blank to use the default scopes.'
|
||||||
|
edit:
|
||||||
|
title: 'Edit application'
|
||||||
|
index:
|
||||||
|
title: 'Your applications'
|
||||||
|
new: 'New Application'
|
||||||
|
name: 'Name'
|
||||||
|
callback_url: 'Callback URL'
|
||||||
|
new:
|
||||||
|
title: 'New Application'
|
||||||
|
show:
|
||||||
|
title: 'Application: %{name}'
|
||||||
|
application_id: 'Application Id'
|
||||||
|
secret: 'Secret'
|
||||||
|
scopes: 'Scopes'
|
||||||
|
callback_urls: 'Callback urls'
|
||||||
|
actions: 'Actions'
|
||||||
|
|
||||||
|
authorizations:
|
||||||
|
buttons:
|
||||||
|
authorize: 'Authorize'
|
||||||
|
deny: 'Deny'
|
||||||
|
error:
|
||||||
|
title: 'An error has occurred'
|
||||||
|
new:
|
||||||
|
title: 'Authorization required'
|
||||||
|
prompt: 'Authorize %{client_name} to use your account?'
|
||||||
|
able_to: 'This application will be able to'
|
||||||
|
show:
|
||||||
|
title: 'Authorization code'
|
||||||
|
|
||||||
|
authorized_applications:
|
||||||
|
confirmations:
|
||||||
|
revoke: 'Are you sure?'
|
||||||
|
buttons:
|
||||||
|
revoke: 'Revoke'
|
||||||
|
index:
|
||||||
|
title: 'Your authorized applications'
|
||||||
|
application: 'Application'
|
||||||
|
created_at: 'Created At'
|
||||||
|
date_format: '%Y-%m-%d %H:%M:%S'
|
||||||
|
|
||||||
|
errors:
|
||||||
|
messages:
|
||||||
|
# Common error messages
|
||||||
|
invalid_request: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.'
|
||||||
|
invalid_redirect_uri: 'The redirect uri included is not valid.'
|
||||||
|
unauthorized_client: 'The client is not authorized to perform this request using this method.'
|
||||||
|
access_denied: 'The resource owner or authorization server denied the request.'
|
||||||
|
invalid_scope: 'The requested scope is invalid, unknown, or malformed.'
|
||||||
|
server_error: 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.'
|
||||||
|
temporarily_unavailable: 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.'
|
||||||
|
|
||||||
|
#configuration error messages
|
||||||
|
credential_flow_not_configured: 'Resource Owner Password Credentials flow failed due to Doorkeeper.configure.resource_owner_from_credentials being unconfigured.'
|
||||||
|
resource_owner_authenticator_not_configured: 'Resource Owner find failed due to Doorkeeper.configure.resource_owner_authenticator being unconfiged.'
|
||||||
|
|
||||||
|
# Access grant errors
|
||||||
|
unsupported_response_type: 'The authorization server does not support this response type.'
|
||||||
|
|
||||||
|
# Access token errors
|
||||||
|
invalid_client: 'Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method.'
|
||||||
|
invalid_grant: 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.'
|
||||||
|
unsupported_grant_type: 'The authorization grant type is not supported by the authorization server.'
|
||||||
|
|
||||||
|
# Password Access token errors
|
||||||
|
invalid_resource_owner: 'The provided resource owner credentials are not valid, or resource owner cannot be found'
|
||||||
|
|
||||||
|
invalid_token:
|
||||||
|
revoked: "The access token was revoked"
|
||||||
|
expired: "The access token expired"
|
||||||
|
unknown: "The access token is invalid"
|
||||||
|
|
||||||
|
flash:
|
||||||
|
applications:
|
||||||
|
create:
|
||||||
|
notice: 'Application created.'
|
||||||
|
destroy:
|
||||||
|
notice: 'Application deleted.'
|
||||||
|
update:
|
||||||
|
notice: 'Application updated.'
|
||||||
|
authorized_applications:
|
||||||
|
destroy:
|
||||||
|
notice: 'Application revoked.'
|
||||||
|
|
||||||
|
layouts:
|
||||||
|
admin:
|
||||||
|
nav:
|
||||||
|
oauth2_provider: 'OAuth2 Provider'
|
||||||
|
applications: 'Applications'
|
||||||
|
home: 'Home'
|
||||||
|
application:
|
||||||
|
title: 'OAuth authorization required'
|
14
config/routes/applications.rb
Normal file
14
config/routes/applications.rb
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
Zammad::Application.routes.draw do
|
||||||
|
api_path = Rails.configuration.api_path
|
||||||
|
|
||||||
|
match api_path + '/applications', to: 'applications#index', via: :get
|
||||||
|
match api_path + '/applications/:id', to: 'applications#show', via: :get
|
||||||
|
match api_path + '/applications', to: 'applications#create', via: :post
|
||||||
|
match api_path + '/applications/:id', to: 'applications#update', via: :put
|
||||||
|
match api_path + '/applications/token', to: 'applications#token', via: :post
|
||||||
|
|
||||||
|
# oauth2 provider routes
|
||||||
|
use_doorkeeper do
|
||||||
|
skip_controllers :applications, :authorized_applications
|
||||||
|
end
|
||||||
|
end
|
|
@ -125,6 +125,8 @@ class CreateBase < ActiveRecord::Migration
|
||||||
create_table :organizations do |t|
|
create_table :organizations do |t|
|
||||||
t.string :name, limit: 100, null: false
|
t.string :name, limit: 100, null: false
|
||||||
t.boolean :shared, null: false, default: true
|
t.boolean :shared, null: false, default: true
|
||||||
|
t.string :domain, limit: 250, null: true, default: ''
|
||||||
|
t.boolean :domain_assignment, null: false, default: false
|
||||||
t.boolean :active, null: false, default: true
|
t.boolean :active, null: false, default: true
|
||||||
t.string :note, limit: 250, null: true, default: ''
|
t.string :note, limit: 250, null: true, default: ''
|
||||||
t.integer :updated_by_id, null: false
|
t.integer :updated_by_id, null: false
|
||||||
|
@ -132,6 +134,7 @@ class CreateBase < ActiveRecord::Migration
|
||||||
t.timestamps limit: 3, null: false
|
t.timestamps limit: 3, null: false
|
||||||
end
|
end
|
||||||
add_index :organizations, [:name], unique: true
|
add_index :organizations, [:name], unique: true
|
||||||
|
add_index :organizations, [:domain]
|
||||||
|
|
||||||
create_table :roles_users, id: false do |t|
|
create_table :roles_users, id: false do |t|
|
||||||
t.integer :user_id
|
t.integer :user_id
|
||||||
|
|
68
db/migrate/20161101131409_create_doorkeeper_tables.rb
Normal file
68
db/migrate/20161101131409_create_doorkeeper_tables.rb
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
class CreateDoorkeeperTables < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
create_table :oauth_applications do |t|
|
||||||
|
t.string :name, null: false
|
||||||
|
t.string :uid, null: false
|
||||||
|
t.string :secret, null: false
|
||||||
|
t.text :redirect_uri, null: false
|
||||||
|
t.string :scopes, null: false, default: ''
|
||||||
|
t.timestamps null: false
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :oauth_applications, :uid, unique: true
|
||||||
|
|
||||||
|
create_table :oauth_access_grants do |t|
|
||||||
|
t.integer :resource_owner_id, null: false
|
||||||
|
t.references :application, null: false
|
||||||
|
t.string :token, null: false
|
||||||
|
t.integer :expires_in, null: false
|
||||||
|
t.text :redirect_uri, null: false
|
||||||
|
t.datetime :created_at, null: false
|
||||||
|
t.datetime :revoked_at
|
||||||
|
t.string :scopes
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :oauth_access_grants, :token, unique: true
|
||||||
|
add_foreign_key(
|
||||||
|
:oauth_access_grants,
|
||||||
|
:oauth_applications,
|
||||||
|
column: :application_id
|
||||||
|
)
|
||||||
|
|
||||||
|
create_table :oauth_access_tokens do |t|
|
||||||
|
t.integer :resource_owner_id
|
||||||
|
t.references :application
|
||||||
|
|
||||||
|
# If you use a custom token generator you may need to change this column
|
||||||
|
# from string to text, so that it accepts tokens larger than 255
|
||||||
|
# characters. More info on custom token generators in:
|
||||||
|
# https://github.com/doorkeeper-gem/doorkeeper/tree/v3.0.0.rc1#custom-access-token-generator
|
||||||
|
#
|
||||||
|
# t.text :token, null: false
|
||||||
|
t.string :token, null: false
|
||||||
|
|
||||||
|
t.string :refresh_token
|
||||||
|
t.integer :expires_in
|
||||||
|
t.datetime :revoked_at
|
||||||
|
t.datetime :created_at, null: false
|
||||||
|
t.string :scopes
|
||||||
|
|
||||||
|
# If there is a previous_refresh_token column,
|
||||||
|
# refresh tokens will be revoked after a related access token is used.
|
||||||
|
# If there is no previous_refresh_token column,
|
||||||
|
# previous tokens are revoked as soon as a new access token is created.
|
||||||
|
# Comment out this line if you'd rather have refresh tokens
|
||||||
|
# instantly revoked.
|
||||||
|
t.string :previous_refresh_token, null: false, default: ''
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :oauth_access_tokens, :token, unique: true
|
||||||
|
add_index :oauth_access_tokens, :resource_owner_id
|
||||||
|
add_index :oauth_access_tokens, :refresh_token, unique: true
|
||||||
|
add_foreign_key(
|
||||||
|
:oauth_access_tokens,
|
||||||
|
:oauth_applications,
|
||||||
|
column: :application_id
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,85 @@
|
||||||
|
class OrganizationDomainBasedAssignment < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
# return if it's a new setup
|
||||||
|
return if !Setting.find_by(name: 'system_init_done')
|
||||||
|
|
||||||
|
add_column :organizations, :domain, :string, limit: 250, null: true, default: ''
|
||||||
|
add_column :organizations, :domain_assignment, :boolean, null: false, default: false
|
||||||
|
add_index :organizations, [:domain]
|
||||||
|
|
||||||
|
ObjectManager::Attribute.add(
|
||||||
|
force: true,
|
||||||
|
object: 'Organization',
|
||||||
|
name: 'domain_assignment',
|
||||||
|
display: 'Domain based assignment',
|
||||||
|
data_type: 'boolean',
|
||||||
|
data_option: {
|
||||||
|
null: true,
|
||||||
|
default: false,
|
||||||
|
note: 'Assign Users based on users domain.',
|
||||||
|
item_class: 'formGroup--halfSize',
|
||||||
|
options: {
|
||||||
|
true: 'yes',
|
||||||
|
false: 'no',
|
||||||
|
},
|
||||||
|
translate: true,
|
||||||
|
},
|
||||||
|
editable: false,
|
||||||
|
active: true,
|
||||||
|
screens: {
|
||||||
|
edit: {
|
||||||
|
Admin: {
|
||||||
|
null: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
'-all-' => {
|
||||||
|
shown: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
to_create: false,
|
||||||
|
to_migrate: false,
|
||||||
|
to_delete: false,
|
||||||
|
position: 1410,
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
ObjectManager::Attribute.add(
|
||||||
|
force: true,
|
||||||
|
object: 'Organization',
|
||||||
|
name: 'domain',
|
||||||
|
display: 'Domain',
|
||||||
|
data_type: 'input',
|
||||||
|
data_option: {
|
||||||
|
type: 'text',
|
||||||
|
maxlength: 150,
|
||||||
|
null: true,
|
||||||
|
item_class: 'formGroup--halfSize',
|
||||||
|
},
|
||||||
|
editable: false,
|
||||||
|
active: true,
|
||||||
|
screens: {
|
||||||
|
edit: {
|
||||||
|
'-all-' => {
|
||||||
|
null: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
'-all-' => {
|
||||||
|
shown: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
to_create: false,
|
||||||
|
to_migrate: false,
|
||||||
|
to_delete: false,
|
||||||
|
position: 1420,
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
Cache.clear
|
||||||
|
end
|
||||||
|
end
|
69
db/seeds.rb
69
db/seeds.rb
|
@ -4660,6 +4660,75 @@ ObjectManager::Attribute.add(
|
||||||
position: 1400,
|
position: 1400,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ObjectManager::Attribute.add(
|
||||||
|
force: true,
|
||||||
|
object: 'Organization',
|
||||||
|
name: 'domain_assignment',
|
||||||
|
display: 'Domain based assignment',
|
||||||
|
data_type: 'boolean',
|
||||||
|
data_option: {
|
||||||
|
null: true,
|
||||||
|
default: false,
|
||||||
|
note: 'Assign Users based on users domain.',
|
||||||
|
item_class: 'formGroup--halfSize',
|
||||||
|
options: {
|
||||||
|
true: 'yes',
|
||||||
|
false: 'no',
|
||||||
|
},
|
||||||
|
translate: true,
|
||||||
|
},
|
||||||
|
editable: false,
|
||||||
|
active: true,
|
||||||
|
screens: {
|
||||||
|
edit: {
|
||||||
|
Admin: {
|
||||||
|
null: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
'-all-' => {
|
||||||
|
shown: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
to_create: false,
|
||||||
|
to_migrate: false,
|
||||||
|
to_delete: false,
|
||||||
|
position: 1410,
|
||||||
|
)
|
||||||
|
|
||||||
|
ObjectManager::Attribute.add(
|
||||||
|
force: true,
|
||||||
|
object: 'Organization',
|
||||||
|
name: 'domain',
|
||||||
|
display: 'Domain',
|
||||||
|
data_type: 'input',
|
||||||
|
data_option: {
|
||||||
|
type: 'text',
|
||||||
|
maxlength: 150,
|
||||||
|
null: true,
|
||||||
|
item_class: 'formGroup--halfSize',
|
||||||
|
},
|
||||||
|
editable: false,
|
||||||
|
active: true,
|
||||||
|
screens: {
|
||||||
|
edit: {
|
||||||
|
'-all-' => {
|
||||||
|
null: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
'-all-' => {
|
||||||
|
shown: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
to_create: false,
|
||||||
|
to_migrate: false,
|
||||||
|
to_delete: false,
|
||||||
|
position: 1420,
|
||||||
|
)
|
||||||
|
|
||||||
ObjectManager::Attribute.add(
|
ObjectManager::Attribute.add(
|
||||||
force: true,
|
force: true,
|
||||||
object: 'Organization',
|
object: 'Organization',
|
||||||
|
|
|
@ -6,7 +6,7 @@ get notification settings for user and notification type
|
||||||
|
|
||||||
result = NotificationFactory::Mailer.notification_settings(user, ticket, type)
|
result = NotificationFactory::Mailer.notification_settings(user, ticket, type)
|
||||||
|
|
||||||
type: create | update | reminder_reached | pending
|
type: create | update | reminder_reached | escalation (escalation_warning)
|
||||||
|
|
||||||
returns
|
returns
|
||||||
|
|
||||||
|
@ -21,6 +21,15 @@ returns
|
||||||
=end
|
=end
|
||||||
|
|
||||||
def self.notification_settings(user, ticket, type)
|
def self.notification_settings(user, ticket, type)
|
||||||
|
|
||||||
|
# map types if needed
|
||||||
|
map = {
|
||||||
|
'escalation_warning' => 'escalation'
|
||||||
|
}
|
||||||
|
if map[type]
|
||||||
|
type = map[type]
|
||||||
|
end
|
||||||
|
|
||||||
return if !user.preferences
|
return if !user.preferences
|
||||||
return if !user.preferences['notification_config']
|
return if !user.preferences['notification_config']
|
||||||
matrix = user.preferences['notification_config']['matrix']
|
matrix = user.preferences['notification_config']['matrix']
|
||||||
|
@ -190,11 +199,12 @@ retunes
|
||||||
)
|
)
|
||||||
|
|
||||||
result = NotificationFactory::Mailer.template(
|
result = NotificationFactory::Mailer.template(
|
||||||
templateInline: "Invitation to <%= c 'product_name' %> at <%= c 'fqdn' %>",
|
templateInline: "Invitation to \#{config.product_name} at \#{config.fqdn}",
|
||||||
locale: 'en-us',
|
locale: 'en-us',
|
||||||
objects: {
|
objects: {
|
||||||
recipient: User.find(2),
|
recipient: User.find(2),
|
||||||
},
|
},
|
||||||
|
quote: true, # html quoting
|
||||||
)
|
)
|
||||||
|
|
||||||
only raw subject/body
|
only raw subject/body
|
||||||
|
@ -221,7 +231,7 @@ returns
|
||||||
def self.template(data)
|
def self.template(data)
|
||||||
|
|
||||||
if data[:templateInline]
|
if data[:templateInline]
|
||||||
return NotificationFactory::Renderer.new(data[:objects], data[:locale], data[:templateInline], false).render
|
return NotificationFactory::Renderer.new(data[:objects], data[:locale], data[:templateInline], data[:quote]).render
|
||||||
end
|
end
|
||||||
|
|
||||||
template = NotificationFactory.template_read(
|
template = NotificationFactory.template_read(
|
||||||
|
|
|
@ -9,7 +9,7 @@ examples how to use
|
||||||
ticket: Ticket.first,
|
ticket: Ticket.first,
|
||||||
},
|
},
|
||||||
'de-de',
|
'de-de',
|
||||||
'some template <b><%= d "ticket.title", false %></b> <%= c "fqdn", false %>',
|
'some template <b>#{ticket.title}</b> {config.fqdn}',
|
||||||
false
|
false
|
||||||
).render
|
).render
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ examples how to use
|
||||||
ticket: Ticket.first,
|
ticket: Ticket.first,
|
||||||
},
|
},
|
||||||
'de-de',
|
'de-de',
|
||||||
'some template <b><%= d "ticket.title", true %></b> <%= c "fqdn", true %>',
|
'some template <b>#{ticket.title}</b> #{config.fqdn}',
|
||||||
).render
|
).render
|
||||||
|
|
||||||
=end
|
=end
|
||||||
|
@ -26,7 +26,7 @@ examples how to use
|
||||||
def initialize(objects, locale, template, escape = true)
|
def initialize(objects, locale, template, escape = true)
|
||||||
@objects = objects
|
@objects = objects
|
||||||
@locale = locale || 'en-us'
|
@locale = locale || 'en-us'
|
||||||
@template = NotificationFactory::Template.new(template)
|
@template = NotificationFactory::Template.new(template, escape)
|
||||||
@escape = escape
|
@escape = escape
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -41,6 +41,25 @@ examples how to use
|
||||||
# do validaton, ignore some methodes
|
# do validaton, ignore some methodes
|
||||||
return "\#{#{key} / not allowed}" if !data_key_valid?(key)
|
return "\#{#{key} / not allowed}" if !data_key_valid?(key)
|
||||||
|
|
||||||
|
# aliases
|
||||||
|
map = {
|
||||||
|
'article.body' => 'article.body_as_text_with_quote.text2html',
|
||||||
|
}
|
||||||
|
if map[key]
|
||||||
|
key = map[key]
|
||||||
|
end
|
||||||
|
|
||||||
|
# escape in html mode
|
||||||
|
if escape
|
||||||
|
no_escape = {
|
||||||
|
'article.body_as_html' => true,
|
||||||
|
'article.body_as_text_with_quote.text2html' => true,
|
||||||
|
}
|
||||||
|
if no_escape[key]
|
||||||
|
escape = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
value = nil
|
value = nil
|
||||||
object_methods = key.split('.')
|
object_methods = key.split('.')
|
||||||
object_name = object_methods.shift
|
object_name = object_methods.shift
|
||||||
|
@ -76,7 +95,11 @@ examples how to use
|
||||||
value = "\#{#{object_name}.#{object_methods_s} / no such method}"
|
value = "\#{#{object_name}.#{object_methods_s} / no such method}"
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
object_refs = object_refs.send(method.to_sym)
|
begin
|
||||||
|
object_refs = object_refs.send(method.to_sym)
|
||||||
|
rescue => e
|
||||||
|
object_refs = "\#{#{object_name}.#{object_methods_s} / e.message}"
|
||||||
|
end
|
||||||
}
|
}
|
||||||
placeholder = if !value
|
placeholder = if !value
|
||||||
object_refs
|
object_refs
|
||||||
|
@ -100,27 +123,6 @@ examples how to use
|
||||||
escaping(translation, escape)
|
escaping(translation, escape)
|
||||||
end
|
end
|
||||||
|
|
||||||
# a_html - article body in html
|
|
||||||
# a_html(article)
|
|
||||||
def a_html(article)
|
|
||||||
content_type = d "#{article}.content_type", false
|
|
||||||
if content_type =~ /html/
|
|
||||||
return d "#{article}.body", false
|
|
||||||
end
|
|
||||||
d("#{article}.body", false).text2html
|
|
||||||
end
|
|
||||||
|
|
||||||
# a_text - article body in text
|
|
||||||
# a_text(article)
|
|
||||||
def a_text(article)
|
|
||||||
content_type = d "#{article}.content_type", false
|
|
||||||
body = d "#{article}.body", false
|
|
||||||
if content_type =~ /html/
|
|
||||||
body = body.html2text
|
|
||||||
end
|
|
||||||
(body.strip + "\n").gsub(/^(.*?)$/, '> \\1')
|
|
||||||
end
|
|
||||||
|
|
||||||
# h - htmlEscape
|
# h - htmlEscape
|
||||||
# h('fqdn', htmlEscape)
|
# h('fqdn', htmlEscape)
|
||||||
def h(key)
|
def h(key)
|
||||||
|
@ -137,7 +139,7 @@ examples how to use
|
||||||
end
|
end
|
||||||
|
|
||||||
def data_key_valid?(key)
|
def data_key_valid?(key)
|
||||||
return false if key =~ /`|\.(|\s*)(save|destroy|delete|remove|drop|update|create|new|all|where|find)/i
|
return false if key =~ /`|\.(|\s*)(save|destroy|delete|remove|drop|update|create|new|all|where|find)/i && key !~ /(update|create)d_at/i
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,8 @@ returns
|
||||||
type: 'slack',
|
type: 'slack',
|
||||||
)
|
)
|
||||||
|
|
||||||
message_subject = NotificationFactory::Renderer.new(data[:objects], data[:locale], template[:subject]).render
|
message_subject = NotificationFactory::Renderer.new(data[:objects], data[:locale], template[:subject], false).render
|
||||||
message_body = NotificationFactory::Renderer.new(data[:objects], data[:locale], template[:body]).render
|
message_body = NotificationFactory::Renderer.new(data[:objects], data[:locale], template[:body], false).render
|
||||||
|
|
||||||
if !data[:raw]
|
if !data[:raw]
|
||||||
application_template = NotificationFactory.application_template_read(
|
application_template = NotificationFactory.application_template_read(
|
||||||
|
@ -43,7 +43,7 @@ returns
|
||||||
)
|
)
|
||||||
data[:objects][:message] = message_body
|
data[:objects][:message] = message_body
|
||||||
data[:objects][:standalone] = data[:standalone]
|
data[:objects][:standalone] = data[:standalone]
|
||||||
message_body = NotificationFactory::Renderer.new(data[:objects], data[:locale], application_template).render
|
message_body = NotificationFactory::Renderer.new(data[:objects], data[:locale], application_template, false).render
|
||||||
end
|
end
|
||||||
{
|
{
|
||||||
subject: message_subject.strip!,
|
subject: message_subject.strip!,
|
||||||
|
|
|
@ -5,13 +5,15 @@ class NotificationFactory::Template
|
||||||
examples how to use
|
examples how to use
|
||||||
|
|
||||||
cleaned_template = NotificationFactory::Template.new(
|
cleaned_template = NotificationFactory::Template.new(
|
||||||
'some template <b><%= d "ticket.title", false %></b> <%= c "fqdn", false %>',
|
'some template <b>#{ticket.title}</b> #{config.fqdn}',
|
||||||
|
true,
|
||||||
).to_s
|
).to_s
|
||||||
|
|
||||||
=end
|
=end
|
||||||
|
|
||||||
def initialize(template)
|
def initialize(template, escape)
|
||||||
@template = template
|
@template = template
|
||||||
|
@escape = escape
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
|
@ -23,7 +25,35 @@ examples how to use
|
||||||
def strip_html
|
def strip_html
|
||||||
# some browsers start adding HTML tags
|
# some browsers start adding HTML tags
|
||||||
# fixes https://github.com/zammad/zammad/issues/385
|
# fixes https://github.com/zammad/zammad/issues/385
|
||||||
@template.gsub!(%r{#\{\s*<[^>]+>([^<]+)</[^>]+>\s*\}}, '\1')
|
@template.gsub!(/\#\{\s*t\((.+?)\)\s*\}/m) do
|
||||||
@template.gsub!(/#\{\s*<[^>]+>([^<]+)\s*\}/, '\1')
|
content = $1
|
||||||
|
if content =~ /^'(.+?)'$/
|
||||||
|
"<%= t \"#{strip_content($1)}\", #{@escape} %>"
|
||||||
|
else
|
||||||
|
"<%= t d\"#{strip_variable(content)}\", #{@escape} %>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@template.gsub!(/\#\{\s*config\.(.+?)\s*\}/m) do
|
||||||
|
"<%= c \"#{strip_variable($1)}\", #{@escape} %>"
|
||||||
|
end
|
||||||
|
@template.gsub!(/\#\{(.*?)\}/m) do
|
||||||
|
"<%= d \"#{strip_variable($1)}\", #{@escape} %>"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def strip_content(string)
|
||||||
|
return string if !string
|
||||||
|
string.gsub!(/\t|\r|\n/, '')
|
||||||
|
string.gsub!(/"/, '\"')
|
||||||
|
string
|
||||||
|
end
|
||||||
|
|
||||||
|
def strip_variable(string)
|
||||||
|
return string if !string
|
||||||
|
string.gsub!(/\t|\r|\n|"|'|§|;/, '')
|
||||||
|
string.gsub!(/\s*/, '')
|
||||||
|
string.gsub!(/<.+?>/, '')
|
||||||
|
string
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,29 +3,40 @@ require 'test_helper'
|
||||||
|
|
||||||
class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
# TODO: should be mocked somehow
|
|
||||||
Translation.load('de-de')
|
|
||||||
|
|
||||||
# RSpec incoming!
|
# RSpec incoming!
|
||||||
def described_class
|
def described_class
|
||||||
NotificationFactory::Renderer
|
NotificationFactory::Renderer
|
||||||
end
|
end
|
||||||
|
|
||||||
Group = Struct.new(:name)
|
group = Group.new(name: 'Users')
|
||||||
State = Struct.new(:name)
|
owner = User.new(firstname: 'Notification<b>xxx</b>', lastname: 'Agent1<b>yyy</b>')
|
||||||
User = Struct.new(:firstname, :lastname, :longname, :fullname)
|
current_user = User.new(firstname: 'CurrentUser<b>xxx</b>', lastname: 'Agent2<b>yyy</b>')
|
||||||
Ticket = Struct.new(:id, :title, :group, :owner, :state)
|
recipient = User.new(firstname: 'Recipient<b>xxx</b>', lastname: 'Customer1<b>yyy</b>')
|
||||||
|
state = Ticket::State.new(name: 'new')
|
||||||
group = Group.new('Users')
|
ticket = Ticket.new(
|
||||||
state = State.new('new')
|
id: 1,
|
||||||
owner = User.new('Notification<b>xxx</b>', 'Agent1<b>yyy</b>', 'Notification<b>xxx</b> Agent1<b>yyy</b>', 'Notification<b>xxx</b> Agent1<b>yyy</b> (Zammad)')
|
title: '<b>Welcome to Zammad!</b>',
|
||||||
current_user = User.new('CurrentUser<b>xxx</b>', 'Agent2<b>yyy</b>', 'CurrentUser<b>xxx</b> Agent2<b>yyy</b>', 'CurrentUser<b>xxx</b> Agent2<b>yyy</b> (Zammad)')
|
group: group,
|
||||||
recipient = User.new('Recipient<b>xxx</b>', 'Customer1<b>yyy</b>', 'Recipient<b>xxx</b> Customer1<b>yyy</b>', 'Recipient<b>xxx</b> Customer1<b>yyy</b> (Zammad)')
|
owner: owner,
|
||||||
ticket = Ticket.new(1, '<b>Welcome to Zammad!</b>', group, owner, state)
|
state: state,
|
||||||
|
created_at: Time.zone.parse('2016-11-12 12:00:00 UTC'),
|
||||||
|
updated_at: Time.zone.parse('2016-11-12 14:00:00 UTC'),
|
||||||
|
)
|
||||||
|
article_html1 = Ticket::Article.new(
|
||||||
|
body: 'test <b>hello</b><br>some new line',
|
||||||
|
content_type: 'text/html',
|
||||||
|
)
|
||||||
|
article_plain1 = Ticket::Article.new(
|
||||||
|
body: "test <b>hello</b>\nsome new line",
|
||||||
|
content_type: 'text/plain',
|
||||||
|
)
|
||||||
|
article_plain2 = Ticket::Article.new(
|
||||||
|
body: "test <b>hello</b>\nsome new line",
|
||||||
|
)
|
||||||
|
|
||||||
test 'replace object attribute' do
|
test 'replace object attribute' do
|
||||||
|
|
||||||
template = "<%= d 'ticket.title' %>"
|
template = "\#{ticket.title}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -35,7 +46,27 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML(ticket.title), result)
|
assert_equal(CGI.escapeHTML(ticket.title), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket. title' %>"
|
template = "\#{ticket.created_at}"
|
||||||
|
result = described_class.new(
|
||||||
|
{
|
||||||
|
ticket: ticket,
|
||||||
|
},
|
||||||
|
'en-us',
|
||||||
|
template,
|
||||||
|
).render
|
||||||
|
assert_equal(ticket.created_at.to_s, result)
|
||||||
|
|
||||||
|
template = "\#{ticket.updated_at}"
|
||||||
|
result = described_class.new(
|
||||||
|
{
|
||||||
|
ticket: ticket,
|
||||||
|
},
|
||||||
|
'en-us',
|
||||||
|
template,
|
||||||
|
).render
|
||||||
|
assert_equal(ticket.updated_at.to_s, result)
|
||||||
|
|
||||||
|
template = "\#{ticket. title}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -45,7 +76,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML(ticket.title), result)
|
assert_equal(CGI.escapeHTML(ticket.title), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.\n title' %>"
|
template = "\#{ticket.\n title}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -55,7 +86,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML(ticket.title), result)
|
assert_equal(CGI.escapeHTML(ticket.title), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.\t title' %>"
|
template = "\#{ticket.\t title}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -65,7 +96,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML(ticket.title), result)
|
assert_equal(CGI.escapeHTML(ticket.title), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.\t\n title\t' %>"
|
template = "\#{ticket.\t\n title\t}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -75,13 +106,50 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML(ticket.title), result)
|
assert_equal(CGI.escapeHTML(ticket.title), result)
|
||||||
|
|
||||||
|
template = "\#{ticket.\" title\t}"
|
||||||
|
result = described_class.new(
|
||||||
|
{
|
||||||
|
ticket: ticket,
|
||||||
|
},
|
||||||
|
'en-us',
|
||||||
|
template,
|
||||||
|
).render
|
||||||
|
assert_equal(CGI.escapeHTML(ticket.title), result)
|
||||||
|
|
||||||
|
template = "some test<br>\#{article.body}"
|
||||||
|
result = described_class.new(
|
||||||
|
{
|
||||||
|
article: article_html1,
|
||||||
|
},
|
||||||
|
'en-us',
|
||||||
|
template,
|
||||||
|
).render
|
||||||
|
assert_equal('some test<br>> test hello<br>> some new line<br>', result)
|
||||||
|
|
||||||
|
result = described_class.new(
|
||||||
|
{
|
||||||
|
article: article_plain1,
|
||||||
|
},
|
||||||
|
'en-us',
|
||||||
|
template,
|
||||||
|
).render
|
||||||
|
assert_equal('some test<br>> test <b>hello</b><br>> some new line<br>', result)
|
||||||
|
|
||||||
|
result = described_class.new(
|
||||||
|
{
|
||||||
|
article: article_plain2,
|
||||||
|
},
|
||||||
|
'en-us',
|
||||||
|
template,
|
||||||
|
).render
|
||||||
|
assert_equal('some test<br>> test <b>hello</b><br>> some new line<br>', result)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'config' do
|
test 'config' do
|
||||||
|
|
||||||
setting = 'fqdn'
|
setting = 'fqdn'
|
||||||
template = "<%= c '#{setting}' %>"
|
template = "\#{config.#{setting}}"
|
||||||
|
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -89,13 +157,11 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
'en-us',
|
'en-us',
|
||||||
template,
|
template,
|
||||||
).render
|
).render
|
||||||
|
|
||||||
assert_equal(Setting.get(setting), result)
|
assert_equal(Setting.get(setting), result)
|
||||||
|
|
||||||
setting1 = 'fqdn'
|
setting1 = 'fqdn'
|
||||||
setting2 = 'product_name'
|
setting2 = 'product_name'
|
||||||
template = "some <%= c '#{setting1}' %> and <%= c '#{setting2}' %>"
|
template = "some \#{config.#{setting1}} and \#{config.#{setting2}}"
|
||||||
|
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -103,14 +169,25 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
'en-us',
|
'en-us',
|
||||||
template,
|
template,
|
||||||
).render
|
).render
|
||||||
|
assert_equal("some #{Setting.get(setting1)} and #{Setting.get(setting2)}", result)
|
||||||
|
|
||||||
|
setting1 = 'fqdn'
|
||||||
|
setting2 = 'product_name'
|
||||||
|
template = "some \#{ config.#{setting1}} and \#{\tconfig.#{setting2}}"
|
||||||
|
result = described_class.new(
|
||||||
|
{
|
||||||
|
ticket: ticket,
|
||||||
|
},
|
||||||
|
'en-us',
|
||||||
|
template,
|
||||||
|
).render
|
||||||
assert_equal("some #{Setting.get(setting1)} and #{Setting.get(setting2)}", result)
|
assert_equal("some #{Setting.get(setting1)} and #{Setting.get(setting2)}", result)
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'translation' do
|
test 'translation' do
|
||||||
|
|
||||||
template = "<%= t 'new' %>"
|
#template = "<%= t 'new' %>"
|
||||||
|
template = "\#{t('new')}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -118,11 +195,9 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
'de-de',
|
'de-de',
|
||||||
template,
|
template,
|
||||||
).render
|
).render
|
||||||
|
|
||||||
assert_equal('neu', result)
|
assert_equal('neu', result)
|
||||||
|
|
||||||
template = "some text <%= t 'new' %> and <%= t 'open' %>"
|
template = "some text \#{t('new')} and \#{t('open')}"
|
||||||
|
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -130,14 +205,33 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
'de-de',
|
'de-de',
|
||||||
template,
|
template,
|
||||||
).render
|
).render
|
||||||
|
assert_equal('some text neu and offen', result)
|
||||||
|
|
||||||
|
template = "some text \#{t('new') } and \#{ t('open')}"
|
||||||
|
result = described_class.new(
|
||||||
|
{
|
||||||
|
ticket: ticket,
|
||||||
|
},
|
||||||
|
'de-de',
|
||||||
|
template,
|
||||||
|
).render
|
||||||
|
assert_equal('some text neu and offen', result)
|
||||||
|
|
||||||
|
template = "some text \#{\nt('new') } and \#{ t('open')\t}"
|
||||||
|
result = described_class.new(
|
||||||
|
{
|
||||||
|
ticket: ticket,
|
||||||
|
},
|
||||||
|
'de-de',
|
||||||
|
template,
|
||||||
|
).render
|
||||||
assert_equal('some text neu and offen', result)
|
assert_equal('some text neu and offen', result)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'chained function calls' do
|
test 'chained function calls' do
|
||||||
|
|
||||||
template = "<%= t d 'ticket.state.name' %>"
|
template = "\#{t(ticket.state.name)}"
|
||||||
|
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
|
@ -152,7 +246,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
test 'not existing object and attribute' do
|
test 'not existing object and attribute' do
|
||||||
|
|
||||||
template = "<%= d '' %>"
|
template = "\#{}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -162,7 +256,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{no such object}'), result)
|
assert_equal(CGI.escapeHTML('#{no such object}'), result)
|
||||||
|
|
||||||
template = "<%= d 'notexsiting.notexsiting' %>"
|
template = "\#{notexsiting.notexsiting}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -172,7 +266,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{notexsiting / no such object}'), result)
|
assert_equal(CGI.escapeHTML('#{notexsiting / no such object}'), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.notexsiting' %>"
|
template = "\#{ticket.notexsiting}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -182,7 +276,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{ticket.notexsiting / no such method}'), result)
|
assert_equal(CGI.escapeHTML('#{ticket.notexsiting / no such method}'), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.' %>"
|
template = "\#{ticket.}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -192,7 +286,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{ticket. / no such method}'), result)
|
assert_equal(CGI.escapeHTML('#{ticket. / no such method}'), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.title.notexsiting' %>"
|
template = "\#{ticket.title.notexsiting}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -202,7 +296,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{ticket.title.notexsiting / no such method}'), result)
|
assert_equal(CGI.escapeHTML('#{ticket.title.notexsiting / no such method}'), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.notexsiting.notexsiting' %>"
|
template = "\#{ticket.notexsiting.notexsiting}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -212,7 +306,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{ticket.notexsiting / no such method}'), result)
|
assert_equal(CGI.escapeHTML('#{ticket.notexsiting / no such method}'), result)
|
||||||
|
|
||||||
template = "<%= d 'notexsiting' %>"
|
template = "\#{notexsiting}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -222,7 +316,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{notexsiting / no such object}'), result)
|
assert_equal(CGI.escapeHTML('#{notexsiting / no such object}'), result)
|
||||||
|
|
||||||
template = "<%= d 'notexsiting.' %>"
|
template = "\#{notexsiting.}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -232,7 +326,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{notexsiting / no such object}'), result)
|
assert_equal(CGI.escapeHTML('#{notexsiting / no such object}'), result)
|
||||||
|
|
||||||
template = "<%= d 'string' %>"
|
template = "\#{string}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
string: 'some string',
|
string: 'some string',
|
||||||
|
@ -242,7 +336,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('some string'), result)
|
assert_equal(CGI.escapeHTML('some string'), result)
|
||||||
|
|
||||||
template = "<%= d 'fixum' %>"
|
template = "\#{fixum}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
fixum: 123,
|
fixum: 123,
|
||||||
|
@ -252,7 +346,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('123'), result)
|
assert_equal(CGI.escapeHTML('123'), result)
|
||||||
|
|
||||||
template = "<%= d 'float' %>"
|
template = "\#{float}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
float: 123.99,
|
float: 123.99,
|
||||||
|
@ -266,7 +360,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
test 'data key validation' do
|
test 'data key validation' do
|
||||||
|
|
||||||
template = "<%= d 'ticket.title `echo 1`' %>"
|
template = "\#{ticket.title `echo 1`}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -274,9 +368,9 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
'en-us',
|
'en-us',
|
||||||
template,
|
template,
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{ticket.title `echo 1` / not allowed}'), result)
|
assert_equal(CGI.escapeHTML('#{ticket.title`echo1` / not allowed}'), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.destroy' %>"
|
template = "\#{ticket.destroy}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -286,7 +380,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{ticket.destroy / not allowed}'), result)
|
assert_equal(CGI.escapeHTML('#{ticket.destroy / not allowed}'), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.save' %>"
|
template = "\#{ticket.save}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -296,7 +390,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{ticket.save / not allowed}'), result)
|
assert_equal(CGI.escapeHTML('#{ticket.save / not allowed}'), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.update' %>"
|
template = "\#{ticket.update}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -306,37 +400,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{ticket.update / not allowed}'), result)
|
assert_equal(CGI.escapeHTML('#{ticket.update / not allowed}'), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.delete' %>"
|
template = "\#{ticket.create}"
|
||||||
result = described_class.new(
|
|
||||||
{
|
|
||||||
ticket: ticket,
|
|
||||||
},
|
|
||||||
'en-us',
|
|
||||||
template,
|
|
||||||
).render
|
|
||||||
assert_equal(CGI.escapeHTML('#{ticket.delete / not allowed}'), result)
|
|
||||||
|
|
||||||
template = "<%= d 'ticket.remove' %>"
|
|
||||||
result = described_class.new(
|
|
||||||
{
|
|
||||||
ticket: ticket,
|
|
||||||
},
|
|
||||||
'en-us',
|
|
||||||
template,
|
|
||||||
).render
|
|
||||||
assert_equal(CGI.escapeHTML('#{ticket.remove / not allowed}'), result)
|
|
||||||
|
|
||||||
template = "<%= d 'ticket.drop' %>"
|
|
||||||
result = described_class.new(
|
|
||||||
{
|
|
||||||
ticket: ticket,
|
|
||||||
},
|
|
||||||
'en-us',
|
|
||||||
template,
|
|
||||||
).render
|
|
||||||
assert_equal(CGI.escapeHTML('#{ticket.drop / not allowed}'), result)
|
|
||||||
|
|
||||||
template = "<%= d 'ticket.create' %>"
|
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -346,7 +410,47 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{ticket.create / not allowed}'), result)
|
assert_equal(CGI.escapeHTML('#{ticket.create / not allowed}'), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.new' %>"
|
template = "\#{ticket.delete}"
|
||||||
|
result = described_class.new(
|
||||||
|
{
|
||||||
|
ticket: ticket,
|
||||||
|
},
|
||||||
|
'en-us',
|
||||||
|
template,
|
||||||
|
).render
|
||||||
|
assert_equal(CGI.escapeHTML('#{ticket.delete / not allowed}'), result)
|
||||||
|
|
||||||
|
template = "\#{ticket.remove}"
|
||||||
|
result = described_class.new(
|
||||||
|
{
|
||||||
|
ticket: ticket,
|
||||||
|
},
|
||||||
|
'en-us',
|
||||||
|
template,
|
||||||
|
).render
|
||||||
|
assert_equal(CGI.escapeHTML('#{ticket.remove / not allowed}'), result)
|
||||||
|
|
||||||
|
template = "\#{ticket.drop}"
|
||||||
|
result = described_class.new(
|
||||||
|
{
|
||||||
|
ticket: ticket,
|
||||||
|
},
|
||||||
|
'en-us',
|
||||||
|
template,
|
||||||
|
).render
|
||||||
|
assert_equal(CGI.escapeHTML('#{ticket.drop / not allowed}'), result)
|
||||||
|
|
||||||
|
template = "\#{ticket.create}"
|
||||||
|
result = described_class.new(
|
||||||
|
{
|
||||||
|
ticket: ticket,
|
||||||
|
},
|
||||||
|
'en-us',
|
||||||
|
template,
|
||||||
|
).render
|
||||||
|
assert_equal(CGI.escapeHTML('#{ticket.create / not allowed}'), result)
|
||||||
|
|
||||||
|
template = "\#{ticket.new}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -356,7 +460,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{ticket.new / not allowed}'), result)
|
assert_equal(CGI.escapeHTML('#{ticket.new / not allowed}'), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.update_att' %>"
|
template = "\#{ticket.update_att}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -366,7 +470,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{ticket.update_att / not allowed}'), result)
|
assert_equal(CGI.escapeHTML('#{ticket.update_att / not allowed}'), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.all' %>"
|
template = "\#{ticket.all}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -376,7 +480,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{ticket.all / not allowed}'), result)
|
assert_equal(CGI.escapeHTML('#{ticket.all / not allowed}'), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.find' %>"
|
template = "\#{ticket.find}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -386,7 +490,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{ticket.find / not allowed}'), result)
|
assert_equal(CGI.escapeHTML('#{ticket.find / not allowed}'), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.where' %>"
|
template = "\#{ticket.where}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -396,7 +500,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{ticket.where / not allowed}'), result)
|
assert_equal(CGI.escapeHTML('#{ticket.where / not allowed}'), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket. destroy' %>"
|
template = "\#{ticket. destroy}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -404,9 +508,9 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
'en-us',
|
'en-us',
|
||||||
template,
|
template,
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML('#{ticket. destroy / not allowed}'), result)
|
assert_equal(CGI.escapeHTML('#{ticket.destroy / not allowed}'), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.\n destroy' %>"
|
template = "\#{ticket.\n destroy}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -414,9 +518,9 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
'en-us',
|
'en-us',
|
||||||
template,
|
template,
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML("\#{ticket.\n destroy / not allowed}"), result)
|
assert_equal(CGI.escapeHTML("\#{ticket.destroy / not allowed}"), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.\t destroy' %>"
|
template = "\#{ticket.\t destroy}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -424,9 +528,9 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
'en-us',
|
'en-us',
|
||||||
template,
|
template,
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML("\#{ticket.\t destroy / not allowed}"), result)
|
assert_equal(CGI.escapeHTML("\#{ticket.destroy / not allowed}"), result)
|
||||||
|
|
||||||
template = "<%= d 'ticket.\r destroy' %>"
|
template = "\#{ticket.\r destroy}"
|
||||||
result = described_class.new(
|
result = described_class.new(
|
||||||
{
|
{
|
||||||
ticket: ticket,
|
ticket: ticket,
|
||||||
|
@ -434,7 +538,7 @@ class NotificationFactoryRendererTest < ActiveSupport::TestCase
|
||||||
'en-us',
|
'en-us',
|
||||||
template,
|
template,
|
||||||
).render
|
).render
|
||||||
assert_equal(CGI.escapeHTML("\#{ticket.\r destroy / not allowed}"), result)
|
assert_equal(CGI.escapeHTML("\#{ticket.destroy / not allowed}"), result)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ class NotificationFactorySlackTemplateTest < ActiveSupport::TestCase
|
||||||
)
|
)
|
||||||
|
|
||||||
assert_match('# Welcome to Zammad!', result[:subject])
|
assert_match('# Welcome to Zammad!', result[:subject])
|
||||||
assert_match('User<b>xxx</b>', result[:body])
|
assert_match('User<b>xxx</b>', result[:body])
|
||||||
assert_match('Created by', result[:body])
|
assert_match('Created by', result[:body])
|
||||||
assert_match('<b>test123</b>', result[:body])
|
assert_match('<b>test123</b>', result[:body])
|
||||||
assert_no_match('Dein', result[:body])
|
assert_no_match('Dein', result[:body])
|
||||||
|
@ -113,7 +113,7 @@ class NotificationFactorySlackTemplateTest < ActiveSupport::TestCase
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
assert_match('# Welcome to Zammad!', result[:subject])
|
assert_match('# Welcome to Zammad!', result[:subject])
|
||||||
assert_match('User<b>xxx</b>', result[:body])
|
assert_match('User<b>xxx</b>', result[:body])
|
||||||
assert_match('state: aaa -> bbb', result[:body])
|
assert_match('state: aaa -> bbb', result[:body])
|
||||||
assert_match('group: xxx -> yyy', result[:body])
|
assert_match('group: xxx -> yyy', result[:body])
|
||||||
assert_no_match('Dein', result[:body])
|
assert_no_match('Dein', result[:body])
|
||||||
|
|
|
@ -11,48 +11,69 @@ class NotificationFactoryTemplateTest < ActiveSupport::TestCase
|
||||||
test 'regular browser html' do
|
test 'regular browser html' do
|
||||||
|
|
||||||
# ensures https://github.com/zammad/zammad/issues/385
|
# ensures https://github.com/zammad/zammad/issues/385
|
||||||
template_before = '<%= d "#{<a href="http://ticket.id" title="http://ticket.id" target="_blank">ticket.id</a>}" %>'
|
template_before = '#{<a href="http://ticket.id" title="http://ticket.id" target="_blank">ticket.id</a>}'
|
||||||
template_after = '<%= d "ticket.id" %>'
|
template_after = '<%= d "ticket.id", true %>'
|
||||||
|
|
||||||
result = described_class.new(template_before).to_s
|
result = described_class.new(template_before, true).to_s
|
||||||
|
assert_equal(template_after, result)
|
||||||
|
|
||||||
|
template_before = '#{<a href="http://ticket.id" title="http://ticket.id" target="_blank">config.fqdn</a>}'
|
||||||
|
template_after = '<%= d "config.fqdn", true %>'
|
||||||
|
|
||||||
|
result = described_class.new(template_before, true).to_s
|
||||||
assert_equal(template_after, result)
|
assert_equal(template_after, result)
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'spaced browser html' do
|
test 'spaced browser html' do
|
||||||
|
|
||||||
# ensures https://github.com/zammad/zammad/issues/385
|
# ensures https://github.com/zammad/zammad/issues/385
|
||||||
template_before = '<%= d "#{ <a href="http://ticket.id" title="http://ticket.id" target="_blank">ticket.id </a>} " %>'
|
template_before = '#{ <a href="http://ticket.id" title="http://ticket.id" target="_blank">ticket.id </a> }'
|
||||||
template_after = '<%= d "ticket.id " %>'
|
template_after = '<%= d "ticket.id", true %>'
|
||||||
|
|
||||||
result = described_class.new(template_before).to_s
|
result = described_class.new(template_before, true).to_s
|
||||||
assert_equal(template_after, result)
|
assert_equal(template_after, result)
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'broken browser html' do
|
test 'broken browser html' do
|
||||||
|
|
||||||
# ensures https://github.com/zammad/zammad/issues/385
|
# ensures https://github.com/zammad/zammad/issues/385
|
||||||
template_before = '<%= d "#{<a href="http://ticket.id" title="http://ticket.id" target="_blank">ticket.id }" %>'
|
template_before = '#{<a href="http://ticket.id" title="http://ticket.id" target="_blank">ticket.id }'
|
||||||
template_after = '<%= d "ticket.id " %>'
|
template_after = '<%= d "ticket.id", true %>'
|
||||||
|
|
||||||
result = described_class.new(template_before).to_s
|
result = described_class.new(template_before, true).to_s
|
||||||
assert_equal(template_after, result)
|
assert_equal(template_after, result)
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'empty tag' do
|
test 'empty tag' do
|
||||||
|
|
||||||
template_before = '<%= d "#{}" %>'
|
template_before = '#{}'
|
||||||
template_after = '<%= d "#{}" %>'
|
template_after = '<%= d "", true %>'
|
||||||
|
|
||||||
result = described_class.new(template_before).to_s
|
result = described_class.new(template_before, true).to_s
|
||||||
assert_equal(template_after, result)
|
assert_equal(template_after, result)
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'empty tag with space' do
|
test 'empty tag with space' do
|
||||||
|
|
||||||
template_before = '<%= d "#{ }" %>'
|
template_before = '#{ }'
|
||||||
template_after = '<%= d "#{ }" %>'
|
template_after = '<%= d "", false %>'
|
||||||
|
|
||||||
result = described_class.new(template_before).to_s
|
result = described_class.new(template_before, false).to_s
|
||||||
|
assert_equal(template_after, result)
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'translation' do
|
||||||
|
|
||||||
|
template_before = "\#{t('some text')}"
|
||||||
|
template_after = '<%= t "some text", false %>'
|
||||||
|
|
||||||
|
result = described_class.new(template_before, false).to_s
|
||||||
|
assert_equal(template_after, result)
|
||||||
|
|
||||||
|
template_before = "\#{t('some \"text\"')}"
|
||||||
|
template_after = '<%= t "some \"text\"", false %>'
|
||||||
|
|
||||||
|
result = described_class.new(template_before, false).to_s
|
||||||
assert_equal(template_after, result)
|
assert_equal(template_after, result)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
78
test/unit/organization_domain_based_assignment_test.rb
Normal file
78
test/unit/organization_domain_based_assignment_test.rb
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class OrganizationDomainBasedAssignmentTest < ActiveSupport::TestCase
|
||||||
|
test 'organization based assignment' do
|
||||||
|
|
||||||
|
organization1 = Organization.create_if_not_exists(
|
||||||
|
name: 'organization based assignment 1',
|
||||||
|
domain: '@examPle1.com ',
|
||||||
|
domain_assignment: true,
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
organization2 = Organization.create_if_not_exists(
|
||||||
|
name: 'organization based assignment 2',
|
||||||
|
domain: 'example2.com',
|
||||||
|
domain_assignment: false,
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
roles = Role.where(name: 'Customer')
|
||||||
|
customer1 = User.create_or_update(
|
||||||
|
login: 'organization-based_assignment-customer1@example1.com',
|
||||||
|
firstname: 'Domain',
|
||||||
|
lastname: 'Agent1',
|
||||||
|
email: 'organization-based_assignment-customer1@example1.com',
|
||||||
|
password: 'customerpw',
|
||||||
|
active: true,
|
||||||
|
roles: roles,
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
assert_equal(organization1.id, customer1.organization_id)
|
||||||
|
|
||||||
|
customer2 = User.create_or_update(
|
||||||
|
login: 'organization-based_assignment-customer2@example1.com',
|
||||||
|
firstname: 'Domain',
|
||||||
|
lastname: 'Agent2',
|
||||||
|
email: 'organization-based_assignment-customer2@example1.com',
|
||||||
|
password: 'customerpw',
|
||||||
|
active: true,
|
||||||
|
organization_id: organization2.id,
|
||||||
|
roles: roles,
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
assert_equal(organization2.id, customer2.organization_id)
|
||||||
|
|
||||||
|
customer3 = User.create_or_update(
|
||||||
|
login: 'organization-based_assignment-customer3@example2.com',
|
||||||
|
firstname: 'Domain',
|
||||||
|
lastname: 'Agent2',
|
||||||
|
email: 'organization-based_assignment-customer3@example2.com',
|
||||||
|
password: 'customerpw',
|
||||||
|
active: true,
|
||||||
|
roles: roles,
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
assert_equal(nil, customer3.organization_id)
|
||||||
|
|
||||||
|
customer4 = User.create_or_update(
|
||||||
|
login: 'organization-based_assignment-customer4',
|
||||||
|
firstname: 'Domain',
|
||||||
|
lastname: 'Agent2',
|
||||||
|
email: '@',
|
||||||
|
password: 'customerpw',
|
||||||
|
active: true,
|
||||||
|
roles: roles,
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
assert_equal(nil, customer4.organization_id)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -13,7 +13,7 @@ class TicketTriggerTest < ActiveSupport::TestCase
|
||||||
},
|
},
|
||||||
perform: {
|
perform: {
|
||||||
'notification.email' => {
|
'notification.email' => {
|
||||||
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}',
|
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}<br>#{article.body}',
|
||||||
'recipient' => 'ticket_customer',
|
'recipient' => 'ticket_customer',
|
||||||
'subject' => 'Thanks for your inquiry (#{ticket.title})!',
|
'subject' => 'Thanks for your inquiry (#{ticket.title})!',
|
||||||
},
|
},
|
||||||
|
@ -60,12 +60,25 @@ class TicketTriggerTest < ActiveSupport::TestCase
|
||||||
created_by_id: 1,
|
created_by_id: 1,
|
||||||
)
|
)
|
||||||
assert(ticket1, 'ticket1 created')
|
assert(ticket1, 'ticket1 created')
|
||||||
|
Ticket::Article.create(
|
||||||
|
ticket_id: ticket1.id,
|
||||||
|
from: 'some_sender@example.com',
|
||||||
|
to: 'some_recipient@example.com',
|
||||||
|
subject: 'some subject',
|
||||||
|
message_id: 'some@id',
|
||||||
|
body: "some message <b>note</b>\nnew line",
|
||||||
|
internal: false,
|
||||||
|
sender: Ticket::Article::Sender.find_by(name: 'Agent'),
|
||||||
|
type: Ticket::Article::Type.find_by(name: 'note'),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
|
||||||
assert_equal('some <b>title</b> äöüß', ticket1.title, 'ticket1.title verify')
|
assert_equal('some <b>title</b> äöüß', ticket1.title, 'ticket1.title verify')
|
||||||
assert_equal('Users', ticket1.group.name, 'ticket1.group verify')
|
assert_equal('Users', ticket1.group.name, 'ticket1.group verify')
|
||||||
assert_equal('new', ticket1.state.name, 'ticket1.state verify')
|
assert_equal('new', ticket1.state.name, 'ticket1.state verify')
|
||||||
assert_equal('2 normal', ticket1.priority.name, 'ticket1.priority verify')
|
assert_equal('2 normal', ticket1.priority.name, 'ticket1.priority verify')
|
||||||
assert_equal(0, ticket1.articles.count, 'ticket1.articles verify')
|
assert_equal(1, ticket1.articles.count, 'ticket1.articles verify')
|
||||||
assert_equal([], Tag.tag_list(object: 'Ticket', o_id: ticket1.id))
|
assert_equal([], Tag.tag_list(object: 'Ticket', o_id: ticket1.id))
|
||||||
|
|
||||||
Observer::Transaction.commit
|
Observer::Transaction.commit
|
||||||
|
@ -75,13 +88,14 @@ class TicketTriggerTest < ActiveSupport::TestCase
|
||||||
assert_equal('Users', ticket1.group.name, 'ticket1.group verify')
|
assert_equal('Users', ticket1.group.name, 'ticket1.group verify')
|
||||||
assert_equal('new', ticket1.state.name, 'ticket1.state verify')
|
assert_equal('new', ticket1.state.name, 'ticket1.state verify')
|
||||||
assert_equal('3 high', ticket1.priority.name, 'ticket1.priority verify')
|
assert_equal('3 high', ticket1.priority.name, 'ticket1.priority verify')
|
||||||
assert_equal(1, ticket1.articles.count, 'ticket1.articles verify')
|
assert_equal(2, ticket1.articles.count, 'ticket1.articles verify')
|
||||||
assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket1.id))
|
assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket1.id))
|
||||||
article1 = ticket1.articles.last
|
article1 = ticket1.articles.last
|
||||||
assert_match('Zammad <zammad@localhost>', article1.from)
|
assert_match('Zammad <zammad@localhost>', article1.from)
|
||||||
assert_match('nicole.braun@zammad.org', article1.to)
|
assert_match('nicole.braun@zammad.org', article1.to)
|
||||||
assert_match('Thanks for your inquiry (some <b>title</b> äöüß)!', article1.subject)
|
assert_match('Thanks for your inquiry (some <b>title</b> äöüß)!', article1.subject)
|
||||||
assert_match('Braun<br>some <b>title</b>', article1.body)
|
assert_match('Braun<br>some <b>title</b>', article1.body)
|
||||||
|
assert_match('> some message <b>note</b><br>> new line', article1.body)
|
||||||
assert_equal('text/html', article1.content_type)
|
assert_equal('text/html', article1.content_type)
|
||||||
|
|
||||||
ticket1.priority = Ticket::Priority.lookup(name: '2 normal')
|
ticket1.priority = Ticket::Priority.lookup(name: '2 normal')
|
||||||
|
@ -93,7 +107,7 @@ class TicketTriggerTest < ActiveSupport::TestCase
|
||||||
assert_equal('Users', ticket1.group.name, 'ticket1.group verify')
|
assert_equal('Users', ticket1.group.name, 'ticket1.group verify')
|
||||||
assert_equal('new', ticket1.state.name, 'ticket1.state verify')
|
assert_equal('new', ticket1.state.name, 'ticket1.state verify')
|
||||||
assert_equal('2 normal', ticket1.priority.name, 'ticket1.priority verify')
|
assert_equal('2 normal', ticket1.priority.name, 'ticket1.priority verify')
|
||||||
assert_equal(1, ticket1.articles.count, 'ticket1.articles verify')
|
assert_equal(2, ticket1.articles.count, 'ticket1.articles verify')
|
||||||
assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket1.id))
|
assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket1.id))
|
||||||
|
|
||||||
ticket1.state = Ticket::State.lookup(name: 'open')
|
ticket1.state = Ticket::State.lookup(name: 'open')
|
||||||
|
@ -105,7 +119,7 @@ class TicketTriggerTest < ActiveSupport::TestCase
|
||||||
assert_equal('Users', ticket1.group.name, 'ticket1.group verify')
|
assert_equal('Users', ticket1.group.name, 'ticket1.group verify')
|
||||||
assert_equal('open', ticket1.state.name, 'ticket1.state verify')
|
assert_equal('open', ticket1.state.name, 'ticket1.state verify')
|
||||||
assert_equal('2 normal', ticket1.priority.name, 'ticket1.priority verify')
|
assert_equal('2 normal', ticket1.priority.name, 'ticket1.priority verify')
|
||||||
assert_equal(1, ticket1.articles.count, 'ticket1.articles verify')
|
assert_equal(2, ticket1.articles.count, 'ticket1.articles verify')
|
||||||
assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket1.id))
|
assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket1.id))
|
||||||
|
|
||||||
ticket1.state = Ticket::State.lookup(name: 'new')
|
ticket1.state = Ticket::State.lookup(name: 'new')
|
||||||
|
@ -118,7 +132,7 @@ class TicketTriggerTest < ActiveSupport::TestCase
|
||||||
assert_equal('Users', ticket1.group.name, 'ticket1.group verify')
|
assert_equal('Users', ticket1.group.name, 'ticket1.group verify')
|
||||||
assert_equal('new', ticket1.state.name, 'ticket1.state verify')
|
assert_equal('new', ticket1.state.name, 'ticket1.state verify')
|
||||||
assert_equal('3 high', ticket1.priority.name, 'ticket1.priority verify')
|
assert_equal('3 high', ticket1.priority.name, 'ticket1.priority verify')
|
||||||
assert_equal(2, ticket1.articles.count, 'ticket1.articles verify')
|
assert_equal(3, ticket1.articles.count, 'ticket1.articles verify')
|
||||||
assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket1.id))
|
assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket1.id))
|
||||||
article1 = ticket1.articles.last
|
article1 = ticket1.articles.last
|
||||||
assert_match('Zammad <zammad@localhost>', article1.from)
|
assert_match('Zammad <zammad@localhost>', article1.from)
|
||||||
|
@ -155,6 +169,56 @@ class TicketTriggerTest < ActiveSupport::TestCase
|
||||||
assert_equal(0, ticket2.articles.count, 'ticket2.articles verify')
|
assert_equal(0, ticket2.articles.count, 'ticket2.articles verify')
|
||||||
assert_equal([], Tag.tag_list(object: 'Ticket', o_id: ticket2.id))
|
assert_equal([], Tag.tag_list(object: 'Ticket', o_id: ticket2.id))
|
||||||
|
|
||||||
|
ticket3 = Ticket.create(
|
||||||
|
title: "some <b>title</b>\n äöüß3",
|
||||||
|
group: Group.lookup(name: 'Users'),
|
||||||
|
customer: User.lookup(email: 'nicole.braun@zammad.org'),
|
||||||
|
state: Ticket::State.lookup(name: 'new'),
|
||||||
|
priority: Ticket::Priority.lookup(name: '2 normal'),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
assert(ticket3, 'ticket3 created')
|
||||||
|
Ticket::Article.create(
|
||||||
|
ticket_id: ticket3.id,
|
||||||
|
from: 'some_sender@example.com',
|
||||||
|
to: 'some_recipient@example.com',
|
||||||
|
subject: 'some subject',
|
||||||
|
message_id: 'some@id',
|
||||||
|
content_type: 'text/html',
|
||||||
|
body: 'some message <b>note</b><br>new line',
|
||||||
|
internal: false,
|
||||||
|
sender: Ticket::Article::Sender.find_by(name: 'Agent'),
|
||||||
|
type: Ticket::Article::Type.find_by(name: 'note'),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_equal('some <b>title</b> äöüß3', ticket3.title, 'ticket3.title verify')
|
||||||
|
assert_equal('Users', ticket3.group.name, 'ticket3.group verify')
|
||||||
|
assert_equal('new', ticket3.state.name, 'ticket3.state verify')
|
||||||
|
assert_equal('2 normal', ticket3.priority.name, 'ticket3.priority verify')
|
||||||
|
assert_equal(1, ticket3.articles.count, 'ticket3.articles verify')
|
||||||
|
assert_equal([], Tag.tag_list(object: 'Ticket', o_id: ticket3.id))
|
||||||
|
|
||||||
|
Observer::Transaction.commit
|
||||||
|
|
||||||
|
ticket3 = Ticket.lookup(id: ticket3.id)
|
||||||
|
assert_equal('some <b>title</b> äöüß3', ticket3.title, 'ticket3.title verify')
|
||||||
|
assert_equal('Users', ticket3.group.name, 'ticket3.group verify')
|
||||||
|
assert_equal('new', ticket3.state.name, 'ticket3.state verify')
|
||||||
|
assert_equal('3 high', ticket3.priority.name, 'ticket3.priority verify')
|
||||||
|
assert_equal(2, ticket3.articles.count, 'ticket3.articles verify')
|
||||||
|
assert_equal(%w(aa kk), Tag.tag_list(object: 'Ticket', o_id: ticket3.id))
|
||||||
|
article3 = ticket3.articles.last
|
||||||
|
assert_match('Zammad <zammad@localhost>', article3.from)
|
||||||
|
assert_match('nicole.braun@zammad.org', article3.to)
|
||||||
|
assert_match('Thanks for your inquiry (some <b>title</b> äöüß3)!', article3.subject)
|
||||||
|
assert_match('Braun<br>some <b>title</b>', article3.body)
|
||||||
|
assert_match('> some message note<br>> new line', article3.body)
|
||||||
|
assert_no_match('> some message <b>note</b><br>> new line', article3.body)
|
||||||
|
assert_equal('text/html', article3.content_type)
|
||||||
|
|
||||||
Trigger.destroy_all
|
Trigger.destroy_all
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue