Removed context menu from my hared tree
[pithos] / web_client / src / gr / grnet / pithos / web / client / Pithos.java
1 /*
2  * Copyright 2011 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.grouptree.User;
48 import gr.grnet.pithos.web.client.mysharedtree.MysharedTreeView;
49 import gr.grnet.pithos.web.client.mysharedtree.MysharedTreeViewModel;
50 import gr.grnet.pithos.web.client.othersharedtree.OtherSharedTreeView;
51 import gr.grnet.pithos.web.client.othersharedtree.OtherSharedTreeViewModel;
52 import gr.grnet.pithos.web.client.rest.DeleteRequest;
53 import gr.grnet.pithos.web.client.rest.GetRequest;
54 import gr.grnet.pithos.web.client.rest.PutRequest;
55 import gr.grnet.pithos.web.client.rest.RestException;
56 import gr.grnet.pithos.web.client.tagtree.Tag;
57 import gr.grnet.pithos.web.client.tagtree.TagTreeView;
58 import gr.grnet.pithos.web.client.tagtree.TagTreeViewModel;
59
60 import java.util.ArrayList;
61 import java.util.HashMap;
62 import java.util.Iterator;
63 import java.util.List;
64 import java.util.Set;
65
66 import com.google.gwt.core.client.EntryPoint;
67 import com.google.gwt.core.client.GWT;
68 import com.google.gwt.core.client.Scheduler;
69 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
70 import com.google.gwt.event.dom.client.ClickEvent;
71 import com.google.gwt.event.dom.client.ClickHandler;
72 import com.google.gwt.event.logical.shared.ResizeEvent;
73 import com.google.gwt.event.logical.shared.ResizeHandler;
74 import com.google.gwt.http.client.Request;
75 import com.google.gwt.http.client.RequestBuilder;
76 import com.google.gwt.http.client.RequestCallback;
77 import com.google.gwt.http.client.RequestException;
78 import com.google.gwt.http.client.Response;
79 import com.google.gwt.json.client.JSONArray;
80 import com.google.gwt.json.client.JSONObject;
81 import com.google.gwt.json.client.JSONParser;
82 import com.google.gwt.json.client.JSONString;
83 import com.google.gwt.json.client.JSONValue;
84 import com.google.gwt.resources.client.ImageResource;
85 import com.google.gwt.user.client.Command;
86 import com.google.gwt.user.client.Cookies;
87 import com.google.gwt.user.client.Event;
88 import com.google.gwt.user.client.History;
89 import com.google.gwt.user.client.Window;
90 import com.google.gwt.user.client.ui.AbstractImagePrototype;
91 import com.google.gwt.user.client.ui.Button;
92 import com.google.gwt.user.client.ui.HTML;
93 import com.google.gwt.user.client.ui.HasHorizontalAlignment;
94 import com.google.gwt.user.client.ui.HasVerticalAlignment;
95 import com.google.gwt.user.client.ui.HorizontalPanel;
96 import com.google.gwt.user.client.ui.HorizontalSplitPanel;
97 import com.google.gwt.user.client.ui.Image;
98 import com.google.gwt.user.client.ui.PushButton;
99 import com.google.gwt.user.client.ui.RootPanel;
100 import com.google.gwt.user.client.ui.VerticalPanel;
101 import com.google.gwt.view.client.SelectionChangeEvent;
102 import com.google.gwt.view.client.SelectionChangeEvent.Handler;
103 import com.google.gwt.view.client.SingleSelectionModel;
104
105 /**
106  * Entry point classes define <code>onModuleLoad()</code>.
107  */
108 public class Pithos implements EntryPoint, ResizeHandler {
109
110         public static final String HOME_CONTAINER = "pithos";
111
112         public static final String TRASH_CONTAINER = "trash";
113         
114         /**
115          * Instantiate an application-level image bundle. This object will provide
116          * programmatic access to all the images needed by widgets.
117          */
118         private static Images images = (Images) GWT.create(Images.class);
119
120     public String getUsername() {
121         return username;
122     }
123
124     public void setAccount(AccountResource acct) {
125         account = acct;
126     }
127
128     public AccountResource getAccount() {
129         return account;
130     }
131
132     public void updateFolder(Folder f, boolean showfiles, Command callback) {
133         folderTreeView.updateFolder(f, showfiles, callback);
134     }
135
136     public void updateGroupNode(Group group) {
137         groupTreeView.updateGroupNode(group);
138     }
139
140     public void updateSharedFolder(Folder f, boolean showfiles) {
141         mysharedTreeView.updateFolder(f, showfiles);
142     }
143     
144     public void updateOtherSharedFolder(Folder f, boolean showfiles) {
145         otherSharedTreeView.updateFolder(f, showfiles);
146     }
147
148     public void updateTag(Tag t) {
149         tagTreeView.updateTag(t);
150     }
151
152     public void updateTags() {
153         tagTreeViewModel.initialize(getAllTags());
154     }
155
156     public List<Tag> getAllTags() {
157         List<Tag> tagList = new ArrayList<Tag>();
158         for (Folder f : account.getContainers()) {
159             for (String t : f.getTags()) {
160                 tagList.add(new Tag(t));
161             }
162         }
163         return tagList;
164     }
165
166     public MysharedTreeView getMySharedTreeView() {
167         return mysharedTreeView;
168     }
169
170     /**
171          * An aggregate image bundle that pulls together all the images for this
172          * application into a single bundle.
173          */
174         public interface Images extends TopPanel.Images, FileList.Images {
175
176                 @Source("gr/grnet/pithos/resources/document.png")
177                 ImageResource folders();
178
179                 @Source("gr/grnet/pithos/resources/edit_group_22.png")
180                 ImageResource groups();
181
182                 @Source("gr/grnet/pithos/resources/search.png")
183                 ImageResource search();
184         }
185
186         /**
187          * The Application Clipboard implementation;
188          */
189         private Clipboard clipboard = new Clipboard();
190
191         /**
192          * The top panel that contains the menu bar.
193          */
194         private TopPanel topPanel;
195
196         /**
197          * The panel that contains the various system messages.
198          */
199         private MessagePanel messagePanel = new MessagePanel(Pithos.images);
200
201         /**
202          * The bottom panel that contains the status bar.
203          */
204         private StatusPanel statusPanel = null;
205
206         /**
207          * The file list widget.
208          */
209         private FileList fileList;
210
211         /**
212          * The tab panel that occupies the right side of the screen.
213          */
214         private VerticalPanel inner = new VerticalPanel();
215
216
217         /**
218          * The split panel that will contain the left and right panels.
219          */
220         private HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
221
222         /**
223          * The currently selected item in the application, for use by the Edit menu
224          * commands. Potential types are Folder, File, User and Group.
225          */
226         private Object currentSelection;
227
228
229         /**
230          * The WebDAV password of the current user
231          */
232         private String webDAVPassword;
233
234         public HashMap<String, String> userFullNameMap = new HashMap<String, String>();
235
236     private String username = null;
237
238     /**
239      * The authentication token of the current user.
240      */
241     private String token;
242
243     SingleSelectionModel<Folder> folderTreeSelectionModel;
244     FolderTreeViewModel folderTreeViewModel;
245     FolderTreeView folderTreeView;
246
247     SingleSelectionModel<Folder> mysharedTreeSelectionModel;
248     private MysharedTreeViewModel mysharedTreeViewModel;
249     MysharedTreeView mysharedTreeView;
250
251     protected SingleSelectionModel<Folder> otherSharedTreeSelectionModel;
252     private OtherSharedTreeViewModel otherSharedTreeViewModel;
253     OtherSharedTreeView otherSharedTreeView;
254
255     protected SingleSelectionModel<Tag> tagTreeSelectionModel;
256     private TagTreeViewModel tagTreeViewModel;
257     private TagTreeView tagTreeView;
258
259     private GroupTreeViewModel groupTreeViewModel;
260     private GroupTreeView groupTreeView;
261
262     private TreeView selectedTree;
263     protected AccountResource account;
264     
265     private Folder trash;
266
267     @SuppressWarnings("rawtypes")
268         private List<SingleSelectionModel> selectionModels = new ArrayList<SingleSelectionModel>();
269
270         @Override
271         public void onModuleLoad() {
272                 if (parseUserCredentials())
273             initialize();
274         }
275
276     private void initialize() {
277         VerticalPanel outer = new VerticalPanel();
278         outer.setWidth("100%");
279
280         topPanel = new TopPanel(this, Pithos.images);
281         topPanel.setWidth("100%");
282         outer.add(topPanel);
283
284         messagePanel.setWidth("100%");
285         messagePanel.setVisible(false);
286         outer.add(messagePanel);
287         outer.setCellHorizontalAlignment(messagePanel, HasHorizontalAlignment.ALIGN_CENTER);
288
289
290         // Inner contains the various lists.
291         inner.sinkEvents(Event.ONCONTEXTMENU);
292         inner.setWidth("100%");
293
294         HorizontalPanel rightside = new HorizontalPanel();
295         rightside.addStyleName("pithos-rightSide");
296         rightside.setSpacing(5);
297
298         PushButton parentButton = new PushButton(new Image(images.asc()), new ClickHandler() {
299             @Override
300             public void onClick(@SuppressWarnings("unused") ClickEvent event) {
301
302             }
303         });
304         parentButton.addStyleName("pithos-parentButton");
305         rightside.add(parentButton);
306
307         HTML folderStatistics = new HTML("5 Files (size: 1.1GB)");
308         folderStatistics.addStyleName("pithos-folderStatistics");
309         rightside.add(folderStatistics);
310         inner.add(rightside);
311         inner.setCellHorizontalAlignment(rightside, HasHorizontalAlignment.ALIGN_RIGHT);
312         inner.setCellVerticalAlignment(rightside, HasVerticalAlignment.ALIGN_MIDDLE);
313         inner.setCellHeight(rightside, "60px");
314
315         folderTreeSelectionModel = new SingleSelectionModel<Folder>();
316         folderTreeSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
317             @Override
318             public void onSelectionChange(@SuppressWarnings("unused") SelectionChangeEvent event) {
319                 if (folderTreeSelectionModel.getSelectedObject() != null) {
320                     deselectOthers(folderTreeView, folderTreeSelectionModel);
321                     Folder f = folderTreeSelectionModel.getSelectedObject();
322                     updateFolder(f, true, null);
323                 }
324             }
325         });
326         selectionModels.add(folderTreeSelectionModel);
327
328         folderTreeViewModel = new FolderTreeViewModel(this, folderTreeSelectionModel);
329         folderTreeView = new FolderTreeView(folderTreeViewModel);
330
331         fileList = new FileList(this, images, folderTreeView);
332         inner.add(fileList);
333
334         mysharedTreeSelectionModel = new SingleSelectionModel<Folder>();
335         mysharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
336             @Override
337             public void onSelectionChange(@SuppressWarnings("unused") SelectionChangeEvent event) {
338                 if (mysharedTreeSelectionModel.getSelectedObject() != null) {
339                     deselectOthers(mysharedTreeView, mysharedTreeSelectionModel);
340                     updateSharedFolder(mysharedTreeSelectionModel.getSelectedObject(), true);
341                 }
342             }
343         });
344         selectionModels.add(mysharedTreeSelectionModel);
345         mysharedTreeViewModel = new MysharedTreeViewModel(this, mysharedTreeSelectionModel);
346         mysharedTreeView = new MysharedTreeView(mysharedTreeViewModel);
347
348         otherSharedTreeSelectionModel = new SingleSelectionModel<Folder>();
349         otherSharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
350             @Override
351             public void onSelectionChange(@SuppressWarnings("unused") SelectionChangeEvent event) {
352                 if (otherSharedTreeSelectionModel.getSelectedObject() != null) {
353                     deselectOthers(otherSharedTreeView, otherSharedTreeSelectionModel);
354                     updateOtherSharedFolder(otherSharedTreeSelectionModel.getSelectedObject(), true);
355                 }
356             }
357         });
358         selectionModels.add(otherSharedTreeSelectionModel);
359         otherSharedTreeViewModel = new OtherSharedTreeViewModel(this, otherSharedTreeSelectionModel);
360         otherSharedTreeView = new OtherSharedTreeView(otherSharedTreeViewModel);
361
362         groupTreeViewModel = new GroupTreeViewModel(this);
363         groupTreeView = new GroupTreeView(groupTreeViewModel);
364
365         VerticalPanel trees = new VerticalPanel();
366
367         Button upload = new Button("Upload File", new ClickHandler() {
368             @Override
369             public void onClick(@SuppressWarnings("unused") ClickEvent event) {
370                 new UploadFileCommand(Pithos.this, null, folderTreeView.getSelection()).execute();
371             }
372         });
373         upload.addStyleName("pithos-uploadButton");
374         trees.add(upload);
375         
376         HorizontalPanel treeHeader = new HorizontalPanel();
377         treeHeader.addStyleName("pithos-treeHeader");
378         treeHeader.add(new HTML("Total Files: 6 | Used: 2.1 of 50 GB (4.2%)"));
379         trees.add(treeHeader);
380
381         trees.add(folderTreeView);
382         trees.add(mysharedTreeView);
383         trees.add(otherSharedTreeView);
384 //        trees.add(tagTreeView);
385         trees.add(groupTreeView);
386         // Add the left and right panels to the split panel.
387         splitPanel.setLeftWidget(trees);
388         splitPanel.setRightWidget(inner);
389         splitPanel.setSplitPosition("25%");
390         splitPanel.setSize("100%", "100%");
391         splitPanel.addStyleName("pithos-splitPanel");
392         outer.add(splitPanel);
393
394         statusPanel = new StatusPanel();
395         outer.add(statusPanel);
396
397
398         // Hook the window resize event, so that we can adjust the UI.
399         Window.addResizeHandler(this);
400         // Clear out the window's built-in margin, because we want to take
401         // advantage of the entire client area.
402         Window.setMargin("0px");
403         // Finally, add the outer panel to the RootPanel, so that it will be
404         // displayed.
405         RootPanel.get().add(outer);
406         // Call the window resized handler to get the initial sizes setup. Doing
407         // this in a deferred command causes it to occur after all widgets'
408         // sizes have been computed by the browser.
409         Scheduler.get().scheduleDeferred(new ScheduledCommand() {
410
411             @Override
412             public void execute() {
413                 onWindowResized(Window.getClientHeight());
414             }
415         });
416
417         Scheduler.get().scheduleDeferred(new ScheduledCommand() {
418             @Override
419             public void execute() {
420                 fetchAccount();
421             }
422         });
423     }
424
425     @SuppressWarnings({ "rawtypes", "unchecked" })
426         public void deselectOthers(TreeView _selectedTree, SingleSelectionModel model) {
427         selectedTree = _selectedTree;
428         for (SingleSelectionModel s : selectionModels)
429             if (!s.equals(model))
430                 s.setSelected(s.getSelectedObject(), false);
431     }
432
433     public void showFiles(Folder f) {
434         Set<File> files = f.getFiles();
435         showFiles(files);
436     }
437
438     public void showFiles(Set<File> files) {
439         //Iterator<File> iter = files.iterator();
440         //fetchFile(iter, files);
441         fileList.setFiles(new ArrayList<File>(files));
442     }
443
444     protected void fetchFile(final Iterator<File> iter, final Set<File> files) {
445         if (iter.hasNext()) {
446             File file = iter.next();
447             String path = file.getUri() + "?format=json";
448             GetRequest<File> getFile = new GetRequest<File>(File.class, getApiPath(), username, path, file) {
449                 @Override
450                 public void onSuccess(@SuppressWarnings("unused") File _result) {
451                     fetchFile(iter, files);
452                 }
453
454                 @Override
455                 public void onError(Throwable t) {
456                     GWT.log("Error getting file", t);
457                     if (t instanceof RestException)
458                         displayError("Error getting file: " + ((RestException) t).getHttpStatusText());
459                     else
460                         displayError("System error fetching file: " + t.getMessage());
461                 }
462             };
463             getFile.setHeader("X-Auth-Token", "0000");
464             Scheduler.get().scheduleDeferred(getFile);
465         }
466         else
467             fileList.setFiles(new ArrayList<File>(files));
468     }
469
470     /**
471          * Parse and store the user credentials to the appropriate fields.
472          */
473         private boolean parseUserCredentials() {
474         username = Window.Location.getParameter("user");
475         token = Window.Location.getParameter("token");
476         Configuration conf = (Configuration) GWT.create(Configuration.class);
477         if (username == null || username.length() == 0 || token == null || token.length() == 0) {
478             String cookie = conf.authCookie();
479             String auth = Cookies.getCookie(cookie);
480             if (auth == null) {
481                 authenticateUser();
482                 return false;
483             }
484                         String[] authSplit = auth.split("\\" + conf.cookieSeparator(), 2);
485                         if (authSplit.length != 2) {
486                             authenticateUser();
487                             return false;
488                         }
489                         username = authSplit[0];
490                         token = authSplit[1];
491                         return true;
492         }
493                 Cookies.setCookie(conf.authCookie(), username + conf.cookieSeparator() + token);
494                 return true;
495     }
496
497     /**
498          * Redirect the user to the login page for authentication.
499          */
500         protected void authenticateUser() {
501                 Configuration conf = (Configuration) GWT.create(Configuration.class);
502         Window.Location.assign(conf.loginUrl() + "?next=" + Window.Location.getHref());
503         }
504
505         protected void fetchAccount() {
506         String path = "?format=json";
507
508         GetRequest<AccountResource> getAccount = new GetRequest<AccountResource>(AccountResource.class, getApiPath(), username, path) {
509             @Override
510             public void onSuccess(AccountResource _result) {
511                 account = _result;
512                 if (!account.hasHomeContainer())
513                     createHomeContainer(account);
514                 else if (!account.hasTrashContainer())
515                         createTrashContainer();
516                 else {
517                         for (Folder f : account.getContainers())
518                                 if (f.getName().equals(Pithos.TRASH_CONTAINER)) {
519                                         trash = f;
520                                         break;
521                                 }
522                     folderTreeViewModel.initialize(account);
523                     groupTreeViewModel.initialize();
524                 }
525             }
526
527             @Override
528             public void onError(Throwable t) {
529                 GWT.log("Error getting account", t);
530                 if (t instanceof RestException)
531                     displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
532                 else
533                     displayError("System error fetching user data: " + t.getMessage());
534             }
535         };
536         getAccount.setHeader("X-Auth-Token", token);
537         Scheduler.get().scheduleDeferred(getAccount);
538     }
539
540     protected void createHomeContainer(final AccountResource account) {
541         String path = "/" + Pithos.HOME_CONTAINER;
542         PutRequest createPithos = new PutRequest(getApiPath(), getUsername(), path) {
543             @Override
544             public void onSuccess(@SuppressWarnings("unused") Resource result) {
545                 if (!account.hasTrashContainer())
546                         createTrashContainer();
547                 else
548                         fetchAccount();
549             }
550
551             @Override
552             public void onError(Throwable t) {
553                 GWT.log("Error creating pithos", t);
554                 if (t instanceof RestException)
555                     displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
556                 else
557                     displayError("System error Error creating pithos: " + t.getMessage());
558             }
559         };
560         createPithos.setHeader("X-Auth-Token", getToken());
561         Scheduler.get().scheduleDeferred(createPithos);
562     }
563
564     protected void createTrashContainer() {
565         String path = "/" + Pithos.TRASH_CONTAINER;
566         PutRequest createPithos = new PutRequest(getApiPath(), getUsername(), path) {
567             @Override
568             public void onSuccess(@SuppressWarnings("unused") Resource result) {
569                         fetchAccount();
570             }
571
572             @Override
573             public void onError(Throwable t) {
574                 GWT.log("Error creating pithos", t);
575                 if (t instanceof RestException)
576                     displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
577                 else
578                     displayError("System error Error creating pithos: " + t.getMessage());
579             }
580         };
581         createPithos.setHeader("X-Auth-Token", getToken());
582         Scheduler.get().scheduleDeferred(createPithos);
583     }
584
585     /**
586          * Creates an HTML fragment that places an image & caption together, for use
587          * in a group header.
588          *
589          * @param imageProto an image prototype for an image
590          * @param caption the group caption
591          * @return the header HTML fragment
592          */
593         private String createHeaderHTML(AbstractImagePrototype imageProto, String caption) {
594                 String captionHTML = "<table class='caption' cellpadding='0' " 
595                 + "cellspacing='0'>" + "<tr><td class='lcaption'>" + imageProto.getHTML() 
596                 + "</td><td id =" + caption +" class='rcaption'><b style='white-space:nowrap'>&nbsp;" 
597                 + caption + "</b></td></tr></table>";
598                 return captionHTML;
599         }
600
601         protected void onWindowResized(int height) {
602                 // Adjust the split panel to take up the available room in the window.
603                 int newHeight = height - splitPanel.getAbsoluteTop() - 60;
604                 if (newHeight < 1)
605                         newHeight = 1;
606                 splitPanel.setHeight("" + newHeight);
607                 inner.setHeight("" + newHeight);
608         }
609
610         @Override
611         public void onResize(ResizeEvent event) {
612                 int height = event.getHeight();
613                 onWindowResized(height);
614         }
615
616         /**
617          * Display an error message.
618          *
619          * @param msg the message to display
620          */
621         public void displayError(String msg) {
622                 messagePanel.displayError(msg);
623         }
624
625         /**
626          * Display a warning message.
627          *
628          * @param msg the message to display
629          */
630         public void displayWarning(String msg) {
631                 messagePanel.displayWarning(msg);
632         }
633
634         /**
635          * Display an informational message.
636          *
637          * @param msg the message to display
638          */
639         public void displayInformation(String msg) {
640                 messagePanel.displayInformation(msg);
641         }
642
643         /**
644          * Retrieve the fileList.
645          *
646          * @return the fileList
647          */
648         public FileList getFileList() {
649                 return fileList;
650         }
651
652         /**
653          * Retrieve the topPanel.
654          *
655          * @return the topPanel
656          */
657         TopPanel getTopPanel() {
658                 return topPanel;
659         }
660
661         /**
662          * Retrieve the clipboard.
663          *
664          * @return the clipboard
665          */
666         public Clipboard getClipboard() {
667                 return clipboard;
668         }
669
670         public StatusPanel getStatusPanel() {
671                 return statusPanel;
672         }
673
674         public String getToken() {
675                 return token;
676         }
677
678         public String getWebDAVPassword() {
679                 return webDAVPassword;
680         }
681
682         public static native void preventIESelection() /*-{
683                 $doc.body.onselectstart = function () { return false; };
684         }-*/;
685
686         public static native void enableIESelection() /*-{
687                 if ($doc.body.onselectstart != null)
688                 $doc.body.onselectstart = null;
689         }-*/;
690
691         /**
692          * @return the absolute path of the API root URL
693          */
694         public String getApiPath() {
695                 Configuration conf = (Configuration) GWT.create(Configuration.class);
696                 return conf.apiPath();
697         }
698
699         /**
700          * History support for folder navigation
701          * adds a new browser history entry
702          *
703          * @param key
704          */
705         public void updateHistory(String key){
706 //              Replace any whitespace of the initial string to "+"
707 //              String result = key.replaceAll("\\s","+");
708 //              Add a new browser history entry.
709 //              History.newItem(result);
710                 History.newItem(key);
711         }
712
713     public void deleteFolder(final Folder folder) {
714         String path = getApiPath() + getUsername() + "/" + folder.getContainer() + "?format=json&delimiter=/&prefix=" + folder.getPrefix();
715         RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, path);
716         builder.setHeader("If-Modified-Since", "0");
717         builder.setHeader("X-Auth-Token", getToken());
718         try {
719             builder.sendRequest("", new RequestCallback() {
720                 @Override
721                 public void onResponseReceived(@SuppressWarnings("unused") Request request, Response response) {
722                     if (response.getStatusCode() == Response.SC_OK) {
723                         JSONValue json = JSONParser.parseStrict(response.getText());
724                         JSONArray array = json.isArray();
725                         int i = 0;
726                         if (array != null) {
727                             deleteObject(folder, i, array);
728                         }
729                     }
730                 }
731
732                 @Override
733                 public void onError(@SuppressWarnings("unused") Request request, Throwable exception) {
734                     displayError("System error unable to delete folder: " + exception.getMessage());
735                 }
736             });
737         }
738         catch (RequestException e) {
739         }
740     }
741
742     public void deleteObject(final Folder folder, final int i, final JSONArray array) {
743         if (i < array.size()) {
744             JSONObject o = array.get(i).isObject();
745             if (o != null && !o.containsKey("subdir")) {
746                 JSONString name = o.get("name").isString();
747                 String path = "/" + folder.getContainer() + "/" + name.stringValue();
748                 DeleteRequest delete = new DeleteRequest(getApiPath(), getUsername(), path) {
749                     @Override
750                     public void onSuccess(@SuppressWarnings("unused") Resource result) {
751                         deleteObject(folder, i + 1, array);
752                     }
753
754                     @Override
755                     public void onError(Throwable t) {
756                         GWT.log("", t);
757                         displayError("System error unable to delete folder: " + t.getMessage());
758                     }
759                 };
760                 delete.setHeader("X-Auth-Token", getToken());
761                 Scheduler.get().scheduleDeferred(delete);
762             }
763             else if (o != null) {
764                 String subdir = o.get("subdir").isString().stringValue();
765                 subdir = subdir.substring(0, subdir.length() - 1);
766                 String path = getApiPath() + getUsername() + "/" + folder.getContainer() + "?format=json&delimiter=/&prefix=" + subdir;
767                 RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, path);
768                 builder.setHeader("If-Modified-Since", "0");
769                 builder.setHeader("X-Auth-Token", getToken());
770                 try {
771                     builder.sendRequest("", new RequestCallback() {
772                         @Override
773                         public void onResponseReceived(@SuppressWarnings("unused") Request request, Response response) {
774                             if (response.getStatusCode() == Response.SC_OK) {
775                                 JSONValue json = JSONParser.parseStrict(response.getText());
776                                 JSONArray array2 = json.isArray();
777                                 if (array2 != null) {
778                                     int l = array.size();
779                                     for (int j=0; j<array2.size(); j++) {
780                                         array.set(l++, array2.get(j));
781                                     }
782                                 }
783                                 deleteObject(folder, i + 1, array);
784                             }
785                         }
786
787                         @Override
788                         public void onError(@SuppressWarnings("unused") Request request, Throwable exception) {
789                             displayError("System error unable to delete folder: " + exception.getMessage());
790                         }
791                     });
792                 }
793                 catch (RequestException e) {
794                 }
795             }
796         }
797         else {
798             String path = folder.getUri();
799             DeleteRequest deleteFolder = new DeleteRequest(getApiPath(), getUsername(), path) {
800                 @Override
801                 public void onSuccess(@SuppressWarnings("unused") Resource result) {
802                     updateFolder(folder.getParent(), true, null);
803                 }
804
805                 @Override
806                 public void onError(Throwable t) {
807                     GWT.log("", t);
808                     if (t instanceof RestException) {
809                         if (((RestException) t).getHttpStatusCode() != Response.SC_NOT_FOUND)
810                                 displayError("Unable to delete folder: "+((RestException) t).getHttpStatusText());
811                         else
812                                 onSuccess(null);
813                     }
814                     else
815                         displayError("System error unable to delete folder: " + t.getMessage());
816                 }
817             };
818             deleteFolder.setHeader("X-Auth-Token", getToken());
819             Scheduler.get().scheduleDeferred(deleteFolder);
820         }
821     }
822
823     public FolderTreeView getFolderTreeView() {
824         return folderTreeView;
825     }
826
827     public void copyFiles(final Iterator<File> iter, final String targetUri, final Command callback) {
828         if (iter.hasNext()) {
829             File file = iter.next();
830             String path = targetUri + "/" + file.getName();
831             PutRequest copyFile = new PutRequest(getApiPath(), getUsername(), path) {
832                 @Override
833                 public void onSuccess(@SuppressWarnings("unused") Resource result) {
834                     copyFiles(iter, targetUri, callback);
835                 }
836
837                 @Override
838                 public void onError(Throwable t) {
839                     GWT.log("", t);
840                     if (t instanceof RestException) {
841                         displayError("Unable to copy file: " + ((RestException) t).getHttpStatusText());
842                     }
843                     else
844                         displayError("System error unable to copy file: "+t.getMessage());
845                 }
846             };
847             copyFile.setHeader("X-Auth-Token", getToken());
848             copyFile.setHeader("X-Copy-From", file.getUri());
849             Scheduler.get().scheduleDeferred(copyFile);
850         }
851         else  if (callback != null) {
852             callback.execute();
853         }
854     }
855
856     public void copySubfolders(final Iterator<Folder> iter, final String targetUri, final Command callback) {
857         if (iter.hasNext()) {
858             final Folder f = iter.next();
859             copyFolder(f, targetUri, callback);
860         }
861         else  if (callback != null) {
862             callback.execute();
863         }
864     }
865
866     public void copyFolder(final Folder f, final String targetUri, final Command callback) {
867         String path = targetUri + "/" + f.getName();
868         PutRequest createFolder = new PutRequest(getApiPath(), getUsername(), path) {
869             @Override
870             public void onSuccess(@SuppressWarnings("unused") Resource result) {
871                 Iterator<File> iter = f.getFiles().iterator();
872                 copyFiles(iter, targetUri + "/" + f.getName(), new Command() {
873                     @Override
874                     public void execute() {
875                         Iterator<Folder> iterf = f.getSubfolders().iterator();
876                         copySubfolders(iterf, targetUri + "/" + f.getName(), new Command() {
877                             @Override
878                             public void execute() {
879                                 callback.execute();
880                             }
881                         });
882                     }
883                 });
884             }
885
886             @Override
887             public void onError(Throwable t) {
888                 GWT.log("", t);
889                 if (t instanceof RestException) {
890                     displayError("Unable to create folder:" + ((RestException) t).getHttpStatusText());
891                 }
892                 else
893                     displayError("System error creating folder:" + t.getMessage());
894             }
895         };
896         createFolder.setHeader("X-Auth-Token", getToken());
897         createFolder.setHeader("Accept", "*/*");
898         createFolder.setHeader("Content-Length", "0");
899         createFolder.setHeader("Content-Type", "application/folder");
900         Scheduler.get().scheduleDeferred(createFolder);
901     }
902     
903     public void addSelectionModel(@SuppressWarnings("rawtypes") SingleSelectionModel model) {
904         selectionModels.add(model);
905     }
906
907         public OtherSharedTreeView getOtherSharedTreeView() {
908                 return otherSharedTreeView;
909         }
910
911         public void updateTrash(boolean showFiles, Command callback) {
912                 updateFolder(trash, showFiles, callback);
913         }
914
915         public void updateGroupsNode() {
916                 groupTreeView.updateGroupNode(null);
917         }
918
919         public void addGroup(String groupname) {
920                 Group newGroup = new Group(groupname);
921                 account.addGroup(newGroup);
922                 groupTreeView.updateGroupNode(null);
923         }
924
925         public void removeGroup(Group group) {
926                 account.removeGroup(group);
927                 updateGroupsNode();
928         }
929
930         public TreeView getSelectedTree() {
931                 return selectedTree;
932         }
933         
934         public Folder getSelection() {
935                 return selectedTree.getSelection();
936         }
937 }