workflows: extend create carddata/formdata user association options (#51593)

This commit is contained in:
Frédéric Péters 2021-03-15 09:49:33 +01:00
parent 5415020101
commit e886daee6f
6 changed files with 251 additions and 25 deletions

View File

@ -5519,7 +5519,7 @@ def test_workflow_inspect_page(pub):
create_formdata.varname = 'resubmitted'
create_formdata.draft = True
create_formdata.formdef_slug = target_formdef.url_name
create_formdata.keep_user = True
create_formdata.user_association_mode = 'keep-user'
create_formdata.backoffice_submission = True
create_formdata.mappings = [
Mapping(field_id='0', expression='=form_var_toto_string'),
@ -5752,7 +5752,7 @@ def create_formdata(request, pub):
create_formdata.varname = 'resubmitted'
create_formdata.draft = True
create_formdata.formdef_slug = target_formdef.url_name
create_formdata.keep_user = True
create_formdata.user_association_mode = 'keep-user'
create_formdata.backoffice_submission = True
create_formdata.attach_to_history = request.param.get('attach_to_history', False)
create_formdata.mappings = [

View File

@ -5633,6 +5633,29 @@ def test_create_formdata(two_pubs):
assert target_formdef.data_class().count() == 0
def test_create_formdata_migration(pub):
wf = Workflow(name='create-formdata')
st1 = wf.add_status('Status1', 'st1')
create = CreateFormdataWorkflowStatusItem()
create.id = '_create'
create.label = 'create a new linked form'
create.varname = 'resubmitted'
create.mappings = [
Mapping(field_id='0', expression='=form_var_toto_string'),
Mapping(field_id='1', expression='=form_var_toto_file_raw'),
Mapping(field_id='2', expression='=form_var_toto_item_raw'),
]
create.parent = st1
create.keep_user = True
st1.items.append(create)
wf.store()
wf = Workflow.get(wf.id)
assert wf.possible_status[0].items[0].user_association_mode == 'keep-user'
assert not hasattr(wf.possible_status[0].items[0], 'keep_user')
def test_create_formdata_tracking_code(two_pubs, emails):
FormDef.wipe()
if two_pubs.is_using_postgresql():
@ -5782,6 +5805,139 @@ def test_create_carddata(two_pubs):
assert carddef.data_class().count() == 0
def test_create_carddata_user_association(two_pubs):
CardDef.wipe()
FormDef.wipe()
two_pubs.user_class.wipe()
user = two_pubs.user_class()
user.email = 'test@example.net'
user.name_identifiers = ['xyz']
user.store()
carddef = CardDef()
carddef.name = 'My card'
carddef.fields = []
carddef.user_support = 'optional'
carddef.store()
wf = Workflow(name='create-carddata')
wf.possible_status = Workflow.get_default_workflow().possible_status[:]
create = CreateCarddataWorkflowStatusItem()
create.label = 'Create CardDef'
create.varname = 'mycard'
create.id = '_create'
create.formdef_slug = carddef.url_name
create.map_fields_by_varname = True
create.parent = wf.possible_status[1]
wf.possible_status[1].items.insert(0, create)
wf.store()
formdef = FormDef()
formdef.name = 'source form'
formdef.fields = []
formdef.workflow_id = wf.id
formdef.store()
formdata = formdef.data_class()()
formdata.data = {}
formdata.user_id = user.id
formdata.just_created()
formdata.perform_workflow()
assert carddef.data_class().count() == 1
assert carddef.data_class().select()[0].user is None
# keep user
carddef.data_class().wipe()
create.user_association_mode = 'keep-user'
wf.store()
formdata = FormDef.get(formdef.id).data_class()()
formdata.data = {}
formdata.user_id = user.id
formdata.just_created()
formdata.perform_workflow()
assert carddef.data_class().count() == 1
assert carddef.data_class().select()[0].user.id == user.id
# user association on direct user
carddef.data_class().wipe()
create.user_association_mode = 'custom'
create.user_association_template = '{{ form_user }}'
wf.store()
formdata = FormDef.get(formdef.id).data_class()()
formdata.data = {}
formdata.user_id = user.id
formdata.just_created()
formdata.perform_workflow()
assert carddef.data_class().count() == 1
assert carddef.data_class().select()[0].user.id == user.id
# user association on user email
carddef.data_class().wipe()
create.user_association_mode = 'custom'
create.user_association_template = 'test@example.net'
wf.store()
formdata = FormDef.get(formdef.id).data_class()()
formdata.data = {}
formdata.user_id = user.id
formdata.just_created()
formdata.perform_workflow()
assert carddef.data_class().count() == 1
assert carddef.data_class().select()[0].user.id == user.id
# user association on name id
carddef.data_class().wipe()
create.user_association_mode = 'custom'
create.user_association_template = 'xyz'
wf.store()
formdata = FormDef.get(formdef.id).data_class()()
formdata.data = {}
formdata.user_id = user.id
formdata.just_created()
formdata.perform_workflow()
assert carddef.data_class().count() == 1
assert carddef.data_class().select()[0].user.id == user.id
# user association on invalid user
carddef.data_class().wipe()
create.user_association_mode = 'custom'
create.user_association_template = 'zzz'
wf.store()
formdata = FormDef.get(formdef.id).data_class()()
formdata.data = {}
formdata.user_id = user.id
formdata.just_created()
formdata.perform_workflow()
assert carddef.data_class().count() == 1
assert carddef.data_class().select()[0].user is None
# user association on invalid template
carddef.data_class().wipe()
create.user_association_mode = 'custom'
create.user_association_template = '{% %}'
wf.store()
formdata = FormDef.get(formdef.id).data_class()()
formdata.data = {}
formdata.user_id = user.id
formdata.just_created()
formdata.perform_workflow()
assert carddef.data_class().count() == 1
assert carddef.data_class().select()[0].user is None
def test_call_external_workflow_with_evolution_linked_object(two_pubs):
FormDef.wipe()
CardDef.wipe()

View File

@ -349,17 +349,8 @@ class ImportFromCsvAfterJob(AfterJob):
def user_lookup(self, user_value):
if self.carddef.user_support != 'optional':
return
if not user_value:
return
users = []
if '@' in user_value:
users = get_publisher().user_class.get_users_with_email(user_value)
else:
users = get_publisher().user_class.get_users_with_name_identifier(user_value)
if not users:
return
return users[0]
return None
return get_publisher().user_class.lookup_by_string(user_value)
def execute(self):
self.carddef = self.kwargs['carddef_class'].get(self.kwargs['carddef_id'])

View File

@ -209,6 +209,23 @@ class User(StorableObject):
d[prefix + 'user_name_identifier_%d' % i] = name_identifier
return d
@classmethod
def lookup_by_string(cls, user_value):
# lookup a single user using some string identifier, it can either be
# an email address or a uuid (name id).
if not user_value:
return None
if '@' in user_value:
users = cls.get_users_with_email(user_value)
else:
users = cls.get_users_with_name_identifier(user_value)
if not users:
return None
# hopefully the list has a single item but sort it on id to get a
# stable value in case there are multiple items.
users.sort(key=lambda x: x.id)
return users[0]
@classmethod
def get_substitution_variables_list(cls, prefix='session_'):
formdef = cls.get_formdef()

View File

@ -39,13 +39,21 @@ class CreateCarddataWorkflowStatusItem(CreateFormdataWorkflowStatusItem):
formdef_label = N_('Card')
mappings_label = N_('Mappings to new card fields')
varname_hint = N_('This is used to get linked card in expressions.')
user_association_option_label = N_('User to associate to card')
@classmethod
def is_available(cls, workflow=None):
return get_publisher().has_site_option('studio')
def get_parameters(self):
return ('formdef_slug', 'mappings', 'keep_user', 'varname', 'condition')
return (
'formdef_slug',
'mappings',
'user_association_mode',
'user_association_template',
'varname',
'condition',
)
register_item_class(CreateCarddataWorkflowStatusItem)

View File

@ -29,6 +29,7 @@ from wcs.qommon.form import (
CompositeWidget,
SingleSelectWidget,
ComputedExpressionWidget,
RadiobuttonsWidget,
CheckboxWidget,
VarnameWidget,
HtmlWidget,
@ -218,16 +219,26 @@ class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
mappings_label = N_('Mappings to new form fields')
accept_empty_value = False
varname_hint = N_('This is used to get linked forms in expressions.')
user_association_option_label = N_('User to associate to form')
draft = False
backoffice_submission = False
keep_user = True
user_association_mode = None
user_association_template = None
keep_submission_context = False
mappings = None
varname = None
map_fields_by_varname = False
attach_to_history = False
def migrate(self):
changed = super().migrate()
if getattr(self, 'keep_user', False) is True:
self.user_association_mode = 'keep-user'
delattr(self, 'keep_user')
changed = True
return changed
def _resolve_formdef_slug(self, formdef_slug):
if formdef_slug:
try:
@ -268,15 +279,33 @@ class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
self.backoffice_submission == CreateFormdataWorkflowStatusItem.backoffice_submission
),
)
if 'keep_user' in parameters:
if 'user_association_mode' in parameters:
form.add(
CheckboxWidget,
'%skeep_user' % prefix,
title=_('Keep user'),
value=self.keep_user,
advanced=(self.keep_user == CreateFormdataWorkflowStatusItem.keep_user),
RadiobuttonsWidget,
'%suser_association_mode' % prefix,
title=_(self.user_association_option_label),
options=[
(None, _('None'), 'none'),
('keep-user', _('Keep Current User'), 'keep-user'),
('custom', _('Custom (user will come from template value)'), 'custom'),
],
value=self.user_association_mode,
attrs={'data-dynamic-display-parent': 'true'},
advanced=bool(self.user_association_mode),
)
if 'keep_submission_context' in parameters and self.keep_user:
if 'user_association_template' in parameters:
form.add(
ComputedExpressionWidget,
'%suser_association_template' % prefix,
title=_('Template for user association (via email or NameID)'),
value=self.user_association_template,
attrs={
'data-dynamic-display-child-of': '%suser_association_mode' % prefix,
'data-dynamic-display-value': 'custom',
},
advanced=bool(self.user_association_mode),
)
if 'keep_submission_context' in parameters:
form.add(
CheckboxWidget,
'%skeep_submission_context' % prefix,
@ -377,7 +406,8 @@ class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
'map_fields_by_varname',
'mappings',
'backoffice_submission',
'keep_user',
'user_association_mode',
'user_association_template',
'keep_submission_context',
'varname',
'attach_to_history',
@ -397,10 +427,34 @@ class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
new_formdata = formdef.data_class()()
new_formdata.receipt_time = time.localtime()
if self.keep_user:
if self.user_association_mode == 'keep-user':
new_formdata.user_id = formdata.user_id
elif self.user_association_mode == 'custom' and self.user_association_template:
with get_publisher().complex_data():
try:
value = self.compute(
self.user_association_template,
formdata=formdata,
raises=True,
allow_complex=True,
status_item=self,
)
except Exception:
# already logged by self.compute
value = None
else:
value = get_publisher().get_cached_complex_data(value)
if self.keep_submission_context and self.keep_user:
from wcs.variables import LazyUser
if isinstance(value, LazyUser):
value = value._user
if isinstance(value, get_publisher().user_class):
new_formdata.user = value
else:
new_formdata.user = get_publisher().user_class.lookup_by_string(value)
if self.keep_submission_context:
new_formdata.submission_context = formdata.submission_context or {}
new_formdata.submission_channel = formdata.submission_channel
new_formdata.submission_agent_id = formdata.submission_agent_id