194 lines
6.8 KiB
Python
194 lines
6.8 KiB
Python
# w.c.s. - web application for online forms
|
|
# Copyright (C) 2005-2023 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 datetime
|
|
import time
|
|
import xml.etree.ElementTree as ET
|
|
|
|
from django.utils.encoding import force_str
|
|
from django.utils.html import urlize
|
|
from quixote import get_publisher
|
|
from quixote.html import htmltext
|
|
|
|
from wcs import data_sources
|
|
from wcs.qommon import _
|
|
from wcs.qommon.form import (
|
|
AutocompleteStringWidget,
|
|
HiddenWidget,
|
|
StringWidget,
|
|
ValidationWidget,
|
|
WcsExtraStringWidget,
|
|
)
|
|
from wcs.qommon.misc import date_format, strftime
|
|
from wcs.qommon.ods import NS as OD_NS
|
|
from wcs.qommon.ods import clean_text as od_clean_text
|
|
|
|
from .base import WidgetField, register_field_class
|
|
|
|
|
|
class StringField(WidgetField):
|
|
key = 'string'
|
|
description = _('Text (line)')
|
|
|
|
widget_class = WcsExtraStringWidget
|
|
size = None
|
|
maxlength = None
|
|
extra_attributes = ['size', 'maxlength']
|
|
validation = {}
|
|
data_source = {}
|
|
keep_raw_value = False
|
|
|
|
def perform_more_widget_changes(self, form, kwargs, edit=True):
|
|
if self.data_source:
|
|
data_source = data_sources.get_object(self.data_source)
|
|
if data_source.can_jsonp():
|
|
kwargs['url'] = data_source.get_jsonp_url()
|
|
self.widget_class = AutocompleteStringWidget
|
|
|
|
@property
|
|
def use_live_server_validation(self):
|
|
if self.validation and self.validation['type']:
|
|
return bool(ValidationWidget.validation_methods.get(self.validation['type']))
|
|
return False
|
|
|
|
def fill_admin_form(self, form):
|
|
WidgetField.fill_admin_form(self, form)
|
|
if self.size:
|
|
form.add(
|
|
StringWidget,
|
|
'size',
|
|
title=_('Line length'),
|
|
hint=_(
|
|
'Deprecated option, it is advised to use CSS classes '
|
|
'to size the fields in a manner compatible with all devices.'
|
|
),
|
|
value=self.size,
|
|
)
|
|
else:
|
|
form.add(HiddenWidget, 'size', value=None)
|
|
form.add(
|
|
ValidationWidget,
|
|
'validation',
|
|
title=_('Validation'),
|
|
value=self.validation,
|
|
advanced=True,
|
|
)
|
|
form.add(
|
|
StringWidget,
|
|
'maxlength',
|
|
title=_('Maximum number of characters'),
|
|
value=self.maxlength,
|
|
advanced=True,
|
|
)
|
|
form.add(
|
|
data_sources.DataSourceSelectionWidget,
|
|
'data_source',
|
|
value=self.data_source,
|
|
title=_('Data Source'),
|
|
hint=_('This will allow autocompletion from an external source.'),
|
|
disallowed_source_types={'geojson'},
|
|
advanced=True,
|
|
required=False,
|
|
)
|
|
|
|
def get_admin_attributes(self):
|
|
return WidgetField.get_admin_attributes(self) + [
|
|
'size',
|
|
'validation',
|
|
'data_source',
|
|
'anonymise',
|
|
'maxlength',
|
|
]
|
|
|
|
def get_view_value(self, value, **kwargs):
|
|
value = value or ''
|
|
if isinstance(value, str) and (value.startswith('http://') or value.startswith('https://')):
|
|
charset = get_publisher().site_charset
|
|
value = force_str(value, charset)
|
|
return htmltext(force_str(urlize(value, nofollow=True, autoescape=True)))
|
|
return str(value)
|
|
|
|
def get_opendocument_node_value(self, value, formdata=None, **kwargs):
|
|
if value.startswith('http://') or value.startswith('https://'):
|
|
node = ET.Element('{%s}a' % OD_NS['text'])
|
|
node.attrib['{%s}href' % OD_NS['xlink']] = value
|
|
else:
|
|
node = ET.Element('{%s}span' % OD_NS['text'])
|
|
node.text = od_clean_text(force_str(value))
|
|
return node
|
|
|
|
def get_rst_view_value(self, value, indent=''):
|
|
return indent + str(value or '')
|
|
|
|
def convert_value_from_str(self, value):
|
|
return value
|
|
|
|
def convert_value_to_str(self, value):
|
|
if value is None:
|
|
return None
|
|
if isinstance(value, (time.struct_time, datetime.date)):
|
|
return strftime(date_format(), value)
|
|
return str(value)
|
|
|
|
@classmethod
|
|
def convert_value_from_anything(cls, value):
|
|
if value is None:
|
|
return None
|
|
return str(value)
|
|
|
|
def get_fts_value(self, data, **kwargs):
|
|
value = super().get_fts_value(data, **kwargs)
|
|
if value and self.validation and self.validation['type']:
|
|
validation_method = ValidationWidget.validation_methods.get(self.validation['type'])
|
|
if validation_method and validation_method.get('normalize_for_fts'):
|
|
# index both original and normalized value
|
|
# in the case of phone numbers this makes sure the "international/E164"
|
|
# format (ex: +33199001234) is indexed.
|
|
value = '%s %s' % (value, validation_method.get('normalize_for_fts')(value))
|
|
return value
|
|
|
|
def migrate(self):
|
|
changed = super().migrate()
|
|
if isinstance(self.validation, str): # 2019-08-10
|
|
self.validation = {'type': 'regex', 'value': self.validation}
|
|
changed = True
|
|
return changed
|
|
|
|
def init_with_xml(self, elem, charset, include_id=False, snapshot=False):
|
|
super().init_with_xml(elem, charset, include_id=include_id)
|
|
self.migrate()
|
|
|
|
def get_validation_parameter_view_value(self, widget):
|
|
if not self.validation:
|
|
return
|
|
validation_type = self.validation['type']
|
|
validation_types = {x: y['title'] for x, y in ValidationWidget.validation_methods.items()}
|
|
if validation_type in ('regex', 'django'):
|
|
validation_value = self.validation.get('value')
|
|
if not validation_value:
|
|
return
|
|
return '%s - %s' % (validation_types.get(validation_type), validation_value)
|
|
return str(validation_types.get(validation_type))
|
|
|
|
def i18n_scan(self, base_location):
|
|
location = '%s%s/' % (base_location, self.id)
|
|
yield from super().i18n_scan(base_location)
|
|
if self.validation and self.validation.get('error_message'):
|
|
yield location, None, self.validation.get('error_message')
|
|
|
|
|
|
register_field_class(StringField)
|