#
# 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.
#
__docformat__ = 'restructuredtext'
# import standard library
# import interfaces
from pyams_file.interfaces import DELETED_FILE, IFile, IFileInfo, IFileFieldContainer
from zope.schema.interfaces import IField
# import packages
from pyams_file.file import FileFactory
from pyams_file.property import FILE_CONTAINER_ATTRIBUTES
from pyams_utils.adapter import get_annotation_adapter
from pyramid.threadlocal import get_current_registry
from z3c.form.interfaces import NOT_CHANGED
from zope.interface import alsoProvides
from zope.lifecycleevent import ObjectCreatedEvent, ObjectRemovedEvent, ObjectAddedEvent
from zope.location import locate
_marker = object()
[docs]class I18nFileProperty(object):
"""I18n property class used to handle files"""
def __init__(self, field, name=None, klass=None, **args):
if not IField.providedBy(field):
raise ValueError("Provided field must implement IField interface...")
if name is None:
name = field.__name__
self.__field = field
self.__name = name
self.__klass = klass
self.__args = args
def __get__(self, instance, klass):
if instance is None:
return self
value = instance.__dict__.get(self.__name, _marker)
if value is _marker:
field = self.__field.bind(instance)
value = getattr(field, 'default', _marker)
if value is _marker:
raise AttributeError(self.__name)
return value
def __set__(self, instance, value):
registry = get_current_registry()
for lang in value:
lang_value = value[lang]
if (lang_value is DELETED_FILE) or (lang_value is NOT_CHANGED):
continue
elif lang_value is not None:
filename = None
# file upload data converter returns a tuple containing
# filename and buffered IO stream extracted from FieldStorage...
if isinstance(lang_value, tuple):
filename, lang_value = lang_value
# initialize file through factory
if not IFile.providedBy(lang_value):
factory = self.__klass or FileFactory
file = factory(lang_value, **self.__args)
registry.notify(ObjectCreatedEvent(file))
if not file.get_size():
lang_value.seek(0) # because factory may read until end of file...
file.data = lang_value
lang_value = file
if filename is not None:
info = IFileInfo(lang_value)
if info is not None:
info.filename = filename
value[lang] = lang_value
field = self.__field.bind(instance)
field.validate(value)
if field.readonly and instance.__dict__.has_key(self.__name):
raise ValueError(self.__name, "Field is readonly")
old_value = instance.__dict__.get(self.__name, _marker)
if old_value != value:
# check for previous value
if old_value is _marker:
old_value = {}
for lang in value:
new_lang_value = value.get(lang)
if new_lang_value is NOT_CHANGED:
continue
old_lang_value = old_value.get(lang, _marker)
if (old_lang_value is not _marker) and (old_lang_value is not None):
registry.notify(ObjectRemovedEvent(old_lang_value))
if new_lang_value is DELETED_FILE:
if self.__name in instance.__dict__:
del old_value[lang]
else:
# set name of new value
name = '++i18n++{0}:{1}'.format(self.__name, lang)
if new_lang_value is not None:
locate(new_lang_value, instance, name)
old_value[lang] = new_lang_value
# store file attributes of instance
if not IFileFieldContainer.providedBy(instance):
alsoProvides(instance, IFileFieldContainer)
attributes = get_annotation_adapter(instance, FILE_CONTAINER_ATTRIBUTES, set,
notify=False, locate=False)
attributes.add('{0}::{1}'.format(self.__name, lang))
registry.notify(ObjectAddedEvent(new_lang_value, instance, name))
instance.__dict__[self.__name] = old_value