Browse Source

Fixes #5089 - Improve localization of triggers and schedulers.

Co-authored-by: Tobias Schäfer <ts@zammad.com>
Florian Liebe 11 months ago
parent
commit
c7afe512ad

+ 39 - 0
app/assets/javascripts/app/controllers/_ui_element/language.coffee

@@ -0,0 +1,39 @@
+# coffeelint: disable=camel_case_classes
+class App.UiElement.language extends App.UiElement.ApplicationUiElement
+  @render: (attributeConfig, params) ->
+    attribute = $.extend(true, {}, attributeConfig)
+
+    # build options list
+    if _.isEmpty(attribute.options)
+      attribute.options = App.Locale.all().map (locale) ->
+        { name: locale.name, value: locale.locale }
+
+    if attribute.show_system_default_option
+      locale_default_name = App.Locale.findByAttribute('locale', App.Config.get('locale_default')).name
+      attribute.options.unshift({ name: __('System default') + " (#{locale_default_name})", value: 'system' })
+
+    # set default value
+    attribute.default = if attribute.show_system_default_option then 'system' else App.Config.get('locale_default')
+
+    if _.isEmpty(attribute.value)
+      attribute.value = if attribute.show_system_default_option then 'system' else attribute.default
+
+    # set translate
+    attribute.translate = true
+
+    # build options list based on config
+    @getConfigOptionList(attribute, params)
+
+    # sort attribute.options
+    @sortOptions(attribute, params)
+
+    # find selected/checked item of list
+    @selectedOptions(attribute, params)
+
+    # disable item of list
+    @disabledOptions(attribute, params)
+
+    # filter attributes
+    @filterOption(attribute, params)
+
+    new App.SearchableSelect(attribute: attribute).element()

+ 9 - 2
app/assets/javascripts/app/controllers/_ui_element/timezone.coffee

@@ -8,13 +8,20 @@ class App.UiElement.timezone extends App.UiElement.ApplicationUiElement
 
     # build list based on config
     for timezone_value, timezone_diff of timezones
-      if timezone_diff > 0
-        timezone_diff = '+' + timezone_diff
+      if !timezone_diff.toString().match(/(\+|\-)/)
+        timezone_diff = "+#{timezone_diff}"
       item =
         name:  "#{timezone_value} (GMT#{timezone_diff})"
         value: timezone_value
       attribute.options.push item
 
+    if attribute.show_system_default_option
+      timezone_default_name = _.find(attribute.options, (option) -> option.value == App.Config.get('timezone_default_sanitized')).name
+      attribute.options.unshift({ name: __('System default') + " (#{timezone_default_name})", value: 'system' })
+
+    # set default value
+    attribute.default = if attribute.show_system_default_option then 'system' else App.Config.get('timezone_default_sanitized')
+
     # add null selection if needed
     @addNullOption(attribute, params)
 

+ 11 - 0
app/assets/javascripts/app/controllers/job.coffee

@@ -4,6 +4,8 @@ class Job extends App.ControllerSubContent
   constructor: ->
     super
 
+    @fetchTimezones()
+
     @genericController = new App.ControllerGenericIndex(
       el: @el
       id: @id
@@ -35,4 +37,13 @@ class Job extends App.ControllerSubContent
 
     @genericController.paginate( @page || 1 )
 
+  fetchTimezones: =>
+    @ajax(
+      id:    'calendar_timezones'
+      type:  'GET'
+      url:   "#{@apiPath}/calendars/timezones"
+      success: (data) ->
+        App.Config.set('timezones', data.timezones)
+    )
+
 App.Config.set('Job', { prio: 3400, name: __('Scheduler'), parent: '#manage', target: '#manage/job', controller: Job, permission: ['admin.scheduler'] }, 'NavBarAdmin')

+ 11 - 0
app/assets/javascripts/app/controllers/trigger.coffee

@@ -5,6 +5,8 @@ class Trigger extends App.ControllerSubContent
   constructor: ->
     super
 
+    @fetchTimezones()
+
     @genericController = new Index(
       el: @el
       id: @id
@@ -33,6 +35,15 @@ class Trigger extends App.ControllerSubContent
 
     @genericController.paginate( @page || 1 )
 
+  fetchTimezones: =>
+    @ajax(
+      id:    'calendar_timezones'
+      type:  'GET'
+      url:   "#{@apiPath}/calendars/timezones"
+      success: (data) ->
+        App.Config.set('timezones', data.timezones)
+    )
+
 class Index extends App.ControllerGenericIndex
   newControllerClass: -> New
   editControllerClass: -> Edit

+ 3 - 1
app/assets/javascripts/app/models/job.coffee

@@ -1,5 +1,5 @@
 class App.Job extends App.Model
-  @configure 'Job', 'name', 'object', 'timeplan', 'condition', 'perform', 'disable_notification', 'note', 'active'
+  @configure 'Job', 'name', 'object', 'timeplan', 'condition', 'perform', 'disable_notification', 'note', 'active', 'localization', 'timezone'
   @extend Spine.Model.Ajax
   @url: @apiPath + '/jobs'
   @configure_attributes = [
@@ -9,6 +9,8 @@ class App.Job extends App.Model
     { name: 'condition',            display: __('Conditions for affected objects'), tag: 'object_selector', null: true, executionTime: true, noCurrentUser: true },
     { name: 'perform',              display: __('Execute changes on objects'),      tag: 'object_perform_action', null: true, notification: true, ticket_delete: true, data_privacy_deletion_task: true },
     { name: 'disable_notification', display: __('Disable Notifications'),           tag: 'boolean', default: true },
+    { name: 'localization',         display: __('Locale'),                          tag: 'language', null: false, class: 'input', show_system_default_option: true },
+    { name: 'timezone',             display: __('Timezone'),                        tag: 'timezone', null: false, class: 'input', show_system_default_option: true },
     { name: 'note',                 display: __('Note'),                            tag: 'textarea', note: __('Notes are visible to agents only, never to customers.'), limit: 250, null: true },
     { name: 'active',               display: __('Active'),                          tag: 'active', default: true },
     { name: 'matching',             display: __('Will process'),                    readonly: 1 },

+ 3 - 1
app/assets/javascripts/app/models/trigger.coffee

@@ -1,5 +1,5 @@
 class App.Trigger extends App.Model
-  @configure 'Trigger', 'name', 'activator', 'execution_condition_mode', 'condition', 'perform', 'active', 'note'
+  @configure 'Trigger', 'name', 'activator', 'execution_condition_mode', 'condition', 'perform', 'active', 'note', 'localization', 'timezone'
   @extend Spine.Model.Ajax
   @url: @apiPath + '/triggers'
   @configure_attributes = [
@@ -8,6 +8,8 @@ class App.Trigger extends App.Model
     { name: 'execution_condition_mode', display: __('Action execution'), tag: 'radio',     type: 'text', limit: 50,   null: true, options: [ { value: 'selective', name: __('Selective (default)'), note: __('When at least one field from conditions was updated or article was added and conditions match') }, { value: 'always', name: __('Always'), note: __('When conditions match') } ] },
     { name: 'condition',                display: __('Conditions for affected objects'), tag: 'ticket_selector',       null: false, preview: false, action: true, hasChanged: true, executionTime: true, hasReached: true, hasRegexOperators: true },
     { name: 'perform',                  display: __('Execute changes on objects'),      tag: 'ticket_perform_action', null: true, notification: true, trigger: true },
+    { name: 'localization',             display: __('Locale'),           tag: 'language',  null: false, class: 'input', show_system_default_option: true },
+    { name: 'timezone',                 display: __('Timezone'),         tag: 'timezone',  null: false, class: 'input', show_system_default_option: true },
     { name: 'note',                     display: __('Note'),             tag: 'textarea',                limit: 250,  null: true },
     { name: 'active',                   display: __('Active'),           tag: 'active',    default: true },
     { name: 'updated_at',               display: __('Updated'),          tag: 'datetime',  readonly: 1 },

+ 22 - 0
app/models/concerns/perform_changes/action.rb

@@ -4,6 +4,7 @@ class PerformChanges::Action
   include Mixin::RequiredSubPaths
 
   attr_accessor :record, :execution_data, :performable, :origin, :context_data, :user_id
+  attr_reader :locale, :timezone
 
   def self.action_lookup
     @action_lookup ||= descendants.index_by { |action| action.name.demodulize.underscore.to_sym }
@@ -20,6 +21,9 @@ class PerformChanges::Action
     @origin = perform_changes_data[:origin]
     @context_data = perform_changes_data[:context_data]
     @user_id = perform_changes_data[:user_id]
+
+    @locale   = fetch_locale
+    @timezone = fetch_timezone
   end
 
   def execute(prepared_actions)
@@ -37,4 +41,22 @@ class PerformChanges::Action
       record.class.name.downcase.to_sym => record,
     }
   end
+
+  def fetch_locale
+    locale = @performable.try(:localization)
+
+    # Returning nil will use the system default locale in the NotificationFactory classes.
+    return nil if locale.blank? || locale == 'system'
+
+    locale
+  end
+
+  def fetch_timezone
+    timezone = @performable.try(:timezone)
+
+    # Returning nil will use the system default timezone in the NotificationFactory classes.
+    return nil if timezone.blank? || timezone == 'system'
+
+    timezone
+  end
 end

+ 2 - 0
app/models/concerns/perform_changes/action/attribute_updates.rb

@@ -121,6 +121,8 @@ class PerformChanges::Action::AttributeUpdates < PerformChanges::Action
       templateInline: value['value'],
       objects:        notification_factory_template_objects,
       quote:          true,
+      locale:         locale,
+      timezone:       timezone,
     )
 
     true

+ 4 - 0
app/models/ticket/perform_changes/action/article_note.rb

@@ -17,12 +17,16 @@ class Ticket::PerformChanges::Action::ArticleNote < Ticket::PerformChanges::Acti
       templateInline: note[:subject],
       objects:        notification_factory_template_objects,
       quote:          true,
+      locale:         locale,
+      timezone:       timezone,
     )
 
     rendered_body = NotificationFactory::Mailer.template(
       templateInline: note[:body],
       objects:        notification_factory_template_objects,
       quote:          true,
+      locale:         locale,
+      timezone:       timezone,
     )
 
     article = Ticket::Article.new(

+ 4 - 0
app/models/ticket/perform_changes/action/notification_email.rb

@@ -105,6 +105,8 @@ class Ticket::PerformChanges::Action::NotificationEmail < Ticket::PerformChanges
       templateInline: execution_data['subject'],
       objects:        notification_factory_template_objects,
       quote:          false,
+      locale:         locale,
+      timezone:       timezone,
     )
   end
 
@@ -113,6 +115,8 @@ class Ticket::PerformChanges::Action::NotificationEmail < Ticket::PerformChanges
       templateInline: execution_data['body'],
       objects:        notification_factory_template_objects,
       quote:          true,
+      locale:         locale,
+      timezone:       timezone,
     )
 
     HtmlSanitizer.replace_inline_images(body, id)

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