572 lines
17 KiB
Java
572 lines
17 KiB
Java
// 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.admin;
|
|
|
|
import static com.google.gerrit.client.ui.Util.highlight;
|
|
|
|
import com.google.gerrit.client.ConfirmationCallback;
|
|
import com.google.gerrit.client.ConfirmationDialog;
|
|
import com.google.gerrit.client.ErrorDialog;
|
|
import com.google.gerrit.client.Gerrit;
|
|
import com.google.gerrit.client.VoidResult;
|
|
import com.google.gerrit.client.access.AccessMap;
|
|
import com.google.gerrit.client.access.ProjectAccessInfo;
|
|
import com.google.gerrit.client.info.WebLinkInfo;
|
|
import com.google.gerrit.client.projects.ProjectApi;
|
|
import com.google.gerrit.client.projects.TagInfo;
|
|
import com.google.gerrit.client.rpc.GerritCallback;
|
|
import com.google.gerrit.client.rpc.Natives;
|
|
import com.google.gerrit.client.rpc.ScreenLoadCallback;
|
|
import com.google.gerrit.client.ui.HintTextBox;
|
|
import com.google.gerrit.client.ui.Hyperlink;
|
|
import com.google.gerrit.client.ui.NavigationTable;
|
|
import com.google.gerrit.client.ui.PagingHyperlink;
|
|
import com.google.gerrit.common.PageLinks;
|
|
import com.google.gerrit.reviewdb.client.Project;
|
|
import com.google.gwt.core.client.JsArray;
|
|
import com.google.gwt.core.client.Scheduler;
|
|
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
|
|
import com.google.gwt.event.dom.client.ClickEvent;
|
|
import com.google.gwt.event.dom.client.ClickHandler;
|
|
import com.google.gwt.event.dom.client.KeyCodes;
|
|
import com.google.gwt.event.dom.client.KeyPressEvent;
|
|
import com.google.gwt.event.dom.client.KeyPressHandler;
|
|
import com.google.gwt.event.dom.client.KeyUpEvent;
|
|
import com.google.gwt.event.dom.client.KeyUpHandler;
|
|
import com.google.gwt.event.logical.shared.ValueChangeEvent;
|
|
import com.google.gwt.event.logical.shared.ValueChangeHandler;
|
|
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.FlowPanel;
|
|
import com.google.gwt.user.client.ui.Grid;
|
|
import com.google.gwt.user.client.ui.HorizontalPanel;
|
|
import com.google.gwt.user.client.ui.InlineHTML;
|
|
import com.google.gwt.user.client.ui.Label;
|
|
import com.google.gwt.user.client.ui.TextBox;
|
|
import com.google.gwt.user.client.ui.Widget;
|
|
import com.google.gwtexpui.globalkey.client.NpTextBox;
|
|
import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
public class ProjectTagsScreen extends PaginatedProjectScreen {
|
|
private Hyperlink prev;
|
|
private Hyperlink next;
|
|
private TagsTable tagTable;
|
|
private Button delTag;
|
|
private Button addTag;
|
|
private HintTextBox nameTxtBox;
|
|
private HintTextBox irevTxtBox;
|
|
private HintTextBox annotationTxtBox;
|
|
private FlowPanel addPanel;
|
|
private NpTextBox filterTxt;
|
|
private Query query;
|
|
|
|
public ProjectTagsScreen(Project.NameKey toShow) {
|
|
super(toShow);
|
|
}
|
|
|
|
@Override
|
|
public String getScreenToken() {
|
|
return PageLinks.toProjectTags(getProjectKey());
|
|
}
|
|
|
|
@Override
|
|
protected void onLoad() {
|
|
super.onLoad();
|
|
addPanel.setVisible(false);
|
|
AccessMap.get(
|
|
getProjectKey(),
|
|
new GerritCallback<ProjectAccessInfo>() {
|
|
@Override
|
|
public void onSuccess(ProjectAccessInfo result) {
|
|
addPanel.setVisible(result.canAddRefs());
|
|
}
|
|
});
|
|
query = new Query(match).start(start).run();
|
|
savedPanel = TAGS;
|
|
}
|
|
|
|
private void updateForm() {
|
|
tagTable.updateDeleteButton();
|
|
addTag.setEnabled(true);
|
|
nameTxtBox.setEnabled(true);
|
|
irevTxtBox.setEnabled(true);
|
|
annotationTxtBox.setEnabled(true);
|
|
}
|
|
|
|
@Override
|
|
protected void onInitUI() {
|
|
super.onInitUI();
|
|
initPageHeader();
|
|
|
|
prev = PagingHyperlink.createPrev();
|
|
prev.setVisible(false);
|
|
|
|
next = PagingHyperlink.createNext();
|
|
next.setVisible(false);
|
|
|
|
addPanel = new FlowPanel();
|
|
|
|
Grid addGrid = new Grid(3, 2);
|
|
addGrid.setStyleName(Gerrit.RESOURCES.css().addBranch());
|
|
int texBoxLength = 50;
|
|
|
|
KeyPressHandler onKeyPress =
|
|
new KeyPressHandler() {
|
|
@Override
|
|
public void onKeyPress(KeyPressEvent event) {
|
|
if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
|
|
doAddNewTag();
|
|
}
|
|
}
|
|
};
|
|
|
|
nameTxtBox = new HintTextBox();
|
|
nameTxtBox.setVisibleLength(texBoxLength);
|
|
nameTxtBox.setHintText(AdminConstants.I.defaultTagName());
|
|
nameTxtBox.addKeyPressHandler(onKeyPress);
|
|
addGrid.setText(0, 0, AdminConstants.I.columnTagName() + ":");
|
|
addGrid.setWidget(0, 1, nameTxtBox);
|
|
|
|
irevTxtBox = new HintTextBox();
|
|
irevTxtBox.setVisibleLength(texBoxLength);
|
|
irevTxtBox.setHintText(AdminConstants.I.defaultRevisionSpec());
|
|
irevTxtBox.addKeyPressHandler(onKeyPress);
|
|
addGrid.setText(1, 0, AdminConstants.I.revision() + ":");
|
|
addGrid.setWidget(1, 1, irevTxtBox);
|
|
|
|
annotationTxtBox = new HintTextBox();
|
|
annotationTxtBox.setVisibleLength(texBoxLength);
|
|
annotationTxtBox.setHintText(AdminConstants.I.annotation());
|
|
annotationTxtBox.addKeyPressHandler(onKeyPress);
|
|
addGrid.setText(2, 0, AdminConstants.I.columnTagAnnotation() + ":");
|
|
addGrid.setWidget(2, 1, annotationTxtBox);
|
|
|
|
addTag = new Button(AdminConstants.I.buttonAddTag());
|
|
addTag.addClickHandler(
|
|
new ClickHandler() {
|
|
@Override
|
|
public void onClick(ClickEvent event) {
|
|
doAddNewTag();
|
|
}
|
|
});
|
|
addPanel.add(addGrid);
|
|
addPanel.add(addTag);
|
|
|
|
tagTable = new TagsTable();
|
|
|
|
delTag = new Button(AdminConstants.I.buttonDeleteTag());
|
|
delTag.setStyleName(Gerrit.RESOURCES.css().branchTableDeleteButton());
|
|
delTag.addClickHandler(
|
|
new ClickHandler() {
|
|
@Override
|
|
public void onClick(ClickEvent event) {
|
|
tagTable.deleteChecked();
|
|
}
|
|
});
|
|
|
|
HorizontalPanel buttons = new HorizontalPanel();
|
|
buttons.setStyleName(Gerrit.RESOURCES.css().branchTablePrevNextLinks());
|
|
buttons.add(delTag);
|
|
buttons.add(prev);
|
|
buttons.add(next);
|
|
add(tagTable);
|
|
add(buttons);
|
|
add(addPanel);
|
|
}
|
|
|
|
private void initPageHeader() {
|
|
parseToken();
|
|
HorizontalPanel hp = new HorizontalPanel();
|
|
hp.setStyleName(Gerrit.RESOURCES.css().projectFilterPanel());
|
|
Label filterLabel = new Label(AdminConstants.I.projectFilter());
|
|
filterLabel.setStyleName(Gerrit.RESOURCES.css().projectFilterLabel());
|
|
hp.add(filterLabel);
|
|
filterTxt = new NpTextBox();
|
|
filterTxt.setValue(match);
|
|
filterTxt.addKeyUpHandler(
|
|
new KeyUpHandler() {
|
|
@Override
|
|
public void onKeyUp(KeyUpEvent event) {
|
|
Query q = new Query(filterTxt.getValue());
|
|
if (match.equals(q.qMatch)) {
|
|
q.start(start);
|
|
} else {
|
|
if (query == null) {
|
|
q.run();
|
|
}
|
|
query = q;
|
|
}
|
|
}
|
|
});
|
|
hp.add(filterTxt);
|
|
add(hp);
|
|
}
|
|
|
|
private void doAddNewTag() {
|
|
String tagName = nameTxtBox.getText().trim();
|
|
if (tagName.isEmpty()) {
|
|
nameTxtBox.setFocus(true);
|
|
return;
|
|
}
|
|
|
|
String rev = irevTxtBox.getText().trim();
|
|
if (rev.isEmpty()) {
|
|
irevTxtBox.setText("HEAD");
|
|
Scheduler.get()
|
|
.scheduleDeferred(
|
|
new ScheduledCommand() {
|
|
@Override
|
|
public void execute() {
|
|
irevTxtBox.selectAll();
|
|
irevTxtBox.setFocus(true);
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
String annotation = annotationTxtBox.getText().trim();
|
|
if (annotation.isEmpty()) {
|
|
annotation = null;
|
|
}
|
|
|
|
addTag.setEnabled(false);
|
|
ProjectApi.createTag(
|
|
getProjectKey(),
|
|
tagName,
|
|
rev,
|
|
annotation,
|
|
new GerritCallback<TagInfo>() {
|
|
@Override
|
|
public void onSuccess(TagInfo tag) {
|
|
showAddedTag(tag);
|
|
nameTxtBox.setText("");
|
|
irevTxtBox.setText("");
|
|
annotationTxtBox.setText("");
|
|
query = new Query(match).start(start).run();
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(Throwable caught) {
|
|
addTag.setEnabled(true);
|
|
selectAllAndFocus(nameTxtBox);
|
|
new ErrorDialog(caught.getMessage()).center();
|
|
}
|
|
});
|
|
}
|
|
|
|
void showAddedTag(TagInfo tag) {
|
|
SafeHtmlBuilder b = new SafeHtmlBuilder();
|
|
b.openElement("b");
|
|
b.append(Gerrit.C.tagCreationConfirmationMessage());
|
|
b.closeElement("b");
|
|
|
|
b.openElement("p");
|
|
b.append(tag.ref());
|
|
b.closeElement("p");
|
|
|
|
ConfirmationDialog confirmationDialog =
|
|
new ConfirmationDialog(
|
|
Gerrit.C.tagCreationDialogTitle(),
|
|
b.toSafeHtml(),
|
|
new ConfirmationCallback() {
|
|
@Override
|
|
public void onOk() {
|
|
//do nothing
|
|
}
|
|
});
|
|
confirmationDialog.center();
|
|
confirmationDialog.setCancelVisible(false);
|
|
}
|
|
|
|
private static void selectAllAndFocus(TextBox textBox) {
|
|
textBox.selectAll();
|
|
textBox.setFocus(true);
|
|
}
|
|
|
|
private class TagsTable extends NavigationTable<TagInfo> {
|
|
private ValueChangeHandler<Boolean> updateDeleteHandler;
|
|
boolean canDelete;
|
|
|
|
TagsTable() {
|
|
table.setWidth("");
|
|
table.setText(0, 2, AdminConstants.I.columnTagName());
|
|
table.setText(0, 3, AdminConstants.I.columnTagRevision());
|
|
|
|
FlexCellFormatter fmt = table.getFlexCellFormatter();
|
|
fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().iconHeader());
|
|
fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
|
|
fmt.addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader());
|
|
fmt.addStyleName(0, 4, Gerrit.RESOURCES.css().dataHeader());
|
|
|
|
updateDeleteHandler =
|
|
new ValueChangeHandler<Boolean>() {
|
|
@Override
|
|
public void onValueChange(ValueChangeEvent<Boolean> event) {
|
|
updateDeleteButton();
|
|
}
|
|
};
|
|
}
|
|
|
|
Set<String> getCheckedRefs() {
|
|
Set<String> refs = new HashSet<>();
|
|
for (int row = 1; row < table.getRowCount(); row++) {
|
|
TagInfo k = getRowItem(row);
|
|
if (k != null
|
|
&& table.getWidget(row, 1) instanceof CheckBox
|
|
&& ((CheckBox) table.getWidget(row, 1)).getValue()) {
|
|
refs.add(k.ref());
|
|
}
|
|
}
|
|
return refs;
|
|
}
|
|
|
|
void setChecked(Set<String> refs) {
|
|
for (int row = 1; row < table.getRowCount(); row++) {
|
|
TagInfo k = getRowItem(row);
|
|
if (k != null && refs.contains(k.ref()) && table.getWidget(row, 1) instanceof CheckBox) {
|
|
((CheckBox) table.getWidget(row, 1)).setValue(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void deleteChecked() {
|
|
final Set<String> refs = getCheckedRefs();
|
|
|
|
SafeHtmlBuilder b = new SafeHtmlBuilder();
|
|
b.openElement("b");
|
|
b.append(Gerrit.C.tagDeletionConfirmationMessage());
|
|
b.closeElement("b");
|
|
|
|
b.openElement("p");
|
|
boolean first = true;
|
|
for (String ref : refs) {
|
|
if (!first) {
|
|
b.append(",").br();
|
|
}
|
|
b.append(ref);
|
|
first = false;
|
|
}
|
|
b.closeElement("p");
|
|
|
|
if (refs.isEmpty()) {
|
|
updateDeleteButton();
|
|
return;
|
|
}
|
|
|
|
delTag.setEnabled(false);
|
|
ConfirmationDialog confirmationDialog =
|
|
new ConfirmationDialog(
|
|
Gerrit.C.tagDeletionDialogTitle(),
|
|
b.toSafeHtml(),
|
|
new ConfirmationCallback() {
|
|
@Override
|
|
public void onOk() {
|
|
deleteTags(refs);
|
|
}
|
|
|
|
@Override
|
|
public void onCancel() {
|
|
tagTable.updateDeleteButton();
|
|
}
|
|
});
|
|
confirmationDialog.center();
|
|
}
|
|
|
|
private void deleteTags(final Set<String> tags) {
|
|
ProjectApi.deleteTags(
|
|
getProjectKey(),
|
|
tags,
|
|
new GerritCallback<VoidResult>() {
|
|
@Override
|
|
public void onSuccess(VoidResult result) {
|
|
query = new Query(match).start(start).run();
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(Throwable caught) {
|
|
query = new Query(match).start(start).run();
|
|
super.onFailure(caught);
|
|
}
|
|
});
|
|
}
|
|
|
|
void display(List<TagInfo> tags) {
|
|
displaySubset(tags, 0, tags.size());
|
|
}
|
|
|
|
void displaySubset(List<TagInfo> tags, int fromIndex, int toIndex) {
|
|
canDelete = false;
|
|
|
|
while (1 < table.getRowCount()) {
|
|
table.removeRow(table.getRowCount() - 1);
|
|
}
|
|
|
|
for (TagInfo k : tags.subList(fromIndex, toIndex)) {
|
|
int row = table.getRowCount();
|
|
table.insertRow(row);
|
|
applyDataRowStyle(row);
|
|
populate(row, k);
|
|
}
|
|
}
|
|
|
|
void populate(int row, TagInfo k) {
|
|
if (k.canDelete()) {
|
|
CheckBox sel = new CheckBox();
|
|
sel.addValueChangeHandler(updateDeleteHandler);
|
|
table.setWidget(row, 1, sel);
|
|
canDelete = true;
|
|
} else {
|
|
table.setText(row, 1, "");
|
|
}
|
|
|
|
table.setWidget(row, 2, new InlineHTML(highlight(k.getShortName(), match)));
|
|
|
|
if (k.revision() != null) {
|
|
table.setText(row, 3, k.revision());
|
|
} else {
|
|
table.setText(row, 3, "");
|
|
}
|
|
|
|
FlowPanel actionsPanel = new FlowPanel();
|
|
if (k.webLinks() != null) {
|
|
for (WebLinkInfo webLink : Natives.asList(k.webLinks())) {
|
|
actionsPanel.add(webLink.toAnchor());
|
|
}
|
|
}
|
|
table.setWidget(row, 4, actionsPanel);
|
|
|
|
FlexCellFormatter fmt = table.getFlexCellFormatter();
|
|
String iconCellStyle = Gerrit.RESOURCES.css().iconCell();
|
|
String dataCellStyle = Gerrit.RESOURCES.css().dataCell();
|
|
fmt.addStyleName(row, 1, iconCellStyle);
|
|
fmt.addStyleName(row, 2, dataCellStyle);
|
|
fmt.addStyleName(row, 3, dataCellStyle);
|
|
fmt.addStyleName(row, 4, dataCellStyle);
|
|
|
|
setRowItem(row, k);
|
|
}
|
|
|
|
boolean hasTagCanDelete() {
|
|
return canDelete;
|
|
}
|
|
|
|
void updateDeleteButton() {
|
|
boolean on = false;
|
|
for (int row = 1; row < table.getRowCount(); row++) {
|
|
Widget w = table.getWidget(row, 1);
|
|
if (w != null && w instanceof CheckBox) {
|
|
CheckBox sel = (CheckBox) w;
|
|
if (sel.getValue()) {
|
|
on = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
delTag.setEnabled(on);
|
|
}
|
|
|
|
@Override
|
|
protected void onOpenRow(int row) {
|
|
if (row > 0) {
|
|
movePointerTo(row);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected Object getRowItemKey(TagInfo item) {
|
|
return item.ref();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onShowView() {
|
|
super.onShowView();
|
|
if (match != null) {
|
|
filterTxt.setCursorPos(match.length());
|
|
}
|
|
filterTxt.setFocus(true);
|
|
}
|
|
|
|
private class Query {
|
|
private String qMatch;
|
|
private int qStart;
|
|
|
|
Query(String match) {
|
|
this.qMatch = match;
|
|
}
|
|
|
|
Query start(int start) {
|
|
this.qStart = start;
|
|
return this;
|
|
}
|
|
|
|
Query run() {
|
|
// Retrieve one more tag than page size to determine if there are more
|
|
// tags to display
|
|
ProjectApi.getTags(
|
|
getProjectKey(),
|
|
pageSize + 1,
|
|
qStart,
|
|
qMatch,
|
|
new ScreenLoadCallback<JsArray<TagInfo>>(ProjectTagsScreen.this) {
|
|
@Override
|
|
public void preDisplay(JsArray<TagInfo> result) {
|
|
if (!isAttached()) {
|
|
// View has been disposed.
|
|
} else if (query == Query.this) {
|
|
query = null;
|
|
showList(result);
|
|
} else {
|
|
query.run();
|
|
}
|
|
}
|
|
});
|
|
return this;
|
|
}
|
|
|
|
void showList(JsArray<TagInfo> result) {
|
|
setToken(getTokenForScreen(qMatch, qStart));
|
|
ProjectTagsScreen.this.match = qMatch;
|
|
ProjectTagsScreen.this.start = qStart;
|
|
|
|
if (result.length() <= pageSize) {
|
|
tagTable.display(Natives.asList(result));
|
|
next.setVisible(false);
|
|
} else {
|
|
tagTable.displaySubset(Natives.asList(result), 0, result.length() - 1);
|
|
setupNavigationLink(next, qMatch, qStart + pageSize);
|
|
}
|
|
if (qStart > 0) {
|
|
setupNavigationLink(prev, qMatch, qStart - pageSize);
|
|
} else {
|
|
prev.setVisible(false);
|
|
}
|
|
|
|
delTag.setVisible(tagTable.hasTagCanDelete());
|
|
Set<String> checkedRefs = tagTable.getCheckedRefs();
|
|
tagTable.setChecked(checkedRefs);
|
|
updateForm();
|
|
|
|
if (!isCurrentView()) {
|
|
display();
|
|
}
|
|
}
|
|
}
|
|
}
|