diff --git a/app/assets/javascripts/app/controllers/_ui_element/richtext.coffee b/app/assets/javascripts/app/controllers/_ui_element/richtext.coffee index 9278c30d4..594da948e 100644 --- a/app/assets/javascripts/app/controllers/_ui_element/richtext.coffee +++ b/app/assets/javascripts/app/controllers/_ui_element/richtext.coffee @@ -7,6 +7,13 @@ class App.UiElement.richtext mode: attribute.type maxlength: attribute.maxlength ) + + if attribute.plugins + for plugin in attribute.plugins + params = plugin.params || {} + params.el = item.find('[contenteditable]').parent() + new App[plugin.controller](params) + if attribute.upload item.append( $( App.view('generic/attachment')( attribute: attribute ) ) ) diff --git a/app/assets/javascripts/app/controllers/_ui_element/ticket_perform_action.coffee b/app/assets/javascripts/app/controllers/_ui_element/ticket_perform_action.coffee index 7aaef98a4..bd7b04dc5 100644 --- a/app/assets/javascripts/app/controllers/_ui_element/ticket_perform_action.coffee +++ b/app/assets/javascripts/app/controllers/_ui_element/ticket_perform_action.coffee @@ -328,6 +328,22 @@ class App.UiElement.ticket_perform_action placeholder: 'message' maxlength: 2000 ) + new App.WidgetPlaceholder( + el: notificationElement.find('.js-body div[contenteditable="true"]').parent() + objects: [ + { + prefix: 'ticket' + object: 'Ticket' + display: 'Ticket' + }, + { + prefix: 'user' + object: 'User' + display: 'Current User' + }, + ] + ) + elementRow.find('.js-setNotification').html(notificationElement) @humanText: (condition) -> diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee index 26d8d153e..2bc5cb8e8 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/article_new.coffee @@ -405,7 +405,7 @@ class App.TicketZoomArticleNew extends App.Controller @$('.js-selectableTypes').addClass('hide').filter("[data-type='#{ type }']").removeClass('hide') # detect current signature (use current group_id, if not set, use ticket.group_id) - ticketCurrent = App.Ticket.find(@ticket_id) + ticketCurrent = App.Ticket.fullLocal(@ticket_id) group_id = ticketCurrent.group_id task = App.TaskManager.get(@task_key) if task && task.state && task.state.ticket && task.state.ticket.group_id diff --git a/app/assets/javascripts/app/controllers/widget/placeholder.coffee b/app/assets/javascripts/app/controllers/widget/placeholder.coffee new file mode 100644 index 000000000..530d499ff --- /dev/null +++ b/app/assets/javascripts/app/controllers/widget/placeholder.coffee @@ -0,0 +1,84 @@ +class App.WidgetPlaceholder extends App.Controller + constructor: -> + super + + if !@data + @data = {} + + # remember instances + @bindElements = [] + if @selector + @bindElements = @$( @selector ).textmodule() + else + if @el.attr('contenteditable') + @bindElements = @el.textmodule() + else + @bindElements = @$('[contenteditable]').textmodule() + @update() + + update: => + all = [] + ignoreAttributes = { + password: true + active: true + } + ignoreSubAttributes = { + password: true + active: true + created_at: true + updated_at: true + } + # add config + for setting in App.Setting.all() + if setting.preferences && setting.preferences.placeholder + all.push { + id: setting.name + keywords: setting.name + name: "#{App.i18n.translateInline('Config')} > #{setting.name}" + content: content + } + for item in @objects + list = {} + if App[item.object] && App[item.object].configure_attributes + for attribute in App[item.object].configure_attributes + if !ignoreAttributes[attribute.name] && attribute.name.substr(attribute.name.length-4,attribute.name.length) isnt '_ids' + list[attribute.name] = attribute + for name in _.keys(list).sort() + attribute = list[name] + name = "\#{#{item.prefix}.#{attribute.name}}" + content = "\#{#{item.prefix}.#{attribute.name}}" + if attribute.relation + subAttributes = { + name: 'Name' + } + if App[attribute.relation] && App[attribute.relation].configure_attributes + subList = {} + subAttributes = {} + for subAttribute in App[attribute.relation].configure_attributes + if !ignoreSubAttributes[subAttribute.name] && subAttribute.name.substr(subAttribute.name.length-3,subAttribute.name.length) isnt '_id' && subAttribute.name.substr(subAttribute.name.length-4,subAttribute.name.length) isnt '_ids' + subList[subAttribute.name] = subAttribute + for subName in _.keys(subList).sort() + subAttributes[subName] = subList[subName].display + relation = "#{item.prefix}.#{attribute.name.substr(0,attribute.name.length-3)}" + for key, display of subAttributes + name = "\#{#{relation}.#{key}}" + content = "\#{#{relation}.#{key}}" + all.push { + id: name + keywords: name + name: "#{App.i18n.translateInline(item.display)} > #{App.i18n.translateInline(attribute.display)} > #{App.i18n.translateInline(display)}" + content: content + } + else + all.push { + id: name + keywords: name + name: "#{App.i18n.translateInline(item.display)} > #{App.i18n.translateInline(attribute.display)}" + content: content + } + + # set new data + if @bindElements[0] + for element in @bindElements + if $(element).data().plugin_textmodule + $(element).data().plugin_textmodule.collection = all diff --git a/app/assets/javascripts/app/lib/app_post/utils.coffee b/app/assets/javascripts/app/lib/app_post/utils.coffee index 806c24e5b..3d28d895d 100644 --- a/app/assets/javascripts/app/lib/app_post/utils.coffee +++ b/app/assets/javascripts/app/lib/app_post/utils.coffee @@ -457,15 +457,20 @@ class App.Utils levels = key.split(/\./) dataRef = objects for level in levels - if dataRef[level] + if level of dataRef dataRef = dataRef[level] + else + dataRef = '' + break if typeof dataRef is 'function' value = dataRef() - else if typeof dataRef is 'string' - value = dataRef + else if dataRef.toString + value = dataRef.toString() else value = '' #console.log( "tag replacement #{key}, #{value} env: ", objects) + if value is '' + value = '-' value ) diff --git a/app/assets/javascripts/app/models/text_module.coffee b/app/assets/javascripts/app/models/text_module.coffee index c5b7f4794..13d02d40f 100644 --- a/app/assets/javascripts/app/models/text_module.coffee +++ b/app/assets/javascripts/app/models/text_module.coffee @@ -3,11 +3,28 @@ class App.TextModule extends App.Model @extend Spine.Model.Ajax @url: @apiPath + '/text_modules' @configure_attributes = [ - { name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false }, - { name: 'keywords', display: 'Keywords', tag: 'input', type: 'text', limit: 100, null: true }, - { name: 'content', display: 'Content', tag: 'richtext', limit: 2000, null: false }, - { name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 }, - { name: 'active', display: 'Active', tag: 'active', default: true }, + { name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false }, + { name: 'keywords', display: 'Keywords', tag: 'input', type: 'text', limit: 100, null: true }, + { name: 'content', display: 'Content', tag: 'richtext', limit: 2000, null: false, plugins: [ + { + controller: 'WidgetPlaceholder' + params: + objects: [ + { + prefix: 'ticket' + object: 'Ticket' + display: 'Ticket' + }, + { + prefix: 'user' + object: 'User' + display: 'Current User' + }, + ] + } + ], note: 'To select placeholders from a list, just enter "::".'}, + { name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 }, + { name: 'active', display: 'Active', tag: 'active', default: true }, ] @configure_delete = true @configure_overview = [ @@ -22,9 +39,9 @@ Create Text Modules to **spend less time writing responses**. TextModules can in Examples of snippets are: -* Hallo Frau #{ticket.customer.lastname}, -* Hallo Herr #{ticket.customer.lastname}, -* Hallo #{ticket.customer.firstname}, +* Hello Mrs. #{ticket.customer.lastname}, +* Hello Mr. #{ticket.customer.lastname}, +* Hello #{ticket.customer.firstname}, * My Name is #{user.firstname}, Of course you can also use multi line snippets. @@ -34,6 +51,9 @@ Available objects are: * ticket.customer (e. g. ticket.customer.firstname, ticket.customer.lastname) * ticket.owner (e. g. ticket.owner.firstname, ticket.owner.lastname) * ticket.organization (e. g. ticket.organization.name) +* user (e. g. user.firstname, user.email) + +To select placeholders from a list, just enter "::". ''' # coffeelint: enable=no_interpolation_in_single_quotes diff --git a/public/assets/tests/html_utils.js b/public/assets/tests/html_utils.js index bf40af834..ddff200c7 100644 --- a/public/assets/tests/html_utils.js +++ b/public/assets/tests/html_utils.js @@ -857,7 +857,39 @@ test("check replace tags", function() { equal(verify, result) message = "
#{user.firstname} #{user.lastname}
" - result = '
Bob
' + result = '
Bob -
' + data = { + user: { + firstname: 'Bob', + }, + } + verify = App.Utils.replaceTags(message, data) + equal(verify, result) + + message = "
#{user.firstname} #{user.lastname}
" + result = '
Bob 0
' + data = { + user: { + firstname: 'Bob', + lastname: 0, + }, + } + verify = App.Utils.replaceTags(message, data) + equal(verify, result) + + message = "
#{user.firstname} #{user.lastname}
" + result = '
Bob -
' + data = { + user: { + firstname: 'Bob', + lastname: '', + }, + } + verify = App.Utils.replaceTags(message, data) + equal(verify, result) + + message = "
#{user.firstname} #{user.not.existing.test}
" + result = '
Bob -
' data = { user: { firstname: 'Bob',