|
@@ -201,57 +201,197 @@ Very much alike the `get_form_field` documented ealier, because this method is o
|
|
|
|
|
|
This method is called to obtain the JSON describing field input to create in end-user facing forms.
|
|
|
|
|
|
-It supports either of those values:
|
|
|
+It supports either of those inputs:
|
|
|
|
|
|
```python
|
|
|
-# this field will have text input to edit it
|
|
|
-def get_input_json(self, request, user):
|
|
|
- return {
|
|
|
- 'type': 'text',
|
|
|
- }
|
|
|
-
|
|
|
-# this field will have textarea to edit it
|
|
|
-def get_input_json(self, request, user):
|
|
|
- return {
|
|
|
- 'type': 'textarea',
|
|
|
- }
|
|
|
-
|
|
|
-# this field will have select to edit it
|
|
|
-def get_input_json(self, request, user):
|
|
|
- choices = []
|
|
|
- for key, choice in self.get_choices():
|
|
|
- choices.append({
|
|
|
- 'value': key,
|
|
|
- 'label': choice,
|
|
|
- })
|
|
|
+# this field will use text input
|
|
|
+class FullNameField(basefields.TextProfileField):
|
|
|
+ fieldname = 'fullname'
|
|
|
+ label = _("Full name")
|
|
|
+
|
|
|
+ def get_input_json(self, request, user):
|
|
|
+ return {
|
|
|
+ 'type': 'text',
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+# this field will use textarea
|
|
|
+class BioField(basefields.UrlifiedTextareaProfileField):
|
|
|
+ fieldname = 'bio'
|
|
|
+ label = _("Bio")
|
|
|
+
|
|
|
+ def get_input_json(self, request, user):
|
|
|
+ return {
|
|
|
+ 'type': 'textarea',
|
|
|
+ }
|
|
|
|
|
|
- return {
|
|
|
- 'type': 'select',
|
|
|
- 'choices': [
|
|
|
- {
|
|
|
- 'value': '',
|
|
|
- 'label': _("Not specified"),
|
|
|
- },
|
|
|
- {
|
|
|
- 'value': 'female',
|
|
|
- 'label': _("Female"),
|
|
|
- },
|
|
|
- {
|
|
|
- 'value': 'male',
|
|
|
- 'label': _("Male"),
|
|
|
- },
|
|
|
- ],
|
|
|
- }
|
|
|
+
|
|
|
+# this field will use select input
|
|
|
+class GenderField(basefields.ChoiceProfileField):
|
|
|
+
|
|
|
+ # ...
|
|
|
+
|
|
|
+ def get_input_json(self, request, user):
|
|
|
+ choices = []
|
|
|
+ for key, choice in self.get_choices():
|
|
|
+ choices.append({
|
|
|
+ 'value': key,
|
|
|
+ 'label': choice,
|
|
|
+ })
|
|
|
+
|
|
|
+ return {
|
|
|
+ 'type': 'select',
|
|
|
+ 'choices': [
|
|
|
+ {
|
|
|
+ 'value': '',
|
|
|
+ 'label': _("Not specified"),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 'value': 'female',
|
|
|
+ 'label': _("Female"),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 'value': 'male',
|
|
|
+ 'label': _("Male"),
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ }
|
|
|
```
|
|
|
|
|
|
Misago comes with convenience base classes for popular input types like text, textarea or select, that are documented further into this document.
|
|
|
|
|
|
|
|
|
### `clean`
|
|
|
+
|
|
|
+In addition to cleaning and validation logic implemented by the form field returned in `get_form_field`, you may include custom cleaning and validation logic on your field that gets run as part of form's clean and validate process.
|
|
|
+
|
|
|
+Below example shows field that performs additional validation and cleanup on user-entered twitter handle:
|
|
|
+
|
|
|
+```python
|
|
|
+class TwitterHandleField(basefields.TextProfileField):
|
|
|
+ fieldname = 'twitter'
|
|
|
+ label = _("Twitter handle")
|
|
|
+
|
|
|
+ def clean(self, request, user, data):
|
|
|
+ data = data.lstrip('@')
|
|
|
+ if data and not re.search('^[A-Za-z0-9_]+$', data):
|
|
|
+ raise ValidationError(ugettext("This is not a valid twitter handle."))
|
|
|
+ return data
|
|
|
+```
|
|
|
+
|
|
|
+Field's `clean` method is called only when user filled it in edit form. It should return cleaned value, or raise ValidationError on any errors. Those errors will be associated with and displayed by their fields in form.
|
|
|
+
|
|
|
+
|
|
|
### `get_display_data`
|
|
|
+
|
|
|
+Just like the `get_input_json` returns JSON describing how form field should be displayed in user-facing edit interface, the `get_display_data` should return JSON describing how its value should be displayed on user's "details" tab in profile. This field is also well covered by the default implementation provided by the `ProfileField` class, which does following:
|
|
|
+
|
|
|
+1. If user's profile doesn't have value for field that we are trying to display, and field is not always readonly, return `None`, telling interface to don't display it.
|
|
|
+2. Call the `get_value_display_data` method to obtain JSON describing how field's value should be displayed on the page. If this method returns `None`, don't display field on user's profile.
|
|
|
+3. Add field's name and label to returned JSON.
|
|
|
+
|
|
|
+```python
|
|
|
+class ProfileField(object):
|
|
|
+
|
|
|
+ # ...
|
|
|
+
|
|
|
+ def get_display_data(self, request, user):
|
|
|
+ value = user.profile_fields.get(self.fieldname, '')
|
|
|
+ if not self.readonly and not len(value):
|
|
|
+ return None
|
|
|
+
|
|
|
+ data = self.get_value_display_data(request, user, value)
|
|
|
+ if not data:
|
|
|
+ return None
|
|
|
+
|
|
|
+ data.update({
|
|
|
+ 'fieldname': self.fieldname,
|
|
|
+ 'name': text_type(self.get_label(user)),
|
|
|
+ })
|
|
|
+
|
|
|
+ return data
|
|
|
+```
|
|
|
+
|
|
|
+Because of such generic implementation, you'll likely limit yourself to customizing the `get_value_display_data` when writing your fields, and only ever use this method if you want to customize displayed label.
|
|
|
+
|
|
|
+
|
|
|
### `get_value_display_data`
|
|
|
+
|
|
|
+This method returns JSON describing how field's value should be displayed on user's profile page. Misago supports quite a few display methods for profile fields:
|
|
|
+
|
|
|
+```python
|
|
|
+# Display value in paragraph
|
|
|
+class FullNameField(basefields.TextProfileField):
|
|
|
+
|
|
|
+ # ...
|
|
|
+
|
|
|
+ def get_value_display_data(self, request, user, value):
|
|
|
+ return {
|
|
|
+ 'type': 'text',
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+# Display value as html
|
|
|
+class BioField(basefields.TextareaProfileField):
|
|
|
+
|
|
|
+ # ...
|
|
|
+
|
|
|
+ def get_value_display_data(self, request, user, value):
|
|
|
+ return {
|
|
|
+ 'html': html.linebreaks(html.escape(value)),
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+# Display value as link
|
|
|
+class WebsiteField(basefields.UrlProfileField):
|
|
|
+
|
|
|
+ # ...
|
|
|
+
|
|
|
+ def get_value_display_data(self, request, user, value):
|
|
|
+ return {
|
|
|
+ 'url': value,
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+# Display value as link with text label
|
|
|
+class TwitterHandleField(basefields.UrlProfileField):
|
|
|
+
|
|
|
+ # ...
|
|
|
+
|
|
|
+ def get_value_display_data(self, request, user, value):
|
|
|
+ return {
|
|
|
+ 'text': '@{}'.format(data),
|
|
|
+ 'url': 'https://twitter.com/{}'.format(data),
|
|
|
+ }
|
|
|
+```
|
|
|
+
|
|
|
+In default implementation provided by the `ProfileField`, this field is called by the `get_display_data`. As such its prefferable in most cases for field to provide custom `get_value_display_data` instead of `get_display_data`.
|
|
|
+
|
|
|
+As documented in the `get_display_data`, returning `None` from this methid will prevent this field from beind displayed on user's profile.
|
|
|
+
|
|
|
+
|
|
|
### `search_users`
|
|
|
|
|
|
+This method is called by search function available on users lists in admin control panel, to search users that have profile field with specified value. Its called with single argument, `criteria`, that contains search string entered in search form, and should return `None` or `Q` object that lets Misago search field's value.
|
|
|
+
|
|
|
+The `ProfileField` class implements generic `search_users` implementation that assumes that field's value is text and performs *case sensitive* search on users `profile_fields` value as well as excludes readonly fields from search:
|
|
|
+
|
|
|
+```python
|
|
|
+class ProfileField(object):
|
|
|
+
|
|
|
+ # ...
|
|
|
+
|
|
|
+ def search_users(self, criteria):
|
|
|
+ if self.readonly:
|
|
|
+ return None
|
|
|
+
|
|
|
+ return Q(**{
|
|
|
+ 'profile_fields__{}__contains'.format(self.fieldname): criteria
|
|
|
+ })
|
|
|
+```
|
|
|
+
|
|
|
+In addition to this `ChoiceProfileField` base field provides custom implementation that searches users by label and value of their choice.
|
|
|
+
|
|
|
|
|
|
## Base fields
|
|
|
|