Update views and search engine so that results displayed in a view component can be excluded from other search results
--- a/src/pyams_content/features/search/__init__.py Fri Dec 14 11:59:12 2018 +0100
+++ b/src/pyams_content/features/search/__init__.py Fri Dec 14 13:32:54 2018 +0100
@@ -9,11 +9,13 @@
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
+from pyams_content.shared.view.portlet import SEARCH_EXCLUDED_ITEMS
+
__docformat__ = 'restructuredtext'
from hypatia.interfaces import ICatalog
-from hypatia.query import Contains, Or
+from hypatia.query import Contains, Or, NotAny
from zope.interface import implementer
from zope.schema.fieldproperty import FieldProperty
@@ -89,9 +91,30 @@
return params
+@adapter_config(name='exclusions', context=SearchFolderQuery, provides=IViewUserQuery)
+class SearchFolderExclusionsQuery(ContextAdapter):
+ """Search folder exclusions query
+
+ This adapter is looking into request's annotations for items which should be excluded
+ from search.
+ """
+
+ @staticmethod
+ def get_user_params(request):
+ # check for results excluded by previous views
+ if request is not None:
+ excluded_items = request.annotations.get(SEARCH_EXCLUDED_ITEMS)
+ if excluded_items:
+ catalog = get_utility(ICatalog)
+ yield NotAny(catalog['oid'], excluded_items)
+
+
@adapter_config(name='user_search', context=SearchFolderQuery, provides=IViewUserQuery)
class SearchFolderUserQuery(ContextAdapter):
- """Search folder user query"""
+ """Search folder user query
+
+ This adapter is looking for any fulltext search entered by user
+ """
@staticmethod
def get_user_params(request):
--- a/src/pyams_content/shared/view/portlet/__init__.py Fri Dec 14 11:59:12 2018 +0100
+++ b/src/pyams_content/shared/view/portlet/__init__.py Fri Dec 14 13:32:54 2018 +0100
@@ -12,14 +12,16 @@
__docformat__ = 'restructuredtext'
-from itertools import islice
+from itertools import islice, tee
from zope.schema.fieldproperty import FieldProperty
from pyams_content.shared.view.interfaces import IViewsManager, IViewsMerger
-from pyams_content.shared.view.portlet.interfaces import IViewItemsPortletSettings, VIEW_DISPLAY_CONTEXT
+from pyams_content.shared.view.portlet.interfaces import IViewItemsPortletSettings, VIEW_DISPLAY_CONTEXT, \
+ SEARCH_EXCLUDED_ITEMS
from pyams_portal.interfaces import PREVIEW_MODE
from pyams_portal.portlet import Portlet, PortletSettings, portlet_config
+from pyams_sequence.interfaces import ISequentialIdInfo
from pyams_sequence.reference import get_sequence_target
from pyams_utils.factory import factory_config
from pyams_utils.interfaces import VIEW_PERMISSION
@@ -45,6 +47,7 @@
views_merge_mode = FieldProperty(IViewItemsPortletSettings['views_merge_mode'])
limit = FieldProperty(IViewItemsPortletSettings['limit'])
start = FieldProperty(IViewItemsPortletSettings['start'])
+ exclude_from_search = FieldProperty(IViewItemsPortletSettings['exclude_from_search'])
def get_views(self):
views_manager = get_utility(IViewsManager)
@@ -71,12 +74,18 @@
merger = self.get_merger(request)
if merger is not None:
start = int(request.params.get('start', 0))
- yield from islice(unique_iter(merger.get_results(self.get_views(),
- context,
- ignore_cache=ignore_cache,
- request=request)),
- start + (self.start or 1) - 1,
- min(limit or 999, self.limit or 999))
+ items = islice(unique_iter(merger.get_results(self.get_views(),
+ context,
+ ignore_cache=ignore_cache,
+ request=request)),
+ start + (self.start or 1) - 1,
+ min(limit or 999, self.limit or 999))
+ if (request is not None) and self.exclude_from_search:
+ (excluded, items) = tee(items)
+ excluded_items = request.annotations.get(SEARCH_EXCLUDED_ITEMS) or set()
+ excluded_items |= set((ISequentialIdInfo(item).hex_oid for item in excluded))
+ request.annotations[SEARCH_EXCLUDED_ITEMS] = excluded_items
+ yield from items
@portlet_config(permission=VIEW_PERMISSION)
--- a/src/pyams_content/shared/view/portlet/interfaces.py Fri Dec 14 11:59:12 2018 +0100
+++ b/src/pyams_content/shared/view/portlet/interfaces.py Fri Dec 14 13:32:54 2018 +0100
@@ -12,20 +12,15 @@
__docformat__ = 'restructuredtext'
+from zope.schema import Bool, Choice, Int
+from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
-# import standard library
-
-# import interfaces
+from pyams_content.shared.view import WfView
from pyams_content.shared.view.interfaces import VIEWS_MERGERS_VOCABULARY
-from pyams_portal.interfaces import IPortletSettings
-
-# import packages
-from pyams_content.shared.view import WfView
from pyams_content.shared.view.merge import CONCAT_VIEWS_MERGE_MODE
from pyams_i18n.schema import I18nTextLineField
+from pyams_portal.interfaces import IPortletSettings
from pyams_sequence.schema import InternalReferencesListField
-from zope.schema import Choice, Int
-from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
from pyams_content import _
@@ -45,6 +40,8 @@
VIEW_CONTEXT_VOCABULARY = SimpleVocabulary([SimpleTerm(item['id'], title=item['title'])
for item in VIEW_CONTEXTS])
+SEARCH_EXCLUDED_ITEMS = 'search.excluded'
+
#
# Views merge modes
@@ -99,3 +96,10 @@
def get_items(self):
"""Get iterator over items returned by selected views, using selected merger"""
+
+ exclude_from_search = Bool(title=_("Exclude from search results"),
+ description=_("If 'yes', and if this portlet is associated with a search engine in the "
+ "same page template, items displayed by this portlet will be excluded "
+ "from search results"),
+ required=True,
+ default=False)
--- a/src/pyams_content/shared/view/reference.py Fri Dec 14 11:59:12 2018 +0100
+++ b/src/pyams_content/shared/view/reference.py Fri Dec 14 13:32:54 2018 +0100
@@ -10,6 +10,7 @@
# FOR A PARTICULAR PURPOSE.
#
+
__docformat__ = 'restructuredtext'
from hypatia.catalog import CatalogQuery
@@ -22,8 +23,8 @@
from pyams_catalog.query import CatalogResultSet
from pyams_content.shared.view.interfaces import ALWAYS_REFERENCE_MODE, IViewInternalReferencesSettings, \
- IViewQueryFilterExtension, IViewQueryParamsExtension, IViewSettings, IWfView, VIEW_REFERENCES_SETTINGS_KEY, \
- ONLY_REFERENCE_MODE
+ IViewQueryFilterExtension, IViewQueryParamsExtension, IViewSettings, IWfView, ONLY_REFERENCE_MODE, \
+ VIEW_REFERENCES_SETTINGS_KEY
from pyams_content.workflow import VISIBLE_STATES
from pyams_sequence.interfaces import ISequentialIdInfo
from pyams_utils.adapter import ContextAdapter, adapter_config, get_annotation_adapter
@@ -59,16 +60,15 @@
def get_params(self, context, request=None):
settings = IViewInternalReferencesSettings(self.context)
+ catalog = get_utility(ICatalog)
# check view settings
if settings.exclude_context:
sequence = ISequentialIdInfo(context, None)
if sequence is not None:
oid = sequence.hex_oid
- catalog = get_utility(ICatalog)
yield NotEq(catalog['oid'], oid)
# check view references mode
if settings.references_mode == ONLY_REFERENCE_MODE:
- catalog = get_utility(ICatalog)
yield Any(catalog['oid'], settings.references), False