Fixes #2709, fixes #2666, fixes #2665, fixes #556, fixes #3275 - Refactoring: Implement new translation toolchain based on gettext.

- Translations are no longer fetched from the cloud.
- Instead, they are extracted from the codebase and stored in i18n/zammad.pot.
- Translations will be managed via a public Weblate instance soon.
- The translated .po files are fed to the database as before.
- It is now possible to change "translation" strings for en-us locally via the admin GUI.
- It is no longer possible to submit local changes.
This commit is contained in:
Martin Gruner 2021-11-15 16:58:19 +01:00
parent 6a6b19b4e6
commit 64a87b1c67
481 changed files with 534434 additions and 3442 deletions

View file

@ -0,0 +1,91 @@
module.exports = class DetectTranslatableString
# coffeelint: disable=detect_translatable_string
rule:
name: 'detect_translatable_string'
level: 'ignore'
message: 'The following string looks like it should be marked as translatable via __(...)'
description: '''
'''
constructor: ->
@callTokens = []
tokens: ['STRING', 'CALL_START', 'CALL_END']
lintToken: (token, tokenApi) ->
[type, tokenValue] = token
if type in ['CALL_START', 'CALL_END']
@trackCall token, tokenApi
return
return false if @isInIgnoredMethod()
return @lintString(token, tokenApi)
lintString: (token, tokenApi) ->
[type, tokenValue] = token
# Remove quotes.
string = tokenValue[1..-2]
# Ignore strings with less than two words.
return false if string.split(' ').length < 2
# Ignore strings that are being used as exception; unlike Ruby exceptions, these should not reach the user.
return false if tokenApi.peek(-3)[1] == 'throw'
return false if tokenApi.peek(-2)[1] == 'throw'
return false if tokenApi.peek(-1)[1] == 'throw'
# Ignore strings that are being used for comparison
return false if tokenApi.peek(-1)[1] == '=='
# String interpolation is handled via concatenation, ignore such strings.
return false if tokenApi.peek(1)[1] == '+'
return false if tokenApi.peek(2)[1] == '+'
BLOCKLIST = [
# Only look at strings starting with upper case letters
/^[^A-Z]/,
# # Ignore strings starting with three upper case letters like SELECT, POST etc.
# /^[A-Z]{3}/,
]
return false if BLOCKLIST.some (entry) ->
#console.log([string, entry, string.match(entry), token, tokenApi.peek(-1), tokenApi.peek(1)])
string.match(entry)
# console.log(tokenApi.peek(-3))
# console.log(tokenApi.peek(-2))
# console.log(tokenApi.peek(-1))
# console.log(token)
return { context: "Found: #{token[1]}" }
ignoredMethods: {
'__': true,
'log': true,
'T': true,
'controllerBind': true,
'error': true, # App.Log.error
'set': true, # App.Config.set
'translateInline': true,
'translateContent': true,
'translatePlain': true,
}
isInIgnoredMethod: ->
#console.log(@callTokens)
for t in @callTokens
return true if t.isIgnoredMethod
return false
trackCall: (token, tokenApi) ->
if token[0] is 'CALL_START'
p = tokenApi.peek(-1)
token.isIgnoredMethod = p and @ignoredMethods[p[1]]
@callTokens.push(token)
else
@callTokens.pop()
return null

4
.gitignore vendored
View file

@ -37,8 +37,8 @@
/db/*.sqlite3 /db/*.sqlite3
/db/schema.rb /db/schema.rb
# translation cache files # legacy translation cache files
/config/locales*.yml /config/locales-*.yml
/config/translations/*.yml /config/translations/*.yml
# NPM / Yarn # NPM / Yarn

View file

@ -22,7 +22,26 @@ shellcheck:
script: script:
- shellcheck -S warning $(find . -name "*.sh" -o -name "functions" | grep -v "/vendor/") - shellcheck -S warning $(find . -name "*.sh" -o -name "functions" | grep -v "/vendor/")
zeitwerk_check: gettext lint:
<<: *template_pre
before_script:
- echo "Disable default before_script."
script:
- for FILE in i18n/*.pot i18n/*.po; do echo "Checking $FILE"; msgfmt -o /dev/null -c $FILE; done
gettext catalog consistency:
<<: *template_pre
extends:
- .tags_docker
- .services_postgresql
script:
- bundle install -j $(nproc) --path vendor
- bundle exec ruby .gitlab/configure_environment.rb
- source .gitlab/environment.env
- bundle exec rake zammad:db:init
- bundle exec rails generate translation_catalog --check
zeitwerk:check:
<<: *template_pre <<: *template_pre
extends: extends:
- .tags_docker - .tags_docker
@ -48,7 +67,7 @@ brakeman:
coffeelint: coffeelint:
<<: *template_pre <<: *template_pre
script: script:
- coffeelint app/ - coffeelint --rules ./.coffeelint/rules/* app/
stylelint: stylelint:
<<: *template_pre <<: *template_pre

View file

@ -9,10 +9,18 @@ PreCommit:
enabled: false enabled: false
RuboCop: RuboCop:
enabled: true enabled: true
on_warn: fail # Treat all warnings as failures on_warn: fail
CoffeeLint: CoffeeLint:
# .coffeelint/rules/* not supported in YAML, specify all rules separately.
flags: ['--reporter=csv', '--rules', './.coffeelint/rules/detect_translatable_string.coffee']
enabled: true enabled: true
on_warn: fail # Treat all warnings as failures on_warn: fail
exclude: public/assets/chat/**/*
CustomScript:
enabled: true
description: 'Check if translation catalog is up-to-date'
required_executable: 'rails'
flags: ['generate', 'translation_catalog', '--check']
Stylelint: Stylelint:
enabled: true enabled: true
@ -43,4 +51,3 @@ PreRebase:
PrepareCommitMsg: PrepareCommitMsg:
ALL: ALL:
enabled: false enabled: false

View file

@ -0,0 +1,90 @@
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
module RuboCop
module Cop
module Zammad
class DetectTranslatableString < Base
extend AutoCorrector
MSG = 'This string looks like it should be marked as translatable via __(...).'.freeze
def on_str(node)
# Constants like __FILE__ are handled as strings, but don't respond to begin.
return if !node.loc.respond_to?(:begin) || !node.loc.begin
return if part_of_ignored_node?(node)
return if !offense?(node)
add_offense(node) do |corrector|
corrector.replace(node, "__(#{node.source})")
end
end
def on_regexp(node)
ignore_node(node)
end
METHOD_NAME_BLOCKLIST = %i[
__ translate
include? eql? parse
debug info warn error fatal unknown log log_error
].freeze
def on_send(node)
ignore_node(node) if METHOD_NAME_BLOCKLIST.include? node.method_name
end
private
PARENT_SOURCE_BLOCKLIST = [
# Ignore logged strings
'Rails.logger'
].freeze
NODE_START_BLOCKLIST = [
# Only look at strings starting with upper case letters
%r{[^A-Z]},
# Ignore strings starting with three upper case letters like SELECT, POST etc.
%r{[A-Z]{3}},
].freeze
NODE_CONTAIN_BLOCKLIST = [
# Ignore strings with interpolation.
'#{',
# Ignore Email addresses
'@'
].freeze
def offense?(node) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
# Ignore Hash Keys
return false if node.parent.type.eql?(:pair) && node.parent.children.first.equal?(node)
# Ignore equality checks like ... == 'My String'
return false if node.left_sibling.eql?(:==)
# Remove quotes
node_source = node.source[1..-2]
# Only match strings with at least two words
return false if node_source.split.count < 2
NODE_START_BLOCKLIST.each do |entry|
return false if node_source.start_with? entry
end
NODE_CONTAIN_BLOCKLIST.each do |entry|
return false if node_source.include? entry
end
parent_source = node.parent.source
PARENT_SOURCE_BLOCKLIST.each do |entry|
return false if parent_source.include? entry
end
true
end
end
end
end
end

View file

@ -317,6 +317,20 @@ Zammad/ExistsResetColumnInformation:
- 'db/migrate/201*_*.rb' - 'db/migrate/201*_*.rb'
- 'db/migrate/2020*_*.rb' - 'db/migrate/2020*_*.rb'
Zammad/DetectTranslatableString:
Enabled: true
Include:
- "app/**/*.rb"
- "db/**/*.rb"
- "lib/**/*.rb"
Exclude:
- "db/migrate/**/*.rb"
- "db/addon/**/*.rb"
- "lib/generators/**/*.rb"
- "lib/sequencer/**/*.rb"
- "lib/import/**/*.rb"
- "lib/tasks/**/*.rb"
Zammad/ExistsDbStrategy: Zammad/ExistsDbStrategy:
Include: Include:
- "spec/**/*.rb" - "spec/**/*.rb"

View file

@ -1,6 +1,7 @@
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/ # Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
require_relative 'cop/zammad/exists_condition' require_relative 'cop/zammad/exists_condition'
require_relative 'cop/zammad/detect_translatable_string'
require_relative 'cop/zammad/exists_date_time_precision' require_relative 'cop/zammad/exists_date_time_precision'
require_relative 'cop/zammad/exists_db_strategy' require_relative 'cop/zammad/exists_db_strategy'
require_relative 'cop/zammad/exists_reset_column_information' require_relative 'cop/zammad/exists_reset_column_information'

View file

@ -149,6 +149,9 @@ gem 'viewpoint', require: false
# integrations - S/MIME # integrations - S/MIME
gem 'openssl' gem 'openssl'
# Translation sync
gem 'PoParser', require: false
# Gems used only for develop/test and not required # Gems used only for develop/test and not required
# in production environments by default. # in production environments by default.
group :development, :test do group :development, :test do

View file

@ -19,6 +19,8 @@ GIT
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
PoParser (3.2.5)
simple_po_parser (~> 1.1.2)
aasm (5.2.0) aasm (5.2.0)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
actioncable (6.0.4.1) actioncable (6.0.4.1)
@ -555,6 +557,7 @@ GEM
shoulda-matchers (5.0.0) shoulda-matchers (5.0.0)
activesupport (>= 5.2.0) activesupport (>= 5.2.0)
simple_oauth (0.3.1) simple_oauth (0.3.1)
simple_po_parser (1.1.5)
simplecov (0.21.2) simplecov (0.21.2)
docile (~> 1.1) docile (~> 1.1)
simplecov-html (~> 0.11) simplecov-html (~> 0.11)
@ -655,6 +658,7 @@ PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
PoParser
aasm aasm
activerecord-import activerecord-import
activerecord-nulldb-adapter activerecord-nulldb-adapter

View file

@ -114,7 +114,7 @@ class App.Controller extends Spine.Controller
if window.clipboardData # IE if window.clipboardData # IE
window.clipboardData.setData('Text', text) window.clipboardData.setData('Text', text)
else else
window.prompt('Copy to clipboard: Ctrl+C, Enter', text) window.prompt(__('Copy to clipboard: Ctrl+C, Enter'), text)
# disable all delay's and interval's # disable all delay's and interval's
disconnectClient: -> disconnectClient: ->

View file

@ -59,7 +59,7 @@ class App.ControllerModal extends App.Controller
false false
content: -> content: ->
'You need to implement a one @content()!' __('You need to implement a one @content()!')
update: => update: =>
if @message if @message
@ -106,7 +106,7 @@ class App.ControllerModal extends App.Controller
if @buttonSubmit is true if @buttonSubmit is true
@buttonSubmit = 'Submit' @buttonSubmit = 'Submit'
if @buttonCancel is true if @buttonCancel is true
@buttonCancel = 'Cancel & Go Back' @buttonCancel = __('Cancel & Go Back')
@update() @update()

View file

@ -1,5 +1,5 @@
class App.ControllerForm extends App.Controller class App.ControllerForm extends App.Controller
fullFormSubmitLabel: 'Submit' fullFormSubmitLabel: __('Submit')
fullFormSubmitAdditionalClasses: '' fullFormSubmitAdditionalClasses: ''
fullFormButtonsContainerClass: '' fullFormButtonsContainerClass: ''
fullFormAdditionalButtons: [] # [{className: 'js-class', text: 'Label'}] fullFormAdditionalButtons: [] # [{className: 'js-class', text: 'Label'}]
@ -173,7 +173,7 @@ class App.ControllerForm extends App.Controller
# input text field with max. 100 size # input text field with max. 100 size
attribute_config = { attribute_config = {
name: 'subject' name: 'subject'
display: 'Subject' display: __('Subject')
tag: 'input' tag: 'input'
type: 'text' type: 'text'
limit: 100 limit: 100
@ -185,7 +185,7 @@ class App.ControllerForm extends App.Controller
# colection as relation with auto completion # colection as relation with auto completion
attribute_config = { attribute_config = {
name: 'customer_id' name: 'customer_id'
display: 'Customer' display: __('Customer')
tag: 'autocompletion' tag: 'autocompletion'
# auto completion params, endpoints, ui,... # auto completion params, endpoints, ui,...
type: 'text' type: 'text'
@ -193,7 +193,7 @@ class App.ControllerForm extends App.Controller
null: false null: false
relation: 'User' relation: 'User'
autocapitalize: false autocapitalize: false
help: 'Select the customer of the ticket or create one.' help: __('Select the customer of the ticket or create one.')
helpLink: '<a href="" class="customer_new">&raquo;</a>' helpLink: '<a href="" class="customer_new">&raquo;</a>'
callback: @userInfo callback: @userInfo
class: 'span7' class: 'span7'
@ -202,7 +202,7 @@ class App.ControllerForm extends App.Controller
# colection as relation # colection as relation
attribute_config = { attribute_config = {
name: 'priority_id' name: 'priority_id'
display: 'Priority' display: __('Priority')
tag: 'select' tag: 'select'
multiple: false multiple: false
null: false null: false
@ -216,7 +216,7 @@ class App.ControllerForm extends App.Controller
# colection as options # colection as options
attribute_config = { attribute_config = {
name: 'priority_id' name: 'priority_id'
display: 'Priority' display: __('Priority')
tag: 'select' tag: 'select'
multiple: false multiple: false
null: false null: false

View file

@ -1,8 +1,8 @@
class App.ControllerGenericDescription extends App.ControllerModal class App.ControllerGenericDescription extends App.ControllerModal
buttonClose: true buttonClose: true
buttonCancel: false buttonCancel: false
buttonSubmit: 'Close' buttonSubmit: __('Close')
head: 'Description' head: __('Description')
content: => content: =>
marked(App.i18n.translateContent(@description)) marked(App.i18n.translateContent(@description))

View file

@ -1,9 +1,9 @@
class App.ControllerGenericDestroyConfirm extends App.ControllerModal class App.ControllerGenericDestroyConfirm extends App.ControllerModal
buttonClose: true buttonClose: true
buttonCancel: true buttonCancel: true
buttonSubmit: 'delete' buttonSubmit: __('delete')
buttonClass: 'btn--danger' buttonClass: 'btn--danger'
head: 'Confirm' head: __('Confirm')
small: true small: true
content: -> content: ->
@ -23,9 +23,9 @@ class App.ControllerGenericDestroyConfirm extends App.ControllerModal
class App.ControllerConfirm extends App.ControllerModal class App.ControllerConfirm extends App.ControllerModal
buttonClose: true buttonClose: true
buttonCancel: true buttonCancel: true
buttonSubmit: 'yes' buttonSubmit: __('yes')
buttonClass: 'btn--danger' buttonClass: 'btn--danger'
head: 'Confirm' head: __('Confirm')
small: true small: true
content: -> content: ->

View file

@ -51,5 +51,5 @@ class App.ControllerGenericEdit extends App.ControllerModal
App[ ui.genericObject ].fetch(id: @id) App[ ui.genericObject ].fetch(id: @id)
ui.log 'errors' ui.log 'errors'
ui.formEnable(e) ui.formEnable(e)
ui.controller.showAlert(details.error_human || details.error || 'Unable to update object!') ui.controller.showAlert(details.error_human || details.error || __('Unable to update object!'))
) )

View file

@ -1,9 +1,9 @@
class App.ControllerErrorModal extends App.ControllerModal class App.ControllerErrorModal extends App.ControllerModal
buttonClose: true buttonClose: true
buttonCancel: false buttonCancel: false
buttonSubmit: 'Close' buttonSubmit: __('Close')
#buttonClass: 'btn--danger' #buttonClass: 'btn--danger'
head: 'Error' head: __('Error')
#small: true #small: true
#shown: true #shown: true
showTrySupport: true showTrySupport: true

View file

@ -5,7 +5,7 @@ class App.GenericHistory extends App.ControllerModal
buttonClose: true buttonClose: true
buttonCancel: false buttonCancel: false
buttonSubmit: false buttonSubmit: false
head: 'History' head: __('History')
shown: false shown: false
constructor: -> constructor: ->

View file

@ -51,5 +51,5 @@ class App.ControllerGenericNew extends App.ControllerModal
fail: (settings, details) -> fail: (settings, details) ->
ui.log 'errors', details ui.log 'errors', details
ui.formEnable(e) ui.formEnable(e)
ui.controller.showAlert(details.error_human || details.error || 'Unable to create object!') ui.controller.showAlert(details.error_human || details.error || __('Unable to create object!'))
) )

View file

@ -1,5 +1,5 @@
class App.ControllerReorderModal extends App.ControllerModal class App.ControllerReorderModal extends App.ControllerModal
head: 'Drag to reorder' head: __('Drag to reorder')
content: -> content: ->
view = $(App.view('reorder_modal')()) view = $(App.view('reorder_modal')())
@ -10,7 +10,7 @@ class App.ControllerReorderModal extends App.ControllerModal
true true
overview: ['title'] overview: ['title']
attribute_list: [ attribute_list: [
{ name: 'title', display: 'Name' } { name: 'title', display: __('Name') }
] ]
objects: @items objects: @items
) )
@ -49,4 +49,3 @@ class App.ControllerReorderModal extends App.ControllerModal
onSubmit: -> onSubmit: ->
super super
@save() @save()

View file

@ -28,7 +28,7 @@
# add new header item # add new header item
attribute = attribute =
name: 'some name' name: 'some name'
display: 'Some Name' display: __('Some Name')
headers.push attribute headers.push attribute
console.log('new header is', headers) console.log('new header is', headers)
headers headers
@ -78,12 +78,12 @@
el: element el: element
overview: ['time', 'area', 'level', 'browser', 'location', 'data'] overview: ['time', 'area', 'level', 'browser', 'location', 'data']
attribute_list: [ attribute_list: [
{ name: 'time', display: 'Time', tag: 'datetime' }, { name: 'time', display: __('Time'), tag: 'datetime' },
{ name: 'area', display: 'Area', type: 'text' }, { name: 'area', display: __('Area'), type: 'text' },
{ name: 'level', display: 'Level', type: 'text' }, { name: 'level', display: __('Level'), type: 'text' },
{ name: 'browser', display: 'Browser', type: 'text' }, { name: 'browser', display: __('Browser'), type: 'text' },
{ name: 'location', display: 'Location', type: 'text' }, { name: 'location', display: __('Location'), type: 'text' },
{ name: 'data', display: 'Data', type: 'text' }, { name: 'data', display: __('Data'), type: 'text' },
] ]
objects: data objects: data
) )
@ -641,7 +641,7 @@ class App.ControllerTable extends App.Controller
if @clone if @clone
@actions.push @actions.push
name: 'clone' name: 'clone'
display: 'Clone' display: __('Clone')
icon: 'clipboard' icon: 'clipboard'
class: 'create js-clone' class: 'create js-clone'
callback: (id) => callback: (id) =>
@ -660,7 +660,7 @@ class App.ControllerTable extends App.Controller
if @destroy if @destroy
@actions.push @actions.push
name: 'delete' name: 'delete'
display: 'Delete' display: __('Delete')
icon: 'trash' icon: 'trash'
class: 'danger js-delete' class: 'danger js-delete'
callback: (id) => callback: (id) =>
@ -672,7 +672,7 @@ class App.ControllerTable extends App.Controller
if @actions.length if @actions.length
@headers.push @headers.push
name: 'action' name: 'action'
display: 'Action' display: __('Action')
width: '50px' width: '50px'
displayWidth: 50 displayWidth: 50
align: 'right' align: 'right'

View file

@ -11,7 +11,7 @@ class App.ChannelEmailFilter extends App.Controller
template = $( '<div><div class="overview"></div><a data-type="new" class="btn btn--success">' + App.i18n.translateContent('New') + '</a></div>' ) template = $( '<div><div class="overview"></div><a data-type="new" class="btn btn--success">' + App.i18n.translateContent('New') + '</a></div>' )
description = 'With filters you can e. g. dispatch new tickets into certain groups or set a certain priority for tickets of a VIP customer.' description = __('With filters you can e. g. dispatch new tickets into certain groups or set a certain priority for tickets of a VIP customer.')
new App.ControllerTable( new App.ControllerTable(
el: template.find('.overview') el: template.find('.overview')
@ -28,7 +28,7 @@ class App.ChannelEmailFilter extends App.Controller
e.preventDefault() e.preventDefault()
new App.ControllerGenericNew( new App.ControllerGenericNew(
pageData: pageData:
object: 'Postmaster Filter' object: __('Postmaster Filter')
genericObject: 'PostmasterFilter' genericObject: 'PostmasterFilter'
container: @el.closest('.content') container: @el.closest('.content')
callback: @load callback: @load
@ -40,7 +40,7 @@ class App.ChannelEmailFilter extends App.Controller
new App.ControllerGenericEdit( new App.ControllerGenericEdit(
id: id, id: id,
pageData: pageData:
object: 'Postmaster Filter' object: __('Postmaster Filter')
genericObject: 'PostmasterFilter' genericObject: 'PostmasterFilter'
container: @el.closest('.content') container: @el.closest('.content')
callback: @load callback: @load

View file

@ -11,11 +11,11 @@ class App.ChannelEmailSignature extends App.Controller
template = $( '<div><div class="overview"></div><a data-type="new" class="btn btn--success">' + App.i18n.translateContent('New') + '</a></div>' ) template = $( '<div><div class="overview"></div><a data-type="new" class="btn btn--success">' + App.i18n.translateContent('New') + '</a></div>' )
description = ''' description = __('''
You can define different signatures for each group. So you can have different email signatures for different departments. You can define different signatures for each group. So you can have different email signatures for different departments.
Once you have created a signature here, you need also to edit the groups where you want to use it. Once you have created a signature here, you need also to edit the groups where you want to use it.
''' ''')
new App.ControllerTable( new App.ControllerTable(
el: template.find('.overview') el: template.find('.overview')
@ -46,7 +46,7 @@ class ChannelEmailSignatureEdit extends App.ControllerModal
buttonClose: true buttonClose: true
buttonCancel: true buttonCancel: true
buttonSubmit: true buttonSubmit: true
head: 'Signature' head: __('Signature')
content: => content: =>
if @object if @object
@ -90,5 +90,5 @@ class ChannelEmailSignatureEdit extends App.ControllerModal
fail: (settings, details) => fail: (settings, details) =>
@log 'errors', details @log 'errors', details
@formEnable(e) @formEnable(e)
@form.showAlert(details.error_human || details.error || 'Unable to create object!') @form.showAlert(details.error_human || details.error || __('Unable to create object!'))
) )

View file

@ -1,6 +1,6 @@
class ChannelChat extends App.ControllerSubContent class ChannelChat extends App.ControllerSubContent
requiredPermission: 'admin.channel_chat' requiredPermission: 'admin.channel_chat'
header: 'Chat' header: __('Chat')
events: events:
'change .js-params': 'updateParams' 'change .js-params': 'updateParams'
'input .js-params': 'updateParams' 'input .js-params': 'updateParams'
@ -32,74 +32,74 @@ class ChannelChat extends App.ControllerSubContent
name: 'chatId' name: 'chatId'
default: '1' default: '1'
type: 'Number' type: 'Number'
description: 'Identifier of the chat-topic.' description: __('Identifier of the chat-topic.')
} }
{ {
name: 'show' name: 'show'
default: true default: true
type: 'Boolean' type: 'Boolean'
description: 'Show the chat when ready.' description: __('Show the chat when ready.')
} }
{ {
name: 'target' name: 'target'
default: "$('body')" default: "$('body')"
type: 'jQuery Object' type: 'jQuery Object'
description: 'Where to append the chat to.' description: __('Where to append the chat to.')
} }
{ {
name: 'host' name: 'host'
default: '(Empty)' default: '(Empty)'
type: 'String' type: 'String'
description: "If left empty, the host gets auto-detected - in this case %s. The auto-detection reads out the host from the <script> tag. If you don't include it via a <script> tag you need to specify the host." description: __("If left empty, the host gets auto-detected - in this case %s. The auto-detection reads out the host from the <script> tag. If you don't include it via a <script> tag you need to specify the host.")
descriptionSubstitute: window.location.origin descriptionSubstitute: window.location.origin
} }
{ {
name: 'debug' name: 'debug'
default: false default: false
type: 'Boolean' type: 'Boolean'
description: 'Enables console logging.' description: __('Enables console logging.')
} }
{ {
name: 'title' name: 'title'
default: "'<strong>Chat</strong> with us!'" default: "'<strong>Chat</strong> with us!'"
type: 'String' type: 'String'
description: 'Welcome Title shown on the closed chat. Can contain HTML.' description: __('Welcome Title shown on the closed chat. Can contain HTML.')
} }
{ {
name: 'fontSize' name: 'fontSize'
default: 'undefined' default: 'undefined'
type: 'String' type: 'String'
description: 'CSS font-size with a unit like 12px, 1.5em. If left to undefined it inherits the font-size of the website.' description: __('CSS font-size with a unit like 12px, 1.5em. If left to undefined it inherits the font-size of the website.')
} }
{ {
name: 'flat' name: 'flat'
default: 'false' default: 'false'
type: 'Boolean' type: 'Boolean'
description: 'Removes the shadows for a flat look.' description: __('Removes the shadows for a flat look.')
} }
{ {
name: 'buttonClass' name: 'buttonClass'
default: "'open-zammad-chat'" default: "'open-zammad-chat'"
type: 'String' type: 'String'
description: 'Add this class to a button on your page that should open the chat.' description: __('Add this class to a button on your page that should open the chat.')
} }
{ {
name: 'inactiveClass' name: 'inactiveClass'
default: "'is-inactive'" default: "'is-inactive'"
type: 'String' type: 'String'
description: 'This class gets added to the button on initialization and gets removed once the chat connection got established.' description: __('This class gets added to the button on initialization and gets removed once the chat connection got established.')
} }
{ {
name: 'cssAutoload' name: 'cssAutoload'
default: 'true' default: 'true'
type: 'Boolean' type: 'Boolean'
description: 'Automatically loads the chat.css file. If you want to use your own css, just set it to false.' description: __('Automatically loads the chat.css file. If you want to use your own css, just set it to false.')
} }
{ {
name: 'cssUrl' name: 'cssUrl'
default: 'undefined' default: 'undefined'
type: 'String' type: 'String'
description: 'Location of an external chat.css file.' description: __('Location of an external chat.css file.')
} }
] ]
@ -353,7 +353,7 @@ class ChannelChat extends App.ControllerSubContent
@code.each (i, block) -> @code.each (i, block) ->
hljs.highlightBlock block hljs.highlightBlock block
App.Config.set('Chat', { prio: 4000, name: 'Chat', parent: '#channels', target: '#channels/chat', controller: ChannelChat, permission: ['admin.channel_chat'] }, 'NavBarAdmin') App.Config.set('Chat', { prio: 4000, name: __('Chat'), parent: '#channels', target: '#channels/chat', controller: ChannelChat, permission: ['admin.channel_chat'] }, 'NavBarAdmin')
class Topics extends App.Controller class Topics extends App.Controller
events: events:
@ -373,9 +373,9 @@ class Topics extends App.Controller
new: (e) => new: (e) =>
new App.ControllerGenericNew( new App.ControllerGenericNew(
pageData: pageData:
title: 'Chats' title: __('Chats')
object: 'Chat' object: __('Chat')
objects: 'Chats' objects: __('Chats')
genericObject: 'Chat' genericObject: 'Chat'
callback: @render callback: @render
container: @el.closest('.content') container: @el.closest('.content')
@ -389,7 +389,7 @@ class Topics extends App.Controller
id: id id: id
genericObject: 'Chat' genericObject: 'Chat'
pageData: pageData:
object: 'Chat' object: __('Chat')
container: @el.closest('.content') container: @el.closest('.content')
callback: @render callback: @render
) )

View file

@ -1,29 +1,29 @@
class ChannelEmail extends App.ControllerTabs class ChannelEmail extends App.ControllerTabs
requiredPermission: 'admin.channel_email' requiredPermission: 'admin.channel_email'
header: 'Email' header: __('Email')
constructor: -> constructor: ->
super super
@title 'Email', true @title __('Email'), true
@tabs = [ @tabs = [
{ {
name: 'Accounts', name: __('Accounts'),
target: 'c-account', target: 'c-account',
controller: ChannelEmailAccountOverview, controller: ChannelEmailAccountOverview,
}, },
{ {
name: 'Filter', name: __('Filter'),
target: 'c-filter', target: 'c-filter',
controller: App.ChannelEmailFilter, controller: App.ChannelEmailFilter,
}, },
{ {
name: 'Signatures', name: __('Signatures'),
target: 'c-signature', target: 'c-signature',
controller: App.ChannelEmailSignature, controller: App.ChannelEmailSignature,
}, },
{ {
name: 'Settings', name: __('Settings'),
target: 'c-setting', target: 'c-setting',
controller: App.SettingsArea, controller: App.SettingsArea,
params: { area: 'Email::Base' }, params: { area: 'Email::Base' },
@ -142,7 +142,7 @@ class ChannelEmailAccountOverview extends App.Controller
e.preventDefault() e.preventDefault()
id = $(e.target).closest('.action').data('id') id = $(e.target).closest('.action').data('id')
new App.ControllerConfirm( new App.ControllerConfirm(
message: 'Sure?' message: __('Sure?')
callback: => callback: =>
@ajax( @ajax(
id: 'email_delete' id: 'email_delete'
@ -197,7 +197,7 @@ class ChannelEmailAccountOverview extends App.Controller
channel_id = $(e.target).closest('.action').data('id') channel_id = $(e.target).closest('.action').data('id')
new App.ControllerGenericNew( new App.ControllerGenericNew(
pageData: pageData:
object: 'Email Address' object: __('Email Address')
genericObject: 'EmailAddress' genericObject: 'EmailAddress'
container: @el.closest('.content') container: @el.closest('.content')
item: item:
@ -210,7 +210,7 @@ class ChannelEmailAccountOverview extends App.Controller
id = $(e.target).closest('li').data('id') id = $(e.target).closest('li').data('id')
new App.ControllerGenericEdit( new App.ControllerGenericEdit(
pageData: pageData:
object: 'Email Address' object: __('Email Address')
genericObject: 'EmailAddress' genericObject: 'EmailAddress'
container: @el.closest('.content') container: @el.closest('.content')
id: id id: id
@ -254,11 +254,11 @@ class ChannelEmailEdit extends App.ControllerModal
buttonClose: true buttonClose: true
buttonCancel: true buttonCancel: true
buttonSubmit: true buttonSubmit: true
head: 'Channel' head: __('Channel')
content: => content: =>
configureAttributesBase = [ configureAttributesBase = [
{ name: 'group_id', display: 'Destination Group', tag: 'select', null: false, relation: 'Group', nulloption: true, filter: { active: true } }, { name: 'group_id', display: __('Destination Group'), tag: 'select', null: false, relation: 'Group', nulloption: true, filter: { active: true } },
] ]
@form = new App.ControllerForm( @form = new App.ControllerForm(
model: model:
@ -298,7 +298,7 @@ class ChannelEmailEdit extends App.ControllerModal
error: (xhr) => error: (xhr) =>
data = JSON.parse(xhr.responseText) data = JSON.parse(xhr.responseText)
@formEnable(e) @formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to save changes.') @el.find('.alert').removeClass('hidden').text(data.error || __('Unable to save changes.'))
) )
class ChannelEmailAccountWizard extends App.ControllerWizardModal class ChannelEmailAccountWizard extends App.ControllerWizardModal
@ -371,10 +371,10 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
# base # base
configureAttributesBase = [ configureAttributesBase = [
{ name: 'realname', display: 'Organization & Department Name', tag: 'input', type: 'text', limit: 160, null: false, placeholder: 'Organization Support', autocomplete: 'off' }, { name: 'realname', display: __('Organization & Department Name'), tag: 'input', type: 'text', limit: 160, null: false, placeholder: __('Organization Support'), autocomplete: 'off' },
{ name: 'email', display: 'Email', tag: 'input', type: 'email', limit: 120, null: false, placeholder: 'support@example.com', autocapitalize: false, autocomplete: 'off' }, { name: 'email', display: __('Email'), tag: 'input', type: 'email', limit: 120, null: false, placeholder: 'support@example.com', autocapitalize: false, autocomplete: 'off' },
{ name: 'password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: false, autocapitalize: false, autocomplete: 'new-password', single: true }, { name: 'password', display: __('Password'), tag: 'input', type: 'password', limit: 120, null: false, autocapitalize: false, autocomplete: 'new-password', single: true },
{ name: 'group_id', display: 'Destination Group', tag: 'select', null: false, relation: 'Group', nulloption: true }, { name: 'group_id', display: __('Destination Group'), tag: 'select', null: false, relation: 'Group', nulloption: true },
] ]
@formMeta = new App.ControllerForm( @formMeta = new App.ControllerForm(
el: @$('.base-settings'), el: @$('.base-settings'),
@ -386,7 +386,7 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
# outbound # outbound
configureAttributesOutbound = [ configureAttributesOutbound = [
{ name: 'adapter', display: 'Send Mails via', tag: 'select', multiple: false, null: false, options: @channelDriver.email.outbound }, { name: 'adapter', display: __('Send Mails via'), tag: 'select', multiple: false, null: false, options: @channelDriver.email.outbound },
] ]
new App.ControllerForm( new App.ControllerForm(
el: @$('.base-outbound-type') el: @$('.base-outbound-type')
@ -400,22 +400,22 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
# inbound # inbound
configureAttributesInbound = [ configureAttributesInbound = [
{ name: 'adapter', display: 'Type', tag: 'select', multiple: false, null: false, options: @channelDriver.email.inbound }, { name: 'adapter', display: __('Type'), tag: 'select', multiple: false, null: false, options: @channelDriver.email.inbound },
{ name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false }, { name: 'options::host', display: __('Host'), tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false },
{ name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autocomplete: 'off' }, { name: 'options::user', display: __('User'), tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autocomplete: 'off' },
{ name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: false, autocapitalize: false, autocomplete: 'new-password', single: true }, { name: 'options::password', display: __('Password'), tag: 'input', type: 'password', limit: 120, null: false, autocapitalize: false, autocomplete: 'new-password', single: true },
{ name: 'options::ssl', display: 'SSL/STARTTLS', tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, default: true, translate: true, item_class: 'formGroup--halfSize' }, { name: 'options::ssl', display: __('SSL/STARTTLS'), tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, default: true, translate: true, item_class: 'formGroup--halfSize' },
{ name: 'options::port', display: 'Port', tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false, default: '993', item_class: 'formGroup--halfSize' }, { name: 'options::port', display: __('Port'), tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false, default: '993', item_class: 'formGroup--halfSize' },
{ name: 'options::folder', display: 'Folder', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, item_class: 'formGroup--halfSize' }, { name: 'options::folder', display: __('Folder'), tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, item_class: 'formGroup--halfSize' },
{ name: 'options::keep_on_server', display: 'Keep messages on server', tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, translate: true, default: false, item_class: 'formGroup--halfSize' }, { name: 'options::keep_on_server', display: __('Keep messages on server'), tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, translate: true, default: false, item_class: 'formGroup--halfSize' },
] ]
if !@channel if !@channel
#Email Inbound form opened from new email wizard, show full settings #Email Inbound form opened from new email wizard, show full settings
configureAttributesInbound = [ configureAttributesInbound = [
{ name: 'options::realname', display: 'Organization & Department Name', tag: 'input', type: 'text', limit: 160, null: false, placeholder: 'Organization Support', autocomplete: 'off' }, { name: 'options::realname', display: __('Organization & Department Name'), tag: 'input', type: 'text', limit: 160, null: false, placeholder: __('Organization Support'), autocomplete: 'off' },
{ name: 'options::email', display: 'Email', tag: 'input', type: 'email', limit: 120, null: false, placeholder: 'support@example.com', autocapitalize: false, autocomplete: 'off' }, { name: 'options::email', display: __('Email'), tag: 'input', type: 'email', limit: 120, null: false, placeholder: 'support@example.com', autocapitalize: false, autocomplete: 'off' },
{ name: 'options::group_id', display: 'Destination Group', tag: 'select', null: false, relation: 'Group', nulloption: true }, { name: 'options::group_id', display: __('Destination Group'), tag: 'select', null: false, relation: 'Group', nulloption: true },
].concat(configureAttributesInbound) ].concat(configureAttributesInbound)
@ -468,10 +468,10 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
adapter = @$('.js-outbound [name=adapter]').val() adapter = @$('.js-outbound [name=adapter]').val()
if adapter is 'smtp' if adapter is 'smtp'
configureAttributesOutbound = [ configureAttributesOutbound = [
{ name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autofocus: true }, { name: 'options::host', display: __('Host'), tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autofocus: true },
{ name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, autocomplete: 'off', }, { name: 'options::user', display: __('User'), tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, autocomplete: 'off', },
{ name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false, autocomplete: 'new-password', single: true }, { name: 'options::password', display: __('Password'), tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false, autocomplete: 'new-password', single: true },
{ name: 'options::port', display: 'Port', tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false }, { name: 'options::port', display: __('Port'), tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false },
] ]
@form = new App.ControllerForm( @form = new App.ControllerForm(
el: @$('.base-outbound-settings') el: @$('.base-outbound-settings')
@ -525,10 +525,10 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
else if data.result is 'duplicate' else if data.result is 'duplicate'
@showSlide('js-intro') @showSlide('js-intro')
@showAlert('js-intro', 'Account already exists!') @showAlert('js-intro', __('Account already exists!'))
else else
@showSlide('js-inbound') @showSlide('js-inbound')
@showAlert('js-inbound', 'Unable to detect your server settings. Manual configuration needed.') @showAlert('js-inbound', __('Unable to detect your server settings. Manual configuration needed.'))
@$('.js-inbound [name="options::user"]').val(@account['meta']['email']) @$('.js-inbound [name="options::user"]').val(@account['meta']['email'])
@$('.js-inbound [name="options::password"]').val(@account['meta']['password']) @$('.js-inbound [name="options::password"]').val(@account['meta']['password'])
@$('.js-inbound [name="options::email"]').val(@account['meta']['email']) @$('.js-inbound [name="options::email"]').val(@account['meta']['email'])
@ -749,7 +749,7 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
@delay( @delay(
=> =>
@showSlide('js-intro') @showSlide('js-intro')
@showAlert('js-intro', 'Unable to verify sending and receiving. Please check your settings.') @showAlert('js-intro', __('Unable to verify sending and receiving. Please check your settings.'))
2300 2300
) )
@ -759,7 +759,7 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
@verify(@account, count + 1) @verify(@account, count + 1)
error: => error: =>
@showSlide('js-intro') @showSlide('js-intro')
@showAlert('js-intro', 'Unable to verify sending and receiving. Please check your settings.') @showAlert('js-intro', __('Unable to verify sending and receiving. Please check your settings.'))
) )
hide: (e) => hide: (e) =>
@ -830,7 +830,7 @@ class ChannelEmailNotificationWizard extends App.ControllerWizardModal
# outbound # outbound
configureAttributesOutbound = [ configureAttributesOutbound = [
{ name: 'adapter', display: 'Send Mails via', tag: 'select', multiple: false, null: false, options: @channelDriver.email.outbound }, { name: 'adapter', display: __('Send Mails via'), tag: 'select', multiple: false, null: false, options: @channelDriver.email.outbound },
] ]
new App.ControllerForm( new App.ControllerForm(
el: @$('.base-outbound-type') el: @$('.base-outbound-type')
@ -849,10 +849,10 @@ class ChannelEmailNotificationWizard extends App.ControllerWizardModal
adapter = @$('.js-outbound [name=adapter]').val() adapter = @$('.js-outbound [name=adapter]').val()
if adapter is 'smtp' if adapter is 'smtp'
configureAttributesOutbound = [ configureAttributesOutbound = [
{ name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autofocus: true }, { name: 'options::host', display: __('Host'), tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autofocus: true },
{ name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, autocomplete: 'off' }, { name: 'options::user', display: __('User'), tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, autocomplete: 'off' },
{ name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false, autocomplete: 'new-password', single: true }, { name: 'options::password', display: __('Password'), tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false, autocomplete: 'new-password', single: true },
{ name: 'options::port', display: 'Port', tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false }, { name: 'options::port', display: __('Port'), tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false },
] ]
@form = new App.ControllerForm( @form = new App.ControllerForm(
el: @$('.base-outbound-settings') el: @$('.base-outbound-settings')
@ -900,4 +900,4 @@ class ChannelEmailNotificationWizard extends App.ControllerWizardModal
@enable(e) @enable(e)
) )
App.Config.set('Email', { prio: 3000, name: 'Email', parent: '#channels', target: '#channels/email', controller: ChannelEmail, permission: ['admin.channel_email'] }, 'NavBarAdmin') App.Config.set('Email', { prio: 3000, name: __('Email'), parent: '#channels', target: '#channels/email', controller: ChannelEmail, permission: ['admin.channel_email'] }, 'NavBarAdmin')

View file

@ -1,6 +1,6 @@
class ChannelFacebook extends App.ControllerSubContent class ChannelFacebook extends App.ControllerSubContent
requiredPermission: 'admin.channel_facebook' requiredPermission: 'admin.channel_facebook'
header: 'Facebook' header: __('Facebook')
events: events:
'click .js-new': 'new' 'click .js-new': 'new'
'click .js-edit': 'edit' 'click .js-edit': 'edit'
@ -95,7 +95,7 @@ class ChannelFacebook extends App.ControllerSubContent
e.preventDefault() e.preventDefault()
id = $(e.target).closest('.action').data('id') id = $(e.target).closest('.action').data('id')
new App.ControllerConfirm( new App.ControllerConfirm(
message: 'Sure?' message: __('Sure?')
callback: => callback: =>
@ajax( @ajax(
id: 'facebook_delete' id: 'facebook_delete'
@ -136,7 +136,7 @@ class ChannelFacebook extends App.ControllerSubContent
) )
class AppConfig extends App.ControllerModal class AppConfig extends App.ControllerModal
head: 'Connect Facebook App' head: __('Connect Facebook App')
shown: true shown: true
button: 'Connect' button: 'Connect'
buttonCancel: true buttonCancel: true
@ -178,15 +178,15 @@ class AppConfig extends App.ControllerModal
@isChanged = true @isChanged = true
@close() @close()
fail: => fail: =>
@el.find('.alert').removeClass('hidden').text('Unable to create entry.') @el.find('.alert').removeClass('hidden').text(__('Unable to create entry.'))
) )
return return
@formEnable(e) @formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to verify App.') @el.find('.alert').removeClass('hidden').text(data.error || __('Unable to verify App.'))
) )
class AccountEdit extends App.ControllerModal class AccountEdit extends App.ControllerModal
head: 'Facebook Account' head: __('Facebook Account')
shown: true shown: true
buttonCancel: true buttonCancel: true
@ -242,7 +242,7 @@ class AccountEdit extends App.ControllerModal
error: (xhr) => error: (xhr) =>
data = JSON.parse(xhr.responseText) data = JSON.parse(xhr.responseText)
@formEnable(e) @formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to save changes.') @el.find('.alert').removeClass('hidden').text(data.error || __('Unable to save changes.'))
) )
App.Config.set('Facebook', { prio: 5100, name: 'Facebook', parent: '#channels', target: '#channels/facebook', controller: ChannelFacebook, permission: ['admin.channel_facebook'] }, 'NavBarAdmin') App.Config.set('Facebook', { prio: 5100, name: __('Facebook'), parent: '#channels', target: '#channels/facebook', controller: ChannelFacebook, permission: ['admin.channel_facebook'] }, 'NavBarAdmin')

View file

@ -1,7 +1,7 @@
# coffeelint: disable=no_unnecessary_double_quotes # coffeelint: disable=no_unnecessary_double_quotes
class ChannelForm extends App.ControllerSubContent class ChannelForm extends App.ControllerSubContent
requiredPermission: 'admin.channel_formular' requiredPermission: 'admin.channel_formular'
header: 'Form' header: __('Form')
events: events:
'change form.js-paramsDesigner': 'updateParamsDesigner' 'change form.js-paramsDesigner': 'updateParamsDesigner'
'keyup form.js-paramsDesigner': 'updateParamsDesigner' 'keyup form.js-paramsDesigner': 'updateParamsDesigner'
@ -88,4 +88,4 @@ class ChannelForm extends App.ControllerSubContent
value = @paramsSetting.find('[name=group_id]').val() value = @paramsSetting.find('[name=group_id]').val()
App.Setting.set('form_ticket_create_group_id', value) App.Setting.set('form_ticket_create_group_id', value)
App.Config.set('Form', { prio: 2000, name: 'Form', parent: '#channels', target: '#channels/form', controller: ChannelForm, permission: ['admin.channel_formular'] }, 'NavBarAdmin') App.Config.set('Form', { prio: 2000, name: __('Form'), parent: '#channels', target: '#channels/form', controller: ChannelForm, permission: ['admin.channel_formular'] }, 'NavBarAdmin')

View file

@ -1,29 +1,29 @@
class App.ChannelGoogle extends App.ControllerTabs class App.ChannelGoogle extends App.ControllerTabs
requiredPermission: 'admin.channel_google' requiredPermission: 'admin.channel_google'
header: 'Google' header: __('Google')
constructor: -> constructor: ->
super super
@title 'Google', true @title __('Google'), true
@tabs = [ @tabs = [
{ {
name: 'Accounts', name: __('Accounts'),
target: 'c-account', target: 'c-account',
controller: ChannelAccountOverview, controller: ChannelAccountOverview,
}, },
{ {
name: 'Filter', name: __('Filter'),
target: 'c-filter', target: 'c-filter',
controller: App.ChannelEmailFilter, controller: App.ChannelEmailFilter,
}, },
{ {
name: 'Signatures', name: __('Signatures'),
target: 'c-signature', target: 'c-signature',
controller: App.ChannelEmailSignature, controller: App.ChannelEmailSignature,
}, },
{ {
name: 'Settings', name: __('Settings'),
target: 'c-setting', target: 'c-setting',
controller: App.SettingsArea, controller: App.SettingsArea,
params: { area: 'Email::Base' }, params: { area: 'Email::Base' },
@ -145,7 +145,7 @@ class ChannelAccountOverview extends App.ControllerSubContent
e.preventDefault() e.preventDefault()
id = $(e.target).closest('.action').data('id') id = $(e.target).closest('.action').data('id')
new App.ControllerConfirm( new App.ControllerConfirm(
message: 'Sure?' message: __('Sure?')
callback: => callback: =>
@ajax( @ajax(
id: 'google_delete' id: 'google_delete'
@ -215,11 +215,11 @@ class ChannelAccountOverview extends App.ControllerSubContent
@load() @load()
@notify @notify
type: 'success' type: 'success'
msg: 'Rollback of channel migration succeeded!' msg: __('Rollback of channel migration succeeded!')
error: (data) => error: (data) =>
@notify @notify
type: 'error' type: 'error'
msg: 'Failed to rollback migration of the channel!' msg: __('Failed to rollback migration of the channel!')
) )
groupChange: (e) => groupChange: (e) =>
@ -237,7 +237,7 @@ class ChannelAccountOverview extends App.ControllerSubContent
channel_id = $(e.target).closest('.action').data('id') channel_id = $(e.target).closest('.action').data('id')
new App.ControllerGenericNew( new App.ControllerGenericNew(
pageData: pageData:
object: 'Email Address' object: __('Email Address')
genericObject: 'EmailAddress' genericObject: 'EmailAddress'
container: @el.closest('.content') container: @el.closest('.content')
item: item:
@ -250,7 +250,7 @@ class ChannelAccountOverview extends App.ControllerSubContent
id = $(e.target).closest('li').data('id') id = $(e.target).closest('li').data('id')
new App.ControllerGenericEdit( new App.ControllerGenericEdit(
pageData: pageData:
object: 'Email Address' object: __('Email Address')
genericObject: 'EmailAddress' genericObject: 'EmailAddress'
container: @el.closest('.content') container: @el.closest('.content')
id: id id: id
@ -271,12 +271,12 @@ class ChannelInboundEdit extends App.ControllerModal
buttonClose: true buttonClose: true
buttonCancel: true buttonCancel: true
buttonSubmit: true buttonSubmit: true
head: 'Channel' head: __('Channel')
content: => content: =>
configureAttributesBase = [ configureAttributesBase = [
{ name: 'options::folder', display: 'Folder', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, placeholder: 'optional' }, { name: 'options::folder', display: __('Folder'), tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, placeholder: __('optional') },
{ name: 'options::keep_on_server', display: 'Keep messages on server', tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, translate: true, default: false }, { name: 'options::keep_on_server', display: __('Keep messages on server'), tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, translate: true, default: false },
] ]
@form = new App.ControllerForm( @form = new App.ControllerForm(
model: model:
@ -323,7 +323,7 @@ class ChannelInboundEdit extends App.ControllerModal
details = xhr.responseJSON || {} details = xhr.responseJSON || {}
@notify @notify
type: 'error' type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to save changes.') msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to save changes.'))
timeout: 6000 timeout: 6000
) )
@ -331,11 +331,11 @@ class ChannelGroupEdit extends App.ControllerModal
buttonClose: true buttonClose: true
buttonCancel: true buttonCancel: true
buttonSubmit: true buttonSubmit: true
head: 'Channel' head: __('Channel')
content: => content: =>
configureAttributesBase = [ configureAttributesBase = [
{ name: 'group_id', display: 'Destination Group', tag: 'select', null: false, relation: 'Group', nulloption: true, filter: { active: true } }, { name: 'group_id', display: __('Destination Group'), tag: 'select', null: false, relation: 'Group', nulloption: true, filter: { active: true } },
] ]
@form = new App.ControllerForm( @form = new App.ControllerForm(
model: model:
@ -375,11 +375,11 @@ class ChannelGroupEdit extends App.ControllerModal
error: (xhr) => error: (xhr) =>
data = JSON.parse(xhr.responseText) data = JSON.parse(xhr.responseText)
@formEnable(e) @formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to save changes.') @el.find('.alert').removeClass('hidden').text(data.error || __('Unable to save changes.'))
) )
class AppConfig extends App.ControllerModal class AppConfig extends App.ControllerModal
head: 'Connect Google App' head: __('Connect Google App')
shown: true shown: true
button: 'Connect' button: 'Connect'
buttonCancel: true buttonCancel: true
@ -421,11 +421,11 @@ class AppConfig extends App.ControllerModal
@isChanged = true @isChanged = true
@close() @close()
fail: => fail: =>
@el.find('.alert').removeClass('hidden').text('Unable to create entry.') @el.find('.alert').removeClass('hidden').text(__('Unable to create entry.'))
) )
return return
@formEnable(e) @formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to verify App.') @el.find('.alert').removeClass('hidden').text(data.error || __('Unable to verify App.'))
) )
App.Config.set('google', { prio: 5000, name: 'Google', parent: '#channels', target: '#channels/google', controller: App.ChannelGoogle, permission: ['admin.channel_google'] }, 'NavBarAdmin') App.Config.set('google', { prio: 5000, name: __('Google'), parent: '#channels', target: '#channels/google', controller: App.ChannelGoogle, permission: ['admin.channel_google'] }, 'NavBarAdmin')

View file

@ -1,29 +1,29 @@
class App.ChannelMicrosoft365 extends App.ControllerTabs class App.ChannelMicrosoft365 extends App.ControllerTabs
requiredPermission: 'admin.channel_microsoft365' requiredPermission: 'admin.channel_microsoft365'
header: 'Microsoft 365' header: __('Microsoft 365')
constructor: -> constructor: ->
super super
@title 'Microsoft 365', true @title __('Microsoft 365'), true
@tabs = [ @tabs = [
{ {
name: 'Accounts', name: __('Accounts'),
target: 'c-account', target: 'c-account',
controller: ChannelAccountOverview, controller: ChannelAccountOverview,
}, },
{ {
name: 'Filter', name: __('Filter'),
target: 'c-filter', target: 'c-filter',
controller: App.ChannelEmailFilter, controller: App.ChannelEmailFilter,
}, },
{ {
name: 'Signatures', name: __('Signatures'),
target: 'c-signature', target: 'c-signature',
controller: App.ChannelEmailSignature, controller: App.ChannelEmailSignature,
}, },
{ {
name: 'Settings', name: __('Settings'),
target: 'c-setting', target: 'c-setting',
controller: App.SettingsArea, controller: App.SettingsArea,
params: { area: 'Email::Base' }, params: { area: 'Email::Base' },
@ -136,7 +136,7 @@ class ChannelAccountOverview extends App.ControllerSubContent
e.preventDefault() e.preventDefault()
id = $(e.target).closest('.action').data('id') id = $(e.target).closest('.action').data('id')
new App.ControllerConfirm( new App.ControllerConfirm(
message: 'Sure?' message: __('Sure?')
callback: => callback: =>
@ajax( @ajax(
id: 'microsoft365_delete' id: 'microsoft365_delete'
@ -206,11 +206,11 @@ class ChannelAccountOverview extends App.ControllerSubContent
@load() @load()
@notify @notify
type: 'success' type: 'success'
msg: 'Rollback of channel migration succeeded!' msg: __('Rollback of channel migration succeeded!')
error: (data) => error: (data) =>
@notify @notify
type: 'error' type: 'error'
msg: 'Failed to rollback migration of the channel!' msg: __('Failed to rollback migration of the channel!')
) )
groupChange: (e) => groupChange: (e) =>
@ -228,7 +228,7 @@ class ChannelAccountOverview extends App.ControllerSubContent
channel_id = $(e.target).closest('.action').data('id') channel_id = $(e.target).closest('.action').data('id')
new App.ControllerGenericNew( new App.ControllerGenericNew(
pageData: pageData:
object: 'Email Address' object: __('Email Address')
genericObject: 'EmailAddress' genericObject: 'EmailAddress'
container: @el.closest('.content') container: @el.closest('.content')
item: item:
@ -241,7 +241,7 @@ class ChannelAccountOverview extends App.ControllerSubContent
id = $(e.target).closest('li').data('id') id = $(e.target).closest('li').data('id')
new App.ControllerGenericEdit( new App.ControllerGenericEdit(
pageData: pageData:
object: 'Email Address' object: __('Email Address')
genericObject: 'EmailAddress' genericObject: 'EmailAddress'
container: @el.closest('.content') container: @el.closest('.content')
id: id id: id
@ -262,12 +262,12 @@ class ChannelInboundEdit extends App.ControllerModal
buttonClose: true buttonClose: true
buttonCancel: true buttonCancel: true
buttonSubmit: true buttonSubmit: true
head: 'Channel' head: __('Channel')
content: => content: =>
configureAttributesBase = [ configureAttributesBase = [
{ name: 'options::folder', display: 'Folder', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false }, { name: 'options::folder', display: __('Folder'), tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false },
{ name: 'options::keep_on_server', display: 'Keep messages on server', tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, translate: true, default: false }, { name: 'options::keep_on_server', display: __('Keep messages on server'), tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, translate: true, default: false },
] ]
@form = new App.ControllerForm( @form = new App.ControllerForm(
model: model:
@ -314,7 +314,7 @@ class ChannelInboundEdit extends App.ControllerModal
details = xhr.responseJSON || {} details = xhr.responseJSON || {}
@notify @notify
type: 'error' type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to save changes.') msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to save changes.'))
timeout: 6000 timeout: 6000
) )
@ -322,11 +322,11 @@ class ChannelGroupEdit extends App.ControllerModal
buttonClose: true buttonClose: true
buttonCancel: true buttonCancel: true
buttonSubmit: true buttonSubmit: true
head: 'Channel' head: __('Channel')
content: => content: =>
configureAttributesBase = [ configureAttributesBase = [
{ name: 'group_id', display: 'Destination Group', tag: 'select', null: false, relation: 'Group', nulloption: true, filter: { active: true } }, { name: 'group_id', display: __('Destination Group'), tag: 'select', null: false, relation: 'Group', nulloption: true, filter: { active: true } },
] ]
@form = new App.ControllerForm( @form = new App.ControllerForm(
model: model:
@ -366,11 +366,11 @@ class ChannelGroupEdit extends App.ControllerModal
error: (xhr) => error: (xhr) =>
data = JSON.parse(xhr.responseText) data = JSON.parse(xhr.responseText)
@formEnable(e) @formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to save changes.') @el.find('.alert').removeClass('hidden').text(data.error || __('Unable to save changes.'))
) )
class AppConfig extends App.ControllerModal class AppConfig extends App.ControllerModal
head: 'Connect Microsoft 365 App' head: __('Connect Microsoft 365 App')
shown: true shown: true
button: 'Connect' button: 'Connect'
buttonCancel: true buttonCancel: true
@ -412,11 +412,11 @@ class AppConfig extends App.ControllerModal
@isChanged = true @isChanged = true
@close() @close()
fail: => fail: =>
@el.find('.alert').removeClass('hidden').text('Unable to create entry.') @el.find('.alert').removeClass('hidden').text(__('Unable to create entry.'))
) )
return return
@formEnable(e) @formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to verify App.') @el.find('.alert').removeClass('hidden').text(data.error || __('Unable to verify App.'))
) )
App.Config.set('microsoft365', { prio: 5000, name: 'Microsoft 365', parent: '#channels', target: '#channels/microsoft365', controller: App.ChannelMicrosoft365, permission: ['admin.channel_microsoft365'] }, 'NavBarAdmin') App.Config.set('microsoft365', { prio: 5000, name: __('Microsoft 365'), parent: '#channels', target: '#channels/microsoft365', controller: App.ChannelMicrosoft365, permission: ['admin.channel_microsoft365'] }, 'NavBarAdmin')

View file

@ -1,13 +1,13 @@
class ChannelSms extends App.ControllerTabs class ChannelSms extends App.ControllerTabs
requiredPermission: 'admin.channel_sms' requiredPermission: 'admin.channel_sms'
header: 'SMS' header: __('SMS')
constructor: -> constructor: ->
super super
@title 'SMS', true @title __('SMS'), true
@tabs = [ @tabs = [
{ {
name: 'Accounts', name: __('Accounts'),
target: 'c-account', target: 'c-account',
controller: ChannelSmsAccountOverview, controller: ChannelSmsAccountOverview,
}, },
@ -136,11 +136,11 @@ class ChannelSmsAccountOverview extends App.Controller
) )
class ChannelSmsAccount extends App.ControllerModal class ChannelSmsAccount extends App.ControllerModal
head: 'SMS Account' head: __('SMS Account')
buttonCancel: true buttonCancel: true
centerButtons: [ centerButtons: [
{ {
text: 'Test' text: __('Test')
className: 'js-test' className: 'js-test'
} }
] ]
@ -164,7 +164,7 @@ class ChannelSmsAccount extends App.ControllerModal
el: el.find('.js-channelAdapterSelector') el: el.find('.js-channelAdapterSelector')
model: model:
configure_attributes: [ configure_attributes: [
{ name: 'options::adapter', display: 'Provider', tag: 'select', null: false, options: options, nulloption: true } { name: 'options::adapter', display: __('Provider'), tag: 'select', null: false, options: options, nulloption: true }
] ]
className: '' className: ''
params: @channel params: @channel
@ -198,7 +198,7 @@ class ChannelSmsAccount extends App.ControllerModal
el: el.find('.js-channelWebhook') el: el.find('.js-channelWebhook')
model: model:
configure_attributes: [ configure_attributes: [
{ name: 'options::webhook', display: 'Webhook', tag: 'input', type: 'text', limit: 200, null: false, default: webhook, disabled: true }, { name: 'options::webhook', display: __('Webhook'), tag: 'input', type: 'text', limit: 200, null: false, default: webhook, disabled: true },
] ]
className: '' className: ''
params: @channel params: @channel
@ -262,7 +262,7 @@ class ChannelSmsAccount extends App.ControllerModal
fail: (settings, details) -> fail: (settings, details) ->
ui.log 'errors', details ui.log 'errors', details
ui.formEnable(e) ui.formEnable(e)
ui.showAlert(details.error_human || details.error || 'Unable to update object!') ui.showAlert(details.error_human || details.error || __('Unable to update object!'))
) )
onTest: (e) -> onTest: (e) ->
@ -273,11 +273,11 @@ class ChannelSmsAccount extends App.ControllerModal
) )
class ChannelSmsNotification extends App.ControllerModal class ChannelSmsNotification extends App.ControllerModal
head: 'SMS Notification' head: __('SMS Notification')
buttonCancel: true buttonCancel: true
centerButtons: [ centerButtons: [
{ {
text: 'Test' text: __('Test')
className: 'js-test' className: 'js-test'
} }
] ]
@ -303,7 +303,7 @@ class ChannelSmsNotification extends App.ControllerModal
el: el.find('.js-channelAdapterSelector') el: el.find('.js-channelAdapterSelector')
model: model:
configure_attributes: [ configure_attributes: [
{ name: 'options::adapter', display: 'Provider', tag: 'select', null: false, options: options, nulloption: true } { name: 'options::adapter', display: __('Provider'), tag: 'select', null: false, options: options, nulloption: true }
] ]
className: '' className: ''
params: @channel params: @channel
@ -376,7 +376,7 @@ class ChannelSmsNotification extends App.ControllerModal
fail: (settings, details) -> fail: (settings, details) ->
ui.log 'errors', details ui.log 'errors', details
ui.formEnable(e) ui.formEnable(e)
ui.showAlert(details.error_human || details.error || 'Unable to update object!') ui.showAlert(details.error_human || details.error || __('Unable to update object!'))
) )
onTest: (e) -> onTest: (e) ->
@ -387,15 +387,15 @@ class ChannelSmsNotification extends App.ControllerModal
) )
class TestModal extends App.ControllerModal class TestModal extends App.ControllerModal
head: 'Test SMS provider' head: __('Test SMS provider')
buttonCancel: true buttonCancel: true
content: -> content: ->
form = new App.ControllerForm( form = new App.ControllerForm(
model: model:
configure_attributes: [ configure_attributes: [
{ name: 'recipient', display: 'Recipient', tag: 'input', null: false } { name: 'recipient', display: __('Recipient'), tag: 'input', null: false }
{ name: 'message', display: 'Message', tag: 'input', null: false, default: 'Test message from Zammad' } { name: 'message', display: __('Message'), tag: 'input', null: false, default: __('Test message from Zammad') }
] ]
className: '' className: ''
) )
@ -435,8 +435,8 @@ class TestModal extends App.ControllerModal
data = JSON.parse(xhr.responseText) data = JSON.parse(xhr.responseText)
@formEnable(@el) @formEnable(@el)
@el.find('.js-danger') @el.find('.js-danger')
.text(@T(data.error || 'Unable to perform test')) .text(@T(data.error || __('Unable to perform test')))
.removeClass('hide') .removeClass('hide')
) )
App.Config.set('SMS', { prio: 3100, name: 'SMS', parent: '#channels', target: '#channels/sms', controller: ChannelSms, permission: ['admin.channel_sms'] }, 'NavBarAdmin') App.Config.set('SMS', { prio: 3100, name: __('SMS'), parent: '#channels', target: '#channels/sms', controller: ChannelSms, permission: ['admin.channel_sms'] }, 'NavBarAdmin')

View file

@ -63,7 +63,7 @@ class ChannelTelegram extends App.ControllerSubContent
e.preventDefault() e.preventDefault()
id = $(e.target).closest('.action').data('id') id = $(e.target).closest('.action').data('id')
new App.ControllerConfirm( new App.ControllerConfirm(
message: 'Sure?' message: __('Sure?')
callback: => callback: =>
@ajax( @ajax(
id: 'telegram_delete' id: 'telegram_delete'
@ -104,7 +104,7 @@ class ChannelTelegram extends App.ControllerSubContent
) )
class BotAdd extends App.ControllerModal class BotAdd extends App.ControllerModal
head: 'Add Telegram Bot' head: __('Add Telegram Bot')
shown: true shown: true
button: 'Add' button: 'Add'
buttonCancel: true buttonCancel: true
@ -149,12 +149,12 @@ class BotAdd extends App.ControllerModal
error: (xhr) => error: (xhr) =>
data = JSON.parse(xhr.responseText) data = JSON.parse(xhr.responseText)
@formEnable(e) @formEnable(e)
error_message = App.i18n.translateContent(data.error || 'Unable to save Bot.') error_message = App.i18n.translateContent(data.error || __('Unable to save Bot.'))
@el.find('.alert').removeClass('hidden').text(error_message) @el.find('.alert').removeClass('hidden').text(error_message)
) )
class BotEdit extends App.ControllerModal class BotEdit extends App.ControllerModal
head: 'Telegram Account' head: __('Telegram Account')
shown: true shown: true
buttonCancel: true buttonCancel: true
@ -197,8 +197,8 @@ class BotEdit extends App.ControllerModal
error: (xhr) => error: (xhr) =>
data = JSON.parse(xhr.responseText) data = JSON.parse(xhr.responseText)
@formEnable(e) @formEnable(e)
error_message = App.i18n.translateContent(data.error || 'Unable to save changes.') error_message = App.i18n.translateContent(data.error || __('Unable to save changes.'))
@el.find('.alert').removeClass('hidden').text(error_message) @el.find('.alert').removeClass('hidden').text(error_message)
) )
App.Config.set('Telegram', { prio: 5100, name: 'Telegram', parent: '#channels', target: '#channels/telegram', controller: ChannelTelegram, permission: ['admin.channel_telegram'] }, 'NavBarAdmin') App.Config.set('Telegram', { prio: 5100, name: __('Telegram'), parent: '#channels', target: '#channels/telegram', controller: ChannelTelegram, permission: ['admin.channel_telegram'] }, 'NavBarAdmin')

View file

@ -102,7 +102,7 @@ class ChannelTwitter extends App.ControllerSubContent
e.preventDefault() e.preventDefault()
id = $(e.target).closest('.action').data('id') id = $(e.target).closest('.action').data('id')
new App.ControllerConfirm( new App.ControllerConfirm(
message: 'Sure?' message: __('Sure?')
callback: => callback: =>
@ajax( @ajax(
id: 'twitter_delete' id: 'twitter_delete'
@ -143,7 +143,7 @@ class ChannelTwitter extends App.ControllerSubContent
) )
class AppConfig extends App.ControllerModal class AppConfig extends App.ControllerModal
head: 'Connect Twitter App' head: __('Connect Twitter App')
shown: true shown: true
button: 'Connect' button: 'Connect'
buttonCancel: true buttonCancel: true
@ -185,15 +185,15 @@ class AppConfig extends App.ControllerModal
@isChanged = true @isChanged = true
@close() @close()
fail: => fail: =>
@el.find('.alert').removeClass('hidden').text('Unable to create entry.') @el.find('.alert').removeClass('hidden').text(__('Unable to create entry.'))
) )
return return
@formEnable(e) @formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to verify App.') @el.find('.alert').removeClass('hidden').text(data.error || __('Unable to verify App.'))
) )
class AccountEdit extends App.ControllerModal class AccountEdit extends App.ControllerModal
head: 'Twitter Account' head: __('Twitter Account')
shown: true shown: true
buttonCancel: true buttonCancel: true
@ -296,7 +296,7 @@ class AccountEdit extends App.ControllerModal
error: (xhr) => error: (xhr) =>
data = JSON.parse(xhr.responseText) data = JSON.parse(xhr.responseText)
@formEnable(e) @formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to save changes.') @el.find('.alert').removeClass('hidden').text(data.error || __('Unable to save changes.'))
) )
App.Config.set('Twitter', { prio: 5000, name: 'Twitter', parent: '#channels', target: '#channels/twitter', controller: ChannelTwitter, permission: ['admin.channel_twitter'] }, 'NavBarAdmin') App.Config.set('Twitter', { prio: 5000, name: __('Twitter'), parent: '#channels', target: '#channels/twitter', controller: ChannelTwitter, permission: ['admin.channel_twitter'] }, 'NavBarAdmin')

View file

@ -1,14 +1,14 @@
class ChannelWeb extends App.ControllerTabs class ChannelWeb extends App.ControllerTabs
requiredPermission: 'admin.channel_web' requiredPermission: 'admin.channel_web'
header: 'Web' header: __('Web')
constructor: -> constructor: ->
super super
@title 'Web', true @title __('Web'), true
@tabs = [ @tabs = [
{ {
name: 'Settings', name: __('Settings'),
target: 'w-setting', target: 'w-setting',
controller: App.SettingsArea, params: { area: 'CustomerWeb::Base' }, controller: App.SettingsArea, params: { area: 'CustomerWeb::Base' },
}, },
@ -16,4 +16,4 @@ class ChannelWeb extends App.ControllerTabs
@render() @render()
App.Config.set('Web', { prio: 1000, name: 'Web', parent: '#channels', target: '#channels/web', controller: ChannelWeb, permission: ['admin.channel_web'] }, 'NavBarAdmin') App.Config.set('Web', { prio: 1000, name: __('Web'), parent: '#channels', target: '#channels/web', controller: ChannelWeb, permission: ['admin.channel_web'] }, 'NavBarAdmin')

View file

@ -38,7 +38,7 @@ class App.DashboardFirstSteps extends App.Controller
e.preventDefault() e.preventDefault()
new App.InviteUser( new App.InviteUser(
#container: @el.closest('.content') #container: @el.closest('.content')
head: 'Invite Colleagues' head: __('Invite Colleagues')
screen: 'invite_agent' screen: 'invite_agent'
) )
@ -46,7 +46,7 @@ class App.DashboardFirstSteps extends App.Controller
e.preventDefault() e.preventDefault()
new App.InviteUser( new App.InviteUser(
#container: @el.closest('.content') #container: @el.closest('.content')
head: 'Invite Customer' head: __('Invite Customer')
screen: 'invite_customer' screen: 'invite_customer'
signup: true signup: true
) )
@ -82,7 +82,7 @@ class App.DashboardFirstSteps extends App.Controller
e.preventDefault() e.preventDefault()
modal = new App.ControllerModal( modal = new App.ControllerModal(
head: 'Test Ticket' head: __('Test Ticket')
#container: @el.parents('.content') #container: @el.parents('.content')
content: @testTicketLoading content: @testTicketLoading
shown: true shown: true

View file

@ -2,22 +2,22 @@ class App.FirstStepsClues extends App.Controller
clues: [ clues: [
{ {
container: '.js-dashboardMenuItem' container: '.js-dashboardMenuItem'
headline: 'Dashboard' headline: __('Dashboard')
text: 'Here you see a quick overview of your and other agents\' performance.' text: __('Here you see a quick overview of your and other agents\' performance.')
actions: [ actions: [
'hover' 'hover'
] ]
} }
{ {
container: '.search-holder' container: '.search-holder'
headline: 'Search' headline: __('Search')
text: 'Here you can search for tickets, customers and organizations. Use the wildcard §*§ to find everything. E. g. §smi*§ or §rosent*l§. You also can use ||double quotes|| for searching phrases §"some phrase"§.' text: __('Here you can search for tickets, customers and organizations. Use the wildcard §*§ to find everything. E. g. §smi*§ or §rosent*l§. You also can use ||double quotes|| for searching phrases §"some phrase"§.')
actions: [] actions: []
} }
{ {
container: '.user-menu .add' container: '.user-menu .add'
headline: 'Create' headline: __('Create')
text: 'Here you can create new tickets. Also if you have the permissions you can create new customers and organizations.' text: __('Here you can create new tickets. Also if you have the permissions you can create new customers and organizations.')
actions: [ actions: [
'hover .navigation', 'hover .navigation',
'hover .user-menu .add' 'hover .user-menu .add'
@ -25,8 +25,8 @@ class App.FirstStepsClues extends App.Controller
} }
{ {
container: '.user-menu .user .dropdown-menu' container: '.user-menu .user .dropdown-menu'
headline: 'Personal Settings' headline: __('Personal Settings')
text: 'Here you can sign out, change the frontend language and see your last viewed items.' text: __('Here you can sign out, change the frontend language and see your last viewed items.')
actions: [ actions: [
'hover .navigation', 'hover .navigation',
'click .user-menu .user .js-action', 'click .user-menu .user .js-action',
@ -35,8 +35,8 @@ class App.FirstStepsClues extends App.Controller
} }
{ {
container: '.js-overviewsMenuItem' container: '.js-overviewsMenuItem'
headline: 'Overviews' headline: __('Overviews')
text: 'Here you find your ticket overviews for open, assigned and escalated tickets.' text: __('Here you find your ticket overviews for open, assigned and escalated tickets.')
actions: [ actions: [
'hover' 'hover'
] ]

View file

@ -24,7 +24,7 @@ class Stats extends App.ControllerDashboardStatsBase
for id, channel of data.StatsTicketChannelDistribution.channels for id, channel of data.StatsTicketChannelDistribution.channels
channel.overal_percentage = Math.round((channel.inbound + channel.outbound) / totalTickets * 100) || 0 channel.overal_percentage = Math.round((channel.inbound + channel.outbound) / totalTickets * 100) || 0
data.StatsTicketChannelDistribution.description = 'How many of your tickets are coming from email, phone, Twitter, or Facebook? (Shows percentages for both inbound and outbound tickets.)' data.StatsTicketChannelDistribution.description = __('How many of your tickets are coming from email, phone, Twitter, or Facebook? (Shows percentages for both inbound and outbound tickets.)')
content = App.view('dashboard/stats/ticket_channel_distribution')(data) content = App.view('dashboard/stats/ticket_channel_distribution')(data)

View file

@ -6,7 +6,7 @@ class Stats extends App.ControllerDashboardStatsBase
own: 0 own: 0
total: 0 total: 0
data.StatsTicketEscalation.description = 'How many escalated tickets do you have open? (Mr. Bubbles gets grumpy if you have too many…)' data.StatsTicketEscalation.description = __('How many escalated tickets do you have open? (Mr. Bubbles gets grumpy if you have too many…)')
content = App.view('dashboard/stats/ticket_escalation')(data) content = App.view('dashboard/stats/ticket_escalation')(data)

View file

@ -7,7 +7,7 @@ class Stats extends App.ControllerDashboardStatsBase
in_process: 0 in_process: 0
average_per_agent: 0 average_per_agent: 0
data.StatsTicketInProcess.description = 'What percentage of your tickets have you responded to, updated, or modified in some way today?' data.StatsTicketInProcess.description = __('What percentage of your tickets have you responded to, updated, or modified in some way today?')
content = App.view('dashboard/stats/ticket_in_process')(data) content = App.view('dashboard/stats/ticket_in_process')(data)

View file

@ -8,7 +8,7 @@ class Stats extends App.ControllerDashboardStatsBase
total: 0 total: 0
average_per_agent: 0 average_per_agent: 0
data.StatsTicketLoadMeasure.description = 'Out of all open tickets (company-wide), how many are assigned to you?' data.StatsTicketLoadMeasure.description = __('Out of all open tickets (company-wide), how many are assigned to you?')
content = App.view('dashboard/stats/ticket_load_measure')(data) content = App.view('dashboard/stats/ticket_load_measure')(data)

View file

@ -6,7 +6,7 @@ class Stats extends App.ControllerDashboardStatsBase
percent: 0 percent: 0
average_per_agent: 0 average_per_agent: 0
data.StatsTicketReopen.description = 'How many of your tickets have been re-opened after being marked “closed”?' data.StatsTicketReopen.description = __('How many of your tickets have been re-opened after being marked “closed”?')
content = App.view('dashboard/stats/ticket_reopen')(data) content = App.view('dashboard/stats/ticket_reopen')(data)
if @$('.ticket_reopen').length > 0 if @$('.ticket_reopen').length > 0

View file

@ -7,7 +7,7 @@ class Stats extends App.ControllerDashboardStatsBase
state: 'supergood' state: 'supergood'
average_per_agent: 0 average_per_agent: 0
data.StatsTicketWaitingTime.description = 'How long has each customer had to wait, on average, to get a response from you today?' data.StatsTicketWaitingTime.description = __('How long has each customer had to wait, on average, to get a response from you today?')
content = App.view('dashboard/stats/ticket_waiting_time')(data) content = App.view('dashboard/stats/ticket_waiting_time')(data)
if @$('.ticket_waiting_time').length > 0 if @$('.ticket_waiting_time').length > 0

View file

@ -10,9 +10,9 @@ App.Config.set('User', {
class: 'user' class: 'user'
}, 'NavBarRight' ) }, 'NavBarRight' )
App.Config.set('Admin', { prio: 9000, parent: '', name: 'Admin', translate: true, target: '#manage', icon: 'cog', permission: ['admin.*'] }, 'NavBarRight') App.Config.set('Admin', { prio: 9000, parent: '', name: __('Admin'), translate: true, target: '#manage', icon: 'cog', permission: ['admin.*'] }, 'NavBarRight')
App.Config.set('New', { prio: 20000, parent: '', name: 'New', translate: true, target: '#new', class: 'add', icon: 'plus' }, 'NavBarRight') App.Config.set('New', { prio: 20000, parent: '', name: __('New'), translate: true, target: '#new', class: 'add', icon: 'plus' }, 'NavBarRight')
App.Config.set('Misc', { prio: 90000, parent: '', name: 'Tools', translate: true, target: '#tools', child: true, class: 'tools' }, 'NavBar') App.Config.set('Misc', { prio: 90000, parent: '', name: __('Tools'), translate: true, target: '#tools', child: true, class: 'tools' }, 'NavBar')
# only for testing # only for testing
#App.Config.set('Misc1', { prio: 1600, parent: '#tools', name: 'Test 1', target: '#test1', permission: ['admin'] }, 'NavBar') #App.Config.set('Misc1', { prio: 1600, parent: '#tools', name: __('Test 1'), target: '#test1', permission: ['admin'] }, 'NavBar')
#App.Config.set('Misc2', { prio: 1700, parent: '#tools', name: 'Test 2', target: '#test2', permission: ['admin'] }, 'NavBar') #App.Config.set('Misc2', { prio: 1700, parent: '#tools', name: __('Test 2'), target: '#test2', permission: ['admin'] }, 'NavBar')

View file

@ -1,10 +1,10 @@
class CheckMk extends App.ControllerIntegrationBase class CheckMk extends App.ControllerIntegrationBase
featureIntegration: 'check_mk_integration' featureIntegration: 'check_mk_integration'
featureName: 'Checkmk' featureName: __('Checkmk')
featureConfig: 'check_mk_config' featureConfig: 'check_mk_config'
description: [ description: [
['This service receives http requests or emails from %s and creates tickets with host and service.', 'Checkmk'] [__('This service receives http requests or emails from %s and creates tickets with host and service.'), 'Checkmk']
['If the host and service has recovered, the ticket can be closed automatically.'] [__('If the host and service has recovered, the ticket can be closed automatically.')]
] ]
render: => render: =>
@ -52,9 +52,9 @@ class Form extends App.Controller
App.Config.set( App.Config.set(
'IntegrationCheckMk' 'IntegrationCheckMk'
{ {
name: 'Checkmk' name: __('Checkmk')
target: '#system/integration/check_mk' target: '#system/integration/check_mk'
description: 'An open source monitoring tool.' description: __('An open source monitoring tool.')
controller: CheckMk controller: CheckMk
state: State state: State
permission: ['admin.integration.check_mk'] permission: ['admin.integration.check_mk']

View file

@ -1,10 +1,9 @@
class Clearbit extends App.ControllerIntegrationBase class Clearbit extends App.ControllerIntegrationBase
featureIntegration: 'clearbit_integration' featureIntegration: 'clearbit_integration'
featureName: 'Clearbit' featureName: __('Clearbit')
featureConfig: 'clearbit_config' featureConfig: 'clearbit_config'
description: [ description: [
['Automatically enrich your customers and organizations with fresh, up-to-date intel. Map data directly to object fields. [__('Automatically enrich your customers and organizations with fresh, up-to-date intel. Map data directly to object fields.')]
']
] ]
render: => render: =>
@ -62,9 +61,9 @@ class Form extends App.Controller
if !@config if !@config
@config = @currentConfig() @config = @currentConfig()
settings = [ settings = [
{ name: 'api_key', display: 'API Key', tag: 'input', type: 'text', limit: 100, null: false, placeholder: '...', note: 'Your api key.' }, { name: 'api_key', display: __('API Key'), tag: 'input', type: 'text', limit: 100, null: false, placeholder: '...', note: __('Your api key.') },
{ name: 'organization_autocreate', display: 'Auto create', tag: 'boolean', type: 'boolean', null: false, note: 'Create organizations automatically if record has one.' }, { name: 'organization_autocreate', display: __('Auto create'), tag: 'boolean', type: 'boolean', null: false, note: __('Create organizations automatically if record has one.') },
{ name: 'organization_shared', display: 'Shared', tag: 'boolean', type: 'boolean', null: false, note: 'New organizations are shared.' }, { name: 'organization_shared', display: __('Shared'), tag: 'boolean', type: 'boolean', null: false, note: __('New organizations are shared.') },
] ]
@html App.view('integration/clearbit')( @html App.view('integration/clearbit')(
@ -152,9 +151,9 @@ class State
App.Config.set( App.Config.set(
'IntegrationClearbit' 'IntegrationClearbit'
{ {
name: 'Clearbit' name: __('Clearbit')
target: '#system/integration/clearbit' target: '#system/integration/clearbit'
description: 'A powerful service to get more information about your customers.' description: __('A powerful service to get more information about your customers.')
controller: Clearbit controller: Clearbit
state: State state: State
permission: ['admin.integration.clearbit'] permission: ['admin.integration.clearbit']

View file

@ -1,10 +1,10 @@
class Cti extends App.ControllerIntegrationBase class Cti extends App.ControllerIntegrationBase
featureIntegration: 'cti_integration' featureIntegration: 'cti_integration'
featureName: 'CTI (generic)' featureName: __('CTI (generic)')
featureConfig: 'cti_config' featureConfig: 'cti_config'
description: [ description: [
['This service shows you contacts of incoming calls and a caller list in realtime.'] [__('This service shows you contacts of incoming calls and a caller list in realtime.')]
['Also caller id of outbound calls can be changed.'] [__('Also caller id of outbound calls can be changed.')]
] ]
events: events:
'click .js-select': 'selectAll' 'click .js-select': 'selectAll'
@ -259,9 +259,9 @@ class State
App.Config.set( App.Config.set(
'IntegrationCti' 'IntegrationCti'
{ {
name: 'CTI (generic)' name: __('CTI (generic)')
target: '#system/integration/cti' target: '#system/integration/cti'
description: 'Generic API to integrate VoIP service provider with realtime push.' description: __('Generic API to integrate VoIP service provider with realtime push.')
controller: Cti controller: Cti
state: State state: State
} }

View file

@ -1,9 +1,9 @@
class Exchange extends App.ControllerIntegrationBase class Exchange extends App.ControllerIntegrationBase
featureIntegration: 'exchange_integration' featureIntegration: 'exchange_integration'
featureName: 'Exchange' featureName: __('Exchange')
featureConfig: 'exchange_config' featureConfig: 'exchange_config'
description: [ description: [
['This service enables Zammad to connect with your Exchange server.'] [__('This service enables Zammad to connect with your Exchange server.')]
] ]
events: events:
'change .js-switch input': 'switch' 'change .js-switch input': 'switch'
@ -282,7 +282,7 @@ class ConnectionWizard extends App.ControllerWizardModal
if !_.isEmpty(detailsRaw) if !_.isEmpty(detailsRaw)
details = JSON.parse(detailsRaw) details = JSON.parse(detailsRaw)
@showSlide('js-discover') @showSlide('js-discover')
@showAlert('js-discover', details.error || 'Unable to perform backend.') @showAlert('js-discover', details.error || __('Unable to perform backend.'))
) )
folders: (e) => folders: (e) =>
@ -323,7 +323,7 @@ class ConnectionWizard extends App.ControllerWizardModal
if !_.isEmpty(detailsRaw) if !_.isEmpty(detailsRaw)
details = JSON.parse(detailsRaw) details = JSON.parse(detailsRaw)
@showSlide('js-bind') @showSlide('js-bind')
@showAlert('js-bind', details.error || 'Unable to perform backend.') @showAlert('js-bind', details.error || __('Unable to perform backend.'))
) )
foldersShow: (alreadyShown) => foldersShow: (alreadyShown) =>
@ -429,7 +429,7 @@ class ConnectionWizard extends App.ControllerWizardModal
if !_.isEmpty(detailsRaw) if !_.isEmpty(detailsRaw)
details = JSON.parse(detailsRaw) details = JSON.parse(detailsRaw)
@showSlide('js-folders') @showSlide('js-folders')
@showAlert('js-folders', details.error || 'Unable to perform backend.') @showAlert('js-folders', details.error || __('Unable to perform backend.'))
) )
mappingShow: (alreadyShown) => mappingShow: (alreadyShown) =>
@ -554,9 +554,9 @@ class ConnectionWizard extends App.ControllerWizardModal
App.Config.set( App.Config.set(
'IntegrationExchange' 'IntegrationExchange'
{ {
name: 'Exchange' name: __('Exchange')
target: '#system/integration/exchange' target: '#system/integration/exchange'
description: 'Exchange integration for contacts management.' description: __('Exchange integration for contacts management.')
controller: Exchange controller: Exchange
state: State state: State
permission: ['admin.integration.exchange'] permission: ['admin.integration.exchange']

View file

@ -1,9 +1,9 @@
class GitHub extends App.ControllerIntegrationBase class GitHub extends App.ControllerIntegrationBase
featureIntegration: 'github_integration' featureIntegration: 'github_integration'
featureName: 'GitHub' featureName: __('GitHub')
featureConfig: 'github_config' featureConfig: 'github_config'
description: [ description: [
['This service allows you to connect %s with %s.', 'GitHub', 'Zammad'] [__('This service allows you to connect %s with %s.'), 'GitHub', 'Zammad']
] ]
events: events:
'change .js-switch input': 'switch' 'change .js-switch input': 'switch'
@ -60,7 +60,7 @@ class Form extends App.Controller
details = data.responseJSON || {} details = data.responseJSON || {}
App.Event.trigger 'notify', { App.Event.trigger 'notify', {
type: 'error' type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to save!') msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to save!'))
} }
) )
@ -71,9 +71,9 @@ class State
App.Config.set( App.Config.set(
'IntegrationGitHub' 'IntegrationGitHub'
{ {
name: 'GitHub' name: __('GitHub')
target: '#system/integration/github' target: '#system/integration/github'
description: 'Link GitHub issues to your tickets.' description: __('Link GitHub issues to your tickets.')
controller: GitHub controller: GitHub
state: State state: State
} }

View file

@ -1,9 +1,9 @@
class GitLab extends App.ControllerIntegrationBase class GitLab extends App.ControllerIntegrationBase
featureIntegration: 'gitlab_integration' featureIntegration: 'gitlab_integration'
featureName: 'GitLab' featureName: __('GitLab')
featureConfig: 'gitlab_config' featureConfig: 'gitlab_config'
description: [ description: [
['This service allows you to connect %s with %s.', 'GitLab', 'Zammad'] [__('This service allows you to connect %s with %s.'), 'GitLab', 'Zammad']
] ]
events: events:
'change .js-switch input': 'switch' 'change .js-switch input': 'switch'
@ -60,7 +60,7 @@ class Form extends App.Controller
details = data.responseJSON || {} details = data.responseJSON || {}
App.Event.trigger 'notify', { App.Event.trigger 'notify', {
type: 'error' type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to save!') msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to save!'))
} }
) )
@ -71,9 +71,9 @@ class State
App.Config.set( App.Config.set(
'IntegrationGitLab' 'IntegrationGitLab'
{ {
name: 'GitLab' name: __('GitLab')
target: '#system/integration/gitlab' target: '#system/integration/gitlab'
description: 'Link GitLab issues to your tickets.' description: __('Link GitLab issues to your tickets.')
controller: GitLab controller: GitLab
state: State state: State
} }

View file

@ -1,10 +1,10 @@
class Icinga extends App.ControllerIntegrationBase class Icinga extends App.ControllerIntegrationBase
featureIntegration: 'icinga_integration' featureIntegration: 'icinga_integration'
featureName: 'Icinga' featureName: __('Icinga')
featureConfig: 'icinga_config' featureConfig: 'icinga_config'
description: [ description: [
['This service receives emails from %s and creates tickets with host and service.', 'Icinga'] [__('This service receives emails from %s and creates tickets with host and service.'), 'Icinga']
['If the host and service is recovered again, the ticket will be closed automatically.'] [__('If the host and service is recovered again, the ticket will be closed automatically.')]
] ]
render: => render: =>
@ -21,9 +21,9 @@ class State
App.Config.set( App.Config.set(
'IntegrationIcinga' 'IntegrationIcinga'
{ {
name: 'Icinga' name: __('Icinga')
target: '#system/integration/icinga' target: '#system/integration/icinga'
description: 'An open source monitoring tool.' description: __('An open source monitoring tool.')
controller: Icinga controller: Icinga
state: State state: State
permission: ['admin.integration.icinga'] permission: ['admin.integration.icinga']

View file

@ -3,7 +3,7 @@ class Idoit extends App.ControllerIntegrationBase
featureName: 'i-doit' featureName: 'i-doit'
featureConfig: 'idoit_config' featureConfig: 'idoit_config'
description: [ description: [
['This service allows you to connect %s with %s.', 'i-doit', 'Zammad'] [__('This service allows you to connect %s with %s.'), 'i-doit', 'Zammad']
] ]
events: events:
'change .js-switch input': 'switch' 'change .js-switch input': 'switch'
@ -73,7 +73,7 @@ class Form extends App.Controller
details = data.responseJSON || {} details = data.responseJSON || {}
@notify( @notify(
type: 'error' type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to save!') msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to save!'))
) )
) )
@ -86,7 +86,7 @@ App.Config.set(
{ {
name: 'i-doit' name: 'i-doit'
target: '#system/integration/idoit' target: '#system/integration/idoit'
description: 'CMDB to document complex relations of your network components.' description: __('CMDB to document complex relations of your network components.')
controller: Idoit controller: Idoit
state: State state: State
} }

View file

@ -1,9 +1,9 @@
class Ldap extends App.ControllerIntegrationBase class Ldap extends App.ControllerIntegrationBase
featureIntegration: 'ldap_integration' featureIntegration: 'ldap_integration'
featureName: 'LDAP' featureName: __('LDAP')
featureConfig: 'ldap_config' featureConfig: 'ldap_config'
description: [ description: [
['This service enables Zammad to connect with your LDAP server.'] [__('This service enables Zammad to connect with your LDAP server.')]
] ]
events: events:
'change .js-switch input': 'switch' 'change .js-switch input': 'switch'
@ -315,7 +315,7 @@ class ConnectionWizard extends App.ControllerWizardModal
if !_.isEmpty(detailsRaw) if !_.isEmpty(detailsRaw)
details = JSON.parse(detailsRaw) details = JSON.parse(detailsRaw)
@showSlide('js-discover') @showSlide('js-discover')
@showAlert('js-discover', details.error || 'Unable to perform backend.') @showAlert('js-discover', details.error || __('Unable to perform backend.'))
) )
@ -345,12 +345,12 @@ class ConnectionWizard extends App.ControllerWizardModal
if _.isEmpty(data.user_attributes) if _.isEmpty(data.user_attributes)
@showSlide('js-bind') @showSlide('js-bind')
@showAlert('js-bind', 'Unable to retrive user information, please check your bind user permissions.') @showAlert('js-bind', __('Unable to retrive user information, please check your bind user permissions.'))
return return
if _.isEmpty(data.groups) if _.isEmpty(data.groups)
@showSlide('js-bind') @showSlide('js-bind')
@showAlert('js-bind', 'Unable to retrive group information, please check your bind user permissions.') @showAlert('js-bind', __('Unable to retrive group information, please check your bind user permissions.'))
return return
# update config if successful # update config if successful
@ -384,7 +384,7 @@ class ConnectionWizard extends App.ControllerWizardModal
if !_.isEmpty(detailsRaw) if !_.isEmpty(detailsRaw)
details = JSON.parse(detailsRaw) details = JSON.parse(detailsRaw)
@showSlide('js-bind') @showSlide('js-bind')
@showAlert('js-bind', details.error || 'Unable to perform backend.') @showAlert('js-bind', details.error || __('Unable to perform backend.'))
) )
mappingShow: (alreadyShown) => mappingShow: (alreadyShown) =>
@ -572,9 +572,9 @@ class ConnectionWizard extends App.ControllerWizardModal
App.Config.set( App.Config.set(
'IntegrationLDAP' 'IntegrationLDAP'
{ {
name: 'LDAP' name: __('LDAP')
target: '#system/integration/ldap' target: '#system/integration/ldap'
description: 'LDAP integration for user management.' description: __('LDAP integration for user management.')
controller: Ldap controller: Ldap
state: State state: State
permission: ['admin.integration.ldap'] permission: ['admin.integration.ldap']

View file

@ -1,10 +1,10 @@
class Monit extends App.ControllerIntegrationBase class Monit extends App.ControllerIntegrationBase
featureIntegration: 'monit_integration' featureIntegration: 'monit_integration'
featureName: 'Monit' featureName: __('Monit')
featureConfig: 'monit_config' featureConfig: 'monit_config'
description: [ description: [
['This service receives emails from %s and creates tickets with host and service.', 'Monit'] [__('This service receives emails from %s and creates tickets with host and service.'), 'Monit']
['If the host and service is recovered again, the ticket will be closed automatically.'] [__('If the host and service is recovered again, the ticket will be closed automatically.')]
] ]
render: => render: =>
@ -21,9 +21,9 @@ class State
App.Config.set( App.Config.set(
'IntegrationMonit' 'IntegrationMonit'
{ {
name: 'Monit' name: __('Monit')
target: '#system/integration/monit' target: '#system/integration/monit'
description: 'An open source monitoring tool.' description: __('An open source monitoring tool.')
controller: Monit controller: Monit
state: State state: State
permission: ['admin.integration.monit'] permission: ['admin.integration.monit']

View file

@ -1,10 +1,10 @@
class Nagios extends App.ControllerIntegrationBase class Nagios extends App.ControllerIntegrationBase
featureIntegration: 'nagios_integration' featureIntegration: 'nagios_integration'
featureName: 'Nagios' featureName: __('Nagios')
featureConfig: 'nagios_config' featureConfig: 'nagios_config'
description: [ description: [
['This service receives emails from %s and creates tickets with host and service.', 'Nagios'] [__('This service receives emails from %s and creates tickets with host and service.'), 'Nagios']
['If the host and service is recovered again, the ticket will be closed automatically.'] [__('If the host and service is recovered again, the ticket will be closed automatically.')]
] ]
render: => render: =>
@ -21,9 +21,9 @@ class State
App.Config.set( App.Config.set(
'IntegrationNagios' 'IntegrationNagios'
{ {
name: 'Nagios' name: __('Nagios')
target: '#system/integration/nagios' target: '#system/integration/nagios'
description: 'An open source monitoring tool.' description: __('An open source monitoring tool.')
controller: Nagios controller: Nagios
state: State state: State
} }

View file

@ -1,10 +1,10 @@
class Placetel extends App.ControllerIntegrationBase class Placetel extends App.ControllerIntegrationBase
featureIntegration: 'placetel_integration' featureIntegration: 'placetel_integration'
featureName: 'Placetel' featureName: __('Placetel')
featureConfig: 'placetel_config' featureConfig: 'placetel_config'
description: [ description: [
['This service shows you contacts of incoming calls and a caller list in realtime.'] [__('This service shows you contacts of incoming calls and a caller list in realtime.')]
['Also caller id of outbound calls can be changed.'] [__('Also caller id of outbound calls can be changed.')]
] ]
events: events:
'click .js-select': 'selectAll' 'click .js-select': 'selectAll'
@ -185,9 +185,9 @@ class State
App.Config.set( App.Config.set(
'IntegrationPlacetel' 'IntegrationPlacetel'
{ {
name: 'Placetel' name: __('Placetel')
target: '#system/integration/placetel' target: '#system/integration/placetel'
description: 'VoIP service provider with realtime push.' description: __('VoIP service provider with realtime push.')
controller: Placetel controller: Placetel
state: State state: State
} }

View file

@ -3,8 +3,8 @@ class SipgateIo extends App.ControllerIntegrationBase
featureName: 'sipgate.io' featureName: 'sipgate.io'
featureConfig: 'sipgate_config' featureConfig: 'sipgate_config'
description: [ description: [
['This service shows you contacts of incoming calls and a caller list in realtime.'] [__('This service shows you contacts of incoming calls and a caller list in realtime.')]
['Also caller id of outbound calls can be changed.'] [__('Also caller id of outbound calls can be changed.')]
] ]
events: events:
'click .js-select': 'selectAll' 'click .js-select': 'selectAll'
@ -187,7 +187,7 @@ App.Config.set(
{ {
name: 'sipgate.io' name: 'sipgate.io'
target: '#system/integration/sipgate' target: '#system/integration/sipgate'
description: 'VoIP service provider with realtime push.' description: __('VoIP service provider with realtime push.')
controller: SipgateIo controller: SipgateIo
state: State state: State
} }

View file

@ -1,10 +1,10 @@
class Slack extends App.ControllerIntegrationBase class Slack extends App.ControllerIntegrationBase
featureIntegration: 'slack_integration' featureIntegration: 'slack_integration'
featureName: 'Slack' featureName: __('Slack')
featureConfig: 'slack_config' featureConfig: 'slack_config'
description: [ description: [
['This service sends notifications to your %s channel.', 'Slack'] [__('This service sends notifications to your %s channel.'), 'Slack']
['To set up this service you need to create a new |"Incoming webhook"| in your %s integration panel, and enter the web hook URL below.', 'Slack'] [__('To set up this service you need to create a new |"Incoming webhook"| in your %s integration panel, and enter the web hook URL below.'), 'Slack']
] ]
events: events:
'click .js-submit': 'update' 'click .js-submit': 'update'
@ -26,12 +26,12 @@ class Slack extends App.ControllerIntegrationBase
escalation_warning: '5. Ticket Escalation Warning' escalation_warning: '5. Ticket Escalation Warning'
configureAttributes = [ configureAttributes = [
{ name: 'types', display: 'Trigger', tag: 'checkbox', options: options, 'null': false, class: 'vertical', note: 'When notification is being sent.' }, { name: 'types', display: __('Trigger'), tag: 'checkbox', options: options, 'null': false, class: 'vertical', note: __('When notification is being sent.') },
{ name: 'group_ids', display: 'Group', tag: 'select', relation: 'Group', multiple: true, 'null': false, note: 'Only for these groups.' }, { name: 'group_ids', display: __('Group'), tag: 'select', relation: 'Group', multiple: true, 'null': false, note: __('Only for these groups.') },
{ name: 'webhook', display: 'Webhook', tag: 'input', type: 'url', limit: 200, 'null': false, placeholder: 'https://hooks.slack.com/services/...' }, { name: 'webhook', display: __('Webhook'), tag: 'input', type: 'url', limit: 200, 'null': false, placeholder: 'https://hooks.slack.com/services/...' },
{ name: 'username', display: 'Username', tag: 'input', type: 'text', limit: 100, 'null': false, placeholder: 'username' }, { name: 'username', display: __('Username'), tag: 'input', type: 'text', limit: 100, 'null': false, placeholder: 'username' },
{ name: 'channel', display: 'Channel', tag: 'input', type: 'text', limit: 100, 'null': true, placeholder: '#channel' }, { name: 'channel', display: __('Channel'), tag: 'input', type: 'text', limit: 100, 'null': true, placeholder: '#channel' },
{ name: 'icon_url', display: 'Icon Url', tag: 'input', type: 'url', limit: 200, 'null': true, placeholder: 'https://example.com/logo.png' }, { name: 'icon_url', display: __('Icon Url'), tag: 'input', type: 'url', limit: 200, 'null': true, placeholder: 'https://example.com/logo.png' },
] ]
settings = [] settings = []
@ -81,10 +81,10 @@ class State
App.Config.set( App.Config.set(
'IntegrationSlack' 'IntegrationSlack'
{ {
name: 'Slack' name: __('Slack')
target: '#system/integration/slack' target: '#system/integration/slack'
description: 'A team communication tool for the 21st century. Compatible with tools like %s.' description: __('A team communication tool for the 21st century. Compatible with tools like %s.')
descriptionSubstitute: 'Mattermost, RocketChat' descriptionSubstitute: __('Mattermost, RocketChat')
controller: Slack controller: Slack
state: State state: State
} }

View file

@ -1,9 +1,9 @@
class Index extends App.ControllerIntegrationBase class Index extends App.ControllerIntegrationBase
featureIntegration: 'smime_integration' featureIntegration: 'smime_integration'
featureName: 'S/MIME' featureName: __('S/MIME')
featureConfig: 'smime_config' featureConfig: 'smime_config'
description: [ description: [
['S/MIME (Secure/Multipurpose Internet Mail Extensions) is a widely accepted method (or more precisely, a protocol) for sending digitally signed and encrypted messages.'] [__('S/MIME (Secure/Multipurpose Internet Mail Extensions) is a widely accepted method (or more precisely, a protocol) for sending digitally signed and encrypted messages.')]
] ]
events: events:
'change .js-switch input': 'switch' 'change .js-switch input': 'switch'
@ -70,16 +70,16 @@ class Form extends App.Controller
class Certificate extends App.ControllerModal class Certificate extends App.ControllerModal
buttonClose: true buttonClose: true
buttonCancel: true buttonCancel: true
buttonSubmit: 'Add' buttonSubmit: __('Add')
autoFocusOnFirstInput: false autoFocusOnFirstInput: false
head: 'Add Certificate' head: __('Add Certificate')
large: true large: true
content: -> content: ->
# show start dialog # show start dialog
content = $(App.view('integration/smime_certificate_add')( content = $(App.view('integration/smime_certificate_add')(
head: 'Add Certificate' head: __('Add Certificate')
)) ))
content content
@ -106,23 +106,23 @@ class Certificate extends App.ControllerModal
details = data.responseJSON || {} details = data.responseJSON || {}
@notify @notify
type: 'error' type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to import!') msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to import!'))
timeout: 6000 timeout: 6000
) )
class PrivateKey extends App.ControllerModal class PrivateKey extends App.ControllerModal
buttonClose: true buttonClose: true
buttonCancel: true buttonCancel: true
buttonSubmit: 'Add' buttonSubmit: __('Add')
autoFocusOnFirstInput: false autoFocusOnFirstInput: false
head: 'Add Private Key' head: __('Add Private Key')
large: true large: true
content: -> content: ->
# show start dialog # show start dialog
content = $(App.view('integration/smime_private_key_add')( content = $(App.view('integration/smime_private_key_add')(
head: 'Add Private Key' head: __('Add Private Key')
)) ))
content content
@ -149,7 +149,7 @@ class PrivateKey extends App.ControllerModal
details = data.responseJSON || {} details = data.responseJSON || {}
@notify @notify
type: 'error' type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to import!') msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to import!'))
timeout: 6000 timeout: 6000
) )
@ -178,7 +178,7 @@ class List extends App.Controller
details = data.responseJSON || {} details = data.responseJSON || {}
@notify( @notify(
type: 'error' type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to load list of certificates!') msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to load list of certificates!'))
) )
# do something # do something
@ -210,7 +210,7 @@ class List extends App.Controller
details = data.responseJSON || {} details = data.responseJSON || {}
@notify( @notify(
type: 'error' type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to save!') msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to save!'))
) )
) )
@ -246,9 +246,9 @@ class State
App.Config.set( App.Config.set(
'Integrationsmime' 'Integrationsmime'
{ {
name: 'S/MIME' name: __('S/MIME')
target: '#system/integration/smime' target: '#system/integration/smime'
description: 'S/MIME enables you to send digitally signed and encrypted messages.' description: __('S/MIME enables you to send digitally signed and encrypted messages.')
controller: Index controller: Index
state: State state: State
} }

View file

@ -1,13 +1,13 @@
class Branding extends App.ControllerTabs class Branding extends App.ControllerTabs
requiredPermission: 'admin.branding' requiredPermission: 'admin.branding'
header: 'Branding' header: __('Branding')
constructor: -> constructor: ->
super super
@title 'Branding', true @title __('Branding'), true
@tabs = [ @tabs = [
{ name: 'Base', 'target': 'base', controller: App.SettingsArea, params: { area: 'System::Branding' } } { name: __('Base'), 'target': 'base', controller: App.SettingsArea, params: { area: 'System::Branding' } }
] ]
@render() @render()
App.Config.set('SettingBranding', { prio: 1200, parent: '#settings', name: 'Branding', target: '#settings/branding', controller: Branding, permission: ['admin.branding'] }, 'NavBarAdmin') App.Config.set('SettingBranding', { prio: 1200, parent: '#settings', name: __('Branding'), target: '#settings/branding', controller: Branding, permission: ['admin.branding'] }, 'NavBarAdmin')

View file

@ -1,5 +1,5 @@
class App.ManageKnowledgeBase extends App.ControllerTabs class App.ManageKnowledgeBase extends App.ControllerTabs
header: 'Knowledge Base' header: __('Knowledge Base')
headerSwitchName: 'kb-activate' headerSwitchName: 'kb-activate'
events: events:
@ -59,7 +59,7 @@ class App.ManageKnowledgeBase extends App.ControllerTabs
@renderNonExistant() @renderNonExistant()
renderNonExistant: -> renderNonExistant: ->
@renderScreenError(detail: 'No Knowledge Base. Please create first Knowledge Base', el: @$('.page-content')) @renderScreenError(detail: __('No Knowledge Base. Please create first Knowledge Base'), el: @$('.page-content'))
@headerSwitchInput.prop('checked', false) @headerSwitchInput.prop('checked', false)
@modal = new App.KnowledgeBaseNewModal( @modal = new App.KnowledgeBaseNewModal(
@ -96,22 +96,22 @@ class App.ManageKnowledgeBase extends App.ControllerTabs
@tabs = [ @tabs = [
{ {
name: 'Theme' name: __('Theme')
target: 'style' target: 'style'
controller: App.KnowledgeBaseForm controller: App.KnowledgeBaseForm
params: _.extend({}, params, { screen: 'style', split: true }) params: _.extend({}, params, { screen: 'style', split: true })
},{ },{
name: 'Languages' name: __('Languages')
target: 'languages' target: 'languages'
controller: App.KnowledgeBaseForm controller: App.KnowledgeBaseForm
params: _.extend({}, params, { screen: 'languages' }) params: _.extend({}, params, { screen: 'languages' })
},{ },{
name: 'Public Menu' name: __('Public Menu')
target: 'public_menu' target: 'public_menu'
controller: App.KnowledgeBasePublicMenuManager controller: App.KnowledgeBasePublicMenuManager
params: _.extend({}, params, { screen: 'public_menu' }) params: _.extend({}, params, { screen: 'public_menu' })
},{ },{
name: 'Delete' name: __('Delete')
target: 'delete' target: 'delete'
controller: App.KnowledgeBaseDelete controller: App.KnowledgeBaseDelete
params: params params: params
@ -120,7 +120,7 @@ class App.ManageKnowledgeBase extends App.ControllerTabs
if !App.Config.get('system_online_service') if !App.Config.get('system_online_service')
@tabs.splice(-1, 0, { @tabs.splice(-1, 0, {
name: 'Custom URL' name: __('Custom URL')
target: 'custom_address' target: 'custom_address'
controller: App.KnowledgeBaseCustomAddressForm, controller: App.KnowledgeBaseCustomAddressForm,
params: _.extend({}, params, { screen: 'custom_address' }) params: _.extend({}, params, { screen: 'custom_address' })
@ -130,4 +130,4 @@ class App.ManageKnowledgeBase extends App.ControllerTabs
@headerSwitchInput.prop('checked', App.KnowledgeBase.find(@knowledge_base_id).active) @headerSwitchInput.prop('checked', App.KnowledgeBase.find(@knowledge_base_id).active)
App.Config.set('KnowledgeBase', { prio: 10000, name: 'Knowledge Base', parent: '#manage', target: '#manage/knowledge_base', controller: App.ManageKnowledgeBase, permission: ['admin.knowledge_base'] }, 'NavBarAdmin') App.Config.set('KnowledgeBase', { prio: 10000, name: __('Knowledge Base'), parent: '#manage', target: '#manage/knowledge_base', controller: App.ManageKnowledgeBase, permission: ['admin.knowledge_base'] }, 'NavBarAdmin')

View file

@ -1,17 +1,16 @@
class Security extends App.ControllerTabs class Security extends App.ControllerTabs
requiredPermission: 'admin.security' requiredPermission: 'admin.security'
header: 'Security' header: __('Security')
constructor: -> constructor: ->
super super
@title 'Security', true @title __('Security'), true
@tabs = [ @tabs = [
{ name: 'Base', 'target': 'base', controller: App.SettingsArea, params: { area: 'Security::Base' } } { name: __('Base'), 'target': 'base', controller: App.SettingsArea, params: { area: 'Security::Base' } }
{ name: 'Password', 'target': 'password', controller: App.SettingsArea, params: { area: 'Security::Password' } } { name: __('Password'), 'target': 'password', controller: App.SettingsArea, params: { area: 'Security::Password' } }
#{ name: 'Authentication', 'target': 'auth', controller: App.SettingsArea, params: { area: 'Security::Authentication' } } #{ name: __('Authentication'), 'target': 'auth', controller: App.SettingsArea, params: { area: 'Security::Authentication' } }
{ name: 'Third-party Applications', 'target': 'third_party_auth', controller: App.SettingsArea, params: { area: 'Security::ThirdPartyAuthentication' } } { name: __('Third-party Applications'), 'target': 'third_party_auth', controller: App.SettingsArea, params: { area: 'Security::ThirdPartyAuthentication' } }
] ]
@render() @render()
App.Config.set('SettingSecurity', { prio: 1600, parent: '#settings', name: 'Security', target: '#settings/security', controller: Security, permission: ['admin.security'] }, 'NavBarAdmin') App.Config.set('SettingSecurity', { prio: 1600, parent: '#settings', name: __('Security'), target: '#settings/security', controller: Security, permission: ['admin.security'] }, 'NavBarAdmin')

View file

@ -1,19 +1,19 @@
class System extends App.ControllerTabs class System extends App.ControllerTabs
requiredPermission: 'admin.setting_system' requiredPermission: 'admin.setting_system'
header: 'System' header: __('System')
constructor: -> constructor: ->
super super
@title 'System', true @title __('System'), true
@tabs = [] @tabs = []
if !App.Config.get('system_online_service') if !App.Config.get('system_online_service')
@tabs.push { name: 'Base', 'target': 'base', controller: App.SettingsArea, params: { area: 'System::Base' } } @tabs.push { name: __('Base'), 'target': 'base', controller: App.SettingsArea, params: { area: 'System::Base' } }
@tabs.push { name: 'Services', 'target': 'services', controller: App.SettingsArea, params: { area: 'System::Services' } } @tabs.push { name: __('Services'), 'target': 'services', controller: App.SettingsArea, params: { area: 'System::Services' } }
if !App.Config.get('system_online_service') if !App.Config.get('system_online_service')
@tabs.push { name: 'Storage', 'target': 'storage', controller: App.SettingsArea, params: { area: 'System::Storage' } } @tabs.push { name: __('Storage'), 'target': 'storage', controller: App.SettingsArea, params: { area: 'System::Storage' } }
if !App.Config.get('system_online_service') if !App.Config.get('system_online_service')
@tabs.push { name: 'Network', 'target': 'network', controller: App.SettingsArea, params: { area: 'System::Network' } } @tabs.push { name: __('Network'), 'target': 'network', controller: App.SettingsArea, params: { area: 'System::Network' } }
@tabs.push { name: 'Frontend', 'target': 'ui', controller: App.SettingsArea, params: { area: 'System::UI' } } @tabs.push { name: __('Frontend'), 'target': 'ui', controller: App.SettingsArea, params: { area: 'System::UI' } }
@render() @render()
App.Config.set('SettingSystem', { prio: 1400, parent: '#settings', name: 'System', target: '#settings/system', controller: System, permission: ['admin.setting_system'] }, 'NavBarAdmin') App.Config.set('SettingSystem', { prio: 1400, parent: '#settings', name: __('System'), target: '#settings/system', controller: System, permission: ['admin.setting_system'] }, 'NavBarAdmin')

View file

@ -1,15 +1,15 @@
class Ticket extends App.ControllerTabs class Ticket extends App.ControllerTabs
requiredPermission: 'admin.ticket' requiredPermission: 'admin.ticket'
header: 'Ticket' header: __('Ticket')
constructor: -> constructor: ->
super super
@title('Ticket', true) @title('Ticket', true)
@tabs = [ @tabs = [
{ name: 'Base', 'target': 'base', controller: App.SettingsArea, params: { area: 'Ticket::Base' } } { name: __('Base'), 'target': 'base', controller: App.SettingsArea, params: { area: 'Ticket::Base' } }
{ name: 'Number', 'target': 'number', controller: App.SettingsArea, params: { area: 'Ticket::Number' } } { name: __('Number'), 'target': 'number', controller: App.SettingsArea, params: { area: 'Ticket::Number' } }
{ name: 'Auto Assignment', 'target': 'auto_assignment', controller: App.SettingTicketAutoAssignment } { name: __('Auto Assignment'), 'target': 'auto_assignment', controller: App.SettingTicketAutoAssignment }
] ]
@render() @render()
App.Config.set('SettingTicket', { prio: 1700, parent: '#settings', name: 'Ticket', target: '#settings/ticket', controller: Ticket, permission: ['admin.ticket'] }, 'NavBarAdmin') App.Config.set('SettingTicket', { prio: 1700, parent: '#settings', name: __('Ticket'), target: '#settings/ticket', controller: Ticket, permission: ['admin.ticket'] }, 'NavBarAdmin')

View file

@ -22,7 +22,7 @@ class App.SettingTicketAutoAssignment extends App.ControllerSubContent
@html(App.view('settings/ticket_auto_assignment')()) @html(App.view('settings/ticket_auto_assignment')())
configure_attributes = [ configure_attributes = [
{ name: 'condition', display: 'Conditions for effected objects', tag: 'ticket_selector', null: false, preview: false, action: false, hasChanged: false, article: false }, { name: 'condition', display: __('Conditions for effected objects'), tag: 'ticket_selector', null: false, preview: false, action: false, hasChanged: false, article: false },
] ]
ticket_auto_assignment_selector = App.Setting.get('ticket_auto_assignment_selector') ticket_auto_assignment_selector = App.Setting.get('ticket_auto_assignment_selector')
@ -36,7 +36,7 @@ class App.SettingTicketAutoAssignment extends App.ControllerSubContent
) )
configure_attributes = [ configure_attributes = [
{ name: 'user_ids', display: 'Exception users', tag: 'column_select', multiple: true, null: true, relation: 'User', sortBy: 'firstname' }, { name: 'user_ids', display: __('Exception users'), tag: 'column_select', multiple: true, null: true, relation: 'User', sortBy: 'firstname' },
] ]
ticket_auto_assignment_user_ids_ignore = App.Setting.get('ticket_auto_assignment_user_ids_ignore') ticket_auto_assignment_user_ids_ignore = App.Setting.get('ticket_auto_assignment_user_ids_ignore')

View file

@ -23,19 +23,19 @@ class ElectronEvents extends App.Controller
createDefault = -> createDefault = ->
menu = new Menu() menu = new Menu()
menu.append(new MenuItem( menu.append(new MenuItem(
label: 'Cut', label: __('Cut'),
role: 'cut' role: 'cut'
)) ))
menu.append(new MenuItem( menu.append(new MenuItem(
label: 'Copy', label: __('Copy'),
role: 'copy' role: 'copy'
)) ))
menu.append(new MenuItem( menu.append(new MenuItem(
label: 'Paste', label: __('Paste'),
role: 'paste' role: 'paste'
)) ))
menu.append(new MenuItem( menu.append(new MenuItem(
label: 'Select All', label: __('Select All'),
role: 'selectall' role: 'selectall'
)) ))
menu menu
@ -81,7 +81,7 @@ class ElectronEvents extends App.Controller
dataURL = badgeDataURL(content.toString()) dataURL = badgeDataURL(content.toString())
sendBadge(dataURL, 'You have unread messages (' + content + ')') sendBadge(dataURL, 'You have unread messages (' + content + ')')
else else
sendBadge(null, 'You have no unread messages') sendBadge(null, __('You have no unread messages'))
setBadgeOSX = (content) -> setBadgeOSX = (content) ->
remote.app.dock.setBadge(content) remote.app.dock.setBadge(content)

View file

@ -1,7 +1,7 @@
class App.KeyboardShortcutModal extends App.ControllerModal class App.KeyboardShortcutModal extends App.ControllerModal
authenticateRequired: true authenticateRequired: true
large: true large: true
head: 'Keyboard Shortcuts' head: __('Keyboard Shortcuts')
buttonClose: true buttonClose: true
buttonCancel: false buttonCancel: false
buttonSubmit: false buttonSubmit: false
@ -82,16 +82,16 @@ App.Config.set(
'keyboard_shortcuts', 'keyboard_shortcuts',
[ [
{ {
headline: 'Navigation' headline: __('Navigation')
location: 'left' location: 'left'
content: [ content: [
{ {
where: 'Used anywhere' where: __('Used anywhere')
shortcuts: [ shortcuts: [
{ {
key: 'd' key: 'd'
hotkeys: true hotkeys: true
description: 'Dashboard' description: __('Dashboard')
globalEvent: 'dashboard' globalEvent: 'dashboard'
callback: -> callback: ->
$('#global-search').blur() $('#global-search').blur()
@ -101,7 +101,7 @@ App.Config.set(
{ {
key: 'o' key: 'o'
hotkeys: true hotkeys: true
description: 'Overviews' description: __('Overviews')
globalEvent: 'overview' globalEvent: 'overview'
callback: -> callback: ->
$('#global-search').blur() $('#global-search').blur()
@ -111,7 +111,7 @@ App.Config.set(
{ {
key: 's' key: 's'
hotkeys: true hotkeys: true
description: 'Search' description: __('Search')
globalEvent: 'search' globalEvent: 'search'
callback: -> callback: ->
App.Event.trigger('keyboard_shortcuts_close') App.Event.trigger('keyboard_shortcuts_close')
@ -120,7 +120,7 @@ App.Config.set(
{ {
key: 'a' key: 'a'
hotkeys: true hotkeys: true
description: 'Notifications' description: __('Notifications')
globalEvent: 'notification' globalEvent: 'notification'
callback: -> callback: ->
$('#global-search').blur() $('#global-search').blur()
@ -130,7 +130,7 @@ App.Config.set(
{ {
key: 'n' key: 'n'
hotkeys: true hotkeys: true
description: 'New Ticket' description: __('New Ticket')
globalEvent: 'new-ticket' globalEvent: 'new-ticket'
callback: -> callback: ->
$('#global-search').blur() $('#global-search').blur()
@ -140,7 +140,7 @@ App.Config.set(
{ {
key: 'e' key: 'e'
hotkeys: true hotkeys: true
description: 'Logout' description: __('Logout')
globalEvent: 'logout' globalEvent: 'logout'
callback: -> callback: ->
App.Event.trigger('keyboard_shortcuts_close') App.Event.trigger('keyboard_shortcuts_close')
@ -149,7 +149,7 @@ App.Config.set(
{ {
key: 'h' key: 'h'
hotkeys: true hotkeys: true
description: 'List of shortcuts' description: __('List of shortcuts')
globalEvent: 'list-of-shortcuts' globalEvent: 'list-of-shortcuts'
callback: => callback: =>
if window.location.hash is '#keyboard_shortcuts' if window.location.hash is '#keyboard_shortcuts'
@ -164,7 +164,7 @@ App.Config.set(
{ {
key: 'w' key: 'w'
hotkeys: true hotkeys: true
description: 'Close current tab' description: __('Close current tab')
globalEvent: 'close-current-tab' globalEvent: 'close-current-tab'
callback: -> callback: ->
App.Event.trigger('keyboard_shortcuts_close') App.Event.trigger('keyboard_shortcuts_close')
@ -173,7 +173,7 @@ App.Config.set(
{ {
key: 'tab' key: 'tab'
hotkeys: true hotkeys: true
description: 'Next in tab' description: __('Next in tab')
globalEvent: 'next-in-tab' globalEvent: 'next-in-tab'
callback: -> callback: ->
App.Event.trigger('keyboard_shortcuts_close') App.Event.trigger('keyboard_shortcuts_close')
@ -196,7 +196,7 @@ App.Config.set(
{ {
key: 'shift+tab' key: 'shift+tab'
hotkeys: true hotkeys: true
description: 'Previous tab' description: __('Previous tab')
globalEvent: 'previous-in-tab' globalEvent: 'previous-in-tab'
callback: -> callback: ->
App.Event.trigger('keyboard_shortcuts_close') App.Event.trigger('keyboard_shortcuts_close')
@ -219,7 +219,7 @@ App.Config.set(
{ {
key: 'return' key: 'return'
hotkeys: true hotkeys: true
description: 'Confirm/submit dialog' description: __('Confirm/submit dialog')
globalEvent: 'submit' globalEvent: 'submit'
callback: -> callback: ->
App.Event.trigger('keyboard_shortcuts_close') App.Event.trigger('keyboard_shortcuts_close')
@ -250,29 +250,29 @@ App.Config.set(
] ]
} }
{ {
where: 'Used in lists (views and results)' where: __('Used in lists (views and results)')
shortcuts: [ shortcuts: [
{ {
key: ['', ''] key: ['', '']
description: 'Move up and down' description: __('Move up and down')
} }
{ {
key: ['', ''] key: ['', '']
description: 'Move left and right' description: __('Move left and right')
} }
{ {
key: 'enter' key: 'enter'
description: 'Select item', description: __('Select item'),
} }
] ]
} }
{ {
where: 'Used in object views' where: __('Used in object views')
shortcuts: [ shortcuts: [
{ {
key: '.' key: '.'
hotkeys: true hotkeys: true
description: 'Copy current object number (e. g. Ticket#) to clipboard' description: __('Copy current object number (e. g. Ticket#) to clipboard')
callback: (shortcut, lastKey, modifier) -> callback: (shortcut, lastKey, modifier) ->
App.Event.trigger('keyboard_shortcuts_close') App.Event.trigger('keyboard_shortcuts_close')
text = $('.active.content .js-objectNumber').first().data('number') || '' text = $('.active.content .js-objectNumber').first().data('number') || ''
@ -298,30 +298,30 @@ App.Config.set(
keyPrefix: '2x' keyPrefix: '2x'
key: '.' key: '.'
hotkeys: true hotkeys: true
description: '...add object title' description: __('...add object title')
} }
{ {
keyPrefix: '3x' keyPrefix: '3x'
key: '.' key: '.'
hotkeys: true hotkeys: true
description: '...add object link URL' description: __('...add object link URL')
} }
] ]
} }
] ]
} }
{ {
headline: 'Translations' headline: __('Translations')
location: 'left' location: 'left'
content: [ content: [
{ {
where: 'Used anywhere (admin only)' where: __('Used anywhere (admin only)')
shortcuts: [ shortcuts: [
{ {
admin: true admin: true
key: 't' key: 't'
hotkeys: true hotkeys: true
description: 'Enable/disable inline translations' description: __('Enable/disable inline translations')
globalEvent: 'translation-mode' globalEvent: 'translation-mode'
} }
] ]
@ -329,16 +329,16 @@ App.Config.set(
] ]
} }
{ {
headline: 'Tickets' headline: __('Tickets')
location: 'right' location: 'right'
content: [ content: [
{ {
where: 'Used when viewing a Ticket' where: __('Used when viewing a Ticket')
shortcuts: [ shortcuts: [
{ {
key: 'm' key: 'm'
hotkeys: true hotkeys: true
description: 'Open note box' description: __('Open note box')
globalEvent: 'article-note-open' globalEvent: 'article-note-open'
callback: -> callback: ->
App.Event.trigger('keyboard_shortcuts_close') App.Event.trigger('keyboard_shortcuts_close')
@ -348,7 +348,7 @@ App.Config.set(
{ {
key: 'g' key: 'g'
hotkeys: true hotkeys: true
description: 'Reply to last article' description: __('Reply to last article')
globalEvent: 'article-reply' globalEvent: 'article-reply'
callback: -> callback: ->
App.Event.trigger('keyboard_shortcuts_close') App.Event.trigger('keyboard_shortcuts_close')
@ -362,7 +362,7 @@ App.Config.set(
{ {
key: 'j' key: 'j'
hotkeys: true hotkeys: true
description: 'Set article to internal/public' description: __('Set article to internal/public')
globalEvent: 'article-internal-public' globalEvent: 'article-internal-public'
callback: -> callback: ->
App.Event.trigger('keyboard_shortcuts_close') App.Event.trigger('keyboard_shortcuts_close')
@ -371,7 +371,7 @@ App.Config.set(
#{ #{
# key: 'm' # key: 'm'
# hotkeys: true # hotkeys: true
# description: 'Open macro selection' # description: __('Open macro selection')
# globalEvent: 'macro-open' # globalEvent: 'macro-open'
# callback: -> # callback: ->
# window.location.hash = '#ticket/create' # window.location.hash = '#ticket/create'
@ -379,7 +379,7 @@ App.Config.set(
{ {
key: 'c' key: 'c'
hotkeys: true hotkeys: true
description: 'Update as closed' description: __('Update as closed')
globalEvent: 'task-update-close' globalEvent: 'task-update-close'
callback: -> callback: ->
App.Event.trigger('keyboard_shortcuts_close') App.Event.trigger('keyboard_shortcuts_close')
@ -390,29 +390,29 @@ App.Config.set(
{ {
key: ['', ''] key: ['', '']
hotkeys: true hotkeys: true
description: 'Navigate through article' description: __('Navigate through article')
} }
] ]
}, },
{ {
where: 'Used when composing a Ticket article' where: __('Used when composing a Ticket article')
shortcuts: [ shortcuts: [
{ {
key: '::' key: '::'
hotkeys: false hotkeys: false
description: 'Inserts Text module' description: __('Inserts Text module')
globalEvent: 'richtext-insert-text-module' globalEvent: 'richtext-insert-text-module'
} }
{ {
key: '??' key: '??'
hotkeys: false hotkeys: false
description: 'Inserts Knowledge Base answer' description: __('Inserts Knowledge Base answer')
globalEvent: 'richtext-insert-kb-answer' globalEvent: 'richtext-insert-kb-answer'
} }
{ {
key: '@@' key: '@@'
hotkeys: false hotkeys: false
description: 'Inserts a mention for a user' description: __('Inserts a mention for a user')
globalEvent: 'richtext-insert-mention-user' globalEvent: 'richtext-insert-mention-user'
} }
] ]
@ -421,101 +421,101 @@ App.Config.set(
] ]
} }
{ {
headline: 'Text editing' headline: __('Text editing')
location: 'right' location: 'right'
content: [ content: [
{ {
where: 'Used when composing a text' where: __('Used when composing a text')
shortcuts: [ shortcuts: [
{ {
key: 'u' key: 'u'
magicKey: true magicKey: true
description: 'Format as _underlined_' description: __('Format as _underlined_')
globalEvent: 'richtext-underline' globalEvent: 'richtext-underline'
} }
{ {
key: 'b' key: 'b'
magicKey: true magicKey: true
description: 'Format as |bold|' description: __('Format as |bold|')
globalEvent: 'richtext-bold' globalEvent: 'richtext-bold'
} }
{ {
key: 'i' key: 'i'
magicKey: true magicKey: true
description: 'Format as ||italic||' description: __('Format as ||italic||')
globalEvent: 'richtext-italic' globalEvent: 'richtext-italic'
} }
{ {
key: 's' key: 's'
magicKey: true magicKey: true
description: 'Format as //strikethrough//' description: __('Format as //strikethrough//')
globalEvent: 'richtext-strikethrough' globalEvent: 'richtext-strikethrough'
} }
{ {
key: 'v' key: 'v'
magicKey: true magicKey: true
description: 'Paste from clipboard' description: __('Paste from clipboard')
globalEvent: 'clipboard-paste' globalEvent: 'clipboard-paste'
} }
{ {
key: 'v' key: 'v'
magicKey: true magicKey: true
shiftKey: true shiftKey: true
description: 'Paste from clipboard (plain text)' description: __('Paste from clipboard (plain text)')
globalEvent: 'clipboard-paste-plain-text' globalEvent: 'clipboard-paste-plain-text'
} }
{ {
key: 'f' key: 'f'
hotkeys: true hotkeys: true
description: 'Removes the formatting' description: __('Removes the formatting')
globalEvent: 'richtext-remove-formating' globalEvent: 'richtext-remove-formating'
} }
{ {
key: 'y' key: 'y'
hotkeys: true hotkeys: true
description: '...of whole textarea' description: __('...of whole textarea')
globalEvent: 'richtext-remove-formating-textarea' globalEvent: 'richtext-remove-formating-textarea'
} }
{ {
key: 'z' key: 'z'
hotkeys: true, hotkeys: true,
description: 'Inserts a horizontal rule' description: __('Inserts a horizontal rule')
globalEvent: 'richtext-hr' globalEvent: 'richtext-hr'
} }
{ {
key: 'l' key: 'l'
hotkeys: true, hotkeys: true,
description: 'Format as unordered list' description: __('Format as unordered list')
globalEvent: 'richtext-ul' globalEvent: 'richtext-ul'
} }
{ {
key: 'k' key: 'k'
hotkeys: true, hotkeys: true,
description: 'Format as ordered list' description: __('Format as ordered list')
globalEvent: 'richtext-ol' globalEvent: 'richtext-ol'
} }
{ {
key: '1' key: '1'
hotkeys: true, hotkeys: true,
description: 'Format as h1 heading' description: __('Format as h1 heading')
globalEvent: 'richtext-h1' globalEvent: 'richtext-h1'
} }
{ {
key: '2' key: '2'
hotkeys: true, hotkeys: true,
description: 'Format as h2 heading' description: __('Format as h2 heading')
globalEvent: 'richtext-h2' globalEvent: 'richtext-h2'
} }
{ {
key: '3' key: '3'
hotkeys: true, hotkeys: true,
description: 'Format as h3 heading' description: __('Format as h3 heading')
globalEvent: 'richtext-h3' globalEvent: 'richtext-h3'
} }
{ {
key: 'x' key: 'x'
hotkeys: true, hotkeys: true,
description: 'Removes any hyperlink' description: __('Removes any hyperlink')
globalEvent: 'richtext-remove-hyperlink' globalEvent: 'richtext-remove-hyperlink'
} }
] ]

View file

@ -22,9 +22,9 @@ class Maintenance extends App.Controller
showMessage: (message = {}) => showMessage: (message = {}) =>
if message.reload if message.reload
@disconnectClient() @disconnectClient()
button = 'Continue session' button = __('Continue session')
else else
button = 'Close' button = __('Close')
new App.SessionMessage( new App.SessionMessage(
head: message.head head: message.head
@ -62,7 +62,7 @@ class Maintenance extends App.Controller
maintanaceRestartManual: (data) => maintanaceRestartManual: (data) =>
return if @messageRestartManual return if @messageRestartManual
@messageRestartManual = new App.SessionMessage( @messageRestartManual = new App.SessionMessage(
head: App.i18n.translateInline('Zammad need a restart!') head: App.i18n.translateInline('Zammad requires a restart!')
contentInline: App.i18n.translateInline('Some system settings have changed, please restart all Zammad processes! If you want to do this automatically, set environment variable APP_RESTART_CMD="/path/to/your_app_script.sh restart".') contentInline: App.i18n.translateInline('Some system settings have changed, please restart all Zammad processes! If you want to do this automatically, set environment variable APP_RESTART_CMD="/path/to/your_app_script.sh restart".')
keyboard: false keyboard: false
backdrop: false backdrop: false
@ -82,7 +82,7 @@ class Maintenance extends App.Controller
keyboard: false keyboard: false
backdrop: true backdrop: true
buttonClose: false buttonClose: false
buttonSubmit: 'Continue session' buttonSubmit: __('Continue session')
forceReload: true forceReload: true
) )
@ -102,7 +102,7 @@ class Maintenance extends App.Controller
keyboard: false keyboard: false
backdrop: true backdrop: true
buttonClose: false buttonClose: false
buttonSubmit: 'Continue session' buttonSubmit: __('Continue session')
forceReload: true forceReload: true
) )
@delay(message, 2000) @delay(message, 2000)

View file

@ -483,7 +483,7 @@ class Navigation extends App.Controller
navheader = false navheader = false
if prio is 80 if prio is 80
divider = true divider = true
navheader = 'Recently viewed' navheader = __('Recently viewed')
prio++ prio++
NavBarRight['RecendViewed::' + item.o_id + item.object + '-' + prio ] = { NavBarRight['RecendViewed::' + item.o_id + item.object + '-' + prio ] = {

View file

@ -35,12 +35,12 @@ class SessionTakeOver extends App.Controller
# only if new client id isn't own client id # only if new client id isn't own client id
if data.taskbar_id isnt App.TaskManager.TaskbarId() if data.taskbar_id isnt App.TaskManager.TaskbarId()
@error = new App.SessionMessage( @error = new App.SessionMessage(
head: 'Session' head: __('Session')
message: 'A new session was created with your account. This session will be stopped to prevent a conflict.' message: __('A new session was created with your account. This session will be stopped to prevent a conflict.')
keyboard: false keyboard: false
backdrop: true backdrop: true
buttonClose: false buttonClose: false
buttonSubmit: 'Continue session' buttonSubmit: __('Continue session')
forceReload: true forceReload: true
) )
@disconnectClient() @disconnectClient()

View file

@ -95,12 +95,12 @@ class SessionTimeout extends App.Controller
return if @warningDialog return if @warningDialog
@warningDialog = new App.ControllerModal( @warningDialog = new App.ControllerModal(
head: 'Session' head: __('Session')
message: 'Due to inactivity are automatically logged out within the next 30 seconds.' message: __('Due to inactivity are automatically logged out within the next 30 seconds.')
keyboard: true keyboard: true
backdrop: true backdrop: true
buttonClose: true buttonClose: true
buttonSubmit: 'Continue session' buttonSubmit: __('Continue session')
onSubmit: => onSubmit: =>
@lastEvent = @currentTime() @lastEvent = @currentTime()
@checkLogout() @checkLogout()

View file

@ -7,8 +7,8 @@ class TranslationSupport extends App.Controller
# only show if system is already up and running # only show if system is already up and running
return if !@Config.get('system_init_done') return if !@Config.get('system_init_done')
# to not translate en # do not show for English locales
return if !App.i18n.notTranslatedFeatureEnabled(App.i18n.get()) return if App.i18n.get().substr(0,2) is 'en'
# only show for admins # only show for admins
return if !@permissionCheck('admin.translation') return if !@permissionCheck('admin.translation')
@ -30,13 +30,13 @@ class TranslationSupport extends App.Controller
if App.Session.get() isnt undefined if App.Session.get() isnt undefined
@delay(check, 2500, 'translation_support') @delay(check, 2500, 'translation_support')
App.Config.set( 'translaton_support', TranslationSupport, 'Plugins' ) App.Config.set( 'translation_support', TranslationSupport, 'Plugins' )
class Modal extends App.ControllerModal class Modal extends App.ControllerModal
buttonClose: true buttonClose: true
buttonCancel: 'No Thanks!' buttonCancel: __('No Thanks!')
buttonSubmit: 'Complete translations' buttonSubmit: __('Complete translations')
head: 'Help to improve Zammad!' head: __('Help to improve Zammad!')
shown: false shown: false
constructor: -> constructor: ->
@ -45,12 +45,8 @@ class Modal extends App.ControllerModal
@render() @render()
content: => content: =>
better = false
if @percent > 80
better = true
App.view('translation/support')( App.view('translation/support')(
percent: @percent percent: @percent
better: better
) )
onCancel: => onCancel: =>

View file

@ -31,11 +31,11 @@ class UserSignupCheck extends App.Controller
class Modal extends App.ControllerModal class Modal extends App.ControllerModal
backdrop: false backdrop: false
keyboard: false keyboard: false
head: 'Account not verified' head: __('Account not verified')
small: true small: true
buttonClose: false buttonClose: false
buttonCancel: false buttonCancel: false
buttonSubmit: 'Resend verification email' buttonSubmit: __('Resend verification email')
constructor: -> constructor: ->
super super

View file

@ -1,6 +1,6 @@
class ProfileAvatar extends App.ControllerSubContent class ProfileAvatar extends App.ControllerSubContent
requiredPermission: 'user_preferences.avatar' requiredPermission: 'user_preferences.avatar'
header: 'Avatar' header: __('Avatar')
elements: elements:
'.js-upload': 'fileInput' '.js-upload': 'fileInput'
'.avatar-gallery': 'avatarGallery' '.avatar-gallery': 'avatarGallery'
@ -145,13 +145,13 @@ class ProfileAvatar extends App.ControllerSubContent
reader.readAsDataURL(@) reader.readAsDataURL(@)
App.Config.set('Avatar', { prio: 1100, name: 'Avatar', parent: '#profile', target: '#profile/avatar', controller: ProfileAvatar, permission: ['user_preferences.avatar'] }, 'NavBarProfile') App.Config.set('Avatar', { prio: 1100, name: __('Avatar'), parent: '#profile', target: '#profile/avatar', controller: ProfileAvatar, permission: ['user_preferences.avatar'] }, 'NavBarProfile')
class ImageCropper extends App.ControllerModal class ImageCropper extends App.ControllerModal
buttonClose: true buttonClose: true
buttonCancel: true buttonCancel: true
buttonSubmit: 'Save' buttonSubmit: __('Save')
head: 'Crop Image' head: __('Crop Image')
elements: elements:
'.imageCropper-image': 'image' '.imageCropper-image': 'image'
@ -229,13 +229,13 @@ class ImageCropper extends App.ControllerModal
class Camera extends App.ControllerModal class Camera extends App.ControllerModal
buttonClose: true buttonClose: true
buttonCancel: true buttonCancel: true
buttonSubmit: 'Save' buttonSubmit: __('Save')
buttonClass: 'btn--success is-disabled' buttonClass: 'btn--success is-disabled'
centerButtons: [{ centerButtons: [{
className: 'btn--success js-shoot is-disabled', className: 'btn--success js-shoot is-disabled',
text: 'Shoot' text: __('Shoot')
}] }]
head: 'Camera' head: __('Camera')
elements: elements:
'.js-shoot': 'shootButton' '.js-shoot': 'shootButton'

View file

@ -1,6 +1,6 @@
class ProfileCalendarSubscriptions extends App.ControllerSubContent class ProfileCalendarSubscriptions extends App.ControllerSubContent
requiredPermission: 'user_preferences.calendar+ticket.agent' requiredPermission: 'user_preferences.calendar+ticket.agent'
header: 'Calendar' header: __('Calendar')
elements: elements:
'input[type=checkbox]': 'options' 'input[type=checkbox]': 'options'
'output': 'output' 'output': 'output'
@ -90,4 +90,4 @@ class ProfileCalendarSubscriptions extends App.ControllerSubContent
msg: App.i18n.translateContent(data.message) msg: App.i18n.translateContent(data.message)
) )
App.Config.set('CalendarSubscriptions', { prio: 3000, name: 'Calendar', parent: '#profile', target: '#profile/calendar_subscriptions', permission: ['user_preferences.calendar+ticket.agent'], controller: ProfileCalendarSubscriptions }, 'NavBarProfile') App.Config.set('CalendarSubscriptions', { prio: 3000, name: __('Calendar'), parent: '#profile', target: '#profile/calendar_subscriptions', permission: ['user_preferences.calendar+ticket.agent'], controller: ProfileCalendarSubscriptions }, 'NavBarProfile')

View file

@ -1,6 +1,6 @@
class ProfileDevices extends App.ControllerSubContent class ProfileDevices extends App.ControllerSubContent
requiredPermission: 'user_preferences.device' requiredPermission: 'user_preferences.device'
header: 'Devices' header: __('Devices')
events: events:
'click [data-type=delete]': 'delete' 'click [data-type=delete]': 'delete'
@ -55,4 +55,4 @@ class ProfileDevices extends App.ControllerSubContent
msg: App.i18n.translateContent(data.message) msg: App.i18n.translateContent(data.message)
) )
App.Config.set('Devices', { prio: 3100, name: 'Devices', parent: '#profile', target: '#profile/devices', controller: ProfileDevices, permission: ['user_preferences.device'] }, 'NavBarProfile') App.Config.set('Devices', { prio: 3100, name: __('Devices'), parent: '#profile', target: '#profile/devices', controller: ProfileDevices, permission: ['user_preferences.device'] }, 'NavBarProfile')

View file

@ -1,6 +1,6 @@
class ProfileLanguage extends App.ControllerSubContent class ProfileLanguage extends App.ControllerSubContent
requiredPermission: 'user_preferences.language' requiredPermission: 'user_preferences.language'
header: 'Language' header: __('Language')
events: events:
'submit form': 'update' 'submit form': 'update'
@ -69,4 +69,4 @@ class ProfileLanguage extends App.ControllerSubContent
msg: App.i18n.translateContent(data.message) msg: App.i18n.translateContent(data.message)
) )
App.Config.set('Language', { prio: 1000, name: 'Language', parent: '#profile', target: '#profile/language', controller: ProfileLanguage, permission: ['user_preferences.language'] }, 'NavBarProfile') App.Config.set('Language', { prio: 1000, name: __('Language'), parent: '#profile', target: '#profile/language', controller: ProfileLanguage, permission: ['user_preferences.language'] }, 'NavBarProfile')

View file

@ -1,6 +1,6 @@
class ProfileLinkedAccounts extends App.ControllerSubContent class ProfileLinkedAccounts extends App.ControllerSubContent
requiredPermission: 'user_preferences.linked_accounts' requiredPermission: 'user_preferences.linked_accounts'
header: 'Linked Accounts' header: __('Linked Accounts')
events: events:
'click .js-add': 'add' 'click .js-add': 'add'
'click .js-remove': 'remove' 'click .js-remove': 'remove'
@ -59,56 +59,56 @@ class ProfileLinkedAccounts extends App.ControllerSubContent
msg: App.i18n.translateContent(data.message) msg: App.i18n.translateContent(data.message)
) )
App.Config.set('LinkedAccounts', { prio: 4000, name: 'Linked Accounts', parent: '#profile', target: '#profile/linked', controller: ProfileLinkedAccounts, permission: ['user_preferences.linked_accounts'] }, 'NavBarProfile') App.Config.set('LinkedAccounts', { prio: 4000, name: __('Linked Accounts'), parent: '#profile', target: '#profile/linked', controller: ProfileLinkedAccounts, permission: ['user_preferences.linked_accounts'] }, 'NavBarProfile')
App.Config.set('auth_provider_all', { App.Config.set('auth_provider_all', {
facebook: facebook:
url: '/auth/facebook' url: '/auth/facebook'
name: 'Facebook' name: __('Facebook')
config: 'auth_facebook' config: 'auth_facebook'
class: 'facebook' class: 'facebook'
twitter: twitter:
url: '/auth/twitter' url: '/auth/twitter'
name: 'Twitter' name: __('Twitter')
config: 'auth_twitter' config: 'auth_twitter'
class: 'twitter' class: 'twitter'
linkedin: linkedin:
url: '/auth/linkedin' url: '/auth/linkedin'
name: 'LinkedIn' name: __('LinkedIn')
config: 'auth_linkedin' config: 'auth_linkedin'
class: 'linkedin' class: 'linkedin'
github: github:
url: '/auth/github' url: '/auth/github'
name: 'GitHub' name: __('GitHub')
config: 'auth_github' config: 'auth_github'
class: 'github' class: 'github'
gitlab: gitlab:
url: '/auth/gitlab' url: '/auth/gitlab'
name: 'GitLab' name: __('GitLab')
config: 'auth_gitlab' config: 'auth_gitlab'
class: 'gitlab' class: 'gitlab'
microsoft_office365: microsoft_office365:
url: '/auth/microsoft_office365' url: '/auth/microsoft_office365'
name: 'Office 365' name: __('Office 365')
config: 'auth_microsoft_office365' config: 'auth_microsoft_office365'
class: 'office365' class: 'office365'
google_oauth2: google_oauth2:
url: '/auth/google_oauth2' url: '/auth/google_oauth2'
name: 'Google' name: __('Google')
config: 'auth_google_oauth2' config: 'auth_google_oauth2'
class: 'google' class: 'google'
weibo: weibo:
url: '/auth/weibo' url: '/auth/weibo'
name: 'Weibo' name: __('Weibo')
config: 'auth_weibo' config: 'auth_weibo'
class: 'weibo' class: 'weibo'
saml: saml:
url: '/auth/saml' url: '/auth/saml'
name: 'SAML' name: __('SAML')
config: 'auth_saml' config: 'auth_saml'
class: 'saml' class: 'saml'
sso: sso:
url: '/auth/sso' url: '/auth/sso'
name: 'SSO' name: __('SSO')
config: 'auth_sso' config: 'auth_sso'
class: 'sso' class: 'sso'
}) })

View file

@ -1,45 +1,45 @@
class ProfileNotification extends App.ControllerSubContent class ProfileNotification extends App.ControllerSubContent
requiredPermission: 'user_preferences.notifications+ticket.agent' requiredPermission: 'user_preferences.notifications+ticket.agent'
header: 'Notifications' header: __('Notifications')
events: events:
'submit form': 'update' 'submit form': 'update'
'change .js-notificationSound': 'previewSound' 'change .js-notificationSound': 'previewSound'
sounds: [ sounds: [
{ {
name: 'Bell' name: __('Bell')
file: 'Bell.mp3' file: 'Bell.mp3'
}, },
{ {
name: 'Kalimba' name: __('Kalimba')
file: 'Kalimba.mp3' file: 'Kalimba.mp3'
}, },
{ {
name: 'Marimba' name: __('Marimba')
file: 'Marimba.mp3' file: 'Marimba.mp3'
}, },
{ {
name: 'Peep' name: __('Peep')
file: 'Peep.mp3' file: 'Peep.mp3'
}, },
{ {
name: 'Plop' name: __('Plop')
file: 'Plop.mp3' file: 'Plop.mp3'
}, },
{ {
name: 'Ring' name: __('Ring')
file: 'Ring.mp3' file: 'Ring.mp3'
}, },
{ {
name: 'Space' name: __('Space')
file: 'Space.mp3' file: 'Space.mp3'
}, },
{ {
name: 'Wood' name: __('Wood')
file: 'Wood.mp3' file: 'Wood.mp3'
}, },
{ {
name: 'Xylo' name: __('Xylo')
file: 'Xylo.mp3' file: 'Xylo.mp3'
} }
] ]
@ -52,13 +52,13 @@ class ProfileNotification extends App.ControllerSubContent
matrix = matrix =
create: create:
name: 'New Ticket' name: __('New Ticket')
update: update:
name: 'Ticket update' name: __('Ticket update')
reminder_reached: reminder_reached:
name: 'Ticket reminder reached' name: __('Ticket reminder reached')
escalation: escalation:
name: 'Ticket escalation' name: __('Ticket escalation')
config = config =
group_ids: [] group_ids: []
@ -196,4 +196,4 @@ class ProfileNotification extends App.ControllerSubContent
return if !params.notification_sound.file return if !params.notification_sound.file
App.OnlineNotification.play(params.notification_sound.file) App.OnlineNotification.play(params.notification_sound.file)
App.Config.set('Notifications', { prio: 2600, name: 'Notifications', parent: '#profile', target: '#profile/notifications', permission: ['user_preferences.notifications+ticket.agent'], controller: ProfileNotification }, 'NavBarProfile') App.Config.set('Notifications', { prio: 2600, name: __('Notifications'), parent: '#profile', target: '#profile/notifications', permission: ['user_preferences.notifications+ticket.agent'], controller: ProfileNotification }, 'NavBarProfile')

View file

@ -1,6 +1,6 @@
class ProfileOutOfOffice extends App.ControllerSubContent class ProfileOutOfOffice extends App.ControllerSubContent
requiredPermission: 'user_preferences.out_of_office+ticket.agent' requiredPermission: 'user_preferences.out_of_office+ticket.agent'
header: 'Out of Office' header: __('Out of Office')
events: events:
'submit form': 'submit' 'submit form': 'submit'
'click .js-disabled': 'disable' 'click .js-disabled': 'disable'
@ -70,7 +70,7 @@ class ProfileOutOfOffice extends App.ControllerSubContent
multiple: false multiple: false
limit: 30 limit: 30
minLengt: 2 minLengt: 2
placeholder: 'Enter Person or Organization/Company' placeholder: __('Enter Person or Organization/Company')
null: false null: false
translate: false translate: false
disableCreateObject: true disableCreateObject: true
@ -131,7 +131,7 @@ class ProfileOutOfOffice extends App.ControllerSubContent
@render() @render()
@notify( @notify(
type: 'success' type: 'success'
msg: App.i18n.translateContent('Successfully!') msg: App.i18n.translateContent('Update successful.')
timeout: 1000 timeout: 1000
) )
else else
@ -143,7 +143,7 @@ class ProfileOutOfOffice extends App.ControllerSubContent
else else
@notify @notify
type: 'error' type: 'error'
msg: 'Please contact your administrator.' msg: __('Please contact your administrator.')
removeAll: true removeAll: true
@formEnable( @$('form') ) @formEnable( @$('form') )
@ -168,4 +168,4 @@ class ProfileOutOfOffice extends App.ControllerSubContent
msg: App.i18n.translateContent(message) msg: App.i18n.translateContent(message)
removeAll: true removeAll: true
App.Config.set('OutOfOffice', { prio: 2800, name: 'Out of Office', parent: '#profile', target: '#profile/out_of_office', permission: ['user_preferences.out_of_office+ticket.agent'], controller: ProfileOutOfOffice }, 'NavBarProfile') App.Config.set('OutOfOffice', { prio: 2800, name: __('Out of Office'), parent: '#profile', target: '#profile/out_of_office', permission: ['user_preferences.out_of_office+ticket.agent'], controller: ProfileOutOfOffice }, 'NavBarProfile')

View file

@ -1,6 +1,6 @@
class ProfilePassword extends App.ControllerSubContent class ProfilePassword extends App.ControllerSubContent
requiredPermission: 'user_preferences.password' requiredPermission: 'user_preferences.password'
header: 'Password' header: __('Password')
events: events:
'submit form': 'update' 'submit form': 'update'
@ -14,8 +14,8 @@ class ProfilePassword extends App.ControllerSubContent
html = $( App.view('profile/password')() ) html = $( App.view('profile/password')() )
configure_attributes = [ configure_attributes = [
{ name: 'password_old', display: 'Current password', tag: 'input', type: 'password', limit: 100, null: false, class: 'input', single: true }, { name: 'password_old', display: __('Current password'), tag: 'input', type: 'password', limit: 100, null: false, class: 'input', single: true },
{ name: 'password_new', display: 'New password', tag: 'input', type: 'password', limit: 100, null: false, class: 'input', }, { name: 'password_new', display: __('New password'), tag: 'input', type: 'password', limit: 100, null: false, class: 'input', },
] ]
@form = new App.ControllerForm( @form = new App.ControllerForm(
@ -37,14 +37,14 @@ class ProfilePassword extends App.ControllerSubContent
@$('[name=password_new_confirm]').val('') @$('[name=password_new_confirm]').val('')
@notify @notify
type: 'error' type: 'error'
msg: 'Can\'t update password, your new passwords do not match. Please try again!' msg: __('Can\'t update password, your new passwords do not match. Please try again!')
removeAll: true removeAll: true
return return
if !params['password_new'] if !params['password_new']
@formEnable(e) @formEnable(e)
@notify @notify
type: 'error' type: 'error'
msg: 'Please supply your new password!' msg: __('Please supply your new password!')
removeAll: true removeAll: true
return return
@ -74,8 +74,8 @@ class ProfilePassword extends App.ControllerSubContent
else else
@notify @notify
type: 'error' type: 'error'
msg: 'Unable to set password. Please contact your administrator.' msg: __('Unable to set password. Please contact your administrator.')
removeAll: true removeAll: true
@formEnable( @$('form') ) @formEnable( @$('form') )
App.Config.set('Password', { prio: 2000, name: 'Password', parent: '#profile', target: '#profile/password', controller: ProfilePassword, permission: ['user_preferences.password'] }, 'NavBarProfile') App.Config.set('Password', { prio: 2000, name: __('Password'), parent: '#profile', target: '#profile/password', controller: ProfilePassword, permission: ['user_preferences.password'] }, 'NavBarProfile')

View file

@ -1,6 +1,6 @@
class ProfileTokenAccess extends App.ControllerSubContent class ProfileTokenAccess extends App.ControllerSubContent
requiredPermission: 'user_preferences.access_token' requiredPermission: 'user_preferences.access_token'
header: 'Token Access' header: __('Token Access')
events: events:
'click .js-delete': 'delete' 'click .js-delete': 'delete'
'click .js-create': 'create' 'click .js-create': 'create'
@ -63,7 +63,7 @@ class ProfileTokenAccess extends App.ControllerSubContent
) )
new App.ControllerConfirm( new App.ControllerConfirm(
message: 'Sure?' message: __('Sure?')
callback: callback callback: callback
container: @el.closest('.content') container: @el.closest('.content')
) )
@ -76,8 +76,8 @@ class ProfileTokenAccess extends App.ControllerSubContent
) )
class Create extends App.ControllerModal class Create extends App.ControllerModal
head: 'Add a Personal Access Token' head: __('Add a Personal Access Token')
buttonSubmit: 'Create' buttonSubmit: __('Create')
buttonCancel: true buttonCancel: true
shown: true shown: true
@ -125,8 +125,8 @@ class Create extends App.ControllerModal
return if !@newToken return if !@newToken
ui = @ ui = @
new App.ControllerModal( new App.ControllerModal(
head: 'Your New Personal Access Token' head: __('Your New Personal Access Token')
buttonSubmit: "OK, I've copied my token" buttonSubmit: __("OK, I've copied my token")
content: -> content: ->
App.view('profile/token_access_created')( App.view('profile/token_access_created')(
name: ui.newToken.name name: ui.newToken.name
@ -149,7 +149,7 @@ class Create extends App.ControllerModal
App.Config.set('Token Access', { App.Config.set('Token Access', {
prio: 3200, prio: 3200,
name: 'Token Access', name: __('Token Access'),
parent: '#profile', parent: '#profile',
target: '#profile/token_access', target: '#profile/token_access',
controller: ProfileTokenAccess, controller: ProfileTokenAccess,

View file

@ -48,12 +48,11 @@ class App.SettingsAreaProxy extends App.Controller
} }
return return
new App.ControllerConfirm( new App.ControllerConfirm(
head: 'Error' head: __('Error')
message: data.message message: data.message
buttonClass: 'btn--success' buttonClass: 'btn--success'
buttonCancel: false buttonCancel: false
buttonSubmit: 'Close' buttonSubmit: __('Close')
container: @el.closest('.content') container: @el.closest('.content')
) )
) )

View file

@ -90,7 +90,7 @@ class App.SettingsAreaSwitch extends App.Controller
ui.formEnable(e) ui.formEnable(e)
App.Event.trigger 'notify', { App.Event.trigger 'notify', {
type: 'error' type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to update object!') msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to update object!'))
timeout: 2000 timeout: 2000
} }
) )

View file

@ -126,7 +126,7 @@ class App.SettingsAreaTicketNumber extends App.Controller
ui.formEnable(e) ui.formEnable(e)
App.Event.trigger 'notify', { App.Event.trigger 'notify', {
type: 'error' type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to update object!') msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to update object!'))
timeout: 2000 timeout: 2000
} }
) )

View file

@ -88,7 +88,7 @@ class App.SettingsForm extends App.Controller
fail: (settings, details) -> fail: (settings, details) ->
App.Event.trigger('notify', { App.Event.trigger('notify', {
type: 'error' type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to update object!') msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to update object!'))
timeout: 2000 timeout: 2000
}) })
) )

View file

@ -5,59 +5,59 @@ class App.UiElement.ApplicationSelector
groups = groups =
ticket: ticket:
name: 'Ticket' name: __('Ticket')
model: 'Ticket' model: 'Ticket'
article: article:
name: 'Article' name: __('Article')
model: 'TicketArticle' model: 'TicketArticle'
customer: customer:
name: 'Customer' name: __('Customer')
model: 'User' model: 'User'
organization: organization:
name: 'Organization' name: __('Organization')
model: 'Organization' model: 'Organization'
if attribute.executionTime if attribute.executionTime
groups.execution_time = groups.execution_time =
name: 'Execution Time' name: __('Execution Time')
operators_type = operators_type =
'^datetime$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'after (relative)', 'within next (relative)', 'within last (relative)', 'till (relative)', 'from (relative)'] '^datetime$': [__('before (absolute)'), __('after (absolute)'), __('before (relative)'), __('after (relative)'), __('within next (relative)'), __('within last (relative)'), __('till (relative)'), __('from (relative)')]
'^timestamp$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'after (relative)', 'within next (relative)', 'within last (relative)', 'till (relative)', 'from (relative)'] '^timestamp$': [__('before (absolute)'), __('after (absolute)'), __('before (relative)'), __('after (relative)'), __('within next (relative)'), __('within last (relative)'), __('till (relative)'), __('from (relative)')]
'^date$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'after (relative)', 'within next (relative)', 'within last (relative)'] '^date$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'after (relative)', 'within next (relative)', 'within last (relative)']
'boolean$': ['is', 'is not'] 'boolean$': [__('is'), __('is not')]
'integer$': ['is', 'is not'] 'integer$': [__('is'), __('is not')]
'^radio$': ['is', 'is not'] '^radio$': [__('is'), __('is not')]
'^select$': ['is', 'is not'] '^select$': [__('is'), __('is not')]
'^tree_select$': ['is', 'is not'] '^tree_select$': [__('is'), __('is not')]
'^input$': ['contains', 'contains not'] '^input$': [__('contains'), __('contains not')]
'^richtext$': ['contains', 'contains not'] '^richtext$': [__('contains'), __('contains not')]
'^textarea$': ['contains', 'contains not'] '^textarea$': [__('contains'), __('contains not')]
'^tag$': ['contains all', 'contains one', 'contains all not', 'contains one not'] '^tag$': [__('contains all'), __('contains one'), __('contains all not'), __('contains one not')]
if attribute.hasChanged if attribute.hasChanged
operators_type = operators_type =
'^datetime$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'after (relative)', 'within next (relative)', 'within last (relative)', 'till (relative)', 'from (relative)', 'has changed'] '^datetime$': [__('before (absolute)'), __('after (absolute)'), __('before (relative)'), __('after (relative)'), __('within next (relative)'), __('within last (relative)'), __('till (relative)'), __('from (relative)'), __('has changed')]
'^timestamp$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'after (relative)', 'within next (relative)', 'within last (relative)', 'till (relative)', 'from (relative)', 'has changed'] '^timestamp$': [__('before (absolute)'), __('after (absolute)'), __('before (relative)'), __('after (relative)'), __('within next (relative)'), __('within last (relative)'), __('till (relative)'), __('from (relative)'), __('has changed')]
'^date$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'after (relative)', 'within next (relative)', 'within last (relative)', 'till (relative)', 'from (relative)', 'has changed'] '^date$': [__('before (absolute)'), __('after (absolute)'), __('before (relative)'), __('after (relative)'), __('within next (relative)'), __('within last (relative)'), __('till (relative)'), __('from (relative)'), __('has changed')]
'boolean$': ['is', 'is not', 'has changed'] 'boolean$': [__('is'), __('is not'), __('has changed')]
'integer$': ['is', 'is not', 'has changed'] 'integer$': [__('is'), __('is not'), __('has changed')]
'^radio$': ['is', 'is not', 'has changed'] '^radio$': [__('is'), __('is not'), __('has changed')]
'^select$': ['is', 'is not', 'has changed'] '^select$': [__('is'), __('is not'), __('has changed')]
'^tree_select$': ['is', 'is not', 'has changed'] '^tree_select$': [__('is'), __('is not'), __('has changed')]
'^input$': ['contains', 'contains not', 'has changed'] '^input$': [__('contains'), __('contains not'), __('has changed')]
'^richtext$': ['contains', 'contains not', 'has changed'] '^richtext$': [__('contains'), __('contains not'), __('has changed')]
'^textarea$': ['contains', 'contains not', 'has changed'] '^textarea$': [__('contains'), __('contains not'), __('has changed')]
'^tag$': ['contains all', 'contains one', 'contains all not', 'contains one not'] '^tag$': [__('contains all'), __('contains one'), __('contains all not'), __('contains one not')]
operators_name = operators_name =
'_id$': ['is', 'is not'] '_id$': [__('is'), __('is not')]
'_ids$': ['is', 'is not'] '_ids$': [__('is'), __('is not')]
if attribute.hasChanged if attribute.hasChanged
operators_name = operators_name =
'_id$': ['is', 'is not', 'has changed'] '_id$': [__('is'), __('is not'), __('has changed')]
'_ids$': ['is', 'is not', 'has changed'] '_ids$': [__('is'), __('is not'), __('has changed')]
# merge config # merge config
elements = {} elements = {}
@ -68,7 +68,7 @@ class App.UiElement.ApplicationSelector
if attribute.action if attribute.action
elements['ticket.action'] = elements['ticket.action'] =
name: 'action' name: 'action'
display: 'Action' display: __('Action')
tag: 'select' tag: 'select'
null: false null: false
translate: true translate: true
@ -77,19 +77,19 @@ class App.UiElement.ApplicationSelector
update: 'updated' update: 'updated'
'update.merged_into': 'merged into' 'update.merged_into': 'merged into'
'update.received_merge': 'received merge' 'update.received_merge': 'received merge'
operator: ['is', 'is not'] operator: [__('is'), __('is not')]
for groupKey, groupMeta of groups for groupKey, groupMeta of groups
if groupKey is 'execution_time' if groupKey is 'execution_time'
if attribute.executionTime if attribute.executionTime
elements['execution_time.calendar_id'] = elements['execution_time.calendar_id'] =
name: 'calendar_id' name: 'calendar_id'
display: 'Calendar' display: __('Calendar')
tag: 'select' tag: 'select'
relation: 'Calendar' relation: 'Calendar'
null: false null: false
translate: false translate: false
operator: ['is in working time', 'is not in working time'] operator: [__('is in working time'), __('is not in working time')]
else else
for row in App[groupMeta.model].configure_attributes for row in App[groupMeta.model].configure_attributes
@ -115,12 +115,12 @@ class App.UiElement.ApplicationSelector
if attribute.out_of_office if attribute.out_of_office
elements['ticket.out_of_office_replacement_id'] = elements['ticket.out_of_office_replacement_id'] =
name: 'out_of_office_replacement_id' name: 'out_of_office_replacement_id'
display: 'Out of office replacement' display: __('Out of office replacement')
tag: 'autocompletion_ajax' tag: 'autocompletion_ajax'
relation: 'User' relation: 'User'
null: false null: false
translate: true translate: true
operator: ['is', 'is not'] operator: [__('is'), __('is not')]
# Remove 'has changed' operator from attributes which don't support the operator. # Remove 'has changed' operator from attributes which don't support the operator.
['ticket.created_at', 'ticket.updated_at'].forEach (element_name) -> ['ticket.created_at', 'ticket.updated_at'].forEach (element_name) ->
@ -128,12 +128,12 @@ class App.UiElement.ApplicationSelector
elements['ticket.mention_user_ids'] = elements['ticket.mention_user_ids'] =
name: 'mention_user_ids' name: 'mention_user_ids'
display: 'Subscribe' display: __('Subscribe')
tag: 'autocompletion_ajax' tag: 'autocompletion_ajax'
relation: 'User' relation: 'User'
null: false null: false
translate: true translate: true
operator: ['is', 'is not'] operator: [__('is'), __('is not')]
[defaults, groups, elements] [defaults, groups, elements]
@ -350,6 +350,9 @@ class App.UiElement.ApplicationSelector
@buildOperator(elementFull, elementRow, groupAndAttribute, elements, meta, attribute) @buildOperator(elementFull, elementRow, groupAndAttribute, elements, meta, attribute)
@mapOperatorDisplayName: (operator) ->
return operator
@buildOperator: (elementFull, elementRow, groupAndAttribute, elements, meta, attribute) -> @buildOperator: (elementFull, elementRow, groupAndAttribute, elements, meta, attribute) ->
currentOperator = elementRow.find('.js-operator option:selected').attr('value') currentOperator = elementRow.find('.js-operator option:selected').attr('value')
@ -376,7 +379,7 @@ class App.UiElement.ApplicationSelector
break break
for operator in attributeConfig.operator for operator in attributeConfig.operator
operatorName = App.i18n.translateInline(operator.replace(/_/g, ' ')) operatorName = App.i18n.translateInline(@mapOperatorDisplayName(operator))
selected = '' selected = ''
if !groupAndAttribute.match(/^ticket/) && operator is 'has changed' if !groupAndAttribute.match(/^ticket/) && operator is 'has changed'
# do nothing, only show "has changed" in ticket attributes # do nothing, only show "has changed" in ticket attributes

View file

@ -8,8 +8,8 @@ class App.UiElement.active extends App.UiElement.ApplicationUiElement
# build options list # build options list
attribute.options = [ attribute.options = [
{ name: 'active', value: true } { name: __('active'), value: true }
{ name: 'inactive', value: false } { name: __('inactive'), value: false }
] ]
# build options list based on config # build options list based on config

View file

@ -145,14 +145,14 @@ class App.UiElement.basedate
@buildCustomDates: -> @buildCustomDates: ->
data = { data = {
days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], days: [__('Sunday'), __('Monday'), __('Tuesday'), __('Wednesday'), __('Thursday'), __('Friday'), __('Saturday')],
daysMin: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], daysMin: [__('Sun'), __('Mon'), __('Tue'), __('Wed'), __('Thu'), __('Fri'), __('Sat')],
daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], daysShort: [__('Sun'), __('Mon'), __('Tue'), __('Wed'), __('Thu'), __('Fri'), __('Sat')],
months: ['January', 'February', 'March', 'April', 'May', 'June', months: [__('January'), __('February'), __('March'), __('April'), __('May'), __('June'),
'July', 'August', 'September', 'October', 'November', 'December'], __('July'), __('August'), __('September'), __('October'), __('November'), __('December')],
monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], monthsShort: [__('Jan'), __('Feb'), __('Mar'), __('Apr'), __('May'), __('Jun'), __('Jul'), __('Aug'), __('Sep'), __('Oct'), __('Nov'), __('Dec')],
today: 'today', today: __('today'),
clear: 'clear' clear: __('clear')
} }
App.i18n.translateDeep(data) App.i18n.translateDeep(data)

View file

@ -5,8 +5,8 @@ class App.UiElement.boolean extends App.UiElement.ApplicationUiElement
# build options list # build options list
if _.isEmpty(attribute.options) if _.isEmpty(attribute.options)
attribute.options = [ attribute.options = [
{ name: 'yes', value: true } { name: __('yes'), value: true }
{ name: 'no', value: false } { name: __('no'), value: false }
] ]
attribute.translate = true attribute.translate = true

View file

@ -5,38 +5,38 @@ class App.UiElement.core_workflow_condition extends App.UiElement.ApplicationSel
groups = groups =
ticket: ticket:
name: 'Ticket' name: __('Ticket')
model: 'Ticket' model: 'Ticket'
model_show: ['Ticket'] model_show: ['Ticket']
group: group:
name: 'Group' name: __('Group')
model: 'Group' model: 'Group'
model_show: ['Group'] model_show: ['Group']
user: user:
name: 'User' name: __('User')
model: 'User' model: 'User'
model_show: ['User'] model_show: ['User']
customer: customer:
name: 'Customer' name: __('Customer')
model: 'User' model: 'User'
model_show: ['Ticket'] model_show: ['Ticket']
organization: organization:
name: 'Organization' name: __('Organization')
model: 'Organization' model: 'Organization'
model_show: ['User', 'Organization'] model_show: ['User', 'Organization']
'customer.organization': 'customer.organization':
name: 'Organization' name: __('Organization')
model: 'Organization' model: 'Organization'
model_show: ['Ticket'] model_show: ['Ticket']
session: session:
name: 'Session' name: __('Session')
model: 'User' model: 'User'
model_show: ['Ticket'] model_show: ['Ticket']
showCustomModules = @coreWorkflowCustomModulesActive() showCustomModules = @coreWorkflowCustomModulesActive()
if showCustomModules if showCustomModules
groups['custom'] = groups['custom'] =
name: 'Custom' name: __('Custom')
model_show: ['Ticket', 'User', 'Organization', 'Sla'] model_show: ['Ticket', 'User', 'Organization', 'Sla']
currentObject = params.object currentObject = params.object
@ -49,16 +49,16 @@ class App.UiElement.core_workflow_condition extends App.UiElement.ApplicationSel
delete groups[key] delete groups[key]
operatorsType = operatorsType =
'active$': ['is'] 'active$': [__('is')]
'boolean$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to'] 'boolean$': [__('is'), __('is not'), __('is set'), __('not set'), _('has changed'), __('changed to')]
'integer$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to'] 'integer$': [__('is'), __('is not'), __('is set'), __('not set'), _('has changed'), __('changed to')]
'^select$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to'] '^select$': [__('is'), __('is not'), __('is set'), __('not set'), _('has changed'), __('changed to')]
'^tree_select$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to'] '^tree_select$': [__('is'), __('is not'), __('is set'), __('not set'), _('has changed'), __('changed to')]
'^(input|textarea|richtext)$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to', 'regex match', 'regex mismatch'] '^(input|textarea|richtext)$': [__('is'), __('is not'), __('is set'), __('not set'), _('has changed'), __('changed to'), __('regex match'), __('regex mismatch')]
operatorsName = operatorsName =
'_id$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to'] '_id$': [__('is'), __('is not'), __('is set'), __('not set'), _('has changed'), __('changed to')]
'_ids$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to'] '_ids$': [__('is'), __('is not'), __('is set'), __('not set'), _('has changed'), __('changed to')]
# merge config # merge config
elements = {} elements = {}
@ -73,76 +73,76 @@ class App.UiElement.core_workflow_condition extends App.UiElement.ApplicationSel
elements['custom.module'] = { elements['custom.module'] = {
name: 'module', name: 'module',
display: 'Module', display: __('Module'),
tag: 'select', tag: 'select',
multiple: true, multiple: true,
options: options, options: options,
null: false, null: false,
operator: ['match one module', 'match all modules', 'match no modules'] operator: [__('match one module'), __('match all modules'), __('match no modules')]
} }
continue continue
if groupKey is 'session' if groupKey is 'session'
elements['session.role_ids'] = { elements['session.role_ids'] = {
name: 'role_ids', name: 'role_ids',
display: 'Role', display: __('Role'),
tag: 'select', tag: 'select',
relation: 'Role', relation: 'Role',
null: false, null: false,
operator: ['is', 'is not'], operator: [__('is'), __('is not')],
multiple: true multiple: true
} }
elements['session.group_ids_read'] = { elements['session.group_ids_read'] = {
name: 'group_ids_read', name: 'group_ids_read',
display: 'Group (read)', display: __('Group (read)'),
tag: 'select', tag: 'select',
relation: 'Group', relation: 'Group',
null: false, null: false,
operator: ['is', 'is not'], operator: [__('is'), __('is not')],
multiple: true multiple: true
} }
elements['session.group_ids_create'] = { elements['session.group_ids_create'] = {
name: 'group_ids_create', name: 'group_ids_create',
display: 'Group (create)', display: __('Group (create)'),
tag: 'select', tag: 'select',
relation: 'Group', relation: 'Group',
null: false, null: false,
operator: ['is', 'is not'], operator: [__('is'), __('is not')],
multiple: true multiple: true
} }
elements['session.group_ids_change'] = { elements['session.group_ids_change'] = {
name: 'group_ids_change', name: 'group_ids_change',
display: 'Group (change)', display: __('Group (change)'),
tag: 'select', tag: 'select',
relation: 'Group', relation: 'Group',
null: false, null: false,
operator: ['is', 'is not'], operator: [__('is'), __('is not')],
multiple: true multiple: true
} }
elements['session.group_ids_overview'] = { elements['session.group_ids_overview'] = {
name: 'group_ids_overview', name: 'group_ids_overview',
display: 'Group (overview)', display: __('Group (overview)'),
tag: 'select', tag: 'select',
relation: 'Group', relation: 'Group',
null: false, null: false,
operator: ['is', 'is not'], operator: [__('is'), __('is not')],
multiple: true multiple: true
} }
elements['session.group_ids_full'] = { elements['session.group_ids_full'] = {
name: 'group_ids_full', name: 'group_ids_full',
display: 'Group (full)', display: __('Group (full)'),
tag: 'select', tag: 'select',
relation: 'Group', relation: 'Group',
null: false, null: false,
operator: ['is', 'is not'], operator: [__('is'), __('is not')],
multiple: true multiple: true
} }
elements['session.permission_ids'] = { elements['session.permission_ids'] = {
name: 'permission_ids', name: 'permission_ids',
display: 'Permissions', display: __('Permissions'),
tag: 'select', tag: 'select',
relation: 'Permission', relation: 'Permission',
null: false, null: false,
operator: ['is', 'is not'], operator: [__('is'), __('is not')],
multiple: true multiple: true
} }

View file

@ -60,7 +60,7 @@ class App.UiElement.core_workflow_perform extends App.UiElement.ApplicationSelec
options = {} options = {}
for module in App.CoreWorkflowCustomModule.all() for module in App.CoreWorkflowCustomModule.all()
options[module.name] = module.name options[module.name] = module.name
elements['custom.module'] = { name: 'module', display: 'Module', tag: 'select', multiple: true, options: options, null: false, operator: ['execute'] } elements['custom.module'] = { name: 'module', display: __('Module'), tag: 'select', multiple: true, options: options, null: false, operator: ['execute'] }
continue continue
for row in App[groupMeta.model].configure_attributes for row in App[groupMeta.model].configure_attributes
@ -136,6 +136,24 @@ class App.UiElement.core_workflow_perform extends App.UiElement.ApplicationSelec
config.nulloption = false config.nulloption = false
return config return config
@mapOperatorDisplayName: (operator) ->
names =
'show': __('show')
'hide': __('hide')
'remove': __('remove')
'set_mandatory': __('set mandatory')
'set_optional': __('set optional')
'set_readonly': __('set readonly')
'unset_readonly': __('unset readonly')
'add_option': __('add option')
'remove_option': __('remove option')
'set_fixed_to': __('set fixed to')
'select': __('select')
'auto_select': __('auto select')
'fill_in': __('fill in')
'fill_in_empty': __('fill in empty')
return names[operator] || operator
@HasPreCondition: -> @HasPreCondition: ->
return false return false

View file

@ -30,13 +30,13 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
localItem.find('.js-dataScreens').html(@dataScreens(attribute, localParams, params)) localItem.find('.js-dataScreens').html(@dataScreens(attribute, localParams, params))
options = options =
datetime: 'Datetime' datetime: __('Datetime')
date: 'Date' date: __('Date')
input: 'Text' input: __('Text')
select: 'Select' select: __('Select')
tree_select: 'Tree Select' tree_select: __('Tree Select')
boolean: 'Boolean' boolean: __('Boolean')
integer: 'Integer' integer: __('Integer')
# if attribute already exists, do not allow to change it anymore # if attribute already exists, do not allow to change it anymore
if params.data_type if params.data_type
@ -173,7 +173,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
@input: (item, localParams, params) -> @input: (item, localParams, params) ->
configureAttributes = [ configureAttributes = [
{ name: 'data_option::default', display: 'Default', tag: 'input', type: 'text', null: true, default: '' }, { name: 'data_option::default', display: __('Default'), tag: 'input', type: 'text', null: true, default: '' },
] ]
inputDefault = new App.ControllerForm( inputDefault = new App.ControllerForm(
model: model:
@ -182,7 +182,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
params: params params: params
) )
configureAttributes = [ configureAttributes = [
{ name: 'data_option::type', display: 'Type', tag: 'select', null: false, default: 'text', options: {text: 'Text', tel: 'Phone', email: 'Email', url: 'Url'}, translate: true }, { name: 'data_option::type', display: __('Type'), tag: 'select', null: false, default: 'text', options: {text: __('Text'), tel: 'Phone', email: 'Email', url: 'Url'}, translate: true },
] ]
inputType = new App.ControllerForm( inputType = new App.ControllerForm(
model: model:
@ -191,7 +191,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
params: params params: params
) )
configureAttributes = [ configureAttributes = [
{ name: 'data_option::maxlength', display: 'Maxlength', tag: 'integer', null: false, default: 120 }, { name: 'data_option::maxlength', display: __('Maxlength'), tag: 'integer', null: false, default: 120 },
] ]
inputMaxlength = new App.ControllerForm( inputMaxlength = new App.ControllerForm(
model: model:
@ -201,7 +201,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
) )
configureAttributes = [ configureAttributes = [
# coffeelint: disable=no_interpolation_in_single_quotes # coffeelint: disable=no_interpolation_in_single_quotes
{ name: 'data_option::linktemplate', display: 'Link-Template', tag: 'input', type: 'text', null: true, default: '', placeholder: 'https://example.com/?q=#{object.attribute_name} - use ticket, user or organization as object' }, { name: 'data_option::linktemplate', display: __('Link-Template'), tag: 'input', type: 'text', null: true, default: '', placeholder: 'https://example.com/?q=#{object.attribute_name} - use ticket, user or organization as object' },
# coffeelint: enable=no_interpolation_in_single_quotes # coffeelint: enable=no_interpolation_in_single_quotes
] ]
inputLinkTemplate = new App.ControllerForm( inputLinkTemplate = new App.ControllerForm(
@ -226,7 +226,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
@datetime: (item, localParams, params) -> @datetime: (item, localParams, params) ->
configureAttributes = [ configureAttributes = [
{ name: 'data_option::future', display: 'Allow future', tag: 'boolean', null: false, default: true }, { name: 'data_option::future', display: __('Allow future'), tag: 'boolean', null: false, default: true },
] ]
datetimeFuture = new App.ControllerForm( datetimeFuture = new App.ControllerForm(
model: model:
@ -235,7 +235,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
params: params params: params
) )
configureAttributes = [ configureAttributes = [
{ name: 'data_option::past', display: 'Allow past', tag: 'boolean', null: false, default: true }, { name: 'data_option::past', display: __('Allow past'), tag: 'boolean', null: false, default: true },
] ]
datetimePast = new App.ControllerForm( datetimePast = new App.ControllerForm(
model: model:
@ -244,7 +244,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
params: params params: params
) )
configureAttributes = [ configureAttributes = [
{ name: 'data_option::diff', display: 'Default time Diff (minutes)', tag: 'integer', null: true }, { name: 'data_option::diff', display: __('Default time Diff (minutes)'), tag: 'integer', null: true },
] ]
datetimeDiff = new App.ControllerForm( datetimeDiff = new App.ControllerForm(
model: model:
@ -258,7 +258,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
@date: (item, localParams, params) -> @date: (item, localParams, params) ->
configureAttributes = [ configureAttributes = [
{ name: 'data_option::diff', display: 'Default time Diff (hours)', tag: 'integer', null: true }, { name: 'data_option::diff', display: __('Default time Diff (hours)'), tag: 'integer', null: true },
] ]
dateDiff = new App.ControllerForm( dateDiff = new App.ControllerForm(
model: model:
@ -270,7 +270,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
@integer: (item, localParams, params) -> @integer: (item, localParams, params) ->
configureAttributes = [ configureAttributes = [
{ name: 'data_option::default', display: 'Default', tag: 'integer', null: true, default: '', min: 1 }, { name: 'data_option::default', display: __('Default'), tag: 'integer', null: true, default: '', min: 1 },
] ]
integerDefault = new App.ControllerForm( integerDefault = new App.ControllerForm(
model: model:
@ -279,7 +279,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
params: params params: params
) )
configureAttributes = [ configureAttributes = [
{ name: 'data_option::min', display: 'Minimal', tag: 'integer', null: false, default: 0, min: -2147483647, max: 2147483647 }, { name: 'data_option::min', display: __('Minimal'), tag: 'integer', null: false, default: 0, min: -2147483647, max: 2147483647 },
] ]
integerMin = new App.ControllerForm( integerMin = new App.ControllerForm(
model: model:
@ -288,7 +288,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
params: params params: params
) )
configureAttributes = [ configureAttributes = [
{ name: 'data_option::max', display: 'Maximal', tag: 'integer', null: false, min: -2147483647, max: 2147483647, default: 999999999 }, { name: 'data_option::max', display: __('Maximal'), tag: 'integer', null: false, min: -2147483647, max: 2147483647, default: 999999999 },
] ]
integerMax = new App.ControllerForm( integerMax = new App.ControllerForm(
model: model:
@ -339,7 +339,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
) )
configureAttributes = [ configureAttributes = [
# coffeelint: disable=no_interpolation_in_single_quotes # coffeelint: disable=no_interpolation_in_single_quotes
{ name: 'data_option::linktemplate', display: 'Link-Template', tag: 'input', type: 'text', null: true, default: '', placeholder: 'https://example.com/?q=#{ticket.attribute_name}' }, { name: 'data_option::linktemplate', display: __('Link-Template'), tag: 'input', type: 'text', null: true, default: '', placeholder: 'https://example.com/?q=#{ticket.attribute_name}' },
# coffeelint: enable=no_interpolation_in_single_quotes # coffeelint: enable=no_interpolation_in_single_quotes
] ]
inputLinkTemplate = new App.ControllerForm( inputLinkTemplate = new App.ControllerForm(
@ -441,7 +441,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
@autocompletion: (item, localParams, params) -> @autocompletion: (item, localParams, params) ->
configureAttributes = [ configureAttributes = [
{ name: 'data_option::default', display: 'Default', tag: 'input', type: 'text', null: true, default: '' }, { name: 'data_option::default', display: __('Default'), tag: 'input', type: 'text', null: true, default: '' },
] ]
autocompletionDefault = new App.ControllerForm( autocompletionDefault = new App.ControllerForm(
model: model:
@ -450,7 +450,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
params: params params: params
) )
configureAttributes = [ configureAttributes = [
{ name: 'data_option::url', display: 'Url (AJAX Endpoint)', tag: 'input', type: 'url', null: false, default: '', placeholder: 'https://example.com/serials' }, { name: 'data_option::url', display: __('Url (AJAX Endpoint)'), tag: 'input', type: 'url', null: false, default: '', placeholder: 'https://example.com/serials' },
] ]
autocompletionUrl = new App.ControllerForm( autocompletionUrl = new App.ControllerForm(
model: model:
@ -459,7 +459,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
params: params params: params
) )
configureAttributes = [ configureAttributes = [
{ name: 'data_option::method', display: 'Method (AJAX Endpoint)', tag: 'input', type: 'url', null: false, default: '', placeholder: 'GET' }, { name: 'data_option::method', display: __('Method (AJAX Endpoint)'), tag: 'input', type: 'url', null: false, default: '', placeholder: __('GET') },
] ]
autocompletionMethod = new App.ControllerForm( autocompletionMethod = new App.ControllerForm(
model: model:

View file

@ -3,35 +3,35 @@ class App.UiElement.postmaster_match
@defaults: -> @defaults: ->
groups = groups =
general: general:
name: 'Basic Settings' name: __('Basic Settings')
options: [ options: [
{ {
value: 'from' value: 'from'
name: 'From' name: __('From')
}, },
{ {
value: 'to' value: 'to'
name: 'To' name: __('To')
}, },
{ {
value: 'cc' value: 'cc'
name: 'Cc' name: __('Cc')
}, },
{ {
value: 'x-any-recipient' value: 'x-any-recipient'
name: 'Any Recipient' name: __('Any Recipient')
}, },
{ {
value: 'subject' value: 'subject'
name: 'Subject' name: __('Subject')
}, },
{ {
value: 'body' value: 'body'
name: 'Body' name: __('Body')
}, },
] ]
expert: expert:
name: 'Expert Settings' name: __('Expert Settings')
options: [ options: [
{ {
value: 'x-spam' value: 'x-spam'

View file

@ -8,34 +8,34 @@ class App.UiElement.postmaster_set
options: [ options: [
{ {
value: 'priority_id' value: 'priority_id'
name: 'Priority' name: __('Priority')
relation: 'TicketPriority' relation: 'TicketPriority'
} }
{ {
value: 'state_id' value: 'state_id'
name: 'State' name: __('State')
relation: 'TicketState' relation: 'TicketState'
} }
{ {
value: 'tags' value: 'tags'
name: 'Tag' name: __('Tag')
tag: 'tag' tag: 'tag'
} }
{ {
value: 'customer_id' value: 'customer_id'
name: 'Customer' name: __('Customer')
relation: 'User' relation: 'User'
tag: 'user_autocompletion' tag: 'user_autocompletion'
disableCreateObject: true disableCreateObject: true
} }
{ {
value: 'group_id' value: 'group_id'
name: 'Group' name: __('Group')
relation: 'Group' relation: 'Group'
} }
{ {
value: 'owner_id' value: 'owner_id'
name: 'Owner' name: __('Owner')
relation: 'User' relation: 'User'
tag: 'user_autocompletion' tag: 'user_autocompletion'
disableCreateObject: true disableCreateObject: true
@ -46,17 +46,17 @@ class App.UiElement.postmaster_set
options: [ options: [
{ {
value: 'x-zammad-article-internal' value: 'x-zammad-article-internal'
name: 'Internal' name: __('Internal')
options: { true: 'yes', false: 'no'} options: { true: 'yes', false: 'no'}
} }
{ {
value: 'x-zammad-article-type_id' value: 'x-zammad-article-type_id'
name: 'Type' name: __('Type')
relation: 'TicketArticleType' relation: 'TicketArticleType'
} }
{ {
value: 'x-zammad-article-sender_id' value: 'x-zammad-article-sender_id'
name: 'Sender' name: __('Sender')
relation: 'TicketArticleSender' relation: 'TicketArticleSender'
} }
] ]
@ -65,7 +65,7 @@ class App.UiElement.postmaster_set
options: [ options: [
{ {
value: 'x-zammad-ignore' value: 'x-zammad-ignore'
name: 'Ignore Message' name: __('Ignore Message')
options: { true: 'yes', false: 'no'} options: { true: 'yes', false: 'no'}
} }
] ]

View file

@ -1,16 +1,16 @@
# coffeelint: disable=camel_case_classes # coffeelint: disable=camel_case_classes
class App.UiElement.richtext.toolButtons.embed_video extends App.UiElement.richtext.additions.RichTextToolButton class App.UiElement.richtext.toolButtons.embed_video extends App.UiElement.richtext.additions.RichTextToolButton
@icon: 'cloud' @icon: 'cloud'
@text: 'Video' @text: __('Video')
@klass: -> App.UiElement.richtext.additions.RichTextToolPopupVideo @klass: -> App.UiElement.richtext.additions.RichTextToolPopupVideo
@initializeAttributes: @initializeAttributes:
model: model:
configure_attributes: [ configure_attributes: [
{ {
name: 'link' name: 'link'
display: 'Link' display: __('Link')
tag: 'input' tag: 'input'
placeholder: 'Youtube or Vimeo address' placeholder: __('Youtube or Vimeo address')
} }
] ]

View file

@ -1,14 +1,14 @@
# coffeelint: disable=camel_case_classes # coffeelint: disable=camel_case_classes
class App.UiElement.richtext.toolButtons.insert_image extends App.UiElement.richtext.additions.RichTextToolButton class App.UiElement.richtext.toolButtons.insert_image extends App.UiElement.richtext.additions.RichTextToolButton
@icon: 'web' @icon: 'web'
@text: 'Image' @text: __('Image')
@klass: -> App.UiElement.richtext.additions.RichTextToolPopupImage @klass: -> App.UiElement.richtext.additions.RichTextToolPopupImage
@initializeAttributes: @initializeAttributes:
model: model:
configure_attributes: [ configure_attributes: [
{ {
name: 'link' name: 'link'
display: 'Image' display: __('Image')
tag: 'input' tag: 'input'
type: 'file' type: 'file'
} }

View file

@ -1,14 +1,14 @@
# coffeelint: disable=camel_case_classes # coffeelint: disable=camel_case_classes
class App.UiElement.richtext.toolButtons.link_answer extends App.UiElement.richtext.additions.RichTextToolButtonLink class App.UiElement.richtext.toolButtons.link_answer extends App.UiElement.richtext.additions.RichTextToolButtonLink
@icon: 'knowledge-base-answer' @icon: 'knowledge-base-answer'
@text: 'Link Answer' @text: __('Link Answer')
@klass: -> App.UiElement.richtext.additions.RichTextToolPopupAnswer @klass: -> App.UiElement.richtext.additions.RichTextToolPopupAnswer
@initializeAttributes: @initializeAttributes:
model: model:
configure_attributes: [ configure_attributes: [
{ {
name: 'link' name: 'link'
display: 'Answer' display: __('Answer')
relation: 'KnowledgeBaseAnswerTranslation' relation: 'KnowledgeBaseAnswerTranslation'
tag: 'autocompletion_ajax' tag: 'autocompletion_ajax'
} }

View file

@ -1,14 +1,14 @@
# coffeelint: disable=camel_case_classes # coffeelint: disable=camel_case_classes
class App.UiElement.richtext.toolButtons.link extends App.UiElement.richtext.additions.RichTextToolButtonLink class App.UiElement.richtext.toolButtons.link extends App.UiElement.richtext.additions.RichTextToolButtonLink
@icon: 'chain' @icon: 'chain'
@text: 'Weblink' @text: __('Weblink')
@klass: -> App.UiElement.richtext.additions.RichTextToolPopupLink @klass: -> App.UiElement.richtext.additions.RichTextToolPopupLink
@initializeAttributes: @initializeAttributes:
model: model:
configure_attributes: [ configure_attributes: [
{ {
name: 'link' name: 'link'
display: 'Link' display: __('Link')
tag: 'input' tag: 'input'
placeholder: 'http://' placeholder: 'http://'
} }

View file

@ -36,7 +36,7 @@ class App.UiElement.richtext.additions.RichTextToolPopupVideo extends App.UiElem
if !markup if !markup
new App.ControllerErrorModal( new App.ControllerErrorModal(
message: 'Invalid video URL' message: __('Invalid video URL')
) )
return return

View file

@ -5,15 +5,15 @@ class App.UiElement.ticket_perform_action
groups = groups =
ticket: ticket:
name: 'Ticket' name: __('Ticket')
model: 'Ticket' model: 'Ticket'
article: article:
name: 'Article' name: __('Article')
model: 'Article' model: 'Article'
if attribute.notification if attribute.notification
groups.notification = groups.notification =
name: 'Notification' name: __('Notification')
model: 'Notification' model: 'Notification'
# merge config # merge config
@ -21,11 +21,11 @@ class App.UiElement.ticket_perform_action
for groupKey, groupMeta of groups for groupKey, groupMeta of groups
if !groupMeta.model || !App[groupMeta.model] if !groupMeta.model || !App[groupMeta.model]
if groupKey is 'notification' if groupKey is 'notification'
elements["#{groupKey}.email"] = { name: 'email', display: 'Email' } elements["#{groupKey}.email"] = { name: 'email', display: __('Email') }
elements["#{groupKey}.sms"] = { name: 'sms', display: 'SMS' } elements["#{groupKey}.sms"] = { name: 'sms', display: __('SMS') }
elements["#{groupKey}.webhook"] = { name: 'webhook', display: 'Webhook' } elements["#{groupKey}.webhook"] = { name: 'webhook', display: __('Webhook') }
else if groupKey is 'article' else if groupKey is 'article'
elements["#{groupKey}.note"] = { name: 'note', display: 'Note' } elements["#{groupKey}.note"] = { name: 'note', display: __('Note') }
else else
for row in App[groupMeta.model].configure_attributes for row in App[groupMeta.model].configure_attributes
@ -49,7 +49,7 @@ class App.UiElement.ticket_perform_action
if attribute.ticket_delete if attribute.ticket_delete
elements['ticket.action'] = elements['ticket.action'] =
name: 'action' name: 'action'
display: 'Action' display: __('Action')
tag: 'select' tag: 'select'
null: false null: false
translate: true translate: true
@ -362,10 +362,10 @@ class App.UiElement.ticket_perform_action
elementRow.find('.js-setNotification').empty() elementRow.find('.js-setNotification').empty()
options = options =
'article_last_sender': 'Article Last Sender' 'article_last_sender': __('Article Last Sender')
'ticket_owner': 'Owner' 'ticket_owner': __('Owner')
'ticket_customer': 'Customer' 'ticket_customer': __('Customer')
'ticket_agents': 'All Agents' 'ticket_agents': __('All Agents')
name = "#{attribute.name}::notification.#{notificationType}" name = "#{attribute.name}::notification.#{notificationType}"
@ -399,11 +399,11 @@ class App.UiElement.ticket_perform_action
name: "#{name}::recipient" name: "#{name}::recipient"
options: [ options: [
{ {
label: 'Variables', label: __('Variables'),
group: columnSelectOptions group: columnSelectOptions
}, },
{ {
label: 'User', label: __('User'),
group: columnSelectRecipientUserOptions group: columnSelectRecipientUserOptions
}, },
] ]
@ -459,7 +459,7 @@ class App.UiElement.ticket_perform_action
name: "#{name}::include_attachments" name: "#{name}::include_attachments"
multiple: false multiple: false
null: false null: false
options: { true: 'Yes', false: 'No' } options: { true: __('Yes'), false: __('No') }
value: meta.include_attachments || 'false' value: meta.include_attachments || 'false'
translate: true translate: true
) )
@ -469,7 +469,7 @@ class App.UiElement.ticket_perform_action
notificationElement.find('.js-body div[contenteditable="true"]').ce( notificationElement.find('.js-body div[contenteditable="true"]').ce(
mode: 'richtext' mode: 'richtext'
placeholder: 'message' placeholder: __('message')
maxlength: messageLength maxlength: messageLength
) )
new App.WidgetPlaceholder( new App.WidgetPlaceholder(
@ -478,17 +478,17 @@ class App.UiElement.ticket_perform_action
{ {
prefix: 'ticket' prefix: 'ticket'
object: 'Ticket' object: 'Ticket'
display: 'Ticket' display: __('Ticket')
}, },
{ {
prefix: 'article' prefix: 'article'
object: 'TicketArticle' object: 'TicketArticle'
display: 'Article' display: __('Article')
}, },
{ {
prefix: 'user' prefix: 'user'
object: 'User' object: 'User'
display: 'Current User' display: __('Current User')
}, },
] ]
) )
@ -500,9 +500,9 @@ class App.UiElement.ticket_perform_action
name: "#{name}::sign" name: "#{name}::sign"
multiple: false multiple: false
options: { options: {
'no': 'Do not sign email' 'no': __('Do not sign email')
'discard': 'Sign email (if not possible, discard notification)' 'discard': __('Sign email (if not possible, discard notification)')
'always': 'Sign email (if not possible, send notification anyway)' 'always': __('Sign email (if not possible, send notification anyway)')
} }
value: meta.sign value: meta.sign
translate: true translate: true
@ -514,9 +514,9 @@ class App.UiElement.ticket_perform_action
name: "#{name}::encryption" name: "#{name}::encryption"
multiple: false multiple: false
options: { options: {
'no': 'Do not encrypt email' 'no': __('Do not encrypt email')
'discard': 'Encrypt email (if not possible, discard notification)' 'discard': __('Encrypt email (if not possible, discard notification)')
'always': 'Encrypt email (if not possible, send notification anyway)' 'always': __('Encrypt email (if not possible, send notification anyway)')
} }
value: meta.encryption value: meta.encryption
translate: true translate: true
@ -535,7 +535,7 @@ class App.UiElement.ticket_perform_action
name: "#{name}::internal" name: "#{name}::internal"
multiple: false multiple: false
null: false null: false
label: 'Visibility' label: __('Visibility')
options: { true: 'internal', false: 'public' } options: { true: 'internal', false: 'public' }
value: meta.internal value: meta.internal
translate: true translate: true
@ -549,7 +549,7 @@ class App.UiElement.ticket_perform_action
articleElement.find('.js-internal').html(selection) articleElement.find('.js-internal').html(selection)
articleElement.find('.js-body div[contenteditable="true"]').ce( articleElement.find('.js-body div[contenteditable="true"]').ce(
mode: 'richtext' mode: 'richtext'
placeholder: 'message' placeholder: __('message')
maxlength: 200000 maxlength: 200000
) )
new App.WidgetPlaceholder( new App.WidgetPlaceholder(
@ -558,17 +558,17 @@ class App.UiElement.ticket_perform_action
{ {
prefix: 'ticket' prefix: 'ticket'
object: 'Ticket' object: 'Ticket'
display: 'Ticket' display: __('Ticket')
}, },
{ {
prefix: 'article' prefix: 'article'
object: 'TicketArticle' object: 'TicketArticle'
display: 'Article' display: __('Article')
}, },
{ {
prefix: 'user' prefix: 'user'
object: 'User' object: 'User'
display: 'Current User' display: __('Current User')
}, },
] ]
) )

View file

@ -2,11 +2,11 @@
class App.UiElement.time_range class App.UiElement.time_range
@render: (attribute) -> @render: (attribute) ->
ranges = ranges =
minute: 'Minute(s)' minute: __('Minute(s)')
hour: 'Hour(s)' hour: __('Hour(s)')
day: 'Day(s)' day: __('Day(s)')
month: 'Month(s)' month: __('Month(s)')
year: 'Year(s)' year: __('Year(s)')
for key, value of ranges for key, value of ranges
ranges[key] = App.i18n.translateInline(value) ranges[key] = App.i18n.translateInline(value)

View file

@ -2,13 +2,13 @@
class App.UiElement.timer class App.UiElement.timer
@render: (attribute) -> @render: (attribute) ->
days = days =
Mon: 'Monday' Mon: __('Monday')
Tue: 'Tuesday' Tue: __('Tuesday')
Wed: 'Wednesday' Wed: __('Wednesday')
Thu: 'Thursday' Thu: __('Thursday')
Fri: 'Friday' Fri: __('Friday')
Sat: 'Saturday' Sat: __('Saturday')
Sun: 'Sunday' Sun: __('Sunday')
hours = hours =
0: '12 am' 0: '12 am'
1: '1 am' 1: '1 am'

Some files were not shown because too many files have changed in this diff Show more