mirror of https://github.com/zulip/zulip.git
profile: Add hint attribute to custom profile fields.
This is the model for #8876.
This commit is contained in:
parent
00ffa808da
commit
0420b89468
|
@ -4527,8 +4527,10 @@ def notify_realm_custom_profile_fields(realm: Realm, operation: str) -> None:
|
||||||
fields=[f.as_dict() for f in fields])
|
fields=[f.as_dict() for f in fields])
|
||||||
send_event(event, active_user_ids(realm.id))
|
send_event(event, active_user_ids(realm.id))
|
||||||
|
|
||||||
def try_add_realm_custom_profile_field(realm: Realm, name: Text, field_type: int) -> CustomProfileField:
|
def try_add_realm_custom_profile_field(realm: Realm, name: Text, field_type: int,
|
||||||
|
hint: Text='') -> CustomProfileField:
|
||||||
field = CustomProfileField(realm=realm, name=name, field_type=field_type)
|
field = CustomProfileField(realm=realm, name=name, field_type=field_type)
|
||||||
|
field.hint = hint
|
||||||
field.save()
|
field.save()
|
||||||
notify_realm_custom_profile_fields(realm, 'add')
|
notify_realm_custom_profile_fields(realm, 'add')
|
||||||
return field
|
return field
|
||||||
|
@ -4542,9 +4544,10 @@ def do_remove_realm_custom_profile_field(realm: Realm, field: CustomProfileField
|
||||||
notify_realm_custom_profile_fields(realm, 'delete')
|
notify_realm_custom_profile_fields(realm, 'delete')
|
||||||
|
|
||||||
def try_update_realm_custom_profile_field(realm: Realm, field: CustomProfileField,
|
def try_update_realm_custom_profile_field(realm: Realm, field: CustomProfileField,
|
||||||
name: Text) -> None:
|
name: Text, hint: Text='') -> None:
|
||||||
field.name = name
|
field.name = name
|
||||||
field.save(update_fields=['name'])
|
field.hint = hint
|
||||||
|
field.save(update_fields=['name', 'hint'])
|
||||||
notify_realm_custom_profile_fields(realm, 'update')
|
notify_realm_custom_profile_fields(realm, 'update')
|
||||||
|
|
||||||
def do_update_user_custom_profile_data(user_profile: UserProfile,
|
def do_update_user_custom_profile_data(user_profile: UserProfile,
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.11 on 2018-04-06 04:10
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('zerver', '0155_change_default_realm_description'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customprofilefield',
|
||||||
|
name='hint',
|
||||||
|
field=models.CharField(default='', max_length=80, null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -740,10 +740,10 @@ class UserProfile(AbstractBaseUser, PermissionsMixin):
|
||||||
unique_together = (('realm', 'email'),)
|
unique_together = (('realm', 'email'),)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def profile_data(self) -> List[Dict[str, Union[int, float, Text]]]:
|
def profile_data(self) -> List[Dict[str, Union[int, float, Optional[Text]]]]:
|
||||||
values = CustomProfileFieldValue.objects.filter(user_profile=self)
|
values = CustomProfileFieldValue.objects.filter(user_profile=self)
|
||||||
user_data = {v.field_id: v.value for v in values}
|
user_data = {v.field_id: v.value for v in values}
|
||||||
data = [] # type: List[Dict[str, Union[int, float, Text]]]
|
data = [] # type: List[Dict[str, Union[int, float, Optional[Text]]]]
|
||||||
for field in custom_profile_fields_for_realm(self.realm_id):
|
for field in custom_profile_fields_for_realm(self.realm_id):
|
||||||
value = user_data.get(field.id, None)
|
value = user_data.get(field.id, None)
|
||||||
field_type = field.field_type
|
field_type = field.field_type
|
||||||
|
@ -751,7 +751,7 @@ class UserProfile(AbstractBaseUser, PermissionsMixin):
|
||||||
converter = field.FIELD_CONVERTERS[field_type]
|
converter = field.FIELD_CONVERTERS[field_type]
|
||||||
value = converter(value)
|
value = converter(value)
|
||||||
|
|
||||||
field_data = {} # type: Dict[str, Union[int, float, Text]]
|
field_data = {} # type: Dict[str, Union[int, float, Optional[Text]]]
|
||||||
for k, v in field.as_dict().items():
|
for k, v in field.as_dict().items():
|
||||||
field_data[k] = v
|
field_data[k] = v
|
||||||
field_data['value'] = value
|
field_data['value'] = value
|
||||||
|
@ -1873,8 +1873,11 @@ class UserHotspot(models.Model):
|
||||||
unique_together = ("user", "hotspot")
|
unique_together = ("user", "hotspot")
|
||||||
|
|
||||||
class CustomProfileField(models.Model):
|
class CustomProfileField(models.Model):
|
||||||
|
HINT_MAX_LENGTH = 80
|
||||||
|
|
||||||
realm = models.ForeignKey(Realm, on_delete=CASCADE) # type: Realm
|
realm = models.ForeignKey(Realm, on_delete=CASCADE) # type: Realm
|
||||||
name = models.CharField(max_length=100) # type: Text
|
name = models.CharField(max_length=100) # type: Text
|
||||||
|
hint = models.CharField(max_length=HINT_MAX_LENGTH, default='', null=True) # type: Optional[Text]
|
||||||
|
|
||||||
SHORT_TEXT = 1
|
SHORT_TEXT = 1
|
||||||
LONG_TEXT = 2
|
LONG_TEXT = 2
|
||||||
|
@ -1895,11 +1898,12 @@ class CustomProfileField(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('realm', 'name')
|
unique_together = ('realm', 'name')
|
||||||
|
|
||||||
def as_dict(self) -> Dict[str, Union[int, Text]]:
|
def as_dict(self) -> Dict[str, Union[int, Optional[Text]]]:
|
||||||
return {
|
return {
|
||||||
'id': self.id,
|
'id': self.id,
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
'type': self.field_type,
|
'type': self.field_type,
|
||||||
|
'hint': self.hint,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
|
|
|
@ -37,6 +37,14 @@ class CustomProfileFieldTest(ZulipTestCase):
|
||||||
self.assert_json_error(result, u'Invalid field type.')
|
self.assert_json_error(result, u'Invalid field type.')
|
||||||
|
|
||||||
data["name"] = "Phone"
|
data["name"] = "Phone"
|
||||||
|
data["hint"] = "*" * 81
|
||||||
|
data["field_type"] = CustomProfileField.SHORT_TEXT
|
||||||
|
result = self.client_post("/json/realm/profile_fields", info=data)
|
||||||
|
msg = "hint is longer than 80."
|
||||||
|
self.assert_json_error(result, msg)
|
||||||
|
|
||||||
|
data["name"] = "Phone"
|
||||||
|
data["hint"] = "Contact number"
|
||||||
data["field_type"] = CustomProfileField.SHORT_TEXT
|
data["field_type"] = CustomProfileField.SHORT_TEXT
|
||||||
result = self.client_post("/json/realm/profile_fields", info=data)
|
result = self.client_post("/json/realm/profile_fields", info=data)
|
||||||
self.assert_json_success(result)
|
self.assert_json_success(result)
|
||||||
|
@ -90,10 +98,31 @@ class CustomProfileFieldTest(ZulipTestCase):
|
||||||
info={'name': 'New phone number',
|
info={'name': 'New phone number',
|
||||||
'field_type': CustomProfileField.SHORT_TEXT})
|
'field_type': CustomProfileField.SHORT_TEXT})
|
||||||
self.assert_json_success(result)
|
self.assert_json_success(result)
|
||||||
|
field = CustomProfileField.objects.get(id=field.id, realm=realm)
|
||||||
|
self.assertEqual(CustomProfileField.objects.count(), 3)
|
||||||
|
self.assertEqual(field.name, 'New phone number')
|
||||||
|
self.assertIs(field.hint, '')
|
||||||
|
self.assertEqual(field.field_type, CustomProfileField.SHORT_TEXT)
|
||||||
|
|
||||||
|
result = self.client_patch(
|
||||||
|
"/json/realm/profile_fields/{}".format(field.id),
|
||||||
|
info={'name': 'New phone number',
|
||||||
|
'hint': '*' * 81,
|
||||||
|
'field_type': CustomProfileField.SHORT_TEXT})
|
||||||
|
msg = "hint is longer than 80."
|
||||||
|
self.assert_json_error(result, msg)
|
||||||
|
|
||||||
|
result = self.client_patch(
|
||||||
|
"/json/realm/profile_fields/{}".format(field.id),
|
||||||
|
info={'name': 'New phone number',
|
||||||
|
'hint': 'New contact number',
|
||||||
|
'field_type': CustomProfileField.SHORT_TEXT})
|
||||||
|
self.assert_json_success(result)
|
||||||
|
|
||||||
field = CustomProfileField.objects.get(id=field.id, realm=realm)
|
field = CustomProfileField.objects.get(id=field.id, realm=realm)
|
||||||
self.assertEqual(CustomProfileField.objects.count(), 3)
|
self.assertEqual(CustomProfileField.objects.count(), 3)
|
||||||
self.assertEqual(field.name, 'New phone number')
|
self.assertEqual(field.name, 'New phone number')
|
||||||
|
self.assertEqual(field.hint, 'New contact number')
|
||||||
self.assertEqual(field.field_type, CustomProfileField.SHORT_TEXT)
|
self.assertEqual(field.field_type, CustomProfileField.SHORT_TEXT)
|
||||||
|
|
||||||
def test_update_is_aware_of_uniqueness(self) -> None:
|
def test_update_is_aware_of_uniqueness(self) -> None:
|
||||||
|
|
|
@ -18,6 +18,7 @@ from zerver.models import (
|
||||||
)
|
)
|
||||||
|
|
||||||
from zerver.lib.actions import (
|
from zerver.lib.actions import (
|
||||||
|
try_update_realm_custom_profile_field,
|
||||||
bulk_add_subscriptions,
|
bulk_add_subscriptions,
|
||||||
bulk_remove_subscriptions,
|
bulk_remove_subscriptions,
|
||||||
check_add_realm_emoji,
|
check_add_realm_emoji,
|
||||||
|
@ -915,6 +916,7 @@ class EventsRegisterTest(ZulipTestCase):
|
||||||
('id', check_int),
|
('id', check_int),
|
||||||
('type', check_int),
|
('type', check_int),
|
||||||
('name', check_string),
|
('name', check_string),
|
||||||
|
('hint', check_string),
|
||||||
]))),
|
]))),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -926,6 +928,20 @@ class EventsRegisterTest(ZulipTestCase):
|
||||||
error = schema_checker('events[0]', events[0])
|
error = schema_checker('events[0]', events[0])
|
||||||
self.assert_on_error(error)
|
self.assert_on_error(error)
|
||||||
|
|
||||||
|
realm = self.user_profile.realm
|
||||||
|
field = realm.customprofilefield_set.get(realm=realm, name='Biography')
|
||||||
|
name = field.name
|
||||||
|
hint = 'Biography of the user'
|
||||||
|
try_update_realm_custom_profile_field(realm, field, name, hint=hint)
|
||||||
|
|
||||||
|
events = self.do_test(
|
||||||
|
lambda: notify_realm_custom_profile_fields(
|
||||||
|
self.user_profile.realm, 'add'),
|
||||||
|
state_change_expected=False,
|
||||||
|
)
|
||||||
|
error = schema_checker('events[0]', events[0])
|
||||||
|
self.assert_on_error(error)
|
||||||
|
|
||||||
def test_presence_events(self) -> None:
|
def test_presence_events(self) -> None:
|
||||||
schema_checker = self.check_events_dict([
|
schema_checker = self.check_events_dict([
|
||||||
('type', equals('presence')),
|
('type', equals('presence')),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
from typing import Text, Union, List, Dict
|
from typing import Text, Union, List, Dict, Optional
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
@ -14,7 +14,7 @@ from zerver.lib.actions import (try_add_realm_custom_profile_field,
|
||||||
try_update_realm_custom_profile_field,
|
try_update_realm_custom_profile_field,
|
||||||
do_update_user_custom_profile_data)
|
do_update_user_custom_profile_data)
|
||||||
from zerver.lib.response import json_success, json_error
|
from zerver.lib.response import json_success, json_error
|
||||||
from zerver.lib.validator import check_dict, check_list, check_int
|
from zerver.lib.validator import check_dict, check_list, check_int, check_capped_string
|
||||||
|
|
||||||
from zerver.models import (custom_profile_fields_for_realm, UserProfile,
|
from zerver.models import (custom_profile_fields_for_realm, UserProfile,
|
||||||
CustomProfileField, custom_profile_fields_for_realm)
|
CustomProfileField, custom_profile_fields_for_realm)
|
||||||
|
@ -23,14 +23,21 @@ def list_realm_custom_profile_fields(request: HttpRequest, user_profile: UserPro
|
||||||
fields = custom_profile_fields_for_realm(user_profile.realm_id)
|
fields = custom_profile_fields_for_realm(user_profile.realm_id)
|
||||||
return json_success({'custom_fields': [f.as_dict() for f in fields]})
|
return json_success({'custom_fields': [f.as_dict() for f in fields]})
|
||||||
|
|
||||||
|
hint_validator = check_capped_string(CustomProfileField.HINT_MAX_LENGTH)
|
||||||
|
|
||||||
@require_realm_admin
|
@require_realm_admin
|
||||||
@has_request_variables
|
@has_request_variables
|
||||||
def create_realm_custom_profile_field(request: HttpRequest,
|
def create_realm_custom_profile_field(request: HttpRequest,
|
||||||
user_profile: UserProfile, name: Text=REQ(),
|
user_profile: UserProfile, name: Text=REQ(),
|
||||||
|
hint: Text=REQ(default=''),
|
||||||
field_type: int=REQ(validator=check_int)) -> HttpResponse:
|
field_type: int=REQ(validator=check_int)) -> HttpResponse:
|
||||||
if not name.strip():
|
if not name.strip():
|
||||||
return json_error(_("Name cannot be blank."))
|
return json_error(_("Name cannot be blank."))
|
||||||
|
|
||||||
|
error = hint_validator('hint', hint)
|
||||||
|
if error:
|
||||||
|
return json_error(error)
|
||||||
|
|
||||||
if field_type not in CustomProfileField.FIELD_VALIDATORS:
|
if field_type not in CustomProfileField.FIELD_VALIDATORS:
|
||||||
return json_error(_("Invalid field type."))
|
return json_error(_("Invalid field type."))
|
||||||
|
|
||||||
|
@ -39,6 +46,7 @@ def create_realm_custom_profile_field(request: HttpRequest,
|
||||||
realm=user_profile.realm,
|
realm=user_profile.realm,
|
||||||
name=name,
|
name=name,
|
||||||
field_type=field_type,
|
field_type=field_type,
|
||||||
|
hint=hint,
|
||||||
)
|
)
|
||||||
return json_success({'id': field.id})
|
return json_success({'id': field.id})
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
|
@ -59,10 +67,16 @@ def delete_realm_custom_profile_field(request: HttpRequest, user_profile: UserPr
|
||||||
@require_realm_admin
|
@require_realm_admin
|
||||||
@has_request_variables
|
@has_request_variables
|
||||||
def update_realm_custom_profile_field(request: HttpRequest, user_profile: UserProfile,
|
def update_realm_custom_profile_field(request: HttpRequest, user_profile: UserProfile,
|
||||||
field_id: int, name: Text=REQ()) -> HttpResponse:
|
field_id: int, name: Text=REQ(),
|
||||||
|
hint: Text=REQ(default='')
|
||||||
|
) -> HttpResponse:
|
||||||
if not name.strip():
|
if not name.strip():
|
||||||
return json_error(_("Name cannot be blank."))
|
return json_error(_("Name cannot be blank."))
|
||||||
|
|
||||||
|
error = hint_validator('hint', hint)
|
||||||
|
if error:
|
||||||
|
return json_error(error, data={'field': 'hint'})
|
||||||
|
|
||||||
realm = user_profile.realm
|
realm = user_profile.realm
|
||||||
try:
|
try:
|
||||||
field = CustomProfileField.objects.get(realm=realm, id=field_id)
|
field = CustomProfileField.objects.get(realm=realm, id=field_id)
|
||||||
|
@ -70,7 +84,7 @@ def update_realm_custom_profile_field(request: HttpRequest, user_profile: UserPr
|
||||||
return json_error(_('Field id {id} not found.').format(id=field_id))
|
return json_error(_('Field id {id} not found.').format(id=field_id))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
try_update_realm_custom_profile_field(realm, field, name)
|
try_update_realm_custom_profile_field(realm, field, name, hint=hint)
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
return json_error(_('A field with that name already exists.'))
|
return json_error(_('A field with that name already exists.'))
|
||||||
return json_success()
|
return json_success()
|
||||||
|
|
|
@ -253,11 +253,14 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
# Create custom profile field data
|
# Create custom profile field data
|
||||||
phone_number = try_add_realm_custom_profile_field(zulip_realm, "Phone number",
|
phone_number = try_add_realm_custom_profile_field(zulip_realm, "Phone number",
|
||||||
CustomProfileField.SHORT_TEXT)
|
CustomProfileField.SHORT_TEXT,
|
||||||
|
hint='')
|
||||||
biography = try_add_realm_custom_profile_field(zulip_realm, "Biography",
|
biography = try_add_realm_custom_profile_field(zulip_realm, "Biography",
|
||||||
CustomProfileField.LONG_TEXT)
|
CustomProfileField.LONG_TEXT,
|
||||||
|
hint='What are you known for?')
|
||||||
favorite_food = try_add_realm_custom_profile_field(zulip_realm, "Favorite food",
|
favorite_food = try_add_realm_custom_profile_field(zulip_realm, "Favorite food",
|
||||||
CustomProfileField.SHORT_TEXT)
|
CustomProfileField.SHORT_TEXT,
|
||||||
|
hint="Or drink, if you'd prefer")
|
||||||
|
|
||||||
# Fill in values for Iago and Hamlet
|
# Fill in values for Iago and Hamlet
|
||||||
hamlet = get_user("hamlet@zulip.com", zulip_realm)
|
hamlet = get_user("hamlet@zulip.com", zulip_realm)
|
||||||
|
|
Loading…
Reference in New Issue