Customizing Contentbrowser Pattern Options

I have a behavior with some RelationChoice and RelationList Fields.
In the end i have "related_items", "related_news", "related_events" and "related_contacts" fields.

My goal:

  • related_items: you can select many items in a site via contentbrowser and respect the INavigationRoot
  • related_news: you can select only one collection in a site via contentbrowser and respect the INavigationRoot
  • related_events: you can select only one collection in a site via contentbrowser and respect the INavigationRoot
  • related_contacts: you can select many items of type "Person" in a site via contentbrowser and respect the INavigationRoot

The Problem:
Some RelationChoice and RelationList Fields in one Form. How can i customize the pattern options in a clean way?

Solution:

  • Subclassing the fields
  • implement a specific interface
  • for this specific interface register an adapter to modify the pattern options

Sample:

class ICollectionRelationChoice(Interface):
  """Marker for pattern option."""
  pass

class IContactRelationList(Interface):
  """Marker for pattern option."""

@implementer(ICollectionRelationChoice)
class CollectionRelationChoice(RelationChoice):
  """ Subclassing RelationChoice Field """
  pass

@implementer(IContactRelationList)
class ContactRelationList(RelationList):
  """ Subclassing RelationList Field """
    pass


@provider(IFormFieldProvider)
class IContentListingBehavior(model.Schema):
  """ Schema of behavior """
  
  related_news = CollectionRelationChoice(
    title=_("News Collection"),
    vocabulary="plone.app.vocabularies.Catalog",
    required=False,
  )

  related_contacts = ContactRelationList(
    title=_("Contact Box"),
    required=False,
    value_type=RelationChoice(
      title="contacts",
      vocabulary="plone.app.vocabularies.Catalog",
    ),
  )
@implementer(IValue)
@adapter(IContentListingMarker, IRequest, IForm, ICollectionRelationChoice, IContentBrowserWidget,)
class CollectionContentbrowserPatternOptions:
  """ Adapter class for custon pattern options """
  def __init__(self, context, request, form, field, widget):
    self.context = context
    self.request = request
    self.form = form
    self.field = field
    self.widget = widget

  def get(self):
    portal_state = getMultiAdapter( (self.context, self.request), name="plone_portal_state")
    return {
      "rootPath": portal_state.navigation_root_path(),
      "selectableTypes": "Collection",
    }

@implementer(IValue)
@adapter(IContentListingMarker, IRequest, IForm, IContactRelationList, ContentBrowserWidget)
class PersonContentbrowserPatternOptions:
  """ Adapter class for custon pattern options """
  def __init__(self, context, request, form, field, widget):
    self.context = context
    self.request = request
    self.form = form
    self.field = field
    self.widget = widget

  def get(self):
    portal_state = getMultiAdapter((self.context, self.request), name="plone_portal_state")
    return {
      "rootPath": portal_state.navigation_root_path(),
      "selectableTypes": "Person",
    }
<configure
    xmlns="http://namespaces.zope.org/zope"
    xmlns:browser="http://namespaces.zope.org/browser"
    xmlns:plone="http://namespaces.plone.org/plone"
    xmlns:z3c="http://namespaces.zope.org/z3c"
    xmlns:zcml="http://namespaces.zope.org/zcml"  
    >

  <!-- override pattern options for contentbrowser-->
    
  <!-- set the root path and selectable types 'Collection'-->
  <adapter
      factory=".patterns.CollectionContentbrowserPatternOptions"
      name="pattern_options"
      />
    
  <!-- set the root path and selectable types 'Person'-->
  <adapter
      factory=".patterns.PersonContentbrowserPatternOptions"
      name="pattern_options"
      />

</configure>

No JS Hacking needed... I love it.. :wink:

4 Likes

You can also assign a function to the widget:

from ..utils import default_favorites

related_image = RelationChoice(
    title=_(u"Related Teaser Image"),
    description=_(u"Add a teaser image"),
    required=False,
    default=None,
    vocabulary='plone.app.vocabularies.Catalog'
)

directives.widget(
    'related_image',
    RelatedItemsFieldWidget,
    pattern_options = {
        'recentlyUsed': True,
        'basePath'    : default_base_path,
        'mode'        : 'auto',
        'favorites'   : default_favorites,
        'folderTypes': ['Folder', 'LIF', 'LRF'],
        'selectableTypes' : ['Image'],
    }
)

Sure, but the access to the current context and request is much clearer in the adapter