Added support for collections
authorThierry Florac <thierry.florac@onf.fr>
Wed, 11 Jul 2018 10:18:10 +0200
changeset 829 f933926ed0a1
parent 828 bf12603398b2
child 830 ea722c2352b7
Added support for collections
src/pyams_content/component/theme/__init__.py
src/pyams_content/component/theme/interfaces/__init__.py
src/pyams_content/component/theme/portlet.py
src/pyams_content/component/theme/zmi/__init__.py
src/pyams_content/component/theme/zmi/manager.py
src/pyams_content/component/theme/zmi/portlet.py
src/pyams_content/generations/__init__.py
src/pyams_content/locales/fr/LC_MESSAGES/pyams_content.mo
src/pyams_content/locales/fr/LC_MESSAGES/pyams_content.po
src/pyams_content/locales/pyams_content.pot
src/pyams_content/shared/view/interfaces/__init__.py
src/pyams_content/shared/view/manager.py
src/pyams_content/shared/view/theme.py
src/pyams_content/shared/view/zmi/theme.py
--- a/src/pyams_content/component/theme/__init__.py	Tue Jul 10 16:59:55 2018 +0200
+++ b/src/pyams_content/component/theme/__init__.py	Wed Jul 11 10:18:10 2018 +0200
@@ -18,7 +18,8 @@
 # import interfaces
 from pyams_content.component.theme.interfaces import IThemesManagerTarget, IThemesManager, THEMES_MANAGER_KEY, \
     IThemesTarget, IThemesInfo, THEMES_INFO_KEY, ITagsManager, ITagsManagerTarget, TAGS_MANAGER_KEY, ITagsInfo, \
-    ITagsTarget, TAGS_INFO_KEY
+    ITagsTarget, TAGS_INFO_KEY, ICollectionsManager, ICollectionsManagerTarget, COLLECTIONS_MANAGER_KEY, \
+    ICollectionsInfo, ICollectionsTarget, COLLECTIONS_INFO_KEY
 from pyams_content.features.checker.interfaces import IContentChecker, ERROR_VALUE
 
 # import packages
@@ -116,7 +117,7 @@
     """Themes info content checker"""
 
     label = _("Themes")
-    weight = 200
+    weight = 210
 
     def inner_check(self, request):
         output = []
@@ -126,3 +127,51 @@
             output.append(translate(ERROR_VALUE).format(field=translate(IThemesInfo['themes'].title),
                                                         message=translate(_("no defined theme"))))
         return output
+
+
+#
+# Collections management
+#
+
+@implementer(ICollectionsManager)
+class CollectionsManager(Persistent, Contained):
+    """Collections manager persistent class"""
+
+    thesaurus_name = FieldProperty(ICollectionsManager['thesaurus_name'])
+    extract_name = FieldProperty(ICollectionsManager['extract_name'])
+
+
+@adapter_config(context=ICollectionsManagerTarget, provides=ICollectionsManager)
+def collections_manager_factory(target):
+    """Collections manager factory"""
+    return get_annotation_adapter(target, COLLECTIONS_MANAGER_KEY, CollectionsManager, name='++collections-manager++')
+
+
+@implementer(ICollectionsInfo)
+class CollectionsInfo(Persistent, Contained):
+    """Collections info persistent class"""
+
+    collections = FieldProperty(ICollectionsInfo['collections'])
+
+
+@adapter_config(context=ICollectionsTarget, provides=ICollectionsInfo)
+def collections_info_factory(target):
+    """Collections info factory"""
+    return get_annotation_adapter(target, COLLECTIONS_INFO_KEY, CollectionsInfo, name='++collections++')
+
+
+@adapter_config(name='collections', context=ICollectionsTarget, provides=IContentChecker)
+class CollectionsContentChecker(BaseContentChecker):
+    """Collections info content checker"""
+
+    label = _("Collections")
+    weight = 215
+
+    def inner_check(self, request):
+        output = []
+        translate = request.localizer.translate
+        collections = ICollectionsInfo(self.context)
+        if not collections.collections:
+            output.append(translate(ERROR_VALUE).format(field=translate(ICollectionsInfo['collections'].title),
+                                                        message=translate(_("no defined collection"))))
+        return output
--- a/src/pyams_content/component/theme/interfaces/__init__.py	Tue Jul 10 16:59:55 2018 +0200
+++ b/src/pyams_content/component/theme/interfaces/__init__.py	Wed Jul 11 10:18:10 2018 +0200
@@ -58,13 +58,16 @@
 class IPortletTagsSettings(Interface):
     """Interface for portlet settings managing tags"""
 
+    tags = ThesaurusTermsListField(title=_("Tags"),
+                                   required=False)
+
 
 class IPortletTagsSettingsTarget(Interface):
     """Marker interface for portlet settings managing tags"""
 
 
 #
-# Tags management
+# Themes management
 #
 
 THEMES_MANAGER_KEY = 'pyams_content.themes.manager'
@@ -96,6 +99,50 @@
 class IPortletThemesSettings(Interface):
     """Interface for portlet settings managing themes"""
 
+    themes = ThesaurusTermsListField(title=_("Themes"),
+                                     required=False)
+
 
 class IPortletThemesSettingsTarget(Interface):
     """Marker interface for portlet settings managing themes"""
+
+
+#
+# Collections management
+#
+
+COLLECTIONS_MANAGER_KEY = 'pyams_content.collections.manager'
+COLLECTIONS_INFO_KEY = 'pyams_content.collections.info'
+
+
+class ICollectionsManager(IThesaurusContextManager):
+    """Collections manager interface"""
+
+
+class ICollectionsManagerTarget(IThesaurusContextManagerTarget):
+    """Marker interface for tools managing collections"""
+
+
+class ICollectionsInfo(Interface):
+    """Collections information interface"""
+
+    collections = ThesaurusTermsListField(title=_("Collections"),
+                                          required=False)
+
+
+class ICollectionsTarget(Interface):
+    """Collections target interface"""
+
+
+PORTLET_SETTINGS_COLLECTIONS_KEY = 'pyams_content.portlet.collections.settings'
+
+
+class IPortletCollectionsSettings(Interface):
+    """Interface for portlet settings managing collections"""
+
+    collections = ThesaurusTermsListField(title=_("Collections"),
+                                          required=False)
+
+
+class IPortletCollectionsSettingsTarget(Interface):
+    """Marker interface for portlet settings managing collections"""
--- a/src/pyams_content/component/theme/portlet.py	Tue Jul 10 16:59:55 2018 +0200
+++ b/src/pyams_content/component/theme/portlet.py	Wed Jul 11 10:18:10 2018 +0200
@@ -16,8 +16,10 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.theme.interfaces import IPortletThemesSettings, IPortletThemesSettingsTarget, \
-    PORTLET_SETTINGS_THEMES_KEY
+from pyams_content.component.theme.interfaces import \
+    IPortletThemesSettings, IPortletThemesSettingsTarget, PORTLET_SETTINGS_THEMES_KEY, \
+    IPortletTagsSettings, IPortletTagsSettingsTarget, PORTLET_SETTINGS_TAGS_KEY, \
+    IPortletCollectionsSettings, IPortletCollectionsSettingsTarget, PORTLET_SETTINGS_COLLECTIONS_KEY
 from zope.traversing.interfaces import ITraversable
 
 # import packages
@@ -25,17 +27,51 @@
 from pyams_utils.adapter import adapter_config, ContextAdapter, get_annotation_adapter
 from zope.container.contained import Contained
 from zope.interface import implementer
+from zope.schema.fieldproperty import FieldProperty
 
 
+#
+# Portlets tags management
+#
+
+@implementer(IPortletTagsSettings)
+class PortletTagsSettings(Persistent, Contained):
+    """Portlet tags settings"""
+
+    tags = FieldProperty(IPortletTagsSettings['tags'])
+
+
+@adapter_config(context=IPortletTagsSettingsTarget, provides=IPortletTagsSettings)
+def portlet_tags_settings_factory(context):
+    """Portlet tags settings adapter"""
+    return get_annotation_adapter(context, PORTLET_SETTINGS_TAGS_KEY, PortletTagsSettings,
+                                  name='++tags++')
+
+
+@adapter_config(name='tags', context=IPortletTagsSettingsTarget, provides=ITraversable)
+class TagsPortletsSettingsTraverser(ContextAdapter):
+    """++tags++ portlet settings adapter"""
+
+    def traverse(self, name, furtherpath=None):
+        return IPortletTagsSettings(self.context)
+
+
+#
+# Portlets themes management
+#
+
 @implementer(IPortletThemesSettings)
 class PortletThemesSettings(Persistent, Contained):
     """Portlet themes settings"""
 
+    themes = FieldProperty(IPortletThemesSettings['themes'])
+
 
 @adapter_config(context=IPortletThemesSettingsTarget, provides=IPortletThemesSettings)
 def portlet_themes_settings_factory(context):
     """Portlet themes settings adapter"""
-    return get_annotation_adapter(context, PORTLET_SETTINGS_THEMES_KEY, PortletThemesSettings, name='++themes++')
+    return get_annotation_adapter(context, PORTLET_SETTINGS_THEMES_KEY, PortletThemesSettings,
+                                  name='++themes++')
 
 
 @adapter_config(name='themes', context=IPortletThemesSettingsTarget, provides=ITraversable)
@@ -44,3 +80,29 @@
 
     def traverse(self, name, furtherpath=None):
         return IPortletThemesSettings(self.context)
+
+
+#
+# Portlets collections management
+#
+
+@implementer(IPortletCollectionsSettings)
+class PortletCollectionsSettings(Persistent, Contained):
+    """Portlet collections settings"""
+
+    collections = FieldProperty(IPortletCollectionsSettings['collections'])
+
+
+@adapter_config(context=IPortletCollectionsSettingsTarget, provides=IPortletCollectionsSettings)
+def portlet_collections_settings_factory(context):
+    """Portlet collections settings adapter"""
+    return get_annotation_adapter(context, PORTLET_SETTINGS_COLLECTIONS_KEY, PortletCollectionsSettings,
+                                  name='++collections++')
+
+
+@adapter_config(name='collections', context=IPortletCollectionsSettingsTarget, provides=ITraversable)
+class CollectionsPortletsSettingsTraverser(ContextAdapter):
+    """++collections++ portlet settings adapter"""
+
+    def traverse(self, name, furtherpath=None):
+        return IPortletCollectionsSettings(self.context)
--- a/src/pyams_content/component/theme/zmi/__init__.py	Tue Jul 10 16:59:55 2018 +0200
+++ b/src/pyams_content/component/theme/zmi/__init__.py	Wed Jul 11 10:18:10 2018 +0200
@@ -17,12 +17,12 @@
 
 # import interfaces
 from pyams_content.component.theme import ITagsTarget, ITagsInfo, ITagsManager, IThemesTarget, IThemesInfo, \
-    IThemesManagerTarget, IThemesManager
+    IThemesManagerTarget, IThemesManager, ICollectionsTarget, ICollectionsInfo, ICollectionsManager, \
+    ICollectionsManagerTarget
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
 from pyams_form.interfaces.form import IWidgetForm
 from pyams_skin.interfaces import IInnerPage, IPageHeader, IDialog
 from pyams_skin.layer import IPyAMSLayer
-from pyams_thesaurus.interfaces.thesaurus import IThesaurus
 from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
 from pyams_zmi.interfaces.menu import IPropertiesMenu
 from pyams_zmi.layer import IAdminLayer
@@ -32,9 +32,8 @@
 from pyams_form.form import ajax_config, AJAXEditForm, EditForm
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.viewlet.menu import MenuItem
-from pyams_thesaurus.widget import ThesaurusTermsTreeFieldWidget
+from pyams_thesaurus.zmi.widget import ThesaurusTermsTreeFieldWidget
 from pyams_utils.adapter import adapter_config
-from pyams_utils.registry import query_utility
 from pyams_utils.traversing import get_parent
 from pyams_viewlet.viewlet import viewlet_config
 from pyams_zmi.form import AdminEditForm, AdminDialogEditForm
@@ -72,33 +71,13 @@
     fields = field.Fields(ITagsInfo)
     fields['tags'].widgetFactory = ThesaurusTermsTreeFieldWidget
 
-    def __init__(self, context, request):
-        super(TagsEditForm, self).__init__(context, request)
-        manager = ITagsManager(request.root)
-        self.thesaurus_name = manager.thesaurus_name
-        self.extract_name = manager.extract_name
-
     def updateWidgets(self, prefix=None):
         super(TagsEditForm, self).updateWidgets(prefix)
-        widget = self.widgets['tags']
-        widget.thesaurus_name = self.thesaurus_name
-        widget.extract_name = self.extract_name
-
-    @property
-    def top_terms(self):
-        thesaurus = query_utility(IThesaurus, name=self.thesaurus_name)
-        if thesaurus is not None:
-            return sorted(thesaurus.get_top_terms(extract=self.extract_name),
-                          key=lambda x: x.label)
-        else:
-            return ()
-
-    def get_subterms(self, term):
-        for subterm in term.specifics:
-            if (not self.extract_name) or (self.extract_name in subterm.extracts):
-                yield subterm
-                for another in self.get_subterms(subterm):
-                    yield another
+        if 'tags' in self.widgets:
+            widget = self.widgets['tags']
+            manager = ITagsManager(self.request.root)
+            widget.thesaurus_name = manager.thesaurus_name
+            widget.extract_name = manager.extract_name
 
 
 @adapter_config(context=(ITagsTarget, IAdminLayer, TagsEditForm), provides=IPageHeader)
@@ -166,3 +145,57 @@
     """Dialog themes edit form"""
 
     dialog_class = 'modal-large'
+
+
+#
+# Collections management
+#
+
+@viewlet_config(name='collections.menu', context=ICollectionsTarget, layer=IAdminLayer,
+                manager=IPropertiesMenu, permission=VIEW_SYSTEM_PERMISSION, weight=354)
+class CollectionsMenu(MenuItem):
+    """Collections menu"""
+
+    label = _("Collections...")
+    icon_class = 'fa-book'
+    url = '#collections.html'
+
+
+@pagelet_config(name='collections.html', context=ICollectionsTarget, layer=IPyAMSLayer,
+                permission=VIEW_SYSTEM_PERMISSION)
+@ajax_config(name='collections.json', context=ICollectionsTarget, layer=IPyAMSLayer,
+             permission=MANAGE_CONTENT_PERMISSION)
+@implementer(IWidgetForm, IInnerPage)
+class CollectionsEditForm(AdminEditForm):
+    """Collections edit form"""
+
+    legend = _("Content collections")
+
+    label_css_class = 'control-label hidden'
+    input_css_class = 'col-md-12'
+
+    fields = field.Fields(ICollectionsInfo)
+    fields['collections'].widgetFactory = ThesaurusTermsTreeFieldWidget
+
+    def __init__(self, context, request):
+        super(CollectionsEditForm, self).__init__(context, request)
+        target = get_parent(self.context, ICollectionsManagerTarget)
+        manager = ICollectionsManager(target)
+        self.thesaurus_name = manager.thesaurus_name
+        self.extract_name = manager.extract_name
+
+    def updateWidgets(self, prefix=None):
+        super(CollectionsEditForm, self).updateWidgets(prefix)
+        if 'collections' in self.widgets:
+            widget = self.widgets['collections']
+            target = get_parent(self.context, ICollectionsManagerTarget)
+            manager = ICollectionsManager(target)
+            widget.thesaurus_name = manager.thesaurus_name
+            widget.extract_name = manager.extract_name
+
+
+@adapter_config(context=(ICollectionsTarget, IAdminLayer, CollectionsEditForm), provides=IPageHeader)
+class CollectionsHeaderAdapter(WfSharedContentHeaderAdapter):
+    """Shared content collections header adapter"""
+
+    icon_class = 'fa fa-fw fa-book'
--- a/src/pyams_content/component/theme/zmi/manager.py	Tue Jul 10 16:59:55 2018 +0200
+++ b/src/pyams_content/component/theme/zmi/manager.py	Wed Jul 11 10:18:10 2018 +0200
@@ -9,6 +9,7 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
+from pyams_content.component.theme import ICollectionsManagerTarget, ICollectionsManager
 
 __docformat__ = 'restructuredtext'
 
@@ -106,7 +107,7 @@
 class ThemesManagerEditForm(AdminDialogEditForm):
     """Themes manager edit form"""
 
-    prefix = 'manager_tags.'
+    prefix = 'manager_themes.'
 
     legend = _("Selected themes")
 
@@ -130,3 +131,50 @@
             }
         }
         alsoProvides(widget, IObjectData)
+
+
+#
+# Collections management view
+#
+
+@viewlet_config(name='collections-manager.menu', context=ICollectionsManagerTarget, layer=IAdminLayer,
+                manager=IPropertiesMenu, permission=MANAGE_TOOL_PERMISSION, weight=42)
+class CollectionsManagerMenu(MenuItem):
+    """Collections menu"""
+
+    label = _("Collections settings...")
+    icon_class = 'fa-book'
+    url = 'collections.html'
+    modal_target = True
+
+
+@pagelet_config(name='collections.html', context=ICollectionsManagerTarget, layer=IPyAMSLayer,
+                permission=MANAGE_TOOL_PERMISSION)
+@ajax_config(name='collections.json', context=ICollectionsManagerTarget, layer=IPyAMSLayer)
+class CollectionsManagerEditForm(AdminDialogEditForm):
+    """Collections manager edit form"""
+
+    prefix = 'manager_collections.'
+
+    legend = _("Selected collections")
+
+    fields = field.Fields(ICollectionsManager)
+    edit_permission = MANAGE_TOOL_PERMISSION
+
+    def updateWidgets(self, prefix=None):
+        super(CollectionsManagerEditForm, self).updateWidgets(prefix)
+        widget = self.widgets['thesaurus_name']
+        widget.object_data = {
+            'ams-plugins': 'pyams_content',
+            'ams-plugin-pyams_content-src': get_resource_path(pyams_content),
+            'ams-plugin-pyams_content-callback': 'PyAMS_content.collections.initExtracts',
+            'ams-plugin-pyams_content-async': 'false'
+        }
+        alsoProvides(widget, IObjectData)
+        widget = self.widgets['extract_name']
+        widget.object_data = {
+            'ams-events-handlers': {
+                'select2-open': 'PyAMS_content.themes.getExtracts'
+            }
+        }
+        alsoProvides(widget, IObjectData)
--- a/src/pyams_content/component/theme/zmi/portlet.py	Tue Jul 10 16:59:55 2018 +0200
+++ b/src/pyams_content/component/theme/zmi/portlet.py	Wed Jul 11 10:18:10 2018 +0200
@@ -16,7 +16,8 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.theme.interfaces import IPortletThemesSettingsTarget
+from pyams_content.component.theme.interfaces import IPortletTagsSettingsTarget, IPortletThemesSettingsTarget, \
+    IPortletCollectionsSettingsTarget, IPortletTagsSettings, IPortletThemesSettings, IPortletCollectionsSettings
 from pyams_form.interfaces.form import IInnerTabForm
 from pyams_skin.layer import IPyAMSLayer
 
@@ -25,11 +26,25 @@
 from pyams_utils.adapter import adapter_config
 from pyams_zmi.form import InnerAdminEditForm
 from z3c.form import field
-from zope.interface import Interface
 
 from pyams_content import _
 
 
+@adapter_config(name='tags', context=(IPortletTagsSettingsTarget, IPyAMSLayer, PortletSettingsEditor),
+                provides=IInnerTabForm)
+class PortletSettingsTagsEditor(InnerAdminEditForm):
+    """Portlet settings for tags"""
+
+    prefix = 'tags_form.'
+
+    tab_label = _("Tags")
+    legend = None
+
+    fields = field.Fields(IPortletTagsSettings)
+
+    weight = 50
+
+
 @adapter_config(name='themes', context=(IPortletThemesSettingsTarget, IPyAMSLayer, PortletSettingsEditor),
                 provides=IInnerTabForm)
 class PortletSettingsThemesEditor(InnerAdminEditForm):
@@ -40,6 +55,21 @@
     tab_label = _("Themes")
     legend = None
 
-    fields = field.Fields(Interface)
+    fields = field.Fields(IPortletThemesSettings)
+
+    weight = 52
+
 
-    weight = 50
+@adapter_config(name='collections', context=(IPortletCollectionsSettingsTarget, IPyAMSLayer, PortletSettingsEditor),
+                provides=IInnerTabForm)
+class PortletSettingsCollectionsEditor(InnerAdminEditForm):
+    """Portlet settings for collections"""
+
+    prefix = 'collections_form.'
+
+    tab_label = _("Collections")
+    legend = None
+
+    fields = field.Fields(IPortletCollectionsSettings)
+
+    weight = 54
--- a/src/pyams_content/generations/__init__.py	Tue Jul 10 16:59:55 2018 +0200
+++ b/src/pyams_content/generations/__init__.py	Wed Jul 11 10:18:10 2018 +0200
@@ -19,7 +19,7 @@
 
 # import interfaces
 from pyams_catalog.interfaces import MINUTE_RESOLUTION, DATE_RESOLUTION
-from pyams_content.component.theme import ITagsInfo, IThemesInfo
+from pyams_content.component.theme import ITagsInfo, IThemesInfo, ICollectionsInfo
 from pyams_content.interfaces import IBaseContent, WEBMASTER_ROLE, OWNER_ROLE, PILOT_ROLE, MANAGER_ROLE, \
     CONTRIBUTOR_ROLE
 from pyams_content.root.interfaces import ISiteRootToolsConfiguration
@@ -145,7 +145,11 @@
     ('themes_all', ThesaurusTermsListFieldIndex, {'interface': IThemesInfo,
                                                   'discriminator': 'themes',
                                                   'include_parents': True,
-                                                  'include_synonyms': True})
+                                                  'include_synonyms': True}),
+    ('collections', ThesaurusTermsListFieldIndex, {'interface': ICollectionsInfo,
+                                                   'discriminator': 'collections',
+                                                   'include_parents': False,
+                                                   'include_synonyms': False})
 ]
 
 
Binary file src/pyams_content/locales/fr/LC_MESSAGES/pyams_content.mo has changed
--- a/src/pyams_content/locales/fr/LC_MESSAGES/pyams_content.po	Tue Jul 10 16:59:55 2018 +0200
+++ b/src/pyams_content/locales/fr/LC_MESSAGES/pyams_content.po	Wed Jul 11 10:18:10 2018 +0200
@@ -5,7 +5,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE 1.0\n"
-"POT-Creation-Date: 2018-07-10 16:58+0200\n"
+"POT-Creation-Date: 2018-07-11 09:29+0200\n"
 "PO-Revision-Date: 2015-09-10 10:42+0200\n"
 "Last-Translator: Thierry Florac <tflorac@ulthar.net>\n"
 "Language-Team: French\n"
@@ -1279,59 +1279,90 @@
 msgid "Presentation template used for this header"
 msgstr "Mode de rendu utilisé par ce chapô"
 
-#: src/pyams_content/component/theme/__init__.py:70
+#: src/pyams_content/component/theme/__init__.py:71
+#: src/pyams_content/component/theme/zmi/portlet.py:40
 #: src/pyams_content/component/theme/interfaces/__init__.py:47
+#: src/pyams_content/component/theme/interfaces/__init__.py:61
 msgid "Tags"
 msgstr "Tags"
 
-#: src/pyams_content/component/theme/__init__.py:118
-#: src/pyams_content/component/theme/zmi/portlet.py:40
-#: src/pyams_content/component/theme/interfaces/__init__.py:85
+#: src/pyams_content/component/theme/__init__.py:119
+#: src/pyams_content/component/theme/zmi/portlet.py:55
+#: src/pyams_content/component/theme/interfaces/__init__.py:88
+#: src/pyams_content/component/theme/interfaces/__init__.py:102
 msgid "Themes"
 msgstr "Thèmes"
 
-#: src/pyams_content/component/theme/__init__.py:79
+#: src/pyams_content/component/theme/__init__.py:167
+#: src/pyams_content/component/theme/zmi/portlet.py:70
+#: src/pyams_content/component/theme/interfaces/__init__.py:129
+#: src/pyams_content/component/theme/interfaces/__init__.py:143
+msgid "Collections"
+msgstr "Collections"
+
+#: src/pyams_content/component/theme/__init__.py:80
 msgid "no defined tag"
 msgstr "aucun tag défini"
 
-#: src/pyams_content/component/theme/__init__.py:127
+#: src/pyams_content/component/theme/__init__.py:128
 msgid "no defined theme"
 msgstr "aucun thème défini"
 
-#: src/pyams_content/component/theme/zmi/__init__.py:56
-#: src/pyams_content/shared/view/zmi/theme.py:53
+#: src/pyams_content/component/theme/__init__.py:176
+msgid "no defined collection"
+msgstr "aucune collection définie"
+
+#: src/pyams_content/component/theme/zmi/__init__.py:55
+#: src/pyams_content/shared/view/zmi/theme.py:56
 msgid "Tags..."
 msgstr "Tags"
 
-#: src/pyams_content/component/theme/zmi/__init__.py:67
+#: src/pyams_content/component/theme/zmi/__init__.py:66
 msgid "Content tags"
 msgstr "Tags du contenu"
 
-#: src/pyams_content/component/theme/zmi/__init__.py:120
-#: src/pyams_content/shared/view/zmi/theme.py:92
+#: src/pyams_content/component/theme/zmi/__init__.py:99
+#: src/pyams_content/shared/view/zmi/theme.py:99
 msgid "Themes..."
 msgstr "Thèmes"
 
-#: src/pyams_content/component/theme/zmi/__init__.py:128
+#: src/pyams_content/component/theme/zmi/__init__.py:107
 msgid "Content themes"
 msgstr "Thèmes du contenu"
 
-#: src/pyams_content/component/theme/zmi/manager.py:50
+#: src/pyams_content/component/theme/zmi/__init__.py:159
+#: src/pyams_content/shared/view/zmi/theme.py:141
+msgid "Collections..."
+msgstr "Collections"
+
+#: src/pyams_content/component/theme/zmi/__init__.py:172
+msgid "Content collections"
+msgstr "Collections associées au contenu"
+
+#: src/pyams_content/component/theme/zmi/manager.py:51
 msgid "Tags settings..."
 msgstr "Paramétrage des tags"
 
-#: src/pyams_content/component/theme/zmi/manager.py:64
+#: src/pyams_content/component/theme/zmi/manager.py:65
 msgid "Selected tags"
 msgstr "Tags sélectionnés"
 
-#: src/pyams_content/component/theme/zmi/manager.py:97
+#: src/pyams_content/component/theme/zmi/manager.py:98
 msgid "Themes settings..."
 msgstr "Paramétrage des thèmes"
 
-#: src/pyams_content/component/theme/zmi/manager.py:111
+#: src/pyams_content/component/theme/zmi/manager.py:112
 msgid "Selected themes"
 msgstr "Thèmes sélectionnés"
 
+#: src/pyams_content/component/theme/zmi/manager.py:145
+msgid "Collections settings..."
+msgstr "Paramétrage des collections"
+
+#: src/pyams_content/component/theme/zmi/manager.py:159
+msgid "Selected collections"
+msgstr "Collections sélectionnées"
+
 #: src/pyams_content/component/association/container.py:88
 #: src/pyams_content/component/association/zmi/__init__.py:296
 #: src/pyams_content/component/association/interfaces/__init__.py:90
@@ -3582,14 +3613,18 @@
 msgid "View « {title} »"
 msgstr "Vue « {title} »"
 
-#: src/pyams_content/shared/view/zmi/theme.py:64
+#: src/pyams_content/shared/view/zmi/theme.py:67
 msgid "View tags settings"
 msgstr "Paramétrage des tags de la vue"
 
-#: src/pyams_content/shared/view/zmi/theme.py:103
+#: src/pyams_content/shared/view/zmi/theme.py:110
 msgid "View themes settings"
 msgstr "Paramétrage des thèmes de la vue"
 
+#: src/pyams_content/shared/view/zmi/theme.py:152
+msgid "View collections settings"
+msgstr "Paramétrage des collections de la vue"
+
 #: src/pyams_content/shared/view/zmi/reference.py:52
 msgid "References..."
 msgstr "Références"
@@ -3802,6 +3837,20 @@
 msgid "Other themes"
 msgstr "Autres thèmes"
 
+#: src/pyams_content/shared/view/interfaces/__init__.py:220
+msgid "Select context collections?"
+msgstr "Collections du contexte ?"
+
+#: src/pyams_content/shared/view/interfaces/__init__.py:221
+msgid "If 'yes', collections will be extracted from context"
+msgstr ""
+"Si 'oui', les collections associées au contexte d'application de la vue seront "
+"automatiquement sélectionnés"
+
+#: src/pyams_content/shared/view/interfaces/__init__.py:225
+msgid "Other collections"
+msgstr "Autres collections"
+
 #: src/pyams_content/shared/imagemap/paragraph.py:91
 msgid "no selected image map"
 msgstr "aucune image cliquable sélectionnée"
@@ -5488,9 +5537,6 @@
 #~ msgid "The content « {0} » has been archived"
 #~ msgstr "Le contenu « {0} » a été archivé"
 
-#~ msgid "Publication settings"
-#~ msgstr "Dates de publication et de retrait"
-
 #~ msgid "Add new data subtype"
 #~ msgstr "Ajout d'un sous-type"
 
--- a/src/pyams_content/locales/pyams_content.pot	Tue Jul 10 16:59:55 2018 +0200
+++ b/src/pyams_content/locales/pyams_content.pot	Wed Jul 11 10:18:10 2018 +0200
@@ -6,7 +6,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE 1.0\n"
-"POT-Creation-Date: 2018-07-10 16:58+0200\n"
+"POT-Creation-Date: 2018-07-11 09:29+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -1228,59 +1228,90 @@
 msgid "Presentation template used for this header"
 msgstr ""
 
-#: ./src/pyams_content/component/theme/__init__.py:70
+#: ./src/pyams_content/component/theme/__init__.py:71
+#: ./src/pyams_content/component/theme/zmi/portlet.py:40
 #: ./src/pyams_content/component/theme/interfaces/__init__.py:47
+#: ./src/pyams_content/component/theme/interfaces/__init__.py:61
 msgid "Tags"
 msgstr ""
 
-#: ./src/pyams_content/component/theme/__init__.py:118
-#: ./src/pyams_content/component/theme/zmi/portlet.py:40
-#: ./src/pyams_content/component/theme/interfaces/__init__.py:85
+#: ./src/pyams_content/component/theme/__init__.py:119
+#: ./src/pyams_content/component/theme/zmi/portlet.py:55
+#: ./src/pyams_content/component/theme/interfaces/__init__.py:88
+#: ./src/pyams_content/component/theme/interfaces/__init__.py:102
 msgid "Themes"
 msgstr ""
 
-#: ./src/pyams_content/component/theme/__init__.py:79
+#: ./src/pyams_content/component/theme/__init__.py:167
+#: ./src/pyams_content/component/theme/zmi/portlet.py:70
+#: ./src/pyams_content/component/theme/interfaces/__init__.py:129
+#: ./src/pyams_content/component/theme/interfaces/__init__.py:143
+msgid "Collections"
+msgstr ""
+
+#: ./src/pyams_content/component/theme/__init__.py:80
 msgid "no defined tag"
 msgstr ""
 
-#: ./src/pyams_content/component/theme/__init__.py:127
+#: ./src/pyams_content/component/theme/__init__.py:128
 msgid "no defined theme"
 msgstr ""
 
-#: ./src/pyams_content/component/theme/zmi/__init__.py:56
-#: ./src/pyams_content/shared/view/zmi/theme.py:53
+#: ./src/pyams_content/component/theme/__init__.py:176
+msgid "no defined collection"
+msgstr ""
+
+#: ./src/pyams_content/component/theme/zmi/__init__.py:55
+#: ./src/pyams_content/shared/view/zmi/theme.py:56
 msgid "Tags..."
 msgstr ""
 
-#: ./src/pyams_content/component/theme/zmi/__init__.py:67
+#: ./src/pyams_content/component/theme/zmi/__init__.py:66
 msgid "Content tags"
 msgstr ""
 
-#: ./src/pyams_content/component/theme/zmi/__init__.py:120
-#: ./src/pyams_content/shared/view/zmi/theme.py:92
+#: ./src/pyams_content/component/theme/zmi/__init__.py:99
+#: ./src/pyams_content/shared/view/zmi/theme.py:99
 msgid "Themes..."
 msgstr ""
 
-#: ./src/pyams_content/component/theme/zmi/__init__.py:128
+#: ./src/pyams_content/component/theme/zmi/__init__.py:107
 msgid "Content themes"
 msgstr ""
 
-#: ./src/pyams_content/component/theme/zmi/manager.py:50
+#: ./src/pyams_content/component/theme/zmi/__init__.py:159
+#: ./src/pyams_content/shared/view/zmi/theme.py:141
+msgid "Collections..."
+msgstr ""
+
+#: ./src/pyams_content/component/theme/zmi/__init__.py:172
+msgid "Content collections"
+msgstr ""
+
+#: ./src/pyams_content/component/theme/zmi/manager.py:51
 msgid "Tags settings..."
 msgstr ""
 
-#: ./src/pyams_content/component/theme/zmi/manager.py:64
+#: ./src/pyams_content/component/theme/zmi/manager.py:65
 msgid "Selected tags"
 msgstr ""
 
-#: ./src/pyams_content/component/theme/zmi/manager.py:97
+#: ./src/pyams_content/component/theme/zmi/manager.py:98
 msgid "Themes settings..."
 msgstr ""
 
-#: ./src/pyams_content/component/theme/zmi/manager.py:111
+#: ./src/pyams_content/component/theme/zmi/manager.py:112
 msgid "Selected themes"
 msgstr ""
 
+#: ./src/pyams_content/component/theme/zmi/manager.py:145
+msgid "Collections settings..."
+msgstr ""
+
+#: ./src/pyams_content/component/theme/zmi/manager.py:159
+msgid "Selected collections"
+msgstr ""
+
 #: ./src/pyams_content/component/association/container.py:88
 #: ./src/pyams_content/component/association/zmi/__init__.py:296
 #: ./src/pyams_content/component/association/interfaces/__init__.py:90
@@ -3357,14 +3388,18 @@
 msgid "View « {title} »"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/zmi/theme.py:64
+#: ./src/pyams_content/shared/view/zmi/theme.py:67
 msgid "View tags settings"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/zmi/theme.py:103
+#: ./src/pyams_content/shared/view/zmi/theme.py:110
 msgid "View themes settings"
 msgstr ""
 
+#: ./src/pyams_content/shared/view/zmi/theme.py:152
+msgid "View collections settings"
+msgstr ""
+
 #: ./src/pyams_content/shared/view/zmi/reference.py:52
 msgid "References..."
 msgstr ""
@@ -3544,6 +3579,18 @@
 msgid "Other themes"
 msgstr ""
 
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:220
+msgid "Select context collections?"
+msgstr ""
+
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:221
+msgid "If 'yes', collections will be extracted from context"
+msgstr ""
+
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:225
+msgid "Other collections"
+msgstr ""
+
 #: ./src/pyams_content/shared/imagemap/paragraph.py:91
 msgid "no selected image map"
 msgstr ""
--- a/src/pyams_content/shared/view/interfaces/__init__.py	Tue Jul 10 16:59:55 2018 +0200
+++ b/src/pyams_content/shared/view/interfaces/__init__.py	Wed Jul 11 10:18:10 2018 +0200
@@ -177,7 +177,7 @@
 
     select_context_tags = Bool(title=_("Select context tags?"),
                                description=_("If 'yes', tags will be extracted from context"),
-                               required=True,
+                               required=False,
                                default=False)
 
     tags = ThesaurusTermsListField(title=_("Other tags"),
@@ -198,7 +198,7 @@
 
     select_context_themes = Bool(title=_("Select context themes?"),
                                  description=_("If 'yes', themes will be extracted from context"),
-                                 required=True,
+                                 required=False,
                                  default=False)
 
     themes = ThesaurusTermsListField(title=_("Other themes"),
@@ -211,6 +211,27 @@
         """Get all themes index values for given context"""
 
 
+VIEW_COLLECTIONS_SETTINGS_KEY = 'pyams_content.view.collections'
+
+
+class IViewCollectionsSettings(IViewSettings):
+    """View collections settings"""
+
+    select_context_collections = Bool(title=_("Select context collections?"),
+                                      description=_("If 'yes', collections will be extracted from context"),
+                                      required=False,
+                                      default=False)
+
+    collections = ThesaurusTermsListField(title=_("Other collections"),
+                                          required=False)
+
+    def get_collections(self, context):
+        """Get all collections for given context"""
+
+    def get_collections_index(self, context):
+        """Get all collections index values for given context"""
+
+
 VIEWS_MERGERS_VOCABULARY = 'pyams_content.views.mergers'
 
 
--- a/src/pyams_content/shared/view/manager.py	Tue Jul 10 16:59:55 2018 +0200
+++ b/src/pyams_content/shared/view/manager.py	Wed Jul 11 10:18:10 2018 +0200
@@ -16,7 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.theme.interfaces import IThemesManagerTarget
+from pyams_content.component.theme.interfaces import IThemesManagerTarget, ICollectionsManagerTarget
 from pyams_content.shared.common.interfaces import ISharedContentFactory
 from pyams_content.shared.view.interfaces import IViewsManager, VIEW_CONTENT_TYPE, IViewsManagerFactory
 from zope.annotation.interfaces import IAttributeAnnotatable
@@ -33,7 +33,7 @@
 from zope.interface import implementer
 
 
-@implementer(IViewsManager, IThemesManagerTarget, IAttributeAnnotatable)
+@implementer(IViewsManager, IThemesManagerTarget, ICollectionsManagerTarget, IAttributeAnnotatable)
 class ViewsManager(SharedTool):
     """Views manager class"""
 
--- a/src/pyams_content/shared/view/theme.py	Tue Jul 10 16:59:55 2018 +0200
+++ b/src/pyams_content/shared/view/theme.py	Wed Jul 11 10:18:10 2018 +0200
@@ -9,6 +9,7 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
+from pyams_content.component.theme import ICollectionsInfo
 
 __docformat__ = 'restructuredtext'
 
@@ -19,7 +20,8 @@
 from hypatia.interfaces import ICatalog
 from pyams_content.component.theme.interfaces import ITagsInfo, IThemesInfo
 from pyams_content.shared.view.interfaces import IWfView, IViewSettings, IViewThemesSettings, \
-    IViewQueryParamsExtension, VIEW_THEMES_SETTINGS_KEY, IViewTagsSettings, VIEW_TAGS_SETTINGS_KEY
+    IViewQueryParamsExtension, VIEW_THEMES_SETTINGS_KEY, IViewTagsSettings, VIEW_TAGS_SETTINGS_KEY, \
+    IViewCollectionsSettings, VIEW_COLLECTIONS_SETTINGS_KEY
 from zope.intid.interfaces import IIntIds
 
 # import packages
@@ -141,3 +143,58 @@
         if themes:
             params = and_(params, Any(catalog['themes'], themes))
         return params
+
+
+#
+# Collections management
+#
+
+@implementer(IViewCollectionsSettings)
+class ViewCollectionsSettings(Persistent, Contained):
+    """View collections settings"""
+
+    select_context_collections = FieldProperty(IViewCollectionsSettings['select_context_collections'])
+    collections = FieldProperty(IViewCollectionsSettings['collections'])
+
+    @property
+    def is_using_context(self):
+        return self.select_context_collections
+
+    def get_collections(self, context):
+        collections = set()
+        if self.select_context_collections:
+            collections_info = ICollectionsInfo(context, None)
+            if collections_info is not None:
+                collections |= set(collections_info.collections or ())
+        if self.collections:
+            collections |= set(self.collections)
+        return collections
+
+    def get_collections_index(self, context):
+        intids = get_utility(IIntIds)
+        return [intids.register(term) for term in self.get_collections(context)]
+
+
+@adapter_config(context=IWfView, provides=IViewCollectionsSettings)
+@adapter_config(name='collections', context=IWfView, provides=IViewSettings)
+def view_collections_settings_factory(view):
+    """View collections settings factory"""
+    return get_annotation_adapter(view, VIEW_COLLECTIONS_SETTINGS_KEY, ViewCollectionsSettings,
+                                  name='++view:collections++')
+
+
+@adapter_config(name='collections', context=IWfView, provides=IViewQueryParamsExtension)
+class ViewCollectionsQueryParamsExtension(ContextAdapter):
+    """View collections query params extension"""
+
+    weight = 54
+
+    def get_params(self, context):
+        catalog = get_utility(ICatalog)
+        settings = IViewCollectionsSettings(self.context)
+        params = None
+        # check collections
+        collections = settings.get_collections_index(context)
+        if collections:
+            params = and_(params, Any(catalog['collections'], collections))
+        return params
--- a/src/pyams_content/shared/view/zmi/theme.py	Tue Jul 10 16:59:55 2018 +0200
+++ b/src/pyams_content/shared/view/zmi/theme.py	Wed Jul 11 10:18:10 2018 +0200
@@ -9,7 +9,6 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
-from pyams_content.component.theme import ITagsManager
 
 __docformat__ = 'restructuredtext'
 
@@ -18,7 +17,8 @@
 
 # import interfaces
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
-from pyams_content.shared.view.interfaces import IWfView, IViewThemesSettings, IViewsManager, IViewTagsSettings
+from pyams_content.shared.view.interfaces import IWfView, IViewThemesSettings, IViewsManager, IViewTagsSettings, \
+    IViewCollectionsSettings
 from pyams_form.interfaces.form import IWidgetForm, IUncheckedEditFormButtons
 from pyams_skin.interfaces import IInnerPage
 from pyams_skin.layer import IPyAMSLayer
@@ -29,13 +29,16 @@
 from z3c.form.interfaces import INPUT_MODE
 
 # import packages
+from pyams_content.component.theme import ITagsManager
 from pyams_form.form import ajax_config
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.viewlet.menu import MenuItem
+from pyams_thesaurus.zmi.widget import ThesaurusTermsTreeFieldWidget
 from pyams_utils.registry import get_utility
 from pyams_viewlet.viewlet import viewlet_config
 from pyams_zmi.form import AdminEditForm
 from z3c.form import field, button
+from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
 from zope.interface import implementer, Interface
 
 from pyams_content import _
@@ -62,8 +65,12 @@
     """View tags settings edit form"""
 
     legend = _("View tags settings")
+    label_css_class = 'control-label col-md-1'
+    input_css_class = 'col-md-11'
 
     fields = field.Fields(IViewTagsSettings)
+    fields['select_context_tags'].widgetFactory = SingleCheckBoxFieldWidget
+    fields['tags'].widgetFactory = ThesaurusTermsTreeFieldWidget
 
     @property
     def buttons(self):
@@ -76,8 +83,11 @@
         super(ViewTagsEditForm, self).updateWidgets(prefix)
         if 'tags' in self.widgets:
             manager = ITagsManager(self.request.root)
-            self.widgets['tags'].thesaurus_name = manager.thesaurus_name
-            self.widgets['tags'].extract_name = manager.extract_name
+            widget = self.widgets['tags']
+            widget.label_css_class = 'control-label col-md-2'
+            widget.input_css_class = 'col-md-12'
+            widget.thesaurus_name = manager.thesaurus_name
+            widget.extract_name = manager.extract_name
 
 
 #
@@ -101,8 +111,12 @@
     """View themes settings edit form"""
 
     legend = _("View themes settings")
+    label_css_class = 'control-label col-md-1'
+    input_css_class = 'col-md-11'
 
     fields = field.Fields(IViewThemesSettings)
+    fields['select_context_themes'].widgetFactory = SingleCheckBoxFieldWidget
+    fields['themes'].widgetFactory = ThesaurusTermsTreeFieldWidget
 
     @property
     def buttons(self):
@@ -114,5 +128,55 @@
     def updateWidgets(self, prefix=None):
         super(ViewThemesEditForm, self).updateWidgets(prefix)
         if 'themes' in self.widgets:
-            manager = get_utility(IViewsManager)
-            self.widgets['themes'].thesaurus_name = IThesaurusContextManager(manager).thesaurus_name
+            manager = IThesaurusContextManager(get_utility(IViewsManager))
+            widget = self.widgets['themes']
+            widget.label_css_class = 'control-label col-md-2'
+            widget.input_css_class = 'col-md-12'
+            widget.thesaurus_name = manager.thesaurus_name
+            widget.extract_name = manager.extract_name
+
+
+#
+# Collections management
+#
+
+@viewlet_config(name='collections.menu', context=IWfView, layer=IAdminLayer,
+                manager=IPropertiesMenu, permission=VIEW_SYSTEM_PERMISSION, weight=354)
+class ViewCollectionsMenu(MenuItem):
+    """View collections menu"""
+
+    label = _("Collections...")
+    icon_class = 'fa-book'
+    url = '#collections.html'
+
+
+@pagelet_config(name='collections.html', context=IWfView, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
+@ajax_config(name='collections.json', context=IWfView, layer=IPyAMSLayer, permission=MANAGE_CONTENT_PERMISSION)
+@implementer(IWidgetForm, IInnerPage)
+class ViewCollectionsEditForm(AdminEditForm):
+    """View collections settings edit form"""
+
+    legend = _("View collections settings")
+    label_css_class = 'control-label col-md-1'
+    input_css_class = 'col-md-11'
+
+    fields = field.Fields(IViewCollectionsSettings)
+    fields['select_context_collections'].widgetFactory = SingleCheckBoxFieldWidget
+    fields['collections'].widgetFactory = ThesaurusTermsTreeFieldWidget
+
+    @property
+    def buttons(self):
+        if self.mode == INPUT_MODE:
+            return button.Buttons(IUncheckedEditFormButtons)
+        else:
+            return button.Buttons(Interface)
+
+    def updateWidgets(self, prefix=None):
+        super(ViewCollectionsEditForm, self).updateWidgets(prefix)
+        if 'collections' in self.widgets:
+            manager = IThesaurusContextManager(get_utility(IViewsManager))
+            widget = self.widgets['collections']
+            widget.label_css_class = 'control-label col-md-2'
+            widget.input_css_class = 'col-md-12'
+            widget.thesaurus_name = manager.thesaurus_name
+            widget.extract_name = manager.extract_name