src/pyams_content/shared/common/zmi/dashboard.py
changeset 0 7c0001cacf8e
child 14 234db8f05928
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/common/zmi/dashboard.py	Thu Oct 08 13:37:29 2015 +0200
@@ -0,0 +1,759 @@
+#
+# Copyright (c) 2008-2015 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_content.interfaces import MANAGE_SITE_ROOT_PERMISSION, PUBLISH_CONTENT_PERMISSION
+from pyams_content.profile.interfaces import IAdminProfile
+from pyams_content.shared.common.interfaces import ISharedTool, IWfSharedContent, IManagerRestrictions
+from pyams_content.shared.common.interfaces.zmi import ISharedToolDashboardTable
+from pyams_content.zmi.interfaces import IDashboardMenu, IMyDashboardMenu, IAllContentsMenu
+from pyams_i18n.interfaces import II18n
+from pyams_security.interfaces import ISecurityManager
+from pyams_sequence.interfaces import ISequentialIdInfo, ISequentialIdTarget, ISequentialIntIds
+from pyams_skin.interfaces import IInnerPage, IPageHeader
+from pyams_skin.interfaces.container import ITableElementName
+from pyams_skin.layer import IPyAMSLayer
+from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
+from pyams_workflow.interfaces import IWorkflowState, IWorkflow, IWorkflowVersions
+from pyams_zmi.interfaces.menu import IContentManagementMenu
+from pyams_zmi.layer import IAdminLayer
+from z3c.table.interfaces import IValues, IColumn
+from zope.dublincore.interfaces import IZopeDublinCore
+
+# import packages
+from hypatia.catalog import CatalogQuery
+from hypatia.query import And, Or, Eq, Any
+from pyams_catalog.query import CatalogResultSet
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_skin.container import ContainerView
+from pyams_skin.page import DefaultPageHeaderAdapter
+from pyams_skin.table import BaseTable, I18nColumn, ActionColumn
+from pyams_skin.viewlet.menu import MenuItem
+from pyams_template.template import template_config
+from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter
+from pyams_utils.date import format_datetime, SH_DATETIME_FORMAT
+from pyams_utils.list import unique
+from pyams_utils.property import cached_property
+from pyams_utils.registry import get_utility
+from pyams_utils.timezone import tztime
+from pyams_utils.traversing import get_parent
+from pyams_viewlet.manager import viewletmanager_config
+from pyams_viewlet.viewlet import viewlet_config
+from pyams_zmi.view import AdminView
+from z3c.table.column import GetAttrColumn
+from zope.interface import implementer, Interface
+
+from pyams_content import _
+
+
+#
+# Shared tools common adapters
+#
+
+@implementer(ISharedToolDashboardTable)
+class BaseDashboardTable(BaseTable):
+    """Base dashboard table"""
+
+    _title = '{0} contents'
+
+    sortOn = None
+    dt_sort_order = 'desc'
+
+    @property
+    def title(self):
+        return self.request.localizer.translate(self._title).format(len(self.values))
+
+    @property
+    def data_attributes(self):
+        attributes = super(BaseDashboardTable, self).data_attributes
+        attributes['table'] = {'data-ams-datatable-sorting': "{0},{1}".format(len(self.columns)-1,
+                                                                              self.dt_sort_order),
+                               'data-ams-datatable-display-length':
+                                   IAdminProfile(self.request.principal).table_page_length}
+        return attributes
+
+    @cached_property
+    def values(self):
+        return tuple(super(BaseDashboardTable, self).values)
+
+
+@adapter_config(context=(IWfSharedContent, IPyAMSLayer, ISharedToolDashboardTable), provides=ITableElementName)
+class SharedToolDashboardContentNameAdapter(ContextRequestViewAdapter):
+    """Shared tool dashboard content name adapter"""
+
+    @property
+    def name(self):
+        return II18n(self.context).query_attribute('short_name', request=self.request)
+
+
+@adapter_config(name='sequence', context=(Interface, IPyAMSLayer, ISharedToolDashboardTable), provides=IColumn)
+class SharedToolDashboardSequenceColumn(I18nColumn, GetAttrColumn):
+    """Shared tool dashboard sequence ID column"""
+
+    _header = _("Unique ID")
+    weight = 14
+
+    def getValue(self, obj):
+        target = get_parent(obj, ISequentialIdTarget)
+        sequence = get_utility(ISequentialIntIds, name=target.sequence_name)
+        return sequence.get_short_oid(ISequentialIdInfo(obj).oid, target.sequence_prefix)
+
+
+@adapter_config(name='version', context=(Interface, IPyAMSLayer, ISharedToolDashboardTable), provides=IColumn)
+class SharedToolDashboardVersionColumn(I18nColumn, GetAttrColumn):
+    """Shared tool dashboard version column"""
+
+    _header = _("Version")
+    weight = 15
+
+    def getValue(self, obj):
+        return str(IWorkflowState(obj).version_id)
+
+
+@adapter_config(name='urgency', context=(Interface, IPyAMSLayer, ISharedToolDashboardTable), provides=IColumn)
+class SharedToolDashboardUrgencyColumn(ActionColumn):
+    """Shared tool dashboard urgency column"""
+
+    icon_class = 'fa fa-fw fa-exclamation-triangle txt-color-red'
+    icon_hint = _("Urgent request !")
+
+    url = '#'
+    weight = 19
+
+    def renderCell(self, item):
+        state = IWorkflowState(item)
+        if not state.state_urgency:
+            return ''
+        else:
+            return super(SharedToolDashboardUrgencyColumn, self).renderCell(item)
+
+    def get_url(self, item):
+        return self.url
+
+
+@adapter_config(name='status', context=(Interface, IPyAMSLayer, ISharedToolDashboardTable), provides=IColumn)
+class SharedToolDashboardStatusColumn(I18nColumn, GetAttrColumn):
+    """Shared tool dashboard status column"""
+
+    _header = _("Status")
+    weight = 20
+
+    def getValue(self, obj):
+        workflow = IWorkflow(obj)
+        state = IWorkflowState(obj)
+        return self.request.localizer.translate(workflow.get_state_label(state.state))
+
+
+@adapter_config(name='status_date', context=(Interface, IPyAMSLayer, ISharedToolDashboardTable), provides=IColumn)
+class SharedToolDashboardStatusDateColumn(I18nColumn, GetAttrColumn):
+    """Shared tool dashboard status date column"""
+
+    _header = _("Status date")
+    weight = 21
+
+    def getValue(self, obj):
+        state = IWorkflowState(obj)
+        return format_datetime(state.state_date, SH_DATETIME_FORMAT, request=self.request)
+
+
+@adapter_config(name='status_principal', context=(Interface, IPyAMSLayer, ISharedToolDashboardTable), provides=IColumn)
+class SharedToolDashboardStatusPrincipalColumn(I18nColumn, GetAttrColumn):
+    """Shared tool dashboard status principal column"""
+
+    _header = _("Status principal")
+    weight = 22
+
+    def getValue(self, obj):
+        state = IWorkflowState(obj)
+        manager = get_utility(ISecurityManager)
+        return manager.get_principal(state.state_principal).title
+
+
+@adapter_config(name='owner', context=(Interface, IPyAMSLayer, ISharedToolDashboardTable), provides=IColumn)
+class SharedToolDashboardOwnerColumn(I18nColumn, GetAttrColumn):
+    """Shared tool dashboard owner column"""
+
+    _header = _("Owner")
+    weight = 30
+
+    def getValue(self, obj):
+        manager = get_utility(ISecurityManager)
+        return manager.get_principal(next(iter(obj.owner))).title
+
+
+@adapter_config(name='modified', context=(Interface, IPyAMSLayer, ISharedToolDashboardTable), provides=IColumn)
+class SharedToolDashboardModifiedColumn(I18nColumn, GetAttrColumn):
+    """Shared tool dashboard modified column"""
+
+    _header = _("Last modification")
+    weight = 40
+
+    def getValue(self, obj):
+        return format_datetime(tztime(IZopeDublinCore(obj).modified), SH_DATETIME_FORMAT, request=self.request)
+
+
+#
+# Shared tool control panel
+#
+
+@viewlet_config(name='dashboard.menu', context=ISharedTool, layer=IAdminLayer,
+                manager=IContentManagementMenu, permission=VIEW_SYSTEM_PERMISSION, weight=1)
+@viewletmanager_config(name='dashboard.menu', layer=IAdminLayer, provides=IDashboardMenu)
+@implementer(IDashboardMenu)
+class SharedToolDashboardMenu(MenuItem):
+    """Shared tool dashboard menu"""
+
+    label = _("Dashboard")
+    icon_class = 'fa-line-chart'
+    url = '#dashboard.html'
+
+
+@pagelet_config(name='dashboard.html', context=ISharedTool, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
+@template_config(template='templates/dashboard.pt', layer=IAdminLayer)
+@implementer(IInnerPage)
+class SharedToolDashboardView(AdminView):
+    """Shared tool dashboard view"""
+
+    title = _("Contents dashboard")
+
+    def __init__(self, context, request):
+        super(SharedToolDashboardView, self).__init__(context, request)
+        self.tables = []
+        self.tables.append(SharedToolDashboardManagerWaitingTable(self.context, self.request))
+        self.tables.append(SharedToolDashboardOwnerWaitingTable(self.context, self.request))
+        self.tables.append(SharedToolDashboardOwnerModifiedTable(self.context, self.request))
+        for table in self.tables:
+            table.hide_toolbar = True
+
+    def update(self):
+        super(SharedToolDashboardView, self).update()
+        [table.update() for table in self.tables]
+
+
+@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolDashboardView), provides=IPageHeader)
+class SharedToolDashboardHeaderAdapter(DefaultPageHeaderAdapter):
+    """Shared tool properties header adapter"""
+
+    back_url = '/admin.html#dashboard.html'
+    back_target = None
+
+    icon_class = 'fa fa-fw fa-line-chart'
+
+
+#
+# Contents waiting for manager action
+#
+
+@implementer(ISharedToolDashboardTable)
+class SharedToolDashboardManagerWaitingTable(BaseDashboardTable):
+    """Shared tool dashboard waiting table"""
+
+    _title = _("MANAGER - {0} content(s) waiting for your action")
+
+    dt_sort_order = 'asc'
+
+
+@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolDashboardManagerWaitingTable), provides=IValues)
+class SharedToolDashboardManagerWaitingValues(ContextRequestViewAdapter):
+    """Shared tool dashboard waiting values adapter"""
+
+    @property
+    def values(self):
+        catalog = get_utility(ICatalog)
+        workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
+        params = Eq(catalog['content_type'], self.context.shared_content_type) & \
+                 Any(catalog['workflow_state'], workflow.waiting_states)
+        return filter(self.check_access,
+                      unique(map(lambda x: sorted(IWorkflowVersions(x).get_versions(IWorkflowState(x).state),
+                                                  key=lambda y: IZopeDublinCore(y).modified, reverse=True)[0],
+                                 CatalogResultSet(CatalogQuery(catalog).query(params,
+                                                                              sort_index='modified_date')))))
+
+    def check_access(self, content):
+        if self.request.has_permission(MANAGE_SITE_ROOT_PERMISSION, context=content):
+            return True
+        if self.request.principal.id in content.managers:
+            return True
+        restrictions = IManagerRestrictions(content).get_restrictions(self.request.principal)
+        if restrictions is not None:
+            return restrictions.check_access(content, PUBLISH_CONTENT_PERMISSION, self.request)
+        else:
+            return False
+
+
+#
+# Last owned contents waiting for action
+#
+
+@implementer(ISharedToolDashboardTable)
+class SharedToolDashboardOwnerWaitingTable(BaseDashboardTable):
+    """Shared tool dashboard waiting owned contents table"""
+
+    _title = _("CONTRIBUTOR - Your {0} content(s) waiting for action")
+
+    dt_sort_order = 'asc'
+
+
+@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolDashboardOwnerWaitingTable), provides=IValues)
+class SharedToolDashboardOwnerWaitingValues(ContextRequestViewAdapter):
+    """Shared tool dashboard waiting owned contents values adapter"""
+
+    @property
+    def values(self):
+        catalog = get_utility(ICatalog)
+        workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
+        params = Eq(catalog['content_type'], self.context.shared_content_type) & \
+                 Any(catalog['workflow_state'], workflow.waiting_states) & \
+                 Eq(catalog['workflow_principal'], self.request.principal.id)
+        return unique(map(lambda x: sorted(IWorkflowVersions(x).get_versions(IWorkflowState(x).state),
+                                           key=lambda y: IZopeDublinCore(y).modified, reverse=True)[0],
+                          CatalogResultSet(CatalogQuery(catalog).query(params,
+                                                                       sort_index='modified_date'))))
+
+
+#
+# Last owned modified contents
+#
+
+@implementer(ISharedToolDashboardTable)
+class SharedToolDashboardOwnerModifiedTable(BaseDashboardTable):
+    """Shared tool dashboard owned modified contents table"""
+
+    _title = _("CONTRIBUTOR - Your last modified contents (limited to {0})")
+
+
+@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolDashboardOwnerModifiedTable), provides=IValues)
+class SharedToolDashboardOwnerModifiedValues(ContextRequestViewAdapter):
+    """Shared tool dashboard waiting owned contents values adapter"""
+
+    @property
+    def values(self):
+        catalog = get_utility(ICatalog)
+        params = And(Eq(catalog['content_type'], self.context.shared_content_type),
+                        Or(Eq(catalog['role:owner'], self.request.principal.id),
+                           Eq(catalog['role:contributor'], self.request.principal.id)))
+        return unique(map(lambda x: sorted(IWorkflowVersions(x).get_versions(IWorkflowState(x).state),
+                                           key=lambda y: IZopeDublinCore(y).modified, reverse=True)[0],
+                          CatalogResultSet(CatalogQuery(catalog).query(params,
+                                                                       limit=IAdminProfile(self.request.principal).table_page_length,
+                                                                       sort_index='modified_date',
+                                                                       reverse=True))))
+
+
+#
+# All my contents menu
+#
+
+@viewlet_config(name='my-contents.menu', context=ISharedTool, layer=IAdminLayer,
+                manager=IContentManagementMenu, permission=VIEW_SYSTEM_PERMISSION, weight=5)
+@viewletmanager_config(name='my-contents.menu', layer=IAdminLayer, provides=IMyDashboardMenu)
+@implementer(IMyDashboardMenu)
+class SharedToolMyDashboardMenu(MenuItem):
+    """Shared tool 'my contents' dashboard menu"""
+
+    label = _("My contents")
+    icon_class = 'fa-user'
+    url = '#'
+    
+    
+#
+# My preparations
+# Dashboard of owned and modified contents which can be updated
+#
+
+@viewlet_config(name='my-preparations.menu', context=ISharedTool, layer=IAdminLayer,
+                manager=IMyDashboardMenu, permission=VIEW_SYSTEM_PERMISSION, weight=5)
+class SharedToolPreparationsMenu(MenuItem):
+    """Site root preparations dashboard menu"""
+
+    label = _("My preparations")
+    icon_class = None
+    url = '#my-preparations.html'
+
+
+@implementer(ISharedToolDashboardTable)
+class SharedToolPreparationsTable(BaseDashboardTable):
+    """Site root preparations table"""
+
+    _title = _("CONTRIBUTOR - Your {0} prepared contents")
+
+
+@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolPreparationsTable), provides=IValues)
+class SharedToolPreparationsValues(ContextRequestViewAdapter):
+    """Site root preparations values adapter"""
+
+    @property
+    def values(self):
+        catalog = get_utility(ICatalog)
+        workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
+        params = And(Eq(catalog['content_type'], self.context.shared_content_type),
+                     Or(Eq(catalog['role:owner'], self.request.principal.id),
+                        Eq(catalog['role:contributor'], self.request.principal.id)),
+                     Any(catalog['workflow_state'], workflow.update_states))
+        return unique(CatalogResultSet(CatalogQuery(catalog).query(params,
+                                                                   sort_index='modified_date',
+                                                                   reverse=True)))
+
+
+@pagelet_config(name='my-preparations.html', context=ISharedTool, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
+@implementer(IInnerPage)
+class SharedToolPreparationsView(AdminView, ContainerView):
+    """Site root preparations view"""
+
+    table_class = SharedToolPreparationsTable
+
+
+@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolPreparationsView), provides=IPageHeader)
+class SharedToolPreparationsHeaderAdapter(DefaultPageHeaderAdapter):
+    """Site root preparations header adapter"""
+
+    back_url = '#dashboard.html'
+    icon_class = 'fa fa-fw fa-user'
+
+    @property
+    def title(self):
+        return II18n(self.context).query_attribute('title', request=self.request)
+
+    subtitle = _("Your prepared contents")
+
+
+#
+# My publications
+# Dashboard of owned and modified contents which are published
+#
+
+@viewlet_config(name='my-publications.menu', context=ISharedTool, layer=IAdminLayer,
+                manager=IMyDashboardMenu, permission=VIEW_SYSTEM_PERMISSION, weight=10)
+class SharedToolPublicationsMenu(MenuItem):
+    """Shared tool publications dashboard menu"""
+
+    label = _("My publications")
+    icon_class = None
+    url = '#my-publications.html'
+
+
+@implementer(ISharedToolDashboardTable)
+class SharedToolPublicationsTable(BaseDashboardTable):
+    """Shared tool publications table"""
+
+    _title = _("CONTRIBUTOR - Your {0} published contents")
+
+
+@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolPublicationsTable), provides=IValues)
+class SharedToolPublicationsValues(ContextRequestViewAdapter):
+    """Shared tool publications values adapter"""
+
+    @property
+    def values(self):
+        catalog = get_utility(ICatalog)
+        workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
+        params = And(Eq(catalog['content_type'], self.context.shared_content_type),
+                     Or(Eq(catalog['role:owner'], self.request.principal.id),
+                        Eq(catalog['role:contributor'], self.request.principal.id)),
+                     Any(catalog['workflow_state'], workflow.published_states))
+        return unique(CatalogResultSet(CatalogQuery(catalog).query(params,
+                                                                   sort_index='modified_date',
+                                                                   reverse=True)))
+
+
+@pagelet_config(name='my-publications.html', context=ISharedTool, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
+@implementer(IInnerPage)
+class SharedToolPublicationsView(AdminView, ContainerView):
+    """Shared tool publications view"""
+
+    table_class = SharedToolPublicationsTable
+
+
+@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolPublicationsView), provides=IPageHeader)
+class SharedToolPublicationsHeaderAdapter(DefaultPageHeaderAdapter):
+    """Shared tool publications header adapter"""
+
+    back_url = '#dashboard.html'
+    icon_class = 'fa fa-fw fa-user'
+
+    @property
+    def title(self):
+        return II18n(self.context).query_attribute('title', request=self.request)
+
+    subtitle = _("Your published contents")
+
+
+#
+# My retired contents
+# Dashboard of owned and modified contents which are retired
+#
+
+@viewlet_config(name='my-retired-contents.menu', context=ISharedTool, layer=IAdminLayer,
+                manager=IMyDashboardMenu, permission=VIEW_SYSTEM_PERMISSION, weight=15)
+class SharedToolRetiredMenu(MenuItem):
+    """Shared tool retired contents dashboard menu"""
+
+    label = _("My retired contents")
+    icon_class = None
+    url = '#my-retired-contents.html'
+
+
+@implementer(ISharedToolDashboardTable)
+class SharedToolRetiredContentsTable(BaseDashboardTable):
+    """Shared tool retired contents table"""
+
+    _title = _("CONTRIBUTOR - Your {0} retired contents")
+
+
+@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolRetiredContentsTable), provides=IValues)
+class SharedToolRetiredContentsValues(ContextRequestViewAdapter):
+    """Shared tool retired contents values adapter"""
+
+    @property
+    def values(self):
+        catalog = get_utility(ICatalog)
+        workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
+        params = And(Eq(catalog['content_type'], self.context.shared_content_type),
+                     Or(Eq(catalog['role:owner'], self.request.principal.id),
+                        Eq(catalog['role:contributor'], self.request.principal.id)),
+                     Any(catalog['workflow_state'], workflow.retired_states))
+        return unique(CatalogResultSet(CatalogQuery(catalog).query(params,
+                                                                   sort_index='modified_date',
+                                                                   reverse=True)))
+
+
+@pagelet_config(name='my-retired-contents.html', context=ISharedTool, layer=IPyAMSLayer,
+                permission=VIEW_SYSTEM_PERMISSION)
+@implementer(IInnerPage)
+class SharedToolRetiredContentsView(AdminView, ContainerView):
+    """Shared tool retired contents view"""
+
+    table_class = SharedToolRetiredContentsTable
+
+
+@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolRetiredContentsView), provides=IPageHeader)
+class SharedToolRetiredContentsHeaderAdapter(DefaultPageHeaderAdapter):
+    """Shared tool retired contents header adapter"""
+
+    back_url = '#dashboard.html'
+    icon_class = 'fa fa-fw fa-user'
+
+    @property
+    def title(self):
+        return II18n(self.context).query_attribute('title', request=self.request)
+
+    subtitle = _("Your retired contents")
+
+
+#
+# My archived contents
+# Dashboard of owned and modified contents which are archived
+#
+
+@viewlet_config(name='my-archived-contents.menu', context=ISharedTool, layer=IAdminLayer,
+                manager=IMyDashboardMenu, permission=VIEW_SYSTEM_PERMISSION, weight=20)
+class SharedToolArchivedMenu(MenuItem):
+    """Shared tool archived contents dashboard menu"""
+
+    label = _("My archived contents")
+    icon_class = None
+    url = '#my-archived-contents.html'
+
+
+@implementer(ISharedToolDashboardTable)
+class SharedToolArchivedContentsTable(BaseDashboardTable):
+    """Shared tool archived contents table"""
+
+    _title = _("CONTRIBUTOR - Your {0} archived contents")
+
+
+@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolArchivedContentsTable), provides=IValues)
+class SharedToolArchivedContentsValues(ContextRequestViewAdapter):
+    """Shared tool archived contents values adapter"""
+
+    @property
+    def values(self):
+        catalog = get_utility(ICatalog)
+        principal_id = self.request.principal.id
+        workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
+        params = And(Eq(catalog['content_type'], self.context.shared_content_type),
+                     Or(Eq(catalog['role:owner'], principal_id),
+                        Eq(catalog['role:contributor'], principal_id)),
+                     Any(catalog['workflow_state'], workflow.readonly_states))
+        return unique(map(lambda x: sorted((version for version in
+                                            IWorkflowVersions(x).get_versions(IWorkflow(x).readonly_states)
+                                            if principal_id in (version.owner | version.contributors)),
+                                           key=lambda x: IWorkflowState(x).version_id,
+                                           reverse=True)[0],
+                          CatalogResultSet(CatalogQuery(catalog).query(params,
+                                                                       sort_index='modified_date',
+                                                                       reverse=True))))
+
+
+@pagelet_config(name='my-archived-contents.html', context=ISharedTool, layer=IPyAMSLayer,
+                permission=VIEW_SYSTEM_PERMISSION)
+@implementer(IInnerPage)
+class SharedToolArchivedContentsView(AdminView, ContainerView):
+    """Shared tool archived contents view"""
+
+    table_class = SharedToolArchivedContentsTable
+
+
+@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolArchivedContentsView), provides=IPageHeader)
+class SharedToolArchivedContentsHeaderAdapter(DefaultPageHeaderAdapter):
+    """Shared tool archived contents header adapter"""
+
+    back_url = '#dashboard.html'
+    icon_class = 'fa fa-fw fa-user'
+
+    @property
+    def title(self):
+        return II18n(self.context).query_attribute('title', request=self.request)
+
+    subtitle = _("Your archived contents")
+
+
+#
+# All interventions
+#
+
+@viewlet_config(name='all-interventions.menu', context=ISharedTool, layer=IAdminLayer,
+                manager=IContentManagementMenu, permission=VIEW_SYSTEM_PERMISSION, weight=10)
+@viewletmanager_config(name='all-interventions.menu', layer=IAdminLayer, provides=IAllContentsMenu)
+@implementer(IAllContentsMenu)
+class SharedToolAllContentsMenu(MenuItem):
+    """Shared tool 'all contents' dashboard menu"""
+
+    label = _("Other interventions")
+    icon_class = 'fa-pencil-square'
+    url = '#'
+
+
+#
+# Last publications
+# Dashboard of all published contents
+#
+
+@viewlet_config(name='all-publications.menu', context=ISharedTool, layer=IAdminLayer,
+                manager=IAllContentsMenu, permission=VIEW_SYSTEM_PERMISSION, weight=10)
+class SharedToolAllPublicationsMenu(MenuItem):
+    """Shared tool published contents dashboard menu"""
+
+    label = _("Last publications")
+    icon_class = None
+    url = '#all-publications.html'
+
+
+@implementer(ISharedToolDashboardTable)
+class SharedToolAllPublicationsTable(BaseDashboardTable):
+    """Shared tool published contents table"""
+
+    _title = _("CONTRIBUTORS - Last published contents (in the limit of 50)")
+
+
+@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolAllPublicationsTable), provides=IValues)
+class SharedToolAllPublicationsValues(ContextRequestViewAdapter):
+    """Shared tool published contents values adapter"""
+
+    @property
+    def values(self):
+        catalog = get_utility(ICatalog)
+        workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
+        params = And(Eq(catalog['content_type'], self.context.shared_content_type),
+                     Any(catalog['workflow_state'], workflow.published_states))
+        return unique(CatalogResultSet(CatalogQuery(catalog).query(params,
+                                                                   limit=50,
+                                                                   sort_index='modified_date',
+                                                                   reverse=True)))
+
+
+@pagelet_config(name='all-publications.html', context=ISharedTool, layer=IPyAMSLayer,
+                permission=VIEW_SYSTEM_PERMISSION)
+@implementer(IInnerPage)
+class SharedToolAllPublicationsView(AdminView, ContainerView):
+    """Shared tool published contents view"""
+
+    table_class = SharedToolAllPublicationsTable
+
+
+@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolAllPublicationsView), provides=IPageHeader)
+class SharedToolAllPublicationsHeaderAdapter(DefaultPageHeaderAdapter):
+    """Shared tool published contents header adapter"""
+
+    back_url = '#dashboard.html'
+    icon_class = 'fa fa-fw fa-pencil-square'
+
+    @property
+    def title(self):
+        return II18n(self.context).query_attribute('title', request=self.request)
+
+    subtitle = _("Last published contents")
+
+
+#
+# Last updates
+# Dashboard of all updated contents
+#
+
+@viewlet_config(name='all-updates.menu', context=ISharedTool, layer=IAdminLayer,
+                manager=IAllContentsMenu, permission=VIEW_SYSTEM_PERMISSION, weight=20)
+class SharedToolAllUpdatesMenu(MenuItem):
+    """Shared tool updated contents dashboard menu"""
+
+    label = _("Last updates")
+    icon_class = None
+    url = '#all-updates.html'
+
+
+@implementer(ISharedToolDashboardTable)
+class SharedToolAllUpdatesTable(BaseDashboardTable):
+    """Shared tool updated contents table"""
+
+    _title = _("CONTRIBUTORS - Last updated contents (in the limit of 50)")
+
+
+@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolAllUpdatesTable), provides=IValues)
+class SharedToolAllUpdatesValues(ContextRequestViewAdapter):
+    """Shared tool updated contents values adapter"""
+
+    @property
+    def values(self):
+        catalog = get_utility(ICatalog)
+        params = Eq(catalog['content_type'], self.context.shared_content_type)
+        return unique(CatalogResultSet(CatalogQuery(catalog).query(params,
+                                                                   limit=50,
+                                                                   sort_index='modified_date',
+                                                                   reverse=True)))
+
+
+@pagelet_config(name='all-updates.html', context=ISharedTool, layer=IPyAMSLayer,
+                permission=VIEW_SYSTEM_PERMISSION)
+@implementer(IInnerPage)
+class SharedToolAllUpdatesView(AdminView, ContainerView):
+    """Shared tool updated contents view"""
+
+    table_class = SharedToolAllUpdatesTable
+
+
+@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolAllUpdatesView), provides=IPageHeader)
+class SharedToolAllUpdatesHeaderAdapter(DefaultPageHeaderAdapter):
+    """Shared tool updated contents header adapter"""
+
+    back_url = '#dashboard.html'
+    icon_class = 'fa fa-fw fa-pencil-square'
+
+    @property
+    def title(self):
+        return II18n(self.context).query_attribute('title', request=self.request)
+
+    subtitle = _("Last updated contents")