Context: I have a simple way to save some json data in annotations (on portal object). This definitions sometimes changes so I just upload the new json.
I'm trying to create a behavior for my content types: a new field that saves the terms you selected from the current definitions.
The problem is that I can't access annotations from the function used to populate the vocabulary. I get:
(Pdb) from plone import api
(Pdb) api.portal.get()
*** plone.api.exc.CannotGetPortalError: Unable to get the portal object. More info on https://docs.plone.org/develop/plone.api/docs/api/exceptions.html#plone.api.exc.CannotGetPortalError
This is what I have now:
vocabulary.py
def generic_vocabulary(_terms, sort=True):
"""Returns a zope vocabulary from a dict or a list"""
if _terms and isinstance(_terms, dict):
_terms = _terms.items()
elif _terms and isinstance(_terms[0], str):
_terms = [(x, x) for x in _terms]
if sort:
_terms = sorted(_terms, key=lambda x: x[0])
def factory(context):
"""Simple Vocabulary factory"""
return SimpleVocabulary(
[SimpleTerm(n, n.encode("utf-8"), term) for n, term in _terms]
)
return factory
_terms_list = (
("key1", "Value 1"),
("key2", "Value 2"),
)
def generate_terms_list():
import pdb
pdb.set_trace()
# TODO Get the list of terms from annotations
# from plone import api
# api.portal.get() ERROR HERE
return _terms_list
terms_list = generic_vocabulary(generate_terms_list())
alsoProvides(terms_list, IVocabularyFactory)
configure.zcml
<utility
name="my.add.on.terms"
component=".vocabulary.terms_list"
/>
behavior.py
from plone.autoform import directives
from plone.autoform.interfaces import IFormFieldProvider
from plone.supermodel import model
from zope import schema
from zope.interface import provider
@provider(IFormFieldProvider)
class ICustomTermsListBehavior(model.Schema):
terms_list = schema.List(
title="Terms List",
description="Select Terms",
required=False,
value_type=schema.Choice(vocabulary="my.add.on.terms"),
)
directives.write_permission(terms_list="cmf.ManagePortal")
The code is working as expected if I use _terms_list
. Now I just want to replace these demo terms with the real content from annotations:
def get_annot():
annot_key = 'TEST_KEY'
container = api.portal.get()
annotations = IAnnotations(container)
return annotations[annot_key]
How can I fix plone.api.exc.CannotGetPortalError: Unable to get the portal object
error, and get my data from annotations?
UPDATE:
Already tried, too:
(Pdb) from zope.component import getUtility
(Pdb) from Products.CMFCore.interfaces import ISiteRoot
(Pdb) portal = getUtility(ISiteRoot)
*** zope.interface.interfaces.ComponentLookupError: (<InterfaceClass Products.CMFCore.interfaces.ISiteRoot>, '')
Same for:
(Pdb) site = api.portal.getSite()
(Pdb) site is None
True
Not working:
(Pdb) from zope.component.hooks import getSite
(Pdb) getSite() is None
True
The answer is: YES.
I found it here: https://5.docs.plone.org/external/plone.app.dexterity/docs/advanced/vocabularies.html#dynamic-sources
Solved with:
def generate_terms_list(context):
terms = [(str(x["id"]), str(x["value"])) for x in get_annot()]
return terms
@provider(IContextSourceBinder)
def get_terms(context):
return generic_vocabulary(generate_terms_list(context))(context)
...
@provider(IFormFieldProvider)
class ICustomTermsListBehavior(model.Schema):
terms_list = schema.List(
title="Terms List",
description="Select Terms",
required=False,
value_type=schema.Choice(source=get_terms),
)
directives.write_permission(terms_list="cmf.ManagePortal")
Note source instead of vocabulary and @provider(IContextSourceBinder)
+ context.