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