import zope.component
import zope.interface
import zope.schema
import zope.schema.interfaces
from import checkPermission
from zope.schema.vocabulary import SimpleVocabulary
from zope.schema.interfaces import ISource, IContextSourceBinder
import z3c.form.interfaces
import z3c.form.button
import z3c.form.form
import z3c.form.field
import z3c.form.widget
import z3c.form.term
import z3c.form.browser.checkbox
from z3c.formwidget.query import MessageFactory as _
HAS_AC = True
    from AccessControl.interfaces import IRoleManager
except ImportError:
    HAS_AC = False
class SourceTerms(z3c.form.term.Terms):
    def __init__(self, context, request, form, field, widget, source):
        self.context = context
        self.request = request
        self.form = form
        self.field = field
        self.widget = widget
        self.terms = source
class QueryTerms(z3c.form.term.Terms):
    def __init__(self, context, request, form, field, widget, terms):
        self.context = context
        self.request = request
        self.form = form
        self.field = field
        self.widget = widget
        self.terms = SimpleVocabulary(terms)
class QuerySubForm(z3c.form.form.Form):
    css_class = 'querySelectSearch'
    fields = z3c.form.field.Fields(
    def __init__(self, context, request, prefix=None):
        super(QuerySubForm, self).__init__(context, request)
        if prefix is not None:
            self.prefix = prefix    
    def search(self, action):
        data, errors = self.widgets.extract()
        if not errors:
            z3c.form.form.applyChanges(self, self.context, data)
class QueryContext(object):
    query = None
class QuerySourceRadioWidget(
    """Query source widget that allows single selection."""
    _radio = True
    _queryform = None
    _resultsform = None
    _bound_source = None
    ignoreMissing = False
    noValueLabel = _(u'(nothing)')
    def source(self):
        """We need to bind the field to the context so that vocabularies
        appear as sources"""
        return self.field.bind(self.context).source
    def bound_source(self):
        if self._bound_source is None:
            source = self.source
            if IContextSourceBinder.providedBy(source):
                source = source(self.context)
            assert ISource.providedBy(source)
            self._bound_source = source
        return self._bound_source
    def update(self):
        # Allow the source to provide terms until we have more specific ones
        # from the query. Things do not go well if self.terms is None
        self._bound_source = None
        source = self.bound_source
        self.terms = SourceTerms(self.context, self.request, self.form, self.field, self, source)
        # If we have values in the request, use these to get the terms.
        # Otherwise, take the value from the current saved value.
        terms = []
        request_values = z3c.form.interfaces.NOVALUE
        if not self.ignoreRequest:
            request_values = self.extract(default=z3c.form.interfaces.NOVALUE)
        if request_values is not z3c.form.interfaces.NOVALUE:
            if not isinstance(request_values, (tuple, set, list)):
                request_values = (request_values,)
            for token in request_values:
                if not token or token == self.noValueToken:
                except LookupError:
                    # Term no longer available
                    if not self.ignoreMissing:
        elif not self.ignoreContext:
            selection = zope.component.getMultiAdapter(
                (self.context, self.field), z3c.form.interfaces.IDataManager).query()
            if selection is z3c.form.interfaces.NOVALUE:
                selection = []
            elif not isinstance(selection, (tuple, set, list)):
                selection = [selection]
            for value in selection:
                if not value:
                if HAS_AC and IRoleManager.providedBy(value):
                    if not checkPermission('zope2.View', value):
                except LookupError:
                    # Term no longer available
                    if not self.ignoreMissing:
        # Set up query form
        subform = self.subform = QuerySubForm(QueryContext(), self.request,
        # Don't carry on any search if we're ignoring the request
        if not self.ignoreRequest:
            data, errors = subform.extractData()
            if errors:
            # perform the search
            query = data['query']
            if query is not None:
                query_terms = set(
                tokens = set([term.token for term in terms])
                for term in query_terms:
                    if term.token not in tokens:
        # set terms
        self.terms = QueryTerms(self.context, self.request, self.form, self.field, self, terms)
        # update widget - will set self.value
        # add "novalue" option
        if self._radio and not self.required:
            self.items.insert(0, {
                'id': + '-novalue',
                'value': self.noValueToken,
                'label': self.noValueLabel,
                'checked': not self.value or self.value[0] == self.noValueToken,
    def extract(self, default=z3c.form.interfaces.NOVALUE):
        return self.extractQueryWidget(default)
    def render(self):
        subform = self.subform
        return "\n".join((subform.render(), self.renderQueryWidget()))
    def __call__(self):
        return self.render()
    # For subclasses to override
    def updateQueryWidget(self):
    def renderQueryWidget(self):
    def extractQueryWidget(self, default=z3c.form.interfaces.NOVALUE):
        return, default)
class QuerySourceCheckboxWidget(
    QuerySourceRadioWidget, z3c.form.browser.checkbox.CheckBoxWidget):
    """Query source widget that allows multiple selections."""
    _radio = False
    def source(self):
        return self.field.bind(self.context).value_type.source
    def updateQueryWidget(self):
    def renderQueryWidget(self):
        return z3c.form.browser.checkbox.CheckBoxWidget.render(self)
    def extractQueryWidget(self, default=z3c.form.interfaces.NOVALUE):
        return z3c.form.browser.checkbox.CheckBoxWidget.extract(self, default)
def QuerySourceFieldRadioWidget(field, request):
    return z3c.form.widget.FieldWidget(field, QuerySourceRadioWidget(request))
def QuerySourceFieldCheckboxWidget(field, request):
    return z3c.form.widget.FieldWidget(field, QuerySourceCheckboxWidget(request))
class IgnoreMissingQuerySourceRadioWidget(QuerySourceRadioWidget):
    """Query source widget that allows single selection and ignores missing
    ignoreMissing = True
class IgnoreMissingQuerySourceCheckboxWidget(QuerySourceRadioWidget):
    """Query source widget that allows multiple selections and ignores missing
    ignoreMissing = True
def IgnoreMissingQuerySourceFieldRadioWidget(field, request):
    return z3c.form.widget.FieldWidget(field,
def IgnoreMissingQuerySourceFieldCheckboxWidget(field, request):
    return z3c.form.widget.FieldWidget(field,