Repository: zulip/zulip

Issue:
Add "Privacy and security" section to personal Settings menu 
Some personal settings are hard to find right now, and some settings pages have too many different kinds of settings. We should make the settings easier to navigate by splitting "Your account" into two sections:

1. **Profile** (1st on the list). We can try removing all the section headers and see if it's OK or confusing.
Settings (in order): Full name, Profile picture, "Deactivate account" button, everything currently under **Your account > Profile** (custom fields).

I'm not entirely sure about the "Deactivate account" button placement; we can play with it.

2. **Privacy and security** (2nd on the list) 
Settings (in order):
  a. **User settings**: Email, password, role
  b.  **Presence** (currently under **Notifications**)
  c.  **API key**




Discussion:
Hello @zulip/server-settings members, this issue was labeled with the "area: settings (user)" label, so you may want to check it out!

<!-- areaLabelAddition -->
@zulipbot claim

Resolving patch:
diff --git a/zerver/lib/markdown/help_relative_links.py b/zerver/lib/markdown/help_relative_links.py
--- a/zerver/lib/markdown/help_relative_links.py
+++ b/zerver/lib/markdown/help_relative_links.py
@@ -17,7 +17,7 @@
     # name is what the item is called in the gear menu: `Select **name**.`
     # link is used for relative links: `Select [name](link).`
     "manage-streams": ["Manage streams", "/#streams/subscribed"],
-    "settings": ["Settings", "/#settings/your-account"],
+    "settings": ["Settings", "/#settings/profile"],
     "manage-organization": ["Manage organization", "/#organization/organization-profile"],
     "integrations": ["Integrations", "/integrations"],
     "stats": ["Usage statistics", "/stats"],
diff --git a/zerver/lib/markdown/help_settings_links.py b/zerver/lib/markdown/help_settings_links.py
--- a/zerver/lib/markdown/help_settings_links.py
+++ b/zerver/lib/markdown/help_settings_links.py
@@ -16,7 +16,8 @@
     # breadcrumb to that setting to the name of its setting type, the setting
     # name as it appears in the user interface, and a relative link that can
     # be used to get to that setting
-    "your-account": ["Settings", "Your account", "/#settings/your-account"],
+    "profile": ["Settings", "Profile", "/#settings/profile"],
+    "account-and-privacy": ["Settings", "Account & privacy", "/#settings/account-and-privacy"],
     "display-settings": ["Settings", "Display settings", "/#settings/display-settings"],
     "notifications": ["Settings", "Notifications", "/#settings/notifications"],
     "your-bots": ["Settings", "Your bots", "/#settings/your-bots"],


Test patch:
diff --git a/frontend_tests/node_tests/browser_history.js b/frontend_tests/node_tests/browser_history.js
--- a/frontend_tests/node_tests/browser_history.js
+++ b/frontend_tests/node_tests/browser_history.js
@@ -22,7 +22,7 @@ function test(label, f) {
 }
 
 test("basics", () => {
-    const hash1 = "#settings/your-account";
+    const hash1 = "#settings/profile";
     const hash2 = "#narrow/is/private";
     browser_history.go_to_location(hash1);
     assert.equal(location.hash, hash1);
diff --git a/frontend_tests/node_tests/hash_util.js b/frontend_tests/node_tests/hash_util.js
--- a/frontend_tests/node_tests/hash_util.js
+++ b/frontend_tests/node_tests/hash_util.js
@@ -86,26 +86,26 @@ run_test("test_get_hash_category", () => {
     assert.deepEqual(hash_util.get_hash_category("#drafts"), "drafts");
     assert.deepEqual(hash_util.get_hash_category("invites"), "invites");
 
-    location.hash = "#settings/your-account";
+    location.hash = "#settings/profile";
     assert.deepEqual(hash_util.get_current_hash_category(), "settings");
 });
 
 run_test("test_get_hash_section", () => {
     assert.equal(hash_util.get_hash_section("streams/subscribed"), "subscribed");
-    assert.equal(hash_util.get_hash_section("#settings/your-account"), "your-account");
+    assert.equal(hash_util.get_hash_section("#settings/profile"), "profile");
 
     assert.equal(hash_util.get_hash_section("settings/10/general/"), "10");
 
     assert.equal(hash_util.get_hash_section("#drafts"), "");
     assert.equal(hash_util.get_hash_section(""), "");
 
-    location.hash = "#settings/your-account";
-    assert.deepEqual(hash_util.get_current_hash_section(), "your-account");
+    location.hash = "#settings/profile";
+    assert.deepEqual(hash_util.get_current_hash_section(), "profile");
 });
 
 run_test("build_reload_url", () => {
-    location.hash = "#settings/your-account";
-    assert.equal(hash_util.build_reload_url(), "+oldhash=settings%2Fyour-account");
+    location.hash = "#settings/profile";
+    assert.equal(hash_util.build_reload_url(), "+oldhash=settings%2Fprofile");
 
     location.hash = "#test";
     assert.equal(hash_util.build_reload_url(), "+oldhash=test");
diff --git a/frontend_tests/puppeteer_tests/settings.ts b/frontend_tests/puppeteer_tests/settings.ts
--- a/frontend_tests/puppeteer_tests/settings.ts
+++ b/frontend_tests/puppeteer_tests/settings.ts
@@ -27,7 +27,7 @@ async function open_settings(page: Page): Promise<void> {
     await page.waitForSelector(settings_selector, {visible: true});
     await page.click(settings_selector);
 
-    await page.waitForSelector("#settings_content .account-settings-form", {visible: true});
+    await page.waitForSelector("#settings_content .profile-settings-form", {visible: true});
     const page_url = await common.page_url_with_fragment(page);
     assert.ok(
         page_url.includes("/#settings/"),
@@ -36,23 +36,18 @@ async function open_settings(page: Page): Promise<void> {
 }
 
 async function test_change_full_name(page: Page): Promise<void> {
-    await page.click("#change_full_name");
-
-    const change_full_name_button_selector = "#change_full_name_button";
-    await page.waitForSelector(change_full_name_button_selector, {visible: true});
+    await page.click("#full_name");
 
     const full_name_input_selector = 'input[name="full_name"]';
-    await page.$eval(full_name_input_selector, (el) => {
-        (el as HTMLInputElement).value = "";
-    });
-    await page.waitForFunction(() => $(":focus").attr("id") === "change_full_name_modal");
-    await page.type(full_name_input_selector, "New name");
-    await page.click(change_full_name_button_selector);
-    await page.waitForFunction(() => $("#change_full_name").text().trim() === "New name");
-    await common.wait_for_modal_to_close(page);
+    await common.clear_and_type(page, full_name_input_selector, "New name");
+
+    await page.click("#settings_content .profile-settings-form");
+    await page.waitForSelector(".full-name-change-form .alert-success", {visible: true});
+    await page.waitForFunction(() => $("#full_name").val() === "New name");
 }
 
 async function test_change_password(page: Page): Promise<void> {
+    await page.click('[data-section="account-and-privacy"]');
     await page.click("#change_password");
 
     const change_password_button_selector = "#change_password_button";
diff --git a/zerver/tests/test_middleware.py b/zerver/tests/test_middleware.py
--- a/zerver/tests/test_middleware.py
+++ b/zerver/tests/test_middleware.py
@@ -98,7 +98,7 @@ def test_admonition_and_link(self) -> None:
         )
 
     def test_settings_tab(self) -> None:
-        # deactivate-your-account starts with {settings_tab|your-account}
+        # deactivate-your-account starts with {settings_tab|account-and-privacy}
         self.check_title_and_description(
             "/help/deactivate-your-account",
             "Deactivate your account (Zulip Help Center)",