diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java index 7d22e9b9c3..a850a9b1fc 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java @@ -49,6 +49,10 @@ public interface GerritConstants extends Constants { String branchCreationConfirmationMessage(); + String tagCreationDialogTitle(); + + String tagCreationConfirmationMessage(); + String branchDeletionDialogTitle(); String branchDeletionConfirmationMessage(); diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties index b4665b234f..cab988b558 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties @@ -20,6 +20,9 @@ confirmationDialogCancel = Cancel branchCreationDialogTitle = Branch Creation branchCreationConfirmationMessage = The following branch was successfully created: +tagCreationDialogTitle = Tag Creation +tagCreationConfirmationMessage = The following tag was successfully created: + branchDeletionDialogTitle = Branch Deletion branchDeletionConfirmationMessage = Do you really want to delete the following branches? diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java index b8da3a7a2f..d4b575fb33 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java @@ -27,6 +27,8 @@ public interface AdminConstants extends Constants { String defaultBranchName(); + String defaultTagName(); + String defaultRevisionSpec(); String buttonDeleteIncludedGroup(); @@ -171,18 +173,22 @@ public interface AdminConstants extends Constants { String columnBranchRevision(); + String columnTagName(); + + String columnTagRevision(); + String initialRevision(); String buttonAddBranch(); String buttonDeleteBranch(); + String buttonAddTag(); + String saveHeadButton(); String cancelHeadButton(); - String columnTagName(); - String groupItemHelp(); String groupListTitle(); diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties index 2f13a50053..404d90373e 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties @@ -1,6 +1,7 @@ defaultAccountName = Name or Email defaultAccountGroupName = Group Name defaultBranchName = Branch Name +defaultTagName = Tag Name defaultRevisionSpec = Revision (Branch or SHA-1) buttonDeleteIncludedGroup = Delete @@ -81,12 +82,14 @@ typeRemoved = Removed columnBranchName = Branch Name columnBranchRevision = Revision +columnTagName = Tag Name +columnTagRevision = Revision initialRevision = Initial Revision buttonAddBranch = Create Branch +buttonAddTag = Create Tag buttonDeleteBranch = Delete saveHeadButton = Save cancelHeadButton = Cancel -columnTagName = Tag Name groupItemHelp = group diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectTagsScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectTagsScreen.java index e64f569356..de40a5d3bb 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectTagsScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectTagsScreen.java @@ -16,32 +16,58 @@ 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.access.AccessMap; +import com.google.gerrit.client.access.ProjectAccessInfo; 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.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.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 NpTextBox filterTxt; - private Query query; private Hyperlink prev; private Hyperlink next; - private TagsTable tagsTable; + private TagsTable tagTable; + private Button addTag; + private HintTextBox nameTxtBox; + private HintTextBox irevTxtBox; + private FlowPanel addPanel; + private NpTextBox filterTxt; + private Query query; public ProjectTagsScreen(Project.NameKey toShow) { super(toShow); @@ -52,31 +78,95 @@ public class ProjectTagsScreen extends PaginatedProjectScreen { return PageLinks.toProjectTags(getProjectKey()); } + @Override + protected void onLoad() { + super.onLoad(); + addPanel.setVisible(false); + AccessMap.get( + getProjectKey(), + new GerritCallback() { + @Override + public void onSuccess(ProjectAccessInfo result) { + addPanel.setVisible(result.canAddRefs()); + } + }); + query = new Query(match).start(start).run(); + savedPanel = TAGS; + } + + private void updateForm() { + addTag.setEnabled(true); + nameTxtBox.setEnabled(true); + irevTxtBox.setEnabled(true); + } + @Override protected void onInitUI() { super.onInitUI(); initPageHeader(); + prev = PagingHyperlink.createPrev(); prev.setVisible(false); next = PagingHyperlink.createNext(); next.setVisible(false); - tagsTable = new TagsTable(); + addPanel = new FlowPanel(); + + Grid addGrid = new Grid(2, 2); + addGrid.setStyleName(Gerrit.RESOURCES.css().addBranch()); + int texBoxLength = 50; + + nameTxtBox = new HintTextBox(); + nameTxtBox.setVisibleLength(texBoxLength); + nameTxtBox.setHintText(AdminConstants.I.defaultTagName()); + nameTxtBox.addKeyPressHandler( + new KeyPressHandler() { + @Override + public void onKeyPress(KeyPressEvent event) { + if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) { + doAddNewTag(); + } + } + }); + 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( + new KeyPressHandler() { + @Override + public void onKeyPress(KeyPressEvent event) { + if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) { + doAddNewTag(); + } + } + }); + addGrid.setText(1, 0, AdminConstants.I.initialRevision() + ":"); + addGrid.setWidget(1, 1, irevTxtBox); + + 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(); HorizontalPanel buttons = new HorizontalPanel(); buttons.setStyleName(Gerrit.RESOURCES.css().branchTablePrevNextLinks()); buttons.add(prev); buttons.add(next); - add(tagsTable); + add(tagTable); add(buttons); - } - - @Override - protected void onLoad() { - super.onLoad(); - query = new Query(match).start(start).run(); - savedPanel = TAGS; + add(addPanel); } private void initPageHeader() { @@ -95,8 +185,10 @@ public class ProjectTagsScreen extends PaginatedProjectScreen { Query q = new Query(filterTxt.getValue()); if (match.equals(q.qMatch)) { q.start(start); - } else if (query == null) { - q.run(); + } else { + if (query == null) { + q.run(); + } query = q; } } @@ -105,16 +197,113 @@ public class ProjectTagsScreen extends PaginatedProjectScreen { 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; + } + + addTag.setEnabled(false); + ProjectApi.createTag( + getProjectKey(), + tagName, + rev, + new GerritCallback() { + @Override + public void onSuccess(TagInfo tag) { + showAddedTag(tag); + nameTxtBox.setText(""); + irevTxtBox.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 { TagsTable() { table.setWidth(""); - table.setText(0, 1, AdminConstants.I.columnTagName()); - table.setText(0, 2, AdminConstants.I.columnBranchRevision()); + 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().dataHeader()); + fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().iconHeader()); fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader()); + fmt.addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader()); + } + + Set getCheckedRefs() { + Set 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 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 display(List tags) { @@ -135,18 +324,22 @@ public class ProjectTagsScreen extends PaginatedProjectScreen { } void populate(int row, TagInfo k) { - table.setWidget(row, 1, new InlineHTML(highlight(k.getShortName(), match))); + table.setText(row, 1, ""); + + table.setWidget(row, 2, new InlineHTML(highlight(k.getShortName(), match))); if (k.revision() != null) { - table.setText(row, 2, k.revision()); + table.setText(row, 3, k.revision()); } else { - table.setText(row, 2, ""); + table.setText(row, 3, ""); } FlexCellFormatter fmt = table.getFlexCellFormatter(); + String iconCellStyle = Gerrit.RESOURCES.css().iconCell(); String dataCellStyle = Gerrit.RESOURCES.css().dataCell(); - fmt.addStyleName(row, 1, dataCellStyle); + fmt.addStyleName(row, 1, iconCellStyle); fmt.addStyleName(row, 2, dataCellStyle); + fmt.addStyleName(row, 3, dataCellStyle); setRowItem(row, k); } @@ -216,10 +409,10 @@ public class ProjectTagsScreen extends PaginatedProjectScreen { ProjectTagsScreen.this.start = qStart; if (result.length() <= pageSize) { - tagsTable.display(Natives.asList(result)); + tagTable.display(Natives.asList(result)); next.setVisible(false); } else { - tagsTable.displaySubset(Natives.asList(result), 0, result.length() - 1); + tagTable.displaySubset(Natives.asList(result), 0, result.length() - 1); setupNavigationLink(next, qMatch, qStart + pageSize); } if (qStart > 0) { @@ -228,6 +421,10 @@ public class ProjectTagsScreen extends PaginatedProjectScreen { prev.setVisible(false); } + Set checkedRefs = tagTable.getCheckedRefs(); + tagTable.setChecked(checkedRefs); + updateForm(); + if (!isCurrentView()) { display(); } diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java index d1343dfd9e..1350e1a67c 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java @@ -60,6 +60,14 @@ public class ProjectApi { return call; } + /** Create a new tag */ + public static void createTag( + Project.NameKey name, String ref, String revision, AsyncCallback cb) { + TagInput input = TagInput.create(); + input.setRevision(revision); + project(name).view("tags").id(ref).ifNoneMatch().put(input, cb); + } + /** Retrieve all visible tags of the project */ public static void getTags(Project.NameKey name, AsyncCallback> cb) { project(name).view("tags").get(cb); @@ -325,6 +333,16 @@ public class ProjectApi { public final native void put(String n, ConfigParameterValue v) /*-{ this[n] = v; }-*/; } + private static class TagInput extends JavaScriptObject { + static TagInput create() { + return (TagInput) createObject(); + } + + protected TagInput() {} + + final native void setRevision(String r) /*-{ if(r)this.revision=r; }-*/; + } + private static class BranchInput extends JavaScriptObject { static BranchInput create() { return (BranchInput) createObject(); diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/TagInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/TagInfo.java index 1657b25aeb..4ed623f11a 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/TagInfo.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/TagInfo.java @@ -14,7 +14,15 @@ package com.google.gerrit.client.projects; +import com.google.gerrit.client.info.ActionInfo; +import com.google.gerrit.client.info.WebLinkInfo; +import com.google.gerrit.client.rpc.NativeMap; +import com.google.gwt.core.client.JsArray; + public class TagInfo extends RefInfo { + public final native NativeMap actions() /*-{ return this.actions }-*/; + + public final native JsArray webLinks() /*-{ return this.web_links; }-*/; // TODO(dpursehouse) add extra tag-related fields (message, tagger, etc) protected TagInfo() {}