Adding a relation outside a subsite returns an error

Using a RelationChoice field, I cannot create a relation outside a subsite. I am using the latest Plone. The site has subsites (each subsite implements INavigationRoot). On the root level, images and files are stored.

When I try to add a relation (outside of the subsite), I get a vague 'constraint not satisfied' error. Adding relations within the subsite is working properly:

Example structure:

- Plone site
  - Subsite 1
  - Subsite 2
  - Root images
  - Root folders

From a subsite I want to create a relation to to one of the root folders. Currently I have this field in my behavior:

    image_reference = RelationChoice(
        title=u'Image',
        required=False,
        vocabulary='plone.app.vocabularies.Catalog',
    )

My hunch is, that is not allowed to create a relation outside of the subsite. However I'll handle the resolving of the root files/images in the view logic. I tried searching in the docs/training.plone.org for this error message but I could not find anything related. It seems the relation field has a constraint set, but where and how to disable this specific one? How can I fix this?

A subsite is supposed to be self-contained - for good or bad. It is as it is. Catalog queries inside a subsite will only return items from the subsite. If needed provide your own vocabulary which could bypass the catalog query. I am not sure at which Plone injects the INavigation constraint however in doubt you might be able to use
portal_catalog.unrestrictedSearchResults() by-passing all constraints including security related constrains...

Ok, sounds logical. :slight_smile: The error is not very clear, but then again I'm customizing Plone with subsites. I've added an override for AddonEditForm.extractData() to allow this specific error and add the object outside the subsite (see this gist for those encountering the same edge case).

Preferably I would remove the constraint but this seems too complex. I've searched in CMFPlone, plone.dexterity and z3c.form but could not find out where this specific constraint is set. The results returned by vocabulary='plone.app.vocabularies.Catalog' already include the objects outside the subsite.

Hi, after five years I came across the same error.

The problem is generated because the catalog vocabulary is called in two different ways: one by the mockup widget thorught the getVocabulary endpoint; the other at save time, tipically by some edit/add form.

In the first case the query path is free to be changed by the editor that can browse the whole portal.

In the second case (at save edit/add form time) the vocabulary is called to validate the value inputed, and the INavigationRoot path is injected in the query path here plone.app.vocabularies/plone/app/vocabularies/catalog.py at 75709b94547af257a37b3b9c4aaab3931721de37 · plone/plone.app.vocabularies · GitHub because parsed variable is empty, resulting in a query like:

{'path': {'query': '/Plone/aaa/bbb/sub-site-folder', 'depth': -1}}

In my opinion this is a misleading UX problem because if it's true that

A subsite is supposed to be self-contained - for good or bad. It is as it is

it's not true that the widget blocks editor from searching for (and select!) an item outside the sub-site: the widget allows it indeed.

The widget can be customized with pattern options directive. It seems that these options affect only the way it's rendered and does not affect the save field validation.

BTW, to provide another solution to whom may need, In our project we have just monkey patched __init__ method of plone.app.vocabularies.catalog.CatalogVocabulary with a brutal bypass that affects broadly every RelatedItem widget (what we actually want):

from plone.app.vocabularies.catalog import CatalogVocabulary
from plone import api
from plone.app.multilingual.browser.interfaces import make_relation_root_path

def init_bypass_navroot_query(self,  query, *interfaces):
    self.query = query

    # the request comes from the widget, the editor is browsing
    if '@@getVocabulary' in api.portal.getRequest().URL:
        return

    if not self.query:
        return

    #  the request comes at savetime    
    if 'path' in query:
        self.query['path']['query'] = make_relation_root_path(api.portal.get())

CatalogVocabulary.__init__ = init_bypass_navroot_query