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