Allow users to stage images before upload

Currently images are uploaded immediately when selected, pasted or
dropped. Some users may prefer a 2 step process to upload images so
that they can check that the correct pictures are selected before
upload. The image upload has then to be triggered explicitely. Whether
images are staged or directly uploaded can be controlled by
configuration and a user preference.

Change-Id: I52845e12d3d71befc077132bea3162b15173d7ce
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/imagare/GetConfig.java b/src/main/java/com/googlesource/gerrit/plugins/imagare/GetConfig.java
index 6a488ae..f5a5f6a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/imagare/GetConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/imagare/GetConfig.java
@@ -41,11 +41,16 @@
     if (LinkDecoration.NONE.equals(info.linkDecoration)) {
       info.linkDecoration = null;
     }
+    info.stage = cfg.getBoolean("stage", false);
+    if (!info.stage) {
+      info.stage = null;
+    }
     return info;
   }
 
   public static class ConfigInfo {
     String defaultProject;
     LinkDecoration linkDecoration;
+    Boolean stage;
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/imagare/GetPreference.java b/src/main/java/com/googlesource/gerrit/plugins/imagare/GetPreference.java
index 532b09d..b2f506e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/imagare/GetPreference.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/imagare/GetPreference.java
@@ -33,6 +33,7 @@
   public static final String PREFERENCE = "preference";
   public static final String KEY_DEFAULT_PROJECT = "defaultProject";
   public static final String KEY_LINK_DECORATION = "linkDecoration";
+  public static final String KEY_STAGE = "stage";
 
   private final Provider<IdentifiedUser> self;
   private final ProjectCache projectCache;
@@ -62,16 +63,26 @@
     Config db =
         projectCache.getAllProjects().getConfig(pluginName + ".config").get();
     ConfigInfo info = new ConfigInfo();
+
     info.defaultProject =
         Objects.firstNonNull(
             db.getString(PREFERENCE, username, KEY_DEFAULT_PROJECT),
             globalCfg.defaultProject);
+
     info.linkDecoration =
         db.getEnum(PREFERENCE, username, KEY_LINK_DECORATION,
             globalCfg.linkDecoration);
     if (LinkDecoration.NONE.equals(info.linkDecoration)) {
       info.linkDecoration = null;
     }
+
+    info.stage =
+        db.getBoolean(PREFERENCE, username, KEY_STAGE,
+            (globalCfg.stage != null ? globalCfg.stage : false));
+    if (!info.stage) {
+      info.stage = null;
+    }
+
     return info;
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/imagare/PutConfig.java b/src/main/java/com/googlesource/gerrit/plugins/imagare/PutConfig.java
index 693fb4f..7475a72 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/imagare/PutConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/imagare/PutConfig.java
@@ -42,6 +42,7 @@
   public static class Input {
     public String defaultProject;
     public LinkDecoration linkDecoration;
+    public Boolean stage;
   }
 
   private final PluginConfigFactory cfgFactory;
@@ -68,6 +69,7 @@
     FileBasedConfig cfg =
         new FileBasedConfig(sitePaths.gerrit_config, FS.DETECTED);
     cfg.load();
+
     if (input.defaultProject != null) {
       if (projectCache.get(new Project.NameKey(input.defaultProject)) == null) {
         throw new UnprocessableEntityException("project '"
@@ -76,6 +78,7 @@
       cfg.setString("plugin", pluginName, "defaultProject",
           Strings.emptyToNull(input.defaultProject));
     }
+
     if (input.linkDecoration != null) {
       if (LinkDecoration.NONE.equals(input.linkDecoration)) {
         cfg.unset("plugin", pluginName, "linkDecoration");
@@ -84,6 +87,15 @@
             input.linkDecoration);
       }
     }
+
+    if (input.stage != null) {
+      if (input.stage) {
+        cfg.setBoolean("plugin", pluginName, "stage", input.stage);
+      } else {
+        cfg.unset("plugin", pluginName, "stage");
+      }
+    }
+
     cfg.save();
     cfgFactory.getFromGerritConfig(pluginName, true);
     return Response.<String> ok("OK");
diff --git a/src/main/java/com/googlesource/gerrit/plugins/imagare/PutPreference.java b/src/main/java/com/googlesource/gerrit/plugins/imagare/PutPreference.java
index 467287f..25a364a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/imagare/PutPreference.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/imagare/PutPreference.java
@@ -17,6 +17,7 @@
 import static com.googlesource.gerrit.plugins.imagare.GetPreference.PREFERENCE;
 import static com.googlesource.gerrit.plugins.imagare.GetPreference.KEY_DEFAULT_PROJECT;
 import static com.googlesource.gerrit.plugins.imagare.GetPreference.KEY_LINK_DECORATION;
+import static com.googlesource.gerrit.plugins.imagare.GetPreference.KEY_STAGE;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.annotations.PluginName;
@@ -108,6 +109,19 @@
       }
     }
 
+    boolean stage = db.getBoolean(PREFERENCE, username, KEY_STAGE, false);
+    if (input.stage != null) {
+      if (input.stage != stage) {
+        db.setBoolean(PREFERENCE, username, KEY_STAGE, input.stage);
+        modified = true;
+      }
+    } else {
+      if (!stage) {
+        db.unset(PREFERENCE, username, KEY_DEFAULT_PROJECT);
+        modified = true;
+      }
+    }
+
     if (modified) {
       MetaDataUpdate md = metaDataUpdateFactory.create(
           projectCache.getAllProjects().getProject().getNameKey());
diff --git a/src/main/java/com/googlesource/gerrit/plugins/imagare/client/ConfigInfo.java b/src/main/java/com/googlesource/gerrit/plugins/imagare/client/ConfigInfo.java
index 6bb83a3..f503031 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/imagare/client/ConfigInfo.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/imagare/client/ConfigInfo.java
@@ -27,8 +27,11 @@
   }
   private final native String link_decoration() /*-{ return this.link_decoration; }-*/;
 
+  final native boolean stage() /*-{ return this.stage ? true : false; }-*/;
+
   final native void setDefaultProject(String p) /*-{ this.default_project = p; }-*/;
   final native void setLinkDecoration(String d) /*-{ this.link_decoration = d; }-*/;
+  final native void setStage(boolean s) /*-{ this.stage = s; }-*/;
 
   static ConfigInfo create() {
     ConfigInfo g = (ConfigInfo) createObject();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/imagare/client/ImagareConfigScreen.java b/src/main/java/com/googlesource/gerrit/plugins/imagare/client/ImagareConfigScreen.java
index 99cb6fe..f554fea 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/imagare/client/ImagareConfigScreen.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/imagare/client/ImagareConfigScreen.java
@@ -20,6 +20,7 @@
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CheckBox;
 import com.google.gwt.user.client.ui.HorizontalPanel;
 import com.google.gwt.user.client.ui.Image;
 import com.google.gwt.user.client.ui.Label;
@@ -32,6 +33,7 @@
 
   private TextBox projectBox;
   private ListBox linkDecorationBox;
+  private CheckBox stageBox;
   private Button saveButton;
 
   protected ImagareConfigScreen(RestApi restApi) {
@@ -85,6 +87,17 @@
     p.add(linkDecorationBox);
     add(p);
 
+    p = new HorizontalPanel();
+    p.setStyleName("imagare-label-panel");
+    stageBox = new CheckBox("Stage images before upload");
+    stageBox.setValue(info.stage());
+    p.add(stageBox);
+    Image stageInfo = new Image(ImagarePlugin.RESOURCES.info());
+    stageInfo.setTitle("Images are not uploaded immediately but put into a "
+        + "staging area. The upload must be triggered explicitely.");
+    p.add(stageInfo);
+    add(p);
+
     HorizontalPanel buttons = new HorizontalPanel();
     add(buttons);
 
@@ -100,6 +113,7 @@
     saveButton.setEnabled(false);
     OnEditEnabler onEditEnabler = new OnEditEnabler(saveButton, projectBox);
     onEditEnabler.listenTo(linkDecorationBox);
+    onEditEnabler.listenTo(stageBox);
 
     projectBox.setFocus(true);
     saveButton.setEnabled(false);
@@ -109,6 +123,7 @@
     ConfigInfo in = ConfigInfo.create();
     in.setDefaultProject(projectBox.getValue());
     in.setLinkDecoration(linkDecorationBox.getValue(linkDecorationBox.getSelectedIndex()));
+    in.setStage(stageBox.getValue());
     restApi.put(in, new AsyncCallback<JavaScriptObject>() {
         @Override
         public void onSuccess(JavaScriptObject result) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/imagare/client/ImageUploadScreen.java b/src/main/java/com/googlesource/gerrit/plugins/imagare/client/ImageUploadScreen.java
index e858a93..21d7f1c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/imagare/client/ImageUploadScreen.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/imagare/client/ImageUploadScreen.java
@@ -42,6 +42,7 @@
   }
 
   static TextBox projectBox;
+  static UploadStagePanel uploadStagePanel;
   static UploadedImagesPanel uploadedPanel;
 
   ImageUploadScreen() {
@@ -56,26 +57,29 @@
 
     add(new UploadByFileSelection());
     add(new UploadByDropOrPastePanel());
+    uploadStagePanel = new UploadStagePanel();
+    add(uploadStagePanel);
     uploadedPanel = new UploadedImagesPanel();
     add(uploadedPanel);
 
-    String project = getParameter("project");
-    if (project != null) {
-      projectBox.setValue(project);
-    } else {
-      new RestApi("accounts").id("self").view(Plugin.get().getPluginName(), "preference")
-          .get(new AsyncCallback<ConfigInfo>() {
-            @Override
-            public void onSuccess(ConfigInfo info) {
+    new RestApi("accounts").id("self").view(Plugin.get().getPluginName(), "preference")
+        .get(new AsyncCallback<ConfigInfo>() {
+          @Override
+          public void onSuccess(ConfigInfo info) {
+            ImageUploader.setStage(info.stage());
+            String project = getParameter("project");
+            if (project != null) {
+              projectBox.setValue(project);
+            } else {
               projectBox.setValue(info.getDefaultProject());
             }
+          }
 
-            @Override
-            public void onFailure(Throwable caught) {
-              // never invoked
-            }
-          });
-    }
+          @Override
+          public void onFailure(Throwable caught) {
+            // never invoked
+          }
+        });
 
     InlineHyperlink preferenceLink =
         new InlineHyperlink("Edit Preferences", "/x/"
diff --git a/src/main/java/com/googlesource/gerrit/plugins/imagare/client/ImageUploader.java b/src/main/java/com/googlesource/gerrit/plugins/imagare/client/ImageUploader.java
index 2a6021f..b055ea4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/imagare/client/ImageUploader.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/imagare/client/ImageUploader.java
@@ -20,6 +20,27 @@
 import com.google.gwt.user.client.rpc.AsyncCallback;
 
 public class ImageUploader {
+  private static boolean stage;
+
+  public static void setStage(boolean s) {
+    stage = s;
+  }
+
+  public static final void stageImage(String imageData) {
+    stageImage(imageData, null);
+  }
+
+  public static final void stageImage(String imageData, String fileName) {
+    stageImage(ImageUploadScreen.projectBox.getValue(), imageData, fileName);
+  }
+
+  public static final void stageImage(String project, String imageData, String fileName) {
+    if (stage) {
+      ImageUploadScreen.uploadStagePanel.add(project, imageData, fileName);
+    } else {
+      uploadImage(project, imageData, fileName);
+    }
+  }
 
   public static final void uploadImage(String imageData) {
     uploadImage(imageData, null);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/imagare/client/UploadByDropOrPastePanel.java b/src/main/java/com/googlesource/gerrit/plugins/imagare/client/UploadByDropOrPastePanel.java
index f782e17..1255f8c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/imagare/client/UploadByDropOrPastePanel.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/imagare/client/UploadByDropOrPastePanel.java
@@ -56,7 +56,7 @@
         if (blob !== null) {
           var reader = new FileReader();
           reader.onload = function(e) {
-            @com.googlesource.gerrit.plugins.imagare.client.ImageUploader::uploadImage(Ljava/lang/String;)(e.target.result);
+            @com.googlesource.gerrit.plugins.imagare.client.ImageUploader::stageImage(Ljava/lang/String;)(e.target.result);
           };
           reader.readAsDataURL(blob);
         }
@@ -81,7 +81,7 @@
 
     function waitOnPaste(max, elem, savedContent) {
       if (elem.childNodes && elem.childNodes.length > 0) {
-        uploadImage(elem, savedContent);
+        stageImage(elem, savedContent);
       } else if (max > 0) {
         that = {
           m: max - 1,
@@ -95,10 +95,10 @@
       }
     }
 
-    function uploadImage(elem, savedContent) {
+    function stageImage(elem, savedContent) {
       var imageData = elem.childNodes[0].getAttribute("src");
       elem.innerHTML = savedContent;
-      @com.googlesource.gerrit.plugins.imagare.client.ImageUploader::uploadImage(Ljava/lang/String;)(imageData);
+      @com.googlesource.gerrit.plugins.imagare.client.ImageUploader::stageImage(Ljava/lang/String;)(imageData);
     }
 
     $wnd.imagareDropHandler = function handleDrop(elem, event) {
@@ -112,7 +112,7 @@
         r.onload = function(e) {
           elem.innerHTML = savedContent;
           if (f.type.match('image/.*')) {
-            @com.googlesource.gerrit.plugins.imagare.client.ImageUploader::uploadImage(Ljava/lang/String;Ljava/lang/String;)(e.target.result, f.name);
+            @com.googlesource.gerrit.plugins.imagare.client.ImageUploader::stageImage(Ljava/lang/String;Ljava/lang/String;)(e.target.result, f.name);
           } else {
             $wnd.Gerrit.showError('no image file');
           }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/imagare/client/UploadByFileSelection.java b/src/main/java/com/googlesource/gerrit/plugins/imagare/client/UploadByFileSelection.java
index 6179784..69a0ad4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/imagare/client/UploadByFileSelection.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/imagare/client/UploadByFileSelection.java
@@ -42,7 +42,7 @@
         var r = new FileReader();
         r.onload = function(e) {
           if (f.type.match('image/.*')) {
-            @com.googlesource.gerrit.plugins.imagare.client.ImageUploader::uploadImage(Ljava/lang/String;Ljava/lang/String;)(e.target.result, f.name);
+            @com.googlesource.gerrit.plugins.imagare.client.ImageUploader::stageImage(Ljava/lang/String;Ljava/lang/String;)(e.target.result, f.name);
           } else {
             $wnd.Gerrit.showError('no image file');
           }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/imagare/client/UploadStagePanel.java b/src/main/java/com/googlesource/gerrit/plugins/imagare/client/UploadStagePanel.java
new file mode 100644
index 0000000..e58dfd3
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/imagare/client/UploadStagePanel.java
@@ -0,0 +1,152 @@
+// Copyright (C) 2014 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.googlesource.gerrit.plugins.imagare.client;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.MouseOutEvent;
+import com.google.gwt.event.dom.client.MouseOutHandler;
+import com.google.gwt.event.dom.client.MouseOverEvent;
+import com.google.gwt.event.dom.client.MouseOverHandler;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.PopupPanel;
+import com.google.gwt.user.client.ui.VerticalPanel;
+
+public class UploadStagePanel extends VerticalPanel {
+
+  private Label uploadedImagesLabel;
+  private FlowPanel stagedImagesPanel;
+  private PopupPanel popup;
+
+  UploadStagePanel() {
+    setStyleName("imagare-upload-stage-panel");
+    setVisible(false);
+
+    uploadedImagesLabel = new Label("Staged Images:");
+    uploadedImagesLabel.setStyleName("imagare-staged-images-label");
+    add(uploadedImagesLabel);
+
+    stagedImagesPanel = new FlowPanel();
+    stagedImagesPanel.setStyleName("imagare-staged-images-panel");
+    add(stagedImagesPanel);
+
+    HorizontalPanel buttons = new HorizontalPanel();
+    add(buttons);
+
+    Button uploadButton = new Button("Upload");
+    uploadButton.setStyleName("imagare-upload-button");
+    uploadButton.addClickHandler(new ClickHandler() {
+      @Override
+      public void onClick(final ClickEvent event) {
+        doUpload();
+      }
+    });
+    buttons.add(uploadButton);
+
+    Button cleanButton = new Button("Clean");
+    cleanButton.setStyleName("imagare-clean-button");
+    cleanButton.addClickHandler(new ClickHandler() {
+      @Override
+      public void onClick(final ClickEvent event) {
+        stagedImagesPanel.clear();
+        setVisible(false);
+      }
+    });
+    buttons.add(cleanButton);
+
+    popup = new PopupPanel();
+    popup.setVisible(false);
+  }
+
+  void add(String project, String dataUrl, String fileName) {
+    setVisible(true);
+    if (!isStaged(project, dataUrl, fileName)) {
+      stagedImagesPanel.insert(new ImagePreview(project, dataUrl, fileName), 0);
+    }
+  }
+
+  boolean isStaged(String project, String dataUrl, String fileName) {
+    for (int i = 0; i < stagedImagesPanel.getWidgetCount(); i++) {
+      ImagePreview ip = (ImagePreview)stagedImagesPanel.getWidget(i);
+      if (project.equals(ip.project)
+          && dataUrl.endsWith(ip.dataUrl)
+          && (fileName != null ? fileName.equals(ip.fileName) : ip.fileName == null)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private void doUpload() {
+    for (int i = 0; i < stagedImagesPanel.getWidgetCount(); i++) {
+      ImagePreview ip = (ImagePreview)stagedImagesPanel.getWidget(i);
+      ImageUploader.uploadImage(ip.project, ip.dataUrl, ip.fileName);
+    }
+    stagedImagesPanel.clear();
+    setVisible(false);
+  }
+
+  private class ImagePreview extends VerticalPanel {
+    final String project;
+    final String dataUrl;
+    final String fileName;
+
+    ImagePreview(String project, final String dataUrl, String fileName) {
+      this.project = project;
+      this.dataUrl = dataUrl;
+      this.fileName = fileName;
+
+      setStyleName("imagare-stage-image-preview-panel");
+
+      Image img = new Image(dataUrl);
+      img.setStyleName("imagare-stage-image-preview");
+      add(img);
+
+      img.addMouseOverHandler(new MouseOverHandler() {
+        @Override
+        public void onMouseOver(MouseOverEvent event) {
+          if (!popup.isVisible()) {
+            Image img = new Image(dataUrl);
+            img.setStyleName("imagare-image-popup");
+            popup.add(img);
+
+            popup.center();
+            popup.setVisible(true);
+          }
+        }
+      });
+      img.addMouseOutHandler(new MouseOutHandler() {
+        @Override
+        public void onMouseOut(MouseOutEvent event) {
+          popup.setVisible(false);
+          popup.clear();
+        }
+      });
+
+      if (fileName != null) {
+        Label fileNameLabel = new Label(fileName);
+        fileNameLabel.setStyleName("imagare-stage-label");
+        add(fileNameLabel);
+      }
+      Label projectLabel = new Label("Project:" + project);
+      projectLabel.setStyleName("imagare-stage-label");
+      add(projectLabel);
+    }
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/imagare/public/imagare.css b/src/main/java/com/googlesource/gerrit/plugins/imagare/public/imagare.css
index 411b928..a57d5f2 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/imagare/public/imagare.css
+++ b/src/main/java/com/googlesource/gerrit/plugins/imagare/public/imagare.css
@@ -1,5 +1,5 @@
 .imagare-image-upload-screen, .imagare-uploaded-images-panel,
-.imagare-config-screen {
+imagare-upload-stage-panel, .imagare-config-screen {
   width: 100%;
   border-spacing: 0px 5px;
 }
@@ -37,14 +37,23 @@
   color: #999;
 }
 
-.imagare-uploaded-images-label {
+.imagare-uploaded-images-label,
+.imagare-staged-images-label {
   font-size: 20px;
 }
 
 .imagare-image-preview {
-  max-width: 50px;
-  max-height: 50px;
+  max-width: 75px;
+  max-height: 75px;
   margin: 5px;
+  border: 1px solid #B3B2B2;
+}
+
+.imagare-stage-image-preview {
+  max-width: 75px;
+  max-height: 75px;
+  margin-top: 10px;
+  border: 1px solid #B3B2B2;
 }
 
 .imagare-image-popup {
@@ -52,6 +61,26 @@
   max-height: 500px;
 }
 
+.imagare-stage-image-preview-panel {
+  display: inline;
+  margin-left: 5px;
+  margin-right: 5px;
+}
+
+.imagare-staged-images-panel {
+  width: 600px;
+}
+
+.imagare-stage-label {
+  max-width: 150px;
+}
+
 .imagare-save-button {
   margin-bottom: 10px;
 }
+
+.imagare-upload-button, .imagare-clean-button {
+  margin-right: 10px;
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index c1fc220..718615c 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -19,3 +19,7 @@
     `NONE`: no decoration, `TOOLTIP`: the image is shown as tooltip on
     mouse over an image link, `INLINE`: the image is inlined instead of
     the URL.
+
+<a id="stage">
+`plugin.@PLUGIN@.stage`
+:	Whether images should be staged before upload.
diff --git a/src/main/resources/Documentation/rest-api-config.md b/src/main/resources/Documentation/rest-api-config.md
index dd8e007..23a959c 100644
--- a/src/main/resources/Documentation/rest-api-config.md
+++ b/src/main/resources/Documentation/rest-api-config.md
@@ -71,6 +71,7 @@
   `NONE`: no decoration, `TOOLTIP`: the image is shown as tooltip on
   mouse over an image link, `INLINE`: the image is inlined instead of
   the URL.
+* _stage_: Whether images should be staged before upload.
 
 SEE ALSO
 --------