diff --git a/docs/HISTORY.txt b/docs/HISTORY.txt index 80b9c1c..b9c0c26 100644 --- a/docs/HISTORY.txt +++ b/docs/HISTORY.txt @@ -4,6 +4,9 @@ Changelog 1.0.6 (unreleased) ------------------ +* Ensure context is a content item of some sort. + [elro] + * Import getSite from zope.component to avoid dependency on zope.app.component. [hvelarde] diff --git a/plone/formwidget/contenttree/source.py b/plone/formwidget/contenttree/source.py index 540c847..29bc00a 100644 --- a/plone/formwidget/contenttree/source.py +++ b/plone/formwidget/contenttree/source.py @@ -1,6 +1,5 @@ import logging -from Acquisition.interfaces import IAcquirer import Missing from zope.interface import implements from zope.component import getMultiAdapter @@ -8,15 +7,8 @@ from zope.component import getMultiAdapter from zope.schema.interfaces import IContextSourceBinder from zope.schema.vocabulary import SimpleTerm -from zope.app.component.hooks import getSite -try: - from zope.globalrequest import getRequest - getRequest # pyflakes -except ImportError: - # Fake it - getRequest = object - from plone.app.layout.navigation.interfaces import INavigationQueryBuilder +from plone.app.layout.navigation.root import getNavigationRootObject from plone.app.vocabularies.catalog import parse_query from Products.CMFCore.utils import getToolByName @@ -24,6 +16,7 @@ from Products.ZCTextIndex.ParseTree import ParseError from plone.formwidget.contenttree.interfaces import IContentSource from plone.formwidget.contenttree.interfaces import IContentFilter +from plone.formwidget.contenttree.utils import closest_content from OFS.interfaces import ITraversable @@ -73,8 +66,8 @@ class PathSource(object): def __init__(self, context, selectable_filter, navigation_tree_query=None): self.context = context - - query_builder = getMultiAdapter((context, self), + nav_root = getNavigationRootObject(context, None) + query_builder = getMultiAdapter((nav_root, self), INavigationQueryBuilder) query = query_builder() @@ -239,7 +232,7 @@ class PathSourceBinder(object): def __call__(self, context): return self.path_source( - self._find_page_context(context), + closest_content(context), selectable_filter=self.selectable_filter, navigation_tree_query=self.navigation_tree_query) @@ -248,28 +241,6 @@ class PathSourceBinder(object): # now and pass through to the bound version return self(None).__contains__(value) - def _find_page_context(self, given_context=None): - """Try to find a usable context, with increasing agression""" - # Normally, we should be given a useful context (e.g the page) - c = given_context - if IAcquirer.providedBy(c): - return c - # Subforms (e.g. DataGridField) may not have a context set, find out - # what page is being published - c = getattr(getRequest(), 'PUBLISHED', None) - if IAcquirer.providedBy(c): - return c - # During widget traversal nothing is being published yet, use getSite() - c = getSite() - if IAcquirer.providedBy(c): - return c - # During kss_z3cform_inline_validation, PUBLISHED and getSite() return - # a Z3CFormValidation object. What we want is it's context. - c = getattr(getattr(getRequest(), 'PUBLISHED', None), 'context', None) - if IAcquirer.providedBy(c): - return c - raise ValueError('Cannot find suitable context to bind to source') - class ObjPathSourceBinder(PathSourceBinder): path_source = ObjPathSource diff --git a/plone/formwidget/contenttree/utils.py b/plone/formwidget/contenttree/utils.py new file mode 100644 index 0000000..3856f09 --- /dev/null +++ b/plone/formwidget/contenttree/utils.py @@ -0,0 +1,48 @@ +from Acquisition import aq_base +from Products.CMFCore.interfaces import IContentish +from Products.CMFCore.interfaces import IFolderish +from zope.component.hooks import getSite +try: + from zope.globalrequest import getRequest + getRequest # pyflakes +except ImportError: + # Fake it + getRequest = object + + +def closest_content(context=None): + """Try to find a usable context, with increasing agression""" + # Normally, we should be given a useful context (e.g the page) + c = context + c = _valid_context(c) + if c is not None: + return c + # Subforms (e.g. DataGridField) may not have a context set, find out + # what page is being published + c = getattr(getRequest(), 'PUBLISHED', None) + c = _valid_context(c) + if c is not None: + return c + # During widget traversal nothing is being published yet, use getSite() + c = getSite() + c = _valid_context(c) + if c is not None: + return c + raise ValueError('Cannot find suitable context to bind to source') + + +def _valid_context(context): + """Walk up until finding a content item.""" + # Avoid loops + seen = set() + while context is not None and aq_base(context) not in seen: + seen.add(aq_base(context)) + if (IContentish.providedBy(context) + or IFolderish.providedBy(context)): + return context + parent = getattr(context, '__parent__', None) + if parent is None: + parent = getattr(context, 'context', None) + context = parent + + return None diff --git a/plone/formwidget/contenttree/widget.py b/plone/formwidget/contenttree/widget.py index a2d934f..6f5945c 100644 --- a/plone/formwidget/contenttree/widget.py +++ b/plone/formwidget/contenttree/widget.py @@ -11,8 +11,6 @@ import z3c.form.interfaces import z3c.form.widget import z3c.form.util -from zope.component.hooks import getSite - from plone.app.layout.navigation.interfaces import INavtreeStrategy from plone.app.layout.navigation.navtree import buildFolderTree @@ -24,6 +22,7 @@ from Products.Five.browser import BrowserView from plone.formwidget.contenttree.interfaces import IContentTreeWidget from plone.formwidget.contenttree import MessageFactory as _ +from plone.formwidget.contenttree.utils import closest_content class Fetch(BrowserView): @@ -80,14 +79,13 @@ class Fetch(BrowserView): level = self.request.form.get('rel', 0) navtree_query = source.navigation_tree_query.copy() - navtree_query['path'] = {'depth': 1, 'query': directory} + if directory is not None: + navtree_query['path'] = {'depth': 1, 'query': directory} if 'is_default_page' not in navtree_query: navtree_query['is_default_page'] = False - content = context - if not IAcquirer.providedBy(content): - content = getSite() + content = closest_content(context) strategy = getMultiAdapter((content, widget), INavtreeStrategy) catalog = getToolByName(content, 'portal_catalog') @@ -138,10 +136,7 @@ class ContentTreeBase(Explicit): return self.bound_source.getTermByBrain(brain) def render_tree(self): - content = self.context - if not IAcquirer.providedBy(content): - content = getSite() - + content = closest_content(self.context) source = self.bound_source strategy = getMultiAdapter((content, self), INavtreeStrategy)