From 0ca0182ab88cda01e9abc059e9e68e636c1437d5 Mon Sep 17 00:00:00 2001 From: David Ostrovsky Date: Fri, 14 Nov 2014 20:16:57 +0100 Subject: [PATCH] Store and load edit preferences to/from All-Users repository This change only handles edit screen user preferences, as discussed in this thread [1]. [1] https://groups.google.com/d/topic/repo-discuss/bX36w565m7c/discussion Change-Id: Icf87fcb6a9c8901d8f5d96a8ae81c78e24441e34 --- Documentation/rest-api-accounts.txt | 98 ++++++++++++++++++- Documentation/user-inline-edit.txt | 3 + .../rest/account/EditPreferencesIT.java | 82 ++++++++++++++++ .../client/EditPreferencesInfo.java | 40 ++++++++ .../server/account/GetEditPreferences.java | 67 +++++++++++++ .../gerrit/server/account/GetPreferences.java | 6 +- .../google/gerrit/server/account/Module.java | 2 + .../server/account/SetEditPreferences.java | 84 ++++++++++++++++ .../gerrit/server/account/SetPreferences.java | 8 +- .../gerrit/server/git/UserConfigSections.java | 27 +++++ 10 files changed, 409 insertions(+), 8 deletions(-) create mode 100644 gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/EditPreferencesIT.java create mode 100644 gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/account/GetEditPreferences.java create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/account/SetEditPreferences.java create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/git/UserConfigSections.java diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt index fb54601a09..d753d90cdf 100644 --- a/Documentation/rest-api-accounts.txt +++ b/Documentation/rest-api-accounts.txt @@ -1314,6 +1314,74 @@ link:#diff-preferences-info[DiffPreferencesInfo] entity. } ---- +[[get-edit-preferences]] +=== Get Edit Preferences +-- +'GET /accounts/link:#account-id[\{account-id\}]/preferences.edit' +-- + +Retrieves the edit preferences of a user. + +.Request +---- + GET /a/accounts/self/preferences.edit HTTP/1.0 +---- + +As result the edit preferences of the user are returned as a +link:#edit-preferences-info[EditPreferencesInfo] entity. + +.Response +---- + HTTP/1.1 200 OK + Content-Disposition: attachment + Content-Type: application/json;charset=UTF-8 + + )]}' + { + "theme": "ECLIPSE", + "tab_size": 4, + "line_length": 80, + "hide_top_menu": true, + "show_whitespace_errors": true, + "hide_line_numbers": true + } +---- + +[[set-edit-preferences]] +=== Set Edit Preferences +-- +'PUT /accounts/link:#account-id[\{account-id\}]/preferences.edit' +-- + +Sets the edit preferences of a user. + +The new edit preferences must be provided in the request body as a +link:#edit-preferences-info[EditPreferencesInfo] entity. + +.Request +---- + PUT /a/accounts/self/preferences.edit HTTP/1.0 + Content-Type: application/json;charset=UTF-8 + + { + "theme": "ECLIPSE", + "tab_size": 4, + "line_length": 80, + "hide_top_menu": true, + "show_tabs": true, + "show_whitespace_errors": true, + "syntax_highlighting": true, + "hide_line_numbers": true + } +---- + +The response is "`204 No Content`" + +.Response +---- + HTTP/1.1 204 No Content +---- + [[get-starred-changes]] === Get Starred Changes -- @@ -1611,7 +1679,7 @@ Whether uncommented files should be skipped on file switch. |`syntax_highlighting` |not set if `false`| Whether syntax highlighting should be enabled. |`hide_top_menu` |not set if `false`| -If true the top menu header and site header is hidden. +If true the top menu header and site header are hidden. |`auto_hide_diff_table_header` |not set if `false`| If true the diff table header is automatically hidden when scrolling down more than half of a page. @@ -1677,6 +1745,34 @@ True if the line numbers should be hidden. Number of spaces that should be used to display one tab. |=========================================== +[[edit-preferences-info]] +=== EditPreferencesInfo +The `EditPreferencesInfo` entity contains information about the edit +preferences of a user. + +[options="header",cols="1,^1,5"] +|=========================================== +|Field Name ||Description +|`theme` || +The CodeMirror theme. Currently only a subset of light and dark +CodeMirror themes are supported. Light themes `DEFAULT`, `ECLIPSE`, +`ELEGANT`, `NEAT`. Dark themes `MIDNIGHT`, `NIGHT`, `TWILIGHT`. +|`tab_size` || +Number of spaces that should be used to display one tab. +|`line_length` || +Number of characters that should be displayed per line. +|`hide_top_menu` |not set if `false`| +If true the top menu header and site header is hidden. +|`show_tabs` |not set if `false`| +Whether tabs should be shown. +|`show_whitespace_errors` |not set if `false`| +Whether whitespace errors should be shown. +|`syntax_highlighting` |not set if `false`| +Whether syntax highlighting should be enabled. +|`hide_line_numbers` |not set if `false`| +Whether line numbers should be hidden. +|=========================================== + [[email-info]] === EmailInfo The `EmailInfo` entity contains information about an email address of a diff --git a/Documentation/user-inline-edit.txt b/Documentation/user-inline-edit.txt index e262cb7831..66f079d83c 100644 --- a/Documentation/user-inline-edit.txt +++ b/Documentation/user-inline-edit.txt @@ -159,6 +159,9 @@ The inline editor uses settings decided from the user's diff preferences, but th preferences are only modifiable from the side-by-side diff screen. It should be possible to open the preferences also from within the editor. +* Support default configuration options for inline editor that an +administrator has set in `refs/users/default:preferences.config` file. + * Allow to rename files that are already contained in the change (from the file table). The same rename file dialog can be used with preselected and disabled original file name. diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/EditPreferencesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/EditPreferencesIT.java new file mode 100644 index 0000000000..de6e3aa017 --- /dev/null +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/EditPreferencesIT.java @@ -0,0 +1,82 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.acceptance.rest.account; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.acceptance.RestResponse; +import com.google.gerrit.extensions.client.EditPreferencesInfo; +import com.google.gerrit.extensions.client.Theme; + +import org.apache.http.HttpStatus; +import org.junit.Test; + +import java.io.IOException; + +public class EditPreferencesIT extends AbstractDaemonTest { + @Test + public void getSetEditPreferences() throws Exception { + String endPoint = "/accounts/" + admin.email + "/preferences.edit"; + RestResponse r = adminSession.get(endPoint); + assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); + EditPreferencesInfo out = getEditPrefInfo(r); + + assertThat(out.lineLength).isEqualTo(100); + assertThat(out.tabSize).isEqualTo(8); + assertThat(out.hideTopMenu).isNull(); + assertThat(out.showTabs).isTrue(); + assertThat(out.showWhitespaceErrors).isNull(); + assertThat(out.syntaxHighlighting).isTrue(); + assertThat(out.hideLineNumbers).isNull(); + assertThat(out.theme).isEqualTo(Theme.DEFAULT); + + // change some default values + out.lineLength = 80; + out.tabSize = 4; + out.hideTopMenu = true; + out.showTabs = false; + out.showWhitespaceErrors = true; + out.syntaxHighlighting = false; + out.hideLineNumbers = true; + out.theme = Theme.TWILIGHT; + + r = adminSession.put(endPoint, out); + assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT); + + r = adminSession.get(endPoint); + assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); + EditPreferencesInfo info = getEditPrefInfo(r); + assertEditPreferences(info, out); + } + + private EditPreferencesInfo getEditPrefInfo(RestResponse r) + throws IOException { + return newGson().fromJson(r.getReader(), + EditPreferencesInfo.class); + } + + private void assertEditPreferences(EditPreferencesInfo out, + EditPreferencesInfo in) { + assertThat(out.lineLength).isEqualTo(in.lineLength); + assertThat(out.tabSize).isEqualTo(in.tabSize); + assertThat(out.hideTopMenu).isEqualTo(in.hideTopMenu); + assertThat(out.showTabs).isNull(); + assertThat(out.showWhitespaceErrors).isEqualTo(in.showWhitespaceErrors); + assertThat(out.syntaxHighlighting).isNull(); + assertThat(out.hideLineNumbers).isEqualTo(in.hideLineNumbers); + assertThat(out.theme).isEqualTo(in.theme); + } +} diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java new file mode 100644 index 0000000000..e80d3d66bf --- /dev/null +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java @@ -0,0 +1,40 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.extensions.client; + +/* This class is stored in Git config file. */ +public class EditPreferencesInfo { + public Integer tabSize; + public Integer lineLength; + public Boolean hideTopMenu; + public Boolean showTabs; + public Boolean showWhitespaceErrors; + public Boolean syntaxHighlighting; + public Boolean hideLineNumbers; + public Theme theme; + + public static EditPreferencesInfo defaults() { + EditPreferencesInfo i = new EditPreferencesInfo(); + i.tabSize = 8; + i.lineLength = 100; + i.hideTopMenu = false; + i.showTabs = true; + i.showWhitespaceErrors = false; + i.syntaxHighlighting = true; + i.hideLineNumbers = false; + i.theme = Theme.DEFAULT; + return i; + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetEditPreferences.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetEditPreferences.java new file mode 100644 index 0000000000..e6e76445f2 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetEditPreferences.java @@ -0,0 +1,67 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.account; + +import static com.google.gerrit.server.config.ConfigUtil.loadSection; + +import com.google.gerrit.extensions.client.EditPreferencesInfo; +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.RestReadView; +import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.config.AllUsersName; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.git.UserConfigSections; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; + +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lib.Repository; + +import java.io.IOException; + +@Singleton +public class GetEditPreferences implements RestReadView { + private final Provider self; + private final AllUsersName allUsersName; + private final GitRepositoryManager gitMgr; + + @Inject + GetEditPreferences(Provider self, + AllUsersName allUsersName, + GitRepositoryManager gitMgr) { + this.self = self; + this.allUsersName = allUsersName; + this.gitMgr = gitMgr; + } + + @Override + public EditPreferencesInfo apply(AccountResource rsrc) throws AuthException, + IOException, ConfigInvalidException { + if (self.get() != rsrc.getUser() + && !self.get().getCapabilities().canModifyAccount()) { + throw new AuthException("restricted to members of Modify Accounts"); + } + + try (Repository git = gitMgr.openRepository(allUsersName)) { + VersionedAccountPreferences p = + VersionedAccountPreferences.forUser(rsrc.getUser().getAccountId()); + p.load(git); + + return loadSection(p.getConfig(), UserConfigSections.EDIT, null, + new EditPreferencesInfo(), EditPreferencesInfo.defaults()); + } + } +} \ No newline at end of file diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetPreferences.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetPreferences.java index 3df6837dd5..08bf83e06d 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetPreferences.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetPreferences.java @@ -30,6 +30,7 @@ import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.config.AllUsersName; import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.git.UserConfigSections; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; @@ -51,7 +52,6 @@ import java.util.Map; public class GetPreferences implements RestReadView { private static final Logger log = LoggerFactory.getLogger(GetPreferences.class); - public static final String MY = "my"; public static final String KEY_URL = "url"; public static final String KEY_TARGET = "target"; public static final String KEY_ID = "id"; @@ -165,7 +165,7 @@ public class GetPreferences implements RestReadView { private List my(VersionedAccountPreferences v) { List my = new ArrayList<>(); Config cfg = v.getConfig(); - for (String subsection : cfg.getSubsections(MY)) { + for (String subsection : cfg.getSubsections(UserConfigSections.MY)) { String url = my(cfg, subsection, KEY_URL, "#/"); String target = my(cfg, subsection, KEY_TARGET, url.startsWith("#") ? null : "_blank"); @@ -178,7 +178,7 @@ public class GetPreferences implements RestReadView { private static String my(Config cfg, String subsection, String key, String defaultValue) { - String val = cfg.getString(MY, subsection, key); + String val = cfg.getString(UserConfigSections.MY, subsection, key); return !Strings.isNullOrEmpty(val) ? val : defaultValue; } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java index 7cf1e377c7..54d4cc05e1 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java @@ -70,6 +70,8 @@ public class Module extends RestApiModule { put(ACCOUNT_KIND, "preferences").to(SetPreferences.class); get(ACCOUNT_KIND, "preferences.diff").to(GetDiffPreferences.class); put(ACCOUNT_KIND, "preferences.diff").to(SetDiffPreferences.class); + get(ACCOUNT_KIND, "preferences.edit").to(GetEditPreferences.class); + put(ACCOUNT_KIND, "preferences.edit").to(SetEditPreferences.class); get(CAPABILITY_KIND).to(GetCapabilities.CheckOne.class); child(ACCOUNT_KIND, "starred.changes").to(StarredChanges.class); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/SetEditPreferences.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/SetEditPreferences.java new file mode 100644 index 0000000000..2df1a77077 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/SetEditPreferences.java @@ -0,0 +1,84 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.account; + +import static com.google.gerrit.server.config.ConfigUtil.storeSection; + +import com.google.gerrit.extensions.client.EditPreferencesInfo; +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.Response; +import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.config.AllUsersName; +import com.google.gerrit.server.git.MetaDataUpdate; +import com.google.gerrit.server.git.UserConfigSections; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; + +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.errors.RepositoryNotFoundException; + +import java.io.IOException; + +@Singleton +public class SetEditPreferences implements + RestModifyView { + + private final Provider self; + private final Provider metaDataUpdateFactory; + private final AllUsersName allUsersName; + + @Inject + SetEditPreferences(Provider self, + Provider metaDataUpdateFactory, + AllUsersName allUsersName) { + this.self = self; + this.metaDataUpdateFactory = metaDataUpdateFactory; + this.allUsersName = allUsersName; + } + + @Override + public Response apply(AccountResource rsrc, EditPreferencesInfo in) + throws AuthException, BadRequestException, RepositoryNotFoundException, + IOException, ConfigInvalidException { + if (self.get() != rsrc.getUser() + && !self.get().getCapabilities().canModifyAccount()) { + throw new AuthException("restricted to members of Modify Accounts"); + } + + if (in == null) { + throw new BadRequestException("input must be provided"); + } + + Account.Id accountId = rsrc.getUser().getAccountId(); + MetaDataUpdate md = metaDataUpdateFactory.get().create(allUsersName); + + VersionedAccountPreferences prefs; + try { + prefs = VersionedAccountPreferences.forUser(accountId); + prefs.load(md); + storeSection(prefs.getConfig(), UserConfigSections.EDIT, null, in, + EditPreferencesInfo.defaults()); + prefs.commit(md); + } finally { + md.close(); + } + + return Response.none(); + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/SetPreferences.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/SetPreferences.java index c7725c5949..d355dae3c0 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/SetPreferences.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/SetPreferences.java @@ -19,7 +19,6 @@ import static com.google.gerrit.server.account.GetPreferences.KEY_MATCH; import static com.google.gerrit.server.account.GetPreferences.KEY_TARGET; import static com.google.gerrit.server.account.GetPreferences.KEY_TOKEN; import static com.google.gerrit.server.account.GetPreferences.KEY_URL; -import static com.google.gerrit.server.account.GetPreferences.MY; import static com.google.gerrit.server.account.GetPreferences.URL_ALIAS; import com.google.common.base.Strings; @@ -42,6 +41,7 @@ import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.account.SetPreferences.Input; import com.google.gerrit.server.config.AllUsersName; import com.google.gerrit.server.git.MetaDataUpdate; +import com.google.gerrit.server.git.UserConfigSections; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; @@ -192,7 +192,7 @@ public class SetPreferences implements RestModifyView { List my) { Config cfg = prefs.getConfig(); if (my != null) { - unsetSection(cfg, MY); + unsetSection(cfg, UserConfigSections.MY); for (TopMenu.MenuItem item : my) { set(cfg, item.name, KEY_URL, item.url); set(cfg, item.name, KEY_TARGET, item.target); @@ -203,9 +203,9 @@ public class SetPreferences implements RestModifyView { private static void set(Config cfg, String section, String key, String val) { if (Strings.isNullOrEmpty(val)) { - cfg.unset(MY, section, key); + cfg.unset(UserConfigSections.MY, section, key); } else { - cfg.setString(MY, section, key, val); + cfg.setString(UserConfigSections.MY, section, key, val); } } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/UserConfigSections.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/UserConfigSections.java new file mode 100644 index 0000000000..29b5373016 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/UserConfigSections.java @@ -0,0 +1,27 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.git; + +public class UserConfigSections { + + /** The my menu user preferences. */ + public static final String MY = "my"; + + /** The edit user preferences. */ + public static final String EDIT = "edit"; + + private UserConfigSections() { + } +}