Source code for pyams_utils.schema

#
# Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#

"""PyAMS_utils.schema module

This module is used to define custom schema fields
"""

import re
import string

from persistent.list import PersistentList as PersistentListType
from persistent.mapping import PersistentMapping
from zope.interface import Interface, implementer
from zope.schema import Choice, Decimal, Dict, List, Password, Text, TextLine, Tuple, \
    ValidationError
from zope.schema.interfaces import IChoice, IDecimal, IDict, IList, IPassword, IText, ITextLine, \
    ITuple

from pyams_utils.interfaces import ENCODINGS_VOCABULARY_NAME, TIMEZONES_VOCABULARY_NAME


__docformat__ = 'restructuredtext'

from pyams_utils import _


#
# Persistent list field
#

[docs]class IPersistentListField(IList): """Persistent list field marker interface"""
[docs]@implementer(IPersistentListField) class PersistentListField(List): """Persistent list field""" _type = PersistentListType
# # Persistent mapping field #
[docs]class IPersistentDictField(IDict): """Persistent mapping field marker interface"""
[docs]@implementer(IPersistentDictField) class PersistentDictField(Dict): """Persistent mapping field""" _type = PersistentMapping
# # Encoded password field #
[docs]class IEncodedPasswordField(IPassword): """Encoded password field interface"""
[docs]@implementer(IEncodedPasswordField) class EncodedPasswordField(Password): """Encoded password field""" _type = None
[docs] def fromUnicode(self, str): # pylint: disable=redefined-builtin return str
[docs] def constraint(self, value): # pylint: disable=method-hidden return True
# # HTML field #
[docs]class IHTMLField(IText): """HTML field interface"""
[docs]@implementer(IHTMLField) class HTMLField(Text): """HTML field"""
# # JSON dict value field #
[docs]class IJSONDictField(IDict): """JSON dict value field interface"""
[docs]class IJSONDictFieldsGetter(Interface): """Adapter interface used to get JSON value fields list""" def get_fields(self, data): """Returns an iterator made of tuples Each tuple may ocntain field name, field label and field value """
[docs]@implementer(IJSONDictField) class JSONDictField(Dict): """JSON dict value field""" def __init__(self, key_type=None, value_type=None, **kw): super(JSONDictField, self).__init__(key_type=TextLine(), value_type=TextLine(), **kw)
# # Color field #
[docs]class IColorField(ITextLine): """Marker interface for color fields"""
[docs]@implementer(IColorField) class ColorField(TextLine): """Color field""" def __init__(self, *args, **kw): super(ColorField, self).__init__(max_length=6, *args, **kw) def _validate(self, value): if len(value) not in (3, 6): raise ValidationError(_("Color length must be 3 or 6 characters")) for val in value: if val not in string.hexdigits: raise ValidationError(_("Color value must contain only valid hexadecimal color " "codes (numbers or letters between 'A' end 'F')")) super(ColorField, self)._validate(value)
# # Pointed decimal field #
[docs]class IDottedDecimalField(IDecimal): """Marker interface for dotted decimal fields"""
[docs]@implementer(IDottedDecimalField) class DottedDecimalField(Decimal): """Dotted decimal field"""
# # Dates range field #
[docs]class IDatesRangeField(ITuple): """Marker interface for dates range fields"""
[docs]@implementer(IDatesRangeField) class DatesRangeField(Tuple): """Dates range field""" def __init__(self, value_type=None, unique=False, **kw): super(DatesRangeField, self).__init__(value_type=None, unique=False, min_length=2, max_length=2, **kw)
# # TextLine list field #
[docs]class ITextLineListField(IList): """Marker interface for textline list field"""
[docs]@implementer(ITextLineListField) class TextLineListField(List): """TextLine list field""" def __init__(self, value_type=None, unique=False, **kw): super(TextLineListField, self).__init__(value_type=TextLine(), unique=True, **kw)
# # Mail address field #
[docs]class IMailAddressField(ITextLine): """Marker interface for mail address field"""
EMAIL_REGEX = re.compile(r"^[^ @]+@[^ @]+\.[^ @]+$")
[docs]class InvalidEmail(ValidationError): """Invalid email validation error""" __doc__ = _( "Email address must be entered as « name@domain.name », without '<' and '>' characters")
[docs]@implementer(IMailAddressField) class MailAddressField(TextLine): """Mail address field""" def _validate(self, value): super(MailAddressField, self)._validate(value) if not EMAIL_REGEX.match(value): raise InvalidEmail(value)
# # Timezone field #
[docs]class ITimezoneField(IChoice): """Marker interface for timezone field"""
[docs]@implementer(ITimezoneField) class TimezoneField(Choice): """Timezone choice field""" def __init__(self, **kw): if 'vocabulary' in kw: kw.pop('vocabulary') if 'default' not in kw: kw['default'] = u'GMT' super(TimezoneField, self).__init__(vocabulary=TIMEZONES_VOCABULARY_NAME, **kw)
# # Encoding field #
[docs]class IEncodingField(IChoice): # pylint: disable=too-many-ancestors """Encoding field interface"""
[docs]@implementer(IEncodingField) class EncodingField(Choice): """Encoding schema field""" def __init__(self, vocabulary=ENCODINGS_VOCABULARY_NAME, **kw): if 'values' in kw: del kw['values'] if 'source' in kw: del kw['source'] kw['vocabulary'] = vocabulary super(EncodingField, self).__init__(**kw)