Updated licence in code files. Removed some unused methods
[pithos] / web_client / src / gr / grnet / pithos / web / client / FileUploadDialog.java
1 /*
2  * Copyright 2011 GRNET S.A. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or
5  * without modification, are permitted provided that the following
6  * conditions are met:
7  *
8  *   1. Redistributions of source code must retain the above
9  *      copyright notice, this list of conditions and the following
10  *      disclaimer.
11  *
12  *   2. Redistributions in binary form must reproduce the above
13  *      copyright notice, this list of conditions and the following
14  *      disclaimer in the documentation and/or other materials
15  *      provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  * The views and conclusions contained in the software and
31  * documentation are those of the authors and should not be
32  * interpreted as representing official policies, either expressed
33  * or implied, of GRNET S.A.
34  */
35 package gr.grnet.pithos.web.client;
36
37 import gr.grnet.pithos.web.client.rest.GetCommand;
38 import gr.grnet.pithos.web.client.rest.PostCommand;
39 import gr.grnet.pithos.web.client.rest.RestCommand;
40 import gr.grnet.pithos.web.client.rest.RestException;
41 import gr.grnet.pithos.web.client.rest.resource.FileResource;
42 import gr.grnet.pithos.web.client.rest.resource.FolderResource;
43 import gr.grnet.pithos.web.client.rest.resource.RestResourceWrapper;
44 import gr.grnet.pithos.web.client.rest.resource.UploadStatusResource;
45
46 import java.util.ArrayList;
47 import java.util.List;
48
49 import com.google.gwt.core.client.GWT;
50 import com.google.gwt.dom.client.NativeEvent;
51 import com.google.gwt.event.dom.client.ClickEvent;
52 import com.google.gwt.event.dom.client.ClickHandler;
53 import com.google.gwt.event.dom.client.KeyCodes;
54 import com.google.gwt.http.client.URL;
55 import com.google.gwt.json.client.JSONObject;
56 import com.google.gwt.json.client.JSONString;
57 import com.google.gwt.user.client.DeferredCommand;
58 import com.google.gwt.user.client.Event.NativePreviewEvent;
59 import com.google.gwt.user.client.Timer;
60 import com.google.gwt.user.client.ui.Button;
61 import com.google.gwt.user.client.ui.DialogBox;
62 import com.google.gwt.user.client.ui.FileUpload;
63 import com.google.gwt.user.client.ui.FormPanel;
64 import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteEvent;
65 import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteHandler;
66 import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
67 import com.google.gwt.user.client.ui.FormPanel.SubmitHandler;
68 import com.google.gwt.user.client.ui.Grid;
69 import com.google.gwt.user.client.ui.HTML;
70 import com.google.gwt.user.client.ui.HasHorizontalAlignment;
71 import com.google.gwt.user.client.ui.Hidden;
72 import com.google.gwt.user.client.ui.HorizontalPanel;
73 import com.google.gwt.user.client.ui.Label;
74 import com.google.gwt.user.client.ui.VerticalPanel;
75
76 /**
77  * The 'File upload' dialog box implementation.
78  */
79 public class FileUploadDialog extends DialogBox implements Updateable {
80
81         protected int prgBarInterval = 1500;
82
83         private ProgressBar progressBar;
84
85         protected RepeatingTimer repeater = new RepeatingTimer(this, prgBarInterval);
86
87         public static final boolean DONE = true;
88
89         /**
90          * The Form element that performs the file upload.
91          */
92         private final FormPanel form = new FormPanel();
93
94         private final FileUpload upload = new FileUpload();
95
96         protected final Label filenameLabel = new Label("");
97
98         protected List<FileResource> files;
99
100         protected boolean cancelEvent = false;
101
102         protected String fileNameToUse;
103
104         protected FolderResource folder;
105
106         /**
107          * The widget's constructor.
108          */
109         public FileUploadDialog() {
110                 // Set the dialog's caption.
111                 setText("File upload");
112                 setAnimationEnabled(true);
113                 // Since we're going to add a FileUpload widget, we'll need to set the
114                 // form to use the POST method, and multipart MIME encoding.
115                 form.setEncoding(FormPanel.ENCODING_MULTIPART);
116                 form.setMethod(FormPanel.METHOD_POST);
117
118                 // Create a panel to hold all of the form widgets.
119                 VerticalPanel panel = new VerticalPanel();
120                 form.setWidget(panel);
121                 final HTML info = new HTML("You may select a file to upload. Install" +
122                                 " <a href='http://gears.google.com/' target='_blank'>Google " +
123                                 "Gears</a><br> for uploading multiple files simultaneously.");
124                 info.addStyleName("pithos-uploadNote");
125                 panel.add(info);
126                 final Hidden date = new Hidden("Date", "");
127                 panel.add(date);
128                 final Hidden auth = new Hidden("Authorization", "");
129                 panel.add(auth);
130                 // Add an informative label with the folder name.
131                 Object selection = GSS.get().getTreeView().getSelection();
132                 folder = ((RestResourceWrapper) selection).getResource();
133                 upload.setName("file");
134                 filenameLabel.setText("");
135                 filenameLabel.setVisible(false);
136                 filenameLabel.setStyleName("props-labels");
137                 HorizontalPanel fileUloadPanel = new HorizontalPanel();
138                 fileUloadPanel.add(filenameLabel);
139                 fileUloadPanel.add(upload);
140                 upload.getElement().setId("fileUploadDiallog.uploadPanel");
141                 Grid generalTable = new Grid(2, 2);
142                 generalTable.setText(0, 0, "Folder");
143                 generalTable.setText(1, 0, "File");
144                 generalTable.setText(0, 1, folder.getName());
145                 generalTable.setWidget(1, 1, fileUloadPanel);
146                 generalTable.getCellFormatter().setStyleName(0, 0, "props-labels");
147                 generalTable.getCellFormatter().setStyleName(1, 0, "props-labels");
148                 generalTable.getCellFormatter().setStyleName(0, 1, "props-values");
149                 generalTable.getCellFormatter().setStyleName(1, 1, "props-values");
150                 generalTable.setCellSpacing(4);
151
152                 panel.add(generalTable);
153
154                 // Create a panel to hold the buttons.
155                 HorizontalPanel buttons = new HorizontalPanel();
156
157                 // Create the 'upload' button, along with a listener that submits the
158                 // form.
159                 final Button submit = new Button("Upload", new ClickHandler() {
160                         @Override
161                         public void onClick(ClickEvent event) {
162                                 prepareAndSubmit();
163                         }
164                 });
165                 submit.getElement().setId("fileUploadDialog.button.upload");
166                 buttons.add(submit);
167                 buttons.setCellHorizontalAlignment(submit, HasHorizontalAlignment.ALIGN_CENTER);
168                 // Create the 'Cancel' button, along with a listener that hides the
169                 // dialog when the button is clicked.
170                 final Button cancel = new Button("Cancel", new ClickHandler() {
171                         @Override
172                         public void onClick(ClickEvent event) {
173                                 repeater.finish();
174                                 hide();
175                         }
176                 });
177                 cancel.getElement().setId("fileUploadDialog.button.cancel");
178                 buttons.add(cancel);
179                 buttons.setCellHorizontalAlignment(cancel, HasHorizontalAlignment.ALIGN_CENTER);
180                 buttons.setSpacing(8);
181                 buttons.addStyleName("pithos-DialogBox");
182
183                 // Add an event handler to the form.
184                 form.addSubmitHandler(new SubmitHandler() {
185
186                         @Override
187                         public void onSubmit(SubmitEvent event) {
188                                 GSS app = GSS.get();
189                                 // This event is fired just before the form is submitted. We can
190                                 // take this opportunity to perform validation.
191                                 if (upload.getFilename().length() == 0) {
192                                         app.displayError("You must select a file!");
193                                         event.cancel();
194                                         hide();
195                                 } else {
196
197                                         canContinue();
198                                         GWT.log("Cancel:" + cancelEvent, null);
199                                         if (cancelEvent) {
200                                                 cancelEvent = false;
201                                                 app.displayError("The specified file name already exists in this folder");
202                                                 event.cancel();
203                                                 hide();
204                                         } else {
205
206                                                 fileNameToUse = getFilename(upload.getFilename());
207                                                 String apath;
208                                                 FileResource selectedFile = getFileForName(fileNameToUse);
209                                                 if (selectedFile == null ) {
210                                                         //we are going to create a file
211                                                         apath = folder.getUri();
212                                                         if (!apath.endsWith("/"))
213                                                                 apath = apath + "/";
214                                                         apath = apath + encodeComponent(fileNameToUse);
215                                                 } else
216                                                         apath = selectedFile.getUri();
217                                                 form.setAction(apath);
218                                                 String dateString = RestCommand.getDate();
219                                                 String resource = apath.substring(app.getApiPath().length() - 1, apath.length());
220                                                 String sig = RestCommand.calculateSig("POST", dateString, resource, RestCommand.base64decode(app.getToken()));
221                                                 date.setValue(dateString);
222                                                 auth.setValue(app.getCurrentUserResource().getUsername() + " " + sig);
223                                                 GWT.log("FolderPATH:" + folder.getUri(), null);
224                                                 submit.setEnabled(false);
225                                                 upload.setVisible(false);
226                                                 filenameLabel.setText(fileNameToUse);
227                                                 filenameLabel.setVisible(true);
228                                                 repeater.start();
229                                                 progressBar.setVisible(true);
230                                         }
231                                 }
232
233                         }
234                 });
235                 form.addSubmitCompleteHandler(new SubmitCompleteHandler() {
236
237                         @Override
238                         public void onSubmitComplete(SubmitCompleteEvent event) {
239                                 // When the form submission is successfully completed, this
240                                 // event is fired. Assuming the service returned a response
241                                 // of type text/html, we can get the result text here (see
242                                 // the FormPanel documentation for further explanation).
243                                 String results = event.getResults();
244
245                                 // Unfortunately the results are never empty, even in
246                                 // the absense of errors, so we have to check for '<pre></pre>'.
247                                 if (!results.equalsIgnoreCase("<pre></pre>")) {
248                                         GWT.log(results, null);
249                                         GSS.get().displayError(results);
250                                 }
251                                 progressBar.setProgress(100);
252                                 cancelUpload();
253                                 GSS.get().getTreeView().updateNode(GSS.get().getTreeView().getSelection());
254                                 GSS.get().getStatusPanel().updateStats();
255
256                         }
257                 });
258
259
260                 panel.add(buttons);
261                 progressBar = new ProgressBar(50, ProgressBar.SHOW_TIME_REMAINING);
262                 panel.add(progressBar);
263                 progressBar.setVisible(false);
264                 panel.setCellHorizontalAlignment(buttons, HasHorizontalAlignment.ALIGN_CENTER);
265                 panel.setCellHorizontalAlignment(progressBar, HasHorizontalAlignment.ALIGN_CENTER);
266                 panel.addStyleName("pithos-DialogBox");
267                 addStyleName("pithos-DialogBox");
268                 setWidget(form);
269         }
270
271         @Override
272         protected void onPreviewNativeEvent(NativePreviewEvent preview) {
273                 super.onPreviewNativeEvent(preview);
274
275                 NativeEvent evt = preview.getNativeEvent();
276                 if (evt.getType().equals("keydown"))
277                         // Use the popup's key preview hooks to close the dialog when either
278                         // enter or escape is pressed.
279                         switch (evt.getKeyCode()) {
280                                 case KeyCodes.KEY_ENTER:
281                                         prepareAndSubmit();
282                                         break;
283                                 case KeyCodes.KEY_ESCAPE:
284                                         cancelUpload();
285                                         break;
286                         }
287         }
288
289
290
291         /**
292          * Cancels the file upload.
293          */
294         private void cancelUpload() {
295                 repeater.finish();
296                 hide();
297         }
298
299         /**
300          * Make any last minute checks and start the upload.
301          */
302         public void prepareAndSubmit() {
303                 final String fname = getFilename(upload.getFilename());
304                 if (getFileForName(fname) == null) {
305                         //we are going to create a file, so we check to see if there is a trashed file with the same name
306                         FileResource same = null;
307                         for (FileResource fres : folder.getFiles())
308                                 if (fres.isDeleted() && fres.getName().equals(fname))
309                                         same = fres;
310                         if (same == null)
311                                 form.submit();
312                         else {
313                                 final FileResource sameFile = same;
314                                 GWT.log("Same deleted file", null);
315                                 ConfirmationDialog confirm = new ConfirmationDialog("A file with " +
316                                                 "the same name exists in the trash. If you continue,<br/>the trashed " +
317                                                 "file  '" + fname + "' will be renamed automatically for you.", "Continue") {
318
319                                         @Override
320                                         public void cancel() {
321                                                 FileUploadDialog.this.hide();
322                                         }
323
324                                         @Override
325                                         public void confirm() {
326                                                 updateTrashedFile(getBackupFilename(fname), sameFile);
327                                         }
328
329                                 };
330                                 confirm.center();
331                         }
332                 }
333                 else {
334                         // We are going to update an existing file, so show a confirmation dialog.
335                         ConfirmationDialog confirm = new ConfirmationDialog("Are you sure " +
336                                         "you want to update " + fname + "?", "Update") {
337
338                                 @Override
339                                 public void cancel() {
340                                         FileUploadDialog.this.hide();
341                                 }
342
343                                 @Override
344                                 public void confirm() {
345                                         form.submit();
346                                 }
347
348                         };
349                         confirm.center();
350                 }
351         }
352
353         /**
354          * Returns the file name from a potential full path argument. Apparently IE
355          * insists on sending the full path name of a file when uploading, forcing
356          * us to trim the extra path info. Since this is only observed on Windows we
357          * get to check for a single path separator value.
358          *
359          * @param name the potentially full path name of a file
360          * @return the file name without extra path information
361          */
362         protected String getFilename(String name) {
363                 int pathSepIndex = name.lastIndexOf("\\");
364                 if (pathSepIndex == -1) {
365                         pathSepIndex = name.lastIndexOf("/");
366                         if (pathSepIndex == -1)
367                                 return name;
368                 }
369                 return name.substring(pathSepIndex + 1);
370         }
371
372         /**
373          * Check whether the file name exists in selected folder.
374          *
375          * @return
376          */
377         private boolean canContinue() {
378                 if (files == null)
379                         return false;
380                 String fileName = getFilename(upload.getFilename());
381                 if (getFileForName(fileName) == null) {
382                         // For file creation, check to see if the file already exists.
383                         GWT.log("filename to upload:" + fileName, null);
384                         for (FileResource dto : files) {
385                                 GWT.log("Check:" + dto.getName() + "/" + fileName, null);
386                                 if (!dto.isDeleted() && dto.getName().equals(fileName)) {
387                                         cancelEvent = true;
388                                         return true;
389                                 }
390                         }
391                 }
392                 return true;
393         }
394
395         class RepeatingTimer extends Timer {
396
397                 private Updateable updateable;
398
399                 private int interval = 1500;
400
401                 private boolean running = true;
402
403                 RepeatingTimer(Updateable _updateable, int _interval) {
404                         updateable = _updateable;
405                         interval = _interval;
406                 }
407
408                 @Override
409                 public void run() {
410                         updateable.update();
411                 }
412
413                 public void start() {
414                         running = true;
415
416                         scheduleRepeating(interval);
417                 }
418
419                 public void finish() {
420                         running = false;
421                         cancel();
422                 }
423
424                 public int getInterval() {
425                         return interval;
426                 }
427
428                 public void setInterval(int anInterval) {
429                         if (interval != anInterval) {
430                                 interval = anInterval;
431                                 if (running) {
432                                         finish();
433                                         start();
434                                 }
435                         }
436                 }
437         }
438
439         @Override
440         public void update() {
441                 String apath = folder.getUri();
442                 if (!apath.endsWith("/"))
443                         apath = apath + "/";
444                 apath = apath + encodeComponent(fileNameToUse) + "?progress=" + encodeComponent(fileNameToUse);
445                 GetCommand eg = new GetCommand<UploadStatusResource>(UploadStatusResource.class, apath, false, null) {
446
447                         @Override
448                         public void onComplete() {
449                                 UploadStatusResource res = getResult();
450                                 progressBar.setProgress(res.percent());
451                         }
452
453                         @Override
454                         public void onError(Throwable t) {
455                                 GWT.log("", t);
456                         }
457
458                 };
459                 DeferredCommand.addCommand(eg);
460         }
461
462         protected String getBackupFilename(String filename) {
463                 List<FileResource> filesInSameFolder = new ArrayList<FileResource>();
464                 for (FileResource deleted : folder.getFiles())
465                         if (deleted.isDeleted())
466                                 filesInSameFolder.add(deleted);
467                 int i = 1;
468                 for (FileResource same : filesInSameFolder)
469                         if (same.getName().startsWith(filename)) {
470                                 String toCheck = same.getName().substring(filename.length(), same.getName().length());
471                                 if (toCheck.startsWith(" ")) {
472                                         int test = -1;
473                                         try {
474                                                 test = Integer.valueOf(toCheck.replace(" ", ""));
475                                         } catch (NumberFormatException e) {
476                                                 // Do nothing since string is not a number.
477                                         }
478                                         if (test >= i)
479                                                 i = test + 1;
480                                 }
481                         }
482
483                 return filename + " " + i;
484         }
485
486         /**
487          * Rename the conflicting trashed file with the supplied new name.
488          */
489         private void updateTrashedFile(String newName, FileResource trashedFile) {
490                 JSONObject json = new JSONObject();
491                 json.put("name", new JSONString(newName));
492                 PostCommand cf = new PostCommand(trashedFile.getUri() + "?update=", json.toString(), 200) {
493
494                         @Override
495                         public void onComplete() {
496                                 form.submit();
497                         }
498
499                         @Override
500                         public void onError(Throwable t) {
501                                 GSS app = GSS.get();
502                                 GWT.log("", t);
503                                 if (t instanceof RestException) {
504                                         int statusCode = ((RestException) t).getHttpStatusCode();
505                                         if (statusCode == 405)
506                                                 app.displayError("You don't have the necessary permissions");
507                                         else if (statusCode == 404)
508                                                 app.displayError("User in permissions does not exist");
509                                         else if (statusCode == 409)
510                                                 app.displayError("A file with the same name already exists");
511                                         else if (statusCode == 413)
512                                                 app.displayError("Your quota has been exceeded");
513                                         else
514                                                 app.displayError("Unable to modify file:" + ((RestException) t).getHttpStatusText());
515                                 } else
516                                         app.displayError("System error modifying file:" + t.getMessage());
517                         }
518
519                 };
520                 DeferredCommand.addCommand(cf);
521         }
522
523         protected FileResource getFileForName(String name){
524                 for (FileResource f : folder.getFiles())
525                         if (!f.isDeleted() && f.getName().equals(name))
526                                 return f;
527                 return null;
528         }
529
530
531         /**
532          * Same as URL.encodeComponent, but also
533          * encode apostrophe since browsers aren't consistent about it
534          * (FF encodes, IE does not).
535          */
536         private String encodeComponent(String decodedURLComponent) {
537                 String retv = URL.encodeComponent(decodedURLComponent);
538                 retv = retv.replaceAll("'", "%27");
539                 return retv;
540         }
541
542         /**
543          * Modify the files.
544          *
545          * @param newFiles the files to set
546          */
547         public void setFiles(List<FileResource> newFiles) {
548                 files = newFiles;
549         }
550 }