Commit 26ec8c7d authored by Romain Courteaud's avatar Romain Courteaud

ProxyField can now surcharged attributes from the template field.

The extra_context attribute is now be deprecated.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@10993 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 91afdf4f
...@@ -57,6 +57,7 @@ def get_value(self, id, **kw): ...@@ -57,6 +57,7 @@ def get_value(self, id, **kw):
self.tales = {} self.tales = {}
tales_expr = self.tales.get(id, "") tales_expr = self.tales.get(id, "")
# tales_expr = self.get_tales(id)
if tales_expr: if tales_expr:
REQUEST = get_request() REQUEST = get_request()
form = self.aq_parent # XXX (JPS) form for default is wrong apparently in listbox - double check form = self.aq_parent # XXX (JPS) form for default is wrong apparently in listbox - double check
...@@ -109,6 +110,7 @@ def get_value(self, id, **kw): ...@@ -109,6 +110,7 @@ def get_value(self, id, **kw):
self.overrides = {} self.overrides = {}
override = self.overrides.get(id, "") override = self.overrides.get(id, "")
# override = self.get_override(id)
if override: if override:
# call wrapped method to get answer # call wrapped method to get answer
value = override.__of__(self)() value = override.__of__(self)()
......
...@@ -62,7 +62,8 @@ class MultiRelationStringFieldWidget(Widget.LinesTextAreaWidget, ...@@ -62,7 +62,8 @@ class MultiRelationStringFieldWidget(Widget.LinesTextAreaWidget,
'container_getter_id', 'catalog_index', 'container_getter_id', 'catalog_index',
'relation_setter_id', 'columns', 'sort', 'relation_setter_id', 'columns', 'sort',
'parameter_list','list_method', 'parameter_list','list_method',
'first_item', 'items', 'size', 'extra_item'] 'first_item', 'items', 'size', 'extra_item',
]
property_names = Widget.LinesTextAreaWidget.property_names + \ property_names = Widget.LinesTextAreaWidget.property_names + \
Widget.TextWidget.property_names + \ Widget.TextWidget.property_names + \
...@@ -548,6 +549,7 @@ class MultiRelationStringFieldValidator(Validator.LinesValidator): ...@@ -548,6 +549,7 @@ class MultiRelationStringFieldValidator(Validator.LinesValidator):
portal_type = relation_uid[len(NEW_CONTENT_PREFIX):] portal_type = relation_uid[len(NEW_CONTENT_PREFIX):]
translated_portal_type = Message(domain='erp5_ui', translated_portal_type = Message(domain='erp5_ui',
message=portal_type) message=portal_type)
# XXX Replace New by Add
message = Message( message = Message(
domain='erp5_ui', message='New ${portal_type}', domain='erp5_ui', message='New ${portal_type}',
mapping={'portal_type': translated_portal_type}) mapping={'portal_type': translated_portal_type})
...@@ -766,15 +768,13 @@ class MultiRelationStringField(ZMIField): ...@@ -766,15 +768,13 @@ class MultiRelationStringField(ZMIField):
widget = MultiRelationStringFieldWidgetInstance widget = MultiRelationStringFieldWidgetInstance
validator = MultiRelationStringFieldValidatorInstance validator = MultiRelationStringFieldValidatorInstance
security.declareProtected('Access contents information', 'get_value') security.declareProtected('Access contents information', 'get_orig_value')
def get_value(self, id, **kw): def get_orig_value(self, id):
""" """
Get value for id. Get value for id; don't do any override calculation.
Optionally pass keyword arguments that get passed to TALES
expression.
""" """
if id in ('is_relation_field', 'is_multi_relation_field'): if id in ('is_relation_field', 'is_multi_relation_field'):
result = 1 result = 1
else: else:
result = ZMIField.get_value(self, id, **kw) result = ZMIField.get_orig_value(self, id)
return result return result
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
# #
# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved. # Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets <jp@nexedi.com> # Jean-Paul Smets <jp@nexedi.com>
# Romain Courteaud <romain@nexedi.com>
# #
# WARNING: This program as such is intended to be used by professional # WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential # programmers who take the whole responsability of assessing all potential
...@@ -43,8 +44,9 @@ from Products.PythonScripts.standard import url_quote_plus ...@@ -43,8 +44,9 @@ from Products.PythonScripts.standard import url_quote_plus
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
import string import string
from zLOG import LOG, WARNING from zLOG import LOG, WARNING, DEBUG
from Acquisition import aq_base, aq_inner, aq_acquire, aq_chain from Acquisition import aq_base, aq_inner, aq_acquire, aq_chain
from Globals import DTMLFile
class ProxyWidget(Widget.Widget): class ProxyWidget(Widget.Widget):
""" """
...@@ -81,7 +83,7 @@ class ProxyWidget(Widget.Widget): ...@@ -81,7 +83,7 @@ class ProxyWidget(Widget.Widget):
# Remove it as soon as possible # Remove it as soon as possible
extra_context = fields.ListTextAreaField( extra_context = fields.ListTextAreaField(
'extra_context', 'extra_context',
title='Extra Context', title='Extra Context (deprecated)',
description='Additional context variables.', description='Additional context variables.',
default=(), default=(),
required=0) required=0)
...@@ -102,7 +104,7 @@ class ProxyWidget(Widget.Widget): ...@@ -102,7 +104,7 @@ class ProxyWidget(Widget.Widget):
proxy_field = field.getTemplateField() proxy_field = field.getTemplateField()
if proxy_field is not None: if proxy_field is not None:
REQUEST = field.updateContext(REQUEST) REQUEST = field.updateContext(REQUEST)
result = proxy_field.widget.render(proxy_field, key, value, REQUEST) result = proxy_field.widget.render(field, key, value, REQUEST)
return result return result
def render_view(self, field, value): def render_view(self, field, value):
...@@ -112,7 +114,7 @@ class ProxyWidget(Widget.Widget): ...@@ -112,7 +114,7 @@ class ProxyWidget(Widget.Widget):
result = '' result = ''
proxy_field = field.getTemplateField() proxy_field = field.getTemplateField()
if proxy_field is not None: if proxy_field is not None:
result = proxy_field.widget.render_view(proxy_field, value) result = proxy_field.widget.render_view(field, value)
return result return result
class ProxyValidator(Validator.Validator): class ProxyValidator(Validator.Validator):
...@@ -125,7 +127,7 @@ class ProxyValidator(Validator.Validator): ...@@ -125,7 +127,7 @@ class ProxyValidator(Validator.Validator):
proxy_field = field.getTemplateField() proxy_field = field.getTemplateField()
REQUEST = field.updateContext(REQUEST) REQUEST = field.updateContext(REQUEST)
try: try:
result = proxy_field.validator.validate(proxy_field, key, REQUEST) result = proxy_field.validator.validate(field, key, REQUEST)
except ValidationError, error: except ValidationError, error:
error.field_id = field.id error.field_id = field.id
raise error raise error
...@@ -141,6 +143,184 @@ class ProxyField(ZMIField): ...@@ -141,6 +143,184 @@ class ProxyField(ZMIField):
widget = ProxyWidgetInstance widget = ProxyWidgetInstance
validator = ProxyValidatorInstance validator = ProxyValidatorInstance
# methods screen
security.declareProtected('View management screens',
'manage_main')
manage_main = DTMLFile('dtml/proxyFieldEdit', globals())
# tales screen
security.declareProtected('View management screens',
'manage_talesForm')
manage_talesForm = DTMLFile('dtml/proxyFieldTales', globals())
# proxy field list header
security.declareProtected('View management screens', 'proxyFieldListHeader')
proxyFieldListHeader = DTMLFile('dtml/proxyFieldListHeader', globals())
security.declareProtected('Change Formulator Forms', 'manage_edit')
def manage_edit(self, REQUEST):
"""
Surcharged values from proxied field.
"""
# Edit template field attributes
template_field = self.getRecursiveTemplateField()
if template_field is not None:
# Check the surcharged checkboxes
surcharge_list = []
for group in template_field.form.get_groups():
for field in template_field.form.get_fields_in_group(group):
field_id = field.id
checkbox_key = "surcharge_%s" % field_id
if not REQUEST.has_key(checkbox_key):
surcharge_list.append(field_id)
try:
# validate the form and get results
result = template_field.form.validate(REQUEST)
except ValidationError, err:
if REQUEST:
message = "Error: %s - %s" % (err.field.get_value('title'),
err.error_text)
return self.manage_main(self, REQUEST,
manage_tabs_message=message)
else:
raise
self._surcharged_edit(result, surcharge_list)
# Edit standards attributes
# XXX It is not possible to call ZMIField.manage_edit because
# it returns at the end...
# we need to had a parameter to the method
try:
# validate the form and get results
result = self.form.validate(REQUEST)
except ValidationError, err:
if REQUEST:
message = "Error: %s - %s" % (err.field.get_value('title'),
err.error_text)
return self.manage_main(self,REQUEST,
manage_tabs_message=message)
else:
raise
self._edit(result)
if REQUEST:
message="Content changed."
return self.manage_main(self, REQUEST,
manage_tabs_message=message)
def _surcharged_edit(self, result, surcharge_list):
# first check for any changes
values = self.values
# if we are in unicode mode, convert result to unicode
# acquire get_unicode_mode and get_stored_encoding from form..
if self.get_unicode_mode():
new_result = {}
for key, value in result.items():
if type(value) == type(''):
# in unicode mode, Formulator UI always uses UTF-8
value = unicode(value, 'UTF-8')
new_result[key] = value
result = new_result
changed = []
for key, value in result.items():
# XXX Remove old values
values.pop(key, None)
# store keys for which we want to notify change
if not values.has_key(key) or values[key] != value:
changed.append(key)
proxied_field = self.getTemplateField()
for key, value in result.items():
if key not in surcharge_list:
result.pop(key)
# now do actual update of values
values.update(result)
self.values = values
self.delegated_list = surcharge_list
# finally notify field of all changed values if necessary
for key in changed:
method_name = "on_value_%s_changed" % key
if hasattr(self, method_name):
getattr(self, method_name)(values[key])
security.declareProtected('Change Formulator Forms', 'manage_tales')
def manage_tales(self, REQUEST):
"""
Surcharged talesfrom proxied field.
"""
template_field = self.getRecursiveTemplateField()
if template_field is not None:
# Check the surcharged checkboxes
surcharge_list = []
for group in template_field.tales_form.get_groups():
for field in template_field.tales_form.get_fields_in_group(group):
field_id = field.id
checkbox_key = "surcharge_%s" % field_id
if not REQUEST.has_key(checkbox_key):
surcharge_list.append(field_id)
try:
# validate the form and get results
result = template_field.tales_form.validate(REQUEST)
except ValidationError, err:
if REQUEST:
message = "Error: %s - %s" % (err.field.get_value('title'),
err.error_text)
return self.manage_talesForm(self, REQUEST,
manage_tabs_message=message)
else:
raise
self._surcharged_tales(result, surcharge_list)
try:
# validate the form and get results
result = self.tales_form.validate(REQUEST)
except ValidationError, err:
if REQUEST:
message = "Error: %s - %s" % (err.field.get_value('title'),
err.error_text)
return self.manage_talesForm(self,REQUEST,
manage_tabs_message=message)
else:
raise
self._edit_tales(result)
if REQUEST:
message="Content changed."
return self.manage_talesForm(self, REQUEST,
manage_tabs_message=message)
def _surcharged_tales(self, result, surcharge_list):
# first check for any changes
tales = self.tales
changed = []
for key, value in result.items():
# XXX Remove old values
tales.pop(key, None)
proxied_field = self.getTemplateField()
for key, value in result.items():
if key not in surcharge_list:
result.pop(key)
# now do actual update of values
tales.update(result)
self.tales = tales
self.delegated_list = surcharge_list
def getTemplateField(self): def getTemplateField(self):
""" """
Return template field of the proxy field. Return template field of the proxy field.
...@@ -158,17 +338,88 @@ class ProxyField(ZMIField): ...@@ -158,17 +338,88 @@ class ProxyField(ZMIField):
proxy_field = None proxy_field = None
return proxy_field return proxy_field
def render_htmlgrid(self, value=None, REQUEST=None, key=None): def getRecursiveTemplateField(self):
""" """
render_htmlgrid returns a list of tuple (title, html render) Return template field of the proxy field.
We will use title generated by the widget. This result must not be a ProxyField.
""" """
result = () template_field = self.getTemplateField()
proxy_field = self.getTemplateField() if template_field.__class__ == ProxyField:
if proxy_field is not None : return template_field.getTemplateField()
result = proxy_field.render_htmlgrid(key=key, value=value, else:
REQUEST=REQUEST) return template_field
return result
security.declareProtected('Access contents information',
'is_delegated')
def is_delegated(self, id):
"""
Return true if we get the value from the proxied field.
No, if we surcharged the value on the proxy field.
"""
# Update old proxied field
try:
if id in self.delegated_list:
return False
else:
return True
except AttributeError:
# Update old proxied field
self.delegated_list = []
return True
security.declareProtected('Access contents information',
'get_recursive_orig_value')
def get_recursive_orig_value(self, id, include=1):
"""
Get value for id recursively.
"""
if include and \
((id in self.widget.property_names) or \
not self.is_delegated(id)):
return self.get_orig_value(id)
else:
proxied_field = self.getTemplateField()
if proxied_field.__class__ == ProxyField:
return proxied_field.get_recursive_orig_value(id)
else:
return proxied_field.get_orig_value(id)
security.declareProtected('View management screens', 'get_recursive_tales')
def get_recursive_tales(self, id, include=1):
"""
Get tales expression method for id.
"""
if include and \
((id in self.widget.property_names) or \
not self.is_delegated(id)):
return self.get_tales(id)
else:
proxied_field = self.getTemplateField()
if proxied_field.__class__ == ProxyField:
return proxied_field.get_recursive_tales(id)
else:
return proxied_field.get_tales(id)
# XXX Not implemented
security.declareProtected('View management screens', 'get_recursive_override')
def get_recursive_override(self, id):
"""
Get override method for id (not wrapped).
"""
return self.overrides.get(id, "")
security.declareProtected('View management screens', 'get_error_message')
def get_error_message(self, name):
"""
"""
try:
return self.message_values[name]
except KeyError:
proxied_field = self.getTemplateField()
if proxied_field is not None:
return proxied_field.get_error_message(name)
else:
return ZMIField.get_error_message(self, name)
def updateContext(self, REQUEST): def updateContext(self, REQUEST):
""" """
...@@ -182,14 +433,19 @@ class ProxyField(ZMIField): ...@@ -182,14 +433,19 @@ class ProxyField(ZMIField):
security.declareProtected('Edit target', 'manage_edit_target') security.declareProtected('Edit target', 'manage_edit_target')
def manage_edit_target(self, REQUEST): def manage_edit_target(self, REQUEST):
""" Edit target field of this proxy """
Edit target field of this proxy
""" """
proxy_field = self.getTemplateField() proxy_field = self.getTemplateField()
if proxy_field: if proxy_field:
url='/'.join((self.absolute_url(),self.get_value('form_id'),self.get_value('field_id'),'manage_main')) url='/'.join((self.absolute_url(),
self.get_value('form_id'),
self.get_value('field_id'),
'manage_main'))
REQUEST.RESPONSE.redirect(url) REQUEST.RESPONSE.redirect(url)
else: else:
# FIXME: should show some error message ("form_id and field_id don't define a valid template") # FIXME: should show some error message
# ("form_id and field_id don't define a valid template")
pass pass
security.declareProtected('Access contents information', 'get_value') security.declareProtected('Access contents information', 'get_value')
...@@ -200,7 +456,8 @@ class ProxyField(ZMIField): ...@@ -200,7 +456,8 @@ class ProxyField(ZMIField):
expression. expression.
""" """
result = None result = None
if id in self.widget.property_names: if (id in self.widget.property_names) or \
(not self.is_delegated(id)):
result = ZMIField.get_value(self, id, **kw) result = ZMIField.get_value(self, id, **kw)
else: else:
proxy_field = self.getTemplateField() proxy_field = self.getTemplateField()
...@@ -214,7 +471,8 @@ class ProxyField(ZMIField): ...@@ -214,7 +471,8 @@ class ProxyField(ZMIField):
Return true if the field defines such a value. Return true if the field defines such a value.
""" """
result = None result = None
if id in self.widget.property_names: if (id in self.widget.property_names) or \
(not self.is_delegated(id)):
result = ZMIField.has_value(self, id) result = ZMIField.has_value(self, id)
else: else:
proxy_field = self.getTemplateField() proxy_field = self.getTemplateField()
......
...@@ -150,17 +150,15 @@ class RelationStringField(ZMIField): ...@@ -150,17 +150,15 @@ class RelationStringField(ZMIField):
widget = RelationStringFieldWidgetInstance widget = RelationStringFieldWidgetInstance
validator = RelationStringFieldValidatorInstance validator = RelationStringFieldValidatorInstance
security.declareProtected('Access contents information', 'get_value') security.declareProtected('Access contents information', 'get_orig_value')
def get_value(self, id, **kw): def get_orig_value(self, id):
""" """
Get value for id. Get value for id; don't do any override calculation.
Optionally pass keyword arguments that get passed to TALES
expression.
""" """
if id == 'is_relation_field': if id == 'is_relation_field':
result = 1 result = 1
elif id == 'is_multi_relation_field': elif id == 'is_multi_relation_field':
result = 0 result = 0
else: else:
result = ZMIField.get_value(self, id, **kw) result = ZMIField.get_orig_value(self, id)
return result return result
<dtml-var manage_page_header>
<dtml-let help_product="'Formulator'" help_topic=meta_type>
<dtml-var manage_tabs>
</dtml-let>
<p class="form-help">
Surcharge <dtml-var meta_type> properties here.
</p>
<form action="manage_edit" method="POST">
<table cellspacing="0" cellpadding="2" border="0">
<!-- First, display ProxyField properties -->
<!-- see: Formulator/dtml/fieldEdit.dtml -->
<dtml-in "form.get_groups()">
<dtml-let group=sequence-item fields="form.get_fields_in_group(group)">
<dtml-if fields>
<tr>
<td colspan="3" class="form-title">
Proxy Widget properties
</td>
</tr>
<dtml-var fieldListHeader>
<dtml-let current_field="this()">
<dtml-in fields>
<dtml-let field=sequence-item field_id="field.id"
value="current_field.get_orig_value(field_id)"
override="current_field.get_override(field_id)"
tales="current_field.get_tales(field_id)">
<tr>
<td align="left" valign="top">
<div class="form-label">
<dtml-if "tales or override">[</dtml-if><dtml-var "field.title()"><dtml-if "field.has_value('required') and field.get_value('required')">*</dtml-if><dtml-if "tales or override">]</dtml-if>
</div>
</td>
<td align="left" valign="top">
<dtml-var "field.render(value)">
</td>
<td><div class="form-element">
<dtml-var "field.meta_type">
</div></td>
</tr>
</dtml-let>
</dtml-in>
</dtml-let>
</dtml-if>
</dtml-let>
</dtml-in>
<!-- Then, display Template Field properties -->
<dtml-let proxy_field="this()"
current_field="proxy_field.getRecursiveTemplateField()">
<dtml-if "current_field is not None">
<dtml-let form="current_field.form">
<dtml-in "form.get_groups()">
<dtml-let group=sequence-item fields="form.get_fields_in_group(group)">
<dtml-if fields>
<tr>
<td colspan="3" class="form-title">
<dtml-var "_.string.capitalize(group)"> properties
</td>
</tr>
<dtml-var proxyFieldListHeader>
<dtml-in fields>
<dtml-let field=sequence-item field_id="field.id"
value="proxy_field.get_recursive_orig_value(field_id)"
override="proxy_field.get_recursive_override(field_id)"
tales="proxy_field.get_recursive_tales(field_id)">
<tr>
<td align="left" valign="top">
<dtml-let checkbox_key="'surcharge_%s' % field_id" >
<dtml-if "proxy_field.is_delegated(field_id)">
<input type="checkbox"
name="<dtml-var checkbox_key>"
checked="checked" />
<dtml-else >
<input type="checkbox"
name="<dtml-var checkbox_key>" />
</dtml-if >
</dtml-let >
</td>
<td align="left" valign="top">
<div class="form-label">
<dtml-if "tales or override">[
</dtml-if>
<dtml-var "field.title()">
<dtml-if "field.has_value('required') and field.get_value('required')">*
</dtml-if>
<dtml-if "tales or override">]
</dtml-if>
</div>
</td>
<td align="left" valign="top">
<dtml-var "field.render(value)">
</td>
<td><div class="form-element">
<dtml-var "field.meta_type">
</div></td>
</tr>
</dtml-let>
</dtml-in>
</dtml-if>
</dtml-let>
</dtml-in>
</dtml-let>
</dtml-if>
</dtml-let>
<tr>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value="Save Changes" />
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
<tr class="list-header">
<td align="left" valign="top">
<div class="form-label">
Delegated
</div>
</td>
<td align="left" valign="top">
<div class="form-label">
Name
</div>
</td>
<td align="left" valign="top">
<div class="form-label">
Value
</div>
</td>
<td align="left" valign="top">
<div class="form-label">
Field
</div>
</td>
</tr>
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<p class="form-help">
Edit <dtml-var meta_type> method TALES expressions here.
<dtml-if "not isTALESAvailable()"><br>
<span style="color: #FF0000;">
Zope Page Templates and therefore TALES is not installed.
This tab can therefore not be used.
</span>
</dtml-if>
</p>
<form action="manage_tales" method="POST">
<table cellspacing="0" cellpadding="2" border="0">
<dtml-in "override_form.get_groups()">
<dtml-let group=sequence-item fields="tales_form.get_fields_in_group(group)">
<dtml-if fields>
<tr>
<td colspan="3" class="form-title">
Proxy Widget properties
</td>
</tr>
<dtml-var fieldListHeader>
<dtml-let current_field="this()">
<dtml-in fields>
<dtml-let field=sequence-item field_id="field.id"
value="current_field.get_tales(field.id)">
<tr>
<td align="left" valign="top">
<div class="form-label">
<dtml-var "field.title()">
</div>
</td>
<td align="left" valign="top">
<dtml-var "field.render(value)">
</td>
<td><div class="form-element">
<dtml-var "current_field.form.get_field(field.id).meta_type">
</div></td>
</tr>
</dtml-let>
</dtml-in>
</dtml-let>
</dtml-if>
</dtml-let>
</dtml-in>
<!-- XXX Loop until find not a proxy field -->
<dtml-let proxy_field="this()"
current_field="proxy_field.getRecursiveTemplateField()">
<dtml-if "current_field is not None">
<dtml-let form="current_field.tales_form">
<dtml-in "form.get_groups()">
<dtml-let group=sequence-item fields="form.get_fields_in_group(group)">
<dtml-if fields>
<tr>
<td colspan="3" class="form-title">
<dtml-var "_.string.capitalize(group)"> properties
</td>
</tr>
<dtml-var proxyFieldListHeader>
<dtml-in fields>
<dtml-let field=sequence-item field_id="field.id"
value="proxy_field.get_recursive_tales(field_id)">
<tr>
<td align="left" valign="top">
<dtml-let checkbox_key="'surcharge_%s' % field_id" >
<dtml-if "proxy_field.is_delegated(field_id)">
<input type="checkbox"
name="<dtml-var checkbox_key>"
checked="checked" />
<dtml-else >
<input type="checkbox"
name="<dtml-var checkbox_key>" />
</dtml-if >
</dtml-let >
</td>
<td align="left" valign="top">
<div class="form-label">
<dtml-var "field.title()">
</div>
</td>
<td align="left" valign="top">
<dtml-var "field.render(value)">
</td>
<td><div class="form-element">
<dtml-var "current_field.form.get_field(field.id).meta_type">
</div></td>
</tr>
</dtml-let>
</dtml-in>
</dtml-if>
</dtml-let>
</dtml-in>
</dtml-let>
</dtml-if>
</dtml-let>
<tr>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value="Save Changes" />
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment