--- a/src/pyams_content/component/links/__init__.py Wed Sep 25 09:47:48 2019 +0200
+++ b/src/pyams_content/component/links/__init__.py Wed Sep 25 09:50:05 2019 +0200
@@ -10,20 +10,20 @@
# FOR A PARTICULAR PURPOSE.
#
-__docformat__ = 'restructuredtext'
-
from html import escape
-from pyramid.encode import url_quote
-from zope.interface import implementer
+from pyramid.encode import url_quote, urlencode
+from pyramid.events import subscriber
+from zope.interface import alsoProvides, implementer, directlyProvidedBy, noLongerProvides
+from zope.lifecycleevent import IObjectAddedEvent, IObjectModifiedEvent
from zope.schema.fieldproperty import FieldProperty
from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
-from pyams_content import _
from pyams_content.component.association import AssociationItem
-from pyams_content.component.association.interfaces import IAssociationContainer, IAssociationContainerTarget, \
- IAssociationInfo
-from pyams_content.component.links.interfaces import IBaseLink, IExternalLink, IInternalLink, IMailtoLink
+from pyams_content.component.association.interfaces import IAssociationContainer, \
+ IAssociationContainerTarget, IAssociationInfo
+from pyams_content.component.links.interfaces import IBaseLink, IExternalLink, IInternalLink, \
+ IInternalLinkCustomInfoTarget, IMailtoLink, IInternalLinkCustomInfo, ICustomInternalLinkTarget
from pyams_content.features.checker import BaseContentChecker
from pyams_content.features.checker.interfaces import ERROR_VALUE, IContentChecker
from pyams_content.interfaces import IBaseContent
@@ -42,6 +42,11 @@
from pyams_workflow.interfaces import IWorkflow, IWorkflowPublicationInfo
+__docformat__ = 'restructuredtext'
+
+from pyams_content import _
+
+
#
# Links vocabulary
#
@@ -55,7 +60,8 @@
target = get_parent(context, IAssociationContainerTarget)
if target is not None:
terms = [SimpleTerm(link.__name__, title=IAssociationInfo(link).inner_title)
- for link in IAssociationContainer(target).values() if IBaseLink.providedBy(link)]
+ for link in IAssociationContainer(target).values() if
+ IBaseLink.providedBy(link)]
super(ContentLinksVocabulary, self).__init__(terms)
@@ -106,7 +112,7 @@
request = check_request()
translate = request.localizer.translate
return II18n(self.context).query_attribute('title', request) or \
- '({0})'.format(translate(self.context.icon_hint).lower())
+ '({0})'.format(translate(self.context.icon_hint).lower())
#
@@ -157,14 +163,48 @@
if target is not None:
if request is None:
request = check_request()
+ params = None
+ if IInternalLinkCustomInfoTarget.providedBy(target):
+ custom_info = IInternalLinkCustomInfo(self, None)
+ if custom_info is not None:
+ params = custom_info.get_url_params()
+ if params:
+ params = urlencode(params)
if self.force_canonical_url:
- return canonical_url(target, request, view_name)
+ return canonical_url(target, request, view_name, query=params)
else:
- return relative_url(target, request, view_name=view_name)
+ return relative_url(target, request, view_name=view_name, query=params)
else:
return ''
+@subscriber(IObjectAddedEvent, context_selector=IInternalLink)
+def handle_new_internal_link(event):
+ """Check if link target is providing custom info"""
+ link = event.object
+ target = link.target
+ if target is not None:
+ info = IInternalLinkCustomInfoTarget(target, None)
+ if info is not None:
+ alsoProvides(link, info.internal_link_marker_interface)
+
+
+@subscriber(IObjectModifiedEvent, context_selector=IInternalLink)
+def handle_updated_internal_link(event):
+ """Check when modified if new link target is providing custom info"""
+ link = event.object
+ # remove previous provided interfaces
+ ifaces = tuple([iface for iface in directlyProvidedBy(link)
+ if issubclass(iface, IInternalLinkCustomInfo)])
+ for iface in ifaces:
+ noLongerProvides(link, iface)
+ target = link.target
+ if target is not None:
+ info = IInternalLinkCustomInfoTarget(target, None)
+ if info is not None:
+ alsoProvides(link, info.internal_link_marker_interface)
+
+
@adapter_config(context=IInternalLink, provides=IAssociationInfo)
class InternalLinkAssociationInfoAdapter(BaseLinkInfoAdapter):
"""Internal link association info adapter"""
@@ -206,8 +246,10 @@
if workflow is not None:
target = self.context.get_target(state=workflow.published_states)
if target is None:
- output.append(translate(ERROR_VALUE).format(field=IInternalLink['reference'].title,
- message=translate(_("target is not published"))))
+ output.append(
+ translate(ERROR_VALUE).format(field=IInternalLink['reference'].title,
+ message=translate(
+ _("target is not published"))))
return output
--- a/src/pyams_content/component/links/zmi/__init__.py Wed Sep 25 09:47:48 2019 +0200
+++ b/src/pyams_content/component/links/zmi/__init__.py Wed Sep 25 09:50:05 2019 +0200
@@ -10,41 +10,50 @@
# FOR A PARTICULAR PURPOSE.
#
-__docformat__ = 'restructuredtext'
-
from z3c.form import field
from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
-from zope.interface import implementer
+from zope.interface import Interface, implementer
-from pyams_content import _
from pyams_content.component.association.interfaces import IAssociationContainer
-from pyams_content.component.association.zmi import AssociationItemAJAXAddForm, AssociationItemAJAXEditForm
+from pyams_content.component.association.zmi import AssociationItemAJAXAddForm, \
+ AssociationItemAJAXEditForm
from pyams_content.component.association.zmi.interfaces import IAssociationsView
-from pyams_content.component.links import ExternalLink, InternalLink, MailtoLink
-from pyams_content.component.links.interfaces import IExternalLink, IInternalLink, ILinkContainerTarget, IMailtoLink
+from pyams_content.component.links import ExternalLink, IInternalLinkCustomInfo, InternalLink, \
+ MailtoLink
+from pyams_content.component.links.interfaces import ICustomInternalLinkTarget, IExternalLink, \
+ IInternalLink, ILinkContainerTarget, IMailtoLink
from pyams_content.component.paragraph.zmi import get_json_paragraph_markers_refresh_event
from pyams_content.component.paragraph.zmi.container import ParagraphContainerCounterBase
-from pyams_content.component.paragraph.zmi.interfaces import IParagraphContainerTable, IParagraphTitleToolbar
+from pyams_content.component.paragraph.zmi.interfaces import IParagraphContainerTable, \
+ IParagraphTitleToolbar
from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
from pyams_content.reference.pictograms.zmi.widget import PictogramSelectFieldWidget
from pyams_form.form import ajax_config
+from pyams_form.interfaces.form import IInnerSubForm
from pyams_form.security import ProtectedFormObjectMixin
from pyams_pagelet.pagelet import pagelet_config
from pyams_skin.interfaces.viewlet import IToolbarAddingMenu
from pyams_skin.layer import IPyAMSLayer
from pyams_skin.viewlet.toolbar import ToolbarMenuItem
+from pyams_utils.adapter import adapter_config
from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
from pyams_viewlet.viewlet import viewlet_config
-from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm
+from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm, InnerAdminEditForm
from pyams_zmi.interfaces import IPropertiesEditForm
+__docformat__ = 'restructuredtext'
+
+from pyams_content import _
+
+
#
# Internal links views
#
-@viewlet_config(name='internal-links', context=ILinkContainerTarget, layer=IPyAMSLayer, view=IParagraphContainerTable,
- manager=IParagraphTitleToolbar, permission=VIEW_SYSTEM_PERMISSION, weight=10)
+@viewlet_config(name='internal-links', context=ILinkContainerTarget, layer=IPyAMSLayer,
+ view=IParagraphContainerTable, manager=IParagraphTitleToolbar,
+ permission=VIEW_SYSTEM_PERMISSION, weight=10)
class InternalLinksCounter(ParagraphContainerCounterBase):
"""Internal links count column"""
@@ -61,8 +70,8 @@
if IInternalLink.providedBy(file)])
-@viewlet_config(name='add-internal-link.menu', context=ILinkContainerTarget, view=IAssociationsView,
- layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=50)
+@viewlet_config(name='add-internal-link.menu', context=ILinkContainerTarget,
+ view=IAssociationsView, layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=50)
class InternalLinkAddMenu(ProtectedFormObjectMixin, ToolbarMenuItem):
"""Internal link add menu"""
@@ -83,7 +92,8 @@
legend = _("Add new internal link")
icon_css_class = 'fa fa-fw fa-external-link-square fa-rotate-90'
- fields = field.Fields(IInternalLink).select('reference', 'force_canonical_url', 'title', 'description',
+ fields = field.Fields(IInternalLink).select('reference', 'force_canonical_url', 'title',
+ 'description',
'pictogram_name')
fields['force_canonical_url'].widgetFactory = SingleCheckBoxFieldWidget
fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
@@ -99,12 +109,14 @@
def get_ajax_output(self, changes):
output = super(self.__class__, self).get_ajax_output(changes)
if output:
- output.setdefault('events', []).append(get_json_paragraph_markers_refresh_event(self.context, self.request,
- self, InternalLinksCounter))
+ output.setdefault('events', []).append(
+ get_json_paragraph_markers_refresh_event(self.context, self.request,
+ self, InternalLinksCounter))
return output
-@pagelet_config(name='properties.html', context=IInternalLink, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
+@pagelet_config(name='properties.html', context=IInternalLink, layer=IPyAMSLayer,
+ permission=VIEW_SYSTEM_PERMISSION)
@ajax_config(name='properties.json', context=IInternalLink, layer=IPyAMSLayer,
permission=MANAGE_CONTENT_PERMISSION, base=AssociationItemAJAXEditForm)
@implementer(IPropertiesEditForm)
@@ -117,7 +129,8 @@
icon_css_class = 'fa fa-fw fa-external-link-square fa-rotate-90'
dialog_class = 'modal-large'
- fields = field.Fields(IInternalLink).select('reference', 'force_canonical_url', 'title', 'description',
+ fields = field.Fields(IInternalLink).select('reference', 'force_canonical_url', 'title',
+ 'description',
'pictogram_name')
fields['force_canonical_url'].widgetFactory = SingleCheckBoxFieldWidget
fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
@@ -131,12 +144,42 @@
return super(self.__class__, self).get_ajax_output(changes)
+@adapter_config(name='custom',
+ context=(ICustomInternalLinkTarget, IPyAMSLayer, InternalLinkPropertiesEditForm),
+ provides=IInnerSubForm)
+class CustomInternalLinkPropertiesEditForm(InnerAdminEditForm):
+ """Custom internal link properties edit form"""
+
+ prefix = 'custom_properties.'
+
+ css_class = 'form-group'
+ padding_class = ''
+
+ legend = _("Custom target properties")
+ fieldset_class = 'bordered'
+
+ @property
+ def fields(self):
+ info = IInternalLinkCustomInfo(self.context, None)
+ if info is not None:
+ return field.Fields(info.properties_interface).omit('properties_interface')
+ return field.Fields(Interface)
+
+ weight = 1
+
+ def render(self):
+ if not self.fields:
+ return ''
+ return super(CustomInternalLinkPropertiesEditForm, self).render()
+
+
#
# External links views
#
-@viewlet_config(name='external-links', context=ILinkContainerTarget, layer=IPyAMSLayer, view=IParagraphContainerTable,
- manager=IParagraphTitleToolbar, permission=VIEW_SYSTEM_PERMISSION, weight=11)
+@viewlet_config(name='external-links', context=ILinkContainerTarget, layer=IPyAMSLayer,
+ view=IParagraphContainerTable, manager=IParagraphTitleToolbar,
+ permission=VIEW_SYSTEM_PERMISSION, weight=11)
class ExternalLinksCounter(ParagraphContainerCounterBase):
"""External links count column"""
@@ -153,8 +196,8 @@
if IExternalLink.providedBy(file)])
-@viewlet_config(name='add-external-link.menu', context=ILinkContainerTarget, view=IAssociationsView,
- layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=51)
+@viewlet_config(name='add-external-link.menu', context=ILinkContainerTarget,
+ view=IAssociationsView, layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=51)
class ExternalLinkAddMenu(ProtectedFormObjectMixin, ToolbarMenuItem):
"""External link add menu"""
@@ -175,7 +218,8 @@
legend = _("Add new external link")
icon_css_class = 'fa fa-fw fa-external-link'
- fields = field.Fields(IExternalLink).select('url', 'title', 'description', 'pictogram_name', 'language')
+ fields = field.Fields(IExternalLink).select('url', 'title', 'description', 'pictogram_name',
+ 'language')
fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
edit_permission = MANAGE_CONTENT_PERMISSION
@@ -189,12 +233,14 @@
def get_ajax_output(self, changes):
output = super(self.__class__, self).get_ajax_output(changes)
if output:
- output.setdefault('events', []).append(get_json_paragraph_markers_refresh_event(self.context, self.request,
- self, ExternalLinksCounter))
+ output.setdefault('events', []).append(
+ get_json_paragraph_markers_refresh_event(self.context, self.request,
+ self, ExternalLinksCounter))
return output
-@pagelet_config(name='properties.html', context=IExternalLink, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
+@pagelet_config(name='properties.html', context=IExternalLink, layer=IPyAMSLayer,
+ permission=VIEW_SYSTEM_PERMISSION)
@ajax_config(name='properties.json', context=IExternalLink, layer=IPyAMSLayer,
permission=MANAGE_CONTENT_PERMISSION, base=AssociationItemAJAXEditForm)
@implementer(IPropertiesEditForm)
@@ -207,7 +253,8 @@
icon_css_class = 'fa fa-fw fa-external-link'
dialog_class = 'modal-large'
- fields = field.Fields(IExternalLink).select('url', 'title', 'description', 'pictogram_name', 'language')
+ fields = field.Fields(IExternalLink).select('url', 'title', 'description', 'pictogram_name',
+ 'language')
fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
edit_permission = None # defined by IFormContextPermissionChecker adapter
@@ -223,8 +270,9 @@
# Mailto links views
#
-@viewlet_config(name='mailto-links', context=ILinkContainerTarget, layer=IPyAMSLayer, view=IParagraphContainerTable,
- manager=IParagraphTitleToolbar, permission=VIEW_SYSTEM_PERMISSION, weight=12)
+@viewlet_config(name='mailto-links', context=ILinkContainerTarget, layer=IPyAMSLayer,
+ view=IParagraphContainerTable, manager=IParagraphTitleToolbar,
+ permission=VIEW_SYSTEM_PERMISSION, weight=12)
class MailtoLinksCounter(ParagraphContainerCounterBase):
"""Mailto links count column"""
@@ -263,7 +311,8 @@
legend = _("Add new mailto link")
icon_css_class = 'fa fa-fw fa-envelope-o'
- fields = field.Fields(IMailtoLink).select('address', 'address_name', 'title', 'description', 'pictogram_name')
+ fields = field.Fields(IMailtoLink).select('address', 'address_name', 'title', 'description',
+ 'pictogram_name')
fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
edit_permission = MANAGE_CONTENT_PERMISSION
@@ -277,12 +326,14 @@
def get_ajax_output(self, changes):
output = super(self.__class__, self).get_ajax_output(changes)
if output:
- output.setdefault('events', []).append(get_json_paragraph_markers_refresh_event(self.context, self.request,
- self, MailtoLinksCounter))
+ output.setdefault('events', []).append(
+ get_json_paragraph_markers_refresh_event(self.context, self.request,
+ self, MailtoLinksCounter))
return output
-@pagelet_config(name='properties.html', context=IMailtoLink, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
+@pagelet_config(name='properties.html', context=IMailtoLink, layer=IPyAMSLayer,
+ permission=VIEW_SYSTEM_PERMISSION)
@ajax_config(name='properties.json', context=IMailtoLink, layer=IPyAMSLayer,
permission=MANAGE_CONTENT_PERMISSION, base=AssociationItemAJAXEditForm)
@implementer(IPropertiesEditForm)
@@ -294,7 +345,8 @@
legend = _("Edit mailto link properties")
icon_css_class = 'fa fa-fw fa-envelope-o'
- fields = field.Fields(IMailtoLink).select('address', 'address_name', 'title', 'description', 'pictogram_name')
+ fields = field.Fields(IMailtoLink).select('address', 'address_name', 'title', 'description',
+ 'pictogram_name')
fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
edit_permission = None # defined by IFormContextPermissionChecker adapter