from django import newforms as forms XFN_FAMILY_RELS = ('child', 'kin', 'parent', 'sibling', 'spouse') XFN_FRIENDSHIP_RELS = ('acquaintance', 'contact', 'friend') XFN_GEOGRAPHICAL_RELS = ('co-resident', 'neighbor') XFN_IDENTITY_RELS = ('me',) XFN_PHYSICAL_RELS = ('met',) XFN_PROFESSIONAL_RELS = ('colleague', 'co-worker') XFN_ROMANTIC_RELS = ('crush', 'date', 'muse', 'sweetheart') XFN_RELS = ( XFN_FAMILY_RELS + XFN_FRIENDSHIP_RELS + XFN_GEOGRAPHICAL_RELS + XFN_IDENTITY_RELS + XFN_PHYSICAL_RELS + XFN_PROFESSIONAL_RELS + XFN_ROMANTIC_RELS ) xfn_family_rels = set(XFN_FAMILY_RELS) xfn_friendship_rels = set(XFN_FRIENDSHIP_RELS) xfn_geographical_rels = set(XFN_GEOGRAPHICAL_RELS) xfn_rels = set(XFN_RELS) class XFNField(forms.Field): """Field to normalize and validate an XFN relationship. Strips extra whitespace, converts to lowercase and removes duplicates. Enforces exclusivity for the ``me`` value and allows at most one value from each of the family, friendship and geographical categories. Sorts values alphabetically -- not required, but *feels* right. http://gmpg.org/xfn/11 """ default_error_messages = { 'family': u'XFN allows only one family value.', 'friendship': u'XFN allows only one friendship value.', 'invalid': u'Enter a valid XFN relationship.', 'geographical': u'XFN allows only one geographical value.', 'me': u'The "me" relationship can\'t be used with other values.', 'required': u'Enter an XFN relationship.' } def clean(self, value): value = super(XFNField, self).clean(value) if value in forms.fields.EMPTY_VALUES: return u'' rels = set(value.strip().lower().split()) if len(rels - xfn_rels): raise forms.ValidationError(self.error_messages['invalid']) if len(rels & xfn_family_rels) > 1: raise forms.ValidationError(self.error_messages['family']) if len(rels & xfn_friendship_rels) > 1: raise forms.ValidationError(self.error_messages['friendship']) if len(rels & xfn_geographical_rels) > 1: raise forms.ValidationError(self.error_messages['geographical']) if 'me' in rels and len(rels) > 1: raise forms.ValidationError(self.error_messages['me']) return u' '.join(sorted(rels)) def widget_attrs(self, widget): return {'class': 'xfn'}