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_PREFERENCES = "/settings/preferences";
|
||||
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_WEBIDENT = "/settings/web-identities";
|
||||
public static final String SETTINGS_MYGROUPS = "/settings/group-memberships";
|
||||
|
@ -91,6 +91,20 @@ public class Natives {
|
||||
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() {
|
||||
}
|
||||
}
|
||||
|
@ -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_CONTACT;
|
||||
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_MYGROUPS;
|
||||
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.MyContactInformationScreen;
|
||||
import com.google.gerrit.client.account.MyGpgKeysScreen;
|
||||
import com.google.gerrit.client.account.MyGroupsScreen;
|
||||
import com.google.gerrit.client.account.MyIdentitiesScreen;
|
||||
import com.google.gerrit.client.account.MyPasswordScreen;
|
||||
@ -536,6 +538,10 @@ public class Dispatcher {
|
||||
return new MySshKeysScreen();
|
||||
}
|
||||
|
||||
if (matchExact(SETTINGS_GPGKEYS, token)) {
|
||||
return new MyGpgKeysScreen();
|
||||
}
|
||||
|
||||
if (matchExact(SETTINGS_WEBIDENT, token)) {
|
||||
return new MyIdentitiesScreen();
|
||||
}
|
||||
|
@ -184,6 +184,7 @@ public interface GerritCss extends CssResource {
|
||||
String sshHostKeyPanelKnownHostEntry();
|
||||
String sshKeyPanelEncodedKey();
|
||||
String sshKeyPanelInvalid();
|
||||
String sshKeyTable();
|
||||
String stringListPanelButtons();
|
||||
String topMostCell();
|
||||
String topmenu();
|
||||
|
@ -17,10 +17,13 @@ package com.google.gerrit.client.account;
|
||||
import com.google.gerrit.client.VoidResult;
|
||||
import com.google.gerrit.client.info.AccountInfo;
|
||||
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.Natives;
|
||||
import com.google.gerrit.client.rpc.RestApi;
|
||||
import com.google.gwt.core.client.JavaScriptObject;
|
||||
import com.google.gwt.core.client.JsArray;
|
||||
import com.google.gwt.core.client.JsArrayString;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
|
||||
import java.util.Set;
|
||||
@ -147,4 +150,42 @@ public class AccountApi {
|
||||
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 tabAccountSummary();
|
||||
String tabPreferences();
|
||||
String tabWatchedProjects();
|
||||
String tabContactInformation();
|
||||
String tabSshKeys();
|
||||
String tabHttpAccess();
|
||||
String tabWebIdentities();
|
||||
String tabMyGroups();
|
||||
String tabAgreements();
|
||||
String tabContactInformation();
|
||||
String tabGpgKeys();
|
||||
String tabHttpAccess();
|
||||
String tabMyGroups();
|
||||
String tabPreferences();
|
||||
String tabSshKeys();
|
||||
String tabWatchedProjects();
|
||||
String tabWebIdentities();
|
||||
|
||||
String buttonShowAddSshKey();
|
||||
String buttonCloseAddSshKey();
|
||||
@ -94,6 +95,10 @@ public interface AccountConstants extends Constants {
|
||||
String sshHostKeyFingerprint();
|
||||
String sshHostKeyKnownHostEntry();
|
||||
|
||||
String gpgKeyId();
|
||||
String gpgKeyFingerprint();
|
||||
String gpgKeyUserIds();
|
||||
|
||||
String webIdStatus();
|
||||
String webIdEmail();
|
||||
String webIdIdentity();
|
||||
|
@ -36,14 +36,15 @@ changeScreenOldUi = Old Screen
|
||||
changeScreenNewUi = New Screen
|
||||
|
||||
tabAccountSummary = Profile
|
||||
tabPreferences = Preferences
|
||||
tabWatchedProjects = Watched Projects
|
||||
tabContactInformation = Contact Information
|
||||
tabSshKeys = SSH Public Keys
|
||||
tabHttpAccess = HTTP Password
|
||||
tabWebIdentities = Identities
|
||||
tabMyGroups = Groups
|
||||
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 ...
|
||||
buttonCloseAddSshKey = Close
|
||||
@ -73,6 +74,10 @@ sshHostKeyTitle = Server Host Key
|
||||
sshHostKeyFingerprint = Fingerprint:
|
||||
sshHostKeyKnownHostEntry = Entry for <code>~/.ssh/known_hosts</code>:
|
||||
|
||||
gpgKeyId = ID
|
||||
gpgKeyFingerprint = Fingerprint
|
||||
gpgKeyUserIds = User IDs
|
||||
|
||||
webIdStatus = Status
|
||||
webIdEmail = Email Address
|
||||
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()) {
|
||||
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.tabMyGroups(), PageLinks.SETTINGS_MYGROUPS);
|
||||
if (Gerrit.info().auth().useContributorAgreements()) {
|
||||
|
@ -1074,6 +1074,10 @@ a:hover.downloadLink {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sshKeyTable td.dataCell, .sshKeyTable td.iconCell {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.createProjectPanel {
|
||||
margin-bottom: 10px;
|
||||
background-color: trimColor;
|
||||
|
Loading…
Reference in New Issue
Block a user