Skip to content

Commit

Permalink
Merge pull request #119 from plone/handle_relations
Browse files Browse the repository at this point in the history
Handle relation-fields for working copies of folderish content
  • Loading branch information
jensens authored Feb 19, 2024
2 parents 9b1654f + 018265e commit 975b5d4
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 40 deletions.
1 change: 1 addition & 0 deletions news/118.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix handling of relation-fields for working copies of folderish content. [pbauer]
39 changes: 0 additions & 39 deletions plone/app/iterate/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@
from plone.app.iterate.interfaces import ICheckinCheckoutPolicy
from plone.app.iterate.interfaces import IObjectCopier
from plone.app.iterate.util import get_storage
from Products.CMFCore import interfaces as cmf_ifaces
from Products.CMFCore.utils import getToolByName
from zc.relation.interfaces import ICatalog
from zope import component
from zope.component import queryAdapter
Expand Down Expand Up @@ -114,43 +112,6 @@ class BaseContentCopier:
def __init__(self, context):
self.context = context

def _recursivelyReattachUIDs(self, baseline, new_baseline):
original_refs = len(new_baseline.getRefs())
original_back_refs = len(new_baseline.getBRefs())
new_baseline._setUID(baseline.UID())
new_refs = len(new_baseline.getRefs())
new_back_refs = len(new_baseline.getBRefs())
if original_refs != new_refs:
self._removeDuplicateReferences(new_baseline, backrefs=False)
if original_back_refs != new_back_refs:
self._removeDuplicateReferences(new_baseline, backrefs=True)

if cmf_ifaces.IFolderish.providedBy(baseline):
new_ids = new_baseline.contentIds()
for child in baseline.contentValues():
if child.getId() in new_ids:
self._recursivelyReattachUIDs(child, new_baseline[child.getId()])

def _removeDuplicateReferences(self, item, backrefs=False):
# Remove duplicate (back) references from this item.
reference_tool = getToolByName(self.context, "reference_catalog")
if backrefs:
ref_func = reference_tool.getBackReferences
else:
ref_func = reference_tool.getReferences
try:
# Plone 4.1 or later
brains = ref_func(item, objects=False)
except TypeError:
# Plone 4.0 or earlier. Nothing to fix here
return
for brain in brains:
if brain.getObject() is None:
reference_tool.uncatalog_object(brain.getPath())

#################################
# Checkout Support Methods

def _copyBaseline(self, container):
# copy the context from source to the target container
source_container = aq_parent(aq_inner(self.context))
Expand Down
35 changes: 34 additions & 1 deletion plone/app/iterate/dexterity/copier.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
from plone.app.iterate.dexterity import ITERATE_RELATION_NAME
from plone.app.iterate.dexterity.relation import StagingRelationValue
from plone.app.iterate.event import AfterCheckinEvent
from plone.app.relationfield.event import update_behavior_relations
from plone.dexterity.utils import createContentInContainer
from plone.dexterity.utils import iterSchemata
from Products.CMFCore.utils import getToolByName
from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition
from z3c.relationfield import event
from z3c.relationfield import RelationValue
from z3c.relationfield.event import updateRelations
from z3c.relationfield.schema import RelationChoice
from z3c.relationfield.schema import RelationList
from zc.relation.interfaces import ICatalog
from ZODB.PersistentMapping import PersistentMapping
from zope import component
Expand Down Expand Up @@ -191,9 +196,19 @@ def _copyBaseline(self, container):
obj.expiration_date = self.context.expiration_date
elif name == "subjects":
obj.setSubject(self.context.Subject())
elif isinstance(field, RelationList):
if value:
field.set(
obj,
[RelationValue(i.to_id) for i in value if not i.isBroken()],
)
elif isinstance(field, RelationChoice):
if value and not value.isBroken():
field.set(obj, RelationValue(value.to_id))
else:
field.set(obj, value)

update_relation_catalog(obj)
obj.reindexObject()

# copy annotations
Expand Down Expand Up @@ -232,9 +247,18 @@ def _replaceBaseline(self, baseline):
baseline.expiration_date = self.context.expiration_date
elif name == "subjects":
baseline.setSubject(self.context.Subject())
elif isinstance(field, RelationList):
if value:
field.set(
baseline,
[RelationValue(i.to_id) for i in value if not i.isBroken()],
)
elif isinstance(field, RelationChoice):
if value and not value.isBroken():
field.set(baseline, RelationValue(value.to_id))
else:
field.set(baseline, value)

update_relation_catalog(baseline)
baseline.reindexObject()

# Move working children (newly created objects)
Expand All @@ -259,3 +283,12 @@ def _replaceBaseline(self, baseline):
wc_container._delObject(wc_id)

return baseline


def update_relation_catalog(obj):
# updateRelations from z3c.relationfield does not properly update relations in behaviors
# that are registered with a marker-interface.
# update_behavior_relations from plone.app.relationfield does that but does not update
# those in the main schema.
updateRelations(obj, None)
update_behavior_relations(obj, None)
26 changes: 26 additions & 0 deletions plone/app/iterate/tests/test_containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from plone.app.testing import TEST_USER_ID
from plone.app.testing import TEST_USER_NAME
from plone.dexterity.utils import createContentInContainer
from z3c.relationfield import RelationValue
from zc.relation.interfaces import ICatalog
from zope import component
from zope.intid.interfaces import IIntIds
Expand Down Expand Up @@ -304,3 +305,28 @@ def test_relationship_deleted_on_cancel_checkout(self):
rels = list(catalog.findRelations({"from_id": obj_id}))

self.assertEqual(len(rels), 0)

def test_relationfield_handling(self):
# relations are not simply copied
folder = self.portal.docs
doc = folder.doc1
target = folder.doc2
intids = component.getUtility(IIntIds)
doc.relatedItems = [RelationValue(intids.getId(target))]
wc = ICheckinCheckoutPolicy(doc).checkout(doc)
self.assertNotEqual(doc.relatedItems[0], wc.relatedItems[0])
self.assertEqual(doc.relatedItems[0].to_object, target)
self.assertEqual(doc.relatedItems[0].from_object, doc)
self.assertEqual(wc.relatedItems[0].to_object, target)
self.assertEqual(wc.relatedItems[0].from_object, wc)
wc = ICheckinCheckoutPolicy(wc).checkin("modified")
self.assertEqual(doc.relatedItems[0].to_object, target)
self.assertEqual(doc.relatedItems[0].from_object, doc)
self.assertEqual(len(doc.relatedItems), 1)

wc = ICheckinCheckoutPolicy(doc).checkout(doc)
wc.relatedItems = [RelationValue(intids.getId(folder))]
wc = ICheckinCheckoutPolicy(wc).checkin("modified")
self.assertEqual(doc.relatedItems[0].to_object, folder)
self.assertEqual(doc.relatedItems[0].from_object, doc)
self.assertEqual(len(doc.relatedItems), 1)
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
install_requires=[
"Acquisition",
"DateTime",
"plone.app.relationfield",
"plone.locking",
"plone.memoize",
"Products.CMFEditions",
Expand Down

0 comments on commit 975b5d4

Please sign in to comment.