--- a/src/pyams_content/shared/site/interfaces.py Fri Nov 23 13:23:22 2018 +0100
+++ b/src/pyams_content/shared/site/interfaces.py Fri Nov 23 13:25:51 2018 +0100
@@ -17,8 +17,8 @@
from zope.annotation.interfaces import IAttributeAnnotatable
from zope.container.constraints import containers, contains
from zope.container.interfaces import IContained, IContainer
-from zope.interface import Attribute, Interface
-from zope.schema import Bool, Choice, Text
+from zope.interface import Attribute, Interface, invariant
+from zope.schema import Bool, Choice, Text, URI
from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
from pyams_content.interfaces import IBaseContent
@@ -159,8 +159,8 @@
"""Workflow managed site topic interface"""
-class IContentLink(ISiteElement, IInternalReference, IAttributeAnnotatable):
- """Rented content interface"""
+class ISiteLink(ISiteElement):
+ """Site link interface"""
navigation_title = I18nTextLineField(title=_("Navigation title"),
description=_("Alternate content's title displayed in navigation pages; "
@@ -176,3 +176,15 @@
description=_("If 'no', link is not visible"),
required=True,
default=True)
+
+
+class IContentLink(ISiteLink, IInternalReference, IAttributeAnnotatable):
+ """Content link interface"""
+
+
+class IExternalContentLink(ISiteLink, IAttributeAnnotatable):
+ """External link interface"""
+
+ url = URI(title=_("Target URL"),
+ description=_("URL used to access external resource"),
+ required=True)
--- a/src/pyams_content/shared/site/link.py Fri Nov 23 13:23:22 2018 +0100
+++ b/src/pyams_content/shared/site/link.py Fri Nov 23 13:25:51 2018 +0100
@@ -17,7 +17,8 @@
from zope.interface import implementer
from zope.schema.fieldproperty import FieldProperty
-from pyams_content.shared.site.interfaces import IContentLink, ISiteElementNavigation
+from pyams_content.component.illustration import ILinkIllustrationTarget
+from pyams_content.shared.site.interfaces import IContentLink, IExternalContentLink, ISiteElementNavigation, ISiteLink
from pyams_sequence.reference import get_reference_target
from pyams_skin.layer import IPyAMSLayer
from pyams_utils.adapter import ContextRequestAdapter, adapter_config
@@ -28,8 +29,25 @@
from pyams_content import _
+@implementer(ISiteLink)
+class SiteLink(Persistent, Contained):
+ """Site link persistent class"""
+
+ navigation_title = FieldProperty(IContentLink['navigation_title'])
+ navigation_header = FieldProperty(IContentLink['navigation_header'])
+ visible = FieldProperty(IContentLink['visible'])
+
+ @staticmethod
+ def is_deletable():
+ return True
+
+
+#
+# Internal content link
+#
+
@implementer(IContentLink)
-class ContentLink(Persistent, Contained):
+class ContentLink(SiteLink):
"""Content link persistent class
A 'content link' is a link to another content, which may be stored anywhere (same site,
@@ -37,16 +55,9 @@
"""
reference = FieldProperty(IContentLink['reference'])
- navigation_title = FieldProperty(IContentLink['navigation_title'])
- navigation_header = FieldProperty(IContentLink['navigation_header'])
- visible = FieldProperty(IContentLink['visible'])
content_name = _("Content link")
- @staticmethod
- def is_deletable():
- return True
-
@volatile_property
def target(self):
target = get_reference_target(self.reference)
@@ -98,3 +109,25 @@
target = context.get_target()
if target is not None:
return IWorkflowPublicationInfo(target, None)
+
+
+#
+# External content link
+#
+
+@implementer(IExternalContentLink, ILinkIllustrationTarget)
+class ExternalContentLink(SiteLink):
+ """External link persistent class"""
+
+ url = FieldProperty(IExternalContentLink['url'])
+
+ content_name = _("External content link")
+
+
+@adapter_config(context=(IExternalContentLink, IPyAMSLayer), provides=ISiteElementNavigation)
+class ExternalContentLinkNavigationAdapter(ContextRequestAdapter):
+ """External content link navigation adapter"""
+
+ @property
+ def visible(self):
+ return self.context.visible
--- a/src/pyams_content/shared/site/zmi/container.py Fri Nov 23 13:23:22 2018 +0100
+++ b/src/pyams_content/shared/site/zmi/container.py Fri Nov 23 13:25:51 2018 +0100
@@ -32,7 +32,7 @@
SharedToolDashboardStatusColumn, SharedToolDashboardStatusDateColumn, SharedToolDashboardStatusPrincipalColumn, \
SharedToolDashboardVersionColumn
from pyams_content.shared.site import WfSiteTopic
-from pyams_content.shared.site.interfaces import IBaseSiteItem, IContentLink, ISiteContainer, ISiteManager
+from pyams_content.shared.site.interfaces import IBaseSiteItem, IContentLink, ISiteContainer, ISiteManager, ISiteLink
from pyams_content.zmi import pyams_content
from pyams_content.zmi.interfaces import ISiteTreeMenu, ISiteTreeTable, IUserAddingsMenuLabel
from pyams_form.form import ajax_config
@@ -311,7 +311,7 @@
weight = 5
def get_icon(self, item):
- if IContentLink.providedBy(item):
+ if ISiteLink.providedBy(item):
icon_class = 'fa-eye' if item.visible else 'fa-eye-slash'
if not IWorkflowPublicationInfo(item.__parent__).is_published():
icon_class += ' text-danger'
@@ -334,13 +334,13 @@
def get_icon_hint(self, item):
translate = self.request.localizer.translate
- if IContentLink.providedBy(item) and self.request.has_permission(self.permission, context=item):
+ if ISiteLink.providedBy(item) and self.request.has_permission(self.permission, context=item):
return translate(self.active_icon_hint)
else:
return translate(self.inactive_icon_hint)
def renderCell(self, item):
- if IContentLink.providedBy(item) and self.request.has_permission(self.permission, context=item):
+ if ISiteLink.providedBy(item) and self.request.has_permission(self.permission, context=item):
return super(SiteContainerTreeVisibleColumn, self).renderCell(item)
else:
return self.get_icon(item)
@@ -352,7 +352,7 @@
"""Switch content link visibility"""
container = ISiteContainer(request.context)
content = container.get(str(request.params.get('object_name')))
- if not IContentLink.providedBy(content):
+ if not ISiteLink.providedBy(content):
raise NotFound()
content.visible = not content.visible
return {
@@ -389,12 +389,12 @@
' <i class="fa fa-fw {switch}"></i>' \
' </span> <span class="title">{title}</span>' \
'</div>'.format(
- padding='<span class="tree-node-padding"></span>' * depth,
- hint=self.request.localizer.translate(_("Click to show/hide inner folders")),
- switch='fa-{state}-square-o switch'.format(
- state=self.table.rows_state or ('minus' if item in lineage(self.context) else 'plus'))
- if ISiteContainer.providedBy(item) else '',
- title=name or super(SiteContainerTreeNameColumn, self).renderCell(item))
+ padding='<span class="tree-node-padding"></span>' * depth,
+ hint=self.request.localizer.translate(_("Click to show/hide inner folders")),
+ switch='fa-{state}-square-o switch'.format(
+ state=self.table.rows_state or ('minus' if item in lineage(self.context) else 'plus'))
+ if ISiteContainer.providedBy(item) else '',
+ title=name or super(SiteContainerTreeNameColumn, self).renderCell(item))
@adapter_config(name='content-type', context=(IBaseSiteItem, IPyAMSLayer, ISiteTreeTable), provides=IColumn)
--- a/src/pyams_content/shared/site/zmi/link.py Fri Nov 23 13:23:22 2018 +0100
+++ b/src/pyams_content/shared/site/zmi/link.py Fri Nov 23 13:25:51 2018 +0100
@@ -15,13 +15,13 @@
from uuid import uuid4
from z3c.form import field
-from zope.interface import Interface
+from zope.interface import Interface, implementer
from zope.intid.interfaces import IIntIds
from zope.schema import Int
from pyams_content.interfaces import CREATE_CONTENT_PERMISSION, MANAGE_CONTENT_PERMISSION
-from pyams_content.shared.site.interfaces import IContentLink, ISiteContainer
-from pyams_content.shared.site.link import ContentLink
+from pyams_content.shared.site.interfaces import IContentLink, IExternalContentLink, ISiteContainer
+from pyams_content.shared.site.link import ContentLink, ExternalContentLink
from pyams_content.shared.site.zmi.container import SiteContainerTreeTable
from pyams_content.shared.site.zmi.widget import SiteManagerFoldersSelectorFieldWidget
from pyams_content.zmi.interfaces import ISiteTreeTable
@@ -32,6 +32,7 @@
from pyams_skin.interfaces.viewlet import IToolbarAddingMenu
from pyams_skin.layer import IPyAMSLayer
from pyams_skin.table import get_object_name
+from pyams_skin.viewlet.menu import MenuDivider
from pyams_skin.viewlet.toolbar import ToolbarMenuItem
from pyams_utils.adapter import ContextRequestViewAdapter, adapter_config
from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
@@ -39,11 +40,22 @@
from pyams_utils.url import absolute_url
from pyams_viewlet.viewlet import viewlet_config
from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm
+from pyams_zmi.interfaces import IPropertiesEditForm
from pyams_zmi.layer import IAdminLayer
from pyams_content import _
+@viewlet_config(name='add-link.divider', context=ISiteContainer, layer=IAdminLayer, view=Interface,
+ manager=IToolbarAddingMenu, permission=CREATE_CONTENT_PERMISSION, weight=89)
+class AddLinkMenuDivider(MenuDivider):
+ """Add links menu divider"""
+
+
+#
+# Content link views
+#
+
@viewlet_config(name='add-content-link.menu', context=ISiteContainer, layer=IAdminLayer, view=Interface,
manager=IToolbarAddingMenu, permission=CREATE_CONTENT_PERMISSION, weight=90)
class ContentLinkAddMenu(ToolbarMenuItem):
@@ -134,7 +146,7 @@
legend = _("Edit content link properties")
- fields = field.Fields(IContentLink).omit('__parent__', '__name__', 'visible')
+ fields = field.Fields(IContentLink).select('reference', 'navigation_title', 'navigation_header')
edit_permission = MANAGE_CONTENT_PERMISSION
def get_ajax_output(self, changes):
@@ -153,3 +165,118 @@
}
})
return output
+
+
+#
+# External content link views
+#
+
+@viewlet_config(name='add-external-link.menu', context=ISiteContainer, layer=IAdminLayer, view=Interface,
+ manager=IToolbarAddingMenu, permission=CREATE_CONTENT_PERMISSION, weight=91)
+class ExternalContentLinkAddMenu(ToolbarMenuItem):
+ """External content link add menu"""
+
+ label = _("External content link...")
+ label_css_class = 'fa fa-fw fa-external-link'
+ url = 'add-external-link.html'
+ modal_target = True
+
+
+class IExternalContentLinkAddFormFields(IExternalContentLink):
+ """External content link add forms fields interface"""
+
+ parent = Int(title=_("Parent"),
+ description=_("Folder's parent"),
+ required=True)
+
+
+@pagelet_config(name='add-external-link.html', context=ISiteContainer, layer=IPyAMSLayer,
+ permission=CREATE_CONTENT_PERMISSION)
+@ajax_config(name='add-external-link.json', context=ISiteContainer, layer=IPyAMSLayer, base=AJAXAddForm)
+class ExternalContentLinkAddForm(AdminDialogAddForm):
+ """External content link add form"""
+
+ legend = _("Link external content")
+
+ fields = field.Fields(IExternalContentLinkAddFormFields).select('url', 'navigation_title',
+ 'navigation_header', 'parent')
+ fields['parent'].widgetFactory = SiteManagerFoldersSelectorFieldWidget
+
+ edit_permission = CREATE_CONTENT_PERMISSION
+
+ __target = None
+
+ def updateWidgets(self, prefix=None):
+ super(ExternalContentLinkAddForm, self).updateWidgets(prefix)
+ if 'parent' in self.widgets:
+ self.widgets['parent'].permission = CREATE_CONTENT_PERMISSION
+
+ def create(self, data):
+ return ExternalContentLink()
+
+ def update_content(self, content, data):
+ data = data.get(self, data)
+ content.url = data.get('url')
+ content.navigation_title = data['navigation_title']
+ content.navigation_header = data['navigation_header']
+ intids = get_utility(IIntIds)
+ parent = intids.queryObject(data.get('parent'))
+ if parent is not None:
+ uuid = str(uuid4())
+ parent[uuid] = content
+ self.__target = parent
+
+ def add(self, content):
+ pass
+
+ def nextURL(self):
+ return absolute_url(self.__target, self.request, 'admin#site-tree.html')
+
+ def get_ajax_output(self, changes):
+ return {'status': 'reload'}
+
+
+@adapter_config(context=(IExternalContentLink, IPyAMSLayer, ISiteTreeTable), provides=ITableElementName)
+class ExternalContentLinkTableElementName(ContextRequestViewAdapter):
+ """External content link table element name"""
+
+ @property
+ def name(self):
+ title = II18n(self.context).query_attribute('navigation_title', request=self.request)
+ if not title:
+ title = self.context.url
+ return '<i class="fa fa-fw fa-external-link"></i>{title}'.format(
+ title=title or '--')
+
+
+@pagelet_config(name='properties.html', context=IExternalContentLink, layer=IPyAMSLayer,
+ permission=VIEW_SYSTEM_PERMISSION)
+@ajax_config(name='properties.json', context=IExternalContentLink, layer=IPyAMSLayer)
+@implementer(IPropertiesEditForm)
+class ExternalContentLinkPropertiesEditForm(AdminDialogEditForm):
+ """External content link properties edit form"""
+
+ prefix = 'link_properties.'
+
+ legend = _("Edit external content link properties")
+ dialog_class = 'modal-large'
+
+ fields = field.Fields(IExternalContentLink).select('url', 'navigation_title', 'navigation_header')
+ edit_permission = MANAGE_CONTENT_PERMISSION
+
+ def get_ajax_output(self, changes):
+ output = super(self.__class__, self).get_ajax_output(changes)
+ intids = get_utility(IIntIds)
+ if changes:
+ table = SiteContainerTreeTable(self.context.__parent__, self.request)
+ table.update()
+ row = table.setUpRow(self.context)
+ output.setdefault('events', []).append({
+ 'event': 'myams.refresh',
+ 'options': {
+ 'handler': 'MyAMS.skin.refreshRow',
+ 'object_id': '{0}::{1}'.format(SiteContainerTreeTable.id, intids.queryId(self.context)),
+ 'row': table.renderRow(row)
+ }
+ })
+ return output