Add settings screen for editing GPG public keys
The UI is almost identical to the UI for editing SSH keys (although implemented with UiBinder). Change-Id: Ic6cf4dc9d7f71b00efea00a86498660d113ebb2b
This commit is contained in:
parent
eab3aff03a
commit
0349f8a1c8
@ -26,6 +26,7 @@ public class PageLinks {
|
|||||||
public static final String SETTINGS = "/settings/";
|
public static final String SETTINGS = "/settings/";
|
||||||
public static final String SETTINGS_PREFERENCES = "/settings/preferences";
|
public static final String SETTINGS_PREFERENCES = "/settings/preferences";
|
||||||
public static final String SETTINGS_SSHKEYS = "/settings/ssh-keys";
|
public static final String SETTINGS_SSHKEYS = "/settings/ssh-keys";
|
||||||
|
public static final String SETTINGS_GPGKEYS = "/settings/gpg-keys";
|
||||||
public static final String SETTINGS_HTTP_PASSWORD = "/settings/http-password";
|
public static final String SETTINGS_HTTP_PASSWORD = "/settings/http-password";
|
||||||
public static final String SETTINGS_WEBIDENT = "/settings/web-identities";
|
public static final String SETTINGS_WEBIDENT = "/settings/web-identities";
|
||||||
public static final String SETTINGS_MYGROUPS = "/settings/group-memberships";
|
public static final String SETTINGS_MYGROUPS = "/settings/group-memberships";
|
||||||
|
@ -91,6 +91,20 @@ public class Natives {
|
|||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static JsArrayString arrayOf(Iterable<String> elements) {
|
||||||
|
JsArrayString arr = JavaScriptObject.createArray().cast();
|
||||||
|
for (String elem : elements) {
|
||||||
|
arr.push(elem);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsArrayString arrayOf(String element) {
|
||||||
|
JsArrayString arr = JavaScriptObject.createArray().cast();
|
||||||
|
arr.push(element);
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
private Natives() {
|
private Natives() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ import static com.google.gerrit.common.PageLinks.SETTINGS;
|
|||||||
import static com.google.gerrit.common.PageLinks.SETTINGS_AGREEMENTS;
|
import static com.google.gerrit.common.PageLinks.SETTINGS_AGREEMENTS;
|
||||||
import static com.google.gerrit.common.PageLinks.SETTINGS_CONTACT;
|
import static com.google.gerrit.common.PageLinks.SETTINGS_CONTACT;
|
||||||
import static com.google.gerrit.common.PageLinks.SETTINGS_EXTENSION;
|
import static com.google.gerrit.common.PageLinks.SETTINGS_EXTENSION;
|
||||||
|
import static com.google.gerrit.common.PageLinks.SETTINGS_GPGKEYS;
|
||||||
import static com.google.gerrit.common.PageLinks.SETTINGS_HTTP_PASSWORD;
|
import static com.google.gerrit.common.PageLinks.SETTINGS_HTTP_PASSWORD;
|
||||||
import static com.google.gerrit.common.PageLinks.SETTINGS_MYGROUPS;
|
import static com.google.gerrit.common.PageLinks.SETTINGS_MYGROUPS;
|
||||||
import static com.google.gerrit.common.PageLinks.SETTINGS_NEW_AGREEMENT;
|
import static com.google.gerrit.common.PageLinks.SETTINGS_NEW_AGREEMENT;
|
||||||
@ -40,6 +41,7 @@ import static com.google.gerrit.common.PageLinks.toChangeQuery;
|
|||||||
|
|
||||||
import com.google.gerrit.client.account.MyAgreementsScreen;
|
import com.google.gerrit.client.account.MyAgreementsScreen;
|
||||||
import com.google.gerrit.client.account.MyContactInformationScreen;
|
import com.google.gerrit.client.account.MyContactInformationScreen;
|
||||||
|
import com.google.gerrit.client.account.MyGpgKeysScreen;
|
||||||
import com.google.gerrit.client.account.MyGroupsScreen;
|
import com.google.gerrit.client.account.MyGroupsScreen;
|
||||||
import com.google.gerrit.client.account.MyIdentitiesScreen;
|
import com.google.gerrit.client.account.MyIdentitiesScreen;
|
||||||
import com.google.gerrit.client.account.MyPasswordScreen;
|
import com.google.gerrit.client.account.MyPasswordScreen;
|
||||||
@ -536,6 +538,10 @@ public class Dispatcher {
|
|||||||
return new MySshKeysScreen();
|
return new MySshKeysScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (matchExact(SETTINGS_GPGKEYS, token)) {
|
||||||
|
return new MyGpgKeysScreen();
|
||||||
|
}
|
||||||
|
|
||||||
if (matchExact(SETTINGS_WEBIDENT, token)) {
|
if (matchExact(SETTINGS_WEBIDENT, token)) {
|
||||||
return new MyIdentitiesScreen();
|
return new MyIdentitiesScreen();
|
||||||
}
|
}
|
||||||
|
@ -184,6 +184,7 @@ public interface GerritCss extends CssResource {
|
|||||||
String sshHostKeyPanelKnownHostEntry();
|
String sshHostKeyPanelKnownHostEntry();
|
||||||
String sshKeyPanelEncodedKey();
|
String sshKeyPanelEncodedKey();
|
||||||
String sshKeyPanelInvalid();
|
String sshKeyPanelInvalid();
|
||||||
|
String sshKeyTable();
|
||||||
String stringListPanelButtons();
|
String stringListPanelButtons();
|
||||||
String topMostCell();
|
String topMostCell();
|
||||||
String topmenu();
|
String topmenu();
|
||||||
|
@ -17,10 +17,13 @@ package com.google.gerrit.client.account;
|
|||||||
import com.google.gerrit.client.VoidResult;
|
import com.google.gerrit.client.VoidResult;
|
||||||
import com.google.gerrit.client.info.AccountInfo;
|
import com.google.gerrit.client.info.AccountInfo;
|
||||||
import com.google.gerrit.client.rpc.CallbackGroup;
|
import com.google.gerrit.client.rpc.CallbackGroup;
|
||||||
|
import com.google.gerrit.client.rpc.NativeMap;
|
||||||
import com.google.gerrit.client.rpc.NativeString;
|
import com.google.gerrit.client.rpc.NativeString;
|
||||||
|
import com.google.gerrit.client.rpc.Natives;
|
||||||
import com.google.gerrit.client.rpc.RestApi;
|
import com.google.gerrit.client.rpc.RestApi;
|
||||||
import com.google.gwt.core.client.JavaScriptObject;
|
import com.google.gwt.core.client.JavaScriptObject;
|
||||||
import com.google.gwt.core.client.JsArray;
|
import com.google.gwt.core.client.JsArray;
|
||||||
|
import com.google.gwt.core.client.JsArrayString;
|
||||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -147,4 +150,42 @@ public class AccountApi {
|
|||||||
protected UsernameInput() {
|
protected UsernameInput() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void addGpgKey(String account, String armored,
|
||||||
|
AsyncCallback<NativeMap<GpgKeyInfo>> cb) {
|
||||||
|
new RestApi("/accounts/")
|
||||||
|
.id(account)
|
||||||
|
.view("gpgkeys")
|
||||||
|
.post(GpgKeysInput.add(armored), cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeGpgKeys(String account,
|
||||||
|
Iterable<String> fingerprints, AsyncCallback<NativeMap<GpgKeyInfo>> cb) {
|
||||||
|
new RestApi("/accounts/")
|
||||||
|
.id(account)
|
||||||
|
.view("gpgkeys")
|
||||||
|
.post(GpgKeysInput.remove(fingerprints), cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GpgKeysInput extends JavaScriptObject {
|
||||||
|
static GpgKeysInput add(String key) {
|
||||||
|
return createAdd(Natives.arrayOf(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
static GpgKeysInput remove(Iterable<String> fingerprints) {
|
||||||
|
return createRemove(Natives.arrayOf(fingerprints));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static native GpgKeysInput createAdd(JsArrayString keys) /*-{
|
||||||
|
return {'add': keys};
|
||||||
|
}-*/;
|
||||||
|
|
||||||
|
private static native GpgKeysInput createRemove(
|
||||||
|
JsArrayString fingerprints) /*-{
|
||||||
|
return {'remove': fingerprints};
|
||||||
|
}-*/;
|
||||||
|
|
||||||
|
protected GpgKeysInput() {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,14 +50,15 @@ public interface AccountConstants extends Constants {
|
|||||||
String myMenuReset();
|
String myMenuReset();
|
||||||
|
|
||||||
String tabAccountSummary();
|
String tabAccountSummary();
|
||||||
String tabPreferences();
|
|
||||||
String tabWatchedProjects();
|
|
||||||
String tabContactInformation();
|
|
||||||
String tabSshKeys();
|
|
||||||
String tabHttpAccess();
|
|
||||||
String tabWebIdentities();
|
|
||||||
String tabMyGroups();
|
|
||||||
String tabAgreements();
|
String tabAgreements();
|
||||||
|
String tabContactInformation();
|
||||||
|
String tabGpgKeys();
|
||||||
|
String tabHttpAccess();
|
||||||
|
String tabMyGroups();
|
||||||
|
String tabPreferences();
|
||||||
|
String tabSshKeys();
|
||||||
|
String tabWatchedProjects();
|
||||||
|
String tabWebIdentities();
|
||||||
|
|
||||||
String buttonShowAddSshKey();
|
String buttonShowAddSshKey();
|
||||||
String buttonCloseAddSshKey();
|
String buttonCloseAddSshKey();
|
||||||
@ -94,6 +95,10 @@ public interface AccountConstants extends Constants {
|
|||||||
String sshHostKeyFingerprint();
|
String sshHostKeyFingerprint();
|
||||||
String sshHostKeyKnownHostEntry();
|
String sshHostKeyKnownHostEntry();
|
||||||
|
|
||||||
|
String gpgKeyId();
|
||||||
|
String gpgKeyFingerprint();
|
||||||
|
String gpgKeyUserIds();
|
||||||
|
|
||||||
String webIdStatus();
|
String webIdStatus();
|
||||||
String webIdEmail();
|
String webIdEmail();
|
||||||
String webIdIdentity();
|
String webIdIdentity();
|
||||||
|
@ -36,14 +36,15 @@ changeScreenOldUi = Old Screen
|
|||||||
changeScreenNewUi = New Screen
|
changeScreenNewUi = New Screen
|
||||||
|
|
||||||
tabAccountSummary = Profile
|
tabAccountSummary = Profile
|
||||||
tabPreferences = Preferences
|
|
||||||
tabWatchedProjects = Watched Projects
|
|
||||||
tabContactInformation = Contact Information
|
|
||||||
tabSshKeys = SSH Public Keys
|
|
||||||
tabHttpAccess = HTTP Password
|
|
||||||
tabWebIdentities = Identities
|
|
||||||
tabMyGroups = Groups
|
|
||||||
tabAgreements = Agreements
|
tabAgreements = Agreements
|
||||||
|
tabContactInformation = Contact Information
|
||||||
|
tabGpgKeys = GPG Public Keys
|
||||||
|
tabHttpAccess = HTTP Password
|
||||||
|
tabMyGroups = Groups
|
||||||
|
tabPreferences = Preferences
|
||||||
|
tabSshKeys = SSH Public Keys
|
||||||
|
tabWatchedProjects = Watched Projects
|
||||||
|
tabWebIdentities = Identities
|
||||||
|
|
||||||
buttonShowAddSshKey = Add Key ...
|
buttonShowAddSshKey = Add Key ...
|
||||||
buttonCloseAddSshKey = Close
|
buttonCloseAddSshKey = Close
|
||||||
@ -73,6 +74,10 @@ sshHostKeyTitle = Server Host Key
|
|||||||
sshHostKeyFingerprint = Fingerprint:
|
sshHostKeyFingerprint = Fingerprint:
|
||||||
sshHostKeyKnownHostEntry = Entry for <code>~/.ssh/known_hosts</code>:
|
sshHostKeyKnownHostEntry = Entry for <code>~/.ssh/known_hosts</code>:
|
||||||
|
|
||||||
|
gpgKeyId = ID
|
||||||
|
gpgKeyFingerprint = Fingerprint
|
||||||
|
gpgKeyUserIds = User IDs
|
||||||
|
|
||||||
webIdStatus = Status
|
webIdStatus = Status
|
||||||
webIdEmail = Email Address
|
webIdEmail = Email Address
|
||||||
webIdIdentity = Identity
|
webIdIdentity = Identity
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
// 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.client.account;
|
||||||
|
|
||||||
|
import com.google.gwt.core.client.JavaScriptObject;
|
||||||
|
import com.google.gwt.core.client.JsArrayString;
|
||||||
|
|
||||||
|
public class GpgKeyInfo extends JavaScriptObject {
|
||||||
|
public final native String id() /*-{ return this.id; }-*/;
|
||||||
|
public final native String fingerprint() /*-{ return this.fingerprint; }-*/;
|
||||||
|
public final native JsArrayString userIds() /*-{ return this.user_ids; }-*/;
|
||||||
|
public final native String key() /*-{ return this.key; }-*/;
|
||||||
|
|
||||||
|
protected GpgKeyInfo() {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,283 @@
|
|||||||
|
// 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.client.account;
|
||||||
|
|
||||||
|
import com.google.gerrit.client.Gerrit;
|
||||||
|
import com.google.gerrit.client.rpc.GerritCallback;
|
||||||
|
import com.google.gerrit.client.rpc.NativeMap;
|
||||||
|
import com.google.gerrit.client.rpc.Natives;
|
||||||
|
import com.google.gerrit.client.ui.FancyFlexTable;
|
||||||
|
import com.google.gwt.core.client.GWT;
|
||||||
|
import com.google.gwt.event.dom.client.ClickEvent;
|
||||||
|
import com.google.gwt.event.logical.shared.ValueChangeEvent;
|
||||||
|
import com.google.gwt.event.logical.shared.ValueChangeHandler;
|
||||||
|
import com.google.gwt.http.client.Response;
|
||||||
|
import com.google.gwt.uibinder.client.UiBinder;
|
||||||
|
import com.google.gwt.uibinder.client.UiField;
|
||||||
|
import com.google.gwt.uibinder.client.UiHandler;
|
||||||
|
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||||
|
import com.google.gwt.user.client.rpc.StatusCodeException;
|
||||||
|
import com.google.gwt.user.client.ui.Button;
|
||||||
|
import com.google.gwt.user.client.ui.CheckBox;
|
||||||
|
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
|
||||||
|
import com.google.gwt.user.client.ui.HTMLPanel;
|
||||||
|
import com.google.gwt.user.client.ui.InlineLabel;
|
||||||
|
import com.google.gwt.user.client.ui.Label;
|
||||||
|
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||||
|
import com.google.gwtexpui.clippy.client.CopyableLabel;
|
||||||
|
import com.google.gwtexpui.globalkey.client.NpTextArea;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MyGpgKeysScreen extends SettingsScreen {
|
||||||
|
interface Binder extends UiBinder<HTMLPanel, MyGpgKeysScreen> {}
|
||||||
|
private static final Binder uiBinder = GWT.create(Binder.class);
|
||||||
|
|
||||||
|
@UiField(provided = true) GpgKeyTable keys;
|
||||||
|
@UiField Button deleteKey;
|
||||||
|
@UiField Button addKey;
|
||||||
|
|
||||||
|
@UiField VerticalPanel addKeyBlock;
|
||||||
|
@UiField NpTextArea keyText;
|
||||||
|
|
||||||
|
@UiField VerticalPanel errorPanel;
|
||||||
|
@UiField Label errorText;
|
||||||
|
|
||||||
|
@UiField Button clearButton;
|
||||||
|
@UiField Button addButton;
|
||||||
|
@UiField Button closeButton;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onInitUI() {
|
||||||
|
super.onInitUI();
|
||||||
|
keys = new GpgKeyTable();
|
||||||
|
add(uiBinder.createAndBindUi(this));
|
||||||
|
keys.updateDeleteButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLoad() {
|
||||||
|
super.onLoad();
|
||||||
|
refreshKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiHandler("deleteKey")
|
||||||
|
void onDeleteKey(@SuppressWarnings("unused") ClickEvent e) {
|
||||||
|
keys.deleteChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiHandler("addKey")
|
||||||
|
void onAddKey(@SuppressWarnings("unused") ClickEvent e) {
|
||||||
|
showAddKeyBlock(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiHandler("clearButton")
|
||||||
|
void onClearButton(@SuppressWarnings("unused") ClickEvent e) {
|
||||||
|
keyText.setText("");
|
||||||
|
keyText.setFocus(true);
|
||||||
|
errorPanel.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiHandler("closeButton")
|
||||||
|
void onCloseButton(@SuppressWarnings("unused") ClickEvent e) {
|
||||||
|
showAddKeyBlock(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiHandler("addButton")
|
||||||
|
void onAddButton(@SuppressWarnings("unused") ClickEvent e) {
|
||||||
|
doAddKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshKeys() {
|
||||||
|
AccountApi.self().view("gpgkeys").get(NativeMap.copyKeysIntoChildren("id",
|
||||||
|
new GerritCallback<NativeMap<GpgKeyInfo>>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(NativeMap<GpgKeyInfo> result) {
|
||||||
|
List<GpgKeyInfo> list = Natives.asList(result.values());
|
||||||
|
// TODO(dborowitz): Sort on something more meaningful, like
|
||||||
|
// created date?
|
||||||
|
Collections.sort(list, new Comparator<GpgKeyInfo>() {
|
||||||
|
@Override
|
||||||
|
public int compare(GpgKeyInfo a, GpgKeyInfo b) {
|
||||||
|
return a.id().compareTo(b.id());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
keys.clear();
|
||||||
|
keyText.setText("");
|
||||||
|
errorPanel.setVisible(false);
|
||||||
|
addButton.setEnabled(true);
|
||||||
|
if (!list.isEmpty()) {
|
||||||
|
keys.setVisible(true);
|
||||||
|
for (GpgKeyInfo k : list) {
|
||||||
|
keys.addOneKey(k);
|
||||||
|
}
|
||||||
|
showKeyTable(true);
|
||||||
|
showAddKeyBlock(false);
|
||||||
|
} else {
|
||||||
|
keys.setVisible(false);
|
||||||
|
showAddKeyBlock(true);
|
||||||
|
showKeyTable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
display();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showAddKeyBlock(boolean show) {
|
||||||
|
addKey.setVisible(!show);
|
||||||
|
addKeyBlock.setVisible(show);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showKeyTable(boolean show) {
|
||||||
|
keys.setVisible(show);
|
||||||
|
deleteKey.setVisible(show);
|
||||||
|
addKey.setVisible(show);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doAddKey() {
|
||||||
|
if (keyText.getText().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addButton.setEnabled(false);
|
||||||
|
keyText.setEnabled(false);
|
||||||
|
AccountApi.addGpgKey("self", keyText.getText(),
|
||||||
|
new AsyncCallback<NativeMap<GpgKeyInfo>>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(NativeMap<GpgKeyInfo> result) {
|
||||||
|
keyText.setEnabled(true);
|
||||||
|
refreshKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable caught) {
|
||||||
|
keyText.setEnabled(true);
|
||||||
|
addButton.setEnabled(true);
|
||||||
|
if (caught instanceof StatusCodeException) {
|
||||||
|
StatusCodeException sce = (StatusCodeException) caught;
|
||||||
|
if (sce.getStatusCode() == Response.SC_CONFLICT
|
||||||
|
|| sce.getStatusCode() == Response.SC_BAD_REQUEST) {
|
||||||
|
errorText.setText(sce.getEncodedResponse());
|
||||||
|
} else {
|
||||||
|
errorText.setText(sce.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errorText.setText(
|
||||||
|
"Unexpected error saving key: " + caught.getMessage());
|
||||||
|
}
|
||||||
|
errorPanel.setVisible(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private class GpgKeyTable extends FancyFlexTable<GpgKeyInfo> {
|
||||||
|
private final ValueChangeHandler<Boolean> updateDeleteHandler;
|
||||||
|
|
||||||
|
GpgKeyTable() {
|
||||||
|
table.setWidth("");
|
||||||
|
table.setText(0, 1, Util.C.gpgKeyId());
|
||||||
|
table.setText(0, 2, Util.C.gpgKeyFingerprint());
|
||||||
|
table.setText(0, 3, Util.C.gpgKeyUserIds());
|
||||||
|
|
||||||
|
FlexCellFormatter fmt = table.getFlexCellFormatter();
|
||||||
|
fmt.addStyleName(0, 0, Gerrit.RESOURCES.css().iconHeader());
|
||||||
|
fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().dataHeader());
|
||||||
|
fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
|
||||||
|
fmt.addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader());
|
||||||
|
|
||||||
|
updateDeleteHandler = new ValueChangeHandler<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void onValueChange(ValueChangeEvent<Boolean> event) {
|
||||||
|
updateDeleteButton();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addOneKey(GpgKeyInfo k) {
|
||||||
|
int row = table.getRowCount();
|
||||||
|
table.insertRow(row);
|
||||||
|
applyDataRowStyle(row);
|
||||||
|
|
||||||
|
CheckBox sel = new CheckBox();
|
||||||
|
sel.addValueChangeHandler(updateDeleteHandler);
|
||||||
|
table.setWidget(row, 0, sel);
|
||||||
|
table.setWidget(row, 1, new CopyableLabel(k.id()));
|
||||||
|
table.setText(row, 2, k.fingerprint());
|
||||||
|
|
||||||
|
VerticalPanel userIds = new VerticalPanel();
|
||||||
|
for (int i = 0; i < k.userIds().length(); i++) {
|
||||||
|
userIds.add(new InlineLabel(k.userIds().get(i)));
|
||||||
|
}
|
||||||
|
table.setWidget(row, 3, userIds);
|
||||||
|
|
||||||
|
FlexCellFormatter fmt = table.getFlexCellFormatter();
|
||||||
|
fmt.addStyleName(row, 0, Gerrit.RESOURCES.css().iconCell());
|
||||||
|
fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().dataCell());
|
||||||
|
fmt.addStyleName(row, 2, Gerrit.RESOURCES.css().dataCell());
|
||||||
|
fmt.addStyleName(row, 3, Gerrit.RESOURCES.css().dataCell());
|
||||||
|
|
||||||
|
setRowItem(row, k);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDeleteButton() {
|
||||||
|
for (int row = 1; row < table.getRowCount(); row++) {
|
||||||
|
if (isChecked(row)) {
|
||||||
|
deleteKey.setEnabled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deleteKey.setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteChecked() {
|
||||||
|
deleteKey.setEnabled(false);
|
||||||
|
List<String> toDelete = new ArrayList<>(table.getRowCount());
|
||||||
|
for (int row = 1; row < table.getRowCount(); row++) {
|
||||||
|
if (isChecked(row)) {
|
||||||
|
toDelete.add(getRowItem(row).fingerprint());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AccountApi.removeGpgKeys("self", toDelete,
|
||||||
|
new GerritCallback<NativeMap<GpgKeyInfo>>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(NativeMap<GpgKeyInfo> result) {
|
||||||
|
refreshKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable caught) {
|
||||||
|
deleteKey.setEnabled(true);
|
||||||
|
super.onFailure(caught);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isChecked(int row) {
|
||||||
|
return ((CheckBox) table.getWidget(row, 0)).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clear() {
|
||||||
|
while (table.getRowCount() > 1) {
|
||||||
|
table.removeRow(1);
|
||||||
|
}
|
||||||
|
for (int i = table.getRowCount() - 1; i >= 1; i++) {
|
||||||
|
table.removeRow(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
|
||||||
|
xmlns:g='urn:import:com.google.gwt.user.client.ui'
|
||||||
|
xmlns:expui='urn:import:com.google.gwtexpui.globalkey.client'>
|
||||||
|
<ui:with field='res' type='com.google.gerrit.client.GerritResources'/>
|
||||||
|
|
||||||
|
<ui:style gss='false'>
|
||||||
|
.errorHeader {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.errorText {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
</ui:style>
|
||||||
|
|
||||||
|
<g:HTMLPanel>
|
||||||
|
<g:Widget ui:field='keys' addStyleNames='{res.css.sshKeyTable}'/>
|
||||||
|
<g:FlowPanel>
|
||||||
|
<g:Button ui:field='deleteKey'>
|
||||||
|
<div><ui:msg>Delete</ui:msg></div>
|
||||||
|
</g:Button>
|
||||||
|
<g:Button ui:field='addKey'>
|
||||||
|
<div><ui:msg>Add Key ...</ui:msg></div>
|
||||||
|
</g:Button>
|
||||||
|
</g:FlowPanel>
|
||||||
|
<g:VerticalPanel ui:field='addKeyBlock'
|
||||||
|
styleName='{res.css.addSshKeyPanel}'
|
||||||
|
visible='false'>
|
||||||
|
<g:Label>Add GPG Public Key</g:Label>
|
||||||
|
<g:DisclosurePanel>
|
||||||
|
<g:header>How to generate a GPG key</g:header>
|
||||||
|
<g:HTMLPanel>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
From the Terminal or Git Bash, run <em>gpg --gen-key</em> and
|
||||||
|
follow the prompts to create the key.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Use the default kind. Use the default (or higher) keysize. Choose
|
||||||
|
any value for your expiration.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
The user ID should contain one of your registered email addresses.
|
||||||
|
</li>
|
||||||
|
<li>Setting a passphrase is strongly recommended.</li>
|
||||||
|
<li>Note the ID of your new key.</li>
|
||||||
|
<li>
|
||||||
|
To export your key, run the following and paste the full output
|
||||||
|
into the text box:
|
||||||
|
<br/>
|
||||||
|
<code>gpg --export -a <key ID></code>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</g:HTMLPanel>
|
||||||
|
</g:DisclosurePanel>
|
||||||
|
<expui:NpTextArea
|
||||||
|
visibleLines='12'
|
||||||
|
characterWidth='80'
|
||||||
|
spellCheck='false'
|
||||||
|
ui:field='keyText'/>
|
||||||
|
<g:VerticalPanel ui:field='errorPanel' visible='false'>
|
||||||
|
<g:Label styleName='{style.errorHeader}'>Error adding GPG key:</g:Label>
|
||||||
|
<g:Label styleName='{style.errorText}' ui:field='errorText'/>
|
||||||
|
</g:VerticalPanel>
|
||||||
|
<g:FlowPanel>
|
||||||
|
<g:Button ui:field='clearButton'>
|
||||||
|
<div><ui:msg>Clear</ui:msg></div>
|
||||||
|
</g:Button>
|
||||||
|
<g:Button ui:field='addButton'>
|
||||||
|
<div><ui:msg>Add</ui:msg></div>
|
||||||
|
</g:Button>
|
||||||
|
<g:Button ui:field='closeButton'>
|
||||||
|
<div><ui:msg>Close</ui:msg></div>
|
||||||
|
</g:Button>
|
||||||
|
</g:FlowPanel>
|
||||||
|
</g:VerticalPanel>
|
||||||
|
</g:HTMLPanel>
|
||||||
|
</ui:UiBinder>
|
@ -45,6 +45,9 @@ public abstract class SettingsScreen extends MenuScreen {
|
|||||||
if (Gerrit.info().auth().isHttpPasswordSettingsEnabled()) {
|
if (Gerrit.info().auth().isHttpPasswordSettingsEnabled()) {
|
||||||
linkByGerrit(Util.C.tabHttpAccess(), PageLinks.SETTINGS_HTTP_PASSWORD);
|
linkByGerrit(Util.C.tabHttpAccess(), PageLinks.SETTINGS_HTTP_PASSWORD);
|
||||||
}
|
}
|
||||||
|
if (Gerrit.info().receive().enableSignedPush()) {
|
||||||
|
linkByGerrit(Util.C.tabGpgKeys(), PageLinks.SETTINGS_GPGKEYS);
|
||||||
|
}
|
||||||
linkByGerrit(Util.C.tabWebIdentities(), PageLinks.SETTINGS_WEBIDENT);
|
linkByGerrit(Util.C.tabWebIdentities(), PageLinks.SETTINGS_WEBIDENT);
|
||||||
linkByGerrit(Util.C.tabMyGroups(), PageLinks.SETTINGS_MYGROUPS);
|
linkByGerrit(Util.C.tabMyGroups(), PageLinks.SETTINGS_MYGROUPS);
|
||||||
if (Gerrit.info().auth().useContributorAgreements()) {
|
if (Gerrit.info().auth().useContributorAgreements()) {
|
||||||
|
@ -1074,6 +1074,10 @@ a:hover.downloadLink {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sshKeyTable td.dataCell, .sshKeyTable td.iconCell {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
.createProjectPanel {
|
.createProjectPanel {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
background-color: trimColor;
|
background-color: trimColor;
|
||||||
|
Loading…
Reference in New Issue
Block a user