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