\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
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
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
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
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
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
});\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
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
}\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
\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
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
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
/**\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