Replaced move procedures with the new PUT with delimiter
[pithos-web-client] / src / gr / grnet / pithos / web / client / Pithos.java
1 /*
2  * Copyright 2011-2012 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.commands.UploadFileCommand;
38 import gr.grnet.pithos.web.client.foldertree.AccountResource;
39 import gr.grnet.pithos.web.client.foldertree.File;
40 import gr.grnet.pithos.web.client.foldertree.Folder;
41 import gr.grnet.pithos.web.client.foldertree.FolderTreeView;
42 import gr.grnet.pithos.web.client.foldertree.FolderTreeViewModel;
43 import gr.grnet.pithos.web.client.foldertree.Resource;
44 import gr.grnet.pithos.web.client.grouptree.Group;
45 import gr.grnet.pithos.web.client.grouptree.GroupTreeView;
46 import gr.grnet.pithos.web.client.grouptree.GroupTreeViewModel;
47 import gr.grnet.pithos.web.client.mysharedtree.MysharedTreeView;
48 import gr.grnet.pithos.web.client.mysharedtree.MysharedTreeViewModel;
49 import gr.grnet.pithos.web.client.othersharedtree.OtherSharedTreeView;
50 import gr.grnet.pithos.web.client.othersharedtree.OtherSharedTreeViewModel;
51 import gr.grnet.pithos.web.client.rest.DeleteRequest;
52 import gr.grnet.pithos.web.client.rest.GetRequest;
53 import gr.grnet.pithos.web.client.rest.HeadRequest;
54 import gr.grnet.pithos.web.client.rest.PutRequest;
55 import gr.grnet.pithos.web.client.rest.RestException;
56
57 import java.util.ArrayList;
58 import java.util.Date;
59 import java.util.HashMap;
60 import java.util.Iterator;
61 import java.util.List;
62 import java.util.Set;
63
64 import org.apache.http.HttpStatus;
65
66 import com.google.gwt.core.client.EntryPoint;
67 import com.google.gwt.core.client.GWT;
68 import com.google.gwt.core.client.JsArrayString;
69 import com.google.gwt.core.client.Scheduler;
70 import com.google.gwt.core.client.Scheduler.RepeatingCommand;
71 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
72 import com.google.gwt.event.dom.client.ClickEvent;
73 import com.google.gwt.event.dom.client.ClickHandler;
74 import com.google.gwt.event.logical.shared.ResizeEvent;
75 import com.google.gwt.event.logical.shared.ResizeHandler;
76 import com.google.gwt.http.client.Request;
77 import com.google.gwt.http.client.RequestBuilder;
78 import com.google.gwt.http.client.RequestCallback;
79 import com.google.gwt.http.client.RequestException;
80 import com.google.gwt.http.client.Response;
81 import com.google.gwt.http.client.URL;
82 import com.google.gwt.i18n.client.DateTimeFormat;
83 import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
84 import com.google.gwt.i18n.client.Dictionary;
85 import com.google.gwt.i18n.client.TimeZone;
86 import com.google.gwt.i18n.client.TimeZoneInfo;
87 import com.google.gwt.i18n.client.constants.TimeZoneConstants;
88 import com.google.gwt.json.client.JSONArray;
89 import com.google.gwt.json.client.JSONObject;
90 import com.google.gwt.json.client.JSONParser;
91 import com.google.gwt.json.client.JSONString;
92 import com.google.gwt.json.client.JSONValue;
93 import com.google.gwt.resources.client.ClientBundle;
94 import com.google.gwt.resources.client.CssResource;
95 import com.google.gwt.resources.client.ImageResource;
96 import com.google.gwt.resources.client.ImageResource.ImageOptions;
97 import com.google.gwt.user.client.Command;
98 import com.google.gwt.user.client.Cookies;
99 import com.google.gwt.user.client.Event;
100 import com.google.gwt.user.client.History;
101 import com.google.gwt.user.client.Window;
102 import com.google.gwt.user.client.ui.AbstractImagePrototype;
103 import com.google.gwt.user.client.ui.Button;
104 import com.google.gwt.user.client.ui.Composite;
105 import com.google.gwt.user.client.ui.FlowPanel;
106 import com.google.gwt.user.client.ui.HTML;
107 import com.google.gwt.user.client.ui.HasHorizontalAlignment;
108 import com.google.gwt.user.client.ui.HasVerticalAlignment;
109 import com.google.gwt.user.client.ui.HorizontalPanel;
110 import com.google.gwt.user.client.ui.HorizontalSplitPanel;
111 import com.google.gwt.user.client.ui.PopupPanel;
112 import com.google.gwt.user.client.ui.RootPanel;
113 import com.google.gwt.user.client.ui.VerticalPanel;
114 import com.google.gwt.view.client.SelectionChangeEvent;
115 import com.google.gwt.view.client.SelectionChangeEvent.Handler;
116 import com.google.gwt.view.client.SingleSelectionModel;
117
118 /**
119  * Entry point classes define <code>onModuleLoad()</code>.
120  */
121 public class Pithos implements EntryPoint, ResizeHandler {
122
123         public static final String HOME_CONTAINER = "pithos";
124
125         public static final String TRASH_CONTAINER = "trash";
126
127         public static final Configuration config = GWT.create(Configuration.class);
128         
129         public interface Style extends CssResource {
130                 String commandAnchor();
131                 
132                 String statistics();
133                 
134                 @ClassName("gwt-HTML")
135                 String html();
136                 
137                 String uploadAlert();
138
139                 String uploadAlertLink();
140
141                 String uploadAlertProgress();
142                 
143                 String uploadAlertPercent();
144                 
145                 String uploadAlertClose();
146         }
147         
148         public interface Resources extends ClientBundle {
149                 @Source("Pithos.css")
150                 Style pithosCss();
151                 
152                 @Source("gr/grnet/pithos/resources/close-popup.png")
153                 ImageResource closePopup();
154         }
155
156         public static Resources resources = GWT.create(Resources.class);
157         
158         /**
159          * Instantiate an application-level image bundle. This object will provide
160          * programmatic access to all the images needed by widgets.
161          */
162         static Images images = (Images) GWT.create(Images.class);
163
164     public String getUsername() {
165         return username;
166     }
167
168     public void setAccount(AccountResource acct) {
169         account = acct;
170     }
171
172     public AccountResource getAccount() {
173         return account;
174     }
175
176     public void updateFolder(Folder f, boolean showfiles, Command callback, final boolean openParent) {
177         folderTreeView.updateFolder(f, showfiles, callback, openParent);
178     }
179
180     public void updateGroupNode(Group group) {
181         groupTreeView.updateGroupNode(group);
182     }
183
184     public void updateMySharedRoot() {
185         mysharedTreeView.updateRoot();
186     }
187     
188     public void updateSharedFolder(Folder f, boolean showfiles, Command callback) {
189         mysharedTreeView.updateFolder(f, showfiles, callback);
190     }
191     
192     public void updateSharedFolder(Folder f, boolean showfiles) {
193         updateSharedFolder(f, showfiles, null);
194     }
195
196     public void updateOtherSharedFolder(Folder f, boolean showfiles, Command callback) {
197         otherSharedTreeView.updateFolder(f, showfiles, callback);
198     }
199
200     public MysharedTreeView getMySharedTreeView() {
201         return mysharedTreeView;
202     }
203
204     /**
205          * An aggregate image bundle that pulls together all the images for this
206          * application into a single bundle.
207          */
208         public interface Images extends TopPanel.Images, FileList.Images, ToolsMenu.Images {
209
210                 @Source("gr/grnet/pithos/resources/document.png")
211                 ImageResource folders();
212
213                 @Source("gr/grnet/pithos/resources/advancedsettings.png")
214                 @ImageOptions(width=32, height=32)
215                 ImageResource tools();
216         }
217
218         private Throwable error;
219         
220         /**
221          * The Application Clipboard implementation;
222          */
223         private Clipboard clipboard = new Clipboard();
224
225         /**
226          * The top panel that contains the menu bar.
227          */
228         private TopPanel topPanel;
229
230         /**
231          * The panel that contains the various system messages.
232          */
233         private MessagePanel messagePanel = new MessagePanel(this, Pithos.images);
234
235         /**
236          * The bottom panel that contains the status bar.
237          */
238         StatusPanel statusPanel = null;
239
240         /**
241          * The file list widget.
242          */
243         private FileList fileList;
244
245         /**
246          * The tab panel that occupies the right side of the screen.
247          */
248         private VerticalPanel inner = new VerticalPanel();
249
250
251         /**
252          * The split panel that will contain the left and right panels.
253          */
254         private HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
255
256         /**
257          * The currently selected item in the application, for use by the Edit menu
258          * commands. Potential types are Folder, File, User and Group.
259          */
260         private Object currentSelection;
261
262         public HashMap<String, String> userFullNameMap = new HashMap<String, String>();
263
264     private String username = null;
265
266     /**
267      * The authentication token of the current user.
268      */
269     private String token;
270
271     VerticalPanel trees;
272     
273     SingleSelectionModel<Folder> folderTreeSelectionModel;
274     FolderTreeViewModel folderTreeViewModel;
275     FolderTreeView folderTreeView;
276
277     SingleSelectionModel<Folder> mysharedTreeSelectionModel;
278     MysharedTreeViewModel mysharedTreeViewModel;
279     MysharedTreeView mysharedTreeView = null;;
280
281     protected SingleSelectionModel<Folder> otherSharedTreeSelectionModel;
282     OtherSharedTreeViewModel otherSharedTreeViewModel;
283     OtherSharedTreeView otherSharedTreeView = null;
284
285     GroupTreeViewModel groupTreeViewModel;
286     GroupTreeView groupTreeView;
287
288     TreeView selectedTree;
289     protected AccountResource account;
290     
291     Folder trash;
292     
293     List<Composite> treeViews = new ArrayList<Composite>();
294
295     @SuppressWarnings("rawtypes") List<SingleSelectionModel> selectionModels = new ArrayList<SingleSelectionModel>();
296     
297     public Button upload;
298     
299     private HTML numOfFiles;
300     
301     private Toolbar toolbar;
302     
303     private FileUploadDialog fileUploadDialog = new FileUploadDialog(this);
304
305         UploadAlert uploadAlert;
306         
307         Date lastModified;
308
309         @Override
310         public void onModuleLoad() {
311                 if (parseUserCredentials())
312             initialize();
313         }
314
315     private void initialize() {
316         lastModified = new Date(); //Initialize if-modified-since value with now.
317         resources.pithosCss().ensureInjected();
318         boolean bareContent = Window.Location.getParameter("noframe") != null;
319         String contentWidth = bareContent ? "100%" : "75%";
320
321         VerticalPanel outer = new VerticalPanel();
322         outer.setWidth("100%");
323         if (!bareContent) {
324                 outer.addStyleName("pithos-outer");
325         }
326
327         if (!bareContent) {
328                 topPanel = new TopPanel(this, Pithos.images);
329                 topPanel.setWidth("100%");
330                 outer.add(topPanel);
331                 outer.setCellHorizontalAlignment(topPanel, HasHorizontalAlignment.ALIGN_CENTER);
332         }
333         
334         messagePanel.setVisible(false);
335         outer.add(messagePanel);
336         outer.setCellHorizontalAlignment(messagePanel, HasHorizontalAlignment.ALIGN_CENTER);
337         outer.setCellVerticalAlignment(messagePanel, HasVerticalAlignment.ALIGN_MIDDLE);
338
339         HorizontalPanel header = new HorizontalPanel();
340         header.addStyleName("pithos-header");
341         header.setWidth(contentWidth);
342         if (bareContent)
343                 header.addStyleName("pithos-header-noframe");
344         upload = new Button("Upload", new ClickHandler() {
345             @Override
346             public void onClick(ClickEvent event) {
347                 if (getSelection() != null)
348                         new UploadFileCommand(Pithos.this, null, getSelection()).execute();
349             }
350         });
351         upload.addStyleName("pithos-uploadButton");
352         header.add(upload);
353         header.setCellHorizontalAlignment(upload, HasHorizontalAlignment.ALIGN_LEFT);
354         header.setCellVerticalAlignment(upload, HasVerticalAlignment.ALIGN_MIDDLE);
355
356         toolbar = new Toolbar(this);
357         header.add(toolbar);
358         header.setCellHorizontalAlignment(toolbar, HasHorizontalAlignment.ALIGN_CENTER);
359         header.setCellVerticalAlignment(toolbar, HasVerticalAlignment.ALIGN_MIDDLE);
360         
361         HorizontalPanel folderStatistics = new HorizontalPanel();
362         folderStatistics.addStyleName("pithos-folderStatistics");
363         numOfFiles = new HTML();
364         folderStatistics.add(numOfFiles);
365         folderStatistics.setCellVerticalAlignment(numOfFiles, HasVerticalAlignment.ALIGN_MIDDLE);
366         HTML numOfFilesLabel = new HTML("&nbsp;Files");
367         folderStatistics.add(numOfFilesLabel);
368         folderStatistics.setCellVerticalAlignment(numOfFilesLabel, HasVerticalAlignment.ALIGN_MIDDLE);
369         header.add(folderStatistics);
370         header.setCellHorizontalAlignment(folderStatistics, HasHorizontalAlignment.ALIGN_RIGHT);
371         header.setCellVerticalAlignment(folderStatistics, HasVerticalAlignment.ALIGN_MIDDLE);
372         header.setCellWidth(folderStatistics, "40px");
373         outer.add(header);
374         outer.setCellHorizontalAlignment(header, HasHorizontalAlignment.ALIGN_CENTER);
375         // Inner contains the various lists
376         inner.sinkEvents(Event.ONCONTEXTMENU);
377         inner.setWidth("100%");
378
379         folderTreeSelectionModel = new SingleSelectionModel<Folder>();
380         folderTreeSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
381             @Override
382             public void onSelectionChange(SelectionChangeEvent event) {
383                 if (folderTreeSelectionModel.getSelectedObject() != null) {
384                     deselectOthers(folderTreeView, folderTreeSelectionModel);
385                     applyPermissions(folderTreeSelectionModel.getSelectedObject());
386                     Folder f = folderTreeSelectionModel.getSelectedObject();
387                         updateFolder(f, true, new Command() {
388                                 
389                                 @Override
390                                 public void execute() {
391                                         updateStatistics();
392                                 }
393                         }, true);
394                         showRelevantToolbarButtons();
395                 }
396                                 else {
397                                         if (getSelectedTree().equals(folderTreeView))
398                                                 setSelectedTree(null);
399                                         if (getSelectedTree() == null)
400                                                 showRelevantToolbarButtons();
401                                 }
402             }
403         });
404         selectionModels.add(folderTreeSelectionModel);
405
406         folderTreeViewModel = new FolderTreeViewModel(this, folderTreeSelectionModel);
407         folderTreeView = new FolderTreeView(folderTreeViewModel);
408         treeViews.add(folderTreeView);
409         
410         fileList = new FileList(this, images);
411         inner.add(fileList);
412
413         trees = new VerticalPanel();
414         trees.setWidth("100%");
415         
416         // Add the left and right panels to the split panel.
417         splitPanel.setLeftWidget(trees);
418         FlowPanel right = new FlowPanel();
419         right.getElement().setId("rightPanel");
420         right.add(inner);
421         splitPanel.setRightWidget(right);
422         splitPanel.setSplitPosition("219px");
423         splitPanel.setSize("100%", "100%");
424         splitPanel.addStyleName("pithos-splitPanel");
425         splitPanel.setWidth(contentWidth);
426         outer.add(splitPanel);
427         outer.setCellHorizontalAlignment(splitPanel, HasHorizontalAlignment.ALIGN_CENTER);
428
429         if (!bareContent) {
430                 statusPanel = new StatusPanel();
431                 statusPanel.setWidth("100%");
432                 outer.add(statusPanel);
433                 outer.setCellHorizontalAlignment(statusPanel, HasHorizontalAlignment.ALIGN_CENTER);
434         }
435         else
436                 splitPanel.addStyleName("pithos-splitPanel-noframe");
437
438         // Hook the window resize event, so that we can adjust the UI.
439         Window.addResizeHandler(this);
440         // Clear out the window's built-in margin, because we want to take
441         // advantage of the entire client area.
442         Window.setMargin("0px");
443         // Finally, add the outer panel to the RootPanel, so that it will be
444         // displayed.
445         RootPanel.get().add(outer);
446         // Call the window resized handler to get the initial sizes setup. Doing
447         // this in a deferred command causes it to occur after all widgets'
448         // sizes have been computed by the browser.
449         Scheduler.get().scheduleIncremental(new RepeatingCommand() {
450                         
451                         @Override
452                         public boolean execute() {
453                                 if (!isCloudbarReady())
454                                         return true;
455                 onWindowResized(Window.getClientHeight());
456                                 return false;
457                         }
458                 });
459         
460         Scheduler.get().scheduleDeferred(new ScheduledCommand() {
461             @Override
462             public void execute() {
463                 fetchAccount(new Command() {
464                                         
465                                         @Override
466                                         public void execute() {
467                                 if (!account.hasHomeContainer())
468                                     createHomeContainer(account, this);
469                                 else if (!account.hasTrashContainer())
470                                         createTrashContainer(this);
471                                 else {
472                                         for (Folder f : account.getContainers())
473                                                 if (f.getName().equals(Pithos.TRASH_CONTAINER)) {
474                                                         trash = f;
475                                                         break;
476                                                 }
477                                     trees.add(folderTreeView);
478                                     folderTreeViewModel.initialize(account, new Command() {
479                                                                 
480                                                                 @Override
481                                                                 public void execute() {
482                                                     createMySharedTree();
483                                                                 }
484                                                         });
485
486                                     HorizontalPanel separator = new HorizontalPanel();
487                                     separator.addStyleName("pithos-statisticsSeparator");
488                                     separator.add(new HTML(""));
489                                     trees.add(separator);
490
491                                     groupTreeViewModel = new GroupTreeViewModel(Pithos.this);
492                                     groupTreeView = new GroupTreeView(groupTreeViewModel);
493                                     treeViews.add(groupTreeView);
494                                     trees.add(groupTreeView);
495                                     folderTreeView.showStatistics(account);
496                                 }
497                                         }
498                                 });
499             }
500         });
501     }
502     
503     public void scheduleResfresh() {
504                 Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
505                         
506                         @Override
507                         public boolean execute() {
508                                 final Folder f = getSelection();
509                                 if (f == null)
510                                         return true;
511                                 
512                         HeadRequest<Folder> head = new HeadRequest<Folder>(Folder.class, getApiPath(), getUsername(), "/" + f.getContainer()) {
513
514                                         @Override
515                                         public void onSuccess(Folder _result) {
516                                                 lastModified = new Date();
517                                                 if (getSelectedTree().equals(folderTreeView))
518                                                         updateFolder(f, true, new Command() {
519         
520                                                                 @Override
521                                                                 public void execute() {
522                                                                         scheduleResfresh();
523                                                                 }
524                                                                 
525                                                         }, false);
526                                                 else if (getSelectedTree().equals(mysharedTreeView))
527                                                         updateSharedFolder(f, true, new Command() {
528         
529                                                                 @Override
530                                                                 public void execute() {
531                                                                         scheduleResfresh();
532                                                                 }
533                                                         });
534                                         }
535
536                                         @Override
537                                         public void onError(Throwable t) {
538                                                 if (t instanceof RestException && ((RestException) t).getHttpStatusCode() == HttpStatus.SC_NOT_MODIFIED)
539                                                         scheduleResfresh();
540                                                 else if (retries >= MAX_RETRIES) {
541                                         GWT.log("Error heading folder", t);
542                                                         setError(t);
543                                         if (t instanceof RestException)
544                                             displayError("Error heading folder: " + ((RestException) t).getHttpStatusText());
545                                         else
546                                             displayError("System error heading folder: " + t.getMessage());
547                                 }
548                                 else {//retry
549                                         GWT.log("Retry " + retries);
550                                         Scheduler.get().scheduleDeferred(this);
551                                 }
552                                         }
553
554                                         @Override
555                                         protected void onUnauthorized(Response response) {
556                                                 if (retries >= MAX_RETRIES)
557                                                         sessionExpired();
558                                 else //retry
559                                         Scheduler.get().scheduleDeferred(this);
560                                         }
561                                 };
562                                 head.setHeader("X-Auth-Token", getToken());
563                                 head.setHeader("If-Modified-Since", DateTimeFormat.getFormat("EEE, dd MMM yyyy HH:mm:ss").format(lastModified, TimeZone.createTimeZone(0)) + " GMT");
564                                 Scheduler.get().scheduleDeferred(head);
565                                 
566                                 return false;
567                         }
568                 }, 3000);
569     }
570
571     public void applyPermissions(Folder f) {
572         if (f != null) {
573                 if (f.isInTrash()) {
574                         upload.setEnabled(false);
575                         disableUploadArea();
576                 }
577                 else {
578                         Boolean[] perms = f.getPermissions().get(username);
579                         if (f.getOwner().equals(username) || (perms != null && perms[1] != null && perms[1])) {
580                                 upload.setEnabled(true);
581                                 enableUploadArea();
582                         }
583                         else {
584                                 upload.setEnabled(false);
585                                 disableUploadArea();
586                         }
587                 }
588         }
589         else {
590                 upload.setEnabled(false);
591                 disableUploadArea();
592         }
593         }
594
595         @SuppressWarnings({ "rawtypes", "unchecked" })
596         public void deselectOthers(TreeView _selectedTree, SingleSelectionModel model) {
597         selectedTree = _selectedTree;
598         
599         for (SingleSelectionModel s : selectionModels)
600             if (!s.equals(model) && s.getSelectedObject() != null)
601                 s.setSelected(s.getSelectedObject(), false);
602     }
603
604     public void showFiles(final Folder f) {
605         Set<File> files = f.getFiles();
606         showFiles(files);
607     }
608
609     public void showFiles(Set<File> files) {
610         fileList.setFiles(new ArrayList<File>(files));
611     }
612
613     /**
614          * Parse and store the user credentials to the appropriate fields.
615          */
616         private boolean parseUserCredentials() {
617         Configuration conf = (Configuration) GWT.create(Configuration.class);
618                 Dictionary otherProperties = Dictionary.getDictionary("otherProperties");
619         String cookie = otherProperties.get("authCookie");
620         String auth = Cookies.getCookie(cookie);
621         if (auth == null) {
622             authenticateUser();
623             return false;
624         }
625         if (auth.startsWith("\""))
626                 auth = auth.substring(1);
627         if (auth.endsWith("\""))
628                 auth = auth.substring(0, auth.length() - 1);
629                 String[] authSplit = auth.split("\\" + conf.cookieSeparator(), 2);
630                 if (authSplit.length != 2) {
631                     authenticateUser();
632                     return false;
633                 }
634                 username = authSplit[0];
635                 token = authSplit[1];
636
637         String gotoUrl = Window.Location.getParameter("goto");
638                 if (gotoUrl != null && gotoUrl.length() > 0) {
639                         Window.Location.assign(gotoUrl);
640                         return false;
641                 }
642                 return true;
643     }
644
645     /**
646          * Redirect the user to the login page for authentication.
647          */
648         protected void authenticateUser() {
649                 Dictionary otherProperties = Dictionary.getDictionary("otherProperties");
650         Window.Location.assign(otherProperties.get("loginUrl") + Window.Location.getHref());
651         }
652
653         public void fetchAccount(final Command callback) {
654         String path = "?format=json";
655
656         GetRequest<AccountResource> getAccount = new GetRequest<AccountResource>(AccountResource.class, getApiPath(), username, path) {
657             @Override
658             public void onSuccess(AccountResource _result) {
659                 account = _result;
660                 if (callback != null)
661                         callback.execute();
662             }
663
664             @Override
665             public void onError(Throwable t) {
666                 GWT.log("Error getting account", t);
667                                 setError(t);
668                 if (t instanceof RestException)
669                     displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
670                 else
671                     displayError("System error fetching user data: " + t.getMessage());
672             }
673
674                         @Override
675                         protected void onUnauthorized(Response response) {
676                                 sessionExpired();
677                         }
678         };
679         getAccount.setHeader("X-Auth-Token", token);
680         Scheduler.get().scheduleDeferred(getAccount);
681     }
682
683     public void updateStatistics() {
684         HeadRequest<AccountResource> headAccount = new HeadRequest<AccountResource>(AccountResource.class, getApiPath(), username, "", account) {
685
686                         @Override
687                         public void onSuccess(AccountResource _result) {
688                                 folderTreeView.showStatistics(account);
689                         }
690
691                         @Override
692                         public void onError(Throwable t) {
693                 GWT.log("Error getting account", t);
694                                 setError(t);
695                 if (t instanceof RestException)
696                     displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
697                 else
698                     displayError("System error fetching user data: " + t.getMessage());
699                         }
700
701                         @Override
702                         protected void onUnauthorized(Response response) {
703                                 sessionExpired();
704                         }
705                 };
706                 headAccount.setHeader("X-Auth-Token", token);
707                 Scheduler.get().scheduleDeferred(headAccount);
708         }
709
710         protected void createHomeContainer(final AccountResource _account, final Command callback) {
711         String path = "/" + Pithos.HOME_CONTAINER;
712         PutRequest createPithos = new PutRequest(getApiPath(), getUsername(), path) {
713             @Override
714             public void onSuccess(Resource result) {
715                 if (!_account.hasTrashContainer())
716                         createTrashContainer(callback);
717                 else
718                         fetchAccount(callback);
719             }
720
721             @Override
722             public void onError(Throwable t) {
723                 GWT.log("Error creating pithos", t);
724                                 setError(t);
725                 if (t instanceof RestException)
726                     displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
727                 else
728                     displayError("System error Error creating pithos: " + t.getMessage());
729             }
730
731                         @Override
732                         protected void onUnauthorized(Response response) {
733                                 sessionExpired();
734                         }
735         };
736         createPithos.setHeader("X-Auth-Token", getToken());
737         Scheduler.get().scheduleDeferred(createPithos);
738     }
739
740     protected void createTrashContainer(final Command callback) {
741         String path = "/" + Pithos.TRASH_CONTAINER;
742         PutRequest createPithos = new PutRequest(getApiPath(), getUsername(), path) {
743             @Override
744             public void onSuccess(Resource result) {
745                         fetchAccount(callback);
746             }
747
748             @Override
749             public void onError(Throwable t) {
750                 GWT.log("Error creating pithos", t);
751                                 setError(t);
752                 if (t instanceof RestException)
753                     displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
754                 else
755                     displayError("System error Error creating pithos: " + t.getMessage());
756             }
757
758                         @Override
759                         protected void onUnauthorized(Response response) {
760                                 sessionExpired();
761                         }
762         };
763         createPithos.setHeader("X-Auth-Token", getToken());
764         Scheduler.get().scheduleDeferred(createPithos);
765     }
766
767     /**
768          * Creates an HTML fragment that places an image & caption together, for use
769          * in a group header.
770          *
771          * @param imageProto an image prototype for an image
772          * @param caption the group caption
773          * @return the header HTML fragment
774          */
775         private String createHeaderHTML(AbstractImagePrototype imageProto, String caption) {
776                 String captionHTML = "<table class='caption' cellpadding='0' " 
777                 + "cellspacing='0'>" + "<tr><td class='lcaption'>" + imageProto.getHTML() 
778                 + "</td><td id =" + caption +" class='rcaption'><b style='white-space:nowrap'>&nbsp;" 
779                 + caption + "</b></td></tr></table>";
780                 return captionHTML;
781         }
782
783         protected void onWindowResized(int height) {
784                 // Adjust the split panel to take up the available room in the window.
785                 int newHeight = height - splitPanel.getAbsoluteTop() - 153;
786                 if (newHeight < 1)
787                         newHeight = 1;
788                 splitPanel.setHeight("" + newHeight);
789                 inner.setHeight("" + newHeight);
790         }
791         
792         native boolean isCloudbarReady()/*-{
793                 if ($wnd.$("div.servicesbar") && $wnd.$("div.servicesbar").height() > 0)
794                         return true;
795                 return false;
796         }-*/;
797         
798         @Override
799         public void onResize(ResizeEvent event) {
800                 int height = event.getHeight();
801                 onWindowResized(height);
802         }
803
804         /**
805          * Display an error message.
806          *
807          * @param msg the message to display
808          */
809         public void displayError(String msg) {
810                 messagePanel.displayError(msg);
811                 onWindowResized(Window.getClientHeight());
812         }
813
814         /**
815          * Display a warning message.
816          *
817          * @param msg the message to display
818          */
819         public void displayWarning(String msg) {
820                 messagePanel.displayWarning(msg);
821                 onWindowResized(Window.getClientHeight());
822         }
823
824         /**
825          * Display an informational message.
826          *
827          * @param msg the message to display
828          */
829         public void displayInformation(String msg) {
830                 messagePanel.displayInformation(msg);
831                 onWindowResized(Window.getClientHeight());
832         }
833
834         /**
835          * Retrieve the fileList.
836          *
837          * @return the fileList
838          */
839         public FileList getFileList() {
840                 return fileList;
841         }
842
843         /**
844          * Retrieve the topPanel.
845          *
846          * @return the topPanel
847          */
848         TopPanel getTopPanel() {
849                 return topPanel;
850         }
851
852         /**
853          * Retrieve the clipboard.
854          *
855          * @return the clipboard
856          */
857         public Clipboard getClipboard() {
858                 return clipboard;
859         }
860
861         public StatusPanel getStatusPanel() {
862                 return statusPanel;
863         }
864
865         public String getToken() {
866                 return token;
867         }
868
869         public static native void preventIESelection() /*-{
870                 $doc.body.onselectstart = function() {
871                         return false;
872                 };
873         }-*/;
874
875         public static native void enableIESelection() /*-{
876                 if ($doc.body.onselectstart != null)
877                         $doc.body.onselectstart = null;
878         }-*/;
879
880         /**
881          * @return the absolute path of the API root URL
882          */
883         public String getApiPath() {
884                 Configuration conf = (Configuration) GWT.create(Configuration.class);
885                 return conf.apiPath();
886         }
887
888         /**
889          * History support for folder navigation
890          * adds a new browser history entry
891          *
892          * @param key
893          */
894         public void updateHistory(String key){
895 //              Replace any whitespace of the initial string to "+"
896 //              String result = key.replaceAll("\\s","+");
897 //              Add a new browser history entry.
898 //              History.newItem(result);
899                 History.newItem(key);
900         }
901
902     public void deleteFolder(final Folder folder, final Command callback) {
903         String path = "/" + folder.getContainer() + "/" + folder.getPrefix() + "?delimiter=/" + "&t=" + System.currentTimeMillis();
904         DeleteRequest deleteFolder = new DeleteRequest(getApiPath(), folder.getOwner(), path) {
905                         
906                         @Override
907                         protected void onUnauthorized(Response response) {
908                                 sessionExpired();
909                         }
910                         
911                         @Override
912                         public void onSuccess(Resource result) {
913                 updateFolder(folder.getParent(), true, new Command() {
914                                         
915                                         @Override
916                                         public void execute() {
917                                                 folderTreeSelectionModel.setSelected(folder.getParent(), true);
918                                                 updateStatistics();
919                                                 if (callback != null)
920                                                         callback.execute();
921                                         }
922                                 }, true);
923                         }
924                         
925                         @Override
926                         public void onError(Throwable t) {
927                 GWT.log("", t);
928                                 setError(t);
929                 if (t instanceof RestException) {
930                         if (((RestException) t).getHttpStatusCode() != Response.SC_NOT_FOUND)
931                                 displayError("Unable to delete folder: "+((RestException) t).getHttpStatusText());
932                         else
933                                 onSuccess(null);
934                 }
935                 else
936                     displayError("System error unable to delete folder: " + t.getMessage());
937                         }
938                 };
939                 deleteFolder.setHeader("X-Auth-Token", getToken());
940                 Scheduler.get().scheduleDeferred(deleteFolder);
941     }
942
943     public FolderTreeView getFolderTreeView() {
944         return folderTreeView;
945     }
946
947     public void copyFiles(final Iterator<File> iter, final String targetUsername, final String targetUri, final Command callback) {
948         if (iter.hasNext()) {
949             File file = iter.next();
950             String path = targetUri + "/" + file.getName();
951             PutRequest copyFile = new PutRequest(getApiPath(), targetUsername, path) {
952                 @Override
953                 public void onSuccess(Resource result) {
954                     copyFiles(iter, targetUsername, targetUri, callback);
955                 }
956
957                 @Override
958                 public void onError(Throwable t) {
959                     GWT.log("", t);
960                                         setError(t);
961                     if (t instanceof RestException) {
962                         displayError("Unable to copy file: " + ((RestException) t).getHttpStatusText());
963                     }
964                     else
965                         displayError("System error unable to copy file: "+t.getMessage());
966                 }
967
968                                 @Override
969                                 protected void onUnauthorized(Response response) {
970                                         sessionExpired();
971                                 }
972             };
973             copyFile.setHeader("X-Auth-Token", getToken());
974             copyFile.setHeader("X-Copy-From", URL.encodePathSegment(file.getUri()));
975             if (!file.getOwner().equals(targetUsername))
976                 copyFile.setHeader("X-Source-Account", URL.encodePathSegment(file.getOwner()));
977             copyFile.setHeader("Content-Type", file.getContentType());
978             Scheduler.get().scheduleDeferred(copyFile);
979         }
980         else  if (callback != null) {
981             callback.execute();
982         }
983     }
984
985     public void copyFolder(final Folder f, final String targetUsername, final String targetUri, boolean move, final Command callback) {
986         String path = targetUri + "?delimiter=/";
987         PutRequest copyFolder = new PutRequest(getApiPath(), targetUsername, path) {
988             @Override
989             public void onSuccess(Resource result) {
990                 if (callback != null)
991                         callback.execute();
992             }
993
994             @Override
995             public void onError(Throwable t) {
996                 GWT.log("", t);
997                                 setError(t);
998                if (t instanceof RestException) {
999                     displayError("Unable to copy folder: " + ((RestException) t).getHttpStatusText());
1000                 }
1001                 else
1002                     displayError("System error copying folder: " + t.getMessage());
1003             }
1004
1005                         @Override
1006                         protected void onUnauthorized(Response response) {
1007                                 sessionExpired();
1008                         }
1009         };
1010         copyFolder.setHeader("X-Auth-Token", getToken());
1011         copyFolder.setHeader("Accept", "*/*");
1012         copyFolder.setHeader("Content-Length", "0");
1013         copyFolder.setHeader("Content-Type", "application/directory");
1014         if (!f.getOwner().equals(targetUsername))
1015                 copyFolder.setHeader("X-Source-Account", f.getOwner());
1016         if (move)
1017             copyFolder.setHeader("X-Move-From", URL.encodePathSegment(f.getUri()));
1018         else
1019                 copyFolder.setHeader("X-Copy-From", URL.encodePathSegment(f.getUri()));
1020         Scheduler.get().scheduleDeferred(copyFolder);
1021     }
1022     
1023     public void addSelectionModel(@SuppressWarnings("rawtypes") SingleSelectionModel model) {
1024         selectionModels.add(model);
1025     }
1026
1027         public OtherSharedTreeView getOtherSharedTreeView() {
1028                 return otherSharedTreeView;
1029         }
1030
1031         public void updateTrash(boolean showFiles, Command callback) {
1032                 updateFolder(trash, showFiles, callback, true);
1033         }
1034
1035         public void updateGroupsNode() {
1036                 groupTreeView.updateGroupNode(null);
1037         }
1038
1039         public Group addGroup(String groupname) {
1040                 Group newGroup = new Group(groupname);
1041                 account.addGroup(newGroup);
1042                 groupTreeView.updateGroupNode(null);
1043                 return newGroup;
1044         }
1045
1046         public void removeGroup(Group group) {
1047                 account.removeGroup(group);
1048                 updateGroupsNode();
1049         }
1050
1051         public TreeView getSelectedTree() {
1052                 return selectedTree;
1053         }
1054         
1055         public void setSelectedTree(TreeView selected) {
1056                 selectedTree = selected;
1057         }
1058
1059         public Folder getSelection() {
1060                 if (selectedTree != null)
1061                         return selectedTree.getSelection();
1062                 return null;
1063         }
1064
1065         public void showFolderStatistics(int folderFileCount) {
1066                 numOfFiles.setHTML(String.valueOf(folderFileCount));
1067         }
1068
1069         public GroupTreeView getGroupTreeView() {
1070                 return groupTreeView;
1071         }
1072
1073         public void sessionExpired() {
1074                 new SessionExpiredDialog(this).center();
1075         }
1076
1077         public void updateRootFolder(Command callback) {
1078                 updateFolder(account.getPithos(), false, callback, true);
1079         }
1080
1081         void createMySharedTree() {
1082                 mysharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1083                 mysharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1084                     @Override
1085                     public void onSelectionChange(SelectionChangeEvent event) {
1086                         if (mysharedTreeSelectionModel.getSelectedObject() != null) {
1087                             deselectOthers(mysharedTreeView, mysharedTreeSelectionModel);
1088                             upload.setEnabled(false);
1089                             disableUploadArea();
1090                             updateSharedFolder(mysharedTreeSelectionModel.getSelectedObject(), true);
1091                                         showRelevantToolbarButtons();
1092                         }
1093                                 else {
1094                                         if (getSelectedTree().equals(mysharedTreeView))
1095                                                 setSelectedTree(null);
1096                                         if (getSelectedTree() == null)
1097                                                 showRelevantToolbarButtons();
1098                                 }
1099                     }
1100                 });
1101                 selectionModels.add(mysharedTreeSelectionModel);
1102                 mysharedTreeViewModel = new MysharedTreeViewModel(Pithos.this, mysharedTreeSelectionModel);
1103                 mysharedTreeViewModel.initialize(new Command() {
1104                         
1105                         @Override
1106                         public void execute() {
1107                             mysharedTreeView = new MysharedTreeView(mysharedTreeViewModel);
1108                                 trees.insert(mysharedTreeView, 2);
1109                                 treeViews.add(mysharedTreeView);
1110                                 createOtherSharedTree();
1111                         }
1112                 });
1113         }
1114
1115         void createOtherSharedTree() {
1116                 otherSharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1117                 otherSharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1118                     @Override
1119                     public void onSelectionChange(SelectionChangeEvent event) {
1120                         if (otherSharedTreeSelectionModel.getSelectedObject() != null) {
1121                             deselectOthers(otherSharedTreeView, otherSharedTreeSelectionModel);
1122                             applyPermissions(otherSharedTreeSelectionModel.getSelectedObject());
1123                             updateOtherSharedFolder(otherSharedTreeSelectionModel.getSelectedObject(), true, null);
1124                                         showRelevantToolbarButtons();
1125                         }
1126                                 else {
1127                                         if (getSelectedTree().equals(otherSharedTreeView))
1128                                                 setSelectedTree(null);
1129                                         if (getSelectedTree() == null)
1130                                                 showRelevantToolbarButtons();
1131                                 }
1132                     }
1133                 });
1134                 selectionModels.add(otherSharedTreeSelectionModel);
1135                 otherSharedTreeViewModel = new OtherSharedTreeViewModel(Pithos.this, otherSharedTreeSelectionModel);
1136                 otherSharedTreeViewModel.initialize(new Command() {
1137                         
1138                         @Override
1139                         public void execute() {
1140                             otherSharedTreeView = new OtherSharedTreeView(otherSharedTreeViewModel);
1141                                 trees.insert(otherSharedTreeView, 1);
1142                                 treeViews.add(otherSharedTreeView);
1143                                 scheduleResfresh();
1144                         }
1145                 });
1146         }
1147
1148         public native void log1(String message)/*-{
1149                 $wnd.console.log(message);
1150         }-*/;
1151
1152         public String getErrorData() {
1153                 if (error != null)
1154                         return error.toString();
1155                 return "";
1156         }
1157         
1158         public void setError(Throwable t) {
1159                 error = t;
1160         }
1161         
1162         public void showRelevantToolbarButtons() {
1163                 toolbar.showRelevantButtons();
1164         }
1165
1166         public FileUploadDialog getFileUploadDialog() {
1167                 if (fileUploadDialog == null)
1168                         fileUploadDialog = new FileUploadDialog(this);
1169                 return fileUploadDialog;
1170         }
1171
1172         public void hideUploadIndicator() {
1173                 upload.removeStyleName("pithos-uploadButton-loading");
1174                 upload.setTitle("");
1175         }
1176         
1177         public void showUploadIndicator() {
1178                 upload.addStyleName("pithos-uploadButton-loading");
1179                 upload.setTitle("Upload in progress. Click for details.");
1180         }
1181
1182         public void scheduleFolderHeadCommand(final Folder folder, final Command callback) {
1183                 if (folder == null) {
1184                         if (callback != null)
1185                                 callback.execute();
1186                 }
1187                 else {
1188                         HeadRequest<Folder> headFolder = new HeadRequest<Folder>(Folder.class, getApiPath(), folder.getOwner(), folder.getUri(), folder) {
1189         
1190                                 @Override
1191                                 public void onSuccess(Folder _result) {
1192                                         if (callback != null)
1193                                                 callback.execute();
1194                                 }
1195         
1196                                 @Override
1197                                 public void onError(Throwable t) {
1198                                 if (t instanceof RestException) {
1199                                         if (((RestException) t).getHttpStatusCode() == Response.SC_NOT_FOUND) {
1200                                 final String path = folder.getUri();
1201                                 PutRequest newFolder = new PutRequest(getApiPath(), folder.getOwner(), path) {
1202                                     @Override
1203                                     public void onSuccess(Resource _result) {
1204                                         scheduleFolderHeadCommand(folder, callback);
1205                                     }
1206         
1207                                     @Override
1208                                     public void onError(Throwable _t) {
1209                                         GWT.log("", _t);
1210                                                                 setError(_t);
1211                                         if(_t instanceof RestException){
1212                                             displayError("Unable to create folder: " + ((RestException) _t).getHttpStatusText());
1213                                         }
1214                                         else
1215                                             displayError("System error creating folder: " + _t.getMessage());
1216                                     }
1217         
1218                                                 @Override
1219                                                 protected void onUnauthorized(Response response) {
1220                                                         sessionExpired();
1221                                                 }
1222                                 };
1223                                 newFolder.setHeader("X-Auth-Token", getToken());
1224                                 newFolder.setHeader("Content-Type", "application/folder");
1225                                 newFolder.setHeader("Accept", "*/*");
1226                                 newFolder.setHeader("Content-Length", "0");
1227                                 Scheduler.get().scheduleDeferred(newFolder);
1228                                         }
1229                                         else
1230                                                 displayError("Error heading folder: " + ((RestException) t).getHttpStatusText());
1231                                 }
1232                                 else
1233                                     displayError("System error heading folder: " + t.getMessage());
1234         
1235                                 GWT.log("Error heading folder", t);
1236                                         setError(t);
1237                                 }
1238         
1239                                 @Override
1240                                 protected void onUnauthorized(Response response) {
1241                                         sessionExpired();
1242                                 }
1243                         };
1244                         headFolder.setHeader("X-Auth-Token", getToken());
1245                         Scheduler.get().scheduleDeferred(headFolder);
1246                 }
1247         }
1248
1249         public void scheduleFileHeadCommand(File f, final Command callback) {
1250                 HeadRequest<File> headFile = new HeadRequest<File>(File.class, getApiPath(), f.getOwner(), f.getUri(), f) {
1251
1252                         @Override
1253                         public void onSuccess(File _result) {
1254                                 if (callback != null)
1255                                         callback.execute();
1256                         }
1257
1258                         @Override
1259                         public void onError(Throwable t) {
1260                         GWT.log("Error heading file", t);
1261                                 setError(t);
1262                         if (t instanceof RestException)
1263                             displayError("Error heading file: " + ((RestException) t).getHttpStatusText());
1264                         else
1265                             displayError("System error heading file: " + t.getMessage());
1266                         }
1267
1268                         @Override
1269                         protected void onUnauthorized(Response response) {
1270                                 sessionExpired();
1271                         }
1272                 };
1273                 headFile.setHeader("X-Auth-Token", getToken());
1274                 Scheduler.get().scheduleDeferred(headFile);
1275         }
1276
1277         public boolean isMySharedSelected() {
1278                 return getSelectedTree().equals(getMySharedTreeView());
1279         }
1280         
1281         private Folder getUploadFolder() {
1282                 if (folderTreeView.equals(getSelectedTree()) || otherSharedTreeView.equals(getSelectedTree())) {
1283                         return getSelection();
1284                 }
1285                 return null;
1286         }
1287         
1288         private void updateUploadFolder() {
1289                 updateUploadFolder(null);
1290         }
1291         
1292         private void updateUploadFolder(final JsArrayString urls) {
1293                 if (folderTreeView.equals(getSelectedTree()) || otherSharedTreeView.equals(getSelectedTree())) {
1294                         Folder f = getSelection();
1295                         if (getSelectedTree().equals(getFolderTreeView()))
1296                                 updateFolder(f, true, new Command() {
1297                                 
1298                                         @Override
1299                                         public void execute() {
1300                                                 updateStatistics();
1301                                                 if (urls != null)
1302                                                         selectUploadedFiles(urls);
1303                                         }
1304                                 }, false);
1305                         else
1306                                 updateOtherSharedFolder(f, true, null);
1307                 }
1308         }
1309
1310         public native void disableUploadArea() /*-{
1311                 var uploader = $wnd.$("#uploader").pluploadQueue();
1312                 var dropElm = $wnd.document.getElementById('rightPanel');
1313                 $wnd.plupload.removeAllEvents(dropElm, uploader.id);
1314         }-*/;
1315
1316         public native void enableUploadArea() /*-{
1317                 var uploader = $wnd.$("#uploader").pluploadQueue();
1318                 var dropElm = $wnd.document.getElementById('rightPanel');
1319                 $wnd.plupload.removeAllEvents(dropElm, uploader.id);
1320                 if (uploader.runtime == 'html5') {
1321                         uploader.settings.drop_element = 'rightPanel';
1322                         uploader.trigger('PostInit');
1323                 }
1324         }-*/;
1325         
1326         public void showUploadAlert(int nOfFiles) {
1327                 if (uploadAlert == null)
1328                         uploadAlert = new UploadAlert(this, nOfFiles);
1329                 if (!uploadAlert.isShowing())
1330                         uploadAlert.setPopupPositionAndShow(new PopupPanel.PositionCallback() {
1331                                 
1332                                 @Override
1333                                 public void setPosition(int offsetWidth, int offsetHeight) {
1334                                         uploadAlert.setPopupPosition((Window.getClientWidth() - offsetWidth)/2, statusPanel.getAbsoluteTop() - offsetHeight);
1335                                 }
1336                         });
1337                 uploadAlert.setNumOfFiles(nOfFiles);
1338         }
1339         
1340         public void hideUploadAlert() {
1341                 if (uploadAlert != null && uploadAlert.isShowing())
1342                         uploadAlert.hide();
1343         }
1344         
1345         public void selectUploadedFiles(JsArrayString urls) {
1346                 List<String> selectedUrls = new ArrayList<String>();
1347                 for (int i=0; i<urls.length(); i++)
1348                         selectedUrls.add(urls.get(i));
1349                 fileList.selectByUrl(selectedUrls);
1350         }
1351         
1352         public void deleteAndCreateTrash() {
1353                 DeleteRequest delete = new DeleteRequest(getApiPath(), getUsername(), "/trash") {
1354                         
1355                         @Override
1356                         protected void onUnauthorized(Response response) {
1357                                 sessionExpired();
1358                         }
1359                         
1360                         @Override
1361                         public void onSuccess(Resource result) {
1362                                 createTrashContainer(null);
1363                         }
1364                         
1365                         @Override
1366                         public void onError(Throwable t) {
1367                 GWT.log("Error deleting trash", t);
1368                                 setError(t);
1369                 if (t instanceof RestException)
1370                     displayError("Error deleting trash: " + ((RestException) t).getHttpStatusText());
1371                 else
1372                     displayError("System error deleting trash: " + t.getMessage());
1373                         }
1374                 };
1375                 delete.setHeader("X-Auth-Token", getToken());
1376                 Scheduler.get().scheduleDeferred(delete);
1377         }
1378 }