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