We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Save failed: Only 2 custom profile fields can be displayed in the profile summary.
I tried to set a custom profile field to be displayed in the profile summary. However, it fails with an error message "Save failed: Only 2 custom profile fields can be displayed in the profile summary." even though no custom profile field has been set to displayed.

https://user-images.githubusercontent.com/2891235/198561250-59519f45-85a4-4490-8f72-9fae343930be.mov


Server: hosted Zulip server
```console
Zulip Server
Version 6.0-dev-2021-gd461383c1f 
Forked from upstream at 6.0-dev-2009-g0affc7ac6f
```

Client: Zulip Desktop v5.9.3

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `zerver/views/custom_profile_fields.py`
Content:
```
1 from typing import List, Optional, cast
2 
3 import orjson
4 from django.core.exceptions import ValidationError
5 from django.db import IntegrityError
6 from django.http import HttpRequest, HttpResponse
7 from django.utils.translation import gettext as _
8 
9 from zerver.actions.custom_profile_fields import (
10     check_remove_custom_profile_field_value,
11     do_remove_realm_custom_profile_field,
12     do_update_user_custom_profile_data_if_changed,
13     try_add_realm_custom_profile_field,
14     try_add_realm_default_custom_profile_field,
15     try_reorder_realm_custom_profile_fields,
16     try_update_realm_custom_profile_field,
17 )
18 from zerver.decorator import human_users_only, require_realm_admin
19 from zerver.lib.exceptions import JsonableError
20 from zerver.lib.external_accounts import validate_external_account_field_data
21 from zerver.lib.request import REQ, has_request_variables
22 from zerver.lib.response import json_success
23 from zerver.lib.types import ProfileDataElementUpdateDict, ProfileFieldData, Validator
24 from zerver.lib.users import validate_user_custom_profile_data
25 from zerver.lib.validator import (
26     check_bool,
27     check_capped_string,
28     check_dict,
29     check_dict_only,
30     check_int,
31     check_list,
32     check_string,
33     check_union,
34     validate_select_field_data,
35 )
36 from zerver.models import CustomProfileField, UserProfile, custom_profile_fields_for_realm
37 
38 
39 def list_realm_custom_profile_fields(
40     request: HttpRequest, user_profile: UserProfile
41 ) -> HttpResponse:
42     fields = custom_profile_fields_for_realm(user_profile.realm_id)
43     return json_success(request, data={"custom_fields": [f.as_dict() for f in fields]})
44 
45 
46 hint_validator = check_capped_string(CustomProfileField.HINT_MAX_LENGTH)
47 name_validator = check_capped_string(CustomProfileField.NAME_MAX_LENGTH)
48 
49 
50 def validate_field_name_and_hint(name: str, hint: str) -> None:
51     if not name.strip():
52         raise JsonableError(_("Label cannot be blank."))
53 
54     try:
55         hint_validator("hint", hint)
56         name_validator("name", name)
57     except ValidationError as error:
58         raise JsonableError(error.message)
59 
60 
61 def validate_custom_field_data(field_type: int, field_data: ProfileFieldData) -> None:
62     try:
63         if field_type == CustomProfileField.SELECT:
64             # Choice type field must have at least have one choice
65             if len(field_data) < 1:
66                 raise JsonableError(_("Field must have at least one choice."))
67             validate_select_field_data(field_data)
68         elif field_type == CustomProfileField.EXTERNAL_ACCOUNT:
69             validate_external_account_field_data(field_data)
70     except ValidationError as error:
71         raise JsonableError(error.message)
72 
73 
74 def validate_display_in_profile_summary_field(
75     field_type: int, display_in_profile_summary: bool
76 ) -> None:
77     if not display_in_profile_summary:
78         return
79 
80     # The LONG_TEXT field type doesn't make sense visually for profile
81     # field summaries. The USER field type will require some further
82     # client support.
83     if field_type == CustomProfileField.LONG_TEXT or field_type == CustomProfileField.USER:
84         raise JsonableError(_("Field type not supported for display in profile summary."))
85 
86 
87 def is_default_external_field(field_type: int, field_data: ProfileFieldData) -> bool:
88     if field_type != CustomProfileField.EXTERNAL_ACCOUNT:
89         return False
90     if field_data["subtype"] == "custom":
91         return False
92     return True
93 
94 
95 def validate_custom_profile_field(
96     name: str,
97     hint: str,
98     field_type: int,
99     field_data: ProfileFieldData,
100     display_in_profile_summary: bool,
101 ) -> None:
102     # Validate field data
103     validate_custom_field_data(field_type, field_data)
104 
105     if not is_default_external_field(field_type, field_data):
106         # If field is default external field then we will fetch all data
107         # from our default field dictionary, so no need to validate name or hint
108         # Validate field name, hint if not default external account field
109         validate_field_name_and_hint(name, hint)
110 
111     field_types = [i[0] for i in CustomProfileField.FIELD_TYPE_CHOICES]
112     if field_type not in field_types:
113         raise JsonableError(_("Invalid field type."))
114 
115     validate_display_in_profile_summary_field(field_type, display_in_profile_summary)
116 
117 
118 check_profile_field_data: Validator[ProfileFieldData] = check_dict(
119     value_validator=check_union([check_dict(value_validator=check_string), check_string])
120 )
121 
122 
123 def update_only_display_in_profile_summary(
124     requested_name: str,
125     requested_hint: str,
126     requested_field_data: ProfileFieldData,
127     existing_field: CustomProfileField,
128 ) -> bool:
129     if (
130         requested_name != existing_field.name
131         or requested_hint != existing_field.hint
132         or requested_field_data != orjson.loads(existing_field.field_data)
133     ):
134         return False
135     return True
136 
137 
138 def display_in_profile_summary_limit_reached(profile_field_id: Optional[int] = None) -> bool:
139     query = CustomProfileField.objects.filter(display_in_profile_summary=True)
140     if profile_field_id is not None:
141         query = query.exclude(id=profile_field_id)
142     return query.count() >= CustomProfileField.MAX_DISPLAY_IN_PROFILE_SUMMARY_FIELDS
143 
144 
145 @require_realm_admin
146 @has_request_variables
147 def create_realm_custom_profile_field(
148     request: HttpRequest,
149     user_profile: UserProfile,
150     name: str = REQ(default="", converter=lambda var_name, x: x.strip()),
151     hint: str = REQ(default=""),
152     field_data: ProfileFieldData = REQ(default={}, json_validator=check_profile_field_data),
153     field_type: int = REQ(json_validator=check_int),
154     display_in_profile_summary: bool = REQ(default=False, json_validator=check_bool),
155 ) -> HttpResponse:
156     if display_in_profile_summary and display_in_profile_summary_limit_reached():
157         raise JsonableError(
158             _("Only 2 custom profile fields can be displayed in the profile summary.")
159         )
160 
161     validate_custom_profile_field(name, hint, field_type, field_data, display_in_profile_summary)
162     try:
163         if is_default_external_field(field_type, field_data):
164             field_subtype = field_data["subtype"]
165             assert isinstance(field_subtype, str)
166             field = try_add_realm_default_custom_profile_field(
167                 realm=user_profile.realm,
168                 field_subtype=field_subtype,
169                 display_in_profile_summary=display_in_profile_summary,
170             )
171             return json_success(request, data={"id": field.id})
172         else:
173             field = try_add_realm_custom_profile_field(
174                 realm=user_profile.realm,
175                 name=name,
176                 field_data=field_data,
177                 field_type=field_type,
178                 hint=hint,
179                 display_in_profile_summary=display_in_profile_summary,
180             )
181             return json_success(request, data={"id": field.id})
182     except IntegrityError:
183         raise JsonableError(_("A field with that label already exists."))
184 
185 
186 @require_realm_admin
187 def delete_realm_custom_profile_field(
188     request: HttpRequest, user_profile: UserProfile, field_id: int
189 ) -> HttpResponse:
190     try:
191         field = CustomProfileField.objects.get(id=field_id)
192     except CustomProfileField.DoesNotExist:
193         raise JsonableError(_("Field id {id} not found.").format(id=field_id))
194 
195     do_remove_realm_custom_profile_field(realm=user_profile.realm, field=field)
196     return json_success(request)
197 
198 
199 @require_realm_admin
200 @has_request_variables
201 def update_realm_custom_profile_field(
202     request: HttpRequest,
203     user_profile: UserProfile,
204     field_id: int,
205     name: str = REQ(default="", converter=lambda var_name, x: x.strip()),
206     hint: str = REQ(default=""),
207     field_data: ProfileFieldData = REQ(default={}, json_validator=check_profile_field_data),
208     display_in_profile_summary: bool = REQ(default=False, json_validator=check_bool),
209 ) -> HttpResponse:
210     realm = user_profile.realm
211     try:
212         field = CustomProfileField.objects.get(realm=realm, id=field_id)
213     except CustomProfileField.DoesNotExist:
214         raise JsonableError(_("Field id {id} not found.").format(id=field_id))
215 
216     if display_in_profile_summary and display_in_profile_summary_limit_reached(field.id):
217         raise JsonableError(
218             _("Only 2 custom profile fields can be displayed in the profile summary.")
219         )
220 
221     if field.field_type == CustomProfileField.EXTERNAL_ACCOUNT:
222         # HACK: Allow changing the display_in_profile_summary property
223         # of default external account types, but not any others.
224         #
225         # TODO: Make the name/hint/field_data parameters optional, and
226         # just require that None was passed for all of them for this case.
227         if is_default_external_field(
228             field.field_type, orjson.loads(field.field_data)
229         ) and not update_only_display_in_profile_summary(name, hint, field_data, field):
230             raise JsonableError(_("Default custom field cannot be updated."))
231 
232     validate_custom_profile_field(
233         name, hint, field.field_type, field_data, display_in_profile_summary
234     )
235     try:
236         try_update_realm_custom_profile_field(
237             realm,
238             field,
239             name,
240             hint=hint,
241             field_data=field_data,
242             display_in_profile_summary=display_in_profile_summary,
243         )
244     except IntegrityError:
245         raise JsonableError(_("A field with that label already exists."))
246     return json_success(request)
247 
248 
249 @require_realm_admin
250 @has_request_variables
251 def reorder_realm_custom_profile_fields(
252     request: HttpRequest,
253     user_profile: UserProfile,
254     order: List[int] = REQ(json_validator=check_list(check_int)),
255 ) -> HttpResponse:
256     try_reorder_realm_custom_profile_fields(user_profile.realm, order)
257     return json_success(request)
258 
259 
260 @human_users_only
261 @has_request_variables
262 def remove_user_custom_profile_data(
263     request: HttpRequest,
264     user_profile: UserProfile,
265     data: List[int] = REQ(json_validator=check_list(check_int)),
266 ) -> HttpResponse:
267     for field_id in data:
268         check_remove_custom_profile_field_value(user_profile, field_id)
269     return json_success(request)
270 
271 
272 check_profile_data_element_update_dict = cast(
273     Validator[ProfileDataElementUpdateDict],
274     check_dict_only(
275         [
276             ("id", check_int),
277             ("value", check_union([check_string, check_list(check_int)])),
278         ]
279     ),
280 )
281 
282 
283 @human_users_only
284 @has_request_variables
285 def update_user_custom_profile_data(
286     request: HttpRequest,
287     user_profile: UserProfile,
288     data: List[ProfileDataElementUpdateDict] = REQ(
289         json_validator=check_list(
290             check_profile_data_element_update_dict,
291         )
292     ),
293 ) -> HttpResponse:
294 
295     validate_user_custom_profile_data(user_profile.realm.id, data)
296     do_update_user_custom_profile_data_if_changed(user_profile, data)
297     # We need to call this explicitly otherwise constraints are not check
298     return json_success(request)
299
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/zerver/views/custom_profile_fields.py b/zerver/views/custom_profile_fields.py
--- a/zerver/views/custom_profile_fields.py
+++ b/zerver/views/custom_profile_fields.py
@@ -33,7 +33,7 @@
     check_union,
     validate_select_field_data,
 )
-from zerver.models import CustomProfileField, UserProfile, custom_profile_fields_for_realm
+from zerver.models import CustomProfileField, Realm, UserProfile, custom_profile_fields_for_realm
 
 
 def list_realm_custom_profile_fields(
@@ -135,8 +135,10 @@
     return True
 
 
-def display_in_profile_summary_limit_reached(profile_field_id: Optional[int] = None) -> bool:
-    query = CustomProfileField.objects.filter(display_in_profile_summary=True)
+def display_in_profile_summary_limit_reached(
+    realm: Realm, profile_field_id: Optional[int] = None
+) -> bool:
+    query = CustomProfileField.objects.filter(realm=realm, display_in_profile_summary=True)
     if profile_field_id is not None:
         query = query.exclude(id=profile_field_id)
     return query.count() >= CustomProfileField.MAX_DISPLAY_IN_PROFILE_SUMMARY_FIELDS
@@ -153,7 +155,7 @@
     field_type: int = REQ(json_validator=check_int),
     display_in_profile_summary: bool = REQ(default=False, json_validator=check_bool),
 ) -> HttpResponse:
-    if display_in_profile_summary and display_in_profile_summary_limit_reached():
+    if display_in_profile_summary and display_in_profile_summary_limit_reached(user_profile.realm):
         raise JsonableError(
             _("Only 2 custom profile fields can be displayed in the profile summary.")
         )
@@ -213,7 +215,9 @@
     except CustomProfileField.DoesNotExist:
         raise JsonableError(_("Field id {id} not found.").format(id=field_id))
 
-    if display_in_profile_summary and display_in_profile_summary_limit_reached(field.id):
+    if display_in_profile_summary and display_in_profile_summary_limit_reached(
+        user_profile.realm, field.id
+    ):
         raise JsonableError(
             _("Only 2 custom profile fields can be displayed in the profile summary.")
         )

{"golden_diff": "diff --git a/zerver/views/custom_profile_fields.py b/zerver/views/custom_profile_fields.py\n--- a/zerver/views/custom_profile_fields.py\n+++ b/zerver/views/custom_profile_fields.py\n@@ -33,7 +33,7 @@\n     check_union,\n     validate_select_field_data,\n )\n-from zerver.models import CustomProfileField, UserProfile, custom_profile_fields_for_realm\n+from zerver.models import CustomProfileField, Realm, UserProfile, custom_profile_fields_for_realm\n \n \n def list_realm_custom_profile_fields(\n@@ -135,8 +135,10 @@\n     return True\n \n \n-def display_in_profile_summary_limit_reached(profile_field_id: Optional[int] = None) -> bool:\n-    query = CustomProfileField.objects.filter(display_in_profile_summary=True)\n+def display_in_profile_summary_limit_reached(\n+    realm: Realm, profile_field_id: Optional[int] = None\n+) -> bool:\n+    query = CustomProfileField.objects.filter(realm=realm, display_in_profile_summary=True)\n     if profile_field_id is not None:\n         query = query.exclude(id=profile_field_id)\n     return query.count() >= CustomProfileField.MAX_DISPLAY_IN_PROFILE_SUMMARY_FIELDS\n@@ -153,7 +155,7 @@\n     field_type: int = REQ(json_validator=check_int),\n     display_in_profile_summary: bool = REQ(default=False, json_validator=check_bool),\n ) -> HttpResponse:\n-    if display_in_profile_summary and display_in_profile_summary_limit_reached():\n+    if display_in_profile_summary and display_in_profile_summary_limit_reached(user_profile.realm):\n         raise JsonableError(\n             _(\"Only 2 custom profile fields can be displayed in the profile summary.\")\n         )\n@@ -213,7 +215,9 @@\n     except CustomProfileField.DoesNotExist:\n         raise JsonableError(_(\"Field id {id} not found.\").format(id=field_id))\n \n-    if display_in_profile_summary and display_in_profile_summary_limit_reached(field.id):\n+    if display_in_profile_summary and display_in_profile_summary_limit_reached(\n+        user_profile.realm, field.id\n+    ):\n         raise JsonableError(\n             _(\"Only 2 custom profile fields can be displayed in the profile summary.\")\n         )\n", "issue": "Save failed: Only 2 custom profile fields can be displayed in the profile summary.\nI tried to set a custom profile field to be displayed in the profile summary. However, it fails with an error message \"Save failed: Only 2 custom profile fields can be displayed in the profile summary.\" even though no custom profile field has been set to displayed.\r\n\r\nhttps://user-images.githubusercontent.com/2891235/198561250-59519f45-85a4-4490-8f72-9fae343930be.mov\r\n\r\n\r\nServer: hosted Zulip server\r\n```console\r\nZulip Server\r\nVersion 6.0-dev-2021-gd461383c1f \r\nForked from upstream at 6.0-dev-2009-g0affc7ac6f\r\n```\r\n\r\nClient: Zulip Desktop v5.9.3\n", "before_files": [{"content": "from typing import List, Optional, cast\n\nimport orjson\nfrom django.core.exceptions import ValidationError\nfrom django.db import IntegrityError\nfrom django.http import HttpRequest, HttpResponse\nfrom django.utils.translation import gettext as _\n\nfrom zerver.actions.custom_profile_fields import (\n    check_remove_custom_profile_field_value,\n    do_remove_realm_custom_profile_field,\n    do_update_user_custom_profile_data_if_changed,\n    try_add_realm_custom_profile_field,\n    try_add_realm_default_custom_profile_field,\n    try_reorder_realm_custom_profile_fields,\n    try_update_realm_custom_profile_field,\n)\nfrom zerver.decorator import human_users_only, require_realm_admin\nfrom zerver.lib.exceptions import JsonableError\nfrom zerver.lib.external_accounts import validate_external_account_field_data\nfrom zerver.lib.request import REQ, has_request_variables\nfrom zerver.lib.response import json_success\nfrom zerver.lib.types import ProfileDataElementUpdateDict, ProfileFieldData, Validator\nfrom zerver.lib.users import validate_user_custom_profile_data\nfrom zerver.lib.validator import (\n    check_bool,\n    check_capped_string,\n    check_dict,\n    check_dict_only,\n    check_int,\n    check_list,\n    check_string,\n    check_union,\n    validate_select_field_data,\n)\nfrom zerver.models import CustomProfileField, UserProfile, custom_profile_fields_for_realm\n\n\ndef list_realm_custom_profile_fields(\n    request: HttpRequest, user_profile: UserProfile\n) -> HttpResponse:\n    fields = custom_profile_fields_for_realm(user_profile.realm_id)\n    return json_success(request, data={\"custom_fields\": [f.as_dict() for f in fields]})\n\n\nhint_validator = check_capped_string(CustomProfileField.HINT_MAX_LENGTH)\nname_validator = check_capped_string(CustomProfileField.NAME_MAX_LENGTH)\n\n\ndef validate_field_name_and_hint(name: str, hint: str) -> None:\n    if not name.strip():\n        raise JsonableError(_(\"Label cannot be blank.\"))\n\n    try:\n        hint_validator(\"hint\", hint)\n        name_validator(\"name\", name)\n    except ValidationError as error:\n        raise JsonableError(error.message)\n\n\ndef validate_custom_field_data(field_type: int, field_data: ProfileFieldData) -> None:\n    try:\n        if field_type == CustomProfileField.SELECT:\n            # Choice type field must have at least have one choice\n            if len(field_data) < 1:\n                raise JsonableError(_(\"Field must have at least one choice.\"))\n            validate_select_field_data(field_data)\n        elif field_type == CustomProfileField.EXTERNAL_ACCOUNT:\n            validate_external_account_field_data(field_data)\n    except ValidationError as error:\n        raise JsonableError(error.message)\n\n\ndef validate_display_in_profile_summary_field(\n    field_type: int, display_in_profile_summary: bool\n) -> None:\n    if not display_in_profile_summary:\n        return\n\n    # The LONG_TEXT field type doesn't make sense visually for profile\n    # field summaries. The USER field type will require some further\n    # client support.\n    if field_type == CustomProfileField.LONG_TEXT or field_type == CustomProfileField.USER:\n        raise JsonableError(_(\"Field type not supported for display in profile summary.\"))\n\n\ndef is_default_external_field(field_type: int, field_data: ProfileFieldData) -> bool:\n    if field_type != CustomProfileField.EXTERNAL_ACCOUNT:\n        return False\n    if field_data[\"subtype\"] == \"custom\":\n        return False\n    return True\n\n\ndef validate_custom_profile_field(\n    name: str,\n    hint: str,\n    field_type: int,\n    field_data: ProfileFieldData,\n    display_in_profile_summary: bool,\n) -> None:\n    # Validate field data\n    validate_custom_field_data(field_type, field_data)\n\n    if not is_default_external_field(field_type, field_data):\n        # If field is default external field then we will fetch all data\n        # from our default field dictionary, so no need to validate name or hint\n        # Validate field name, hint if not default external account field\n        validate_field_name_and_hint(name, hint)\n\n    field_types = [i[0] for i in CustomProfileField.FIELD_TYPE_CHOICES]\n    if field_type not in field_types:\n        raise JsonableError(_(\"Invalid field type.\"))\n\n    validate_display_in_profile_summary_field(field_type, display_in_profile_summary)\n\n\ncheck_profile_field_data: Validator[ProfileFieldData] = check_dict(\n    value_validator=check_union([check_dict(value_validator=check_string), check_string])\n)\n\n\ndef update_only_display_in_profile_summary(\n    requested_name: str,\n    requested_hint: str,\n    requested_field_data: ProfileFieldData,\n    existing_field: CustomProfileField,\n) -> bool:\n    if (\n        requested_name != existing_field.name\n        or requested_hint != existing_field.hint\n        or requested_field_data != orjson.loads(existing_field.field_data)\n    ):\n        return False\n    return True\n\n\ndef display_in_profile_summary_limit_reached(profile_field_id: Optional[int] = None) -> bool:\n    query = CustomProfileField.objects.filter(display_in_profile_summary=True)\n    if profile_field_id is not None:\n        query = query.exclude(id=profile_field_id)\n    return query.count() >= CustomProfileField.MAX_DISPLAY_IN_PROFILE_SUMMARY_FIELDS\n\n\n@require_realm_admin\n@has_request_variables\ndef create_realm_custom_profile_field(\n    request: HttpRequest,\n    user_profile: UserProfile,\n    name: str = REQ(default=\"\", converter=lambda var_name, x: x.strip()),\n    hint: str = REQ(default=\"\"),\n    field_data: ProfileFieldData = REQ(default={}, json_validator=check_profile_field_data),\n    field_type: int = REQ(json_validator=check_int),\n    display_in_profile_summary: bool = REQ(default=False, json_validator=check_bool),\n) -> HttpResponse:\n    if display_in_profile_summary and display_in_profile_summary_limit_reached():\n        raise JsonableError(\n            _(\"Only 2 custom profile fields can be displayed in the profile summary.\")\n        )\n\n    validate_custom_profile_field(name, hint, field_type, field_data, display_in_profile_summary)\n    try:\n        if is_default_external_field(field_type, field_data):\n            field_subtype = field_data[\"subtype\"]\n            assert isinstance(field_subtype, str)\n            field = try_add_realm_default_custom_profile_field(\n                realm=user_profile.realm,\n                field_subtype=field_subtype,\n                display_in_profile_summary=display_in_profile_summary,\n            )\n            return json_success(request, data={\"id\": field.id})\n        else:\n            field = try_add_realm_custom_profile_field(\n                realm=user_profile.realm,\n                name=name,\n                field_data=field_data,\n                field_type=field_type,\n                hint=hint,\n                display_in_profile_summary=display_in_profile_summary,\n            )\n            return json_success(request, data={\"id\": field.id})\n    except IntegrityError:\n        raise JsonableError(_(\"A field with that label already exists.\"))\n\n\n@require_realm_admin\ndef delete_realm_custom_profile_field(\n    request: HttpRequest, user_profile: UserProfile, field_id: int\n) -> HttpResponse:\n    try:\n        field = CustomProfileField.objects.get(id=field_id)\n    except CustomProfileField.DoesNotExist:\n        raise JsonableError(_(\"Field id {id} not found.\").format(id=field_id))\n\n    do_remove_realm_custom_profile_field(realm=user_profile.realm, field=field)\n    return json_success(request)\n\n\n@require_realm_admin\n@has_request_variables\ndef update_realm_custom_profile_field(\n    request: HttpRequest,\n    user_profile: UserProfile,\n    field_id: int,\n    name: str = REQ(default=\"\", converter=lambda var_name, x: x.strip()),\n    hint: str = REQ(default=\"\"),\n    field_data: ProfileFieldData = REQ(default={}, json_validator=check_profile_field_data),\n    display_in_profile_summary: bool = REQ(default=False, json_validator=check_bool),\n) -> HttpResponse:\n    realm = user_profile.realm\n    try:\n        field = CustomProfileField.objects.get(realm=realm, id=field_id)\n    except CustomProfileField.DoesNotExist:\n        raise JsonableError(_(\"Field id {id} not found.\").format(id=field_id))\n\n    if display_in_profile_summary and display_in_profile_summary_limit_reached(field.id):\n        raise JsonableError(\n            _(\"Only 2 custom profile fields can be displayed in the profile summary.\")\n        )\n\n    if field.field_type == CustomProfileField.EXTERNAL_ACCOUNT:\n        # HACK: Allow changing the display_in_profile_summary property\n        # of default external account types, but not any others.\n        #\n        # TODO: Make the name/hint/field_data parameters optional, and\n        # just require that None was passed for all of them for this case.\n        if is_default_external_field(\n            field.field_type, orjson.loads(field.field_data)\n        ) and not update_only_display_in_profile_summary(name, hint, field_data, field):\n            raise JsonableError(_(\"Default custom field cannot be updated.\"))\n\n    validate_custom_profile_field(\n        name, hint, field.field_type, field_data, display_in_profile_summary\n    )\n    try:\n        try_update_realm_custom_profile_field(\n            realm,\n            field,\n            name,\n            hint=hint,\n            field_data=field_data,\n            display_in_profile_summary=display_in_profile_summary,\n        )\n    except IntegrityError:\n        raise JsonableError(_(\"A field with that label already exists.\"))\n    return json_success(request)\n\n\n@require_realm_admin\n@has_request_variables\ndef reorder_realm_custom_profile_fields(\n    request: HttpRequest,\n    user_profile: UserProfile,\n    order: List[int] = REQ(json_validator=check_list(check_int)),\n) -> HttpResponse:\n    try_reorder_realm_custom_profile_fields(user_profile.realm, order)\n    return json_success(request)\n\n\n@human_users_only\n@has_request_variables\ndef remove_user_custom_profile_data(\n    request: HttpRequest,\n    user_profile: UserProfile,\n    data: List[int] = REQ(json_validator=check_list(check_int)),\n) -> HttpResponse:\n    for field_id in data:\n        check_remove_custom_profile_field_value(user_profile, field_id)\n    return json_success(request)\n\n\ncheck_profile_data_element_update_dict = cast(\n    Validator[ProfileDataElementUpdateDict],\n    check_dict_only(\n        [\n            (\"id\", check_int),\n            (\"value\", check_union([check_string, check_list(check_int)])),\n        ]\n    ),\n)\n\n\n@human_users_only\n@has_request_variables\ndef update_user_custom_profile_data(\n    request: HttpRequest,\n    user_profile: UserProfile,\n    data: List[ProfileDataElementUpdateDict] = REQ(\n        json_validator=check_list(\n            check_profile_data_element_update_dict,\n        )\n    ),\n) -> HttpResponse:\n\n    validate_user_custom_profile_data(user_profile.realm.id, data)\n    do_update_user_custom_profile_data_if_changed(user_profile, data)\n    # We need to call this explicitly otherwise constraints are not check\n    return json_success(request)\n", "path": "zerver/views/custom_profile_fields.py"}], "after_files": [{"content": "from typing import List, Optional, cast\n\nimport orjson\nfrom django.core.exceptions import ValidationError\nfrom django.db import IntegrityError\nfrom django.http import HttpRequest, HttpResponse\nfrom django.utils.translation import gettext as _\n\nfrom zerver.actions.custom_profile_fields import (\n    check_remove_custom_profile_field_value,\n    do_remove_realm_custom_profile_field,\n    do_update_user_custom_profile_data_if_changed,\n    try_add_realm_custom_profile_field,\n    try_add_realm_default_custom_profile_field,\n    try_reorder_realm_custom_profile_fields,\n    try_update_realm_custom_profile_field,\n)\nfrom zerver.decorator import human_users_only, require_realm_admin\nfrom zerver.lib.exceptions import JsonableError\nfrom zerver.lib.external_accounts import validate_external_account_field_data\nfrom zerver.lib.request import REQ, has_request_variables\nfrom zerver.lib.response import json_success\nfrom zerver.lib.types import ProfileDataElementUpdateDict, ProfileFieldData, Validator\nfrom zerver.lib.users import validate_user_custom_profile_data\nfrom zerver.lib.validator import (\n    check_bool,\n    check_capped_string,\n    check_dict,\n    check_dict_only,\n    check_int,\n    check_list,\n    check_string,\n    check_union,\n    validate_select_field_data,\n)\nfrom zerver.models import CustomProfileField, Realm, UserProfile, custom_profile_fields_for_realm\n\n\ndef list_realm_custom_profile_fields(\n    request: HttpRequest, user_profile: UserProfile\n) -> HttpResponse:\n    fields = custom_profile_fields_for_realm(user_profile.realm_id)\n    return json_success(request, data={\"custom_fields\": [f.as_dict() for f in fields]})\n\n\nhint_validator = check_capped_string(CustomProfileField.HINT_MAX_LENGTH)\nname_validator = check_capped_string(CustomProfileField.NAME_MAX_LENGTH)\n\n\ndef validate_field_name_and_hint(name: str, hint: str) -> None:\n    if not name.strip():\n        raise JsonableError(_(\"Label cannot be blank.\"))\n\n    try:\n        hint_validator(\"hint\", hint)\n        name_validator(\"name\", name)\n    except ValidationError as error:\n        raise JsonableError(error.message)\n\n\ndef validate_custom_field_data(field_type: int, field_data: ProfileFieldData) -> None:\n    try:\n        if field_type == CustomProfileField.SELECT:\n            # Choice type field must have at least have one choice\n            if len(field_data) < 1:\n                raise JsonableError(_(\"Field must have at least one choice.\"))\n            validate_select_field_data(field_data)\n        elif field_type == CustomProfileField.EXTERNAL_ACCOUNT:\n            validate_external_account_field_data(field_data)\n    except ValidationError as error:\n        raise JsonableError(error.message)\n\n\ndef validate_display_in_profile_summary_field(\n    field_type: int, display_in_profile_summary: bool\n) -> None:\n    if not display_in_profile_summary:\n        return\n\n    # The LONG_TEXT field type doesn't make sense visually for profile\n    # field summaries. The USER field type will require some further\n    # client support.\n    if field_type == CustomProfileField.LONG_TEXT or field_type == CustomProfileField.USER:\n        raise JsonableError(_(\"Field type not supported for display in profile summary.\"))\n\n\ndef is_default_external_field(field_type: int, field_data: ProfileFieldData) -> bool:\n    if field_type != CustomProfileField.EXTERNAL_ACCOUNT:\n        return False\n    if field_data[\"subtype\"] == \"custom\":\n        return False\n    return True\n\n\ndef validate_custom_profile_field(\n    name: str,\n    hint: str,\n    field_type: int,\n    field_data: ProfileFieldData,\n    display_in_profile_summary: bool,\n) -> None:\n    # Validate field data\n    validate_custom_field_data(field_type, field_data)\n\n    if not is_default_external_field(field_type, field_data):\n        # If field is default external field then we will fetch all data\n        # from our default field dictionary, so no need to validate name or hint\n        # Validate field name, hint if not default external account field\n        validate_field_name_and_hint(name, hint)\n\n    field_types = [i[0] for i in CustomProfileField.FIELD_TYPE_CHOICES]\n    if field_type not in field_types:\n        raise JsonableError(_(\"Invalid field type.\"))\n\n    validate_display_in_profile_summary_field(field_type, display_in_profile_summary)\n\n\ncheck_profile_field_data: Validator[ProfileFieldData] = check_dict(\n    value_validator=check_union([check_dict(value_validator=check_string), check_string])\n)\n\n\ndef update_only_display_in_profile_summary(\n    requested_name: str,\n    requested_hint: str,\n    requested_field_data: ProfileFieldData,\n    existing_field: CustomProfileField,\n) -> bool:\n    if (\n        requested_name != existing_field.name\n        or requested_hint != existing_field.hint\n        or requested_field_data != orjson.loads(existing_field.field_data)\n    ):\n        return False\n    return True\n\n\ndef display_in_profile_summary_limit_reached(\n    realm: Realm, profile_field_id: Optional[int] = None\n) -> bool:\n    query = CustomProfileField.objects.filter(realm=realm, display_in_profile_summary=True)\n    if profile_field_id is not None:\n        query = query.exclude(id=profile_field_id)\n    return query.count() >= CustomProfileField.MAX_DISPLAY_IN_PROFILE_SUMMARY_FIELDS\n\n\n@require_realm_admin\n@has_request_variables\ndef create_realm_custom_profile_field(\n    request: HttpRequest,\n    user_profile: UserProfile,\n    name: str = REQ(default=\"\", converter=lambda var_name, x: x.strip()),\n    hint: str = REQ(default=\"\"),\n    field_data: ProfileFieldData = REQ(default={}, json_validator=check_profile_field_data),\n    field_type: int = REQ(json_validator=check_int),\n    display_in_profile_summary: bool = REQ(default=False, json_validator=check_bool),\n) -> HttpResponse:\n    if display_in_profile_summary and display_in_profile_summary_limit_reached(user_profile.realm):\n        raise JsonableError(\n            _(\"Only 2 custom profile fields can be displayed in the profile summary.\")\n        )\n\n    validate_custom_profile_field(name, hint, field_type, field_data, display_in_profile_summary)\n    try:\n        if is_default_external_field(field_type, field_data):\n            field_subtype = field_data[\"subtype\"]\n            assert isinstance(field_subtype, str)\n            field = try_add_realm_default_custom_profile_field(\n                realm=user_profile.realm,\n                field_subtype=field_subtype,\n                display_in_profile_summary=display_in_profile_summary,\n            )\n            return json_success(request, data={\"id\": field.id})\n        else:\n            field = try_add_realm_custom_profile_field(\n                realm=user_profile.realm,\n                name=name,\n                field_data=field_data,\n                field_type=field_type,\n                hint=hint,\n                display_in_profile_summary=display_in_profile_summary,\n            )\n            return json_success(request, data={\"id\": field.id})\n    except IntegrityError:\n        raise JsonableError(_(\"A field with that label already exists.\"))\n\n\n@require_realm_admin\ndef delete_realm_custom_profile_field(\n    request: HttpRequest, user_profile: UserProfile, field_id: int\n) -> HttpResponse:\n    try:\n        field = CustomProfileField.objects.get(id=field_id)\n    except CustomProfileField.DoesNotExist:\n        raise JsonableError(_(\"Field id {id} not found.\").format(id=field_id))\n\n    do_remove_realm_custom_profile_field(realm=user_profile.realm, field=field)\n    return json_success(request)\n\n\n@require_realm_admin\n@has_request_variables\ndef update_realm_custom_profile_field(\n    request: HttpRequest,\n    user_profile: UserProfile,\n    field_id: int,\n    name: str = REQ(default=\"\", converter=lambda var_name, x: x.strip()),\n    hint: str = REQ(default=\"\"),\n    field_data: ProfileFieldData = REQ(default={}, json_validator=check_profile_field_data),\n    display_in_profile_summary: bool = REQ(default=False, json_validator=check_bool),\n) -> HttpResponse:\n    realm = user_profile.realm\n    try:\n        field = CustomProfileField.objects.get(realm=realm, id=field_id)\n    except CustomProfileField.DoesNotExist:\n        raise JsonableError(_(\"Field id {id} not found.\").format(id=field_id))\n\n    if display_in_profile_summary and display_in_profile_summary_limit_reached(\n        user_profile.realm, field.id\n    ):\n        raise JsonableError(\n            _(\"Only 2 custom profile fields can be displayed in the profile summary.\")\n        )\n\n    if field.field_type == CustomProfileField.EXTERNAL_ACCOUNT:\n        # HACK: Allow changing the display_in_profile_summary property\n        # of default external account types, but not any others.\n        #\n        # TODO: Make the name/hint/field_data parameters optional, and\n        # just require that None was passed for all of them for this case.\n        if is_default_external_field(\n            field.field_type, orjson.loads(field.field_data)\n        ) and not update_only_display_in_profile_summary(name, hint, field_data, field):\n            raise JsonableError(_(\"Default custom field cannot be updated.\"))\n\n    validate_custom_profile_field(\n        name, hint, field.field_type, field_data, display_in_profile_summary\n    )\n    try:\n        try_update_realm_custom_profile_field(\n            realm,\n            field,\n            name,\n            hint=hint,\n            field_data=field_data,\n            display_in_profile_summary=display_in_profile_summary,\n        )\n    except IntegrityError:\n        raise JsonableError(_(\"A field with that label already exists.\"))\n    return json_success(request)\n\n\n@require_realm_admin\n@has_request_variables\ndef reorder_realm_custom_profile_fields(\n    request: HttpRequest,\n    user_profile: UserProfile,\n    order: List[int] = REQ(json_validator=check_list(check_int)),\n) -> HttpResponse:\n    try_reorder_realm_custom_profile_fields(user_profile.realm, order)\n    return json_success(request)\n\n\n@human_users_only\n@has_request_variables\ndef remove_user_custom_profile_data(\n    request: HttpRequest,\n    user_profile: UserProfile,\n    data: List[int] = REQ(json_validator=check_list(check_int)),\n) -> HttpResponse:\n    for field_id in data:\n        check_remove_custom_profile_field_value(user_profile, field_id)\n    return json_success(request)\n\n\ncheck_profile_data_element_update_dict = cast(\n    Validator[ProfileDataElementUpdateDict],\n    check_dict_only(\n        [\n            (\"id\", check_int),\n            (\"value\", check_union([check_string, check_list(check_int)])),\n        ]\n    ),\n)\n\n\n@human_users_only\n@has_request_variables\ndef update_user_custom_profile_data(\n    request: HttpRequest,\n    user_profile: UserProfile,\n    data: List[ProfileDataElementUpdateDict] = REQ(\n        json_validator=check_list(\n            check_profile_data_element_update_dict,\n        )\n    ),\n) -> HttpResponse:\n\n    validate_user_custom_profile_data(user_profile.realm.id, data)\n    do_update_user_custom_profile_data_if_changed(user_profile, data)\n    # We need to call this explicitly otherwise constraints are not check\n    return json_success(request)\n", "path": "zerver/views/custom_profile_fields.py"}]}