Add push cert status to change screen

Add a status icon next to the uploader with a check/warning/X
depending on the push cert status. Include messages in the title on
hover. If the uploader is the same as the change owner, place the
status icon next to the owner.

PushCertificateInfo and GpgKeyInfo are moved to gerrit-gwtui-common so
they can be referenced from ChangeInfo.

The new question mark icon is the help-browser icon from the
public-domain Tango collection:
http://tango.freedesktop.org/Tango_Icon_Library

Change-Id: Iec666c668342b00fd92609dd28feb082d5a560a8
This commit is contained in:
Dave Borowitz
2015-09-15 11:06:47 -04:00
parent 8f10d51c0d
commit ed1523094d
11 changed files with 143 additions and 17 deletions

View File

@@ -104,4 +104,7 @@ public interface Resources extends ClientBundle {
@Source("warning.png")
public ImageResource warning();
@Source("question.png")
public ImageResource question();
}

View File

@@ -311,6 +311,9 @@ public class ChangeInfo extends JavaScriptObject {
public final native boolean hasFetch() /*-{ return this.hasOwnProperty('fetch') }-*/;
public final native NativeMap<FetchInfo> fetch() /*-{ return this.fetch; }-*/;
public final native boolean hasPushCertificate() /*-{ return this.hasOwnProperty('push_certificate'); }-*/;
public final native PushCertificateInfo pushCertificate() /*-{ return this.push_certificate; }-*/;
public static void sortRevisionInfoByNumber(JsArray<RevisionInfo> list) {
final int editParent = findEditParent(list);
Collections.sort(Natives.asList(list), new Comparator<RevisionInfo>() {

View File

@@ -12,17 +12,34 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.account;
package com.google.gerrit.client.info;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayString;
public class GpgKeyInfo extends JavaScriptObject {
public enum Status {
BAD, OK, TRUSTED;
}
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; }-*/;
private final native String statusRaw() /*-{ return this.status; }-*/;
public final Status status() {
String s = statusRaw();
if (s == null) {
return null;
}
return Status.valueOf(s);
}
public final native boolean hasProblems()
/*-{ return this.hasOwnProperty('problems'); }-*/;
public final native JsArrayString problems() /*-{ return this.problems; }-*/;
protected GpgKeyInfo() {
}
}

View File

@@ -0,0 +1,25 @@
// 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.info;
import com.google.gwt.core.client.JavaScriptObject;
public class PushCertificateInfo extends JavaScriptObject {
public final native String certificate() /*-{ return this.certificate; }-*/;
public final native GpgKeyInfo key() /*-{ return this.key; }-*/;
protected PushCertificateInfo() {
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 B

View File

@@ -16,6 +16,7 @@ package com.google.gerrit.client.account;
import com.google.gerrit.client.VoidResult;
import com.google.gerrit.client.info.AccountInfo;
import com.google.gerrit.client.info.GpgKeyInfo;
import com.google.gerrit.client.rpc.CallbackGroup;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.NativeString;

View File

@@ -15,6 +15,7 @@
package com.google.gerrit.client.account;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.info.GpgKeyInfo;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.Natives;

View File

@@ -38,6 +38,8 @@ import com.google.gerrit.client.info.ChangeInfo.LabelInfo;
import com.google.gerrit.client.info.ChangeInfo.MessageInfo;
import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
import com.google.gerrit.client.info.FileInfo;
import com.google.gerrit.client.info.GpgKeyInfo;
import com.google.gerrit.client.info.PushCertificateInfo;
import com.google.gerrit.client.projects.ConfigInfoCache;
import com.google.gerrit.client.projects.ConfigInfoCache.Entry;
import com.google.gerrit.client.rpc.CallbackGroup;
@@ -112,17 +114,18 @@ public class ChangeScreen extends Screen {
private static final Binder uiBinder = GWT.create(Binder.class);
interface Style extends CssResource {
String labelName();
String avatar();
String label_user();
String label_ok();
String label_reject();
String hashtagName();
String highlight();
String labelName();
String label_may();
String label_need();
String label_ok();
String label_reject();
String label_user();
String pushCertStatus();
String replyBox();
String selected();
String highlight();
String hashtagName();
}
static ChangeScreen get(NativeEvent in) {
@@ -164,11 +167,14 @@ public class ChangeScreen extends Screen {
@UiField Reviewers reviewers;
@UiField Hashtags hashtags;
@UiField Element hashtagTableRow;
@UiField FlowPanel ownerPanel;
@UiField InlineHyperlink ownerLink;
@UiField Element uploaderRow;
@UiField FlowPanel uploaderPanel;
@UiField InlineLabel uploaderName;
@UiField Element statusText;
@UiField Image projectSettings;
@UiField AnchorElement projectSettingsLink;
@@ -292,11 +298,19 @@ public class ChangeScreen extends Screen {
p.add(extensionPanel);
}
private boolean enableSignedPush() {
return Gerrit.info().receive().enableSignedPush();
}
void loadChangeInfo(boolean fg, AsyncCallback<ChangeInfo> cb) {
RestApi call = ChangeApi.detail(changeId.get());
ChangeList.addOptions(call, EnumSet.of(
ListChangesOption.CHANGE_ACTIONS,
ListChangesOption.ALL_REVISIONS));
EnumSet<ListChangesOption> opts = EnumSet.of(
ListChangesOption.ALL_REVISIONS,
ListChangesOption.CHANGE_ACTIONS);
if (enableSignedPush()) {
opts.add(ListChangesOption.PUSH_CERTIFICATES);
}
ChangeList.addOptions(call, opts);
if (!fg) {
call.background();
}
@@ -1146,13 +1160,14 @@ public class ChangeScreen extends Screen {
}
private void renderChangeInfo(ChangeInfo info) {
RevisionInfo revisionInfo = info.revision(revision);
changeInfo = info;
lastDisplayedUpdate = info.updated();
labels.set(info);
renderOwner(info);
renderUploader(info, revision);
renderUploader(info, revisionInfo);
renderActionTextDate(info);
renderDiffBaseListBox(info);
initReplyButton(info, revision);
@@ -1189,7 +1204,7 @@ public class ChangeScreen extends Screen {
// render it faster.
if (!info.status().isOpen()
|| !revision.equals(info.currentRevision())
|| info.revision(revision).isEdit()) {
|| revisionInfo.isEdit()) {
setVisible(strategy, false);
}
@@ -1200,7 +1215,6 @@ public class ChangeScreen extends Screen {
quickApprove.setVisible(false);
actions.reloadRevisionActions(emptyMap);
RevisionInfo revisionInfo = info.revision(revision);
boolean current = revision.equals(info.currentRevision())
&& !revisionInfo.isEdit();
@@ -1252,10 +1266,12 @@ public class ChangeScreen extends Screen {
: String.valueOf(info.owner()._accountId()), Change.Status.NEW));
}
private void renderUploader(ChangeInfo info, String revision) {
AccountInfo uploader = info.revision(revision).uploader();
if (uploader == null
|| uploader._accountId() == info.owner()._accountId()) {
private void renderUploader(ChangeInfo changeInfo, RevisionInfo revInfo) {
AccountInfo uploader = revInfo.uploader();
boolean isOwner = uploader == null
|| uploader._accountId() == changeInfo.owner()._accountId();
renderPushCertificate(revInfo, isOwner ? ownerPanel : uploaderPanel);
if (isOwner) {
uploaderRow.getStyle().setDisplay(Display.NONE);
return;
}
@@ -1269,6 +1285,37 @@ public class ChangeScreen extends Screen {
uploaderName.setTitle(email(uploader, name));
}
private void renderPushCertificate(RevisionInfo revInfo, FlowPanel panel) {
if (!enableSignedPush()) {
return;
}
Image status = new Image();
panel.add(status);
status.setStyleName(style.pushCertStatus());
if (!revInfo.hasPushCertificate()
|| revInfo.pushCertificate().key() == null) {
status.setResource(Gerrit.RESOURCES.question());
status.setTitle(Util.C.pushCertMissing());
return;
}
PushCertificateInfo certInfo = revInfo.pushCertificate();
GpgKeyInfo.Status s = certInfo.key().status();
switch (s) {
case BAD:
status.setResource(Gerrit.RESOURCES.redNot());
status.setTitle(problems(Util.C.pushCertBad(), certInfo));
break;
case OK:
status.setResource(Gerrit.RESOURCES.warning());
status.setTitle(problems(Util.C.pushCertOk(), certInfo));
break;
case TRUSTED:
status.setResource(Gerrit.RESOURCES.greenCheck());
status.setTitle(Util.C.pushCertTrusted());
break;
}
}
private static String name(AccountInfo info) {
return info.name() != null
? info.name()
@@ -1279,6 +1326,21 @@ public class ChangeScreen extends Screen {
return info.email() != null ? info.email() : name;
}
private static String problems(String msg, PushCertificateInfo info) {
if (info.key() == null
|| !info.key().hasProblems()
|| info.key().problems().length() == 0) {
return msg;
}
StringBuilder sb = new StringBuilder();
sb.append(msg).append(':');
for (String problem : Natives.asList(info.key().problems())) {
sb.append('\n').append(problem);
}
return sb.toString();
}
private void renderSubmitType(String action) {
try {
SubmitType type = SubmitType.valueOf(action);

View File

@@ -334,6 +334,10 @@ limitations under the License.
.changeExtension {
padding-top: 5px;
}
.pushCertStatus {
padding-left: 5px;
}
</ui:style>
<g:HTMLPanel styleName='{style.cs2}'>

View File

@@ -202,4 +202,9 @@ public interface ChangeConstants extends Constants {
String diffAllUnified();
String votable();
String pushCertMissing();
String pushCertBad();
String pushCertOk();
String pushCertTrusted();
}

View File

@@ -184,3 +184,8 @@ diffAllSideBySide = All Side-by-Side
diffAllUnified = All Unified
votable = Votable:
pushCertMissing = This patch set was created without a push certificate
pushCertBad = Push certificate is invalid
pushCertOk = Push certificate is valid, but key is not trusted
pushCertTrusted = Push certificate is valid and key is trusted