Add UpdateUserCatalogs as a convenience wrapper around GetUserCatalogs
[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         System.out.println("otherProperties = " + otherProperties);
664         String cookie = otherProperties.get("authCookie");
665         System.out.println("cookie = " + cookie);
666         Cookies.setCookie(cookie, "868fbb51-94a4-477d-8da3-dadad080c861|HlPjywM5FGAncsoNDzPZ2Q==");
667 //        Cookies.setCookie(cookie, "868fbb51-94a4-477d-8da3-dadad080c861%7CHlPjywM5FGAncsoNDzPZ2Q%3D%3D");
668         for(String name: Cookies.getCookieNames()) {
669             System.out.println("cookie name: " + name);
670         }
671         String auth = Cookies.getCookie(cookie);
672         System.out.println("auth = " + auth);
673         if(auth == null) {
674             authenticateUser();
675             return false;
676         }
677         if(auth.startsWith("\"")) {
678             auth = auth.substring(1);
679         }
680         if(auth.endsWith("\"")) {
681             auth = auth.substring(0, auth.length() - 1);
682         }
683         String[] authSplit = auth.split("\\" + conf.cookieSeparator(), 2);
684         for(String authPart: authSplit) {
685             System.out.println("authPart: " + authPart);
686         }
687         if(authSplit.length != 2) {
688             authenticateUser();
689             return false;
690         }
691         userID = authSplit[0];
692         userToken = authSplit[1];
693         System.out.println("userID = " + userID);
694         System.out.println("userToken = " + userToken);
695
696         String gotoUrl = Window.Location.getParameter("goto");
697         if(gotoUrl != null && gotoUrl.length() > 0) {
698             Window.Location.assign(gotoUrl);
699             return false;
700         }
701         System.out.println("Returning true");
702         return true;
703     }
704
705     /**
706      * Redirect the user to the login page for authentication.
707      */
708     protected void authenticateUser() {
709         Dictionary otherProperties = Dictionary.getDictionary("otherProperties");
710         Window.Location.assign(otherProperties.get("loginUrl") + Window.Location.getHref());
711     }
712
713     public void fetchAccount(final Command callback) {
714         System.out.println("fetchAccount(), userID = " + this.userID + ", userToken = " + this.userToken);
715         String path = "?format=json";
716
717         GetRequest<AccountResource> getAccount = new GetRequest<AccountResource>(AccountResource.class, getApiPath(), userID, path) {
718             @Override
719             public void onSuccess(AccountResource _result) {
720                 System.out.println("fetchAccount(), userID = " + userID + ", userToken = " + userToken + " onSuccess()");
721                 account = _result;
722                 if(callback != null) {
723                     callback.execute();
724                 }
725                 // Initialize the user catalog
726                 new UpdateUserCatalogs(Pithos.this, Pithos.this.getUserID()).scheduleDeferred();
727             }
728
729             @Override
730             public void onError(Throwable t) {
731                 System.out.println("fetchAccount(), userID = " + userID + ", userToken = " + userToken + " onError() " + t.getClass().getName() + ": " + t.getMessage());
732                 GWT.log("Error getting account", t);
733                 setError(t);
734                 if(t instanceof RestException) {
735                     System.out.println("fetchAccount(), userID = " + userID + ", userToken = " + userToken + " Error getting account: " + ((RestException) t).getHttpStatusText());
736                     displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
737                 }
738                 else {
739                     System.out.println("fetchAccount(), userID = " + userID + ", userToken = " + userToken + "System error fetching user data: " + t.getMessage());
740                     displayError("System error fetching user data: " + t.getMessage());
741                 }
742             }
743
744             @Override
745             protected void onUnauthorized(Response response) {
746                 System.out.println("fetchAccount(), userID = " + userID + ", userToken = " + userToken + " onUnauthorized()");
747                 sessionExpired();
748             }
749         };
750         getAccount.setHeader("X-Auth-Token", userToken);
751         Scheduler.get().scheduleDeferred(getAccount);
752     }
753
754     public void updateStatistics() {
755         HeadRequest<AccountResource> headAccount = new HeadRequest<AccountResource>(AccountResource.class, getApiPath(), userID, "", account) {
756
757             @Override
758             public void onSuccess(AccountResource _result) {
759                 folderTreeView.showStatistics(account);
760             }
761
762             @Override
763             public void onError(Throwable t) {
764                 GWT.log("Error getting account", t);
765                 setError(t);
766                 if(t instanceof RestException) {
767                     displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
768                 }
769                 else {
770                     displayError("System error fetching user data: " + t.getMessage());
771                 }
772             }
773
774             @Override
775             protected void onUnauthorized(Response response) {
776                 sessionExpired();
777             }
778         };
779         headAccount.setHeader("X-Auth-Token", userToken);
780         Scheduler.get().scheduleDeferred(headAccount);
781     }
782
783     protected void createHomeContainer(final AccountResource _account, final Command callback) {
784         String path = "/" + Pithos.HOME_CONTAINER;
785         PutRequest createPithos = new PutRequest(getApiPath(), getUserID(), path) {
786             @Override
787             public void onSuccess(Resource result) {
788                 if(!_account.hasTrashContainer()) {
789                     createTrashContainer(callback);
790                 }
791                 else {
792                     fetchAccount(callback);
793                 }
794             }
795
796             @Override
797             public void onError(Throwable t) {
798                 GWT.log("Error creating pithos", t);
799                 setError(t);
800                 if(t instanceof RestException) {
801                     displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
802                 }
803                 else {
804                     displayError("System error Error creating pithos: " + t.getMessage());
805                 }
806             }
807
808             @Override
809             protected void onUnauthorized(Response response) {
810                 sessionExpired();
811             }
812         };
813         createPithos.setHeader("X-Auth-Token", getUserToken());
814         Scheduler.get().scheduleDeferred(createPithos);
815     }
816
817     protected void createTrashContainer(final Command callback) {
818         String path = "/" + Pithos.TRASH_CONTAINER;
819         PutRequest createPithos = new PutRequest(getApiPath(), getUserID(), path) {
820             @Override
821             public void onSuccess(Resource result) {
822                 fetchAccount(callback);
823             }
824
825             @Override
826             public void onError(Throwable t) {
827                 GWT.log("Error creating pithos", t);
828                 setError(t);
829                 if(t instanceof RestException) {
830                     displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
831                 }
832                 else {
833                     displayError("System error Error creating pithos: " + t.getMessage());
834                 }
835             }
836
837             @Override
838             protected void onUnauthorized(Response response) {
839                 sessionExpired();
840             }
841         };
842         createPithos.setHeader("X-Auth-Token", getUserToken());
843         Scheduler.get().scheduleDeferred(createPithos);
844     }
845
846     /**
847      * Creates an HTML fragment that places an image & caption together, for use
848      * in a group header.
849      *
850      * @param imageProto an image prototype for an image
851      * @param caption    the group caption
852      * @return the header HTML fragment
853      */
854     private String createHeaderHTML(AbstractImagePrototype imageProto, String caption) {
855         String captionHTML = "<table class='caption' cellpadding='0' "
856             + "cellspacing='0'>" + "<tr><td class='lcaption'>" + imageProto.getHTML()
857             + "</td><td id =" + caption + " class='rcaption'><b style='white-space:nowrap'>&nbsp;"
858             + caption + "</b></td></tr></table>";
859         return captionHTML;
860     }
861
862     protected void onWindowResized(int height) {
863         // Adjust the split panel to take up the available room in the window.
864         int newHeight = height - splitPanel.getAbsoluteTop() - 153;
865         if(newHeight < 1) {
866             newHeight = 1;
867         }
868         splitPanel.setHeight("" + newHeight);
869         inner.setHeight("" + newHeight);
870     }
871
872     native boolean isCloudbarReady()/*-{
873       if ($wnd.$("div.cloudbar") && $wnd.$("div.cloudbar").height() > 0)
874         return true;
875       return false;
876     }-*/;
877
878     @Override
879     public void onResize(ResizeEvent event) {
880         int height = event.getHeight();
881         onWindowResized(height);
882     }
883
884     /**
885      * Display an error message.
886      *
887      * @param msg the message to display
888      */
889     public void displayError(String msg) {
890         messagePanel.displayError(msg);
891         onWindowResized(Window.getClientHeight());
892     }
893
894     /**
895      * Display a warning message.
896      *
897      * @param msg the message to display
898      */
899     public void displayWarning(String msg) {
900         messagePanel.displayWarning(msg);
901         onWindowResized(Window.getClientHeight());
902     }
903
904     /**
905      * Display an informational message.
906      *
907      * @param msg the message to display
908      */
909     public void displayInformation(String msg) {
910         messagePanel.displayInformation(msg);
911         onWindowResized(Window.getClientHeight());
912     }
913
914     /**
915      * Retrieve the fileList.
916      *
917      * @return the fileList
918      */
919     public FileList getFileList() {
920         return fileList;
921     }
922
923     /**
924      * Retrieve the topPanel.
925      *
926      * @return the topPanel
927      */
928     TopPanel getTopPanel() {
929         return topPanel;
930     }
931
932     /**
933      * Retrieve the clipboard.
934      *
935      * @return the clipboard
936      */
937     public Clipboard getClipboard() {
938         return clipboard;
939     }
940
941     public StatusPanel getStatusPanel() {
942         return statusPanel;
943     }
944
945     public String getUserToken() {
946         return userToken;
947     }
948
949     public static native void preventIESelection() /*-{
950       $doc.body.onselectstart = function () {
951         return false;
952       };
953     }-*/;
954
955     public static native void enableIESelection() /*-{
956       if ($doc.body.onselectstart != null)
957         $doc.body.onselectstart = null;
958     }-*/;
959
960     /**
961      * @return the absolute path of the API root URL
962      */
963     public String getApiPath() {
964         Configuration conf = (Configuration) GWT.create(Configuration.class);
965         return conf.apiPath();
966     }
967
968     /**
969      * History support for folder navigation
970      * adds a new browser history entry
971      *
972      * @param key
973      */
974     public void updateHistory(String key) {
975 //              Replace any whitespace of the initial string to "+"
976 //              String result = key.replaceAll("\\s","+");
977 //              Add a new browser history entry.
978 //              History.newItem(result);
979         History.newItem(key);
980     }
981
982     public void deleteFolder(final Folder folder, final Command callback) {
983         final PleaseWaitPopup pwp = new PleaseWaitPopup();
984         pwp.center();
985         String path = "/" + folder.getContainer() + "/" + folder.getPrefix() + "?delimiter=/" + "&t=" + System.currentTimeMillis();
986         DeleteRequest deleteFolder = new DeleteRequest(getApiPath(), folder.getOwnerID(), path) {
987
988             @Override
989             protected void onUnauthorized(Response response) {
990                 pwp.hide();
991                 sessionExpired();
992             }
993
994             @Override
995             public void onSuccess(Resource result) {
996                 updateFolder(folder.getParent(), true, new Command() {
997
998                     @Override
999                     public void execute() {
1000                         folderTreeSelectionModel.setSelected(folder.getParent(), true);
1001                         updateStatistics();
1002                         if(callback != null) {
1003                             callback.execute();
1004                         }
1005                         pwp.hide();
1006                     }
1007                 }, true);
1008             }
1009
1010             @Override
1011             public void onError(Throwable t) {
1012                 GWT.log("", t);
1013                 setError(t);
1014                 if(t instanceof RestException) {
1015                     if(((RestException) t).getHttpStatusCode() != Response.SC_NOT_FOUND) {
1016                         displayError("Unable to delete folder: " + ((RestException) t).getHttpStatusText());
1017                     }
1018                     else {
1019                         onSuccess(null);
1020                     }
1021                 }
1022                 else {
1023                     displayError("System error unable to delete folder: " + t.getMessage());
1024                 }
1025                 pwp.hide();
1026             }
1027         };
1028         deleteFolder.setHeader("X-Auth-Token", getUserToken());
1029         Scheduler.get().scheduleDeferred(deleteFolder);
1030     }
1031
1032     public FolderTreeView getFolderTreeView() {
1033         return folderTreeView;
1034     }
1035
1036     public void copyFiles(final Iterator<File> iter, final String targetUsername, final String targetUri, final Command callback) {
1037         if(iter.hasNext()) {
1038             File file = iter.next();
1039             String path = targetUri + "/" + file.getName();
1040             PutRequest copyFile = new PutRequest(getApiPath(), targetUsername, path) {
1041                 @Override
1042                 public void onSuccess(Resource result) {
1043                     copyFiles(iter, targetUsername, targetUri, callback);
1044                 }
1045
1046                 @Override
1047                 public void onError(Throwable t) {
1048                     GWT.log("", t);
1049                     setError(t);
1050                     if(t instanceof RestException) {
1051                         displayError("Unable to copy file: " + ((RestException) t).getHttpStatusText());
1052                     }
1053                     else {
1054                         displayError("System error unable to copy file: " + t.getMessage());
1055                     }
1056                 }
1057
1058                 @Override
1059                 protected void onUnauthorized(Response response) {
1060                     sessionExpired();
1061                 }
1062             };
1063             copyFile.setHeader("X-Auth-Token", getUserToken());
1064             copyFile.setHeader("X-Copy-From", URL.encodePathSegment(file.getUri()));
1065             if(!file.getOwnerID().equals(targetUsername)) {
1066                 copyFile.setHeader("X-Source-Account", URL.encodePathSegment(file.getOwnerID()));
1067             }
1068             copyFile.setHeader("Content-Type", file.getContentType());
1069             Scheduler.get().scheduleDeferred(copyFile);
1070         }
1071         else if(callback != null) {
1072             callback.execute();
1073         }
1074     }
1075
1076     public void copyFolder(final Folder f, final String targetUsername, final String targetUri, boolean move, final Command callback) {
1077         String path = targetUri + "?delimiter=/";
1078         PutRequest copyFolder = new PutRequest(getApiPath(), targetUsername, path) {
1079             @Override
1080             public void onSuccess(Resource result) {
1081                 if(callback != null) {
1082                     callback.execute();
1083                 }
1084             }
1085
1086             @Override
1087             public void onError(Throwable t) {
1088                 GWT.log("", t);
1089                 setError(t);
1090                 if(t instanceof RestException) {
1091                     displayError("Unable to copy folder: " + ((RestException) t).getHttpStatusText());
1092                 }
1093                 else {
1094                     displayError("System error copying folder: " + t.getMessage());
1095                 }
1096             }
1097
1098             @Override
1099             protected void onUnauthorized(Response response) {
1100                 sessionExpired();
1101             }
1102         };
1103         copyFolder.setHeader("X-Auth-Token", getUserToken());
1104         copyFolder.setHeader("Accept", "*/*");
1105         copyFolder.setHeader("Content-Length", "0");
1106         copyFolder.setHeader("Content-Type", "application/directory");
1107         if(!f.getOwnerID().equals(targetUsername)) {
1108             copyFolder.setHeader("X-Source-Account", f.getOwnerID());
1109         }
1110         if(move) {
1111             copyFolder.setHeader("X-Move-From", URL.encodePathSegment(f.getUri()));
1112         }
1113         else {
1114             copyFolder.setHeader("X-Copy-From", URL.encodePathSegment(f.getUri()));
1115         }
1116         Scheduler.get().scheduleDeferred(copyFolder);
1117     }
1118
1119     public void addSelectionModel(@SuppressWarnings("rawtypes") SingleSelectionModel model) {
1120         selectionModels.add(model);
1121     }
1122
1123     public OtherSharedTreeView getOtherSharedTreeView() {
1124         return otherSharedTreeView;
1125     }
1126
1127     public void updateTrash(boolean showFiles, Command callback) {
1128         updateFolder(trash, showFiles, callback, true);
1129     }
1130
1131     public void updateGroupsNode() {
1132         groupTreeView.updateGroupNode(null);
1133     }
1134
1135     public Group addGroup(String groupname) {
1136         Group newGroup = new Group(groupname);
1137         account.addGroup(newGroup);
1138         groupTreeView.updateGroupNode(null);
1139         return newGroup;
1140     }
1141
1142     public void removeGroup(Group group) {
1143         account.removeGroup(group);
1144         updateGroupsNode();
1145     }
1146
1147     public TreeView getSelectedTree() {
1148         return selectedTree;
1149     }
1150
1151     public void setSelectedTree(TreeView selected) {
1152         selectedTree = selected;
1153     }
1154
1155     public Folder getSelection() {
1156         if(selectedTree != null) {
1157             return selectedTree.getSelection();
1158         }
1159         return null;
1160     }
1161
1162     public void showFolderStatistics(int folderFileCount) {
1163         numOfFiles.setHTML(String.valueOf(folderFileCount));
1164     }
1165
1166     public GroupTreeView getGroupTreeView() {
1167         return groupTreeView;
1168     }
1169
1170     public void sessionExpired() {
1171         new SessionExpiredDialog(this).center();
1172     }
1173
1174     public void updateRootFolder(Command callback) {
1175         updateFolder(account.getPithos(), false, callback, true);
1176     }
1177
1178     void createMySharedTree() {
1179         mysharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1180         mysharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1181             @Override
1182             public void onSelectionChange(SelectionChangeEvent event) {
1183                 if(mysharedTreeSelectionModel.getSelectedObject() != null) {
1184                     deselectOthers(mysharedTreeView, mysharedTreeSelectionModel);
1185                     upload.setEnabled(false);
1186                     disableUploadArea();
1187                     updateSharedFolder(mysharedTreeSelectionModel.getSelectedObject(), true);
1188                     showRelevantToolbarButtons();
1189                 }
1190                 else {
1191                     if(getSelectedTree().equals(mysharedTreeView)) {
1192                         setSelectedTree(null);
1193                     }
1194                     if(getSelectedTree() == null) {
1195                         showRelevantToolbarButtons();
1196                     }
1197                 }
1198             }
1199         });
1200         selectionModels.add(mysharedTreeSelectionModel);
1201         mysharedTreeViewModel = new MysharedTreeViewModel(Pithos.this, mysharedTreeSelectionModel);
1202         mysharedTreeViewModel.initialize(new Command() {
1203
1204             @Override
1205             public void execute() {
1206                 mysharedTreeView = new MysharedTreeView(mysharedTreeViewModel);
1207                 trees.insert(mysharedTreeView, 2);
1208                 treeViews.add(mysharedTreeView);
1209                 createOtherSharedTree();
1210             }
1211         });
1212     }
1213
1214     void createOtherSharedTree() {
1215         otherSharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1216         otherSharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1217             @Override
1218             public void onSelectionChange(SelectionChangeEvent event) {
1219                 if(otherSharedTreeSelectionModel.getSelectedObject() != null) {
1220                     deselectOthers(otherSharedTreeView, otherSharedTreeSelectionModel);
1221                     applyPermissions(otherSharedTreeSelectionModel.getSelectedObject());
1222                     updateOtherSharedFolder(otherSharedTreeSelectionModel.getSelectedObject(), true, null);
1223                     showRelevantToolbarButtons();
1224                 }
1225                 else {
1226                     if(getSelectedTree().equals(otherSharedTreeView)) {
1227                         setSelectedTree(null);
1228                     }
1229                     if(getSelectedTree() == null) {
1230                         showRelevantToolbarButtons();
1231                     }
1232                 }
1233             }
1234         });
1235         selectionModels.add(otherSharedTreeSelectionModel);
1236         otherSharedTreeViewModel = new OtherSharedTreeViewModel(Pithos.this, otherSharedTreeSelectionModel);
1237         otherSharedTreeViewModel.initialize(new Command() {
1238
1239             @Override
1240             public void execute() {
1241                 otherSharedTreeView = new OtherSharedTreeView(otherSharedTreeViewModel);
1242                 trees.insert(otherSharedTreeView, 1);
1243                 treeViews.add(otherSharedTreeView);
1244                 scheduleResfresh();
1245             }
1246         });
1247     }
1248
1249     public native void log1(String message)/*-{
1250       $wnd.console.log(message);
1251     }-*/;
1252
1253     public String getErrorData() {
1254         if(error != null) {
1255             return error.toString();
1256         }
1257         return "";
1258     }
1259
1260     public void setError(Throwable t) {
1261         error = t;
1262     }
1263
1264     public void showRelevantToolbarButtons() {
1265         toolbar.showRelevantButtons();
1266     }
1267
1268     public FileUploadDialog getFileUploadDialog() {
1269         if(fileUploadDialog == null) {
1270             fileUploadDialog = new FileUploadDialog(this);
1271         }
1272         return fileUploadDialog;
1273     }
1274
1275     public void hideUploadIndicator() {
1276         upload.removeStyleName("pithos-uploadButton-loading");
1277         upload.setTitle("");
1278     }
1279
1280     public void showUploadIndicator() {
1281         upload.addStyleName("pithos-uploadButton-loading");
1282         upload.setTitle("Upload in progress. Click for details.");
1283     }
1284
1285     public void scheduleFolderHeadCommand(final Folder folder, final Command callback) {
1286         if(folder == null) {
1287             if(callback != null) {
1288                 callback.execute();
1289             }
1290         }
1291         else {
1292             HeadRequest<Folder> headFolder = new HeadRequest<Folder>(Folder.class, getApiPath(), folder.getOwnerID(), folder.getUri(), folder) {
1293
1294                 @Override
1295                 public void onSuccess(Folder _result) {
1296                     if(callback != null) {
1297                         callback.execute();
1298                     }
1299                 }
1300
1301                 @Override
1302                 public void onError(Throwable t) {
1303                     if(t instanceof RestException) {
1304                         if(((RestException) t).getHttpStatusCode() == Response.SC_NOT_FOUND) {
1305                             final String path = folder.getUri();
1306                             PutRequest newFolder = new PutRequest(getApiPath(), folder.getOwnerID(), path) {
1307                                 @Override
1308                                 public void onSuccess(Resource _result) {
1309                                     scheduleFolderHeadCommand(folder, callback);
1310                                 }
1311
1312                                 @Override
1313                                 public void onError(Throwable _t) {
1314                                     GWT.log("", _t);
1315                                     setError(_t);
1316                                     if(_t instanceof RestException) {
1317                                         displayError("Unable to create folder: " + ((RestException) _t).getHttpStatusText());
1318                                     }
1319                                     else {
1320                                         displayError("System error creating folder: " + _t.getMessage());
1321                                     }
1322                                 }
1323
1324                                 @Override
1325                                 protected void onUnauthorized(Response response) {
1326                                     sessionExpired();
1327                                 }
1328                             };
1329                             newFolder.setHeader("X-Auth-Token", getUserToken());
1330                             newFolder.setHeader("Content-Type", "application/folder");
1331                             newFolder.setHeader("Accept", "*/*");
1332                             newFolder.setHeader("Content-Length", "0");
1333                             Scheduler.get().scheduleDeferred(newFolder);
1334                         }
1335                         else if(((RestException) t).getHttpStatusCode() == Response.SC_FORBIDDEN) {
1336                             onSuccess(folder);
1337                         }
1338                         else {
1339                             displayError("Error heading folder: " + ((RestException) t).getHttpStatusText());
1340                         }
1341                     }
1342                     else {
1343                         displayError("System error heading folder: " + t.getMessage());
1344                     }
1345
1346                     GWT.log("Error heading folder", t);
1347                     setError(t);
1348                 }
1349
1350                 @Override
1351                 protected void onUnauthorized(Response response) {
1352                     sessionExpired();
1353                 }
1354             };
1355             headFolder.setHeader("X-Auth-Token", getUserToken());
1356             Scheduler.get().scheduleDeferred(headFolder);
1357         }
1358     }
1359
1360     public void scheduleFileHeadCommand(File f, final Command callback) {
1361         HeadRequest<File> headFile = new HeadRequest<File>(File.class, getApiPath(), f.getOwnerID(), f.getUri(), f) {
1362
1363             @Override
1364             public void onSuccess(File _result) {
1365                 if(callback != null) {
1366                     callback.execute();
1367                 }
1368             }
1369
1370             @Override
1371             public void onError(Throwable t) {
1372                 GWT.log("Error heading file", t);
1373                 setError(t);
1374                 if(t instanceof RestException) {
1375                     displayError("Error heading file: " + ((RestException) t).getHttpStatusText());
1376                 }
1377                 else {
1378                     displayError("System error heading file: " + t.getMessage());
1379                 }
1380             }
1381
1382             @Override
1383             protected void onUnauthorized(Response response) {
1384                 sessionExpired();
1385             }
1386         };
1387         headFile.setHeader("X-Auth-Token", getUserToken());
1388         Scheduler.get().scheduleDeferred(headFile);
1389     }
1390
1391     public boolean isMySharedSelected() {
1392         return getSelectedTree().equals(getMySharedTreeView());
1393     }
1394
1395     private Folder getUploadFolder() {
1396         if(folderTreeView.equals(getSelectedTree()) || otherSharedTreeView.equals(getSelectedTree())) {
1397             return getSelection();
1398         }
1399         return null;
1400     }
1401
1402     private void updateUploadFolder() {
1403         updateUploadFolder(null);
1404     }
1405
1406     private void updateUploadFolder(final JsArrayString urls) {
1407         if(folderTreeView.equals(getSelectedTree()) || otherSharedTreeView.equals(getSelectedTree())) {
1408             Folder f = getSelection();
1409             if(getSelectedTree().equals(getFolderTreeView())) {
1410                 updateFolder(f, true, new Command() {
1411
1412                     @Override
1413                     public void execute() {
1414                         updateStatistics();
1415                         if(urls != null) {
1416                             selectUploadedFiles(urls);
1417                         }
1418                     }
1419                 }, false);
1420             }
1421             else {
1422                 updateOtherSharedFolder(f, true, null);
1423             }
1424         }
1425     }
1426
1427     public native void disableUploadArea() /*-{
1428       var uploader = $wnd.$("#uploader").pluploadQueue();
1429       var dropElm = $wnd.document.getElementById('rightPanel');
1430       $wnd.plupload.removeAllEvents(dropElm, uploader.id);
1431     }-*/;
1432
1433     public native void enableUploadArea() /*-{
1434       var uploader = $wnd.$("#uploader").pluploadQueue();
1435       var dropElm = $wnd.document.getElementById('rightPanel');
1436       $wnd.plupload.removeAllEvents(dropElm, uploader.id);
1437       if (uploader.runtime == 'html5') {
1438         uploader.settings.drop_element = 'rightPanel';
1439         uploader.trigger('PostInit');
1440       }
1441     }-*/;
1442
1443     public void showUploadAlert(int nOfFiles) {
1444         if(uploadAlert == null) {
1445             uploadAlert = new UploadAlert(this, nOfFiles);
1446         }
1447         if(!uploadAlert.isShowing()) {
1448             uploadAlert.setPopupPositionAndShow(new PopupPanel.PositionCallback() {
1449
1450                 @Override
1451                 public void setPosition(int offsetWidth, int offsetHeight) {
1452                     uploadAlert.setPopupPosition((Window.getClientWidth() - offsetWidth) / 2, statusPanel.getAbsoluteTop() - offsetHeight);
1453                 }
1454             });
1455         }
1456         uploadAlert.setNumOfFiles(nOfFiles);
1457     }
1458
1459     public void hideUploadAlert() {
1460         if(uploadAlert != null && uploadAlert.isShowing()) {
1461             uploadAlert.hide();
1462         }
1463     }
1464
1465     public void selectUploadedFiles(JsArrayString urls) {
1466         List<String> selectedUrls = new ArrayList<String>();
1467         for(int i = 0; i < urls.length(); i++) {
1468             selectedUrls.add(urls.get(i));
1469         }
1470         fileList.selectByUrl(selectedUrls);
1471     }
1472
1473     public void emptyContainer(final Folder container) {
1474         String path = "/" + container.getName() + "?delimiter=/";
1475         DeleteRequest delete = new DeleteRequest(getApiPath(), getUserID(), path) {
1476
1477             @Override
1478             protected void onUnauthorized(Response response) {
1479                 sessionExpired();
1480             }
1481
1482             @Override
1483             public void onSuccess(Resource result) {
1484                 updateFolder(container, true, null, true);
1485             }
1486
1487             @Override
1488             public void onError(Throwable t) {
1489                 GWT.log("Error deleting trash", t);
1490                 setError(t);
1491                 if(t instanceof RestException) {
1492                     displayError("Error deleting trash: " + ((RestException) t).getHttpStatusText());
1493                 }
1494                 else {
1495                     displayError("System error deleting trash: " + t.getMessage());
1496                 }
1497             }
1498         };
1499         delete.setHeader("X-Auth-Token", getUserToken());
1500         Scheduler.get().scheduleDeferred(delete);
1501     }
1502 }