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
This commit is contained in:
David Ostrovsky
2014-11-14 20:16:57 +01:00
parent a1520bb9d4
commit 0ca0182ab8
10 changed files with 409 additions and 8 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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<AccountResource> {
private final Provider<CurrentUser> self;
private final AllUsersName allUsersName;
private final GitRepositoryManager gitMgr;
@Inject
GetEditPreferences(Provider<CurrentUser> 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());
}
}
}

View File

@@ -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<AccountResource> {
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<AccountResource> {
private List<TopMenu.MenuItem> my(VersionedAccountPreferences v) {
List<TopMenu.MenuItem> 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<AccountResource> {
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;
}

View File

@@ -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);

View File

@@ -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<AccountResource, EditPreferencesInfo> {
private final Provider<CurrentUser> self;
private final Provider<MetaDataUpdate.User> metaDataUpdateFactory;
private final AllUsersName allUsersName;
@Inject
SetEditPreferences(Provider<CurrentUser> self,
Provider<MetaDataUpdate.User> 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();
}
}

View File

@@ -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<AccountResource, Input> {
List<TopMenu.MenuItem> 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<AccountResource, Input> {
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);
}
}

View File

@@ -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() {
}
}