Fixes #2102 - Read-only custom objects.
This commit is contained in:
parent
85cee64c73
commit
d5d5b3b7b8
11 changed files with 261 additions and 25 deletions
|
@ -371,6 +371,9 @@ class App.ControllerForm extends App.Controller
|
||||||
@fieldIsRemoved: (field) ->
|
@fieldIsRemoved: (field) ->
|
||||||
return field.closest('.form-group').hasClass('is-removed')
|
return field.closest('.form-group').hasClass('is-removed')
|
||||||
|
|
||||||
|
@fieldIsReadonly: (field) ->
|
||||||
|
return field.closest('.form-group').hasClass('is-readonly')
|
||||||
|
|
||||||
attributeIsMandatory: (name) ->
|
attributeIsMandatory: (name) ->
|
||||||
field_by_name = @constructor.findFieldByName(name, @form)
|
field_by_name = @constructor.findFieldByName(name, @form)
|
||||||
if field_by_name.length > 0
|
if field_by_name.length > 0
|
||||||
|
@ -438,6 +441,34 @@ class App.ControllerForm extends App.Controller
|
||||||
field_by_data.parents('.form-group').find('label span').html('')
|
field_by_data.parents('.form-group').find('label span').html('')
|
||||||
field_by_data.closest('.form-group').removeClass('is-required')
|
field_by_data.closest('.form-group').removeClass('is-required')
|
||||||
|
|
||||||
|
readonly: (name, el = @form) ->
|
||||||
|
if !_.isArray(name)
|
||||||
|
name = [name]
|
||||||
|
for key in name
|
||||||
|
field_by_name = @constructor.findFieldByName(key, el)
|
||||||
|
field_by_data = @constructor.findFieldByData(key, el)
|
||||||
|
|
||||||
|
if !@constructor.fieldIsReadonly(field_by_name)
|
||||||
|
field_by_name.closest('.form-group').find('input, select').attr('readonly', true)
|
||||||
|
field_by_name.closest('.form-group').addClass('is-readonly')
|
||||||
|
if !@constructor.fieldIsReadonly(field_by_data)
|
||||||
|
field_by_data.closest('.form-group').find('input, select').attr('readonly', true)
|
||||||
|
field_by_data.closest('.form-group').addClass('is-readonly')
|
||||||
|
|
||||||
|
changeable: (name, el = @form) ->
|
||||||
|
if !_.isArray(name)
|
||||||
|
name = [name]
|
||||||
|
for key in name
|
||||||
|
field_by_name = @constructor.findFieldByName(key, el)
|
||||||
|
field_by_data = @constructor.findFieldByData(key, el)
|
||||||
|
|
||||||
|
if @constructor.fieldIsReadonly(field_by_name)
|
||||||
|
field_by_name.closest('.form-group').find('input, select').attr('readonly', false)
|
||||||
|
field_by_name.closest('.form-group').removeClass('is-readonly')
|
||||||
|
if @constructor.fieldIsReadonly(field_by_data)
|
||||||
|
field_by_data.closest('.form-group').find('input, select').attr('readonly', false)
|
||||||
|
field_by_data.closest('.form-group').removeClass('is-readonly')
|
||||||
|
|
||||||
validate: (params) ->
|
validate: (params) ->
|
||||||
App.Model.validate(
|
App.Model.validate(
|
||||||
model: @model
|
model: @model
|
||||||
|
|
|
@ -37,17 +37,17 @@ class App.UiElement.core_workflow_perform extends App.UiElement.ApplicationSelec
|
||||||
delete groups[key]
|
delete groups[key]
|
||||||
|
|
||||||
operatorsType =
|
operatorsType =
|
||||||
'boolean$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'add_option', 'remove_option', 'set_fixed_to']
|
'boolean$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'add_option', 'remove_option', 'set_fixed_to']
|
||||||
'integer$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional']
|
'integer$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly']
|
||||||
'^select$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'add_option', 'remove_option', 'set_fixed_to', 'select', 'auto_select']
|
'^select$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'add_option', 'remove_option', 'set_fixed_to', 'select', 'auto_select']
|
||||||
'^tree_select$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'add_option', 'remove_option', 'set_fixed_to', 'select', 'auto_select']
|
'^tree_select$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'add_option', 'remove_option', 'set_fixed_to', 'select', 'auto_select']
|
||||||
'^input$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'fill_in', 'fill_in_empty']
|
'^input$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'fill_in', 'fill_in_empty']
|
||||||
|
|
||||||
operatorsName =
|
operatorsName =
|
||||||
'_id$': ['show', 'hide', 'set_mandatory', 'set_optional', 'add_option', 'remove_option', 'set_fixed_to', 'select', 'auto_select']
|
'_id$': ['show', 'hide', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'add_option', 'remove_option', 'set_fixed_to', 'select', 'auto_select']
|
||||||
'_ids$': ['show', 'hide', 'set_mandatory', 'set_optional']
|
'_ids$': ['show', 'hide', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly']
|
||||||
'organization_id$': ['show', 'hide', 'set_mandatory', 'set_optional', 'add_option', 'remove_option']
|
'organization_id$': ['show', 'hide', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'add_option', 'remove_option']
|
||||||
'owner_id$': ['show', 'hide', 'set_mandatory', 'set_optional', 'add_option', 'remove_option', 'select', 'auto_select']
|
'owner_id$': ['show', 'hide', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'add_option', 'remove_option', 'select', 'auto_select']
|
||||||
|
|
||||||
# merge config
|
# merge config
|
||||||
elements = {}
|
elements = {}
|
||||||
|
|
|
@ -205,6 +205,16 @@ class App.FormHandlerCoreWorkflow
|
||||||
else
|
else
|
||||||
ui.optional(field, form)
|
ui.optional(field, form)
|
||||||
|
|
||||||
|
# changes the mandatory flag of form elements
|
||||||
|
@changeReadonly: (form, ui, data) ->
|
||||||
|
return if _.isEmpty(data)
|
||||||
|
|
||||||
|
for field, state of data
|
||||||
|
if state
|
||||||
|
ui.readonly(field, form)
|
||||||
|
else
|
||||||
|
ui.changeable(field, form)
|
||||||
|
|
||||||
# executes individual js commands of the Core Workflow engine
|
# executes individual js commands of the Core Workflow engine
|
||||||
@executeEval: (form, ui, data) ->
|
@executeEval: (form, ui, data) ->
|
||||||
return if _.isEmpty(data)
|
return if _.isEmpty(data)
|
||||||
|
@ -226,6 +236,7 @@ class App.FormHandlerCoreWorkflow
|
||||||
App.FormHandlerCoreWorkflow.fillIn(classname, form, ui, attributes, params, data.fill_in)
|
App.FormHandlerCoreWorkflow.fillIn(classname, form, ui, attributes, params, data.fill_in)
|
||||||
App.FormHandlerCoreWorkflow.changeVisibility(form, ui, data.visibility)
|
App.FormHandlerCoreWorkflow.changeVisibility(form, ui, data.visibility)
|
||||||
App.FormHandlerCoreWorkflow.changeMandatory(form, ui, data.mandatory, data.visibility)
|
App.FormHandlerCoreWorkflow.changeMandatory(form, ui, data.mandatory, data.visibility)
|
||||||
|
App.FormHandlerCoreWorkflow.changeReadonly(form, ui, data.readonly)
|
||||||
App.FormHandlerCoreWorkflow.executeEval(form, ui, data.eval)
|
App.FormHandlerCoreWorkflow.executeEval(form, ui, data.eval)
|
||||||
App.FormHandlerCoreWorkflow.runCallbacks(ui)
|
App.FormHandlerCoreWorkflow.runCallbacks(ui)
|
||||||
|
|
||||||
|
|
|
@ -1782,6 +1782,11 @@ fieldset > .form-group {
|
||||||
&.form-group--inactive {
|
&.form-group--inactive {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-readonly {
|
||||||
|
pointer-events: none;
|
||||||
|
cursor: not-allowed !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.date.form-group .controls {
|
.date.form-group .controls {
|
||||||
|
|
|
@ -71,8 +71,12 @@ class CoreWorkflow::Attributes
|
||||||
attribute[:screens].dig(@payload['screen'], type)
|
attribute[:screens].dig(@payload['screen'], type)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def request_id_default
|
||||||
|
payload['request_id']
|
||||||
|
end
|
||||||
|
|
||||||
# dont cache this else the result object will work with references and cache bugs occur
|
# dont cache this else the result object will work with references and cache bugs occur
|
||||||
def shown_default
|
def visibility_default
|
||||||
object_elements.each_with_object({}) do |attribute, result|
|
object_elements.each_with_object({}) do |attribute, result|
|
||||||
result[ attribute[:name] ] = if @payload['request_id'] == 'ChecksCoreWorkflow.validate_workflows'
|
result[ attribute[:name] ] = if @payload['request_id'] == 'ChecksCoreWorkflow.validate_workflows'
|
||||||
'show'
|
'show'
|
||||||
|
@ -106,6 +110,33 @@ class CoreWorkflow::Attributes
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# dont cache this else the result object will work with references and cache bugs occur
|
||||||
|
def readonly_default
|
||||||
|
object_elements.each_with_object({}) do |attribute, result|
|
||||||
|
result[ attribute[:name] ] = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def select_default
|
||||||
|
@result_object.result[:select] || {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def fill_in_default
|
||||||
|
@result_object.result[:fill_in] || {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def eval_default
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
def matched_workflows_default
|
||||||
|
@result_object.result[:matched_workflows] || []
|
||||||
|
end
|
||||||
|
|
||||||
|
def rerun_count_default
|
||||||
|
@result_object.result[:rerun_count] || 0
|
||||||
|
end
|
||||||
|
|
||||||
def options_array(options)
|
def options_array(options)
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
|
|
|
@ -28,17 +28,10 @@ class CoreWorkflow::Result
|
||||||
def set_default
|
def set_default
|
||||||
@rerun = false
|
@rerun = false
|
||||||
|
|
||||||
@result = {
|
@result[:restrict_values] = {}
|
||||||
request_id: payload['request_id'],
|
%i[request_id visibility mandatory readonly select fill_in eval matched_workflows rerun_count].each do |group|
|
||||||
restrict_values: {},
|
@result[group] = attributes.send(:"#{group}_default")
|
||||||
visibility: attributes.shown_default,
|
end
|
||||||
mandatory: attributes.mandatory_default,
|
|
||||||
select: @result[:select] || {},
|
|
||||||
fill_in: @result[:fill_in] || {},
|
|
||||||
eval: [],
|
|
||||||
matched_workflows: @result[:matched_workflows] || [],
|
|
||||||
rerun_count: @result[:rerun_count] || 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
# restrict init defaults to make sure param values to removed if not allowed
|
# restrict init defaults to make sure param values to removed if not allowed
|
||||||
attributes.restrict_values_default.each do |field, values|
|
attributes.restrict_values_default.each do |field, values|
|
||||||
|
|
8
app/models/core_workflow/result/set_readonly.rb
Normal file
8
app/models/core_workflow/result/set_readonly.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
class CoreWorkflow::Result::SetReadonly < CoreWorkflow::Result::Backend
|
||||||
|
def run
|
||||||
|
@result_object.result[:readonly][field] = true
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
8
app/models/core_workflow/result/unset_readonly.rb
Normal file
8
app/models/core_workflow/result/unset_readonly.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
class CoreWorkflow::Result::UnsetReadonly < CoreWorkflow::Result::Backend
|
||||||
|
def run
|
||||||
|
@result_object.result[:readonly][field] = false
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
|
@ -56,10 +56,9 @@ RSpec.describe CoreWorkflow::Attributes, type: :model do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#shown_default' do
|
describe '#visibility_default' do
|
||||||
it 'priority should be shown by default' do
|
it 'priority should be shown by default' do
|
||||||
expect(result.shown_default['priority_id']).to eq('show')
|
expect(result.visibility_default['priority_id']).to eq('show')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1561,4 +1561,46 @@ RSpec.describe CoreWorkflow, type: :model do
|
||||||
expect(result[:visibility]['priority_id']).to eq('hide')
|
expect(result[:visibility]['priority_id']).to eq('hide')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '.perform - Readonly' do
|
||||||
|
let!(:workflow1) do
|
||||||
|
create(:core_workflow,
|
||||||
|
object: 'Ticket',
|
||||||
|
perform: {
|
||||||
|
'ticket.group_id': {
|
||||||
|
operator: 'set_readonly',
|
||||||
|
set_readonly: 'true'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does match workflow' do
|
||||||
|
expect(result[:matched_workflows]).to include(workflow1.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does set group readonly' do
|
||||||
|
expect(result[:readonly]['group_id']).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when readonly unset' do
|
||||||
|
let!(:workflow2) do
|
||||||
|
create(:core_workflow,
|
||||||
|
object: 'Ticket',
|
||||||
|
perform: {
|
||||||
|
'ticket.group_id': {
|
||||||
|
operator: 'unset_readonly',
|
||||||
|
unset_readonly: 'true'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does match workflows' do
|
||||||
|
expect(result[:matched_workflows]).to include(workflow1.id, workflow2.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does set group readonly' do
|
||||||
|
expect(result[:readonly]['group_id']).to eq(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -122,6 +122,42 @@ RSpec.shared_examples 'core workflow' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'action - unset_readonly' do
|
||||||
|
before do
|
||||||
|
create(:core_workflow,
|
||||||
|
object: object_name,
|
||||||
|
perform: {
|
||||||
|
"#{object_name.downcase}.#{field_name}": {
|
||||||
|
operator: 'unset_readonly',
|
||||||
|
unset_readonly: 'true'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does perform' do
|
||||||
|
before_it.call
|
||||||
|
expect(page).to have_no_selector("div[data-attribute-name='#{field_name}'].is-readonly", wait: 10)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'action - set_readonly' do
|
||||||
|
before do
|
||||||
|
create(:core_workflow,
|
||||||
|
object: object_name,
|
||||||
|
perform: {
|
||||||
|
"#{object_name.downcase}.#{field_name}": {
|
||||||
|
operator: 'set_readonly',
|
||||||
|
set_readonly: 'true'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does perform' do
|
||||||
|
before_it.call
|
||||||
|
expect(page).to have_selector("div[data-attribute-name='#{field_name}'].is-readonly", wait: 10)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'action - fill_in' do
|
describe 'action - fill_in' do
|
||||||
before do
|
before do
|
||||||
create(:core_workflow,
|
create(:core_workflow,
|
||||||
|
@ -284,6 +320,42 @@ RSpec.shared_examples 'core workflow' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'action - unset_readonly' do
|
||||||
|
before do
|
||||||
|
create(:core_workflow,
|
||||||
|
object: object_name,
|
||||||
|
perform: {
|
||||||
|
"#{object_name.downcase}.#{field_name}": {
|
||||||
|
operator: 'unset_readonly',
|
||||||
|
unset_readonly: 'true'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does perform' do
|
||||||
|
before_it.call
|
||||||
|
expect(page).to have_no_selector("div[data-attribute-name='#{field_name}'].is-readonly", wait: 10)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'action - set_readonly' do
|
||||||
|
before do
|
||||||
|
create(:core_workflow,
|
||||||
|
object: object_name,
|
||||||
|
perform: {
|
||||||
|
"#{object_name.downcase}.#{field_name}": {
|
||||||
|
operator: 'set_readonly',
|
||||||
|
set_readonly: 'true'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does perform' do
|
||||||
|
before_it.call
|
||||||
|
expect(page).to have_selector("div[data-attribute-name='#{field_name}'].is-readonly", wait: 10)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'action - restrict values' do
|
describe 'action - restrict values' do
|
||||||
before do
|
before do
|
||||||
create(:core_workflow,
|
create(:core_workflow,
|
||||||
|
@ -607,6 +679,42 @@ RSpec.shared_examples 'core workflow' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'action - unset_readonly' do
|
||||||
|
before do
|
||||||
|
create(:core_workflow,
|
||||||
|
object: object_name,
|
||||||
|
perform: {
|
||||||
|
"#{object_name.downcase}.#{field_name}": {
|
||||||
|
operator: 'unset_readonly',
|
||||||
|
unset_readonly: 'true'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does perform' do
|
||||||
|
before_it.call
|
||||||
|
expect(page).to have_no_selector("div[data-attribute-name='#{field_name}'].is-readonly", wait: 10)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'action - set_readonly' do
|
||||||
|
before do
|
||||||
|
create(:core_workflow,
|
||||||
|
object: object_name,
|
||||||
|
perform: {
|
||||||
|
"#{object_name.downcase}.#{field_name}": {
|
||||||
|
operator: 'set_readonly',
|
||||||
|
set_readonly: 'true'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does perform' do
|
||||||
|
before_it.call
|
||||||
|
expect(page).to have_selector("div[data-attribute-name='#{field_name}'].is-readonly", wait: 10)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'action - restrict values' do
|
describe 'action - restrict values' do
|
||||||
before do
|
before do
|
||||||
create(:core_workflow,
|
create(:core_workflow,
|
||||||
|
|
Loading…
Reference in a new issue