Skip to content
Permalink
Browse files

Fixes issue #2103 - clickable URL (template) for ObjectManager Attrib…

…ute.
  • Loading branch information...
zammad-sync authored and thorsteneckel committed Aug 12, 2019
1 parent 29c9d35 commit 848175a290e69a3903a4ebbe88bcb52d8413e382
@@ -360,11 +360,16 @@ class App.ControllerForm extends App.Controller

return item
else
placeholderObjects = {}
if @model.className && !_.isEmpty(attribute.linktemplate) && !_.isEmpty(@params[attribute.name])
placeholderObjects = { attribute: attribute, user: App.Session.get(), config: App.Config.all() }
placeholderObjects[@model.className.toLowerCase()] = @params
fullItem = $(
App.view('generic/attribute')(
attribute: attribute,
item: '',
bookmarkable: @bookmarkable
placeholderObjects: placeholderObjects
)
)
fullItem.find('.controls').prepend(item)
@@ -194,9 +194,21 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
noFieldset: true
params: params
)
configureAttributes = [
# 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' },
# coffeelint: enable=no_interpolation_in_single_quotes
]
inputLinkTemplate = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
params: params
)
item.find('.js-inputDefault').html(inputDefault.form)
item.find('.js-inputType').html(inputType.form)
item.find('.js-inputMaxlength').html(inputMaxlength.form)
item.find('.js-inputLinkTemplate').html(inputLinkTemplate.form)

@datetime: (item, localParams, params) ->
configureAttributes = [
@@ -311,6 +323,18 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
return
lastSelected = value
)
configureAttributes = [
# 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}' },
# coffeelint: enable=no_interpolation_in_single_quotes
]
inputLinkTemplate = new App.ControllerForm(
model:
configure_attributes: configureAttributes
noFieldset: true
params: params
)
item.find('.js-inputLinkTemplate').html(inputLinkTemplate.form)

@buildRow: (element, child, level = 0, parentElement) ->
newRow = element.find('.js-template').clone().removeClass('js-template')
@@ -46,10 +46,10 @@ class App extends Spine.Controller
if object[attributeNameWithoutRef]
valueRef = object[attributeNameWithoutRef]

@viewPrintItem(value, attributeConfig, valueRef, table)
@viewPrintItem(value, attributeConfig, valueRef, table, object)

# define print name helper
@viewPrintItem: (item, attributeConfig = {}, valueRef, table) ->
@viewPrintItem: (item, attributeConfig = {}, valueRef, table, object) ->
return '-' if item is undefined
return '-' if item is ''
return item if item is null
@@ -107,18 +107,23 @@ class App extends Spine.Controller
# translate content
if attributeConfig.translate || (isObject && item.translate && item.translate())
isHtmlEscape = true
resultLocal = App.i18n.translateContent(resultLocal)
resultLocal = App.i18n.translateContent(resultLocal)

# transform date
if attributeConfig.tag is 'date'
isHtmlEscape = true
resultLocal = App.i18n.translateDate(resultLocal)

linktemplate = @_placeholderReplacement(object, attributeConfig, resultLocal)
if linktemplate && isHtmlEscape is false
resultLocal = linktemplate
isHtmlEscape = true

# transform input tel|url to make it clickable
if attributeConfig.tag is 'input'
if attributeConfig.tag is 'input' && !linktemplate
if attributeConfig.type is 'tel'
resultLocal = "<a href=\"#{App.Utils.phoneify(resultLocal)}\">#{App.Utils.htmlEscape(resultLocal)}</a>"
else if attributeConfig.type is 'url'
else if attributeConfig.type is 'url' && !linktemplate
resultLocal = App.Utils.linkify(resultLocal)
else
resultLocal = App.Utils.htmlEscape(resultLocal)
@@ -146,6 +151,17 @@ class App extends Spine.Controller

result

@_placeholderReplacement: (object, attributeConfig, resultLocal) ->
return if !object
return if !attributeConfig
return if _.isEmpty(attributeConfig.linktemplate)
return if !object.constructor
return if !object.constructor.className
return if _.isEmpty(object[attributeConfig.name])
placeholderObjects = { attribute: attributeConfig, user: App.Session.get(), config: App.Config.all() }
placeholderObjects[object.constructor.className.toLowerCase()] = object
"<a href=\"#{App.Utils.replaceTags(attributeConfig.linktemplate, placeholderObjects, true)}\" target=\"blank\">#{App.Utils.htmlEscape(resultLocal)}</a>"

@view: (name) ->
template = (params = {}) ->
JST["app/views/#{name}"](_.extend(params, App.ViewHelpers))
@@ -704,7 +704,7 @@ class App.Utils
res.join('')

# textReplaced = App.Utils.replaceTags( template, { user: { firstname: 'Bob', lastname: 'Smith' } } )
@replaceTags: (template, objects) ->
@replaceTags: (template, objects, encodeLink = false) ->
template = template.replace( /#\{\s{0,2}(.+?)\s{0,2}\}/g, (index, key) ->
key = key.replace(/<.+?>/g, '')
levels = key.split(/\./)
@@ -744,6 +744,7 @@ class App.Utils
else
value = ''
value = '-' if value is ''
value = encodeURIComponent(value) if encodeLink
value
)

@@ -228,3 +228,6 @@ App.ViewHelpers =
className: params.className
iconset: params.iconset
)

replacePlaceholder: (template, items, encodeLink = false) ->
App.Utils.replaceTags(template, items, encodeLink)
@@ -20,9 +20,12 @@
</h2>
<p class="help-text"><% if @attribute.help: %><%- @T(@attribute.help) %><% end %><%- @attribute.helpLink %></p>
<% end %>
<div class="controls">
<% if @attribute.remove: %><span><a href="#" class="glyphicon glyphicon-minus"></a></span><% end %>
<% if @attribute.add: %><span><a href="#" class="glyphicon glyphicon-plus"></a></span><% end %>
<div class="controls <% if !_.isEmpty(@attribute.linktemplate) && !_.isEmpty(@placeholderObjects): %>controls--button<% end %>">
<% if !_.isEmpty(@attribute.linktemplate) && !_.isEmpty(@placeholderObjects): %>
<a href="<%- @replacePlaceholder(@attribute.linktemplate, @placeholderObjects, true) %>" class="controls-button" target="_blank" rel="nofollow">
<span class="controls-button-inner"><%- @Icon('external') %></span>
</a>
<% end %>
<span class="help-inline"></span>
<% if @attribute.style != 'block': %>
<span class="help-block"><% if @attribute.help: %><%- @T(@attribute.help) %><% end %><%- @attribute.helpLink %></span>
@@ -2,4 +2,5 @@
<div class="js-inputDefault"></div>
<div class="js-inputType"></div>
<div class="js-inputMaxlength"></div>
<div class="js-inputLinkTemplate"></div>
</div>
@@ -50,4 +50,5 @@
<%- @Icon('trash') %> <%- @T('Remove') %>
</div>
</table>
<div class="js-inputLinkTemplate"></div>
</div>
@@ -2102,10 +2102,20 @@ input.has-error {

.controls--button {
display: flex;
flex-wrap: wrap;

.controls {
flex: 1;
}

.help-inline,
.help-block {
flex-basis: 100%;
}

input,
.form-control {
flex: 1;
flex: 1 1 0%;
@include bidi-style(border-right-width, 0, border-left-width, 1px);
@include bidi-style(border-top-right-radius, 0, border-top-left-radius, 3px);
@include bidi-style(border-bottom-right-radius, 0, border-bottom-left-radius, 3px);
@@ -2154,6 +2164,14 @@ input.has-error {
position: relative;
border: 1px solid hsl(0, 0%, 90%);
@include bidi-style(border-radius, 0 3px 3px 0, border-radius, 3px 0 0 3px);

.icon {
fill: hsl(0,0%,61%);
}

&:hover .icon {
fill: hsl(0,0%,33%);
}
}

.searchfield {
@@ -133,6 +133,7 @@ def self.list_full
maxlength: 200,
null: true,
note: 'some additional comment', # optional
link_template: '', # optional
},
# select
@@ -150,6 +151,7 @@ def self.list_full
multiple: false, # currently only "false" supported
translate: true, # optional
note: 'some additional comment', # optional
link_template: '', # optional
},
# tree_select
@@ -1107,6 +1107,7 @@ test("object manager form 1", function() {
var test_params = {
data_option: {
default: "",
linktemplate: "",
maxlength: 120,
type: "text"
},
@@ -1218,6 +1219,7 @@ test("object manager form 2", function() {
var test_params = {
data_option: {
default: "",
linktemplate: "",
maxlength: 120,
type: "text"
},
@@ -1271,6 +1273,7 @@ test("object manager form 3", function() {
var test_params = {
data_option: {
default: "",
linktemplate: "",
maxlength: 120,
type: "text"
},
@@ -1308,6 +1311,7 @@ test("object manager form 3", function() {
test_params = {
data_option: {
default: "",
linktemplate: "",
maxlength: 120,
type: "text"
},
@@ -1596,3 +1600,28 @@ test("form deep nesting", function() {
params = App.ControllerForm.params(el)
deepEqual(params, defaults, 'nested params')
});

test("form with external links", function() {
$('#forms').append('<hr><h1>form with external links</h1><div><form id="form20"></form></div>')
var el = $('#form20')
var defaults = {
a: '133',
b: 'abc d',
}
new App.ControllerForm({
el: el,
model: {
configure_attributes: [
{ name: 'a', display: 'Input1', tag: 'input', type: 'text', limit: 100, null: true, linktemplate: "https://example.com/?q=#{ticket.a}" },
{ name: 'b', display: 'Select1', tag: 'select', type: 'text', options: { a: 1, b: 2 }, limit: 100, null: true, linktemplate: "https://example.com/?q=#{ticket.b}" },
],
className: 'Ticket',
},
params: defaults,
});

params = App.ControllerForm.params(el)
deepEqual(params, defaults)
equal('https://example.com/?q=133', el.find('input[name="a"]').parents('.controls').find('a[href]').attr('href'))
equal('https://example.com/?q=abc%20d', el.find('select[name="b"]').parents('.controls').find('a[href]').attr('href'))
});
@@ -1425,7 +1425,7 @@ test("check replace tags", function() {

user = new App.User({
firstname: 'Bob',
lastname: 'Smith',
lastname: 'Smith Good',
created_at: '2018-10-31T10:00:00Z',
})
message = "<div>#{user.firstname} #{user.created_at}</div>"
@@ -1451,6 +1451,14 @@ test("check replace tags", function() {
}
verify = App.Utils.replaceTags(message, data)
equal(verify, result)

message = "<a href=\"https://example.co/q=#{user.lastname}\">some text</a>"
result = '<a href=\"https://example.co/q=Smith%20Good\">some text</a>'
data = {
user: user
}
verify = App.Utils.replaceTags(message, data, true)
equal(verify, result)
});

// check attibute validation

1 comment on commit 848175a

@thorsteneckel

This comment has been minimized.

Copy link
Collaborator

commented on 848175a Aug 12, 2019

IDK why but the reference in the Issue is missing - therefore manually: #2103

Please sign in to comment.
You can’t perform that action at this time.