diff options
11 files changed, 277 insertions, 274 deletions
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt index 2e74b7c281..8ab906b090 100644 --- a/Documentation/dev-plugins.txt +++ b/Documentation/dev-plugins.txt @@ -982,6 +982,20 @@ MyType(@PluginData java.io.File myDir) { } ---- +[[download-commands]] +Download Commands +----------------- + +Gerrit offers commands for downloading changes using different +download schemes (e.g. for downloading via different network +protocols). Plugins can contribute download schemes and download +commands by implementing +`com.google.gerrit.extensions.config.DownloadScheme` and +`com.google.gerrit.extensions.config.DownloadCommand`. + +The download schemes and download commands which are used most often +are provided by the Gerrit core plugin `download-commands`. + [[documentation]] Documentation ------------- diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt index 8c1fa16e9c..9404d00846 100644 --- a/Documentation/rest-api-changes.txt +++ b/Documentation/rest-api-changes.txt @@ -171,6 +171,13 @@ default. Optional fields are: * `ALL_REVISIONS`: describe all revisions, not just current. -- +[[download_commands]] +-- +* `DOWNLOAD_COMMANDS`: include the `commands` field in the + link:#fetch-info[FetchInfo] for revisions. Only valid when the + `CURRENT_REVISION` or `ALL_REVISIONS` option is selected. +-- + [[draft_comments]] -- * `DRAFT_COMMENTS`: include the `has_draft_comments` field for @@ -233,7 +240,7 @@ default. Optional fields are: .Request ---- - GET /changes/?q=97&o=CURRENT_REVISION&o=CURRENT_COMMIT&o=CURRENT_FILES HTTP/1.0 + GET /changes/?q=97&o=CURRENT_REVISION&o=CURRENT_COMMIT&o=CURRENT_FILES&o=DOWNLOAD_COMMANDS HTTP/1.0 ---- .Response @@ -267,11 +274,33 @@ default. Optional fields are: "fetch": { "git": { "url": "git://localhost/gerrit", - "ref": "refs/changes/97/97/1" + "ref": "refs/changes/97/97/1", + "commands": { + "Checkout": "git fetch git://localhost/gerrit refs/changes/97/97/1 \u0026\u0026 git checkout FETCH_HEAD", + "Cherry-Pick": "git fetch git://localhost/gerrit refs/changes/97/97/1 \u0026\u0026 git cherry-pick FETCH_HEAD", + "Format-Patch": "git fetch git://localhost/gerrit refs/changes/97/97/1 \u0026\u0026 git format-patch -1 --stdout FETCH_HEAD", + "Pull": "git pull git://localhost/gerrit refs/changes/97/97/1" + } }, "http": { - "url": "http://127.0.0.1:8080/gerrit", - "ref": "refs/changes/97/97/1" + "url": "http://myuser@127.0.0.1:8080/gerrit", + "ref": "refs/changes/97/97/1", + "commands": { + "Checkout": "git fetch http://myuser@127.0.0.1:8080/gerrit refs/changes/97/97/1 \u0026\u0026 git checkout FETCH_HEAD", + "Cherry-Pick": "git fetch http://myuser@127.0.0.1:8080/gerrit refs/changes/97/97/1 \u0026\u0026 git cherry-pick FETCH_HEAD", + "Format-Patch": "git fetch http://myuser@127.0.0.1:8080/gerrit refs/changes/97/97/1 \u0026\u0026 git format-patch -1 --stdout FETCH_HEAD", + "Pull": "git pull http://myuser@127.0.0.1:8080/gerrit refs/changes/97/97/1" + } + }, + "ssh": { + "url": "ssh://myuser@*:29418/gerrit", + "ref": "refs/changes/97/97/1", + "commands": { + "Checkout": "git fetch ssh://myuser@*:29418/gerrit refs/changes/97/97/1 \u0026\u0026 git checkout FETCH_HEAD", + "Cherry-Pick": "git fetch ssh://myuser@*:29418/gerrit refs/changes/97/97/1 \u0026\u0026 git cherry-pick FETCH_HEAD", + "Format-Patch": "git fetch ssh://myuser@*:29418/gerrit refs/changes/97/97/1 \u0026\u0026 git format-patch -1 --stdout FETCH_HEAD", + "Pull": "git pull ssh://myuser@*:29418/gerrit refs/changes/97/97/1" + } } }, "commit": { @@ -2949,11 +2978,15 @@ FetchInfo The `FetchInfo` entity contains information about how to fetch a patch set via a certain protocol. -[options="header",width="50%",cols="1,6"] +[options="header",width="50%",cols="1,^1,5"] |========================== -|Field Name |Description -|`url` |The URL of the project. -|`ref` |The ref of the patch set. +|Field Name ||Description +|`url` ||The URL of the project. +|`ref` ||The ref of the patch set. +|`commands` |optional| +The download commands for this patch set as a map that maps the command +names to the commands. + +Only set if link:#download_commands[download commands] are requested. |========================== [[file-info]] diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/changes/ListChangesOption.java b/gerrit-common/src/main/java/com/google/gerrit/common/changes/ListChangesOption.java index ff80b43870..276c332607 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/changes/ListChangesOption.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/changes/ListChangesOption.java @@ -46,7 +46,10 @@ public enum ListChangesOption { REVIEWED(11), /** Include draft comments for the caller. */ - DRAFT_COMMENTS(12); + DRAFT_COMMENTS(12), + + /** Include download commands for the caller. */ + DOWNLOAD_COMMANDS(13); private final int value; diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java index 0cb3fa20fb..9842049410 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java @@ -92,6 +92,12 @@ public interface GerritCss extends CssResource { String diffTextHunkHeader(); String diffTextINSERT(); String diffTextNoLF(); + String downloadBox(); + String downloadBoxTable(); + String downloadBoxTableCommandColumn(); + String downloadBoxSpacer(); + String downloadBoxScheme(); + String downloadBoxCopyLabel(); String downloadLink(); String downloadLinkCopyLabel(); String downloadLinkHeader(); diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java index 9ba04dd369..5f6b4e9615 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java @@ -280,11 +280,8 @@ public class ChangeScreen2 extends Screen { } private void initDownloadAction(ChangeInfo info, String revision) { - downloadAction = new DownloadAction( - info.legacy_id(), - info.project(), - info.revision(revision), - style, headerLine, download); + downloadAction = + new DownloadAction(info, revision, style, headerLine, download); } private void initProjectLink(ChangeInfo info) { diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadAction.java index 91ff99cbcb..891cc10f21 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadAction.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadAction.java @@ -14,10 +14,7 @@ package com.google.gerrit.client.change; -import com.google.gerrit.client.changes.ChangeInfo.FetchInfo; -import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo; -import com.google.gerrit.client.rpc.NativeMap; -import com.google.gerrit.reviewdb.client.Change; +import com.google.gerrit.client.changes.ChangeInfo; import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gwt.user.client.ui.UIObject; import com.google.gwt.user.client.ui.Widget; @@ -26,20 +23,15 @@ class DownloadAction extends RightSidePopdownAction { private final DownloadBox downloadBox; DownloadAction( - Change.Id changeId, - String project, - RevisionInfo revision, + ChangeInfo info, + String revision, ChangeScreen2.Style style, UIObject relativeTo, Widget downloadButton) { super(style, relativeTo, downloadButton); - this.downloadBox = new DownloadBox( - revision.has_fetch() - ? revision.fetch() - : NativeMap.<FetchInfo> create(), - revision.name(), - project, - new PatchSet.Id(changeId, revision._number())); + this.downloadBox = new DownloadBox(info, revision, + new PatchSet.Id(info.legacy_id(), + info.revision(revision)._number())); } Widget getWidget() { diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java index ae29801eab..7c57620ad4 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java @@ -14,102 +14,107 @@ package com.google.gerrit.client.change; -import static com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadScheme.REPO_DOWNLOAD; - import com.google.gerrit.client.Gerrit; import com.google.gerrit.client.account.AccountApi; +import com.google.gerrit.client.changes.ChangeApi; +import com.google.gerrit.client.changes.ChangeInfo; import com.google.gerrit.client.changes.ChangeInfo.FetchInfo; +import com.google.gerrit.client.changes.ChangeList; import com.google.gerrit.client.rpc.NativeMap; +import com.google.gerrit.client.rpc.Natives; import com.google.gerrit.client.rpc.RestApi; +import com.google.gerrit.common.changes.ListChangesOption; import com.google.gerrit.reviewdb.client.AccountGeneralPreferences; import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadScheme; import com.google.gerrit.reviewdb.client.PatchSet; -import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JavaScriptObject; -import com.google.gwt.dom.client.AnchorElement; -import com.google.gwt.dom.client.Element; import com.google.gwt.event.dom.client.ChangeEvent; -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.event.dom.client.ChangeHandler; import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.HTMLPanel; +import com.google.gwt.user.client.ui.Anchor; +import com.google.gwt.user.client.ui.FlexTable; +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.InlineLabel; import com.google.gwt.user.client.ui.ListBox; -import com.google.gwt.user.client.ui.UIObject; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.user.client.ui.Widget; import com.google.gwtexpui.clippy.client.CopyableLabel; -class DownloadBox extends Composite { - interface Binder extends UiBinder<HTMLPanel, DownloadBox> {} - private static final Binder uiBinder = GWT.create(Binder.class); +import java.util.EnumSet; - private final NativeMap<FetchInfo> fetch; +class DownloadBox extends VerticalPanel { + private final ChangeInfo change; private final String revision; - private final String project; private final PatchSet.Id psId; + private final FlexTable commandTable; + private final ListBox scheme; + private NativeMap<FetchInfo> fetch; - @UiField ListBox scheme; - @UiField CopyableLabel checkout; - @UiField CopyableLabel cherryPick; - @UiField CopyableLabel pull; - @UiField AnchorElement patchBase64; - @UiField AnchorElement patchZip; - @UiField Element repoSection; - @UiField CopyableLabel repoDownload; - - DownloadBox(NativeMap<FetchInfo> fetch, String revision, - String project, PatchSet.Id psId) { - this.fetch = fetch; + DownloadBox(ChangeInfo change, String revision, PatchSet.Id psId) { + this.change = change; this.revision = revision; - this.project = project; this.psId = psId; - initWidget(uiBinder.createAndBindUi(this)); + this.commandTable = new FlexTable(); + this.scheme = new ListBox(); + this.scheme.addChangeHandler(new ChangeHandler() { + @Override + public void onChange(ChangeEvent event) { + renderCommands(); + if (Gerrit.isSignedIn()) { + saveScheme(); + } + } + }); + + setStyleName(Gerrit.RESOURCES.css().downloadBox()); + commandTable.setStyleName(Gerrit.RESOURCES.css().downloadBoxTable()); + scheme.setStyleName(Gerrit.RESOURCES.css().downloadBoxScheme()); + add(commandTable); } @Override protected void onLoad() { - if (scheme.getItemCount() == 0) { - renderScheme(fetch); - } - } - - @UiHandler("scheme") - void onScheme(ChangeEvent event) { - renderCommands(); + if (fetch == null) { + RestApi call = ChangeApi.detail(change.legacy_id().get()); + ChangeList.addOptions(call, EnumSet.of( + revision.equals(change.current_revision()) + ? ListChangesOption.CURRENT_REVISION + : ListChangesOption.ALL_REVISIONS, + ListChangesOption.DOWNLOAD_COMMANDS)); + call.get(new AsyncCallback<ChangeInfo>() { + @Override + public void onSuccess(ChangeInfo result) { + fetch = result.revision(revision).fetch(); + renderScheme(); + } - if (Gerrit.isSignedIn()) { - saveScheme(); + @Override + public void onFailure(Throwable caught) { + } + }); } } private void renderCommands() { - FetchInfo info = fetch.get(scheme.getValue(scheme.getSelectedIndex())); - checkout(info); - cherryPick(info); - pull(info); - patch(info); - repo(info); - } + commandTable.removeAllRows(); - private void checkout(FetchInfo info) { - checkout.setText( - "git fetch " + info.url() + " " + info.ref() - + " && git checkout FETCH_HEAD"); - } - - private void cherryPick(FetchInfo info) { - cherryPick.setText( - "git fetch " + info.url() + " " + info.ref() - + " && git cherry-pick FETCH_HEAD"); - } - - private void pull(FetchInfo info) { - pull.setText("git pull " + info.url() + " " + info.ref()); + if (scheme.getItemCount() > 0) { + FetchInfo fetchInfo = + fetch.get(scheme.getValue(scheme.getSelectedIndex())); + for (String commandName : Natives.keys(fetchInfo.commands())) { + CopyableLabel copyLabel = + new CopyableLabel(fetchInfo.command(commandName)); + copyLabel.setStyleName(Gerrit.RESOURCES.css().downloadBoxCopyLabel()); + insertCommand(commandName, copyLabel); + } + } + insertPatch(); + insertCommand(null, scheme); } - private void patch(FetchInfo info) { + private void insertPatch() { String id = revision.substring(0, 7); - patchBase64.setInnerText(id + ".diff.base64"); + Anchor patchBase64 = new Anchor(id + ".diff.base64"); patchBase64.setHref(new RestApi("/changes/") .id(psId.getParentKey().get()) .view("revisions") @@ -118,7 +123,7 @@ class DownloadBox extends Composite { .addParameterTrue("download") .url()); - patchZip.setInnerText(id + ".diff.zip"); + Anchor patchZip = new Anchor(id + ".diff.zip"); patchZip.setHref(new RestApi("/changes/") .id(psId.getParentKey().get()) .view("revisions") @@ -126,45 +131,52 @@ class DownloadBox extends Composite { .view("patch") .addParameterTrue("zip") .url()); + + HorizontalPanel p = new HorizontalPanel(); + p.add(patchBase64); + InlineLabel spacer = new InlineLabel("|"); + spacer.setStyleName(Gerrit.RESOURCES.css().downloadBoxSpacer()); + p.add(spacer); + p.add(patchZip); + insertCommand("Patch-File", p); } - private void repo(FetchInfo info) { - if (Gerrit.getConfig().getDownloadSchemes().contains(REPO_DOWNLOAD)) { - UIObject.setVisible(repoSection, true); - repoDownload.setText("repo download " - + project - + " " + psId.getParentKey().get() + "/" + psId.get()); + private void insertCommand(String commandName, Widget w) { + int row = commandTable.getRowCount(); + commandTable.insertRow(row); + commandTable.getCellFormatter().addStyleName(row, 0, + Gerrit.RESOURCES.css().downloadBoxTableCommandColumn()); + if (commandName != null) { + commandTable.setText(row, 0, commandName); + } + if (w != null) { + commandTable.setWidget(row, 1, w); } } - private void renderScheme(NativeMap<FetchInfo> fetch) { + private void renderScheme() { for (String id : fetch.keySet()) { - FetchInfo info = fetch.get(id); - String u = info.url(); - int css = u.indexOf("://"); - if (css > 0) { - int s = u.indexOf('/', css + 3); - if (s > 0) { - u = u.substring(0, s + 1); - } - } - scheme.addItem(u, id); + scheme.addItem(id); } - if (scheme.getItemCount() == 1) { - scheme.setSelectedIndex(0); + if (scheme.getItemCount() == 0) { scheme.setVisible(false); } else { - int select = 0; - String find = getUserPreference(); - if (find != null) { - for (int i = 0; i < scheme.getItemCount(); i++) { - if (find.equals(scheme.getValue(i))) { - select = i; - break; + if (scheme.getItemCount() == 1) { + scheme.setSelectedIndex(0); + scheme.setVisible(false); + } else { + int select = 0; + String find = getUserPreference(); + if (find != null) { + for (int i = 0; i < scheme.getItemCount(); i++) { + if (find.equals(scheme.getValue(i))) { + select = i; + break; + } } } + scheme.setSelectedIndex(select); } - scheme.setSelectedIndex(select); } renderCommands(); } @@ -177,11 +189,14 @@ class DownloadBox extends Composite { switch (pref) { case ANON_GIT: return "git"; - case HTTP: case ANON_HTTP: + return "anonymous http"; + case HTTP: return "http"; case SSH: return "ssh"; + case REPO_DOWNLOAD: + return "repo"; default: return null; } @@ -216,10 +231,14 @@ class DownloadBox extends Composite { String id = scheme.getValue(scheme.getSelectedIndex()); if ("git".equals(id)) { return DownloadScheme.ANON_GIT; + } else if ("anonymous http".equals(id)) { + return DownloadScheme.ANON_HTTP; } else if ("http".equals(id)) { return DownloadScheme.HTTP; } else if ("ssh".equals(id)) { return DownloadScheme.SSH; + } else if ("repo".equals(id)) { + return DownloadScheme.REPO_DOWNLOAD; } return null; } diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.ui.xml deleted file mode 100644 index 449098253c..0000000000 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.ui.xml +++ /dev/null @@ -1,98 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -Copyright (C) 2013 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:c='urn:import:com.google.gwtexpui.clippy.client'> - <ui:with field='res' type='com.google.gerrit.client.change.Resources'/> - <ui:style> - @external .gwt-TextBox; - - .downloadBox { - min-width: 580px; - margin: 5px; - } - - .table { - border-spacing: 0; - width: 100%; - } - .table th { - text-align: left; - font-weight: normal; - white-space: nowrap; - max-height: 18px; - width: 80px; - padding-right: 5px; - } - - .scheme { - float: right; - } - - .clippy { - font-size: smaller; - font-family: monospace; - } - .clippy span { - width: 500px; - white-space: nowrap; - display: inline-block; - overflow: hidden; - text-overflow: ellipsis; - } - .clippy .gwt-TextBox { - padding: 0; - margin: 0; - border: 0; - max-height: 18px; - width: 500px; - } - .clippy div { - float: right; - } - </ui:style> - <g:HTMLPanel styleName='{style.downloadBox}'> - <table class='{style.table}'> - <tr> - <th><ui:msg>Checkout</ui:msg></th> - <td><c:CopyableLabel ui:field='checkout' styleName='{style.clippy}'/></td> - </tr> - <tr> - <th><ui:msg>Cherry Pick</ui:msg></th> - <td><c:CopyableLabel ui:field='cherryPick' styleName='{style.clippy}'/></td> - </tr> - <tr> - <th><ui:msg>Pull</ui:msg></th> - <td><c:CopyableLabel ui:field='pull' styleName='{style.clippy}'/></td> - </tr> - <tr> - <th><ui:msg>Patch File</ui:msg></th> - <td><a ui:field='patchZip'/> | <a ui:field='patchBase64'/></td> - </tr> - <tr ui:field='repoSection' style='display: NONE' aria-hidden='true'> - <th><ui:msg>repo</ui:msg></th> - <td><c:CopyableLabel ui:field='repoDownload' styleName='{style.clippy}'/></td> - </tr> - <tr> - <td colspan='2'> - <g:ListBox ui:field='scheme' styleName='{style.scheme}'/> - </td> - </tr> - </table> - </g:HTMLPanel> -</ui:UiBinder> diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java index d46b7cabad..f82abee9ac 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java @@ -232,6 +232,8 @@ public class ChangeInfo extends JavaScriptObject { public static class FetchInfo extends JavaScriptObject { public final native String url() /*-{ return this.url }-*/; public final native String ref() /*-{ return this.ref }-*/; + public final native NativeMap<NativeString> commands() /*-{ return this.commands }-*/; + public final native String command(String n) /*-{ return this.commands[n]; }-*/; protected FetchInfo () { } diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css index 651ab513ad..5e604fbacd 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css @@ -1090,6 +1090,51 @@ td.removeReviewerCell { padding-left: 4em; border-left: none; } + +.downloadBox { + min-width: 580px; + margin: 5px; +} +.downloadBoxTable { + border-spacing: 0; + width: 100%; +} +.downloadBoxTableCommandColumn { + text-align: left; + font-weight: normal; + white-space: nowrap; + max-height: 18px; + width: 80px; + padding-right: 5px; +} +.downloadBoxSpacer { + margin-left: 5px; + margin-right: 5px; +} +.downloadBoxScheme { + float: right; +} +.downloadBoxCopyLabel { + font-size: smaller; + font-family: monospace; +} +.downloadBoxCopyLabel span { + width: 500px; + white-space: nowrap; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; +} +.downloadBoxCopyLabel .gwt-TextBox { + padding: 0; + margin: 0; + border: 0; + max-height: 18px; + width: 500px; +} +.downloadBoxCopyLabel div { + float: right; +} td.downloadLinkListCell { padding: 0px; } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java index c7f6d162be..d6ecc5e4c9 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java @@ -23,6 +23,7 @@ import static com.google.gerrit.common.changes.ListChangesOption.CURRENT_FILES; import static com.google.gerrit.common.changes.ListChangesOption.CURRENT_REVISION; import static com.google.gerrit.common.changes.ListChangesOption.DETAILED_ACCOUNTS; import static com.google.gerrit.common.changes.ListChangesOption.DETAILED_LABELS; +import static com.google.gerrit.common.changes.ListChangesOption.DOWNLOAD_COMMANDS; import static com.google.gerrit.common.changes.ListChangesOption.DRAFT_COMMENTS; import static com.google.gerrit.common.changes.ListChangesOption.LABELS; import static com.google.gerrit.common.changes.ListChangesOption.MESSAGES; @@ -30,7 +31,6 @@ import static com.google.gerrit.common.changes.ListChangesOption.REVIEWED; import com.google.common.base.Joiner; import com.google.common.base.Objects; -import com.google.common.base.Strings; import com.google.common.collect.HashBasedTable; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableList; @@ -50,6 +50,8 @@ import com.google.gerrit.common.data.LabelValue; import com.google.gerrit.common.data.Permission; import com.google.gerrit.common.data.PermissionRange; import com.google.gerrit.common.data.SubmitRecord; +import com.google.gerrit.extensions.config.DownloadCommand; +import com.google.gerrit.extensions.config.DownloadScheme; import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.restapi.RestView; import com.google.gerrit.extensions.restapi.Url; @@ -69,8 +71,6 @@ import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.AccountInfo; import com.google.gerrit.server.actions.ActionInfo; -import com.google.gerrit.server.config.CanonicalWebUrl; -import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.extensions.webui.UiActions; import com.google.gerrit.server.git.LabelNormalizer; import com.google.gerrit.server.patch.PatchListNotAvailableException; @@ -79,14 +79,11 @@ import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException; import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.NoSuchChangeException; import com.google.gerrit.server.query.change.ChangeData; -import com.google.gerrit.server.ssh.SshAdvertisedAddresses; import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.ResultSet; import com.google.inject.Inject; import com.google.inject.Provider; -import com.google.inject.Singleton; -import org.eclipse.jgit.lib.Config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -120,30 +117,6 @@ public class ChangeJson { } }; - @Singleton - static class Urls { - final String git; - final String http; - final String ssh; - - @Inject - Urls(@GerritServerConfig Config cfg, - @SshAdvertisedAddresses List<String> sshAddresses) { - this.git = ensureSlash(cfg.getString("gerrit", null, "canonicalGitUrl")); - this.http = ensureSlash(cfg.getString("gerrit", null, "gitHttpUrl")); - this.ssh = !sshAddresses.isEmpty() - ? ensureSlash("ssh://" + sshAddresses.get(0)) - : null; - } - - private static String ensureSlash(String in) { - if (in != null && !in.endsWith("/")) { - return in + "/"; - } - return in; - } - } - private final Provider<ReviewDb> db; private final LabelNormalizer labelNormalizer; private final Provider<CurrentUser> userProvider; @@ -153,8 +126,8 @@ public class ChangeJson { private final PatchSetInfoFactory patchSetInfoFactory; private final FileInfoJson fileInfoJson; private final AccountInfo.Loader.Factory accountLoaderFactory; - private final Provider<String> urlProvider; - private final Urls urls; + private final DynamicMap<DownloadScheme> downloadSchemes; + private final DynamicMap<DownloadCommand> downloadCommands; private final DynamicMap<RestView<ChangeResource>> changes; private final Revisions revisions; @@ -175,8 +148,8 @@ public class ChangeJson { PatchSetInfoFactory psi, FileInfoJson fileInfoJson, AccountInfo.Loader.Factory ailf, - @CanonicalWebUrl Provider<String> curl, - Urls urls, + DynamicMap<DownloadScheme> downloadSchemes, + DynamicMap<DownloadCommand> downloadCommands, DynamicMap<RestView<ChangeResource>> changes, Revisions revisions) { this.db = db; @@ -188,8 +161,8 @@ public class ChangeJson { this.patchSetInfoFactory = psi; this.fileInfoJson = fileInfoJson; this.accountLoaderFactory = ailf; - this.urlProvider = curl; - this.urls = urls; + this.downloadSchemes = downloadSchemes; + this.downloadCommands = downloadCommands; this.changes = changes; this.revisions = revisions; @@ -872,28 +845,37 @@ public class ChangeJson { private Map<String, FetchInfo> makeFetchMap(ChangeData cd, PatchSet in) throws OrmException { Map<String, FetchInfo> r = Maps.newLinkedHashMap(); - String refName = in.getRefName(); - ChangeControl ctl = control(cd); - if (ctl != null && ctl.forUser(anonymous).isPatchVisible(in, db.get())) { - if (urls.git != null) { - r.put("git", new FetchInfo(urls.git - + cd.change(db).getProject().get(), refName)); + + for (DynamicMap.Entry<DownloadScheme> e : downloadSchemes) { + String schemeName = e.getExportName(); + DownloadScheme scheme = e.getProvider().get(); + if (!scheme.isEnabled() + || (scheme.isAuthRequired() && !userProvider.get().isIdentifiedUser())) { + continue; } - } - if (urls.http != null) { - r.put("http", new FetchInfo(urls.http - + cd.change(db).getProject().get(), refName)); - } else { - String http = urlProvider.get(); - if (!Strings.isNullOrEmpty(http)) { - r.put("http", new FetchInfo(http - + cd.change(db).getProject().get(), refName)); + + ChangeControl ctl = control(cd); + if (!scheme.isAuthRequired() + && !ctl.forUser(anonymous).isPatchVisible(in, db.get())) { + continue; + } + + String projectName = ctl.getProject().getNameKey().get(); + String url = scheme.getUrl(projectName); + String refName = in.getRefName(); + FetchInfo fetchInfo = new FetchInfo(url, refName); + r.put(schemeName, fetchInfo); + + if (has(DOWNLOAD_COMMANDS)) { + for (DynamicMap.Entry<DownloadCommand> e2 : downloadCommands) { + String commandName = e2.getExportName(); + DownloadCommand command = e2.getProvider().get(); + String c = command.getCommand(scheme, projectName, refName); + if (c != null) { + fetchInfo.addCommand(commandName, c); + } + } } - } - if (urls.ssh != null) { - r.put("ssh", new FetchInfo( - urls.ssh + cd.change(db).getProject().get(), - refName)); } return r; @@ -960,11 +942,19 @@ public class ChangeJson { static class FetchInfo { String url; String ref; + Map<String, String> commands; FetchInfo(String url, String ref) { this.url = url; this.ref = ref; } + + void addCommand(String name, String command) { + if (commands == null) { + commands = Maps.newTreeMap(); + } + commands.put(name, command); + } } static class GitPerson { |