Skip to content

Commit c9480fa

Browse files
authored
Merge pull request #275 from collective/139-fix-parent-type
Create parent of the correct type if the parent is missing
2 parents 0735953 + 5ed4deb commit c9480fa

2 files changed

Lines changed: 97 additions & 15 deletions

File tree

src/collective/exportimport/import_content.py

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -982,11 +982,11 @@ def get_parent_as_container(self, item):
982982
return self.create_container(item)
983983

984984
def create_container(self, item):
985-
"""Create container for item.
985+
"""Create parent container for item.
986986
987987
See remarks in get_parent_as_container for some corner cases.
988988
"""
989-
folder = self.context
989+
container = self.context
990990
parent_url = unquote(item["parent"]["@id"])
991991
parent_url_parsed = urlparse(parent_url)
992992
# Get the path part, split it, remove the always empty first element.
@@ -1001,28 +1001,52 @@ def create_container(self, item):
10011001
parent_path = parent_path[1:]
10021002

10031003
# Handle folderish Documents provided by plone.volto
1004-
fti = getUtility(IDexterityFTI, name="Document")
1005-
parent_type = (
1006-
"Document" if fti.klass.endswith("FolderishDocument") else "Folder"
1004+
document_fti = getUtility(IDexterityFTI, name="Document")
1005+
container_type = (
1006+
"Document" if document_fti.klass.endswith("FolderishDocument") else "Folder"
10071007
)
10081008
# create original structure for imported content
1009-
for element in parent_path:
1010-
if element not in folder:
1011-
folder = api.content.create(
1012-
container=folder,
1013-
type=parent_type,
1009+
for element in parent_path[:-1]:
1010+
if element not in container:
1011+
container = api.content.create(
1012+
container=container,
1013+
type=container_type,
10141014
id=element,
10151015
title=element,
10161016
)
10171017
logger.info(
10181018
u"Created container %s to hold %s",
1019-
folder.absolute_url(),
1019+
container.absolute_url(),
10201020
item["@id"],
10211021
)
10221022
else:
1023-
folder = folder[element]
1023+
container = container[element]
10241024

1025-
return folder
1025+
# Finally create parent
1026+
parent_type = item["parent"]["@type"]
1027+
try:
1028+
parent = api.content.create(
1029+
container=container,
1030+
type=parent_type,
1031+
id=parent_path[-1],
1032+
title=item["parent"]["title"],
1033+
)
1034+
except InvalidParameterError:
1035+
logger.info("Cannot add %s in %r. Fallback to %s", parent_type, container, container_type, exc_info=True)
1036+
parent = api.content.create(
1037+
container=container,
1038+
type=container_type,
1039+
id=parent_path[-1],
1040+
title=item["parent"]["title"],
1041+
)
1042+
logger.info(
1043+
u"Created %s %s to hold %s",
1044+
parent.portal_type,
1045+
parent.absolute_url(),
1046+
item["@id"],
1047+
)
1048+
1049+
return parent
10261050

10271051
def set_uuid(self, item, obj):
10281052
uuid = item.get("UID")

src/collective/exportimport/tests/test_import.py

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from plone.app.testing import SITE_OWNER_NAME
1111
from plone.app.testing import SITE_OWNER_PASSWORD
1212
from plone.app.textfield.value import RichTextValue
13+
from plone.dexterity.fti import DexterityFTI
1314
from plone.namedfile.file import NamedBlobImage
1415
from plone.namedfile.file import NamedImage
1516
from Products.CMFPlone.interfaces.constrains import ENABLED
@@ -411,7 +412,64 @@ def test_import_content_with_missing_folder(self):
411412
self.assertIn("folder1", portal.contentIds())
412413
new_folder = portal["folder1"]
413414
# The auto generated folder will have its id as title
414-
self.assertEqual(new_folder.Title(), "folder1")
415+
self.assertEqual(new_folder.Title(), "Folder 1")
416+
# The document should be back.
417+
self.assertIn("doc1", new_folder.contentIds())
418+
new_doc = new_folder["doc1"]
419+
self.assertEqual(new_doc.Title(), "Document 1")
420+
self.assertEqual(new_doc.portal_type, "Document")
421+
422+
def test_import_content_with_missing_container_of_different_type(self):
423+
# First create some content.
424+
app = self.layer["app"]
425+
portal = self.layer["portal"]
426+
login(app, SITE_OWNER_NAME)
427+
428+
# Create custom container type
429+
fti = DexterityFTI("Container")
430+
portal.portal_types._setObject("Container", fti)
431+
fti.klass = "plone.dexterity.content.Container"
432+
fti.filter_content_types = False
433+
fti.behaviors = ("plone.app.dexterity.behaviors.metadata.IBasic",)
434+
435+
folder = api.content.create(
436+
container=portal, type="Folder", id="folder1", title="Folder 1"
437+
)
438+
api.content.create(
439+
container=folder, type="Document", id="doc1", title="Document 1"
440+
)
441+
transaction.commit()
442+
443+
# Now export the document.
444+
browser = self.open_page("@@export_content")
445+
browser.getControl(name="portal_type").value = ["Document"]
446+
browser.getForm(action="@@export_content").submit(name="submit")
447+
raw_data = browser.contents
448+
if not browser.contents:
449+
raw_data = DATA[-1]
450+
451+
# Edit the raw data to pretend the parent was not a Folder but a custom container type
452+
raw_data = raw_data.replace(b'"@type": "Folder"', b'"@type": "Container"')
453+
454+
# Remove both the folder and document.
455+
api.content.delete(folder)
456+
transaction.commit()
457+
self.assertNotIn("folder1", portal.contentIds())
458+
459+
# Now import the document.
460+
# The missing folder structure should be created.
461+
browser = self.open_page("@@import_content")
462+
upload = browser.getControl(name="jsonfile")
463+
upload.add_file(raw_data, "application/json", "Document.json")
464+
browser.getForm(action="@@import_content").submit()
465+
self.assertIn("Imported 1 items", browser.contents)
466+
467+
# The folder should be back.
468+
self.assertIn("folder1", portal.contentIds())
469+
new_folder = portal["folder1"]
470+
# The auto generated container will have the parents type and title
471+
self.assertEqual(new_folder.portal_type, "Container")
472+
self.assertEqual(new_folder.Title(), "Folder 1")
415473
# The document should be back.
416474
self.assertIn("doc1", new_folder.contentIds())
417475
new_doc = new_folder["doc1"]
@@ -463,7 +521,7 @@ def test_import_content_from_other_plone_site(self):
463521
self.assertIn("folder1", portal.contentIds())
464522
new_folder = portal["folder1"]
465523
# The auto generated folder will have its id as title
466-
self.assertEqual(new_folder.Title(), "folder1")
524+
self.assertEqual(new_folder.Title(), "Folder 1")
467525
# The document should be back.
468526
self.assertIn("doc1", new_folder.contentIds())
469527
new_doc = new_folder["doc1"]

0 commit comments

Comments
 (0)