Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions docs/portal.md
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,80 @@ for vocabulary_name in common_vocabularies:
assert vocabulary_name in vocabulary_names
```

(portal-add-catalog-indexes-example)=

## Add catalog indexes

To add new indexes to the portal catalog if they don't already exist, use {meth}`api.portal.add_catalog_indexes`.

```python
from plone import api

# Add new indexes with default logging
indexes = [
('custom_field', 'FieldIndex'),
('review_date', 'DateIndex')
]
api.portal.add_catalog_indexes(wanted_indexes=indexes)

# Add indexes but skip reindexing
api.portal.add_catalog_indexes(
wanted_indexes=[('modified_by', 'FieldIndex')],
reindex=False
)

# Add indexes with custom logger
import logging
custom_logger = logging.getLogger('my.package')
api.portal.add_catalog_indexes(
wanted_indexes=[('creation_user', 'FieldIndex')],
logger=custom_logger
)
```

% invisible-code-block: python
%
% # Verify the indexes were added to the catalog
% catalog = api.portal.get_tool('portal_catalog')
% self.assertIn('custom_field', catalog.indexes())
% self.assertIn('review_date', catalog.indexes())
% self.assertIn('modified_by', catalog.indexes())
% self.assertIn('creation_user', catalog.indexes())

This function returns a list of the names of the indexes that were added.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move above.

Suggested change
This function returns a list of the names of the indexes that were added.


(portal-add-catalog-metadata-example)=

## Add catalog metadata columns

To add new metadata columns to the portal catalog if they don't already exist, use {meth}`api.portal.add_catalog_metadata`.

```python
from plone import api

# Add new metadata columns with default logging
columns = ['custom_metadata', 'author_email']
api.portal.add_catalog_metadata(wanted_columns=columns)

# Add columns with custom logger
import logging
custom_logger = logging.getLogger('my.package')
api.portal.add_catalog_metadata(
wanted_columns=['publication_date'],
logger=custom_logger
)
```

% invisible-code-block: python
%
% # Verify the columns were added to the catalog
% catalog = api.portal.get_tool('portal_catalog')
% self.assertIn('custom_metadata', catalog.schema())
% self.assertIn('author_email', catalog.schema())
% self.assertIn('publication_date', catalog.schema())

This function returns a list of the names of the columns that were added. Note that adding metadata columns only makes them available for storage - you still need to reindex your content to populate the values.

## Further reading

For more information on possible flags and usage options please see the full {ref}`plone-api-portal` specification.
3 changes: 3 additions & 0 deletions news/404.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Added two new helper methods to plone.api.portal:
- add_catalog_indexes: Adds the specified indexes to portal_catalog if they don't already exist @rohnsha0
- add_catalog_metadata: Adds the specified metadata columns to portal_catalog if they don't already exist @rohnsha0
62 changes: 62 additions & 0 deletions src/plone/api/portal.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from zope.schema.interfaces import IVocabularyFactory

import datetime as dtime
import logging
import re


Expand Down Expand Up @@ -472,3 +473,64 @@ def get_vocabulary_names():
:Example: :ref:`portal-get-all-vocabulary-names-example`
"""
return sorted([name for name, vocabulary in getUtilitiesFor(IVocabularyFactory)])


@required_parameters("wanted_indexes")
def add_catalog_indexes(wanted_indexes, reindex=True, logger=None):
"""
Add the specified indexes to portal_catalog if they don't already exist.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Add the specified indexes to portal_catalog if they don't already exist.
Add the specified indexes to portal_catalog.


Parameters:
- wanted_indexes: List of tuples in format (index_name, index_type)
- reindex: Boolean indicating if newly added indexes should be reindexed
- logger: Optional logger instance

Returns:
- List of newly added index names
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- List of newly added index names
- List of newly added index names
Example:
- :ref:`portal-add-catalog-indexes-example`

"""
if logger is None:
logger = logging.getLogger("plone.api.portal")

catalog = get_tool("portal_catalog")
existing_indexes = catalog.indexes()

added_indexes = []
for name, meta_type in wanted_indexes:
if name not in existing_indexes:
catalog.addIndex(name, meta_type)
added_indexes.append(name)
logger.info("Added %s index for field %s.", meta_type, name)

if reindex and added_indexes:
logger.info("Reindexing new indexes: %s", ", ".join(added_indexes))
catalog.manage_reindexIndex(ids=added_indexes)

return added_indexes


@required_parameters("wanted_columns")
def add_catalog_metadata(wanted_columns, logger=None):
"""Add the specified metadata columns to portal_catalog if they don't already exist.

:param wanted_columns: [required] List of column names to add
:type wanted_columns: list
:param logger: Optional custom logger instance
:type logger: logging.Logger
:returns: List of names of columns that were added
:rtype: list
:Example: :ref:`portal-add-catalog-metadata-example`
"""
if logger is None:
logger = logging.getLogger("plone.api.portal")

catalog = get_tool("portal_catalog")
existing_columns = catalog.schema()

added_columns = []
for name in wanted_columns:
if name not in existing_columns:
catalog.addColumn(name)
added_columns.append(name)
logger.info("Added metadata column: %s", name)

return added_columns
100 changes: 100 additions & 0 deletions src/plone/api/tests/test_portal.py
Original file line number Diff line number Diff line change
Expand Up @@ -963,3 +963,103 @@ def test_vocabulary_terms(self):
states = [term.value for term in states_vocabulary]
self.assertIn("private", states)
self.assertIn("published", states)

def test_add_catalog_indexes(self):
"""Test adding catalog indexes."""
import logging

catalog = portal.get_tool("portal_catalog")

# Test adding new indexes
test_indexes = [
("test_field1", "FieldIndex"),
("test_field2", "KeywordIndex"),
]

added = portal.add_catalog_indexes(test_indexes)

# Verify indexes were added
self.assertEqual(len(added), 2)
self.assertIn("test_field1", added)
self.assertIn("test_field2", added)
self.assertIn("test_field1", catalog.indexes())
self.assertIn("test_field2", catalog.indexes())

# Test adding already existing indexes
added = portal.add_catalog_indexes(test_indexes)
self.assertEqual(len(added), 0) # No new indexes should be added

# Test with reindex=False
test_indexes2 = [
("test_field3", "FieldIndex"),
]

# Create a mock for catalog.manage_reindexIndex to verify it's called or not
original_reindex = catalog.manage_reindexIndex

try:
reindex_called = [False]

def mock_reindex(ids=None):
reindex_called[0] = True
self.assertEqual(ids, ["test_field3"])
original_reindex(ids)

catalog.manage_reindexIndex = mock_reindex

portal.add_catalog_indexes(test_indexes2, reindex=True)
self.assertTrue(reindex_called[0])

# Reset flag and test with reindex=False
reindex_called[0] = False
portal.add_catalog_indexes([("test_field4", "FieldIndex")], reindex=False)
self.assertFalse(reindex_called[0])

finally:
# Restore original method
catalog.manage_reindexIndex = original_reindex

# Test with custom logger
test_logger = logging.getLogger("test.plone.api.portal")

with self.assertLogs("test.plone.api.portal", level="INFO") as cm:
portal.add_catalog_indexes(
[("test_field5", "FieldIndex")], logger=test_logger
)

log_output = "\n".join(cm.output)
self.assertIn("Added FieldIndex index for field test_field5", log_output)
self.assertIn("Reindexing new indexes: test_field5", log_output)

def test_add_catalog_metadata(self):
"""Test adding catalog metadata columns."""
from plone.api.portal import add_catalog_metadata

import logging

catalog = portal.get_tool("portal_catalog")

# Test adding new columns
test_columns = ["test_col1", "test_col2"]

added = add_catalog_metadata(test_columns)

# Verify columns were added
self.assertEqual(len(added), 2)
self.assertIn("test_col1", added)
self.assertIn("test_col2", added)
self.assertIn("test_col1", catalog.schema())
self.assertIn("test_col2", catalog.schema())

# Test adding already existing columns
added = add_catalog_metadata(test_columns)
self.assertEqual(len(added), 0) # No new columns should be added

# Test with custom logger
test_logger = logging.getLogger("test.plone.api.portal")

with self.assertLogs("test.plone.api.portal", level="INFO") as cm:
add_catalog_metadata(["test_col3"], logger=test_logger)

log_output = "\n".join(cm.output)
self.assertIn("Added metadata column: test_col3", log_output)
Loading