253 lines
9.4 KiB
Python
253 lines
9.4 KiB
Python
# w.c.s. - web application for online forms
|
|
# Copyright (C) 2005-2013 Entr'ouvert
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import collections
|
|
import xml.etree.ElementTree as ET
|
|
|
|
from quixote import get_publisher
|
|
from quixote.html import htmltext
|
|
|
|
from wcs.roles import get_user_roles
|
|
from wcs.workflows import WorkflowStatusItem, XmlSerialisable, get_role_name, register_item_class
|
|
|
|
from ..qommon import _
|
|
from ..qommon.form import (
|
|
CompositeWidget,
|
|
RadiobuttonsWidget,
|
|
SingleSelectWidget,
|
|
SingleSelectWidgetWithOther,
|
|
StringWidget,
|
|
WidgetListAsTable,
|
|
)
|
|
from ..qommon.template import Template
|
|
|
|
|
|
class AutomaticDispatchRowWidget(CompositeWidget):
|
|
def __init__(self, name, value=None, **kwargs):
|
|
CompositeWidget.__init__(self, name, value, **kwargs)
|
|
if not value:
|
|
value = {}
|
|
self.add(StringWidget, name='value', title=_('Value'), value=value.get('value'), **kwargs)
|
|
self.add(
|
|
SingleSelectWidget,
|
|
name='role_id',
|
|
title=_('Role'),
|
|
value=value.get('role_id'),
|
|
options=[(None, '----', None)] + get_user_roles(),
|
|
)
|
|
|
|
def _parse(self, request):
|
|
if self.get('value') or self.get('role_id'):
|
|
self.value = {'value': self.get('value'), 'role_id': self.get('role_id')}
|
|
else:
|
|
self.value = None
|
|
|
|
|
|
class AutomaticDispatchTableWidget(WidgetListAsTable):
|
|
readonly = False
|
|
|
|
def __init__(self, name, **kwargs):
|
|
super().__init__(name, element_type=AutomaticDispatchRowWidget, **kwargs)
|
|
|
|
|
|
class RuleNode(XmlSerialisable):
|
|
node_name = 'rule'
|
|
|
|
def __init__(self, rule=None):
|
|
rule = rule or {}
|
|
self.role_id = rule.get('role_id')
|
|
self.value = rule.get('value')
|
|
|
|
def as_dict(self):
|
|
return {'role_id': self.role_id, 'value': self.value}
|
|
|
|
def get_parameters(self):
|
|
return ('role_id', 'value')
|
|
|
|
def role_id_export_to_xml(self, item, charset, include_id=False):
|
|
self._role_export_to_xml('role_id', item, charset, include_id=include_id)
|
|
|
|
def role_id_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
|
|
self._role_init_with_xml('role_id', elem, charset, include_id=include_id, snapshot=snapshot)
|
|
|
|
|
|
class DispatchWorkflowStatusItem(WorkflowStatusItem):
|
|
description = _('Function/Role Linking')
|
|
key = 'dispatch'
|
|
category = 'formdata-action'
|
|
|
|
role_id = None
|
|
role_key = None
|
|
dispatch_type = 'manual'
|
|
variable = None
|
|
rules = None
|
|
|
|
def get_parameters(self):
|
|
return ('role_key', 'dispatch_type', 'role_id', 'variable', 'rules', 'condition')
|
|
|
|
def role_id_export_to_xml(self, item, charset, include_id=False):
|
|
self._role_export_to_xml('role_id', item, charset, include_id=include_id)
|
|
|
|
def role_id_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
|
|
self._role_init_with_xml('role_id', elem, charset, include_id=include_id, snapshot=snapshot)
|
|
|
|
def rules_export_to_xml(self, item, charset, include_id=False):
|
|
if self.dispatch_type != 'automatic' or not self.rules:
|
|
return
|
|
|
|
rules_node = ET.SubElement(item, 'rules')
|
|
for rule in self.rules:
|
|
rules_node.append(RuleNode(rule).export_to_xml(charset=charset, include_id=include_id))
|
|
|
|
return rules_node
|
|
|
|
def rules_init_with_xml(self, elem, charset, include_id=False, snapshot=False):
|
|
rules = []
|
|
if elem is None:
|
|
return
|
|
for rule_xml_node in elem.findall('rule'):
|
|
rule_node = RuleNode()
|
|
rule_node.init_with_xml(rule_xml_node, charset, include_id=include_id, snapshot=snapshot)
|
|
rules.append(rule_node.as_dict())
|
|
if rules:
|
|
self.rules = rules
|
|
|
|
def get_line_details(self):
|
|
if self.role_key:
|
|
function_label = self.parent.parent.roles.get(self.role_key, '?')
|
|
return function_label
|
|
return None
|
|
|
|
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None, **kwargs):
|
|
super().add_parameters_widgets(form, parameters, prefix=prefix, formdef=formdef, **kwargs)
|
|
if 'role_key' in parameters:
|
|
if not self.parent.parent.roles:
|
|
self.parent.parent.roles = {}
|
|
form.add(
|
|
SingleSelectWidget,
|
|
'%srole_key' % prefix,
|
|
title=_('Function to Set'),
|
|
value=self.role_key,
|
|
options=[(None, '----', None)] + [(x, y, x) for x, y in self.parent.parent.roles.items()],
|
|
)
|
|
dispatch_types = collections.OrderedDict([('manual', _('Simple')), ('automatic', _('Multiple'))])
|
|
if 'dispatch_type' in parameters:
|
|
form.add(
|
|
RadiobuttonsWidget,
|
|
'%sdispatch_type' % prefix,
|
|
title=_('Dispatch Type'),
|
|
options=list(dispatch_types.items()),
|
|
value=self.dispatch_type,
|
|
required=True,
|
|
attrs={'data-dynamic-display-parent': 'true'},
|
|
extra_css_class='widget-inline-radio',
|
|
)
|
|
if 'role_id' in parameters:
|
|
form.add(
|
|
SingleSelectWidgetWithOther,
|
|
'%srole_id' % prefix,
|
|
title=_('Role'),
|
|
value=str(self.role_id) if self.role_id else None,
|
|
options=[(None, '---', None)] + get_user_roles(),
|
|
attrs={
|
|
'data-dynamic-display-child-of': '%sdispatch_type' % prefix,
|
|
'data-dynamic-display-value': dispatch_types.get('manual'),
|
|
},
|
|
)
|
|
if 'variable' in parameters:
|
|
form.add(
|
|
StringWidget,
|
|
'%svariable' % prefix,
|
|
title=_('Variable'),
|
|
value=self.variable,
|
|
attrs={
|
|
'data-dynamic-display-child-of': '%sdispatch_type' % prefix,
|
|
'data-dynamic-display-value': dispatch_types.get('automatic'),
|
|
},
|
|
)
|
|
if 'rules' in parameters:
|
|
form.add(
|
|
AutomaticDispatchTableWidget,
|
|
'%srules' % prefix,
|
|
title=_('Rules'),
|
|
value=self.rules,
|
|
attrs={
|
|
'data-dynamic-display-child-of': '%sdispatch_type' % prefix,
|
|
'data-dynamic-display-value': dispatch_types.get('automatic'),
|
|
},
|
|
)
|
|
|
|
def get_role_id_parameter_view_value(self):
|
|
return get_role_name(self.role_id)
|
|
|
|
def get_rules_parameter_view_value(self):
|
|
result = []
|
|
for rule in self.rules or []:
|
|
result.append(
|
|
htmltext('<li>%s → %s</li>') % (rule.get('value'), get_role_name(rule.get('role_id')))
|
|
)
|
|
return htmltext('<ul class="rules">%s</ul>') % htmltext('').join(result)
|
|
|
|
def perform(self, formdata):
|
|
if not formdata.workflow_roles:
|
|
formdata.workflow_roles = {}
|
|
|
|
new_role_id = None
|
|
|
|
if self.dispatch_type == 'manual' or not self.dispatch_type:
|
|
if not (self.role_id and self.role_key):
|
|
return
|
|
new_role_id = self.get_computed_role_id(self.role_id)
|
|
if not new_role_id:
|
|
get_publisher().record_error(
|
|
_('error in dispatch, missing role (%s)') % self.role_id, formdata=formdata
|
|
)
|
|
elif self.dispatch_type == 'automatic':
|
|
if not (self.role_key and self.variable and self.rules):
|
|
return
|
|
variable_values = []
|
|
if Template.is_template_string(self.variable):
|
|
variable_values = [self.compute(self.variable, formdata=formdata, status_item=self)]
|
|
else:
|
|
# legacy, self.variable as a straight variable name
|
|
variables = get_publisher().substitutions.get_context_variables()
|
|
# convert the given value to a few different types, to allow more
|
|
# diversity in matching.
|
|
variable_values = [variables.get(self.variable)]
|
|
if not variable_values[0]:
|
|
variable_values.append(None)
|
|
try:
|
|
variable_values.append(int(variable_values[0]))
|
|
except (ValueError, TypeError):
|
|
pass
|
|
|
|
for rule in self.rules:
|
|
if rule.get('value') in variable_values:
|
|
new_role_id = rule.get('role_id')
|
|
break
|
|
|
|
if new_role_id:
|
|
if not get_publisher().role_class.get(new_role_id, ignore_errors=True):
|
|
get_publisher().record_error(
|
|
_('error in dispatch, missing role (%s)') % new_role_id, formdata=formdata
|
|
)
|
|
else:
|
|
formdata.workflow_roles[self.role_key] = str(new_role_id)
|
|
formdata.store()
|
|
|
|
|
|
register_item_class(DispatchWorkflowStatusItem)
|