custom fields: Add default external account custom fields.

This commit is contained in:
Yashashvi Dave 2019-08-24 17:22:25 +05:30 committed by Tim Abbott
parent e8ee71e9a2
commit e05429b6a9
5 changed files with 115 additions and 23 deletions

View File

@ -40,6 +40,7 @@ from zerver.lib.emoji import emoji_name_to_emoji_code, get_emoji_file_name
from zerver.lib.exceptions import StreamDoesNotExistError, \
StreamWithIDDoesNotExistError
from zerver.lib.export import get_realm_exports_serialized
from zerver.lib.external_accounts import DEFAULT_EXTERNAL_ACCOUNTS
from zerver.lib.hotspots import get_next_hotspots
from zerver.lib.message import (
access_message,
@ -5376,6 +5377,19 @@ def notify_realm_custom_profile_fields(realm: Realm, operation: str) -> None:
fields=[f.as_dict() for f in fields])
send_event(realm, event, active_user_ids(realm.id))
def try_add_realm_default_custom_profile_field(realm: Realm,
field_subtype: str) -> CustomProfileField:
field_data = DEFAULT_EXTERNAL_ACCOUNTS[field_subtype]
field = CustomProfileField(realm=realm, name=field_data['name'],
field_type=CustomProfileField.EXTERNAL_ACCOUNT,
hint=field_data['hint'],
field_data=ujson.dumps(dict(subtype=field_subtype)))
field.save()
field.order = field.id
field.save(update_fields=['order'])
notify_realm_custom_profile_fields(realm, 'add')
return field
def try_add_realm_custom_profile_field(realm: Realm, name: str, field_type: int,
hint: str='',
field_data: Optional[ProfileFieldData]=None) -> CustomProfileField:

View File

@ -8,14 +8,26 @@ from zerver.lib.validator import check_required_string, \
check_external_account_url_pattern, check_dict_only
from zerver.lib.types import ProfileFieldData
# Default external account fields are by default avaliable
# to realm admins, where realm admin only need to select
# the default field and other values(i.e. name, url) will be
# fetch from this dictionary.
# text: Field text for admins - custom profile field in org settngs view
# name: Field label or name - user profile in user settings view
# hint: Field hint for realm users
# url_patter: Field url linkifier
DEFAULT_EXTERNAL_ACCOUNTS = {
"twitter": {
"text": "Twitter",
"url_pattern": "https://twitter.com/%(username)s"
"url_pattern": "https://twitter.com/%(username)s",
"name": "Twitter",
"hint": "Enter your Twitter username",
},
"github": {
"text": 'GitHub',
"url_pattern": "https://github.com/%(username)s"
"url_pattern": "https://github.com/%(username)s",
"name": "GitHub",
"hint": "Enter your GitHub username",
},
}

View File

@ -9,6 +9,7 @@ from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.bugdown import convert as bugdown_convert
from zerver.models import CustomProfileField, \
custom_profile_fields_for_realm, CustomProfileFieldValue, get_realm
from zerver.lib.external_accounts import DEFAULT_EXTERNAL_ACCOUNTS
import ujson
class CustomProfileFieldTestCase(ZulipTestCase):
@ -120,6 +121,53 @@ class CreateCustomProfileFieldTest(CustomProfileFieldTestCase):
result = self.client_post("/json/realm/profile_fields", info=data)
self.assert_json_success(result)
def test_create_default_external_account_field(self) -> None:
self.login(self.example_email("iago"))
realm = get_realm("zulip")
field_type = CustomProfileField.EXTERNAL_ACCOUNT # type: int
field_data = ujson.dumps({
'subtype': 'twitter'
}) # type: str
invalid_field_name = "Not required field name" # type: str
invalid_field_hint = "Not required field hint" # type: str
result = self.client_post("/json/realm/profile_fields",
info=dict(
field_type=field_type,
field_data=field_data,
hint=invalid_field_hint,
name=invalid_field_name,
))
self.assert_json_success(result)
# Sliently overwrite name and hint with values set in default fields dict
# for default custom external account fields.
with self.assertRaises(CustomProfileField.DoesNotExist):
field = CustomProfileField.objects.get(name=invalid_field_name, realm=realm)
# The field is created with 'Twitter' name as per values in default fields dict
field = CustomProfileField.objects.get(name='Twitter')
self.assertEqual(field.name, DEFAULT_EXTERNAL_ACCOUNTS['twitter']['name'])
self.assertEqual(field.hint, DEFAULT_EXTERNAL_ACCOUNTS['twitter']['hint'])
result = self.client_delete("/json/realm/profile_fields/{}".format(field.id))
self.assert_json_success(result)
# Should also work without name or hint and only external field type and subtype data
result = self.client_post("/json/realm/profile_fields",
info=dict(field_type=field_type, field_data=field_data))
self.assert_json_success(result)
# Default external account field data cannot be updated
field = CustomProfileField.objects.get(name="Twitter", realm=realm)
result = self.client_patch(
"/json/realm/profile_fields/{}".format(field.id),
info={'name': 'Twitter username',
'field_type': CustomProfileField.EXTERNAL_ACCOUNT}
)
self.assert_json_error(result, 'Default custom field cannot be updated.')
result = self.client_delete("/json/realm/profile_fields/{}".format(field.id))
self.assert_json_success(result)
def test_create_external_account_field(self) -> None:
self.login(self.example_email("iago"))
realm = get_realm('zulip')
@ -297,7 +345,6 @@ class UpdateCustomProfileFieldTest(CustomProfileFieldTestCase):
def test_update(self) -> None:
self.login(self.example_email("iago"))
realm = get_realm('zulip')
result = self.client_patch(
"/json/realm/profile_fields/100",
info={'name': 'Phone Number',

View File

@ -12,6 +12,7 @@ from zerver.lib.actions import (try_add_realm_custom_profile_field,
try_update_realm_custom_profile_field,
do_update_user_custom_profile_data,
try_reorder_realm_custom_profile_fields,
try_add_realm_default_custom_profile_field,
check_remove_custom_profile_field_value)
from zerver.lib.response import json_success, json_error
from zerver.lib.types import ProfileFieldData
@ -57,13 +58,24 @@ def validate_custom_field_data(field_type: int,
if error:
raise JsonableError(error)
def is_default_external_field(field_type: int, field_data: ProfileFieldData) -> bool:
if field_type != CustomProfileField.EXTERNAL_ACCOUNT:
return False
if field_data['subtype'] == 'custom':
return False
return True
def validate_custom_profile_field(name: str, hint: str, field_type: int,
field_data: ProfileFieldData) -> None:
# Validate field data
validate_custom_field_data(field_type, field_data)
# Validate field name, hint and type
validate_field_name_and_hint(name, hint)
if not is_default_external_field(field_type, field_data):
# If field is default external field then we will fetch all data
# from our default field dictionary, so no need to validate name or hint
# Validate field name, hint if not default external account field
validate_field_name_and_hint(name, hint)
field_types = [i[0] for i in CustomProfileField.FIELD_TYPE_CHOICES]
if field_type not in field_types:
raise JsonableError(_("Invalid field type."))
@ -72,22 +84,30 @@ def validate_custom_profile_field(name: str, hint: str, field_type: int,
@has_request_variables
def create_realm_custom_profile_field(request: HttpRequest,
user_profile: UserProfile,
name: str=REQ(),
name: str=REQ(default=''),
hint: str=REQ(default=''),
field_data: ProfileFieldData=REQ(default={},
converter=ujson.loads),
field_type: int=REQ(validator=check_int)) -> HttpResponse:
validate_custom_profile_field(name, hint, field_type, field_data)
try:
field = try_add_realm_custom_profile_field(
realm=user_profile.realm,
name=name,
field_data=field_data,
field_type=field_type,
hint=hint,
)
return json_success({'id': field.id})
if is_default_external_field(field_type, field_data):
field_subtype = '' # type: str
field_subtype = field_data['subtype'] # type: ignore # key for "Union[Dict[str, str], str]" can be str
field = try_add_realm_default_custom_profile_field(
realm=user_profile.realm,
field_subtype=field_subtype,
)
return json_success({'id': field.id})
else:
field = try_add_realm_custom_profile_field(
realm=user_profile.realm,
name=name,
field_data=field_data,
field_type=field_type,
hint=hint,
)
return json_success({'id': field.id})
except IntegrityError:
return json_error(_("A field with that label already exists."))
@ -107,7 +127,7 @@ def delete_realm_custom_profile_field(request: HttpRequest, user_profile: UserPr
@has_request_variables
def update_realm_custom_profile_field(request: HttpRequest, user_profile: UserProfile,
field_id: int,
name: str=REQ(),
name: str=REQ(default=''),
hint: str=REQ(default=''),
field_data: ProfileFieldData=REQ(default={},
converter=ujson.loads),
@ -118,6 +138,10 @@ def update_realm_custom_profile_field(request: HttpRequest, user_profile: UserPr
except CustomProfileField.DoesNotExist:
return json_error(_('Field id {id} not found.').format(id=field_id))
if field.field_type == CustomProfileField.EXTERNAL_ACCOUNT:
if is_default_external_field(field.field_type, ujson.loads(field.field_data)):
return json_error(_("Default custom field cannot be updated."))
validate_custom_profile_field(name, hint, field.field_type, field_data)
try:
try_update_realm_custom_profile_field(realm, field, name, hint=hint,

View File

@ -15,7 +15,7 @@ from django.utils.timezone import timedelta as timezone_timedelta
from zerver.lib.actions import STREAM_ASSIGNMENT_COLORS, check_add_realm_emoji, \
do_change_is_admin, do_send_messages, do_update_user_custom_profile_data, \
try_add_realm_custom_profile_field
try_add_realm_custom_profile_field, try_add_realm_default_custom_profile_field
from zerver.lib.bulk_create import bulk_create_streams, bulk_create_users
from zerver.lib.cache import cache_set
from zerver.lib.generate_test_data import create_test_data
@ -360,12 +360,7 @@ class Command(BaseCommand):
hint="Or your personal blog's URL")
mentor = try_add_realm_custom_profile_field(zulip_realm, "Mentor",
CustomProfileField.USER)
external_account_field_data = {
'subtype': 'github'
} # type: ProfileFieldData
github_profile = try_add_realm_custom_profile_field(zulip_realm, "GitHub",
CustomProfileField.EXTERNAL_ACCOUNT,
field_data=external_account_field_data)
github_profile = try_add_realm_default_custom_profile_field(zulip_realm, "github")
# Fill in values for Iago and Hamlet
hamlet = get_user("hamlet@zulip.com", zulip_realm)