--- 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"""
--- /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 <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 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]
--- 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
--- 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
--- 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"""
--- 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))