def getInputValue(self): """Return converted and validated widget data. If there is no user input and the field is required, then a ``MissingInputError`` will be raised. If there is no user input and the field is not required, then the field default value will be returned. A ``WidgetInputError`` is raised in the case of one or more errors encountered, inputting, converting, or validating the data. """ if self.hasInput(): self.preserve_widgets = True sequence = self._type(self._generateSequence()) if sequence != self.context.missing_value: # catch and set field errors to ``_error`` attribute try: self.context.validate(sequence) except WidgetInputError as error: self._error = error raise self._error except ValidationError as error: self._error = WidgetInputError(self.context.__name__, self.label, error) raise self._error elif self.context.required: raise MissingInputError(self.context.__name__, self.context.title) return sequence raise MissingInputError(self.context.__name__, self.context.title)
def getInputValue(self): self._error = None field = self.context # form input is required, otherwise raise an error if not self.hasInput(): raise MissingInputError(self.name, self.label, None) # convert input to suitable value - may raise conversion error try: value = self._toFieldValue(self._getFormInput()) except ConversionError as error: # ConversionError is already a WidgetInputError self._error = error raise self._error # allow missing values only for non-required fields if value == field.missing_value and not field.required: return value # value must be valid per the field constraints try: field.validate(value) except ValidationError as v: self._error = WidgetInputError(self.context.__name__, self.label, v) raise self._error return value
def getInputValue(self): self._error = None field = self.context # form input is required, otherwise raise an error if not self.hasInput(): raise MissingInputError(self.name, self.label, None) # convert input to suitable value - may raise conversion error try: value = self._toFieldValue(self._getFormInput()) except ConversionError, error: # ConversionError is already a WidgetInputError self._error = error raise self._error
def getWidgetsData(view, schema, names=None): """Returns user entered data for a set of `schema` fields. The return value is a map of field names to data values. `view` is the view containing the widgets. `schema` is the schema that defines the widget fields. An optional `names` argument can be provided to specify an alternate list of field values to return. If `names` is not specified, or is ``None``, `getWidgetsData` will attempt to return values for all of the fields in the schema. A requested field value may be omitted from the result for one of two reasons: - The field is read only, in which case its widget will not have user input. - The field is editable and not required but its widget does not contain user input. If a field is required and its widget does not have input, `getWidgetsData` raises an error. A widget may raise a validation error if it cannot return a value that satisfies its field's contraints. Errors, if any, are collected for all fields and reraised as a single `WidgetsError`. """ result = {} errors = [] for name, field in _fieldlist(names, schema): widget = getattr(view, name + '_widget') if IInputWidget.providedBy(widget): if widget.hasInput(): try: result[name] = widget.getInputValue() except InputErrors as error: errors.append(error) elif field.required: errors.append(MissingInputError( name, widget.label, 'the field is required')) if errors: raise WidgetsError(errors, widgetsData=result) return result
def getInputValue(self): for name, queryview in self.queryviews: if name + '.apply' in self.request: token = self.request.form.get(name + '.selection') if token is not None: break else: token = self.request.get(self.name) field = self.context if token is None: if field.required: # TODO This code path is untested. raise MissingInputError( field.__name__, self.label, ) return field.missing_value try: value = self.terms.getValue(str(token)) except LookupError: # TODO This code path is untested. err = zope.schema.interfaces.ValidationError( "Invalid value id", token) raise WidgetInputError(field.__name__, self.label, err) # Remaining code copied from SimpleInputWidget # value must be valid per the field constraints try: field.validate(value) except ValidationError as err: # TODO This code path is untested. self._error = WidgetInputError(field.__name__, self.label, err) raise self._error return value
def __call__(self): name = self.request.form.get('name') if name is None: raise MissingInputError('name', '') search_text = self.request.form.get('search_text') if search_text is None: raise MissingInputError('search_text', '') search_filter = self.request.form.get('search_filter') try: factory = getUtility(IVocabularyFactory, name) except ComponentLookupError: raise UnexpectedFormData( 'Unknown vocabulary %r' % name) vocabulary = factory(self.context) if IHugeVocabulary.providedBy(vocabulary): matches = vocabulary.searchForTerms(search_text, search_filter) total_size = matches.count() else: matches = list(vocabulary) total_size = len(matches) batch_navigator = BatchNavigator(matches, self.request) # We need to collate what IPickerEntrySource adapters are required for # the items in the current batch. We expect that the batch will be # homogenous and so only one adapter instance is required, but we # allow for the case where the batch may contain disparate entries # requiring different adapter implementations. # A mapping from adapter class name -> adapter instance adapter_cache = {} # A mapping from adapter class name -> list of vocab terms picker_entry_terms = {} for term in batch_navigator.currentBatch(): picker_entry_source = IPickerEntrySource(term.value) adapter_class = picker_entry_source.__class__.__name__ picker_terms = picker_entry_terms.get(adapter_class) if picker_terms is None: picker_terms = [] picker_entry_terms[adapter_class] = picker_terms adapter_cache[adapter_class] = picker_entry_source picker_terms.append(term.value) # A mapping from vocab terms -> picker entries picker_term_entries = {} # For the list of terms associated with a picker adapter, we get the # corresponding picker entries by calling the adapter. for adapter_class, term_values in picker_entry_terms.items(): picker_entries = adapter_cache[adapter_class].getPickerEntries( term_values, self.context) for term_value, picker_entry in izip(term_values, picker_entries): picker_term_entries[term_value] = picker_entry result = [] for term in batch_navigator.currentBatch(): entry = dict(value=term.token, title=term.title) # The canonical_url without just the path (no hostname) can # be passed directly into the REST PATCH call. api_request = IWebServiceClientRequest(self.request) try: entry['api_uri'] = canonical_url( term.value, request=api_request, path_only_if_possible=True) except NoCanonicalUrl: # The exception is caught, because the api_url is only # needed for inplace editing via a REST call. The # form picker doesn't need the api_url. entry['api_uri'] = 'Could not find canonical url.' picker_entry = picker_term_entries[term.value] if picker_entry.description is not None: if len(picker_entry.description) > MAX_DESCRIPTION_LENGTH: entry['description'] = ( picker_entry.description[:MAX_DESCRIPTION_LENGTH - 3] + '...') else: entry['description'] = picker_entry.description if picker_entry.image is not None: entry['image'] = picker_entry.image if picker_entry.css is not None: entry['css'] = picker_entry.css if picker_entry.alt_title is not None: entry['alt_title'] = picker_entry.alt_title if picker_entry.title_link is not None: entry['title_link'] = picker_entry.title_link if picker_entry.details is not None: entry['details'] = picker_entry.details if picker_entry.alt_title_link is not None: entry['alt_title_link'] = picker_entry.alt_title_link if picker_entry.link_css is not None: entry['link_css'] = picker_entry.link_css if picker_entry.badges: entry['badges'] = picker_entry.badges if picker_entry.metadata is not None: entry['metadata'] = picker_entry.metadata if picker_entry.target_type is not None: entry['target_type'] = picker_entry.target_type result.append(entry) self.request.response.setHeader('Content-type', 'application/json') return simplejson.dumps(dict(total_size=total_size, entries=result))
class SequenceWidget(BrowserWidget, InputWidget): """A widget baseclass for a sequence of fields. subwidget - Optional CustomWidget used to generate widgets for the items in the sequence """ implements(IInputWidget) template = ViewPageTemplateFile('sequencewidget.pt') _type = tuple def __init__(self, context, field, request, subwidget=None): super(SequenceWidget, self).__init__(context, request) self.subwidget = subwidget # The subwidgets are cached in this dict if preserve_widgets is True. self._widgets = {} self.preserve_widgets = False def __call__(self): """Render the widget""" self._update() return self.template() def _update(self): """Set various attributes for the template""" sequence = self._getRenderedValue() num_items = len(sequence) self.need_add = (not self.context.max_length or num_items < self.context.max_length) self.need_delete = num_items and num_items > self.context.min_length self.marker = self._getPresenceMarker(num_items) def widgets(self): """Return a list of widgets to display""" sequence = self._getRenderedValue() result = [] for i, value in enumerate(sequence): widget = self._getWidget(i) widget.setRenderedValue(value) result.append(widget) return result def addButtonLabel(self): button_label = _('Add %s') button_label = translate(button_label, context=self.request, default=button_label) title = self.context.title or self.context.__name__ title = translate(title, context=self.request, default=title) return button_label % title def _getWidget(self, i): """Return a widget for the i-th number of the sequence. Normally this method creates a new widget each time, but when the ``preserve_widgets`` attribute is True, it starts caching widgets. We need it so that the errors on the subwidgets would appear only if ``getInputValue`` was called. ``getInputValue`` on the subwidgets gets called on each request that has data. """ if i not in self._widgets: field = self.context.value_type if self.subwidget is not None: widget = self.subwidget(field, self.request) else: widget = component.getMultiAdapter( (field, self.request), IInputWidget) widget.setPrefix('%s.%d.' % (self.name, i)) if not self.preserve_widgets: return widget self._widgets[i] = widget return self._widgets[i] def hidden(self): """Render the list as hidden fields.""" # length of sequence info sequence = self._getRenderedValue() num_items = len(sequence) # generate hidden fields for each value parts = [self._getPresenceMarker(num_items)] for i in range(num_items): value = sequence[i] widget = self._getWidget(i) widget.setRenderedValue(value) parts.append(widget.hidden()) return "\n".join(parts) def _getRenderedValue(self): """Returns a sequence from the request or _data""" if self._renderedValueSet(): if self._data is self.context.missing_value: sequence = [] else: sequence = list(self._data) elif self.hasInput(): sequence = self._generateSequence() elif self.context.default is not None: sequence = self.context.default else: sequence = [] # ensure minimum number of items in the form while len(sequence) < self.context.min_length: # Shouldn't this use self.field.value_type.missing_value, # instead of None? sequence.append(None) return sequence def _getPresenceMarker(self, count=0): return ('<input type="hidden" name="%s.count" value="%d" />' % (self.name, count)) def getInputValue(self): """Return converted and validated widget data. If there is no user input and the field is required, then a ``MissingInputError`` will be raised. If there is no user input and the field is not required, then the field default value will be returned. A ``WidgetInputError`` is raised in the case of one or more errors encountered, inputting, converting, or validating the data. """ if self.hasInput(): self.preserve_widgets = True sequence = self._type(self._generateSequence()) if sequence != self.context.missing_value: # catch and set field errors to ``_error`` attribute try: self.context.validate(sequence) except WidgetInputError, error: self._error = error raise self._error except ValidationError, error: self._error = WidgetInputError( self.context.__name__, self.label, error) raise self._error elif self.context.required: raise MissingInputError(self.context.__name__, self.context.title)
if sequence != self.context.missing_value: # catch and set field errors to ``_error`` attribute try: self.context.validate(sequence) except WidgetInputError, error: self._error = error raise self._error except ValidationError, error: self._error = WidgetInputError( self.context.__name__, self.label, error) raise self._error elif self.context.required: raise MissingInputError(self.context.__name__, self.context.title) return sequence raise MissingInputError(self.context.__name__, self.context.title) # TODO: applyChanges isn't reporting "change" correctly (we're # re-generating the sequence with every edit, and need to be smarter) def applyChanges(self, content): field = self.context value = self.getInputValue() change = field.query(content, self) != value if change: field.set(content, value) return change def hasInput(self): """Is there input data for the field Return ``True`` if there is data and ``False`` otherwise.