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