Merge "InlineEdit: Add support for diff web links"
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index c7bcd86..576cec6 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -1445,6 +1445,50 @@
   RnJvbSA3ZGFkY2MxNTNmZGVhMTdhYTg0ZmYzMmE2ZTI0NWRiYjY...
 ----
 
+[[get-edit-meta-data]]
+=== Retrieve meta data of a file from Change Edit
+--
+'GET /changes/link:#change-id[\{change-id\}]/edit/path%2fto%2ffile/meta
+--
+
+Retrieves meta data of a file from a change edit. Currently only
+web links are returned.
+
+.Request
+----
+  GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/edit/foo/meta HTTP/1.0
+----
+
+This REST endpoint retrieves additional information for a file in a
+change edit. As result an link:#edit-file-info[EditFileInfo] entity is
+returned.
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+  "web_links":[
+    {
+      "show_on_side_by_side_diff_view": true,
+      "name": "side-by-side preview diff",
+      "image_url": "plugins/xdocs/static/sideBySideDiffPreview.png",
+      "url": "#/x/xdocs/c/42/1..0/README.md",
+      "target": "_self"
+    },
+    {
+      "show_on_unified_diff_view": true,
+      "name": "unified preview diff",
+      "image_url": "plugins/xdocs/static/unifiedDiffPreview.png",
+      "url": "#/x/xdocs/c/42/1..0/README.md,unified",
+      "target": "_self"
+    }
+  ]}
+----
+
 [[get-edit-message]]
 === Retrieve commit message from Change Edit or current patch set of the change
 --
@@ -3558,6 +3602,19 @@
 Whether the web link should be shown on the unified diff screen.
 |=======================
 
+[[edit-file-info]]
+=== EditFileInfo
+The `EditFileInfo` entity contains additional information
+of a file within a change edit.
+
+[options="header",cols="1,^1,5"]
+|===========================
+|Field Name    ||Description
+|`web_links`   |optional|
+Links to the diff info in external sites as a list of
+link:#web-link-info[WebLinkInfo] entities.
+|===========================
+
 [[edit-info]]
 === EditInfo
 The `EditInfo` entity contains information about a change edit.
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java
index ca5d434..dd02cb1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.client.changes;
 
 import com.google.gerrit.client.VoidResult;
+import com.google.gerrit.client.editor.EditFileInfo;
 import com.google.gerrit.client.rpc.GerritCallback;
 import com.google.gerrit.client.rpc.HttpCallback;
 import com.google.gerrit.client.rpc.NativeString;
@@ -42,6 +43,20 @@
     api.get(cb);
   }
 
+  /** Get meta info for change edit. */
+  public static void getMeta(PatchSet.Id id, String path,
+      AsyncCallback<EditFileInfo> cb) {
+    RestApi api;
+    if (id.get() != 0) {
+      throw new IllegalStateException("only supported for edits");
+    } else if (Patch.COMMIT_MSG.equals(path)) {
+      api = metaFile(id.getParentKey().get(), Patch.COMMIT_MSG);
+    } else {
+      api = metaFile(id.getParentKey().get(), path);
+    }
+    api.get(cb);
+  }
+
   /** Put message into a change edit. */
   public static void putMessage(int id, String m, GerritCallback<VoidResult> cb) {
     editMessage(id).put(m, cb);
@@ -77,6 +92,10 @@
     return ChangeApi.edit(id).id(path);
   }
 
+  private static RestApi metaFile(int id, String path) {
+    return ChangeApi.edit(id).id(path).view("meta");
+  }
+
   private static class Input extends JavaScriptObject {
     static Input create() {
       return createObject().cast();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditFileInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditFileInfo.java
new file mode 100644
index 0000000..c833c5d
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditFileInfo.java
@@ -0,0 +1,26 @@
+// 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.editor;
+
+import com.google.gerrit.client.DiffWebLinkInfo;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
+
+public class EditFileInfo extends JavaScriptObject {
+  public final native JsArray<DiffWebLinkInfo> web_links() /*-{ return this.web_links; }-*/;
+
+  protected EditFileInfo() {
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
index a945bd2..47afb0a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
@@ -17,6 +17,7 @@
 import static com.google.gwt.dom.client.Style.Visibility.HIDDEN;
 import static com.google.gwt.dom.client.Style.Visibility.VISIBLE;
 
+import com.google.gerrit.client.DiffWebLinkInfo;
 import com.google.gerrit.client.Gerrit;
 import com.google.gerrit.client.JumpKeys;
 import com.google.gerrit.client.VoidResult;
@@ -24,6 +25,8 @@
 import com.google.gerrit.client.changes.ChangeApi;
 import com.google.gerrit.client.changes.ChangeEditApi;
 import com.google.gerrit.client.changes.ChangeInfo;
+import com.google.gerrit.client.diff.DiffApi;
+import com.google.gerrit.client.diff.DiffInfo;
 import com.google.gerrit.client.diff.FileInfo;
 import com.google.gerrit.client.diff.Header;
 import com.google.gerrit.client.patches.PatchUtil;
@@ -32,6 +35,7 @@
 import com.google.gerrit.client.rpc.HttpCallback;
 import com.google.gerrit.client.rpc.HttpResponse;
 import com.google.gerrit.client.rpc.NativeString;
+import com.google.gerrit.client.rpc.Natives;
 import com.google.gerrit.client.rpc.RestApi;
 import com.google.gerrit.client.rpc.ScreenLoadCallback;
 import com.google.gerrit.client.ui.InlineHyperlink;
@@ -85,6 +89,8 @@
   private DiffPreferences prefs;
   private CodeMirror cm;
   private HttpResponse<NativeString> content;
+  private EditFileInfo editFileInfo;
+  private DiffInfo patchSetDiffInfo;
 
   @UiField Element header;
   @UiField Element project;
@@ -153,6 +159,33 @@
           }
         }));
 
+
+    if (revision.get() == 0) {
+      ChangeEditApi.getMeta(revision, path,
+          group1.add(new AsyncCallback<EditFileInfo>() {
+            @Override
+            public void onSuccess(EditFileInfo editInfo) {
+              editFileInfo = editInfo;
+            }
+
+            @Override
+            public void onFailure(Throwable e) {
+            }
+          }));
+    } else {
+      // TODO(davido): We probably want to create dedicated GET EditScreenMeta
+      // REST endpoint. Abuse GET diff for now, as it retrieves links we need.
+      DiffApi.diff(revision, path)
+        .base(base)
+        .webLinksOnly()
+        .get(new GerritCallback<DiffInfo>() {
+          @Override
+          public void onSuccess(DiffInfo diffInfo) {
+            patchSetDiffInfo = diffInfo;
+          }
+      });
+    }
+
     ChangeEditApi.get(revision, path,
         group2.add(new HttpCallback<NativeString>() {
           final AsyncCallback<Void> modeCallback = group3.addEmpty();
@@ -182,8 +215,10 @@
     group3.addListener(new ScreenLoadCallback<Void>(this) {
       @Override
       protected void preDisplay(Void result) {
-        initEditor(content);
+        initEditor(content, editFileInfo, patchSetDiffInfo);
         content = null;
+        editFileInfo = null;
+        patchSetDiffInfo = null;
       }
     });
     group1.done();
@@ -301,7 +336,8 @@
     Gerrit.display(PageLinks.toChangeInEditMode(revision.getParentKey()));
   }
 
-  private void initEditor(HttpResponse<NativeString> file) {
+  private void initEditor(HttpResponse<NativeString> file,
+      EditFileInfo info, DiffInfo diffInfo) {
     ModeInfo mode = null;
     String content = "";
     if (file != null) {
@@ -310,7 +346,7 @@
         mode = ModeInfo.findMode(file.getContentType(), path);
       }
     }
-    renderLinks();
+    renderLinks(info, diffInfo);
     cm = CodeMirror.create(editor, Configuration.create()
         .set("value", content)
         .set("readOnly", false)
@@ -330,10 +366,23 @@
         .on("Ctrl-S", save()));
   }
 
-  private void renderLinks() {
+  private void renderLinks(EditFileInfo info, DiffInfo diffInfo) {
     for (InlineHyperlink link : getLinks()) {
       linkPanel.add(link);
     }
+    if (info != null) {
+      renderPluginLinks(Natives.asList(info.web_links()));
+    } else if (diffInfo != null) {
+      renderPluginLinks(Natives.asList(diffInfo.web_links()));
+    }
+  }
+
+  private void renderPluginLinks(List<DiffWebLinkInfo> links) {
+    if (links != null) {
+      for (DiffWebLinkInfo webLink : links) {
+        linkPanel.add(webLink.toAnchor());
+      }
+    }
   }
 
   private List<InlineHyperlink> getLinks() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java
index 0759370..e5f5381 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java
@@ -16,7 +16,9 @@
 
 import com.google.common.base.Optional;
 import com.google.common.base.Strings;
+import com.google.common.collect.FluentIterable;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.common.DiffWebLinkInfo;
 import com.google.gerrit.extensions.common.EditInfo;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AcceptsCreate;
@@ -39,6 +41,7 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.WebLinks;
 import com.google.gerrit.server.edit.ChangeEdit;
 import com.google.gerrit.server.edit.ChangeEditJson;
 import com.google.gerrit.server.edit.ChangeEditModifier;
@@ -56,6 +59,7 @@
 import org.kohsuke.args4j.Option;
 
 import java.io.IOException;
+import java.util.List;
 
 @Singleton
 public class ChangeEdits implements
@@ -451,6 +455,38 @@
   }
 
   @Singleton
+  static class GetMeta implements RestReadView<ChangeEditResource> {
+    private final WebLinks webLinks;
+
+    @Inject
+    GetMeta(WebLinks webLinks) {
+      this.webLinks = webLinks;
+    }
+
+    @Override
+    public FileInfo apply(ChangeEditResource rsrc) {
+      FileInfo r = new FileInfo();
+      ChangeEdit edit = rsrc.getChangeEdit();
+      Change change = edit.getChange();
+      FluentIterable<DiffWebLinkInfo> links =
+          webLinks.getDiffLinks(change.getProject().get(),
+              change.getChangeId(),
+              edit.getBasePatchSet().getPatchSetId(),
+              edit.getBasePatchSet().getRefName(),
+              rsrc.getPath(),
+              0,
+              edit.getRefName(),
+              rsrc.getPath());
+      r.webLinks = links.isEmpty() ? null : links.toList();
+      return r;
+    }
+
+    static class FileInfo {
+      List<DiffWebLinkInfo> webLinks;
+    }
+  }
+
+  @Singleton
   public static class EditMessage implements
       RestModifyView<ChangeResource, EditMessage.Input> {
     public static class Input {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
index 3645f36..ac4489e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
@@ -113,6 +113,7 @@
     put(CHANGE_EDIT_KIND, "/").to(ChangeEdits.Put.class);
     delete(CHANGE_EDIT_KIND).to(ChangeEdits.DeleteContent.class);
     get(CHANGE_EDIT_KIND, "/").to(ChangeEdits.Get.class);
+    get(CHANGE_EDIT_KIND, "meta").to(ChangeEdits.GetMeta.class);
 
     install(new FactoryModule() {
       @Override