# HG changeset patch # User Thierry Florac # Date 1531404006 -7200 # Node ID 3f9dda94c3548ae876f8732fd0aad452b7fcffe4 # Parent 2342203445f1e2f42545a56b36bf1022cd6625f2 Updated internal references management diff -r 2342203445f1 -r 3f9dda94c354 src/pyams_sequence/interfaces/__init__.py --- a/src/pyams_sequence/interfaces/__init__.py Mon Jul 09 11:42:45 2018 +0200 +++ b/src/pyams_sequence/interfaces/__init__.py Thu Jul 12 16:00:06 2018 +0200 @@ -16,11 +16,11 @@ # import standard library # import interfaces -from pyams_sequence.schema import InternalReference, InternalReferencesList +from pyams_sequence.schema import InternalReferenceField, InternalReferencesListField from zope.annotation.interfaces import IAttributeAnnotatable # import packages -from zope.interface import Interface +from zope.interface import Interface, Attribute from zope.schema import TextLine, Int from pyams_sequence import _ @@ -99,16 +99,24 @@ class IInternalReference(Interface): """Internal link interface""" - reference = InternalReference(title=_("Internal reference"), - description=_("Internal link target reference. You can search a reference using " + reference = InternalReferenceField(title=_("Internal reference"), + description=_("Internal link target reference. You can search a reference using " "'+' followed by internal number, of by entering text matching " "content title."), - required=True) + required=True) + + target = Attribute("Internal reference target") + + def get_target(self, state=None): + """Get target from internal reference""" class IInternalReferencesList(Interface): """Internal references list""" - references = InternalReferencesList(title=_("Internal references"), - description=_("List of internal references"), - required=False) \ No newline at end of file + references = InternalReferencesListField(title=_("Internal references"), + description=_("List of internal references"), + required=False) + + def get_targets(self, state=None): + """Get iterator over targets from internal references""" diff -r 2342203445f1 -r 3f9dda94c354 src/pyams_sequence/reference.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_sequence/reference.py Thu Jul 12 16:00:06 2018 +0200 @@ -0,0 +1,146 @@ +# +# Copyright (c) 2008-2018 Thierry Florac +# 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 hypatia.interfaces import ICatalog +from pyams_i18n.interfaces import II18n +from pyams_sequence.interfaces import IInternalReference, ISequentialIdInfo, ISequentialIntIds +from pyams_skin.layer import IPyAMSUserLayer +from zope.lifecycleevent.interfaces import IObjectModifiedEvent + +try: + from pyams_workflow.interfaces import IWorkflow, IWorkflowVersions, IWorkflowVersion, \ + IWorkflowState, IWorkflowManagedContent, IWorkflowPublicationInfo, IWorkflowTransitionEvent +except ImportError: + handle_workflow = False +else: + handle_workflow = True + +# import packages +from hypatia.catalog import CatalogQuery +from hypatia.query import Eq +from pyams_catalog.query import CatalogResultSet +from pyams_utils.registry import get_utility +from pyams_utils.request import check_request +from pyramid.events import subscriber + + +@subscriber(IObjectModifiedEvent, context_selector=IInternalReference) +def handle_modified_reference(event): + """Handle modified reference""" + for description in event.descriptions: + for attribute in description.attributes: + if attribute == 'reference': + del event.object.target + return + + +if handle_workflow: + # Target is handled as a volatile property to keep references in memory + # Reference must be updated when a content is published or retired while being in version + # greater than 1 + @subscriber(IWorkflowTransitionEvent) + def handle_workflow_transition(event): + """Handle workflow transition to update internal references""" + workflow_state = IWorkflowState(event.object, None) + if (workflow_state is None) or (workflow_state.version_id == 1): + return # don't update references on first version + sequence_info = ISequentialIdInfo(event.object, None) + if sequence_info is not None: + catalog = get_utility(ICatalog) + params = Eq(catalog['link_reference'], sequence_info.hex_oid) + for result in CatalogResultSet(CatalogQuery(catalog).query(params)): + del result.target + + +def get_last_version(content): + """Check for last available version""" + if handle_workflow and (IWorkflowVersion.providedBy(content) or IWorkflowManagedContent.providedBy(content)): + content = IWorkflowVersions(content).get_last_versions()[0] + if ISequentialIdInfo(content, None) is not None: + return content + else: + return None + + +def get_visible_version(content): + """Check for visible version""" + if handle_workflow: + if IWorkflowVersion.providedBy(content) or IWorkflowManagedContent.providedBy(content): + workflow = IWorkflow(content) + versions = IWorkflowVersions(content).get_versions(workflow.published_states, sort=True) + if versions: + return versions[-1] + publication_info = IWorkflowPublicationInfo(content, None) + if publication_info is not None: + if publication_info.is_visible(): + return content + else: + return None + return content + + +def get_version_in_state(content, state): + """Check for versions in given status""" + if handle_workflow and (IWorkflowVersion.providedBy(content) or IWorkflowManagedContent.providedBy(content)): + versions = IWorkflowVersions(content).get_versions(state) + if versions: + content = versions[0] + if ISequentialIdInfo(content, None) is not None: + return content + else: + return None + + +def get_sequence_dict(version, attribute='title', request=None): + """Get OID and label matching given version""" + sequence = get_utility(ISequentialIntIds) + info = ISequentialIdInfo(version) + return {'id': info.hex_oid, + 'text': '{0} ({1})'.format(II18n(version).query_attribute(attribute, request=request), + sequence.get_short_oid(info.oid))} + + +def get_sequence_target(oid, state): + """Get content matching given OID""" + sequence = get_utility(ISequentialIntIds) + content = sequence.query_object_from_oid(oid) + if handle_workflow and (IWorkflowVersion.providedBy(content) or IWorkflowManagedContent.providedBy(content)): + versions = IWorkflowVersions(content).get_versions(state, sort=True) + if versions: + content = versions[0] + return content + + +def get_reference_target(reference, state=None, request=None): + """Get target of given reference OID""" + catalog = get_utility(ICatalog) + params = Eq(catalog['oid'], reference) + results = list(CatalogResultSet(CatalogQuery(catalog).query(params))) + if results: + if state: + results = list(filter(lambda x: get_version_in_state(x, state), results)) + else: + if request is None: + request = check_request() + if IPyAMSUserLayer.providedBy(request): + getter = get_visible_version + else: + getter = get_last_version + results = list(map(getter, results)) + if results: + return results[0] diff -r 2342203445f1 -r 3f9dda94c354 src/pyams_sequence/rpc/json/__init__.py --- a/src/pyams_sequence/rpc/json/__init__.py Mon Jul 09 11:42:45 2018 +0200 +++ b/src/pyams_sequence/rpc/json/__init__.py Thu Jul 12 16:00:06 2018 +0200 @@ -24,7 +24,7 @@ from hypatia.catalog import CatalogQuery from hypatia.query import Eq, Contains from pyams_catalog.query import CatalogResultSet -from pyams_sequence.utility import get_last_version, get_sequence_dict +from pyams_sequence.reference import get_last_version, get_sequence_dict from pyams_utils.list import unique from pyams_utils.registry import get_utility from pyramid_rpc.jsonrpc import jsonrpc_method diff -r 2342203445f1 -r 3f9dda94c354 src/pyams_sequence/schema.py --- a/src/pyams_sequence/schema.py Mon Jul 09 11:42:45 2018 +0200 +++ b/src/pyams_sequence/schema.py Thu Jul 12 16:00:06 2018 +0200 @@ -23,33 +23,33 @@ from zope.schema import TextLine, List -class IInternalReference(ITextLine): +class IInternalReferenceField(ITextLine): """Internal reference field interface""" content_type = Attribute("Requested target content type") -@implementer(IInternalReference) -class InternalReference(TextLine): +@implementer(IInternalReferenceField) +class InternalReferenceField(TextLine): """Internal reference field""" def __init__(self, content_type=None, *args, **kwargs): - super(InternalReference, self).__init__(*args, **kwargs) + super(InternalReferenceField, self).__init__(*args, **kwargs) self.content_type = content_type -class IInternalReferencesList(IList): +class IInternalReferencesListField(IList): """Internal references list field interface""" content_type = Attribute("Requested target content type") -@implementer(IInternalReferencesList) -class InternalReferencesList(List): +@implementer(IInternalReferencesListField) +class InternalReferencesListField(List): """Internal references list field""" def __init__(self, content_type=None, value_type=None, unique=False, *args, **kwargs): - super(InternalReferencesList, self).__init__(value_type=TextLine(), - unique=True, - *args, **kwargs) + super(InternalReferencesListField, self).__init__(value_type=TextLine(), + unique=True, + *args, **kwargs) self.content_type = content_type diff -r 2342203445f1 -r 3f9dda94c354 src/pyams_sequence/utility.py --- a/src/pyams_sequence/utility.py Mon Jul 09 11:42:45 2018 +0200 +++ b/src/pyams_sequence/utility.py Thu Jul 12 16:00:06 2018 +0200 @@ -16,27 +16,11 @@ # import standard library # import interfaces -from hypatia.interfaces import ICatalog -from pyams_i18n.interfaces import II18n from pyams_sequence.interfaces import ISequentialIntIds, ISequentialIdTarget, ISequentialIdInfo -from pyams_skin.layer import IPyAMSUserLayer - -try: - from pyams_workflow.interfaces import IWorkflowVersions, IWorkflowVersion, IWorkflowManagedContent, IWorkflow, \ - IWorkflowPublicationInfo -except ImportError: - handle_workflow = False -else: - handle_workflow = True - from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectRemovedEvent # import packages -from hypatia.catalog import CatalogQuery -from hypatia.query import Eq, Any -from pyams_catalog.query import CatalogResultSet -from pyams_utils.registry import query_utility, get_utility -from pyams_utils.request import check_request +from pyams_utils.registry import query_utility from pyramid.events import subscriber from zope.interface import implementer, Invalid from zope.intid import IntIds @@ -45,85 +29,6 @@ from pyams_sequence import _ -def get_last_version(content): - """Check for last available version""" - if handle_workflow and (IWorkflowVersion.providedBy(content) or IWorkflowManagedContent.providedBy(content)): - content = IWorkflowVersions(content).get_last_versions()[0] - if ISequentialIdInfo(content, None) is not None: - return content - else: - return None - - -def get_visible_version(content): - """Check for visible version""" - if handle_workflow: - if IWorkflowVersion.providedBy(content) or IWorkflowManagedContent.providedBy(content): - workflow = IWorkflow(content) - versions = IWorkflowVersions(content).get_versions(workflow.published_states, sort=True) - if versions: - return versions[-1] - publication_info = IWorkflowPublicationInfo(content, None) - if publication_info is not None: - if publication_info.is_visible(): - return content - else: - return None - return content - - -def get_version_in_state(content, state): - """Check for versions in given status""" - if handle_workflow and (IWorkflowVersion.providedBy(content) or IWorkflowManagedContent.providedBy(content)): - versions = IWorkflowVersions(content).get_versions(state) - if versions: - content = versions[0] - if ISequentialIdInfo(content, None) is not None: - return content - else: - return None - - -def get_sequence_dict(version, attribute='title', request=None): - """Get OID and label matching given version""" - sequence = get_utility(ISequentialIntIds) - info = ISequentialIdInfo(version) - return {'id': info.hex_oid, - 'text': '{0} ({1})'.format(II18n(version).query_attribute(attribute, request=request), - sequence.get_short_oid(info.oid))} - - -def get_sequence_target(oid, state): - """Get content matching given OID""" - sequence = get_utility(ISequentialIntIds) - content = sequence.query_object_from_oid(oid) - if handle_workflow and (IWorkflowVersion.providedBy(content) or IWorkflowManagedContent.providedBy(content)): - versions = IWorkflowVersions(content).get_versions(state, sort=True) - if versions: - content = versions[0] - return content - - -def get_reference_target(reference, state=None, request=None): - """Get target of given reference OID""" - catalog = get_utility(ICatalog) - params = Eq(catalog['oid'], reference) - results = list(CatalogResultSet(CatalogQuery(catalog).query(params))) - if results: - if state: - results = list(filter(lambda x: get_version_in_state(x, state), results)) - else: - if request is None: - request = check_request() - if IPyAMSUserLayer.providedBy(request): - getter = get_visible_version - else: - getter = get_last_version - results = list(map(getter, results)) - if results: - return results[0] - - @implementer(ISequentialIntIds) class SequentialIntIds(IntIds): """Sequential IntIds utility""" diff -r 2342203445f1 -r 3f9dda94c354 src/pyams_sequence/widget/__init__.py --- a/src/pyams_sequence/widget/__init__.py Mon Jul 09 11:42:45 2018 +0200 +++ b/src/pyams_sequence/widget/__init__.py Thu Jul 12 16:00:06 2018 +0200 @@ -21,7 +21,7 @@ from pyams_form.interfaces.form import IFormLayer from pyams_i18n.interfaces import II18n from pyams_sequence.interfaces import ISequentialIntIds, ISequentialIdInfo -from pyams_sequence.schema import IInternalReference, IInternalReferencesList +from pyams_sequence.schema import IInternalReferenceField, IInternalReferencesListField from pyams_sequence.widget.interfaces import IInternalReferenceWidget, IInternalReferencesListWidget try: @@ -38,7 +38,7 @@ from hypatia.query import Eq, Any from pyams_catalog.query import CatalogResultSet from pyams_form.widget import widgettemplate_config -from pyams_sequence.utility import get_last_version +from pyams_sequence.reference import get_last_version from pyams_utils.adapter import adapter_config from pyams_utils.registry import get_utility from z3c.form.browser.widget import HTMLInputWidget @@ -80,7 +80,7 @@ return json.dumps({self.value: translate(_("missing reference: {0}")).format(self.value)}) -@adapter_config(context=(IInternalReference, IFormLayer), provides=IFieldWidget) +@adapter_config(context=(IInternalReferenceField, IFormLayer), provides=IFieldWidget) def InternalReferenceFieldWidget(field, request): """Internal reference field widget factory""" return FieldWidget(field, InternalReferenceWidget(request)) @@ -90,7 +90,7 @@ # Internal references list widget # -@adapter_config(context=(IInternalReferencesList, IInternalReferencesListWidget), provides=IDataConverter) +@adapter_config(context=(IInternalReferencesListField, IInternalReferencesListWidget), provides=IDataConverter) class InternalReferencesListDataConverter(BaseDataConverter): """Internal references list data converter""" @@ -135,7 +135,7 @@ return json.dumps(results) -@adapter_config(context=(IInternalReferencesList, IFormLayer), provides=IFieldWidget) +@adapter_config(context=(IInternalReferencesListField, IFormLayer), provides=IFieldWidget) def InternalReferencesListFieldWidget(field, request): """Internal references list field widget factory""" return FieldWidget(field, InternalReferencesListWidget(request))