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