Skip to content

Commit

Permalink
Merge pull request #38 from plone/issue-33-permission-constrains
Browse files Browse the repository at this point in the history
Set constraints after setting local permissions on content
  • Loading branch information
davisagli authored Sep 27, 2024
2 parents df82c1b + 5d7acaa commit fdaa795
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 28 deletions.
1 change: 1 addition & 0 deletions news/33.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Set constraints after setting local permissions on content [@ericof]
6 changes: 6 additions & 0 deletions src/plone/exportimport/importers/content.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ def construct(self, item: dict) -> DexterityContent:
# Deserialize
new = self.deserialize(data=item, obj=new, config=config)

# Handle constraints
constraints = item.pop(settings.SERIALIZER_CONSTRAINS_KEY, {})
if constraints:
item_uid = item["UID"]
self.metadata.constraints[item_uid] = constraints

# Updaters
for updater in content_utils.updaters():
logger.debug(f"{config.logger_prefix} Running {updater.name} for {new}")
Expand Down
3 changes: 3 additions & 0 deletions src/plone/exportimport/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class ExportImportMetadata:
ordering: dict = field(default_factory=dict)
local_roles: dict = field(default_factory=dict)
local_permissions: dict = field(default_factory=dict)
constraints: dict = field(default_factory=dict)
relations: list = field(default_factory=list)
__version__: str = "1.0.0"
_data_files_: List[str] = field(default_factory=list)
Expand All @@ -32,6 +33,8 @@ class ExportImportMetadata:

def export(self) -> dict:
dump = asdict(self)
# Ignore constraints on export
dump.pop("constraints", None)
files = dump.pop("_all_", {})
dump["_data_files_"] = [v for _, v in sorted(files.items())]
return dump
Expand Down
6 changes: 3 additions & 3 deletions src/plone/exportimport/utils/content/export_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,8 @@ def fix_allow_discussion(
return item


def add_constrains_info(obj: DexterityContent, config: types.ExporterConfig) -> dict:
"""Return constrains info for an object."""
def add_constraints_info(obj: DexterityContent, config: types.ExporterConfig) -> dict:
"""Return constraints info for an object."""
key = settings.SERIALIZER_CONSTRAINS_KEY
results = {key: {}}
constrains = ISelectableConstrainTypes(obj, None)
Expand Down Expand Up @@ -311,7 +311,7 @@ def fixers() -> List[types.ExportImportHelper]:
def enrichers() -> List[types.ExportImportHelper]:
enrichers = []
funcs = [
add_constrains_info,
add_constraints_info,
add_workflow_history,
add_revisions_history,
]
Expand Down
58 changes: 35 additions & 23 deletions src/plone/exportimport/utils/content/import_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,28 +134,6 @@ def update_uid(item: dict, obj: DexterityContent) -> DexterityContent:
return obj


def update_constrains(item: dict, obj: DexterityContent) -> DexterityContent:
"""Update constrains in object."""
item_id = item["@id"]
item_constrains = item.get(settings.SERIALIZER_CONSTRAINS_KEY)
if not item_constrains:
return obj
constrains = ISelectableConstrainTypes(obj, None)
if constrains:
constrains.setConstrainTypesMode(ENABLED)
for key, func in (
("locally_allowed_types", constrains.setLocallyAllowedTypes),
("immediately_addable_types", constrains.setImmediatelyAddableTypes),
):
value = item_constrains.get(key)
try:
func(value)
except ValueError:
logger.warning(f"Cannot set {key} on {item_id}", exc_info=True)

return obj


def update_review_state(item: dict, obj: DexterityContent) -> DexterityContent:
"""Update review state on the object."""
portal_workflow = api.portal.get_tool("portal_workflow")
Expand Down Expand Up @@ -207,7 +185,6 @@ def updaters() -> List[types.ExportImportHelper]:
updaters = []
funcs = [
update_uid,
update_constrains,
update_review_state,
update_workflow_history,
update_dates,
Expand All @@ -223,6 +200,40 @@ def updaters() -> List[types.ExportImportHelper]:
return updaters


def set_constraints(uid: str, value: dict) -> bool:
"""Update constraints in object."""
status = False
obj = object_from_uid(uid)
if not value:
return status
constraints = ISelectableConstrainTypes(obj, None)
if constraints:
status = True
with api.env.adopt_roles(["Manager", "Site Administrator"]):
constraints.setConstrainTypesMode(ENABLED)
for key, func, check in (
(
"locally_allowed_types",
constraints.setLocallyAllowedTypes,
constraints.getLocallyAllowedTypes,
),
(
"immediately_addable_types",
constraints.setImmediatelyAddableTypes,
constraints.getImmediatelyAddableTypes,
),
):
local_value = value.get(key)
try:
func(local_value)
except ValueError:
logger.warning(f"Cannot set {key} on {uid}", exc_info=True)
else:
result = check()
status = status and (result == local_value)
return status


def set_default_page(uid: str, value: dict) -> bool:
"""Set default page on object with the given uid."""
obj = object_from_uid(uid)
Expand Down Expand Up @@ -298,6 +309,7 @@ def metadata_setters() -> List[types.ExportImportHelper]:
(set_ordering, "ordering"),
(set_local_roles, "local_roles"),
(set_local_permissions, "local_permissions"),
(set_constraints, "constraints"),
]
for func, attr in funcs:
helpers.append(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,15 @@
"effective": null,
"exclude_from_nav": false,
"expires": null,
"exportimport.constrains": {},
"exportimport.constrains": {
"immediately_addable_types": [
"Image"
],
"locally_allowed_types": [
"Document",
"Image"
]
},
"exportimport.conversation": [],
"exportimport.versions": {},
"id": "bar",
Expand Down Expand Up @@ -60,4 +68,4 @@
},
"working_copy": null,
"working_copy_of": null
}
}
35 changes: 35 additions & 0 deletions tests/importers/test_importers_content.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from plone import api
from plone.exportimport import interfaces
from plone.exportimport.importers import content
from zope.component import getAdapter
Expand Down Expand Up @@ -59,3 +60,37 @@ def test_permission_is_set(self, uid, permission_name, roles):
for role in roles:
all_permissions = [p["name"] for p in content.permissionsOfRole(role)]
assert permission_name in all_permissions


class TestImporterConstrains:
@pytest.fixture(autouse=True)
def _init(self, portal, base_import_path, load_json):
self.portal = portal
importer = content.ContentImporter(portal)
importer.import_data(base_path=base_import_path)

@pytest.mark.parametrize(
"uid,method,types",
[
[
"35661c9bb5be42c68f665aa1ed291418",
"getImmediatelyAddableTypes",
["Image"],
],
[
"35661c9bb5be42c68f665aa1ed291418",
"getLocallyAllowedTypes",
["Document", "Image"],
],
],
)
def test_constrain_is_set(self, uid, method, types):
from plone.base.interfaces.constrains import ISelectableConstrainTypes
from plone.exportimport.utils.content import object_from_uid

content = object_from_uid(uid)
with api.env.adopt_roles(["Manager", "Site Administrator"]):
behavior = ISelectableConstrainTypes(content, None)
constrains = getattr(behavior, method)()
for type_ in types:
assert type_ in constrains

0 comments on commit fdaa795

Please sign in to comment.