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