def __get__(self, inst, klass): if inst is None: return self attribute = getattr(inst.context, self._get_name, _marker) if attribute is _marker: field = self._field.bind(inst) attribute = getattr(field, 'default', _marker) if attribute is _marker: raise AttributeError(self._field.__name__) elif callable(attribute): attribute = attribute() if isinstance(attribute, DateTime): # Ensure datetime value is stripped of any timezone and seconds # so that it can be compared with the value returned by the widget return datetime(*map(int, attribute.parts()[:6])) if attribute is None: return if IText.providedBy(self._field): return attribute.decode('utf-8') if ISequence.providedBy(self._field): if IText.providedBy(self._field.value_type): return type(attribute)(item.decode('utf-8') for item in attribute) return attribute
def __set__(self, inst, value): field = self._field.bind(inst) field.validate(value) if field.readonly: raise ValueError(self._field.__name__, 'field is readonly') if isinstance(value, datetime): # The ensures that the converted DateTime value is in the # server's local timezone rather than GMT. value = DateTime(value.year, value.month, value.day, value.hour, value.minute) elif value is not None: if IText.providedBy(self._field): value = value.encode('utf-8') elif ISequence.providedBy(self._field): if IText.providedBy(self._field.value_type): value = type(value)( item.encode('utf-8') for item in value ) if self._set_name: getattr(inst.context, self._set_name)(value) elif inst.context.hasProperty(self._get_name): inst.context._updateProperty(self._get_name, value) else: setattr(inst.context, self._get_name, value)
def __get__(self, inst, klass): if inst is None: return self attribute = getattr(inst.context, self._get_name, _marker) if attribute is _marker: field = self._field.bind(inst) attribute = getattr(field, 'default', _marker) if attribute is _marker: raise AttributeError(self._field.__name__) elif callable(attribute): attribute = attribute() if isinstance(attribute, DateTime): # Ensure datetime value is stripped of any timezone and seconds # so that it can be compared with the value returned by the widget return datetime(*map(int, attribute.parts()[:6])) if attribute is None: return if IText.providedBy(self._field): return attribute.decode('utf-8') if ISequence.providedBy(self._field): if IText.providedBy(self._field.value_type): return type(attribute)( item.decode('utf-8') for item in attribute ) return attribute
def encode(data, schema, ignore=()): """Given a data dictionary with key/value pairs and schema, return an encoded query string. This is similar to urllib.urlencode(), but field names will include the appropriate field type converters, e.g. an int field will be encoded as fieldname:int=123. Fields not found in the data dict will be ignored, and items in the dict not in the schema will also be ignored. Additional fields to ignore can be passed with the 'ignore' parameter. If any fields cannot be converted, a ComponentLookupError will be raised. """ encode = [] for name, field in getFieldsInOrder(schema): if name in ignore or name not in data: continue converter = IFieldTypeConverter(field, None) if converter is None: raise ComponentLookupError(u"Cannot URL encode %s of type %s" % ( name, field.__class__,)) encoded_name = name if converter.token: encoded_name = "%s:%s" % (name, converter.token,) value = data[name] if value is None: continue if ISequence.providedBy(field): value_type_converter = IFieldTypeConverter(field.value_type, None) if value_type_converter is None: raise ComponentLookupError(u"Cannot URL encode value type " u"for %s of type %s : %s" % ( name, field.__class__, field.value_type.__class__,)) if value_type_converter.token: encoded_name = "%s:%s:%s" % ( name, value_type_converter.token, converter.token,) for item in value: if isinstance(item, bool): item = item and '1' or '' encode.append((encoded_name, item,)) else: # The :bool converter just does bool() value, but urlencode() does # str() on the object. The result is False => 'False' => True :( if isinstance(value, bool): value = value and '1' or '' encode.append((encoded_name, value)) return urllib.urlencode(encode)
def decode(data, schema, missing=True): """Decode a data dict according to a schema. The returned dictionary will contain only keys matching schema names, and will force type values appropriately. This function is only necessary because the encoders used by encode() are not sufficiently detailed to always return the exact type expected by a field, e.g. resulting in ascii/unicode discrepancies. If missing is True, fields that are in the schema but not in the data will be set to field.missing_value. Otherwise, they are ignored. """ decoded = {} for name, field in getFields(schema).items(): if name not in data: if missing: decoded[name] = field.missing_value continue value = data[name] if value is None: continue field_type = field._type if isinstance(field_type, (tuple, list,)): field_type = field_type[-1] if ISequence.providedBy(field): converted = [] value_type_field_type = field.value_type._type if isinstance(value_type_field_type, (tuple, list,)): value_type_field_type = value_type_field_type[-1] for item in value: if isinstance(item, str): value = unicode(item, 'utf-8') if field.value_type._type and not isinstance( item, field.value_type._type): item = value_type_field_type(item) converted.append(item) value = converted elif isinstance(value, (tuple, list)) and value: value = value[0] if isinstance(value, str): value = unicode(value, 'utf-8') if field._type is not None and not isinstance(value, field._type): value = field_type(value) decoded[name] = value return decoded
def encode(data, schema, ignore=()): """Given a data dictionary with key/value pairs and schema, return an encoded query string. This is similar to urllib.urlencode(), but field names will include the appropriate field type converters, e.g. an int field will be encoded as fieldname:int=123. Fields not found in the data dict will be ignored, and items in the dict not in the schema will also be ignored. Additional fields to ignore can be passed with the 'ignore' parameter. If any fields cannot be converted, a ComponentLookupError will be raised. """ encode = [] for name, field in getFieldsInOrder(schema): if name in ignore or name not in data: continue converter = IFieldTypeConverter(field, None) if converter is None: raise ComponentLookupError(u"Cannot URL encode %s of type %s" % ( name, field.__class__,)) encoded_name = name if converter.token: encoded_name = "%s:%s" % (name, converter.token,) value = data[name] if value is None: continue if ISequence.providedBy(field): value_type_converter = IFieldTypeConverter(field.value_type, None) if value_type_converter is None: raise ComponentLookupError( u"Cannot URL encode value type for %s of type %s : %s" % ( name, field.__class__, field.value_type.__class__,)) if value_type_converter.token: encoded_name = "%s:%s:%s" % ( name, value_type_converter.token, converter.token,) for item in value: if isinstance(item, bool): item = item and '1' or '' encode.append((encoded_name, item,)) else: # The :bool converter just does bool() value, but urlencode() does # str() on the object. The result is False => 'False' => True :( if isinstance(value, bool): value = value and '1' or '' encode.append((encoded_name, value)) return urllib.urlencode(encode)
def _base_args(self): """Method which will calculate _base class arguments. Returns (as python dictionary): - `pattern`: pattern name - `pattern_options`: pattern options - `name`: field name - `value`: field value :returns: Arguments which will be passed to _base :rtype: dict """ args = super(AjaxSelectWidget, self)._base_args() args['name'] = self.name args['value'] = self.value args.setdefault('pattern_options', {}) context = self.context field = None if IChoice.providedBy(self.field): args['pattern_options']['maximumSelectionSize'] = 1 field = self.field elif ICollection.providedBy(self.field): field = self.field.value_type if IChoice.providedBy(field): args['pattern_options']['allowNewItems'] = 'false' args['pattern_options'] = dict_merge(self._ajaxselect_options(), args['pattern_options']) if field and getattr(field, 'vocabulary', None): form_url = self.request.getURL() source_url = '{0:s}/++widget++{1:s}/@@getSource'.format( form_url, self.name, ) args['pattern_options']['vocabularyUrl'] = source_url # ISequence represents an orderable collection if ISequence.providedBy(self.field) or self.orderable: args['pattern_options']['orderable'] = True if self.vocabulary == 'plone.app.vocabularies.Keywords': membership = getToolByName(context, 'portal_membership') user = membership.getAuthenticatedMember() registry = getUtility(IRegistry) roles_allowed_to_add_keywords = registry.get( 'plone.roles_allowed_to_add_keywords', set()) roles = set(user.getRolesInContext(context)) allowNewItems = bool( roles.intersection(roles_allowed_to_add_keywords), ) args['pattern_options']['allowNewItems'] = str( allowNewItems, ).lower() return args
def _base_args(self): """Method which will calculate _base class arguments. Returns (as python dictionary): - `pattern`: pattern name - `pattern_options`: pattern options - `name`: field name - `value`: field value - `multiple`: field multiple - `items`: field items from which we can select to :returns: Arguments which will be passed to _base :rtype: dict """ args = super(SelectWidget, self)._base_args() args['name'] = self.name args['value'] = self.value args['multiple'] = self.multiple self.required = self.field.required options = args.setdefault('pattern_options', {}) if self.multiple or ICollection.providedBy(self.field): options['multiple'] = args['multiple'] = self.multiple = True # ISequence represents an orderable collection if ISequence.providedBy(self.field) or self.orderable: options['orderable'] = True if self.multiple: options['separator'] = self.separator # Allow to clear field value if it is not required if not self.required: options['allowClear'] = True base_items = self.items if callable(base_items): # items used to be a property in all widgets, then in the select # widget it became a method, then in a few others too, but never in # all, so this was reverted to let it be a property again. Let's # support both here to avoid breaking on some z3c.form versions. # See https://github.com/zopefoundation/z3c.form/issues/44 base_items = base_items() items = [] for item in base_items: if not isinstance(item['content'], basestring): item['content'] = translate( item['content'], context=self.request, default=item['value']) items.append((item['value'], item['content'])) args['items'] = items return args
def _base_args(self): """Method which will calculate _base class arguments. Returns (as python dictionary): - `pattern`: pattern name - `pattern_options`: pattern options - `name`: field name - `value`: field value - `multiple`: field multiple - `items`: field items from which we can select to :returns: Arguments which will be passed to _base :rtype: dict """ args = super(SelectWidget, self)._base_args() args['name'] = self.name args['value'] = self.value args['multiple'] = self.multiple self.required = self.field.required options = args.setdefault('pattern_options', {}) if self.multiple or ICollection.providedBy(self.field): options['multiple'] = args['multiple'] = self.multiple = True # ISequence represents an orderable collection if ISequence.providedBy(self.field) or self.orderable: options['orderable'] = True if self.multiple: options['separator'] = self.separator # Allow to clear field value if it is not required if not self.required: options['allowClear'] = True base_items = self.items if callable(base_items): # items used to be a property in all widgets, then in the select # widget it became a method, then in a few others too, but never in # all, so this was reverted to let it be a property again. Let's # support both here to avoid breaking on some z3c.form versions. # See https://github.com/zopefoundation/z3c.form/issues/44 base_items = base_items() items = [] for item in base_items: if not isinstance(item['content'], six.string_types): item['content'] = translate(item['content'], context=self.request, default=item['value']) items.append((item['value'], item['content'])) args['items'] = items return args
def _base_args(self): """Method which will calculate _base class arguments. Returns (as python dictionary): - `pattern`: pattern name - `pattern_options`: pattern options - `name`: field name - `value`: field value :returns: Arguments which will be passed to _base :rtype: dict """ args = super(AjaxSelectWidget, self)._base_args() args["name"] = self.name args["value"] = self.value args.setdefault("pattern_options", {}) field_name = self.field and self.field.__name__ or None context = self.context # We need special handling for AddForms if IAddForm.providedBy(getattr(self, "form")): context = self.form vocabulary_name = self.vocabulary field = None if IChoice.providedBy(self.field): args["pattern_options"]["maximumSelectionSize"] = 1 field = self.field elif ICollection.providedBy(self.field): field = self.field.value_type if not vocabulary_name and field is not None: vocabulary_name = field.vocabularyName args["pattern_options"] = dict_merge( get_ajaxselect_options( context, args["value"], self.separator, vocabulary_name, self.vocabulary_view, field_name ), args["pattern_options"], ) if field and getattr(field, "vocabulary", None): form_url = self.request.getURL() source_url = "%s/++widget++%s/@@getSource" % (form_url, self.name) args["pattern_options"]["vocabularyUrl"] = source_url # ISequence represents an orderable collection if ISequence.providedBy(self.field) or self.orderable: args["pattern_options"]["orderable"] = True return args
def _base_args(self): """Method which will calculate _base class arguments. Returns (as python dictionary): - `pattern`: pattern name - `pattern_options`: pattern options - `name`: field name - `value`: field value :returns: Arguments which will be passed to _base :rtype: dict """ args = super(AjaxSelectWidget, self)._base_args() args['name'] = self.name args['value'] = self.value args.setdefault('pattern_options', {}) field_name = self.field and self.field.__name__ or None context = self.context # We need special handling for AddForms if IAddForm.providedBy(getattr(self, 'form')): context = self.form vocabulary_name = self.vocabulary field = None if IChoice.providedBy(self.field): args['pattern_options']['maximumSelectionSize'] = 1 field = self.field elif ICollection.providedBy(self.field): field = self.field.value_type if not vocabulary_name and field is not None: vocabulary_name = field.vocabularyName args['pattern_options'] = dict_merge( get_ajaxselect_options(context, args['value'], self.separator, vocabulary_name, self.vocabulary_view, field_name), args['pattern_options']) if field and getattr(field, 'vocabulary', None): form_url = self.request.getURL() source_url = "%s/++widget++%s/@@getSource" % (form_url, self.name) args['pattern_options']['vocabularyUrl'] = source_url # ISequence represents an orderable collection if ISequence.providedBy(self.field) or self.orderable: args['pattern_options']['orderable'] = True return args
def _base_args(self): """Method which will calculate _base class arguments. Returns (as python dictionary): - `pattern`: pattern name - `pattern_options`: pattern options - `name`: field name - `value`: field value - `multiple`: field multiple - `items`: field items from which we can select to :returns: Arguments which will be passed to _base :rtype: dict """ args = super(SelectWidget, self)._base_args() args['name'] = self.name args['value'] = self.value args['multiple'] = self.multiple self.required = self.field.required options = args.setdefault('pattern_options', {}) if self.multiple or ICollection.providedBy(self.field): options['multiple'] = args['multiple'] = self.multiple = True # ISequence represents an orderable collection if ISequence.providedBy(self.field) or self.orderable: options['orderable'] = True if self.multiple: options['separator'] = self.separator # Allow to clear field value if it is not required if not self.required: options['allowClear'] = True items = [] for item in self.items(): if not isinstance(item['content'], basestring): item['content'] = translate( item['content'], context=self.request, default=item['value']) items.append((item['value'], item['content'])) args['items'] = items return args
def _base_args(self): """Method which will calculate _base class arguments. Returns (as python dictionary): - `pattern`: pattern name - `pattern_options`: pattern options - `name`: field name - `value`: field value - `multiple`: field multiple - `items`: field items from which we can select to :returns: Arguments which will be passed to _base :rtype: dict """ args = super(SelectWidget, self)._base_args() args["name"] = self.name args["value"] = self.value args["multiple"] = self.multiple items = [] for item in self.items(): if not isinstance(item["content"], basestring): item["content"] = translate(item["content"], context=self.request, default=item["value"]) items.append((item["value"], item["content"])) args["items"] = items options = args.setdefault("pattern_options", {}) if self.multiple or ICollection.providedBy(self.field): options["multiple"] = args["multiple"] = self.multiple = True # ISequence represents an orderable collection if ISequence.providedBy(self.field) or self.orderable: options["orderable"] = True if self.multiple: options["separator"] = self.separator return args
def _base_args(self): """Method which will calculate _base class arguments. Returns (as python dictionary): - `pattern`: pattern name - `pattern_options`: pattern options - `name`: field name - `value`: field value :returns: Arguments which will be passed to _base :rtype: dict """ args = super(AjaxSelectWidget, self)._base_args() args['name'] = self.name args['value'] = self.value args.setdefault('pattern_options', {}) field_name = self.field and self.field.__name__ or None context = self.context # We need special handling for AddForms if IAddForm.providedBy(getattr(self, 'form')): context = self.form args['pattern_options'] = dict_merge( get_ajaxselect_options(context, args['value'], self.separator, self.vocabulary, self.vocabulary_view, field_name), args['pattern_options']) # ISequence represents an orderable collection if ISequence.providedBy(self.field) or self.orderable: args['pattern_options']['orderable'] = True return args
def encode(data, schema, ignore=()): """Given a data dictionary with key/value pairs and schema, return an encoded query string. This is similar to urllib.urlencode(), but field names will include the appropriate field type converters, e.g. an int field will be encoded as fieldname:int=123. Fields not found in the data dict will be ignored, and items in the dict not in the schema will also be ignored. Additional fields to ignore can be passed with the 'ignore' parameter. If any fields cannot be converted, a ComponentLookupError will be raised. """ encode = [] for name, field in getFieldsInOrder(schema): if HAS_RFC822 and IPrimaryField.providedBy(field): continue if name in ignore or name not in data: continue converter = IFieldTypeConverter(field, None) if converter is None: raise ComponentLookupError( u'Cannot URL encode {0} of type {1}'.format( name, field.__class__ ) ) encoded_name = name if converter.token: encoded_name = ':'.join([name, converter.token]) value = data[name] if value is None: continue elif isinstance(value, six.text_type): value = value.encode('utf-8') if ISequence.providedBy(field): value_type_converter = IFieldTypeConverter(field.value_type, None) if value_type_converter is None: raise ComponentLookupError( u'Cannot URL encode value type for {0} of type ' u'{1} : {2}'.format( name, field.__class__, field.value_type.__class__ ) ) if value_type_converter.token: encoded_name = ':'.join([ name, value_type_converter.token, converter.token ]) for item in value: if isinstance(item, bool): item = item and '1' or '' elif isinstance(item, six.text_type): item = item.encode('utf-8') if isinstance(item, dict): encode.extend(map_to_pairs(encoded_name, item)) else: encode.append((encoded_name, item,)) else: # The :bool converter just does bool() value, but urlencode() does # str() on the object. The result is False => 'False' => True :( if isinstance(value, bool): value = value and '1' or '' if isinstance(value, dict): encode.extend(map_to_pairs(encoded_name, value)) else: encode.append((encoded_name, value)) return parse.urlencode(encode)
def testImplements(self): field = List() self.failUnless(IList.providedBy(field)) self.failUnless(ISequence.providedBy(field)) self.failUnless(ICollection.providedBy(field))
def testImplements(self): field = List() self.assertTrue(IList.providedBy(field)) self.assertTrue(ISequence.providedBy(field)) self.assertTrue(ICollection.providedBy(field))
def _base_args(self): """Method which will calculate _base class arguments. Returns (as python dictionary): - `pattern`: pattern name - `pattern_options`: pattern options - `name`: field name - `value`: field value :returns: Arguments which will be passed to _base :rtype: dict """ args = super(AjaxSelectWidget, self)._base_args() args['name'] = self.name args['value'] = self.value args.setdefault('pattern_options', {}) field_name = self.field and self.field.__name__ or None context = self.context view_context = get_widget_form(self) # For EditForms and non-Forms (in tests), the vocabulary is looked # up on the context, otherwise on the view if (IEditForm.providedBy(view_context) or not IForm.providedBy(view_context)): view_context = context vocabulary_name = self.vocabulary field = None if IChoice.providedBy(self.field): args['pattern_options']['maximumSelectionSize'] = 1 field = self.field elif ICollection.providedBy(self.field): field = self.field.value_type if IChoice.providedBy(field): args['pattern_options']['allowNewItems'] = 'false' args['pattern_options'] = dict_merge( get_ajaxselect_options(view_context, args['value'], self.separator, vocabulary_name, self.vocabulary_view, field_name), args['pattern_options']) if field and getattr(field, 'vocabulary', None): form_url = self.request.getURL() source_url = '{0:s}/++widget++{1:s}/@@getSource'.format( form_url, self.name, ) args['pattern_options']['vocabularyUrl'] = source_url # ISequence represents an orderable collection if ISequence.providedBy(self.field) or self.orderable: args['pattern_options']['orderable'] = True if self.vocabulary == 'plone.app.vocabularies.Keywords': membership = getToolByName(context, 'portal_membership') user = membership.getAuthenticatedMember() registry = getUtility(IRegistry) roles_allowed_to_add_keywords = registry.get( 'plone.roles_allowed_to_add_keywords', []) roles = set(user.getRolesInContext(context)) allowNewItems = 'false' if roles.intersection(roles_allowed_to_add_keywords): allowNewItems = 'true' args['pattern_options']['allowNewItems'] = allowNewItems return args
def testImplements(self): field = Tuple() self.failUnless(ITuple.providedBy(field)) self.failUnless(ISequence.providedBy(field)) self.failUnless(ICollection.providedBy(field))
def decode(data, schema, missing=True, primary=False): """Decode a data dict according to a schema. The returned dictionary will contain only keys matching schema names, and will force type values appropriately. This function is only necessary because the encoders used by encode() are not sufficiently detailed to always return the exact type expected by a field, e.g. resulting in ascii/unicode discrepancies. If missing is True, fields that are in the schema but not in the data will be set to field.missing_value. Otherwise, they are ignored. If primary is True, also fields that are marged as primary fields are decoded from the data. (Primary fields are not decoded by default, because primary field are mainly used for rich text or binary fields and data is usually parsed from query string with length limitations.) """ decoded = {} for name, field in getFields(schema).items(): if not primary and HAS_RFC822 and IPrimaryField.providedBy(field): continue if name not in data: if missing: decoded[name] = field.missing_value continue value = data[name] if value is None: continue field_type = field._type if isinstance(field_type, (tuple, list,)): field_type = field_type[-1] if ISequence.providedBy(field): converted = [] value_type_field_type = field.value_type._type if isinstance(value_type_field_type, (tuple, list,)): value_type_field_type = value_type_field_type[-1] for item in value: if field.value_type._type and not isinstance( item, field.value_type._type): item = value_type_field_type(item) converted.append(item) value = converted elif isinstance(value, (tuple, list)) and value: value = value[0] if isinstance(value, six.binary_type): value = value.decode('utf-8') if field._type is not None and not isinstance(value, field._type): value = field_type(value) decoded[name] = value return decoded
def _base_args(self): """Method which will calculate _base class arguments. Returns (as python dictionary): - `pattern`: pattern name - `pattern_options`: pattern options - `name`: field name - `value`: field value :returns: Arguments which will be passed to _base :rtype: dict """ args = super(AjaxSelectWidget, self)._base_args() args['name'] = self.name args['value'] = self.value args.setdefault('pattern_options', {}) field_name = self.field and self.field.__name__ or None context = self.context view_context = get_widget_form(self) # For EditForms and non-Forms (in tests), the vocabulary is looked # up on the context, otherwise on the view if ( IEditForm.providedBy(view_context) or not IForm.providedBy(view_context) ): view_context = context vocabulary_name = self.vocabulary field = None if IChoice.providedBy(self.field): args['pattern_options']['maximumSelectionSize'] = 1 field = self.field elif ICollection.providedBy(self.field): field = self.field.value_type if IChoice.providedBy(field): args['pattern_options']['allowNewItems'] = 'false' args['pattern_options'] = dict_merge( get_ajaxselect_options(view_context, args['value'], self.separator, vocabulary_name, self.vocabulary_view, field_name), args['pattern_options']) if field and getattr(field, 'vocabulary', None): form_url = self.request.getURL() source_url = '{0:s}/++widget++{1:s}/@@getSource'.format( form_url, self.name ) args['pattern_options']['vocabularyUrl'] = source_url # ISequence represents an orderable collection if ISequence.providedBy(self.field) or self.orderable: args['pattern_options']['orderable'] = True if self.vocabulary == 'plone.app.vocabularies.Keywords': membership = getToolByName(context, 'portal_membership') user = membership.getAuthenticatedMember() registry = getUtility(IRegistry) roles_allowed_to_add_keywords = registry.get( 'plone.roles_allowed_to_add_keywords', []) roles = set(user.getRolesInContext(context)) allowNewItems = 'false' if roles.intersection(roles_allowed_to_add_keywords): allowNewItems = 'true' args['pattern_options']['allowNewItems'] = allowNewItems return args