diff --git a/CHANGES.rst b/CHANGES.rst
index eb32a383bf..3e6aad5f61 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -7,6 +7,7 @@ Changelog
**Added**
+- #1489 Support Multiple Catalogs for Dexterity Contents
- #1481 Filter Templates field when Sample Type is selected in Sample Add form
- #1483 Added Accredited symbol in Analyses listings
- #1466 Support for "readonly" and "hidden" visibility modes in ReferenceWidget
diff --git a/bika/lims/catalog/catalog_multiplex_processor.py b/bika/lims/catalog/catalog_multiplex_processor.py
new file mode 100644
index 0000000000..2cd3cf4a24
--- /dev/null
+++ b/bika/lims/catalog/catalog_multiplex_processor.py
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+
+from Acquisition import aq_base
+from bika.lims import api
+from bika.lims import logger
+from bika.lims.config import USE_COLLECTIVE_INDEXING
+from bika.lims.interfaces import IMultiCatalogBehavior
+from zope.interface import implements
+
+if USE_COLLECTIVE_INDEXING:
+ from collective.indexing.interfaces import IIndexQueueProcessor
+
+REQUIRED_CATALOGS = [
+ "auditlog_catalog",
+]
+
+
+class CatalogMultiplexProcessor(object):
+ """A catalog multiplex processor
+ """
+ if USE_COLLECTIVE_INDEXING:
+ implements(IIndexQueueProcessor)
+
+ def get_catalogs_for(self, obj):
+ catalogs = getattr(obj, "_catalogs", [])
+ for rc in REQUIRED_CATALOGS:
+ if rc in catalogs:
+ continue
+ catalogs.append(rc)
+ return map(api.get_tool, catalogs)
+
+ def supports_multi_catalogs(self, obj):
+ """Check if the Multi Catalog Behavior is enabled
+ """
+ if IMultiCatalogBehavior(obj, None) is None:
+ return False
+ return True
+
+ def index(self, obj, attributes=None):
+ if attributes is None:
+ attributes = []
+
+ if not self.supports_multi_catalogs(obj):
+ return
+
+ catalogs = self.get_catalogs_for(obj)
+ url = api.get_path(obj)
+
+ for catalog in catalogs:
+ logger.info(
+ "CatalogMultiplexProcessor::indexObject:catalog={} url={}"
+ .format(catalog.id, url))
+ # We want the intersection of the catalogs idxs
+ # and the incoming list.
+ indexes = set(catalog.indexes()).intersection(attributes)
+ # Skip reindexing if no indexes match
+ if attributes and not indexes:
+ continue
+ # recatalog the object
+ catalog.catalog_object(obj, url, idxs=list(indexes))
+
+ def reindex(self, obj, attributes=None):
+ self.index(obj, attributes)
+
+ def unindex(self, obj):
+ wrapped_obj = obj
+ if aq_base(obj).__class__.__name__ == "PathWrapper":
+ # Could be a PathWrapper object from collective.indexing.
+ obj = obj.context
+
+ if not self.supports_multi_catalogs(obj):
+ return
+
+ catalogs = self.get_catalogs_for(obj)
+ # get the old path from the wrapped object
+ url = api.get_path(wrapped_obj)
+
+ for catalog in catalogs:
+ if catalog._catalog.uids.get(url, None) is not None:
+ logger.info(
+ "CatalogMultiplexProcessor::unindex:catalog={} url={}"
+ .format(catalog.id, url))
+ catalog.uncatalog_object(url)
+
+ def begin(self):
+ pass
+
+ def commit(self):
+ pass
+
+ def abort(self):
+ pass
diff --git a/bika/lims/catalog/configure.zcml b/bika/lims/catalog/configure.zcml
new file mode 100644
index 0000000000..2b03094dad
--- /dev/null
+++ b/bika/lims/catalog/configure.zcml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bika/lims/config.py b/bika/lims/config.py
index a1ab3cae30..8f3acac4dc 100644
--- a/bika/lims/config.py
+++ b/bika/lims/config.py
@@ -27,6 +27,14 @@
from bika.lims.utils import t # noqa
from bika.lims.permissions import * # noqa
+try:
+ import collective.indexing
+ collective.indexing # noqa
+except ImportError:
+ USE_COLLECTIVE_INDEXING = False
+else:
+ USE_COLLECTIVE_INDEXING = True
+
PROJECTNAME = "bika.lims"
diff --git a/bika/lims/configure.zcml b/bika/lims/configure.zcml
index 8ecf56822c..926cdbd5ef 100644
--- a/bika/lims/configure.zcml
+++ b/bika/lims/configure.zcml
@@ -26,7 +26,7 @@
-
+
@@ -51,6 +51,13 @@
provides="bika.lims.interfaces.IAutoGenerateID"
/>
+
+
+
=1.2.0',
# Python 2/3 compatibility library: https://six.readthedocs.io/
'six',
+ # Needed for `IPortalCatalogQueueProcessor`, which will be included in
+ # `Products.CMFCore` in Plone 5. Remove after we are on Plone 5!
+ 'collective.indexing',
],
extras_require={
'test': [