Use SSL transport all over the place, for the production deployment.
[pithos] / src / gr / ebs / gss / client / FileUploadGearsDialog.java
index 1a58633..2eed966 100644 (file)
@@ -26,9 +26,13 @@ import gr.ebs.gss.client.rest.resource.FolderResource;
 \r
 import java.util.ArrayList;\r
 import java.util.Arrays;\r
+import java.util.HashMap;\r
 import java.util.List;\r
+import java.util.Map;\r
 \r
 import com.google.gwt.core.client.GWT;\r
+import com.google.gwt.event.dom.client.ClickEvent;\r
+import com.google.gwt.event.dom.client.ClickHandler;\r
 import com.google.gwt.gears.client.Factory;\r
 import com.google.gwt.gears.client.desktop.Desktop;\r
 import com.google.gwt.gears.client.desktop.File;\r
@@ -37,34 +41,51 @@ import com.google.gwt.gears.client.httprequest.HttpRequest;
 import com.google.gwt.gears.client.httprequest.ProgressEvent;\r
 import com.google.gwt.gears.client.httprequest.ProgressHandler;\r
 import com.google.gwt.gears.client.httprequest.RequestCallback;\r
+import com.google.gwt.http.client.URL;\r
 import com.google.gwt.json.client.JSONObject;\r
 import com.google.gwt.json.client.JSONString;\r
 import com.google.gwt.user.client.Command;\r
 import com.google.gwt.user.client.DeferredCommand;\r
 import com.google.gwt.user.client.ui.Button;\r
-import com.google.gwt.user.client.ui.ClickListener;\r
-import com.google.gwt.user.client.ui.Grid;\r
+import com.google.gwt.user.client.ui.FlexTable;\r
+import com.google.gwt.user.client.ui.HTML;\r
 import com.google.gwt.user.client.ui.HasHorizontalAlignment;\r
 import com.google.gwt.user.client.ui.HorizontalPanel;\r
-import com.google.gwt.user.client.ui.TextBox;\r
 import com.google.gwt.user.client.ui.VerticalPanel;\r
-import com.google.gwt.user.client.ui.Widget;\r
 \r
 /**\r
  * The 'File upload' dialog box implementation with Google Gears support.\r
  */\r
 public class FileUploadGearsDialog extends FileUploadDialog implements Updateable {\r
 \r
-       private final Factory factory = Factory.getInstance();\r
+       protected final Factory factory = Factory.getInstance();\r
 \r
-       private List<File> selectedFiles = new ArrayList<File>();\r
+       /**\r
+        * The array of files to upload.\r
+        */\r
+       private File[] fileObjects;\r
 \r
-       private TextBox selected;\r
+       /**\r
+        * A list of files to upload, created from files array. Used to signal\r
+        * finished state when empty.\r
+        */\r
+       protected List<File> selectedFiles = new ArrayList<File>();\r
+\r
+       /**\r
+        * The list of progress bars for individual files.\r
+        */\r
+       protected List<ProgressBar> progressBars = new ArrayList<ProgressBar>();\r
 \r
        private Button browse;\r
 \r
        private Button submit;\r
 \r
+       private FlexTable generalTable;\r
+\r
+       private Map<String, FileResource> toRename;\r
+\r
+       protected List<HttpRequest> requests = new ArrayList<HttpRequest>();\r
+\r
        /**\r
         * The widget's constructor.\r
         */\r
@@ -74,24 +95,19 @@ public class FileUploadGearsDialog extends FileUploadDialog implements Updateabl
                setAnimationEnabled(true);\r
                // Create a panel to hold all of the dialog widgets.\r
                VerticalPanel panel = new VerticalPanel();\r
+               final HTML info = new HTML("You may select one or more files to upload.");\r
+               info.addStyleName("gss-uploadNote");\r
+               panel.add(info);\r
                // Add an informative label with the folder name.\r
                Object selection = GSS.get().getFolders().getCurrent().getUserObject();\r
                folder = (FolderResource) selection;\r
-               filenameLabel.setText("");\r
-               filenameLabel.setVisible(false);\r
-               filenameLabel.setStyleName("props-labels");\r
 \r
                browse = new Button("Browse...");\r
 \r
-               selected = new TextBox();\r
-               selected.setEnabled(false);\r
-\r
                HorizontalPanel fileUploadPanel = new HorizontalPanel();\r
-               fileUploadPanel.add(filenameLabel);\r
-               fileUploadPanel.add(selected);\r
                fileUploadPanel.add(browse);\r
 \r
-               Grid generalTable = new Grid(2, 2);\r
+               generalTable = new FlexTable();\r
                generalTable.setText(0, 0, "Folder");\r
                generalTable.setText(1, 0, "File");\r
                generalTable.setText(0, 1, folder.getName());\r
@@ -108,9 +124,9 @@ public class FileUploadGearsDialog extends FileUploadDialog implements Updateabl
                HorizontalPanel buttons = new HorizontalPanel();\r
 \r
                submit = new Button("Upload");\r
-               submit.addClickListener(new ClickListener() {\r
-\r
-                       public void onClick(Widget sender) {\r
+               submit.addClickHandler(new ClickHandler() {\r
+                       @Override\r
+                       public void onClick(ClickEvent event) {\r
                                prepareAndSubmit();\r
                        }\r
                });\r
@@ -119,10 +135,10 @@ public class FileUploadGearsDialog extends FileUploadDialog implements Updateabl
                buttons.setCellHorizontalAlignment(submit, HasHorizontalAlignment.ALIGN_CENTER);\r
                // Create the 'Cancel' button, along with a listener that hides the\r
                // dialog when the button is clicked.\r
-               Button cancel = new Button("Cancel", new ClickListener() {\r
-\r
-                       public void onClick(Widget sender) {\r
-                               hide();\r
+               Button cancel = new Button("Cancel", new ClickHandler() {\r
+                       @Override\r
+                       public void onClick(ClickEvent event) {\r
+                               cancelUpload();\r
                        }\r
                });\r
                buttons.add(cancel);\r
@@ -130,14 +146,24 @@ public class FileUploadGearsDialog extends FileUploadDialog implements Updateabl
                buttons.setSpacing(8);\r
                buttons.addStyleName("gss-DialogBox");\r
 \r
-               browse.addClickListener(new ClickListener() {\r
-                       public void onClick(Widget sender) {\r
+               browse.addClickHandler(new ClickHandler() {\r
+                       @Override\r
+                       public void onClick(ClickEvent event) {\r
                                Desktop desktop = factory.createDesktop();\r
                                desktop.openFiles(new OpenFilesHandler() {\r
 \r
                                        public void onOpenFiles(OpenFilesEvent event) {\r
-                                               selectedFiles.addAll(Arrays.asList(event.getFiles()));\r
-                                               selected.setText(selectedFiles.get(0).getName());\r
+                                               fileObjects = event.getFiles();\r
+                                               selectedFiles.addAll(Arrays.asList(fileObjects));\r
+                                               for (int i = 0; i< selectedFiles.size(); i++) {\r
+                                                       generalTable.setText(i+1, 0, "File");\r
+                                                       generalTable.setText(i+1, 1, selectedFiles.get(i).getName());\r
+                                                       ProgressBar progress = new ProgressBar(20, 0);\r
+                                                       generalTable.setWidget(i+1, 2, progress);\r
+                                                       progressBars.add(progress);\r
+                                                       generalTable.getCellFormatter().setStyleName(i+1, 0, "props-labels");\r
+                                                       generalTable.getCellFormatter().setStyleName(i+1, 1, "props-values");\r
+                                               }\r
                                                submit.setEnabled(true);\r
                                        }\r
                                });\r
@@ -145,17 +171,22 @@ public class FileUploadGearsDialog extends FileUploadDialog implements Updateabl
                });\r
 \r
                panel.add(buttons);\r
-               progressBar = new ProgressBar(50, ProgressBar.SHOW_TIME_REMAINING);\r
-               panel.add(progressBar);\r
-               progressBar.setVisible(false);\r
                panel.setCellHorizontalAlignment(buttons, HasHorizontalAlignment.ALIGN_CENTER);\r
-               panel.setCellHorizontalAlignment(progressBar, HasHorizontalAlignment.ALIGN_CENTER);\r
                panel.addStyleName("gss-DialogBox");\r
                addStyleName("gss-DialogBox");\r
                setWidget(panel);\r
        }\r
 \r
        /**\r
+        * Cancels the file upload.\r
+        */\r
+       private void cancelUpload() {\r
+               for (HttpRequest request: requests)\r
+                       request.abort();\r
+               hide();\r
+       }\r
+\r
+       /**\r
         * Check whether the specified file name exists in the selected folder.\r
         */\r
        private boolean canContinue(File file) {\r
@@ -176,7 +207,7 @@ public class FileUploadGearsDialog extends FileUploadDialog implements Updateabl
                        hide();\r
                        return;\r
                }\r
-               for(File file: selectedFiles)\r
+               for (File file: selectedFiles)\r
                        if (!canContinue(file)) {\r
                                app.displayError("The file name " + file.getName() +\r
                                                        " already exists in this folder");\r
@@ -185,41 +216,33 @@ public class FileUploadGearsDialog extends FileUploadDialog implements Updateabl
                        }\r
                submit.setEnabled(false);\r
                browse.setVisible(false);\r
-               final String fname = getFilename(selectedFiles.get(0).getName());\r
-               if (getFileForName(fname) == null) {\r
-                       // We are going to create a file, so we check to see if there is a\r
-                       // trashed file with the same name.\r
-                       FileResource same = null;\r
-                       for (FileResource fres : folder.getFiles())\r
-                               if (fres.isDeleted() && fres.getName().equals(fname))\r
-                                       same = fres;\r
-                       if (same == null)\r
-                               uploadFiles();\r
-                       else {\r
-                               final FileResource sameFile = same;\r
-                               GWT.log("Same deleted file", null);\r
-                               ConfirmationDialog confirm = new ConfirmationDialog("A file " +\r
-                                               "with the same name exists in the trash. If you " +\r
-                                               "continue,<br/>the trashed file  '" + fname +\r
-                                               "' will be renamed automatically for you.", "Continue") {\r
-\r
-                                       @Override\r
-                                       public void cancel() {\r
-                                               hide();\r
-                                       }\r
-\r
-                                       @Override\r
-                                       public void confirm() {\r
-                                               updateTrashedFile(getBackupFilename(fname), sameFile);\r
-                                       }\r
+               List<String> toUpdate = new ArrayList<String>();\r
+               toRename = new HashMap<String, FileResource>();\r
+               for (File file: selectedFiles) {\r
+                       String fname = getFilename(file.getName());\r
+                       if (getFileForName(fname) == null) {\r
+                               // We are going to create a file, so we check to see if there is a\r
+                               // trashed file with the same name.\r
+                               FileResource same = null;\r
+                               for (FileResource fres : folder.getFiles())\r
+                                       if (fres.isDeleted() && fres.getName().equals(fname))\r
+                                               same = fres;\r
+                               // In that case add it to the list of files to rename.\r
+                               if (same != null)\r
+                                       toRename.put(getBackupFilename(fname), same);\r
+                       } else\r
+                               // If we are updating a file add it to the list of files to update.\r
+                               toUpdate.add(fname);\r
+               }\r
 \r
-                               };\r
-                               confirm.center();\r
-                       }\r
-               } else {\r
-                       // We are going to update an existing file, so show a confirmation dialog.\r
+               if (!toUpdate.isEmpty()) {\r
+                       StringBuffer sb = new StringBuffer();\r
+                       for (String name: toUpdate)\r
+                               sb.append(name).append("<br/>");\r
+                       // We are going to update existing files, so show a confirmation dialog.\r
                        ConfirmationDialog confirm = new ConfirmationDialog("Are you sure " +\r
-                                       "you want to update " + fname + "?", "Update"){\r
+                                       "you want to update the following files?<br/><i>" + sb +\r
+                                       "</i>", "Update") {\r
 \r
                                @Override\r
                                public void cancel() {\r
@@ -228,83 +251,115 @@ public class FileUploadGearsDialog extends FileUploadDialog implements Updateabl
 \r
                                @Override\r
                                public void confirm() {\r
-                                       uploadFiles();\r
+                                       confirmRename();\r
                                }\r
 \r
                        };\r
                        confirm.center();\r
-               }\r
+               } else\r
+                       confirmRename();\r
        }\r
 \r
-       private void updateTrashedFile(String newName, FileResource trashedFile) {\r
-               JSONObject json = new JSONObject();\r
-               json.put("name", new JSONString(newName));\r
-               PostCommand cf = new PostCommand(trashedFile.getUri() + "?update=", json.toString(), 200) {\r
+       /**\r
+        * Confirm the renames of synonymous files already in the trash.\r
+        */\r
+       private void confirmRename() {\r
+               if (!toRename.isEmpty()) {\r
+                       StringBuffer sb = new StringBuffer();\r
+                       for (FileResource file: toRename.values())\r
+                               sb.append(file.getName()).append("<br/>");\r
+                       ConfirmationDialog confirm = new ConfirmationDialog("Files " +\r
+                                       "with the following names already exist in the trash. If" +\r
+                                       " you continue,<br/>the trashed files will be renamed " +\r
+                                       "automatically for you:<br/><i>" + sb + "</i>", "Continue") {\r
 \r
-                       @Override\r
-                       public void onComplete() {\r
-                               uploadFiles();\r
-                       }\r
+                               @Override\r
+                               public void cancel() {\r
+                                       hide();\r
+                               }\r
 \r
-                       @Override\r
-                       public void onError(Throwable t) {\r
-                               GWT.log("", t);\r
-                               if (t instanceof RestException) {\r
-                                       int statusCode = ((RestException) t).getHttpStatusCode();\r
-                                       if (statusCode == 405)\r
-                                               GSS.get().displayError("You don't have the necessary permissions");\r
-                                       else if (statusCode == 404)\r
-                                               GSS.get().displayError("User in permissions does not exist");\r
-                                       else if (statusCode == 409)\r
-                                               GSS.get().displayError("A file with the same name already exists");\r
-                                       else if (statusCode == 413)\r
-                                               GSS.get().displayError("Your quota has been exceeded");\r
-                                       else\r
-                                               GSS.get().displayError("Unable to modify file:" +((RestException)t).getHttpStatusText());\r
-                               } else\r
-                                       GSS.get().displayError("System error modifying file:" + t.getMessage());\r
-                       }\r
+                               @Override\r
+                               public void confirm() {\r
+                                       updateTrashedFiles();\r
+                               }\r
+\r
+                       };\r
+                       confirm.center();\r
+               } else\r
+                       uploadFiles();\r
+       }\r
+\r
+       /**\r
+        * Rename the conflicting trashed files with the supplied new names.\r
+        */\r
+       private void updateTrashedFiles() {\r
+               for (final String name: toRename.keySet()) {\r
+                       JSONObject json = new JSONObject();\r
+                       json.put("name", new JSONString(name));\r
+                       PostCommand cf = new PostCommand(toRename.get(name).getUri() + "?update=", json.toString(), 200) {\r
+\r
+                               @Override\r
+                               public void onComplete() {\r
+                                       toRename.remove(name);\r
+                                       uploadFiles();\r
+                               }\r
 \r
-               };\r
-               DeferredCommand.addCommand(cf);\r
+                               @Override\r
+                               public void onError(Throwable t) {\r
+                                       GSS app = GSS.get();\r
+                                       GWT.log("", t);\r
+                                       if (t instanceof RestException) {\r
+                                               int statusCode = ((RestException) t).getHttpStatusCode();\r
+                                               if (statusCode == 405)\r
+                                                       app.displayError("You don't have the necessary permissions");\r
+                                               else if (statusCode == 404)\r
+                                                       app.displayError("User in permissions does not exist");\r
+                                               else if (statusCode == 409)\r
+                                                       app.displayError("A file with the same name already exists");\r
+                                               else if (statusCode == 413)\r
+                                                       app.displayError("Your quota has been exceeded");\r
+                                               else\r
+                                                       app.displayError("Unable to modify file:" + ((RestException) t).getHttpStatusText());\r
+                                       } else\r
+                                               app.displayError("System error modifying file:" + t.getMessage());\r
+                               }\r
+\r
+                       };\r
+                       DeferredCommand.addCommand(cf);\r
+               }\r
        }\r
 \r
        /**\r
-        * Schedule the PUT requests to upload the files.\r
+        * Checks if the renaming step for already trashed files is complete and\r
+        * starts file uploads.\r
         */\r
        private void uploadFiles() {\r
-               for (final File file: selectedFiles)\r
+               if (!toRename.isEmpty()) return;\r
+               for (int i = 0; i< fileObjects.length; i++) {\r
+                       final int index = i;\r
                        DeferredCommand.addCommand(new Command() {\r
                                public void execute() {\r
-                                       doPut(file);\r
+                                       doSend(fileObjects[index], index);\r
                                }\r
                        });\r
+               }\r
        }\r
 \r
        /**\r
-        * Perform the HTTP PUT requests to upload the specified file.\r
+        * Perform the HTTP request to upload the specified file.\r
         */\r
-       protected void doPut(final File file) {\r
-               GSS app = GSS.get();\r
+       protected void doSend(final File file, final int index) {\r
+               final GSS app = GSS.get();\r
                HttpRequest request = factory.createHttpRequest();\r
+               requests.add(request);\r
                String method = "PUT";\r
 \r
-               fileNameToUse = getFilename(file.getName());\r
-               selected.setVisible(false);\r
-               filenameLabel.setText(fileNameToUse);\r
-               filenameLabel.setVisible(true);\r
-               progressBar.setVisible(true);\r
-\r
                String path;\r
-               FileResource selectedResource = getFileForName(fileNameToUse);\r
-               if (selectedResource == null ) {\r
-                       // We are going to create a file.\r
-                       path = folder.getUri();\r
-                       if (!path.endsWith("/"))\r
-                               path = path + "/";\r
-                       path = path + encodeComponent(fileNameToUse);\r
-               } else\r
-                       path = selectedResource.getUri();\r
+               final String filename = getFilename(file.getName());\r
+               path = folder.getUri();\r
+               if (!path.endsWith("/"))\r
+                       path = path + "/";\r
+               path = path + encode(filename);\r
 \r
                String token = app.getToken();\r
                String resource = path.substring(app.getApiPath().length()-1, path.length());\r
@@ -316,6 +371,8 @@ public class FileUploadGearsDialog extends FileUploadDialog implements Updateabl
                request.setRequestHeader("Accept", "application/json; charset=utf-8");\r
                request.setCallback(new RequestCallback() {\r
                        public void onResponseReceived(HttpRequest req) {\r
+                               int state = req.getReadyState();\r
+                               if (state != 4) return;\r
                                switch(req.getStatus()) {\r
                                        case 201: // Created falls through to updated.\r
                                        case 204:\r
@@ -326,15 +383,28 @@ public class FileUploadGearsDialog extends FileUploadDialog implements Updateabl
                                                SessionExpiredDialog dlg = new SessionExpiredDialog();\r
                                                dlg.center();\r
                                                break;\r
+                                       case 405:\r
+                                               app.displayError("You don't have permission to " +\r
+                                                               "upload file " + filename);\r
+                                               break;\r
+                                       case 409:\r
+                                               app.displayError("A folder with the name " + filename +\r
+                                                               " already exists at this level");\r
+                                               break;\r
+                                       case 413:\r
+                                               app.displayError("There is not enough free space " +\r
+                                                               "available for uploading " + filename);\r
+                                               break;\r
                                        default:\r
-                                               DisplayHelper.log(req.getStatus() + ":" + req.getStatusText());\r
+                                               app.displayError("Error uploading file " + filename +\r
+                                                                       ": " + req.getStatus());\r
                                }\r
                        }\r
                });\r
                request.getUpload().setProgressHandler(new ProgressHandler() {\r
                        public void onProgress(ProgressEvent event) {\r
                                double pcnt = (double) event.getLoaded() / event.getTotal();\r
-                               progressBar.setProgress((int) Math.floor(pcnt * 100));\r
+                               progressBars.get(index).setProgress((int) Math.floor(pcnt * 100));\r
                        }\r
                });\r
                request.send(file.getBlob());\r
@@ -343,10 +413,22 @@ public class FileUploadGearsDialog extends FileUploadDialog implements Updateabl
        /**\r
         * Perform the final actions after the files are uploaded.\r
         */\r
-       private void finish() {\r
+       protected void finish() {\r
                if (!selectedFiles.isEmpty()) return;\r
                hide();\r
                GSS.get().showFileList(true);\r
                GSS.get().getStatusPanel().updateStats();\r
        }\r
+\r
+       /**\r
+        * Same as URL.encode, but also encode apostrophe since browsers aren't\r
+        * consistent about it (FF encodes, IE does not).\r
+        */\r
+       protected String encode(String decodedURL) {\r
+               String retv = decodedURL.replaceAll("@", "_"); // Replace bad character\r
+               retv = URL.encodeComponent(retv);\r
+               retv = retv.replaceAll("'", "%27");\r
+               return retv;\r
+       }\r
+\r
 }\r