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:
@@ -104,4 +104,7 @@ public interface Resources extends ClientBundle {
|
|||||||
|
|
||||||
@Source("warning.png")
|
@Source("warning.png")
|
||||||
public ImageResource warning();
|
public ImageResource warning();
|
||||||
|
|
||||||
|
@Source("question.png")
|
||||||
|
public ImageResource question();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -311,6 +311,9 @@ public class ChangeInfo extends JavaScriptObject {
|
|||||||
public final native boolean hasFetch() /*-{ return this.hasOwnProperty('fetch') }-*/;
|
public final native boolean hasFetch() /*-{ return this.hasOwnProperty('fetch') }-*/;
|
||||||
public final native NativeMap<FetchInfo> fetch() /*-{ return this.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) {
|
public static void sortRevisionInfoByNumber(JsArray<RevisionInfo> list) {
|
||||||
final int editParent = findEditParent(list);
|
final int editParent = findEditParent(list);
|
||||||
Collections.sort(Natives.asList(list), new Comparator<RevisionInfo>() {
|
Collections.sort(Natives.asList(list), new Comparator<RevisionInfo>() {
|
||||||
|
|||||||
@@ -12,17 +12,34 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// 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.JavaScriptObject;
|
||||||
import com.google.gwt.core.client.JsArrayString;
|
import com.google.gwt.core.client.JsArrayString;
|
||||||
|
|
||||||
public class GpgKeyInfo extends JavaScriptObject {
|
public class GpgKeyInfo extends JavaScriptObject {
|
||||||
|
public enum Status {
|
||||||
|
BAD, OK, TRUSTED;
|
||||||
|
}
|
||||||
|
|
||||||
public final native String id() /*-{ return this.id; }-*/;
|
public final native String id() /*-{ return this.id; }-*/;
|
||||||
public final native String fingerprint() /*-{ return this.fingerprint; }-*/;
|
public final native String fingerprint() /*-{ return this.fingerprint; }-*/;
|
||||||
public final native JsArrayString userIds() /*-{ return this.user_ids; }-*/;
|
public final native JsArrayString userIds() /*-{ return this.user_ids; }-*/;
|
||||||
public final native String key() /*-{ return this.key; }-*/;
|
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() {
|
protected GpgKeyInfo() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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 |
@@ -16,6 +16,7 @@ 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.info.GpgKeyInfo;
|
||||||
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.NativeMap;
|
||||||
import com.google.gerrit.client.rpc.NativeString;
|
import com.google.gerrit.client.rpc.NativeString;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
package com.google.gerrit.client.account;
|
package com.google.gerrit.client.account;
|
||||||
|
|
||||||
import com.google.gerrit.client.Gerrit;
|
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.GerritCallback;
|
||||||
import com.google.gerrit.client.rpc.NativeMap;
|
import com.google.gerrit.client.rpc.NativeMap;
|
||||||
import com.google.gerrit.client.rpc.Natives;
|
import com.google.gerrit.client.rpc.Natives;
|
||||||
|
|||||||
@@ -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.MessageInfo;
|
||||||
import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
|
import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
|
||||||
import com.google.gerrit.client.info.FileInfo;
|
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;
|
||||||
import com.google.gerrit.client.projects.ConfigInfoCache.Entry;
|
import com.google.gerrit.client.projects.ConfigInfoCache.Entry;
|
||||||
import com.google.gerrit.client.rpc.CallbackGroup;
|
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);
|
private static final Binder uiBinder = GWT.create(Binder.class);
|
||||||
|
|
||||||
interface Style extends CssResource {
|
interface Style extends CssResource {
|
||||||
String labelName();
|
|
||||||
String avatar();
|
String avatar();
|
||||||
String label_user();
|
String hashtagName();
|
||||||
String label_ok();
|
String highlight();
|
||||||
String label_reject();
|
String labelName();
|
||||||
String label_may();
|
String label_may();
|
||||||
String label_need();
|
String label_need();
|
||||||
|
String label_ok();
|
||||||
|
String label_reject();
|
||||||
|
String label_user();
|
||||||
|
String pushCertStatus();
|
||||||
String replyBox();
|
String replyBox();
|
||||||
String selected();
|
String selected();
|
||||||
String highlight();
|
|
||||||
String hashtagName();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ChangeScreen get(NativeEvent in) {
|
static ChangeScreen get(NativeEvent in) {
|
||||||
@@ -164,11 +167,14 @@ public class ChangeScreen extends Screen {
|
|||||||
@UiField Reviewers reviewers;
|
@UiField Reviewers reviewers;
|
||||||
@UiField Hashtags hashtags;
|
@UiField Hashtags hashtags;
|
||||||
@UiField Element hashtagTableRow;
|
@UiField Element hashtagTableRow;
|
||||||
|
|
||||||
@UiField FlowPanel ownerPanel;
|
@UiField FlowPanel ownerPanel;
|
||||||
@UiField InlineHyperlink ownerLink;
|
@UiField InlineHyperlink ownerLink;
|
||||||
|
|
||||||
@UiField Element uploaderRow;
|
@UiField Element uploaderRow;
|
||||||
@UiField FlowPanel uploaderPanel;
|
@UiField FlowPanel uploaderPanel;
|
||||||
@UiField InlineLabel uploaderName;
|
@UiField InlineLabel uploaderName;
|
||||||
|
|
||||||
@UiField Element statusText;
|
@UiField Element statusText;
|
||||||
@UiField Image projectSettings;
|
@UiField Image projectSettings;
|
||||||
@UiField AnchorElement projectSettingsLink;
|
@UiField AnchorElement projectSettingsLink;
|
||||||
@@ -292,11 +298,19 @@ public class ChangeScreen extends Screen {
|
|||||||
p.add(extensionPanel);
|
p.add(extensionPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean enableSignedPush() {
|
||||||
|
return Gerrit.info().receive().enableSignedPush();
|
||||||
|
}
|
||||||
|
|
||||||
void loadChangeInfo(boolean fg, AsyncCallback<ChangeInfo> cb) {
|
void loadChangeInfo(boolean fg, AsyncCallback<ChangeInfo> cb) {
|
||||||
RestApi call = ChangeApi.detail(changeId.get());
|
RestApi call = ChangeApi.detail(changeId.get());
|
||||||
ChangeList.addOptions(call, EnumSet.of(
|
EnumSet<ListChangesOption> opts = EnumSet.of(
|
||||||
ListChangesOption.CHANGE_ACTIONS,
|
ListChangesOption.ALL_REVISIONS,
|
||||||
ListChangesOption.ALL_REVISIONS));
|
ListChangesOption.CHANGE_ACTIONS);
|
||||||
|
if (enableSignedPush()) {
|
||||||
|
opts.add(ListChangesOption.PUSH_CERTIFICATES);
|
||||||
|
}
|
||||||
|
ChangeList.addOptions(call, opts);
|
||||||
if (!fg) {
|
if (!fg) {
|
||||||
call.background();
|
call.background();
|
||||||
}
|
}
|
||||||
@@ -1146,13 +1160,14 @@ public class ChangeScreen extends Screen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void renderChangeInfo(ChangeInfo info) {
|
private void renderChangeInfo(ChangeInfo info) {
|
||||||
|
RevisionInfo revisionInfo = info.revision(revision);
|
||||||
changeInfo = info;
|
changeInfo = info;
|
||||||
lastDisplayedUpdate = info.updated();
|
lastDisplayedUpdate = info.updated();
|
||||||
|
|
||||||
labels.set(info);
|
labels.set(info);
|
||||||
|
|
||||||
renderOwner(info);
|
renderOwner(info);
|
||||||
renderUploader(info, revision);
|
renderUploader(info, revisionInfo);
|
||||||
renderActionTextDate(info);
|
renderActionTextDate(info);
|
||||||
renderDiffBaseListBox(info);
|
renderDiffBaseListBox(info);
|
||||||
initReplyButton(info, revision);
|
initReplyButton(info, revision);
|
||||||
@@ -1189,7 +1204,7 @@ public class ChangeScreen extends Screen {
|
|||||||
// render it faster.
|
// render it faster.
|
||||||
if (!info.status().isOpen()
|
if (!info.status().isOpen()
|
||||||
|| !revision.equals(info.currentRevision())
|
|| !revision.equals(info.currentRevision())
|
||||||
|| info.revision(revision).isEdit()) {
|
|| revisionInfo.isEdit()) {
|
||||||
setVisible(strategy, false);
|
setVisible(strategy, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1200,7 +1215,6 @@ public class ChangeScreen extends Screen {
|
|||||||
quickApprove.setVisible(false);
|
quickApprove.setVisible(false);
|
||||||
actions.reloadRevisionActions(emptyMap);
|
actions.reloadRevisionActions(emptyMap);
|
||||||
|
|
||||||
RevisionInfo revisionInfo = info.revision(revision);
|
|
||||||
boolean current = revision.equals(info.currentRevision())
|
boolean current = revision.equals(info.currentRevision())
|
||||||
&& !revisionInfo.isEdit();
|
&& !revisionInfo.isEdit();
|
||||||
|
|
||||||
@@ -1252,10 +1266,12 @@ public class ChangeScreen extends Screen {
|
|||||||
: String.valueOf(info.owner()._accountId()), Change.Status.NEW));
|
: String.valueOf(info.owner()._accountId()), Change.Status.NEW));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderUploader(ChangeInfo info, String revision) {
|
private void renderUploader(ChangeInfo changeInfo, RevisionInfo revInfo) {
|
||||||
AccountInfo uploader = info.revision(revision).uploader();
|
AccountInfo uploader = revInfo.uploader();
|
||||||
if (uploader == null
|
boolean isOwner = uploader == null
|
||||||
|| uploader._accountId() == info.owner()._accountId()) {
|
|| uploader._accountId() == changeInfo.owner()._accountId();
|
||||||
|
renderPushCertificate(revInfo, isOwner ? ownerPanel : uploaderPanel);
|
||||||
|
if (isOwner) {
|
||||||
uploaderRow.getStyle().setDisplay(Display.NONE);
|
uploaderRow.getStyle().setDisplay(Display.NONE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1269,6 +1285,37 @@ public class ChangeScreen extends Screen {
|
|||||||
uploaderName.setTitle(email(uploader, name));
|
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) {
|
private static String name(AccountInfo info) {
|
||||||
return info.name() != null
|
return info.name() != null
|
||||||
? info.name()
|
? info.name()
|
||||||
@@ -1279,6 +1326,21 @@ public class ChangeScreen extends Screen {
|
|||||||
return info.email() != null ? info.email() : name;
|
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) {
|
private void renderSubmitType(String action) {
|
||||||
try {
|
try {
|
||||||
SubmitType type = SubmitType.valueOf(action);
|
SubmitType type = SubmitType.valueOf(action);
|
||||||
|
|||||||
@@ -334,6 +334,10 @@ limitations under the License.
|
|||||||
.changeExtension {
|
.changeExtension {
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pushCertStatus {
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
</ui:style>
|
</ui:style>
|
||||||
|
|
||||||
<g:HTMLPanel styleName='{style.cs2}'>
|
<g:HTMLPanel styleName='{style.cs2}'>
|
||||||
|
|||||||
@@ -202,4 +202,9 @@ public interface ChangeConstants extends Constants {
|
|||||||
String diffAllUnified();
|
String diffAllUnified();
|
||||||
|
|
||||||
String votable();
|
String votable();
|
||||||
|
|
||||||
|
String pushCertMissing();
|
||||||
|
String pushCertBad();
|
||||||
|
String pushCertOk();
|
||||||
|
String pushCertTrusted();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,3 +184,8 @@ diffAllSideBySide = All Side-by-Side
|
|||||||
diffAllUnified = All Unified
|
diffAllUnified = All Unified
|
||||||
|
|
||||||
votable = Votable:
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user