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) ->
|
||||
return field.closest('.form-group').hasClass('is-removed')
|
||||
|
||||
@fieldIsReadonly: (field) ->
|
||||
return field.closest('.form-group').hasClass('is-readonly')
|
||||
|
||||
attributeIsMandatory: (name) ->
|
||||
field_by_name = @constructor.findFieldByName(name, @form)
|
||||
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.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) ->
|
||||
App.Model.validate(
|
||||
model: @model
|
||||
|
|
|
@ -37,17 +37,17 @@ class App.UiElement.core_workflow_perform extends App.UiElement.ApplicationSelec
|
|||
delete groups[key]
|
||||
|
||||
operatorsType =
|
||||
'boolean$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'add_option', 'remove_option', 'set_fixed_to']
|
||||
'integer$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional']
|
||||
'^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', 'add_option', 'remove_option', 'set_fixed_to', 'select', 'auto_select']
|
||||
'^input$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'fill_in', 'fill_in_empty']
|
||||
'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', 'set_readonly', 'unset_readonly']
|
||||
'^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', 'set_readonly', 'unset_readonly', 'add_option', 'remove_option', 'set_fixed_to', 'select', 'auto_select']
|
||||
'^input$': ['show', 'hide', 'remove', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'fill_in', 'fill_in_empty']
|
||||
|
||||
operatorsName =
|
||||
'_id$': ['show', 'hide', 'set_mandatory', 'set_optional', 'add_option', 'remove_option', 'set_fixed_to', 'select', 'auto_select']
|
||||
'_ids$': ['show', 'hide', 'set_mandatory', 'set_optional']
|
||||
'organization_id$': ['show', 'hide', 'set_mandatory', 'set_optional', 'add_option', 'remove_option']
|
||||
'owner_id$': ['show', 'hide', 'set_mandatory', 'set_optional', 'add_option', 'remove_option', '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', 'set_readonly', 'unset_readonly']
|
||||
'organization_id$': ['show', 'hide', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'add_option', 'remove_option']
|
||||
'owner_id$': ['show', 'hide', 'set_mandatory', 'set_optional', 'set_readonly', 'unset_readonly', 'add_option', 'remove_option', 'select', 'auto_select']
|
||||
|
||||
# merge config
|
||||
elements = {}
|
||||
|
|
|
@ -205,6 +205,16 @@ class App.FormHandlerCoreWorkflow
|
|||
else
|
||||
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
|
||||
@executeEval: (form, ui, 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.changeVisibility(form, ui, 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.runCallbacks(ui)
|
||||
|
||||
|
|
|
@ -1782,6 +1782,11 @@ fieldset > .form-group {
|
|||
&.form-group--inactive {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&.is-readonly {
|
||||
pointer-events: none;
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
}
|
||||
|
||||
.date.form-group .controls {
|
||||
|
@ -12960,7 +12965,7 @@ span.is-disabled {
|
|||
padding: 6px 5px 11px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
|
||||
.tag {
|
||||
margin: 2px;
|
||||
display: inline-block;
|
||||
|
|
|
@ -71,8 +71,12 @@ class CoreWorkflow::Attributes
|
|||
attribute[:screens].dig(@payload['screen'], type)
|
||||
end
|
||||
|
||||
def request_id_default
|
||||
payload['request_id']
|
||||
end
|
||||
|
||||
# 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|
|
||||
result[ attribute[:name] ] = if @payload['request_id'] == 'ChecksCoreWorkflow.validate_workflows'
|
||||
'show'
|
||||
|
@ -106,6 +110,33 @@ class CoreWorkflow::Attributes
|
|||
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)
|
||||
result = []
|
||||
|
||||
|
|
|
@ -28,17 +28,10 @@ class CoreWorkflow::Result
|
|||
def set_default
|
||||
@rerun = false
|
||||
|
||||
@result = {
|
||||
request_id: payload['request_id'],
|
||||
restrict_values: {},
|
||||
visibility: attributes.shown_default,
|
||||
mandatory: attributes.mandatory_default,
|
||||
select: @result[:select] || {},
|
||||
fill_in: @result[:fill_in] || {},
|
||||
eval: [],
|
||||
matched_workflows: @result[:matched_workflows] || [],
|
||||
rerun_count: @result[:rerun_count] || 0,
|
||||
}
|
||||
@result[:restrict_values] = {}
|
||||
%i[request_id visibility mandatory readonly select fill_in eval matched_workflows rerun_count].each do |group|
|
||||
@result[group] = attributes.send(:"#{group}_default")
|
||||
end
|
||||
|
||||
# restrict init defaults to make sure param values to removed if not allowed
|
||||
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
|
||||
|
||||
describe '#shown_default' do
|
||||
describe '#visibility_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
|
||||
|
|
|
@ -1561,4 +1561,46 @@ RSpec.describe CoreWorkflow, type: :model do
|
|||
expect(result[:visibility]['priority_id']).to eq('hide')
|
||||
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
|
||||
|
|
|
@ -122,6 +122,42 @@ RSpec.shared_examples 'core workflow' do
|
|||
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
|
||||
before do
|
||||
create(:core_workflow,
|
||||
|
@ -284,6 +320,42 @@ RSpec.shared_examples 'core workflow' do
|
|||
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
|
||||
before do
|
||||
create(:core_workflow,
|
||||
|
@ -607,6 +679,42 @@ RSpec.shared_examples 'core workflow' do
|
|||
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
|
||||
before do
|
||||
create(:core_workflow,
|
||||
|
|
Loading…
Reference in a new issue