general: add support for a readonly marker in objects (#45924)

This commit is contained in:
Frédéric Péters 2020-08-15 17:00:39 +02:00
parent c8d5aa67f6
commit 0f860af96d
15 changed files with 120 additions and 62 deletions

View File

@ -23,7 +23,7 @@ from quixote.html import TemplateIO, htmltext
from wcs.blocks import BlockDef, BlockdefImportError
from wcs.qommon.form import Form, StringWidget, HtmlWidget, FileWidget
from wcs.qommon import _, misc, template
from wcs.qommon import _, N_, misc, template
from wcs.qommon.backoffice.menu import html_top
from wcs.admin.fields import FieldDefPage, FieldsDirectory
@ -43,6 +43,7 @@ class BlockDirectory(FieldsDirectory):
field_def_page_class = BlockFieldDefPage
blacklisted_types = ['page', 'table', 'table-select', 'tablerows', 'ranked-items', 'blocks']
support_import = False
readonly_message = N_('This block of fields is readonly.')
def __init__(self, section, *args, **kwargs):
self.section = section
@ -57,7 +58,8 @@ class BlockDirectory(FieldsDirectory):
r += htmltext('<div id="appbar">')
r += htmltext('<h2>%s</h2>') % self.objectdef.name
r += htmltext('<span class="actions">')
r += htmltext('<a href="delete" rel="popup">%s</a>') % _('Delete')
if not self.objectdef.is_readonly():
r += htmltext('<a href="delete" rel="popup">%s</a>') % _('Delete')
r += htmltext('<a href="export">%s</a>') % _('Export')
r += htmltext('<a href="settings" rel="popup">%s</a>') % _('Settings')
r += htmltext('</span>')
@ -137,7 +139,8 @@ class BlockDirectory(FieldsDirectory):
form.add(
StringWidget, 'digest_template', title=_('Digest Template'), value=self.objectdef.digest_template, size=50
)
form.add_submit('submit', _('Submit'))
if not self.objectdef.is_readonly():
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():

View File

@ -120,7 +120,8 @@ class NamedDataSourceUI(object):
title=_('Identifier'),
required=True, advanced=True,
)
form.add_submit('submit', _('Submit'))
if not self.datasource.is_readonly():
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
return form
@ -167,6 +168,9 @@ class NamedDataSourcePage(Directory):
def _q_index(self):
html_top('datasources', title=self.datasource.name)
if self.datasource.is_readonly():
get_response().filter['sidebar'] = htmltext(
'<div class="infonotice"><p>%s</p></div>') % _('This data source is readonly.')
return template.QommonTemplateResponse(
templates=['wcs/backoffice/data-source.html'],
context={'view': self, 'datasource': self.datasource})

View File

@ -56,7 +56,8 @@ class FieldDefPage(Directory):
self.field.fill_admin_form(form)
form.widgets = [x for x in form.widgets
if getattr(x, 'name', None) not in self.blacklisted_attributes]
form.add_submit('submit', _('Submit'))
if not self.objectdef.is_readonly():
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
return form
@ -201,6 +202,7 @@ class FieldsDirectory(Directory):
blacklisted_types = []
page_id = None
field_var_prefix = '..._'
readonly_message = N_('The fields are readonly.')
support_import = True
@ -313,12 +315,16 @@ class FieldsDirectory(Directory):
r += command_icon('pages/%s/' % field.id, 'view',
label = _('Limit display to this page'))
r += command_icon('%s/' % field.id, 'edit')
r += command_icon('%s/duplicate' % field.id, 'duplicate')
r += command_icon('%s/delete' % field.id, 'remove', popup = True)
if not self.objectdef.is_readonly():
r += command_icon('%s/duplicate' % field.id, 'duplicate')
r += command_icon('%s/delete' % field.id, 'remove', popup=True)
r += htmltext('</p></li>')
r += htmltext('</ul>')
get_response().filter['sidebar'] = str(self.get_new_field_form(self.page_id))
if self.objectdef.is_readonly():
get_response().filter['sidebar'] = '<div class="infonotice"><p>%s</p></div>' % _(self.readonly_message)
else:
get_response().filter['sidebar'] = str(self.get_new_field_form(self.page_id))
r += self.index_bottom()
return r.getvalue()

View File

@ -90,7 +90,8 @@ class FormDefUI(object):
form.add(SingleSelectWidget, 'workflow_id', title=_('Workflow'),
value=formdef.workflow_id,
options=workflows)
form.add_submit('submit', _('Submit'))
if not formdef.is_readonly():
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
return form
@ -136,6 +137,7 @@ class FieldDefPage(FieldDefPage):
class FieldsDirectory(FieldsDirectory):
field_def_page_class = FieldDefPage
field_var_prefix = 'form_var_'
readonly_message = N_('This form is readonly.')
def index_bottom(self):
if hasattr(self.objectdef, str('disabled')) and self.objectdef.disabled:
@ -277,7 +279,8 @@ class OptionsDirectory(Directory):
return result
def handle(self, form, title):
form.add_submit('submit', _('Submit'))
if not self.formdef.is_readonly():
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('..')
@ -330,7 +333,8 @@ class WorkflowRoleDirectory(Directory):
options.extend(get_user_roles())
form = Form(enctype='multipart/form-data')
form.add(SingleSelectWidget, 'role_id', value=role_id, options=options)
form.add_submit('submit', _('Submit'))
if not self.formdef.is_readonly():
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
@ -404,7 +408,8 @@ class FormDefPage(Directory):
r += htmltext('<div id="appbar">')
r += htmltext('<h2>%s</h2>') % self.formdef.name
r += htmltext('<span class="actions">')
r += htmltext('<a rel="popup" href="title">%s</a>') % _('change title')
if not self.formdef.is_readonly():
r += htmltext('<a rel="popup" href="title">%s</a>') % _('change title')
r += htmltext('</span>')
r += htmltext('</div>')
@ -604,6 +609,9 @@ class FormDefPage(Directory):
def get_sidebar(self):
r = TemplateIO(html=True)
if self.formdef.is_readonly():
r += htmltext('<div class="infonotice"><p>%s</p></div>') % _('This form is readonly.')
return r.getvalue()
r += htmltext('<ul id="sidebar-actions">')
r += htmltext('<li><a href="delete" rel="popup">%s</a></li>') % _('Delete')
r += htmltext('<li><a href="duplicate">%s</a></li>') % _('Duplicate')
@ -658,7 +666,8 @@ class FormDefPage(Directory):
form.add(SingleSelectWidget, 'category_id',
value=self.formdef.category_id,
options=[(None, '---', '')] + categories)
form.add_submit('submit', _('Submit'))
if not self.formdef.is_readonly():
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
@ -697,7 +706,8 @@ class FormDefPage(Directory):
title=_('Required authentication contexts'),
value=self.formdef.required_authentication_contexts,
options=list(auth_contexts.items()))
form.add_submit('submit', _('Submit'))
if not self.formdef.is_readonly():
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
@ -750,7 +760,8 @@ class FormDefPage(Directory):
form.add(ValidatedStringWidget, 'url_name', title=_('Identifier in URLs'),
size=40, required=True, value=self.formdef.url_name,
regex=r'^[a-zA-Z0-9_-]+', **kwargs)
form.add_submit('submit', _('Submit'))
if not self.formdef.is_readonly():
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
@ -789,7 +800,8 @@ class FormDefPage(Directory):
form.add(SingleSelectWidget, 'workflow_id',
value=self.formdef.workflow_id,
options=workflows)
form.add_submit('submit', _('Submit'))
if not self.formdef.is_readonly():
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')

View File

@ -277,7 +277,7 @@ class WorkflowItemPage(Directory):
form = Form(enctype='multipart/form-data')
self.item.fill_admin_form(form)
if not str(self.workflow.id).startswith(str('_')):
if not self.workflow.is_readonly():
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
@ -436,7 +436,7 @@ class WorkflowStatusPage(Directory):
if self.status.get_visibility_restricted_roles():
r += htmltext('<div class="bo-block">')
r += _('This status is hidden from the user.')
if not str(self.workflow.id).startswith(str('_')):
if not self.workflow.is_readonly():
r += ' '
r += htmltext('(<a href="display" rel="popup">%s</a>)') % _('change')
r += htmltext('</div>')
@ -445,7 +445,7 @@ class WorkflowStatusPage(Directory):
r += htmltext('<div class="infonotice">%s</div>') % _('There are not yet any items in this status.')
else:
r += htmltext('<div class="bo-block">')
if str(self.workflow.id).startswith(str('_')):
if self.workflow.is_readonly():
r += htmltext('<ul id="items-list" class="biglist">')
else:
r += htmltext('<p class="hint">')
@ -454,18 +454,16 @@ class WorkflowStatusPage(Directory):
r += htmltext('<ul id="items-list" class="biglist sortable">')
for i, item in enumerate(self.status.items):
r += htmltext('<li class="biglistitem" id="itemId_%s">') % item.id
if str(self.workflow.id).startswith(str('_')):
r += item.render_as_line()
if hasattr(item, str('fill_admin_form')):
r += htmltext('<a href="items/%s/">%s</a>') % (item.id, item.render_as_line())
else:
if hasattr(item, str('fill_admin_form')):
r += htmltext('<a href="items/%s/">%s</a>') % (item.id, item.render_as_line())
else:
r += item.render_as_line()
r += htmltext('<p class="commands">')
if hasattr(item, str('fill_admin_form')):
r += item.render_as_line()
r += htmltext('<p class="commands">')
if not self.workflow.is_readonly():
if hasattr(item, 'fill_admin_form'):
r += command_icon('items/%s/' % item.id, 'edit')
r += command_icon('items/%s/delete' % item.id, 'remove', popup = True)
r += htmltext('</p>')
r += htmltext('</p>')
r += htmltext('</li>')
r += htmltext('</ul>')
r += htmltext('</div>') # bo-block
@ -509,11 +507,13 @@ class WorkflowStatusPage(Directory):
def get_sidebar(self):
get_response().add_javascript(['popup.js', 'jquery.colourpicker.js'])
r = TemplateIO(html=True)
if str(self.workflow.id).startswith(str('_')):
if self.workflow.is_default():
r += htmltext('<p>')
r += _('''This is the default workflow, you cannot edit it but you can
duplicate it to base your own workflow on it.''')
r += htmltext('</p>')
elif self.workflow.is_readonly():
r += htmltext('<div class="infonotice"><p>%s</p></div>') % _('This workflow is readonly.')
else:
r += htmltext('<ul id="sidebar-actions">')
r += htmltext('<li><a href="edit" rel="popup">%s</a></li>') % _('Change Status Name')
@ -885,6 +885,7 @@ class WorkflowVariablesFieldsDirectory(FieldsDirectory):
support_import = False
blacklisted_types = ['page', 'blocks']
field_var_prefix = 'form_option_'
readonly_message = N_('This workflow is readonly.')
def index_top(self):
r = TemplateIO(html=True)
@ -908,6 +909,7 @@ class WorkflowBackofficeFieldsDirectory(FieldsDirectory):
blacklisted_types = ['page', 'blocks']
blacklisted_attributes = ['condition']
field_var_prefix = 'form_var_'
readonly_message = N_('This workflow is readonly.')
def index_top(self):
r = TemplateIO(html=True)
@ -1165,7 +1167,7 @@ class GlobalActionPage(WorkflowStatusPage):
if not self.action.items:
r += htmltext('<p>%s</p>') % _('There are not yet any items in this action.')
else:
if str(self.workflow.id).startswith('_'):
if self.workflow.is_readonly():
r += htmltext('<ul id="items-list" class="biglist">')
else:
r += htmltext('<p class="items">')
@ -1174,7 +1176,7 @@ class GlobalActionPage(WorkflowStatusPage):
r += htmltext('<ul id="items-list" class="biglist sortable">')
for i, item in enumerate(self.action.items):
r += htmltext('<li class="biglistitem" id="itemId_%s">') % item.id
if str(self.workflow.id).startswith('_'):
if self.workflow.is_readonly():
r += item.render_as_line()
else:
if hasattr(item, 'fill_admin_form'):
@ -1259,7 +1261,7 @@ class GlobalActionPage(WorkflowStatusPage):
def get_sidebar(self):
get_response().add_javascript(['popup.js', 'jquery.colourpicker.js'])
r = TemplateIO(html=True)
if str(self.workflow.id).startswith('_'):
if self.workflow.is_default():
r += htmltext('<p>')
r += _('''This is the default workflow, you cannot edit it but you can
duplicate it to base your own workflow on it.''')
@ -1394,7 +1396,7 @@ class WorkflowPage(Directory):
'svg-pan-zoom.js', 'jquery.colourpicker.js'])
r += htmltext('<div id="appbar">')
r += htmltext('<h2>%s</h2>') % self.workflow.name
if not str(self.workflow.id).startswith('_'):
if not self.workflow.is_readonly():
r += htmltext('<span class="actions">')
r += htmltext('<a rel="popup" href="edit">%s</a>') % _('change title')
r += htmltext('</span>')
@ -1411,7 +1413,7 @@ class WorkflowPage(Directory):
if not self.workflow.possible_status:
r += htmltext('<p>%s</p>') % _('There are not yet any status defined in this workflow.')
else:
if not str(self.workflow.id).startswith(str('_')):
if not self.workflow.is_readonly():
r += htmltext('<p class="hint">')
r += _('Use drag and drop with the handles to reorder status.')
r += htmltext('</p>')
@ -1438,7 +1440,7 @@ class WorkflowPage(Directory):
r += htmltext('<div class="splitcontent-right">')
r += htmltext('<div class="bo-block">')
r += htmltext('<h3>%s') % _('Workflow Functions')
if not str(self.workflow.id).startswith('_'):
if not self.workflow.is_readonly():
r += htmltext(' <span class="change">(<a rel="popup" href="functions/new">%s</a>)</span>') % _('add function')
r += htmltext('</h3>')
r += htmltext('<ul id="roles-list" class="biglist">')
@ -1446,7 +1448,7 @@ class WorkflowPage(Directory):
workflow_roles.sort(key=lambda x: '' if x[0] == '_receiver' else misc.simplify(x[1]))
for key, label in workflow_roles:
r += htmltext('<li class="biglistitem">')
if not str(self.workflow.id).startswith('_'):
if not self.workflow.is_readonly():
r += htmltext('<a rel="popup" href="functions/%s">%s</a>') % (key[1:], label)
else:
r += htmltext('<a>%s</a>') % label
@ -1454,10 +1456,12 @@ class WorkflowPage(Directory):
r += htmltext('</ul>')
r += htmltext('</div>')
if not str(self.workflow.id).startswith('_'):
if not self.workflow.is_default():
r += htmltext('<div class="bo-block">')
r += htmltext('<h3>%s') % _('Workflow Variables')
r += htmltext(' <span class="change">(<a href="variables/">%s</a>)</span></h3>') % _('change')
if not self.workflow.is_readonly():
r += htmltext(' <span class="change">(<a href="variables/">%s</a>)</span>') % _('change')
r += htmltext('</h3>')
if self.workflow.variables_formdef:
r += htmltext('<ul class="biglist">')
for field in self.workflow.variables_formdef.fields:
@ -1470,14 +1474,14 @@ class WorkflowPage(Directory):
r += htmltext('</ul>')
r += htmltext('</div>')
if not str(self.workflow.id).startswith('_'):
if not self.workflow.is_default():
r += htmltext('<div class="bo-block">')
r += htmltext('<h3>%s') % _('Global Actions')
if not str(self.workflow.id).startswith('_'):
if not self.workflow.is_readonly():
r += htmltext(' <span class="change">(<a rel="popup" href="global-actions/new">%s</a>)</span>') % _('add global action')
r += htmltext('</h3>')
if not str(self.workflow.id).startswith('_'):
if not self.workflow.is_readonly():
r += htmltext('<ul id="status-list" class="biglist sortable" '
'data-order-function="update_actions_order">')
else:
@ -1485,19 +1489,16 @@ class WorkflowPage(Directory):
for action in (self.workflow.global_actions or []):
r += htmltext('<li class="biglistitem" id="itemId_%s">' % action.id)
if not str(self.workflow.id).startswith('_'):
r += htmltext('<a href="global-actions/%s/">%s</a>') % (
r += htmltext('<a href="global-actions/%s/">%s</a>') % (
action.id, action.name)
else:
r += htmltext('<a>%s</a>') % action.name
r += htmltext('</li>')
r += htmltext('</ul>')
r += htmltext('</div>')
if not str(self.workflow.id).startswith('_'):
if not self.workflow.is_default():
r += htmltext('<div class="bo-block">')
r += htmltext('<h3>%s') % _('Criticality Levels')
if not str(self.workflow.id).startswith('_'):
if not self.workflow.is_readonly():
r += htmltext(' <span class="change">'
'(<a rel="popup" href="criticality-levels/new">'
'%s</a>)</span>') % _('add criticality level')
@ -1509,19 +1510,17 @@ class WorkflowPage(Directory):
if level.colour:
style = 'style="border-left-color: #%s"' % level.colour
r += htmltext('<li class="biglistitem" id="itemId_%s" %s>' % (level.id, style))
if not str(self.workflow.id).startswith('_'):
r += htmltext('<a rel="popup" href="criticality-levels/%s">%s</a>') % (
r += htmltext('<a rel="popup" href="criticality-levels/%s">%s</a>') % (
level.id, level.name)
else:
r += htmltext('<a>%s</a>') % level.name
r += htmltext('</li>')
r += htmltext('</ul>')
r += htmltext('</div>')
if not str(self.workflow.id).startswith('_'):
if not self.workflow.is_default():
r += htmltext('<div class="bo-block">')
r += htmltext('<h3>%s') % _('Backoffice Fields')
r += htmltext(' <span class="change">(<a href="backoffice-fields/">%s</a>)</span></h3>') % _('change')
if not self.workflow.is_readonly():
r += htmltext(' <span class="change">(<a href="backoffice-fields/">%s</a>)</span></h3>') % _('change')
if self.workflow.backoffice_fields_formdef:
r += htmltext('<ul class="biglist">')
for field in self.workflow.backoffice_fields_formdef.fields:
@ -1558,19 +1557,23 @@ class WorkflowPage(Directory):
def get_sidebar(self):
r = TemplateIO(html=True)
if str(self.workflow.id).startswith(str('_')):
if self.workflow.is_default():
r += htmltext('<p>')
r += _('''This is the default workflow, you cannot edit it but you can
duplicate it to base your own workflow on it.''')
r += htmltext('</p>')
elif self.workflow.is_readonly():
r += htmltext('<div class="infonotice"><p>%s</p></div>') % _('This workflow is readonly.')
return r.getvalue()
r += htmltext('<ul id="sidebar-actions">')
if not str(self.workflow.id).startswith(str('_')):
if not self.workflow.is_readonly():
r += htmltext('<li><a href="delete" rel="popup">%s</a></li>') % _('Delete')
r += htmltext('<li><a href="duplicate">%s</a></li>') % _('Duplicate')
r += htmltext('<li><a href="export">%s</a></li>') % _('Export')
r += htmltext('</ul>')
if not str(self.workflow.id).startswith(str('_')):
if not self.workflow.is_readonly():
r += self.get_new_status_form()
r += LoggedErrorsDirectory.errors_block(workflow_id=self.workflow.id)
return r.getvalue()

View File

@ -49,7 +49,8 @@ class NamedWsCallUI(object):
value=self.wscall.request,
title=_('Request'), required=True)
form.add_submit('submit', _('Submit'))
if not self.wscall.is_readonly():
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
return form
@ -93,13 +94,18 @@ class NamedWsCallPage(Directory):
html_top('wscalls', title=self.wscall.name)
r = TemplateIO(html=True)
if self.wscall.is_readonly():
get_response().filter['sidebar'] = htmltext(
'<div class="infonotice"><p>%s</p></div>') % _('This webservice call is readonly.')
r += htmltext('<div id="appbar">')
r += htmltext('<h2>%s - ') % _('Webservice Call')
r += self.wscall.name
r += htmltext('</h2>')
r += htmltext('<span class="actions">')
r += htmltext('<a href="delete" rel="popup">%s</a>') % _('Delete')
r += htmltext('<a href="edit">%s</a>') % _('Edit')
if not self.wscall.is_readonly():
r += htmltext('<a href="delete" rel="popup">%s</a>') % _('Delete')
r += htmltext('<a href="edit">%s</a>') % _('Edit')
r += htmltext('</span>')
r += htmltext('</div>')

View File

@ -66,9 +66,10 @@ class CardDefPage(FormDefPage):
r += htmltext('<div id="appbar">')
r += htmltext('<h2>%s</h2>') % self.formdef.name
r += htmltext('<span class="actions">')
r += htmltext('<a rel="popup" href="title">%s</a>') % _('change title')
r += htmltext('</span>')
if not self.formdef.is_readonly():
r += htmltext('<span class="actions">')
r += htmltext('<a rel="popup" href="title">%s</a>') % _('change title')
r += htmltext('</span>')
r += htmltext('</div>')
r += utils.last_modification_block(obj=self.formdef)
@ -173,6 +174,9 @@ class CardDefPage(FormDefPage):
def get_sidebar(self):
r = TemplateIO(html=True)
if self.formdef.is_readonly():
r += htmltext('<div class="infonotice"><p>%s</p></div>') % _('This card model is readonly.')
return r.getvalue()
r += htmltext('<ul id="sidebar-actions">')
r += htmltext('<li><a href="delete" rel="popup">%s</a></li>') % _('Delete')
r += htmltext('<li><a href="duplicate">%s</a></li>') % _('Duplicate')

View File

@ -57,6 +57,7 @@ class BlockDef(StorableObject):
self.fields = []
def store(self):
assert not self.is_readonly()
if self.slug is None:
# set slug if it's not yet there
self.slug = self.get_new_slug()

View File

@ -356,6 +356,7 @@ class NamedDataSource(XmlStorableObject):
self.store()
def store(self):
assert not self.is_readonly()
if self.slug is None:
# set slug if it's not yet there
self.slug = self.get_new_slug()

View File

@ -366,6 +366,7 @@ class FormDef(StorableObject):
sql.formdef_wipe()
def store(self):
assert not self.is_readonly()
if self.url_name is None:
# set url name if it's not yet there
self.url_name = self.get_new_url_name()

View File

@ -292,6 +292,9 @@ class StorableObject(object):
def __init__(self, id = None):
self.id = id
def is_readonly(self):
return getattr(self, 'readonly', False)
@classmethod
def get_table_name(cls):
return cls._names
@ -594,6 +597,7 @@ class StorableObject(object):
return pickle.dumps(object, protocol=2)
def store(self, async_op=False):
assert not self.is_readonly()
objects_dir = self.get_objects_dir()
new_object = False
if self._filename:
@ -810,6 +814,7 @@ class StorableObject(object):
os.unlink(os.path.join(objects_dir, fix_key(id)))
def remove_self(self):
assert not self.is_readonly()
self.remove_object(self.id)
@classmethod

View File

@ -5,8 +5,10 @@
<h2>{% trans "Data Source" %} - {{ datasource.name }}</h2>
<span class="actions">
<a href="export">{% trans "Export" %}</a>
{% if not datasource.is_readonly %}
<a href="delete" rel="popup">{% trans "Delete" %}</a>
<a href="edit">{% trans "Edit" %}</a>
{% endif %}
</span>
</div>

View File

@ -155,6 +155,8 @@ class FormWorkflowStatusItem(WorkflowStatusItem):
if component == 'fields':
if not self.formdef:
self.formdef = WorkflowFormFieldsFormDef(item=self)
if workflow.is_readonly():
self.formdef.readonly = True
fields_directory = WorkflowFormFieldsDirectory(self.formdef)
if self.varname:
fields_directory.field_var_prefix = '%s_var_' % self.varname

View File

@ -348,6 +348,7 @@ class Workflow(StorableObject):
self.store()
def store(self):
assert not self.is_readonly()
must_update = False
if self.id:
old_self = self.get(self.id, ignore_errors=True, ignore_migration=True)
@ -849,6 +850,12 @@ class Workflow(StorableObject):
return workflow
def is_default(self):
return str(self.id).startswith(str('_'))
def is_readonly(self):
return self.is_default() or super().is_readonly()
def formdefs(self, **kwargs):
return list(FormDef.select(lambda x: x.workflow_id == self.id, **kwargs))

View File

@ -176,6 +176,7 @@ class NamedWsCall(XmlStorableObject):
return request
def store(self):
assert not self.is_readonly()
if self.slug is None:
# set slug if it's not yet there
self.slug = self.get_new_slug()