From 256091dc1535a792a84fc88b7947ca15fc68bc24 Mon Sep 17 00:00:00 2001 From: Tim Abbott Date: Wed, 7 Jul 2021 13:08:11 -0700 Subject: [PATCH] settings: Merge settings API endpoints. This API change removes unnecessary complexity from a client that wants to change a user's personal settings, and also saves developers from needing to make decisions about what sort of setting something is at the API level. We preserve the old settings endpoints as mapping to the same function as the new one for backwards-compatibility. We delete the documentation for the old endpoints, though the documentation for the merged /settings endpoint mentions how to use the old endpoints when needed. We migrate all backend tests to the new endpoints, except for individual tests for each legacy endpoint to verify they still work. Co-authored-by: sahil839 --- templates/zerver/api/changelog.md | 19 +- .../zerver/help/include/rest-endpoints.md | 3 +- tools/linter_lib/custom_check.py | 1 - version.py | 2 +- zerver/openapi/openapi.py | 4 +- zerver/openapi/python_examples.py | 29 +- zerver/openapi/zulip.yaml | 783 +++++++++--------- zerver/tests/test_openapi.py | 1 - zerver/tests/test_settings.py | 43 +- zerver/views/user_settings.py | 202 ++--- zproject/urls.py | 11 +- 11 files changed, 547 insertions(+), 551 deletions(-) diff --git a/templates/zerver/api/changelog.md b/templates/zerver/api/changelog.md index e58e15f8dd..90e8a3858f 100644 --- a/templates/zerver/api/changelog.md +++ b/templates/zerver/api/changelog.md @@ -11,6 +11,14 @@ below features are supported. ## Changes in Zulip 5.0 +**Feature level 80** + +* [`PATCH /settings`](/api/update-settings): The + `/settings/notifications` and `/settings/display` endpoints were + merged into the main `/settings` endpoint; now all personal settings + should be edited using that single endpoint. The old URLs are + preserved as deprecated aliases for backwards compatibility. + **Feature level 79** * [`GET /users/me/subscriptions`](/api/get-subscriptions): The @@ -22,12 +30,13 @@ below features are supported. **Feature level 78** -* `PATCH /settings`: Added `ignored_parameters_unsupported` field, - which is a list of parameters that were ignored by the endpoint, - to the response object. +* [`PATCH /settings`](/api/update-settings): Added + `ignored_parameters_unsupported` field, which is a list of + parameters that were ignored by the endpoint, to the response + object. -* `PATCH /settings`: Removed `full_name` and `account_email` fields - from the response object. +* [`PATCH /settings`](/api/update-settings): Removed `full_name` and + `account_email` fields from the response object. **Feature level 77** diff --git a/templates/zerver/help/include/rest-endpoints.md b/templates/zerver/help/include/rest-endpoints.md index 14f161df2f..f6cde3b639 100644 --- a/templates/zerver/help/include/rest-endpoints.md +++ b/templates/zerver/help/include/rest-endpoints.md @@ -44,8 +44,7 @@ * [Set "typing" status](/api/set-typing-status) * [Get user presence](/api/get-user-presence) * [Get attachments](/api/get-attachments) -* [Update display settings](/api/update-display-settings) -* [Update notification settings](/api/update-notification-settings) +* [Update settings](/api/update-settings) * [Get user groups](/api/get-user-groups) * [Create a user group](/api/create-user-group) * [Update a user group](/api/update-user-group) diff --git a/tools/linter_lib/custom_check.py b/tools/linter_lib/custom_check.py index c82a0a37a9..247d277940 100644 --- a/tools/linter_lib/custom_check.py +++ b/tools/linter_lib/custom_check.py @@ -820,7 +820,6 @@ markdown_docs_length_exclude = { "templates/zerver/api/get-messages.md", # This macro has a long indented URL "templates/zerver/help/include/git-webhook-url-with-branches-indented.md", - "templates/zerver/api/update-notification-settings.md", # These two are the same file and have some too-long lines for GitHub badges "README.md", "docs/overview/readme.md", diff --git a/version.py b/version.py index f2ff8a245b..d6d3e60fbe 100644 --- a/version.py +++ b/version.py @@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.4.3" # Changes should be accompanied by documentation explaining what the # new level means in templates/zerver/api/changelog.md, as well as # "**Changes**" entries in the endpoint's documentation in `zulip.yaml`. -API_FEATURE_LEVEL = 79 +API_FEATURE_LEVEL = 80 # Bump the minor PROVISION_VERSION to indicate that folks should provision # only when going from an old version of the code to a newer version. Bump diff --git a/zerver/openapi/openapi.py b/zerver/openapi/openapi.py index b39e89c8fd..5f7daa11d9 100644 --- a/zerver/openapi/openapi.py +++ b/zerver/openapi/openapi.py @@ -28,9 +28,7 @@ EXCLUDE_UNDOCUMENTED_ENDPOINTS = { } # Consists of endpoints with some documentation remaining. # These are skipped but return true as the validator cannot exclude objects -EXCLUDE_DOCUMENTED_ENDPOINTS = { - ("/settings/notifications", "patch"), -} +EXCLUDE_DOCUMENTED_ENDPOINTS: Set[Tuple[str, str]] = set([]) # Most of our code expects allOf to be preprocessed away because that is what # yamole did. Its algorithm for doing so is not standards compliant, but we diff --git a/zerver/openapi/python_examples.py b/zerver/openapi/python_examples.py index 1bf726800f..7e01549bc4 100644 --- a/zerver/openapi/python_examples.py +++ b/zerver/openapi/python_examples.py @@ -1132,35 +1132,20 @@ def get_server_settings(client: Client) -> None: validate_against_openapi_schema(result, "/server_settings", "get", "200") -@openapi_test_function("/settings/notifications:patch") -def update_notification_settings(client: Client) -> None: +@openapi_test_function("/settings:patch") +def update_settings(client: Client) -> None: # {code_example|start} - # Enable push notifications even when online + # Enable push notifications even when online and change emojiset request = { "enable_offline_push_notifications": True, "enable_online_push_notifications": True, - } - result = client.update_notification_settings(request) - # {code_example|end} - - validate_against_openapi_schema(result, "/settings/notifications", "patch", "200") - - -@openapi_test_function("/settings/display:patch") -def update_display_settings(client: Client) -> None: - - # {code_example|start} - # Show user list on left sidebar in narrow windows. - # Change emoji set used for display to Google modern. - request = { - "left_side_userlist": True, "emojiset": "google", } - result = client.call_endpoint("settings/display", method="PATCH", request=request) + result = client.call_endpoint("/settings", method="PATCH", request=request) # {code_example|end} - validate_against_openapi_schema(result, "/settings/display", "patch", "200") + validate_against_openapi_schema(result, "/settings", "patch", "200") @openapi_test_function("/user_uploads:post") @@ -1488,8 +1473,7 @@ def test_users(client: Client, owner_client: Client) -> None: get_user_by_email(client) get_subscription_status(client) get_profile(client) - update_notification_settings(client) - update_display_settings(client) + update_settings(client) upload_file(client) get_attachments(client) set_typing_status(client) @@ -1520,7 +1504,6 @@ def test_streams(client: Client, nonadmin_client: Client) -> None: remove_subscriptions(client) toggle_mute_topic(client) update_subscription_settings(client) - update_notification_settings(client) get_stream_topics(client, 1) archive_stream(client, stream_id) diff --git a/zerver/openapi/zulip.yaml b/zerver/openapi/zulip.yaml index cb4384f08f..799db38b58 100644 --- a/zerver/openapi/zulip.yaml +++ b/zerver/openapi/zulip.yaml @@ -279,7 +279,7 @@ paths: - type: object description: | Event sent to a user's clients when that user's [notification - settings](/api/update-notification-settings) have changed. + settings](/api/update-settings) have changed. properties: id: $ref: "#/components/schemas/EventIdSchema" @@ -8423,152 +8423,152 @@ paths: Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. enable_digest_emails: type: boolean description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. enable_login_emails: type: boolean description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. enable_marketing_emails: type: boolean description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. enable_offline_email_notifications: type: boolean description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. enable_offline_push_notifications: type: boolean description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. enable_online_push_notifications: type: boolean description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. enable_sounds: type: boolean description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. enable_stream_desktop_notifications: type: boolean description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. enable_stream_email_notifications: type: boolean description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. enable_stream_push_notifications: type: boolean description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. enable_stream_audible_notifications: type: boolean description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. wildcard_mentions_notify: type: boolean description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. message_content_in_email_notifications: type: boolean description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. notification_sound: type: string description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. pm_content_in_desktop_notifications: type: boolean description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. desktop_icon_count_display: type: integer description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. realm_name_in_notifications: type: boolean description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. presence_enabled: type: boolean description: | Present if `update_global_notifications` is present in `fetch_event_types`. The current value of this global notification setting for the user. - See [update-notification-settings](/api/update-notification-settings) - for details on notification settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. available_notification_sounds: type: array items: @@ -8586,8 +8586,8 @@ paths: The color scheme selected by the user. - See [PATCH /settings/display](/api/update-display-settings) - for api details on display settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. default_language: type: string description: | @@ -8595,8 +8595,8 @@ paths: The default language chosen by the user. - See [PATCH /settings/display](/api/update-display-settings) - for api details on display settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. demote_inactive_streams: type: integer description: | @@ -8604,8 +8604,8 @@ paths: Whether the user has chosen to demote inactive streams. - See [PATCH /settings/display](/api/update-display-settings) - for api details on display settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. dense_mode: type: boolean description: | @@ -8614,8 +8614,8 @@ paths: Whether the user has switched on dense mode. Dense mode is an experimental feature that is only available in development environments. - See [PATCH /settings/display](/api/update-display-settings) - for api details on display settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. emojiset: type: string description: | @@ -8623,8 +8623,8 @@ paths: The name of the emojiset that the user has chosen. - See [PATCH /settings/display](/api/update-display-settings) - for api details on display settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. fluid_layout_width: type: boolean description: | @@ -8632,8 +8632,8 @@ paths: Whether the user has chosen for the layout width to be fluid. - See [PATCH /settings/display](/api/update-display-settings) - for api details on display settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. default_view: type: string description: | @@ -8644,8 +8644,8 @@ paths: Currently supported values are `all_messages` and `recent_topics`. - See [PATCH /settings/display](/api/update-display-settings) - for api details on display settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. high_contrast_mode: type: boolean description: | @@ -8653,8 +8653,8 @@ paths: Whether has switched on high contrast mode. - See [PATCH /settings/display](/api/update-display-settings) - for api details on display settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. left_side_userlist: type: boolean description: | @@ -8664,8 +8664,8 @@ paths: on the left side of the screen (for desktop app and web app) in narrow windows. - See [PATCH /settings/display](/api/update-display-settings) - for api details on display settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. starred_message_counts: type: boolean description: | @@ -8674,8 +8674,8 @@ paths: Whether the user has chosen the number of starred messages to be displayed similar to unread counts. - See [PATCH /settings/display](/api/update-display-settings) - for api details on display settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. timezone: type: string description: | @@ -8684,8 +8684,8 @@ paths: The timezone configured for the user. This is used primarily to display the user's timezone to other users. - See [PATCH /settings/display](/api/update-display-settings) - for api details on display settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. translate_emoticons: type: boolean description: | @@ -8694,8 +8694,8 @@ paths: Whether the user has chosen for emoticons to be translated into emoji in the Zulip compose box. - See [PATCH /settings/display](/api/update-display-settings) - for api details on display settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. twenty_four_hour_time: type: boolean description: | @@ -8704,8 +8704,8 @@ paths: Whether the user has chosen a twenty four hour time display (true) or a twelve hour one (false). - See [PATCH /settings/display](/api/update-display-settings) - for api details on display settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. emojiset_choices: description: | Present if `update_display_settings` is present in `fetch_event_types`. @@ -8716,8 +8716,8 @@ paths: Only relevant to clients with configuration UI for choosing an emojiset; the currently selected emojiset is available in the `emojiset` key. - See [PATCH /settings/display](/api/update-display-settings) - for api details on display settings. + See [PATCH /settings](/api/update-settings) for details on + the meaning of this setting. type: array items: type: object @@ -9926,263 +9926,32 @@ paths: }, ], } - /settings/notifications: + /settings: patch: - operationId: update-notification-settings - summary: Update notification settings + operationId: update-settings + summary: Update settings tags: ["users"] description: | - This endpoint is used to edit the user's global notification settings. - See [this endpoint](/api/update-subscription-settings) for - per-stream notification settings. + This endpoint is used to edit the current user's settings. - `PATCH {{ api_url }}/v1/settings/notifications` - x-curl-examples-parameters: - oneOf: - - type: include - parameters: - enum: - - enable_offline_push_notifications - - enable_online_push_notifications - parameters: - - name: enable_stream_desktop_notifications - in: query - description: | - Enable visual desktop notifications for stream messages. - schema: - type: boolean - example: true - - name: enable_stream_email_notifications - in: query - description: | - Enable email notifications for stream messages. - schema: - type: boolean - example: true - - name: enable_stream_push_notifications - in: query - description: | - Enable mobile notifications for stream messages. - schema: - type: boolean - example: true - - name: enable_stream_audible_notifications - in: query - description: | - Enable audible desktop notifications for stream messages. - schema: - type: boolean - example: true - - name: notification_sound - in: query - description: | - Notification sound name. + `PATCH {{ api_url }}/v1/settings` - **Changes**: Removed unnecessary JSON-encoding of parameter in Zulip 4.0 (feature level 63). - schema: - type: string - example: ding - - name: enable_desktop_notifications - in: query - description: | - Enable visual desktop notifications for private messages and @-mentions. - schema: - type: boolean - example: true - - name: enable_sounds - in: query - description: | - Enable audible desktop notifications for private messages and - @-mentions. - schema: - type: boolean - example: true - - name: enable_offline_email_notifications - in: query - description: | - Enable email notifications for private messages and @-mentions received - when the user is offline. - schema: - type: boolean - example: true - - name: enable_offline_push_notifications - in: query - description: | - Enable mobile notification for private messages and @-mentions received - when the user is offline. - schema: - type: boolean - example: true - - name: enable_online_push_notifications - in: query - description: | - Enable mobile notification for private messages and @-mentions received - when the user is online. - schema: - type: boolean - example: true - - name: enable_digest_emails - in: query - description: | - Enable digest emails when the user is away. - schema: - type: boolean - example: true - - name: enable_marketing_emails - in: query - description: | - Enable marketing emails. Has no function outside Zulip Cloud. - schema: - type: boolean - example: true - - name: enable_login_emails - in: query - description: | - Enable email notifications for new logins to account. - schema: - type: boolean - example: true - - name: message_content_in_email_notifications - in: query - description: | - Include the message's content in email notifications for new messages. - schema: - type: boolean - example: true - - name: pm_content_in_desktop_notifications - in: query - description: | - Include content of private messages in desktop notifications. - schema: - type: boolean - example: true - - name: wildcard_mentions_notify - in: query - description: | - Whether wildcard mentions (E.g. @**all**) should send notifications - like a personal mention. - schema: - type: boolean - example: true - - name: desktop_icon_count_display - in: query - description: | - Unread count summary (appears in desktop sidebar and browser tab) + **Changes**: Prior to Zulip 5.0 (feature level 80), this + endpoint only supported the `full_name`, `email`, + `old_password`, and `new_password` parameters. Notification + settings were managed by `PATCH /settings/notifications`, and + all other settings by `PATCH /settings/display`. The feature level + 80 migration to merge these endpoints did not change how request + parameters are encoded. Note, however, that it did change the + handling of any invalid parameters present in a request to change + notification or display settings, since the merged endpoint uses + the new response format that was introduced for `/settings` in + Zulip 5.0 (feature level 78). - * 1 - All unreads - * 2 - Private messages and mentions - * 3 - None - content: - application/json: - schema: - type: integer - enum: - - 1 - - 2 - - 3 - example: 1 - - name: realm_name_in_notifications - in: query - description: | - Include organization name in subject of message notification emails. - schema: - type: boolean - example: true - - name: presence_enabled - in: query - description: | - Display the presence status to other users when online. - schema: - type: boolean - example: true - x-response-description: | - The server will return the settings that have been changed after the request, - with their new value. Please note that this doesn't necessarily mean that it - will return all the settings passed as parameters in the request, but only - those ones that were different than the already existing setting. - responses: - "200": - description: Success. - content: - application/json: - schema: - allOf: - - $ref: "#/components/schemas/JsonSuccessBase" - - $ref: "#/components/schemas/SuccessDescription" - - additionalProperties: false - properties: - result: {} - msg: {} - enable_desktop_notifications: - type: boolean - description: | - The setting for `enable_desktop_notifications`, if it was changed in - this request. - enable_digest_emails: - type: boolean - description: | - The setting for `enable_digest_emails`, if it was changed in this - request. - enable_marketing_emails: - type: boolean - description: | - The setting for `enable_marketing_emails`, if it was changed in this - request. - enable_offline_email_notifications: - type: boolean - description: | - The setting for `enable_offline_email_notifications`, if it was changed - in this request. - enable_offline_push_notifications: - type: boolean - description: | - The setting for `enable_offline_push_notifications`, if it was changed - in this request. - enable_online_push_notifications: - type: boolean - description: | - The setting for `enable_online_push_notifications`, if it was changed in - this request. - enable_sounds: - type: boolean - description: | - The setting for `enable_sounds`, if it was changed in this request. - enable_stream_email_notifications: - type: boolean - description: | - The setting for `enable_stream_email_notifications`, if it was changed - in this request. - enable_stream_push_notifications: - type: boolean - description: | - The setting for `enable_stream_push_notifications`, if it was changed in - this request. - enable_stream_audible_notifications: - type: boolean - description: | - The setting for `enable_stream_audible_notifications`, if it was changed - in this request. - message_content_in_email_notifications: - type: boolean - description: | - The setting for `message_content_in_email_notifications`, if it was - changed in this request. - example: - { - "enable_offline_push_notifications": true, - "enable_online_push_notifications": true, - "msg": "", - "result": "success", - } - /settings/display: - patch: - operationId: update-display-settings - summary: Update display settings - tags: ["users"] - description: | - This endpoint is used to edit the current user's user interface settings. - - `PATCH {{ api_url }}/v1/settings/display` + The `/settings/display` and `/settings/notifications` + endpoints are now deprecated aliases for this endpoint for + backwards-compatibility, and will be removed once clients have + migrated to use this endpoint. x-curl-examples-parameters: oneOf: - type: include @@ -10190,18 +9959,48 @@ paths: enum: - left_side_userlist - emojiset - x-response-description: | - #### Return values - - The server will return the settings that have been changed after the request, - with their new value. Please note that this doesn't necessarily mean that it - will return all the settings passed as parameters in the request, but only - those ones that were different from the already existing setting. parameters: + - name: full_name + in: query + description: | + A new display name for the user. + schema: + type: string + example: NewName + - name: email + in: query + description: | + Asks the server to initiate a confirmation sequence to change the user's email + address to the indicated value. The user will need to demonstrate control of the + new email address by clicking a confirmation link sent to that address. + schema: + type: string + example: newname@example.com + - name: old_password + in: query + description: | + The user's old Zulip password (or LDAP password, if LDAP authentication is in use). + + Required only when sending the `new_password` parameter. + schema: + type: string + example: old12345 + - name: new_password + in: query + description: | + The user's new Zulip password (or LDAP password, if LDAP authentication is in use). + + The `old_password` parameter must be included in the request. + schema: + type: string + example: new12345 - name: twenty_four_hour_time in: query description: | Whether time should be [displayed in 24-hour notation](/help/change-the-time-format). + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/display` endpoint. schema: type: boolean example: true @@ -10210,6 +10009,9 @@ paths: description: | This setting has no effect at present. It is reserved for use in controlling the default font size in Zulip. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/display` endpoint. schema: type: boolean example: true @@ -10218,6 +10020,9 @@ paths: description: | Whether clients should display the [number of starred messages](/help/star-a-message#display-the-number-of-starred-messages). + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/display` endpoint. schema: type: boolean example: true @@ -10226,6 +10031,9 @@ paths: description: | Whether to use the [maximum available screen width](/help/enable-full-width-display) for the web app's center panel (message feed, recent topics) on wide screens. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/display` endpoint. schema: type: boolean example: true @@ -10234,6 +10042,9 @@ paths: description: | This setting is reserved for use to control variations in Zulip's design to help visually impaired users. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/display` endpoint. schema: type: boolean example: true @@ -10248,6 +10059,9 @@ paths: Automatic detection is implementing using the standard `prefers-color-scheme` media query. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/display` endpoint. content: application/json: schema: @@ -10262,6 +10076,9 @@ paths: description: | Whether to [translate emoticons to emoji](/help/enable-emoticon-translations) in messages the user sends. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/display` endpoint. schema: type: boolean example: true @@ -10275,7 +10092,10 @@ paths: The value needs to be a standard language code that the Zulip server has translation data for; for example, `"en"` for English or `"de"` for German. - **Changes**: Removed unnecessary JSON-encoding of parameter in Zulip 4.0 (feature level 63). + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/display` endpoint. + + Unnecessary JSON-encoding of this parameter was removed in Zulip 4.0 (feature level 63). schema: type: string example: en @@ -10288,7 +10108,10 @@ paths: * "recent_topics" - Recent topics view * "all_messages" - All messages view - **Changes**: Removed unnecessary JSON-encoding of parameter in Zulip 4.0 (feature level 64). + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/display` endpoint. + + Unnecessary JSON-encoding of this parameter was removed in Zulip 4.0 (feature level 64). schema: type: string example: all_messages @@ -10298,6 +10121,9 @@ paths: Whether the users list on left sidebar in narrow windows. This feature is not heavily used and is likely to be reworked. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/display` endpoint. schema: type: boolean example: true @@ -10312,7 +10138,10 @@ paths: * "twitter" - Twitter * "text" - Plain text - **Changes**: Removed unnecessary JSON-encoding of parameter in Zulip 4.0 (feature level 64). + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/display` endpoint. + + Unnecessary JSON-encoding of this parameter was removed in Zulip 4.0 (feature level 64). schema: type: string example: "google" @@ -10324,6 +10153,9 @@ paths: * 1 - Automatic * 2 - Always * 3 - Never + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/display` endpoint. content: application/json: schema: @@ -10341,10 +10173,220 @@ paths: Timezone values supported by the server are served at [/static/generated/timezones.json](/static/generated/timezones.json). - **Changes**: Removed unnecessary JSON-encoding of parameter in Zulip 4.0 (feature level 64). + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/display` endpoint. + + Unnecessary JSON-encoding of this parameter was removed in Zulip 4.0 (feature level 64). schema: type: string example: "Asia/Kolkata" + - name: enable_stream_desktop_notifications + in: query + description: | + Enable visual desktop notifications for stream messages. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + schema: + type: boolean + example: true + - name: enable_stream_email_notifications + in: query + description: | + Enable email notifications for stream messages. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + schema: + type: boolean + example: true + - name: enable_stream_push_notifications + in: query + description: | + Enable mobile notifications for stream messages. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + schema: + type: boolean + example: true + - name: enable_stream_audible_notifications + in: query + description: | + Enable audible desktop notifications for stream messages. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + schema: + type: boolean + example: true + - name: notification_sound + in: query + description: | + Notification sound name. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + + Unnecessary JSON-encoding of this parameter was removed in Zulip 4.0 (feature level 63). + schema: + type: string + example: ding + - name: enable_desktop_notifications + in: query + description: | + Enable visual desktop notifications for private messages and @-mentions. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + schema: + type: boolean + example: true + - name: enable_sounds + in: query + description: | + Enable audible desktop notifications for private messages and + @-mentions. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + schema: + type: boolean + example: true + - name: enable_offline_email_notifications + in: query + description: | + Enable email notifications for private messages and @-mentions received + when the user is offline. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + schema: + type: boolean + example: true + - name: enable_offline_push_notifications + in: query + description: | + Enable mobile notification for private messages and @-mentions received + when the user is offline. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + schema: + type: boolean + example: true + - name: enable_online_push_notifications + in: query + description: | + Enable mobile notification for private messages and @-mentions received + when the user is online. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + schema: + type: boolean + example: true + - name: enable_digest_emails + in: query + description: | + Enable digest emails when the user is away. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + schema: + type: boolean + example: true + - name: enable_marketing_emails + in: query + description: | + Enable marketing emails. Has no function outside Zulip Cloud. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + schema: + type: boolean + example: true + - name: enable_login_emails + in: query + description: | + Enable email notifications for new logins to account. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + schema: + type: boolean + example: true + - name: message_content_in_email_notifications + in: query + description: | + Include the message's content in email notifications for new messages. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + schema: + type: boolean + example: true + - name: pm_content_in_desktop_notifications + in: query + description: | + Include content of private messages in desktop notifications. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + schema: + type: boolean + example: true + - name: wildcard_mentions_notify + in: query + description: | + Whether wildcard mentions (E.g. @**all**) should send notifications + like a personal mention. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + schema: + type: boolean + example: true + - name: desktop_icon_count_display + in: query + description: | + Unread count summary (appears in desktop sidebar and browser tab) + + * 1 - All unreads + * 2 - Private messages and mentions + * 3 - None + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + content: + application/json: + schema: + type: integer + enum: + - 1 + - 2 + - 3 + example: 1 + - name: realm_name_in_notifications + in: query + description: | + Include organization name in subject of message notification emails. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + schema: + type: boolean + example: true + - name: presence_enabled + in: query + description: | + Display the presence status to other users when online. + + **Changes**: Before Zulip 5.0 (feature level 80), this setting was managed by + the `PATCH /settings/notifications` endpoint. + schema: + type: boolean + example: true responses: "200": description: Success @@ -10358,69 +10400,32 @@ paths: properties: result: {} msg: {} - twenty_four_hour_time: - type: boolean + ignored_parameters_unsupported: + type: array + items: + type: string description: | - The setting for `twenty_four_hour_time`, if it was changed in this request. - dense_mode: - type: boolean - description: | - The setting for `dense_mode`, if it was changed in this request. - This setting is however reserved for future, and can not be - modified in production environment. - starred_message_counts: - type: boolean - description: | - The setting for `starred_message_counts`, if it was changed - in this request. - fluid_layout_width: - type: boolean - description: | - The setting for `fluid_layout_width`, if it was changed - in this request. - high_contrast_mode: - type: boolean - description: | - The setting for `high_contrast_mode`, if it was changed in - this request. - This setting is however reserved for future, and can not be - modified in production environment. - color_scheme: - type: integer - description: | - The numerical key corresponding to new `color_scheme` if it was changed in this request. - translate_emoticons: - type: boolean - description: | - The setting for `translate_emoticons`, if it was changed in this request. - default_language: - type: string - description: | - The language code corresponding to new `default_language` if it was changed in this request. - default_view: - type: string - description: | - The new setting for `default_view`, if it was changed in this request. - left_side_userlist: - type: boolean - description: | - The setting for `left_side_userlist`, if it was changed in this request. - emojiset: - type: string - description: | - The string identifier corresponding to new `emojiset` if it was changed in this request. - demote_inactive_streams: - type: integer - description: | - The numerical key corresponding to new `demote_inactive_streams` setting if it was changed in this request. - timezone: - type: string - description: | - The setting for `timezone`, if it was changed in this request. + This field lists any parameters sent in the request that are not + supported by the endpoint. While this can be expected, e.g. when sending + both current and legacy names for a parameter to a Zulip server of + unknown version, this often indicates a bug in the client + implementation or an attempt to configure a new feature, while + connected to an older Zulip server that does not support the feature. + + **Changes**: New in Zulip 5.0 (feature level 78). Previously, + the `/settings` endpoint indicated which parameters it had + processed by including in the response object `"key": value` + entries for values successfully changed by the request. + + The `/settings/notifications` and `/settings/display` endpoints + also had this behavior before they became aliases of `/settings` + in Zulip 5.0 (feature level 80). + + Before those changes, request parameters that were not supported + or were unchanged were silently ignored. example: { - "emojiset": "google", - "left_side_userlist": true, + "ignored_parameters_unsupported": ["name", "password"], "msg": "", "result": "success", } diff --git a/zerver/tests/test_openapi.py b/zerver/tests/test_openapi.py index 7f06ba83a9..c06d2c2061 100644 --- a/zerver/tests/test_openapi.py +++ b/zerver/tests/test_openapi.py @@ -253,7 +253,6 @@ class OpenAPIArgumentsTest(ZulipTestCase): "/users/me/android_gcm_reg_id", "/users/me/apns_device_token", #### These personal settings endpoints have modest value to document: - "/settings", "/users/me/avatar", "/users/me/api_key/regenerate", # Much more valuable would be an org admin bulk-upload feature. diff --git a/zerver/tests/test_settings.py b/zerver/tests/test_settings.py index 4357afac01..0c77e484d8 100644 --- a/zerver/tests/test_settings.py +++ b/zerver/tests/test_settings.py @@ -158,12 +158,10 @@ class ChangeSettingsTest(ZulipTestCase): # # TODO: Make this work more like do_test_realm_update_api if notification_setting != "notification_sound": - self.check_for_toggle_param_patch( - "/json/settings/notifications", notification_setting - ) + self.check_for_toggle_param_patch("/json/settings", notification_setting) def test_change_notification_sound(self) -> None: - pattern = "/json/settings/notifications" + pattern = "/json/settings" param = "notification_sound" user_profile = self.example_user("hamlet") self.login_user(user_profile) @@ -191,7 +189,7 @@ class ChangeSettingsTest(ZulipTestCase): s for s in UserProfile.property_types if UserProfile.property_types[s] is bool ) for display_setting in boolean_settings: - self.check_for_toggle_param_patch("/json/settings/display", display_setting) + self.check_for_toggle_param_patch("/json/settings", display_setting) def test_enter_sends_setting(self) -> None: self.check_for_toggle_param("/json/users/me/enter-sends", "enter_sends") @@ -344,7 +342,7 @@ class ChangeSettingsTest(ZulipTestCase): else: data = {setting_name: orjson.dumps(test_value).decode()} - result = self.client_patch("/json/settings/display", data) + result = self.client_patch("/json/settings", data) self.assert_json_success(result) user_profile = self.example_user("hamlet") self.assertEqual(getattr(user_profile, setting_name), test_value) @@ -356,7 +354,7 @@ class ChangeSettingsTest(ZulipTestCase): else: data = {setting_name: orjson.dumps(invalid_value).decode()} - result = self.client_patch("/json/settings/display", data) + result = self.client_patch("/json/settings", data) # the json error for multiple word setting names (ex: default_language) # displays as 'Invalid language'. Using setting_name.split('_') to format. self.assert_json_error(result, f"Invalid {setting_name}") @@ -376,7 +374,7 @@ class ChangeSettingsTest(ZulipTestCase): def do_change_emojiset(self, emojiset: str) -> HttpResponse: self.login("hamlet") data = {"emojiset": emojiset} - result = self.client_patch("/json/settings/display", data) + result = self.client_patch("/json/settings", data) return result def test_emojiset(self) -> None: @@ -415,6 +413,35 @@ class ChangeSettingsTest(ZulipTestCase): self.assertIn("ignored_parameters_unsupported", result) self.assertEqual(result["ignored_parameters_unsupported"], ["invalid_setting"]) + def test_changing_setting_using_display_setting_endpoint(self) -> None: + """ + This test is just for adding coverage for `/settings/display` endpoint which is + now depreceated. + """ + self.login("hamlet") + + result = self.client_patch( + "/json/settings/display", dict(color_scheme=UserProfile.COLOR_SCHEME_NIGHT) + ) + self.assert_json_success(result) + hamlet = self.example_user("hamlet") + self.assertEqual(hamlet.color_scheme, UserProfile.COLOR_SCHEME_NIGHT) + + def test_changing_setting_using_notification_setting_endpoint(self) -> None: + """ + This test is just for adding coverage for `/settings/notifications` endpoint which is + now depreceated. + """ + self.login("hamlet") + + result = self.client_patch( + "/json/settings/notifications", + dict(enable_stream_desktop_notifications=orjson.dumps(True).decode()), + ) + self.assert_json_success(result) + hamlet = self.example_user("hamlet") + self.assertEqual(hamlet.enable_stream_desktop_notifications, True) + class UserChangesTest(ZulipTestCase): def test_update_api_key(self) -> None: diff --git a/zerver/views/user_settings.py b/zerver/views/user_settings.py index d362f756d7..2a4e313b07 100644 --- a/zerver/views/user_settings.py +++ b/zerver/views/user_settings.py @@ -87,6 +87,10 @@ def confirm_email_change(request: HttpRequest, confirmation_key: str) -> HttpRes return render(request, "confirmation/confirm_email_change.html", context=ctx) +emojiset_choices = {emojiset["key"] for emojiset in UserProfile.emojiset_choices()} +default_view_options = ["recent_topics", "all_messages"] + + @human_users_only @has_request_variables def json_change_settings( @@ -96,7 +100,74 @@ def json_change_settings( email: str = REQ(default=""), old_password: str = REQ(default=""), new_password: str = REQ(default=""), + twenty_four_hour_time: Optional[bool] = REQ(json_validator=check_bool, default=None), + dense_mode: Optional[bool] = REQ(json_validator=check_bool, default=None), + starred_message_counts: Optional[bool] = REQ(json_validator=check_bool, default=None), + fluid_layout_width: Optional[bool] = REQ(json_validator=check_bool, default=None), + high_contrast_mode: Optional[bool] = REQ(json_validator=check_bool, default=None), + color_scheme: Optional[int] = REQ( + json_validator=check_int_in(UserProfile.COLOR_SCHEME_CHOICES), default=None + ), + translate_emoticons: Optional[bool] = REQ(json_validator=check_bool, default=None), + default_language: Optional[str] = REQ(default=None), + default_view: Optional[str] = REQ( + str_validator=check_string_in(default_view_options), default=None + ), + left_side_userlist: Optional[bool] = REQ(json_validator=check_bool, default=None), + emojiset: Optional[str] = REQ(str_validator=check_string_in(emojiset_choices), default=None), + demote_inactive_streams: Optional[int] = REQ( + json_validator=check_int_in(UserProfile.DEMOTE_STREAMS_CHOICES), default=None + ), + timezone: Optional[str] = REQ( + str_validator=check_string_in(pytz.all_timezones_set), default=None + ), + enable_stream_desktop_notifications: Optional[bool] = REQ( + json_validator=check_bool, default=None + ), + enable_stream_email_notifications: Optional[bool] = REQ( + json_validator=check_bool, default=None + ), + enable_stream_push_notifications: Optional[bool] = REQ(json_validator=check_bool, default=None), + enable_stream_audible_notifications: Optional[bool] = REQ( + json_validator=check_bool, default=None + ), + wildcard_mentions_notify: Optional[bool] = REQ(json_validator=check_bool, default=None), + notification_sound: Optional[str] = REQ(default=None), + enable_desktop_notifications: Optional[bool] = REQ(json_validator=check_bool, default=None), + enable_sounds: Optional[bool] = REQ(json_validator=check_bool, default=None), + enable_offline_email_notifications: Optional[bool] = REQ( + json_validator=check_bool, default=None + ), + enable_offline_push_notifications: Optional[bool] = REQ( + json_validator=check_bool, default=None + ), + enable_online_push_notifications: Optional[bool] = REQ(json_validator=check_bool, default=None), + enable_digest_emails: Optional[bool] = REQ(json_validator=check_bool, default=None), + enable_login_emails: Optional[bool] = REQ(json_validator=check_bool, default=None), + enable_marketing_emails: Optional[bool] = REQ(json_validator=check_bool, default=None), + message_content_in_email_notifications: Optional[bool] = REQ( + json_validator=check_bool, default=None + ), + pm_content_in_desktop_notifications: Optional[bool] = REQ( + json_validator=check_bool, default=None + ), + desktop_icon_count_display: Optional[int] = REQ(json_validator=check_int, default=None), + realm_name_in_notifications: Optional[bool] = REQ(json_validator=check_bool, default=None), + presence_enabled: Optional[bool] = REQ(json_validator=check_bool, default=None), ) -> HttpResponse: + # We can't use REQ for this widget because + # get_available_language_codes requires provisioning to be + # complete. + if default_language is not None and default_language not in get_available_language_codes(): + raise JsonableError(_("Invalid default_language")) + + if ( + notification_sound is not None + and notification_sound not in get_available_notification_sounds() + and notification_sound != "none" + ): + raise JsonableError(_("Invalid notification sound '{}'").format(notification_sound)) + if new_password != "": return_data: Dict[str, Any] = {} if email_belongs_to_ldap(user_profile.realm, user_profile.delivery_email): @@ -172,6 +243,23 @@ def json_change_settings( # Note that check_change_full_name strips the passed name automatically check_change_full_name(user_profile, full_name, user_profile) + # Loop over user_profile.property_types + request_settings = {k: v for k, v in list(locals().items()) if k in user_profile.property_types} + for k, v in list(request_settings.items()): + if v is not None and getattr(user_profile, k) != v: + do_set_user_display_setting(user_profile, k, v) + + req_vars = { + k: v for k, v in list(locals().items()) if k in user_profile.notification_setting_types + } + + for k, v in list(req_vars.items()): + if v is not None and getattr(user_profile, k) != v: + do_change_notification_settings(user_profile, k, v, acting_user=user_profile) + + if timezone is not None and user_profile.timezone != timezone: + do_set_user_display_setting(user_profile, "timezone", timezone) + # TODO: Do this more generally. from zerver.lib.request import get_request_notes @@ -186,120 +274,6 @@ def json_change_settings( return json_success(result) -emojiset_choices = {emojiset["key"] for emojiset in UserProfile.emojiset_choices()} -default_view_options = ["recent_topics", "all_messages"] - - -@human_users_only -@has_request_variables -def update_display_settings_backend( - request: HttpRequest, - user_profile: UserProfile, - twenty_four_hour_time: Optional[bool] = REQ(json_validator=check_bool, default=None), - dense_mode: Optional[bool] = REQ(json_validator=check_bool, default=None), - starred_message_counts: Optional[bool] = REQ(json_validator=check_bool, default=None), - fluid_layout_width: Optional[bool] = REQ(json_validator=check_bool, default=None), - high_contrast_mode: Optional[bool] = REQ(json_validator=check_bool, default=None), - color_scheme: Optional[int] = REQ( - json_validator=check_int_in(UserProfile.COLOR_SCHEME_CHOICES), default=None - ), - translate_emoticons: Optional[bool] = REQ(json_validator=check_bool, default=None), - default_language: Optional[str] = REQ(default=None), - default_view: Optional[str] = REQ( - str_validator=check_string_in(default_view_options), default=None - ), - left_side_userlist: Optional[bool] = REQ(json_validator=check_bool, default=None), - emojiset: Optional[str] = REQ(str_validator=check_string_in(emojiset_choices), default=None), - demote_inactive_streams: Optional[int] = REQ( - json_validator=check_int_in(UserProfile.DEMOTE_STREAMS_CHOICES), default=None - ), - timezone: Optional[str] = REQ( - str_validator=check_string_in(pytz.all_timezones_set), default=None - ), -) -> HttpResponse: - - # We can't use REQ for this widget because - # get_available_language_codes requires provisioning to be - # complete. - if default_language is not None and default_language not in get_available_language_codes(): - raise JsonableError(_("Invalid default_language")) - - request_settings = {k: v for k, v in list(locals().items()) if k in user_profile.property_types} - result: Dict[str, Any] = {} - for k, v in list(request_settings.items()): - if v is not None and getattr(user_profile, k) != v: - do_set_user_display_setting(user_profile, k, v) - result[k] = v - - if timezone is not None and user_profile.timezone != timezone: - do_set_user_display_setting(user_profile, "timezone", timezone) - result["timezone"] = timezone - - return json_success(result) - - -@human_users_only -@has_request_variables -def json_change_notify_settings( - request: HttpRequest, - user_profile: UserProfile, - enable_stream_desktop_notifications: Optional[bool] = REQ( - json_validator=check_bool, default=None - ), - enable_stream_email_notifications: Optional[bool] = REQ( - json_validator=check_bool, default=None - ), - enable_stream_push_notifications: Optional[bool] = REQ(json_validator=check_bool, default=None), - enable_stream_audible_notifications: Optional[bool] = REQ( - json_validator=check_bool, default=None - ), - wildcard_mentions_notify: Optional[bool] = REQ(json_validator=check_bool, default=None), - notification_sound: Optional[str] = REQ(default=None), - enable_desktop_notifications: Optional[bool] = REQ(json_validator=check_bool, default=None), - enable_sounds: Optional[bool] = REQ(json_validator=check_bool, default=None), - enable_offline_email_notifications: Optional[bool] = REQ( - json_validator=check_bool, default=None - ), - enable_offline_push_notifications: Optional[bool] = REQ( - json_validator=check_bool, default=None - ), - enable_online_push_notifications: Optional[bool] = REQ(json_validator=check_bool, default=None), - enable_digest_emails: Optional[bool] = REQ(json_validator=check_bool, default=None), - enable_login_emails: Optional[bool] = REQ(json_validator=check_bool, default=None), - enable_marketing_emails: Optional[bool] = REQ(json_validator=check_bool, default=None), - message_content_in_email_notifications: Optional[bool] = REQ( - json_validator=check_bool, default=None - ), - pm_content_in_desktop_notifications: Optional[bool] = REQ( - json_validator=check_bool, default=None - ), - desktop_icon_count_display: Optional[int] = REQ(json_validator=check_int, default=None), - realm_name_in_notifications: Optional[bool] = REQ(json_validator=check_bool, default=None), - presence_enabled: Optional[bool] = REQ(json_validator=check_bool, default=None), -) -> HttpResponse: - result = {} - - # Stream notification settings. - - if ( - notification_sound is not None - and notification_sound not in get_available_notification_sounds() - and notification_sound != "none" - ): - raise JsonableError(_("Invalid notification sound '{}'").format(notification_sound)) - - req_vars = { - k: v for k, v in list(locals().items()) if k in user_profile.notification_setting_types - } - - for k, v in list(req_vars.items()): - if v is not None and getattr(user_profile, k) != v: - do_change_notification_settings(user_profile, k, v, acting_user=user_profile) - result[k] = v - - return json_success(result) - - def set_avatar_backend(request: HttpRequest, user_profile: UserProfile) -> HttpResponse: if len(request.FILES) != 1: raise JsonableError(_("You must upload exactly one avatar.")) diff --git a/zproject/urls.py b/zproject/urls.py index 8c7f487707..54e20e17bd 100644 --- a/zproject/urls.py +++ b/zproject/urls.py @@ -182,11 +182,9 @@ from zerver.views.user_settings import ( change_enter_sends, confirm_email_change, delete_avatar_backend, - json_change_notify_settings, json_change_settings, regenerate_api_key, set_avatar_backend, - update_display_settings_backend, ) from zerver.views.users import ( add_bot_backend, @@ -414,8 +412,13 @@ v1_api_and_json_patterns = [ ), # settings -> zerver.views.user_settings rest_path("settings", PATCH=json_change_settings), - rest_path("settings/display", PATCH=update_display_settings_backend), - rest_path("settings/notifications", PATCH=json_change_notify_settings), + # These next two are legacy aliases for /settings, from before + # we merged the endpoints. They are documented in the `/json/settings` + # documentation, rather than having dedicated pages. + rest_path("settings/display", PATCH=(json_change_settings, {"intentionally_undocumented"})), + rest_path( + "settings/notifications", PATCH=(json_change_settings, {"intentionally_undocumented"}) + ), # users/me/alert_words -> zerver.views.alert_words rest_path( "users/me/alert_words",