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