Added content sharing support
authorThierry Florac <tflorac@ulthar.net>
Wed, 28 Nov 2018 17:54:22 +0100
changeset 1138 6de2ab88b4fe
parent 1137 b8068bb58b9e
child 1139 b99e7d0757cd
Added content sharing support
src/pyams_content/features/share/__init__.py
src/pyams_content/features/share/container.py
src/pyams_content/features/share/interfaces.py
src/pyams_content/features/share/portlet/__init__.py
src/pyams_content/features/share/portlet/interfaces.py
src/pyams_content/features/share/portlet/zmi/__init__.py
src/pyams_content/features/share/portlet/zmi/templates/toolbox-preview.pt
src/pyams_content/features/share/zmi/__init__.py
src/pyams_content/features/share/zmi/container.py
src/pyams_content/root/__init__.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/share/__init__.py	Wed Nov 28 17:54:22 2018 +0100
@@ -0,0 +1,67 @@
+#
+# 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'
+
+from persistent import Persistent
+from pyramid.encode import url_quote
+from zope.container.contained import Contained
+from zope.interface import implementer
+from zope.schema.fieldproperty import FieldProperty
+
+from pyams_content.features.share.interfaces import ISocialShareItem
+from pyams_content.interfaces import MANAGE_SITE_ROOT_PERMISSION
+from pyams_content.reference.pictograms import IPictogramTable
+from pyams_form.interfaces.form import IFormContextPermissionChecker
+from pyams_i18n.interfaces import II18n
+from pyams_utils.adapter import ContextAdapter, adapter_config
+from pyams_utils.registry import query_utility
+from pyams_utils.url import canonical_url
+from pyams_utils.zodb import volatile_property
+
+
+@implementer(ISocialShareItem)
+class SocialShareItem(Persistent, Contained):
+    """Social network share item"""
+
+    active = FieldProperty(ISocialShareItem['active'])
+    label = FieldProperty(ISocialShareItem['label'])
+    target_url = FieldProperty(ISocialShareItem['target_url'])
+    _pictogram_name = FieldProperty(ISocialShareItem['pictogram_name'])
+
+    @property
+    def pictogram_name(self):
+        return self._pictogram_name
+
+    @pictogram_name.setter
+    def pictogram_name(self, value):
+        if value != self._pictogram_name:
+            self._pictogram_name = value
+            del self.pictogram
+
+    @volatile_property
+    def pictogram(self):
+        table = query_utility(IPictogramTable)
+        if table is not None:
+            return table.get(self._pictogram_name)
+
+    def get_url(self, context, request):
+        url = II18n(self).query_attribute('target_url', request=request)
+        return url.format(url=url_quote(canonical_url(context, request)),
+                          title=url_quote(II18n(context).query_attribute('title', request=request)))
+
+
+@adapter_config(context=ISocialShareItem, provides=IFormContextPermissionChecker)
+class SocialShareItemPermissionChecker(ContextAdapter):
+    """Social share item permission checker"""
+
+    edit_permission = MANAGE_SITE_ROOT_PERMISSION
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/share/container.py	Wed Nov 28 17:54:22 2018 +0100
@@ -0,0 +1,68 @@
+#
+# 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'
+
+from zope.container.ordered import OrderedContainer
+from zope.interface import implementer
+from zope.location import locate
+from zope.location.interfaces import ISublocations
+from zope.traversing.interfaces import ITraversable
+
+from pyams_catalog.utils import index_object
+from pyams_content.features.share import ISocialShareItem
+from pyams_content.features.share.interfaces import ISocialShareManager, ISocialShareManagerTarget, \
+    SOCIAL_SHARE_MANAGER_KEY
+from pyams_utils.adapter import ContextAdapter, adapter_config, get_annotation_adapter
+
+
+@implementer(ISocialShareManager)
+class SocialShareManager(OrderedContainer):
+    """Social network share manager"""
+
+    last_id = 1
+
+    def append(self, value, notify=True):
+        key = str(self.last_id)
+        if not notify:
+            # pre-locate item to avoid multiple notifications
+            locate(value, self, key)
+        self[key] = value
+        self.last_id += 1
+        if not notify:
+            # make sure that item is correctly indexed
+            index_object(value)
+
+    def get_active_items(self):
+        yield from filter(lambda x: ISocialShareItem(x).active, self.values())
+
+
+@adapter_config(context=ISocialShareManagerTarget, provides=ISocialShareManager)
+def social_share_manager_factory(context):
+    """Social network share manager factory"""
+    return get_annotation_adapter(context, SOCIAL_SHARE_MANAGER_KEY, SocialShareManager, name='++social-share++')
+
+
+@adapter_config(name='social-share', context=ISocialShareManagerTarget, provides=ITraversable)
+class SocialShareManagerNamespace(ContextAdapter):
+    """Social network share manager ++social-share++ namespace"""
+
+    def traverse(self, name, furtherpath=None):
+        return ISocialShareManager(self.context)
+
+
+@adapter_config(name='social-share', context=ISocialShareManagerTarget, provides=ISublocations)
+class SocialShareManagerSublocations(ContextAdapter):
+    """Social network share manager sub-locations adapter"""
+
+    def sublocations(self):
+        return ISocialShareManager(self.context).values()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/share/interfaces.py	Wed Nov 28 17:54:22 2018 +0100
@@ -0,0 +1,73 @@
+#
+# 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'
+
+from zope.container.constraints import containers, contains
+from zope.interface import Attribute, Interface
+from zope.schema import Bool, Choice
+
+from pyams_content.interfaces.container import IOrderedContainer
+from pyams_content.reference.pictograms.interfaces import SELECTED_PICTOGRAM_VOCABULARY
+from pyams_i18n.schema import I18nTextLineField
+
+from pyams_content import _
+
+
+SOCIAL_SHARE_MANAGER_KEY = 'pyams_content.social_share'
+
+
+class ISocialShareItem(Interface):
+    """Social network share item interface"""
+
+    containers('.ISocialShareManager')
+
+    active = Bool(title=_("Active item?"),
+                  description=_("If 'no', selected item is inactive"),
+                  required=True,
+                  default=True)
+
+    label = I18nTextLineField(title=_("Label"),
+                              description=_("This label will be associated to share link"),
+                              required=True)
+
+    target_url = I18nTextLineField(title=_("Content share URL"),
+                                   description=_("URL used to share this content on given network; {url} string "
+                                                 "will be replaced automatically by content canonical URL, "
+                                                 "and {title} by content title (if required)"),
+                                   required=True)
+
+    def get_url(self, context, request):
+        """Get canonical URL for given context and request"""
+
+    pictogram_name = Choice(title=_("Pictogram"),
+                            description=_("Name of pictogram associated with this social network"),
+                            required=False,
+                            vocabulary=SELECTED_PICTOGRAM_VOCABULARY)
+
+    pictogram = Attribute("Selected pictogram object")
+
+
+class ISocialShareManager(IOrderedContainer):
+    """Social network share item container interface"""
+
+    contains(ISocialShareItem)
+
+    def append(self, value, notify=True):
+        """Append given pictogram item to container"""
+
+    def get_active_items(self):
+        """Get list of visible pictogram items"""
+
+
+class ISocialShareManagerTarget(Interface):
+    """Content share manager target marker interface"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/share/portlet/__init__.py	Wed Nov 28 17:54:22 2018 +0100
@@ -0,0 +1,47 @@
+#
+# 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'
+
+from zope.interface import implementer
+from zope.schema.fieldproperty import FieldProperty
+
+from pyams_content.features.share.portlet.interfaces import IToolboxPortletSettings
+from pyams_portal.portlet import Portlet, PortletSettings, portlet_config
+from pyams_utils.factory import factory_config
+from pyams_utils.interfaces import VIEW_PERMISSION
+
+from pyams_content import _
+
+
+TOOLBOX_PORTLET_NAME = 'pyams_content.portlet.toolbox'
+
+
+@implementer(IToolboxPortletSettings)
+@factory_config(provided=IToolboxPortletSettings)
+class ToolboxPortletSettings(PortletSettings):
+    """Toolbox portlet settings"""
+
+    allow_printing = FieldProperty(IToolboxPortletSettings['allow_printing'])
+    allow_sharing = FieldProperty(IToolboxPortletSettings['allow_sharing'])
+
+
+@portlet_config(permission=VIEW_PERMISSION)
+class ToolboxPortet(Portlet):
+    """Toolbox portlet"""
+
+    name = TOOLBOX_PORTLET_NAME
+    label = _("Toolbox")
+
+    toolbar_css_class = 'fa fa-fw fa-print'
+
+    settings_factory = IToolboxPortletSettings
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/share/portlet/interfaces.py	Wed Nov 28 17:54:22 2018 +0100
@@ -0,0 +1,33 @@
+#
+# 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'
+
+from zope.schema import Bool
+
+from pyams_portal.interfaces import IPortletSettings
+
+from pyams_content import _
+
+
+class IToolboxPortletSettings(IPortletSettings):
+    """Toolbox portlet settings"""
+
+    allow_printing = Bool(title=_("Allow printing?"),
+                          description=_("If 'no', printing will be disabled"),
+                          required=True,
+                          default=True)
+
+    allow_sharing = Bool(title=_("Allow sharing?"),
+                         description=_("If 'no', sharing buttons won't be displayed"),
+                         required=True,
+                         default=True)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/share/portlet/zmi/__init__.py	Wed Nov 28 17:54:22 2018 +0100
@@ -0,0 +1,47 @@
+#
+# 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'
+
+from zope.interface import Interface
+
+from pyams_content.features.share.portlet import IToolboxPortletSettings
+from pyams_form.form import AJAXEditForm
+from pyams_pagelet.interfaces import IPagelet
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_portal.interfaces import IPortletPreviewer
+from pyams_portal.portlet import PortletPreviewer
+from pyams_portal.zmi.portlet import PortletSettingsEditor
+from pyams_skin.layer import IPyAMSLayer
+from pyams_template.template import template_config
+from pyams_utils.adapter import adapter_config
+from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
+
+
+@pagelet_config(name='properties.html', context=IToolboxPortletSettings, layer=IPyAMSLayer,
+                permission=VIEW_SYSTEM_PERMISSION)
+class ToolboxPortletSettingsEditor(PortletSettingsEditor):
+    """Toolbox portlet settings editor"""
+
+    settings = IToolboxPortletSettings
+
+
+@adapter_config(name='properties.json', context=(IToolboxPortletSettings, IPyAMSLayer), provides=IPagelet)
+class ToolboxPortletSettingsAJAXEditor(AJAXEditForm, ToolboxPortletSettingsEditor):
+    """Toolbox portlet settings editor, JSON renderer"""
+
+
+@adapter_config(context=(Interface, IPyAMSLayer, Interface, IToolboxPortletSettings),
+                provides=IPortletPreviewer)
+@template_config(template='templates/toolbox-preview.pt', layer=IPyAMSLayer)
+class ToolboxPortletPreviewer(PortletPreviewer):
+    """Toolbox portlet previewer"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/share/portlet/zmi/templates/toolbox-preview.pt	Wed Nov 28 17:54:22 2018 +0100
@@ -0,0 +1,10 @@
+<tal:var define="settings view.settings" i18n:domain="pyams_content">
+	<div class="padding-x-10">
+		<i class="fa fa-fw fa-${'check-' if settings.allow_printing else ''}square-o"></i>
+		<span i18n:translate="">Allow printing</span>
+	</div>
+	<div class="padding-x-10">
+		<i class="fa fa-fw fa-${'check-' if settings.allow_sharing else ''}square-o"></i>
+		<span i18n:translate="">Allow sharing</span>
+	</div>
+</tal:var>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/share/zmi/__init__.py	Wed Nov 28 17:54:22 2018 +0100
@@ -0,0 +1,97 @@
+#
+# 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'
+
+from z3c.form import field
+
+from pyams_content.features.share import ISocialShareItem, SocialShareItem
+from pyams_content.features.share.interfaces import ISocialShareManager, ISocialShareManagerTarget
+from pyams_content.features.share.zmi.container import SocialShareContainerTable, SocialShareContainerView
+from pyams_content.interfaces import MANAGE_SITE_ROOT_PERMISSION
+from pyams_content.reference.pictograms.zmi.widget import PictogramSelectFieldWidget
+from pyams_form.form import AJAXAddForm, ajax_config
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_skin.event import get_json_table_row_refresh_event
+from pyams_skin.interfaces.viewlet import IWidgetTitleViewletManager
+from pyams_skin.layer import IPyAMSLayer
+from pyams_skin.viewlet.toolbar import ToolbarAction
+from pyams_utils.traversing import get_parent
+from pyams_utils.url import absolute_url
+from pyams_viewlet.viewlet import viewlet_config
+from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm
+
+from pyams_content import _
+
+
+@viewlet_config(name='add-social-share.action', context=ISocialShareManagerTarget, layer=IPyAMSLayer,
+                view=SocialShareContainerView, manager=IWidgetTitleViewletManager,
+                permission=MANAGE_SITE_ROOT_PERMISSION, weight=1)
+class SocialShareItemAddAction(ToolbarAction):
+    """Social network share item add action"""
+
+    label = _("Add network share")
+    label_css_class = 'fa fa-fw fa-plus'
+    url = 'add-social-share.html'
+    modal_target = True
+
+
+@pagelet_config(name='add-social-share.html', context=ISocialShareManagerTarget, layer=IPyAMSLayer,
+                permission=MANAGE_SITE_ROOT_PERMISSION)
+@ajax_config(name='add-social-share.json', context=ISocialShareManagerTarget, layer=IPyAMSLayer, base=AJAXAddForm)
+class SocialShareItemAddForm(AdminDialogAddForm):
+    """Social network share item add form"""
+
+    dialog_class = 'modal-large'
+    legend = _("Add social network share")
+    icon_css_class = 'fa fa-fw fa-share-alt'
+
+    fields = field.Fields(ISocialShareItem).omit('__parent__', '__name__', 'active')
+    fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
+
+    edit_permission = MANAGE_SITE_ROOT_PERMISSION
+
+    def create(self, data):
+        return SocialShareItem()
+
+    def add(self, object):
+        ISocialShareManager(self.context).append(object)
+
+    def nextURL(self):
+        return absolute_url(self.context, self.request, 'social-share.html')
+
+
+@pagelet_config(name='properties.html', context=ISocialShareItem, layer=IPyAMSLayer,
+                permission=MANAGE_SITE_ROOT_PERMISSION)
+@ajax_config(name='properties.json', context=ISocialShareItem, layer=IPyAMSLayer)
+class SocialShareItemPropertiesEditForm(AdminDialogEditForm):
+    """Social network share item properties edit form"""
+
+    dialog_class = 'modal-large'
+    prefix = 'social_share_properties.'
+
+    legend = _("Edit social network share properties")
+    icon_css_class = 'fa fa-fw fa-share-alt'
+
+    fields = field.Fields(ISocialShareItem).omit('__parent__', '__name__', 'active')
+    fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
+
+    edit_permission = MANAGE_SITE_ROOT_PERMISSION
+
+    def get_ajax_output(self, changes):
+        output = super(SocialShareItemPropertiesEditForm, self).get_ajax_output(changes)
+        updated = changes.get(ISocialShareItem, ())
+        if updated:
+            target = get_parent(self.context, ISocialShareManagerTarget)
+            output.setdefault('events', []).append(
+                get_json_table_row_refresh_event(target, self.request, SocialShareContainerTable, self.context))
+        return output
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/share/zmi/container.py	Wed Nov 28 17:54:22 2018 +0100
@@ -0,0 +1,186 @@
+#
+# 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 json
+
+from pyramid.decorator import reify
+from pyramid.exceptions import NotFound
+from pyramid.view import view_config
+from z3c.table.interfaces import IColumn, IValues
+
+from pyams_content.features.share.interfaces import ISocialShareManager, ISocialShareManagerTarget
+from pyams_content.interfaces import MANAGE_SITE_ROOT_PERMISSION
+from pyams_content.zmi import pyams_content
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_skin.help import ContentHelp
+from pyams_skin.interfaces import IContentHelp, IPageHeader
+from pyams_skin.layer import IPyAMSLayer
+from pyams_skin.page import DefaultPageHeaderAdapter
+from pyams_skin.table import AttributeSwitcherColumn, BaseTable, I18nColumn, I18nValueColumn, SorterColumn, TrashColumn
+from pyams_skin.viewlet.menu import MenuItem
+from pyams_utils.adapter import ContextRequestViewAdapter, adapter_config
+from pyams_utils.fanstatic import get_resource_path
+from pyams_utils.url import absolute_url
+from pyams_viewlet.viewlet import viewlet_config
+from pyams_zmi.interfaces.menu import IPropertiesMenu
+from pyams_zmi.layer import IAdminLayer
+from pyams_zmi.view import ContainerAdminView
+
+from pyams_content import _
+
+
+@viewlet_config(name='social-share.menu', context=ISocialShareManagerTarget, layer=IPyAMSLayer,
+                manager=IPropertiesMenu, permission=MANAGE_SITE_ROOT_PERMISSION, weight=25)
+class SocialShareMenu(MenuItem):
+    """Social network share menu"""
+
+    label = _("Network shares...")
+    icon_class = 'fa-share-alt'
+    url = '#social-share.html'
+
+
+class SocialShareContainerTable(BaseTable):
+    """Social network share items container table"""
+
+    prefix = 'social'
+
+    hide_header = True
+    sortOn = None
+
+    cssClasses = {'table': 'table table-bordered table-striped table-hover table-tight table-dnd'}
+
+    @property
+    def data_attributes(self):
+        attributes = super(SocialShareContainerTable, self).data_attributes
+        attributes.setdefault('table', {}).update({
+            'data-ams-plugins': 'pyams_content',
+            'data-ams-plugin-pyams_content-src': get_resource_path(pyams_content),
+            'data-ams-location': absolute_url(ISocialShareManager(self.context), self.request),
+            'data-ams-tablednd-drag-handle': 'td.sorter',
+            'data-ams-tablednd-drop-target': 'set-social-items-order.json',
+            'data-ams-active-icon-on': 'fa fa-fw fa-check-square-o',
+            'data-ams-active-icon-off': 'fa fa-fw fa-square-o txt-color-silver opacity-75'
+        })
+        attributes.setdefault('td', {}).update({
+            'data-ams-attribute-switcher': 'switch-item-activity.json',
+            'data-ams-switcher-attribute-name': 'active'
+        })
+        return attributes
+
+    @reify
+    def values(self):
+        return list(super(SocialShareContainerTable, self).values)
+
+    def render(self):
+        if not self.values:
+            translate = self.request.localizer.translate
+            return translate(_("No currently defined social network share item."))
+        return super(SocialShareContainerTable, self).render()
+
+
+@adapter_config(context=(ISocialShareManagerTarget, IPyAMSLayer, SocialShareContainerTable), provides=IValues)
+class SocialShareContainerValues(ContextRequestViewAdapter):
+    """Social network share items container values"""
+
+    @property
+    def values(self):
+        return ISocialShareManager(self.context).values()
+
+
+@adapter_config(name='sorter', context=(ISocialShareManagerTarget, IPyAMSLayer, SocialShareContainerTable),
+                provides=IColumn)
+class SocialShareContainerSorterColumn(SorterColumn):
+    """Social network share items container sorter column"""
+
+
+@view_config(name='set-social-items-order.json', context=ISocialShareManager, request_type=IPyAMSLayer,
+             permission=MANAGE_SITE_ROOT_PERMISSION, renderer='json', xhr=True)
+def set_social_items_order(request):
+    """Update social network share items order"""
+    order = list(map(str, json.loads(request.params.get('names'))))
+    request.context.updateOrder(order)
+    return {'status': 'success'}
+
+
+@adapter_config(name='enable-disable', context=(ISocialShareManagerTarget, IPyAMSLayer, SocialShareContainerTable),
+                provides=IColumn)
+class SocialShareContainerEnablerColumn(AttributeSwitcherColumn):
+    """Social network share items container enabler switcher column"""
+
+    switch_attribute = 'active'
+
+    on_icon_class = 'fa fa-fw fa-check-square-o'
+    off_icon_class = 'fa fa-fw fa-square-o txt-color-silver opacity-75'
+
+    icon_hint = _("Enable/disable item")
+
+    weight = 6
+
+
+@view_config(name='switch-item-activity.json', context=ISocialShareManager, request_type=IPyAMSLayer,
+             permission=MANAGE_SITE_ROOT_PERMISSION, renderer='json', xhr=True)
+def switch_social_item_activity(request):
+    """Switch social item activity"""
+    container = ISocialShareManager(request.context)
+    item = container.get(str(request.params.get('object_name')))
+    if item is None:
+        raise NotFound()
+    item.active = not item.active
+    return {'on': item.active}
+
+
+@adapter_config(name='name', context=(ISocialShareManagerTarget, IPyAMSLayer, SocialShareContainerTable),
+                provides=IColumn)
+class SocialShareContainerNameColumn(I18nColumn, I18nValueColumn):
+    """Social network share items container name column"""
+
+    _header = _("Label")
+    attrName = 'label'
+    weight = 10
+
+
+@adapter_config(name='trash', context=(ISocialShareManagerTarget, IPyAMSLayer, SocialShareContainerTable),
+                provides=IColumn)
+class SocialShareContainerTrashColumn(TrashColumn):
+    """Social network share items container trash column"""
+
+    permission = MANAGE_SITE_ROOT_PERMISSION
+
+
+@pagelet_config(name='social-share.html', context=ISocialShareManagerTarget, layer=IPyAMSLayer,
+                permission=MANAGE_SITE_ROOT_PERMISSION)
+class SocialShareContainerView(ContainerAdminView):
+    """Social network share items container view"""
+
+    title = _("Social networks share")
+    table_class = SocialShareContainerTable
+
+
+@adapter_config(context=(ISocialShareManagerTarget, IAdminLayer, SocialShareContainerView), provides=IPageHeader)
+class SocialShareContainerViewHeaderAdapter(DefaultPageHeaderAdapter):
+    """Social network share items container view header adapter"""
+
+    icon_class = 'fa fa-fw fa-share-alt'
+
+
+@adapter_config(context=(ISocialShareManagerTarget, IAdminLayer, SocialShareContainerView), provides=IContentHelp)
+class SocialShareContainerHelpAdapter(ContentHelp):
+    """Social network share items container help adapter"""
+
+    header = _("Social networks share")
+    message = _("""Social networks share items are used to define share options available on your contents.
+    
+**WARNING**: don't forget to include a toolbox in your presentation template to display social networks shares!!
+""")
+    message_format = 'rest'
--- a/src/pyams_content/root/__init__.py	Wed Nov 28 11:56:29 2018 +0100
+++ b/src/pyams_content/root/__init__.py	Wed Nov 28 17:54:22 2018 +0100
@@ -18,7 +18,6 @@
 from pyramid.events import subscriber
 from zope.interface import implementer
 
-from pyams_content import _
 from pyams_content.component.illustration.interfaces import IIllustrationTarget
 from pyams_content.component.theme.interfaces import ITagsManagerTarget
 from pyams_content.features.alert.interfaces import IAlertTarget
@@ -26,6 +25,7 @@
 from pyams_content.features.header.interfaces import IHeaderTarget
 from pyams_content.features.preview.interfaces import IPreviewTarget
 from pyams_content.features.redirect.interfaces import IRedirectionManagerTarget
+from pyams_content.features.share.interfaces import ISocialShareManagerTarget
 from pyams_content.interfaces import MANAGE_SITE_ROOT_PERMISSION, OPERATOR_ROLE, WEBMASTER_ROLE
 from pyams_content.root.interfaces import ISiteRoot, ISiteRootBackOfficeConfiguration, ISiteRootConfiguration, \
     ISiteRootRoles, ISiteRootToolsConfiguration
@@ -44,10 +44,12 @@
 from pyams_utils.site import BaseSiteRoot
 from pyams_utils.traversing import get_parent
 
+from pyams_content import _
+
 
 @implementer(IDefaultProtectionPolicy, ISiteRoot, ISiteRootRoles, IPortalContext, ITagsManagerTarget,
-             IIllustrationTarget, IHeaderTarget, IFooterTarget, IAlertTarget, IRedirectionManagerTarget,
-             IPreviewTarget)
+             IIllustrationTarget, IHeaderTarget, IFooterTarget, ISocialShareManagerTarget, IAlertTarget,
+             IRedirectionManagerTarget, IPreviewTarget)
 class SiteRoot(ProtectedObject, BaseSiteRoot, UserSkinnableContent):
     """Main site root"""