.preserve file not respected when creating default content

In Creating objects — Plone Documentation v5.2 (yes, old version) the .preserve file is supposed to allow you to prevent overwriting of content items, specified by their ID.

in profiles/default/structure there is a .objects file containing:

tools,Folder
notes,Folder
images,Folder
documents,Folder

and a .preserve file containing

tools
notes
documents
images

But the .preserve file is not being respected: anything that existed inside, say, the tools folder is gone after I uninstall and reinstall the add-on.

Where in Plone does this code live? I'd love to step through it...

From what I can see it does not prevent overwriting, but it does prevent deleting the existing item before overwriting it. Products.GenericSetup/src/Products/GenericSetup/content.py at master · zopefoundation/Products.GenericSetup · GitHub

If you have a version of Plone that still uses portal_quickinstaller then your problem might also be at the uninstall step, where it deletes any objects that were added to the portal during installation: Products.CMFQuickInstallerTool/Products/CMFQuickInstallerTool/InstalledProduct.py at master · plone/Products.CMFQuickInstallerTool · GitHub You can avoid that by doing a reinstall rather than separate uninstall and install steps: Products.CMFQuickInstallerTool/Products/CMFQuickInstallerTool/QuickInstallerTool.py at master · plone/Products.CMFQuickInstallerTool · GitHub

1 Like

Just a thought: could it be ok to check if folder exists and then run a profile if it does not?

In setuphandlers you could check for (example):

def post_install(context):
    portal = api.portal.get()
    if not portal.get('templates', False):

         ## install profile 'add_templates'
1 Like

This is on Plone 6.1.3.

Uninstall does not remove the contents of the tools and notes folders, so it's at the install step that it must be happening.

It looks like .preserve should be renamed to something more honest :slight_smile:

Yeah, I am going to try using a setup handler...

On Plone 6.1, maybe you want to use plone.exportimport (which is based on plone.restapi serialization to JSON) instead of the old GenericSetup content thing.

1 Like

Thanks, I saw that mentioned in a couple of places, but this code worked nicely for what I needed (to create the folders if needed and to constrain the addable types).

import plone.api as api
from Products.CMFPlone.interfaces import constrains
from plone.base.interfaces.constrains import ISelectableConstrainTypes


def install(context):
    """Post install script"""
    portal = api.portal.get()

    if not portal.hasObject('notes'):
        notes_folder = api.content.create(
            container=portal,
            type='Folder',
            id='notes',
            title='Notes',
            description='A folder to contain all notes.'
        )
    else:
        notes_folder = portal['notes']
    # Set constraints on the notes folder
    constraints = ISelectableConstrainTypes(notes_folder)
    constraints.setConstrainTypesMode(constrains.ENABLED)
    constraints.setLocallyAllowedTypes(['note'])
    constraints.setImmediatelyAddableTypes(['note'])

    if not portal.hasObject('tools'):
        tools_folder = api.content.create(
            container=portal,
            type='Folder',
            id='tools',
            title='Tools',
            description='A folder to contain all tools.'
        )
    else:
        tools_folder = portal['tools']
    # Set constraints on the tools folder
    constraints = ISelectableConstrainTypes(tools_folder)
    constraints.setConstrainTypesMode(constrains.ENABLED)
    constraints.setLocallyAllowedTypes(['tool'])
    constraints.setImmediatelyAddableTypes(['tool'])

    if not portal.hasObject('images'):
        images_folder = api.content.create(
            container=portal,
            type='Folder',
            id='images',
            title='Images',
            description='A folder to contain all images.'
        )
    else:
        images_folder = portal['images']
    # Set constraints on the images folder
    constraints = ISelectableConstrainTypes(images_folder)
    constraints.setConstrainTypesMode(constrains.ENABLED)
    constraints.setLocallyAllowedTypes(['Image'])
    constraints.setImmediatelyAddableTypes(['Image'])

    if not portal.hasObject('documents'):
        documents_folder = api.content.create(
            container=portal,
            type='Folder',
            id='documents',
            title='Documents',
            description='A folder to contain all documents.'
        )
    else:
        documents_folder = portal['documents']
    # Set constraints on the documents folder
    constraints = ISelectableConstrainTypes(documents_folder)
    constraints.setConstrainTypesMode(constrains.ENABLED)
    constraints.setLocallyAllowedTypes(['File'])
    constraints.setImmediatelyAddableTypes(['File'])

I figured it might not work to try to constrain the addable types to the PloneSite, and, sure enough, it did not:

    # Set constraints on the site root
    constraints = ISelectableConstrainTypes(portal)
    allowed_types = constraints.getLocallyAllowedTypes()
    if 'note' in allowed_types:
        allowed_types.remove('note')
    if 'tool' in allowed_types:
        allowed_types.remove('tool')
    constraints.setConstrainTypesMode(constrains.ENABLED)
    constraints.setLocallyAllowedTypes(allowed_types)
    constraints.setImmediatelyAddableTypes(allowed_types)
TypeError: ('Could not adapt', <PloneSite at /Plone>, <InterfaceClass plone.base.interfaces.constrains.ISelectableConstrainTypes>)

Changing the Note (or Tool) content type's global_allow property indeed does it everywhere, including in the folders where I do want them addable.

Is there another way to prevent adding them at the site root but still have them addable in folders?

I’ve done this: set global_allow=True, but use an add_permission which is disabled for all roles at the site root, and added to the Contributor role on specific folders.

That’s probably not quite the right fit here, since you want it for all folders except the root.

1 Like

I think I'll create a content rule that automatically moves any tool or note created at the site root into the appropriate folder.

1 Like

A post was split to a new topic: Creating a content rule in code