# HG changeset patch # User Thierry Florac # Date 1544790774 -3600 # Node ID 41310cefa42a412bb9b7ceda0f3eee82b8e60a03 # Parent 49cba50f36cbbe1b5ed02fbb9e885150c81bc13a Update views and search engine so that results displayed in a view component can be excluded from other search results diff -r 49cba50f36cb -r 41310cefa42a src/pyams_content/features/search/__init__.py --- 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): diff -r 49cba50f36cb -r 41310cefa42a src/pyams_content/shared/view/portlet/__init__.py --- 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) diff -r 49cba50f36cb -r 41310cefa42a src/pyams_content/shared/view/portlet/interfaces.py --- 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) diff -r 49cba50f36cb -r 41310cefa42a src/pyams_content/shared/view/reference.py --- 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