mirror of https://github.com/zulip/zulip.git
custom fields: Add default external account custom fields.
This commit is contained in:
parent
e8ee71e9a2
commit
e05429b6a9
|
@ -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:
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue