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