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