Source code for pyams_content.component.illustration

#
# 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'

from persistent import Persistent
from pyramid.events import subscriber
from pyramid.threadlocal import get_current_registry
from zope.container.contained import Contained
from zope.interface import alsoProvides
from zope.lifecycleevent import ObjectAddedEvent
from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectModifiedEvent
from zope.location.interfaces import ISublocations
from zope.schema.fieldproperty import FieldProperty
from zope.traversing.interfaces import ITraversable

from pyams_content.component.illustration.interfaces import BASIC_ILLUSTRATION_KEY, IBasicIllustration, \
    IBasicIllustrationTarget, IIllustration, IIllustrationTarget, IIllustrationTargetBase, ILLUSTRATION_KEY, \
    ILLUSTRATION_RENDERERS, ILinkIllustration, ILinkIllustrationTarget, LINK_ILLUSTRATION_KEY
from pyams_content.features.checker import BaseContentChecker
from pyams_content.features.checker.interfaces import IContentChecker, MISSING_LANG_VALUE, MISSING_VALUE
from pyams_content.features.renderer import RenderedContentMixin, RenderersVocabulary
from pyams_file.interfaces import IFileInfo, IImage, IResponsiveImage
from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
from pyams_i18n.property import I18nFileProperty
from pyams_utils.adapter import ContextAdapter, adapter_config, get_annotation_adapter
from pyams_utils.factory import factory_config
from pyams_utils.registry import get_global_registry, get_utility, query_utility
from pyams_utils.request import check_request
from pyams_utils.traversing import get_parent
from pyams_utils.vocabulary import vocabulary_config

from pyams_content import _


[docs]@factory_config(provided=IBasicIllustration) class BasicIllustration(Persistent, Contained): """Illustration persistent class""" _data = I18nFileProperty(IBasicIllustration['data']) title = FieldProperty(IBasicIllustration['title']) alt_title = FieldProperty(IBasicIllustration['alt_title']) author = FieldProperty(IBasicIllustration['author']) @property def data(self): return self._data @data.setter def data(self, value): self._data = value for data in (self._data or {}).values(): if IImage.providedBy(data): alsoProvides(data, IResponsiveImage)
[docs] def has_data(self): if not self._data: return False for data in self._data.values(): if bool(data): return True return False
[docs]@factory_config(provided=IIllustration) class Illustration(RenderedContentMixin, BasicIllustration): """Illustration persistent class""" description = FieldProperty(IIllustration['description']) renderer = FieldProperty(IIllustration['renderer'])
[docs]@adapter_config(context=IBasicIllustrationTarget, provides=IIllustration) def basic_illustration_factory(context): """Basic illustration factory""" def illustration_callback(illustration): get_current_registry().notify(ObjectAddedEvent(illustration, context, illustration.__name__)) return get_annotation_adapter(context, BASIC_ILLUSTRATION_KEY, IBasicIllustration, name='++illustration++', callback=illustration_callback)
[docs]@adapter_config(context=IIllustrationTarget, provides=IIllustration) def illustration_factory(context): """Illustration factory""" def illustration_callback(illustration): get_current_registry().notify(ObjectAddedEvent(illustration, context, illustration.__name__)) return get_annotation_adapter(context, ILLUSTRATION_KEY, IIllustration, name='++illustration++', callback=illustration_callback)
[docs]def update_illustration_properties(illustration): """Update missing file properties""" request = check_request() i18n = query_utility(INegotiator) if i18n is not None: lang = i18n.server_language data = II18n(illustration).get_attribute('data', lang, request) if data: info = IFileInfo(data) info.title = II18n(illustration).get_attribute('title', lang, request) info.description = II18n(illustration).get_attribute('alt_title', lang, request)
[docs]@subscriber(IObjectAddedEvent, context_selector=IBasicIllustration) def handle_added_illustration(event): """Handle added illustration""" illustration = event.object update_illustration_properties(illustration)
[docs]@subscriber(IObjectModifiedEvent, context_selector=IBasicIllustration) def handle_modified_illustration(event): """Handle modified illustration""" illustration = event.object update_illustration_properties(illustration)
[docs]@adapter_config(name='illustration', context=IIllustrationTargetBase, provides=ITraversable) class IllustrationNamespace(ContextAdapter): """++illustration++ namespace adapter"""
[docs] def traverse(self, name, furtherpath=None): registry = get_global_registry() return registry.queryAdapter(self.context, IIllustration, name=name)
[docs]@adapter_config(name='illustration', context=IIllustrationTargetBase, provides=ISublocations) class IllustrationSublocations(ContextAdapter): """Illustration sub-locations adapter"""
[docs] def sublocations(self): registry = get_global_registry() for name, adapter in registry.getAdapters((self,), IBasicIllustration): yield adapter
[docs]@adapter_config(context=IIllustration, provides=IContentChecker) class IllustrationContentChecker(BaseContentChecker): """Illustration content checker""" label = _("Illustration") weight = 40
[docs] def inner_check(self, request): output = [] translate = request.localizer.translate manager = get_parent(self.context, II18nManager) if manager is not None: langs = manager.get_languages() else: negotiator = get_utility(INegotiator) langs = (negotiator.server_language,) missing_value = translate(MISSING_VALUE) missing_lang_value = translate(MISSING_LANG_VALUE) i18n = II18n(self.context) has_data = False for attr in ('title', 'alt_title', 'description'): for lang in langs: data = i18n.get_attribute('data', lang, request) if not data: continue has_data = True value = i18n.get_attribute(attr, lang, request) if not value: if len(langs) == 1: output.append(missing_value.format(field=translate(IIllustration[attr].title))) else: output.append(missing_lang_value.format(field=translate(IIllustration[attr].title), lang=lang)) if has_data: for attr in ('author',): value = getattr(self.context, attr) if not value: output.append(missing_value.format(field=translate(IIllustration[attr].title))) return output
[docs]@adapter_config(name='illustration', context=IIllustrationTarget, provides=IContentChecker) def illustration_target_content_checker(context): """Illustration target content checker""" illustration = IIllustration(context, None) if illustration is not None: return IContentChecker(illustration)
[docs]@vocabulary_config(name=ILLUSTRATION_RENDERERS) class IllustrationRendererVocabulary(RenderersVocabulary): """Illustration renderers vocabulary""" content_interface = IIllustration
# # Custom image file to illustration adapter #
[docs]@adapter_config(context=IImage, provides=IBasicIllustration) class VirtualIllustration(object): """Virtual illustration based on image file""" title = None alt_title = None author = None def __init__(self, source): self.source = source @property def data(self): return self.source
[docs] def has_data(self): return bool(self.source)