Petri
Petri

Reputation: 5006

custom Plone Dexterity factory to create subcontent

I thought it would be possible to create a custom Dexterity factory that calls the default factory and then adds some subcontent (in my case Archetypes-based) to the created 'parent' Dexterity content.

I have no problem creating and registering the custom factory.

However, regardless of what method I use (to create the AT subcontent), the subcontent creation fails when attempted from within the custom factory.

I've tried everything from plone.api to invokeFactory to direct instantiation of the AT content class.

In most cases, traceback shows the underlying Plone/CMF code tries to get portal_types tool using getToolByName and fails; similarly when trying to instantiate the AT class directly, the manage_afterAdd then tries to access reference_catalog, which fails.

Is there any way to make this work?

Upvotes: 1

Views: 142

Answers (2)

Petri
Petri

Reputation: 5006

After some trials and errors, it turns out this is possible:

from zope.container.interfaces import INameChooser
from zope.component.hooks import getSite
from plone.dexterity.factory import DexterityFactory


class CustomDexterityFactory(DexterityFactory):

   def __call__(self, *args, **kw):
      folder = DexterityFactory.__call__(self, *args, **kw)

      # we are given no context to work with so need to resort to getSite
      # hook or zope.globalrequest.getRequest and then wrap the folder
      # in the context of the add view
      site = getSite()
      wrapped = folder.__of__(site["PUBLISHED"].context)

      # invokeFactory fails if the container has no id
      folder.id = "tmp_folder_id" 

      # standard AT content creation
      wrapped.invokeFactory("Page", "tmp_obj_id")
      page = wrapped["tmp_obj_id"]
      new_id = INameChooser(service_obj).chooseName(title, page)
      page.setId(new_id)
      page.setTitle(title)

      # empty the id, otherwise it will keep
      folder.id = None 

      return folder

While the above works, at some point the created Page gets indexed (perhaps by invokeFactory), which means there will be a bogus entry in the catalog. Code to remove the entry could be added to the factory.

Overall, it would be easier to just create an event handler, as suggested by @keul in his answer.

Upvotes: 0

keul
keul

Reputation: 7819

A different approach can simply be to add event handlers for IObjectAddedEvent, and add there your subcontents using common APIs.

Upvotes: 6

Related Questions