Removed some unused methods
[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 import gr.grnet.pithos.web.client.tagtree.Tag;
57
58 import java.util.ArrayList;
59 import java.util.HashMap;
60 import java.util.Iterator;
61 import java.util.List;
62 import java.util.Set;
63
64 import com.google.gwt.core.client.EntryPoint;
65 import com.google.gwt.core.client.GWT;
66 import com.google.gwt.core.client.Scheduler;
67 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
68 import com.google.gwt.event.dom.client.ClickEvent;
69 import com.google.gwt.event.dom.client.ClickHandler;
70 import com.google.gwt.event.logical.shared.ResizeEvent;
71 import com.google.gwt.event.logical.shared.ResizeHandler;
72 import com.google.gwt.http.client.Request;
73 import com.google.gwt.http.client.RequestBuilder;
74 import com.google.gwt.http.client.RequestCallback;
75 import com.google.gwt.http.client.RequestException;
76 import com.google.gwt.http.client.Response;
77 import com.google.gwt.http.client.URL;
78 import com.google.gwt.i18n.client.NumberFormat;
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.resources.client.ImageResource.ImageOptions;
86 import com.google.gwt.user.client.Command;
87 import com.google.gwt.user.client.Cookies;
88 import com.google.gwt.user.client.Event;
89 import com.google.gwt.user.client.History;
90 import com.google.gwt.user.client.Window;
91 import com.google.gwt.user.client.ui.AbstractImagePrototype;
92 import com.google.gwt.user.client.ui.Button;
93 import com.google.gwt.user.client.ui.Composite;
94 import com.google.gwt.user.client.ui.HTML;
95 import com.google.gwt.user.client.ui.HasHorizontalAlignment;
96 import com.google.gwt.user.client.ui.HasVerticalAlignment;
97 import com.google.gwt.user.client.ui.HorizontalPanel;
98 import com.google.gwt.user.client.ui.HorizontalSplitPanel;
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         public static final Configuration config = GWT.create(Configuration.class);
115         
116         /**
117          * Instantiate an application-level image bundle. This object will provide
118          * programmatic access to all the images needed by widgets.
119          */
120         static Images images = (Images) GWT.create(Images.class);
121
122     public String getUsername() {
123         return username;
124     }
125
126     public void setAccount(AccountResource acct) {
127         account = acct;
128     }
129
130     public AccountResource getAccount() {
131         return account;
132     }
133
134     public void updateFolder(Folder f, boolean showfiles, Command callback) {
135         folderTreeView.updateFolder(f, showfiles, callback);
136     }
137
138     public void updateGroupNode(Group group) {
139         groupTreeView.updateGroupNode(group);
140     }
141
142     public void updateMySharedRoot() {
143         mysharedTreeView.updateRoot();
144     }
145     
146     public void updateSharedFolder(Folder f, boolean showfiles) {
147         mysharedTreeView.updateFolder(f, showfiles);
148     }
149     
150     public void updateOtherSharedFolder(Folder f, boolean showfiles) {
151         otherSharedTreeView.updateFolder(f, showfiles);
152     }
153
154     public MysharedTreeView getMySharedTreeView() {
155         return mysharedTreeView;
156     }
157
158     /**
159          * An aggregate image bundle that pulls together all the images for this
160          * application into a single bundle.
161          */
162         public interface Images extends TopPanel.Images, FileList.Images, ToolsMenu.Images {
163
164                 @Source("gr/grnet/pithos/resources/document.png")
165                 ImageResource folders();
166
167                 @Source("gr/grnet/pithos/resources/advancedsettings.png")
168                 @ImageOptions(width=32, height=32)
169                 ImageResource tools();
170         }
171
172         private Throwable error;
173         
174         /**
175          * The Application Clipboard implementation;
176          */
177         private Clipboard clipboard = new Clipboard();
178
179         /**
180          * The top panel that contains the menu bar.
181          */
182         private TopPanel topPanel;
183
184         /**
185          * The panel that contains the various system messages.
186          */
187         private MessagePanel messagePanel = new MessagePanel(this, Pithos.images);
188
189         /**
190          * The bottom panel that contains the status bar.
191          */
192         private StatusPanel statusPanel = null;
193
194         /**
195          * The file list widget.
196          */
197         private FileList fileList;
198
199         /**
200          * The tab panel that occupies the right side of the screen.
201          */
202         private VerticalPanel inner = new VerticalPanel();
203
204
205         /**
206          * The split panel that will contain the left and right panels.
207          */
208         private HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
209
210         /**
211          * The currently selected item in the application, for use by the Edit menu
212          * commands. Potential types are Folder, File, User and Group.
213          */
214         private Object currentSelection;
215
216         public HashMap<String, String> userFullNameMap = new HashMap<String, String>();
217
218     private String username = null;
219
220     /**
221      * The authentication token of the current user.
222      */
223     private String token;
224
225     VerticalPanel trees;
226     
227     SingleSelectionModel<Folder> folderTreeSelectionModel;
228     FolderTreeViewModel folderTreeViewModel;
229     FolderTreeView folderTreeView;
230
231     SingleSelectionModel<Folder> mysharedTreeSelectionModel;
232     MysharedTreeViewModel mysharedTreeViewModel;
233     MysharedTreeView mysharedTreeView = null;;
234
235     protected SingleSelectionModel<Folder> otherSharedTreeSelectionModel;
236     OtherSharedTreeViewModel otherSharedTreeViewModel;
237     OtherSharedTreeView otherSharedTreeView = null;
238
239     GroupTreeViewModel groupTreeViewModel;
240     private GroupTreeView groupTreeView;
241
242     TreeView selectedTree;
243     protected AccountResource account;
244     
245     Folder trash;
246     
247     List<Composite> treeViews = new ArrayList<Composite>();
248
249     @SuppressWarnings("rawtypes") List<SingleSelectionModel> selectionModels = new ArrayList<SingleSelectionModel>();
250     
251     Button upload;
252     
253     private HTML usedBytes;
254     
255     private HTML totalBytes;
256     
257     private HTML usedPercent;
258     
259     private HTML numOfFiles;
260     
261     private Toolbar toolbar;
262     
263     private FileUploadDialog fileUploadDialog;
264     
265         @Override
266         public void onModuleLoad() {
267                 if (parseUserCredentials())
268             initialize();
269         }
270
271     private void initialize() {
272         boolean bareContent = Window.Location.getParameter("noframe") != null;
273         String contentWidth = bareContent ? "100%" : "75%";
274
275         VerticalPanel outer = new VerticalPanel();
276         outer.setWidth("100%");
277         if (!bareContent) {
278                 outer.addStyleName("pithos-outer");
279         }
280
281         if (!bareContent) {
282                 topPanel = new TopPanel(this, Pithos.images);
283                 topPanel.setWidth("100%");
284                 outer.add(topPanel);
285                 outer.setCellHorizontalAlignment(topPanel, HasHorizontalAlignment.ALIGN_CENTER);
286         }
287         
288         messagePanel.setVisible(false);
289         outer.add(messagePanel);
290         outer.setCellHorizontalAlignment(messagePanel, HasHorizontalAlignment.ALIGN_CENTER);
291         outer.setCellVerticalAlignment(messagePanel, HasVerticalAlignment.ALIGN_MIDDLE);
292
293         HorizontalPanel header = new HorizontalPanel();
294         header.addStyleName("pithos-header");
295         header.setWidth(contentWidth);
296         if (bareContent)
297                 header.addStyleName("pithos-header-noframe");
298         upload = new Button("Upload", new ClickHandler() {
299             @Override
300             public void onClick(ClickEvent event) {
301                 if (getSelection() != null)
302                         new UploadFileCommand(Pithos.this, null, getSelection()).execute();
303             }
304         });
305         upload.addStyleName("pithos-uploadButton");
306         header.add(upload);
307         header.setCellHorizontalAlignment(upload, HasHorizontalAlignment.ALIGN_LEFT);
308         header.setCellVerticalAlignment(upload, HasVerticalAlignment.ALIGN_MIDDLE);
309
310         toolbar = new Toolbar(this);
311         header.add(toolbar);
312         header.setCellHorizontalAlignment(toolbar, HasHorizontalAlignment.ALIGN_CENTER);
313         header.setCellVerticalAlignment(toolbar, HasVerticalAlignment.ALIGN_MIDDLE);
314         
315         HorizontalPanel folderStatistics = new HorizontalPanel();
316         folderStatistics.addStyleName("pithos-folderStatistics");
317         numOfFiles = new HTML();
318         folderStatistics.add(numOfFiles);
319         folderStatistics.setCellVerticalAlignment(numOfFiles, HasVerticalAlignment.ALIGN_MIDDLE);
320         HTML numOfFilesLabel = new HTML("&nbsp;Files");
321         folderStatistics.add(numOfFilesLabel);
322         folderStatistics.setCellVerticalAlignment(numOfFilesLabel, HasVerticalAlignment.ALIGN_MIDDLE);
323         header.add(folderStatistics);
324         header.setCellHorizontalAlignment(folderStatistics, HasHorizontalAlignment.ALIGN_RIGHT);
325         header.setCellVerticalAlignment(folderStatistics, HasVerticalAlignment.ALIGN_MIDDLE);
326         header.setCellWidth(folderStatistics, "40px");
327         outer.add(header);
328         outer.setCellHorizontalAlignment(header, HasHorizontalAlignment.ALIGN_CENTER);
329         // Inner contains the various lists.nner
330         inner.sinkEvents(Event.ONCONTEXTMENU);
331         inner.setWidth("100%");
332
333         folderTreeSelectionModel = new SingleSelectionModel<Folder>();
334         folderTreeSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
335             @Override
336             public void onSelectionChange(SelectionChangeEvent event) {
337                 if (folderTreeSelectionModel.getSelectedObject() != null) {
338                     deselectOthers(folderTreeView, folderTreeSelectionModel);
339                     applyPermissions(folderTreeSelectionModel.getSelectedObject());
340                     Folder f = folderTreeSelectionModel.getSelectedObject();
341                         updateFolder(f, true, new Command() {
342                                 
343                                 @Override
344                                 public void execute() {
345                                         updateStatistics();
346                                 }
347                         });
348                 }
349                 showRelevantToolbarButtons();
350             }
351         });
352         selectionModels.add(folderTreeSelectionModel);
353
354         folderTreeViewModel = new FolderTreeViewModel(this, folderTreeSelectionModel);
355         folderTreeView = new FolderTreeView(folderTreeViewModel);
356         treeViews.add(folderTreeView);
357         
358         fileList = new FileList(this, images, folderTreeView);
359         inner.add(fileList);
360
361         groupTreeViewModel = new GroupTreeViewModel(this);
362         groupTreeView = new GroupTreeView(groupTreeViewModel);
363         treeViews.add(groupTreeView);
364         
365         trees = new VerticalPanel();
366         trees.setWidth("100%");
367
368         
369         HorizontalPanel treeHeader = new HorizontalPanel();
370         treeHeader.addStyleName("pithos-treeHeader");
371         treeHeader.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
372         treeHeader.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
373         HorizontalPanel statistics = new HorizontalPanel();
374         statistics.addStyleName("pithos-statistics");
375         statistics.add(new HTML("Used:&nbsp;"));
376         usedBytes = new HTML();
377         statistics.add(usedBytes);
378         statistics.add(new HTML("&nbsp;of&nbsp;"));
379         totalBytes = new HTML();
380         statistics.add(totalBytes);
381         statistics.add(new HTML("&nbsp;("));
382         usedPercent = new HTML();
383         statistics.add(usedPercent);
384         statistics.add(new HTML(")"));
385         treeHeader.add(statistics);
386         treeHeader.setCellHorizontalAlignment(statistics, HasHorizontalAlignment.ALIGN_LEFT);
387         trees.add(treeHeader);
388
389         trees.add(folderTreeView);
390         trees.add(groupTreeView);
391         // Add the left and right panels to the split panel.
392         splitPanel.setLeftWidget(trees);
393         splitPanel.setRightWidget(inner);
394         splitPanel.setSplitPosition("35%");
395         splitPanel.setSize("100%", "100%");
396         splitPanel.addStyleName("pithos-splitPanel");
397         splitPanel.setWidth(contentWidth);
398         outer.add(splitPanel);
399         outer.setCellHorizontalAlignment(splitPanel, HasHorizontalAlignment.ALIGN_CENTER);
400
401         if (!bareContent) {
402                 statusPanel = new StatusPanel();
403                 statusPanel.setWidth("100%");
404                 outer.add(statusPanel);
405                 outer.setCellHorizontalAlignment(statusPanel, HasHorizontalAlignment.ALIGN_CENTER);
406         }
407         else
408                 splitPanel.addStyleName("pithos-splitPanel-noframe");
409
410         // Hook the window resize event, so that we can adjust the UI.
411         Window.addResizeHandler(this);
412         // Clear out the window's built-in margin, because we want to take
413         // advantage of the entire client area.
414         Window.setMargin("0px");
415         // Finally, add the outer panel to the RootPanel, so that it will be
416         // displayed.
417         RootPanel.get().add(outer);
418         // Call the window resized handler to get the initial sizes setup. Doing
419         // this in a deferred command causes it to occur after all widgets'
420         // sizes have been computed by the browser.
421         Scheduler.get().scheduleDeferred(new ScheduledCommand() {
422
423             @Override
424             public void execute() {
425                 onWindowResized(Window.getClientHeight());
426             }
427         });
428
429         Scheduler.get().scheduleDeferred(new ScheduledCommand() {
430             @Override
431             public void execute() {
432                 fetchAccount(new Command() {
433                                         
434                                         @Override
435                                         public void execute() {
436                                 if (!account.hasHomeContainer())
437                                     createHomeContainer(account, this);
438                                 else if (!account.hasTrashContainer())
439                                         createTrashContainer(this);
440                                 else {
441                                         for (Folder f : account.getContainers())
442                                                 if (f.getName().equals(Pithos.TRASH_CONTAINER)) {
443                                                         trash = f;
444                                                         break;
445                                                 }
446                                     folderTreeViewModel.initialize(account, new Command() {
447                                                                 
448                                                                 @Override
449                                                                 public void execute() {
450                                                     createMySharedTree();
451                                                                 }
452                                                         });
453                                     groupTreeViewModel.initialize();
454                                     showStatistics();
455                                 }
456                                         }
457                                 });
458             }
459         });
460         
461 //        Scheduler.get().scheduleDeferred(new Command() {
462 //                      
463 //                      @Override
464 //                      public void execute() {
465 //                              displayError("lalala");
466 //                              
467 //                      }
468 //              });
469     }
470
471     public void applyPermissions(Folder f) {
472         if (f != null) {
473                 if (f.isInTrash())
474                         upload.setEnabled(false);
475                 else {
476                         Boolean[] perms = f.getPermissions().get(username);
477                         if (f.getOwner().equals(username) || (perms != null && perms[1] != null && perms[1])) {
478                                 upload.setEnabled(true);
479                         }
480                         else
481                                 upload.setEnabled(false);
482                 }
483         }
484         else
485                 upload.setEnabled(false);
486         }
487
488         @SuppressWarnings({ "rawtypes", "unchecked" })
489         public void deselectOthers(TreeView _selectedTree, SingleSelectionModel model) {
490         selectedTree = _selectedTree;
491         
492         for (Composite c : treeViews)
493                 if (c.equals(selectedTree))
494                         c.addStyleName("cellTreeWidget-selectedTree");
495                 else
496                         c.removeStyleName("cellTreeWidget-selectedTree");
497         
498         for (SingleSelectionModel s : selectionModels)
499             if (!s.equals(model))
500                 s.setSelected(s.getSelectedObject(), false);
501     }
502
503     public void showFiles(final Folder f) {
504         Set<File> files = f.getFiles();
505         showFiles(files);
506     }
507
508     public void showFiles(Set<File> files) {
509         fileList.setFiles(new ArrayList<File>(files));
510     }
511
512     /**
513          * Parse and store the user credentials to the appropriate fields.
514          */
515         private boolean parseUserCredentials() {
516         username = Window.Location.getParameter("user");
517         token = Window.Location.getParameter("token");
518         Configuration conf = (Configuration) GWT.create(Configuration.class);
519         if (username == null || username.length() == 0 || token == null || token.length() == 0) {
520             String cookie = conf.authCookie();
521             String auth = Cookies.getCookie(cookie);
522             if (auth == null) {
523                 authenticateUser();
524                 return false;
525             }
526             if (auth.startsWith("\""))
527                 auth = auth.substring(1);
528             if (auth.endsWith("\""))
529                 auth = auth.substring(0, auth.length() - 1);
530                         String[] authSplit = auth.split("\\" + conf.cookieSeparator(), 2);
531                         if (authSplit.length != 2) {
532                             authenticateUser();
533                             return false;
534                         }
535                         username = authSplit[0];
536                         token = authSplit[1];
537                         return true;
538         }
539         
540                 Cookies.setCookie(conf.authCookie(), username + conf.cookieSeparator() + token, null, "", "/", false);
541                 return true;
542     }
543
544     /**
545          * Redirect the user to the login page for authentication.
546          */
547         protected void authenticateUser() {
548         Window.Location.assign(config.loginUrl());
549         }
550
551         protected void fetchAccount(final Command callback) {
552         String path = "?format=json";
553
554         GetRequest<AccountResource> getAccount = new GetRequest<AccountResource>(AccountResource.class, getApiPath(), username, path) {
555             @Override
556             public void onSuccess(AccountResource _result) {
557                 account = _result;
558                 if (callback != null)
559                         callback.execute();
560             }
561
562             @Override
563             public void onError(Throwable t) {
564                 GWT.log("Error getting account", t);
565                                 setError(t);
566                 if (t instanceof RestException)
567                     displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
568                 else
569                     displayError("System error fetching user data: " + t.getMessage());
570             }
571
572                         @Override
573                         protected void onUnauthorized(Response response) {
574                                 sessionExpired();
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(AccountResource _result) {
586                                 showStatistics();
587                         }
588
589                         @Override
590                         public void onError(Throwable t) {
591                 GWT.log("Error getting account", t);
592                                 setError(t);
593                 if (t instanceof RestException)
594                     displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
595                 else
596                     displayError("System error fetching user data: " + t.getMessage());
597                         }
598
599                         @Override
600                         protected void onUnauthorized(Response response) {
601                                 sessionExpired();
602                         }
603                 };
604                 headAccount.setHeader("X-Auth-Token", token);
605                 Scheduler.get().scheduleDeferred(headAccount);
606         }
607
608         protected void showStatistics() {
609         usedBytes.setHTML(String.valueOf(account.getFileSizeAsString()));
610         totalBytes.setHTML(String.valueOf(account.getQuotaAsString()));
611         NumberFormat nf = NumberFormat.getPercentFormat();
612         usedPercent.setHTML(nf.format(account.getUsedPercentage()));
613         }
614
615         protected void createHomeContainer(final AccountResource _account, final Command callback) {
616         String path = "/" + Pithos.HOME_CONTAINER;
617         PutRequest createPithos = new PutRequest(getApiPath(), getUsername(), path) {
618             @Override
619             public void onSuccess(Resource result) {
620                 if (!_account.hasTrashContainer())
621                         createTrashContainer(callback);
622                 else
623                         fetchAccount(callback);
624             }
625
626             @Override
627             public void onError(Throwable t) {
628                 GWT.log("Error creating pithos", t);
629                                 setError(t);
630                 if (t instanceof RestException)
631                     displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
632                 else
633                     displayError("System error Error creating pithos: " + t.getMessage());
634             }
635
636                         @Override
637                         protected void onUnauthorized(Response response) {
638                                 sessionExpired();
639                         }
640         };
641         createPithos.setHeader("X-Auth-Token", getToken());
642         Scheduler.get().scheduleDeferred(createPithos);
643     }
644
645     protected void createTrashContainer(final Command callback) {
646         String path = "/" + Pithos.TRASH_CONTAINER;
647         PutRequest createPithos = new PutRequest(getApiPath(), getUsername(), path) {
648             @Override
649             public void onSuccess(Resource result) {
650                         fetchAccount(callback);
651             }
652
653             @Override
654             public void onError(Throwable t) {
655                 GWT.log("Error creating pithos", t);
656                                 setError(t);
657                 if (t instanceof RestException)
658                     displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
659                 else
660                     displayError("System error Error creating pithos: " + t.getMessage());
661             }
662
663                         @Override
664                         protected void onUnauthorized(Response response) {
665                                 sessionExpired();
666                         }
667         };
668         createPithos.setHeader("X-Auth-Token", getToken());
669         Scheduler.get().scheduleDeferred(createPithos);
670     }
671
672     /**
673          * Creates an HTML fragment that places an image & caption together, for use
674          * in a group header.
675          *
676          * @param imageProto an image prototype for an image
677          * @param caption the group caption
678          * @return the header HTML fragment
679          */
680         private String createHeaderHTML(AbstractImagePrototype imageProto, String caption) {
681                 String captionHTML = "<table class='caption' cellpadding='0' " 
682                 + "cellspacing='0'>" + "<tr><td class='lcaption'>" + imageProto.getHTML() 
683                 + "</td><td id =" + caption +" class='rcaption'><b style='white-space:nowrap'>&nbsp;" 
684                 + caption + "</b></td></tr></table>";
685                 return captionHTML;
686         }
687
688         protected void onWindowResized(int height) {
689                 // Adjust the split panel to take up the available room in the window.
690                 int newHeight = height - splitPanel.getAbsoluteTop();
691                 if (newHeight < 1)
692                         newHeight = 1;
693                 splitPanel.setHeight("" + newHeight);
694                 inner.setHeight("" + newHeight);
695         }
696
697         @Override
698         public void onResize(ResizeEvent event) {
699                 int height = event.getHeight();
700                 onWindowResized(height);
701         }
702
703         /**
704          * Display an error message.
705          *
706          * @param msg the message to display
707          */
708         public void displayError(String msg) {
709                 messagePanel.displayError(msg);
710         }
711
712         /**
713          * Display a warning message.
714          *
715          * @param msg the message to display
716          */
717         public void displayWarning(String msg) {
718                 messagePanel.displayWarning(msg);
719         }
720
721         /**
722          * Display an informational message.
723          *
724          * @param msg the message to display
725          */
726         public void displayInformation(String msg) {
727                 messagePanel.displayInformation(msg);
728         }
729
730         /**
731          * Retrieve the fileList.
732          *
733          * @return the fileList
734          */
735         public FileList getFileList() {
736                 return fileList;
737         }
738
739         /**
740          * Retrieve the topPanel.
741          *
742          * @return the topPanel
743          */
744         TopPanel getTopPanel() {
745                 return topPanel;
746         }
747
748         /**
749          * Retrieve the clipboard.
750          *
751          * @return the clipboard
752          */
753         public Clipboard getClipboard() {
754                 return clipboard;
755         }
756
757         public StatusPanel getStatusPanel() {
758                 return statusPanel;
759         }
760
761         public String getToken() {
762                 return token;
763         }
764
765         public static native void preventIESelection() /*-{
766                 $doc.body.onselectstart = function () { return false; };
767         }-*/;
768
769         public static native void enableIESelection() /*-{
770                 if ($doc.body.onselectstart != null)
771                 $doc.body.onselectstart = null;
772         }-*/;
773
774         /**
775          * @return the absolute path of the API root URL
776          */
777         public String getApiPath() {
778                 Configuration conf = (Configuration) GWT.create(Configuration.class);
779                 return conf.apiPath();
780         }
781
782         /**
783          * History support for folder navigation
784          * adds a new browser history entry
785          *
786          * @param key
787          */
788         public void updateHistory(String key){
789 //              Replace any whitespace of the initial string to "+"
790 //              String result = key.replaceAll("\\s","+");
791 //              Add a new browser history entry.
792 //              History.newItem(result);
793                 History.newItem(key);
794         }
795
796     public void deleteFolder(final Folder folder) {
797         String path = getApiPath() + folder.getOwner() + "/" + folder.getContainer() + "?format=json&delimiter=/&prefix=" + URL.encodeQueryString(folder.getPrefix()) + "&t=" + System.currentTimeMillis();
798         RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, path);
799         builder.setHeader("X-Auth-Token", getToken());
800         try {
801             builder.sendRequest("", new RequestCallback() {
802                 @Override
803                 public void onResponseReceived(Request request, Response response) {
804                     if (response.getStatusCode() == Response.SC_OK) {
805                         JSONValue json = JSONParser.parseStrict(response.getText());
806                         JSONArray array = json.isArray();
807                         int i = 0;
808                         if (array != null) {
809                             deleteObject(folder, i, array);
810                         }
811                     }
812                 }
813
814                 @Override
815                 public void onError(Request request, Throwable exception) {
816                         setError(exception);
817                     displayError("System error unable to delete folder: " + exception.getMessage());
818                 }
819             });
820         }
821         catch (RequestException e) {
822         }
823     }
824
825     void deleteObject(final Folder folder, final int i, final JSONArray array) {
826         if (i < array.size()) {
827             JSONObject o = array.get(i).isObject();
828             if (o != null && !o.containsKey("subdir")) {
829                 JSONString name = o.get("name").isString();
830                 String path = "/" + folder.getContainer() + "/" + name.stringValue();
831                 DeleteRequest delete = new DeleteRequest(getApiPath(), folder.getOwner(), URL.encode(path)) {
832                     @Override
833                     public void onSuccess(Resource result) {
834                         deleteObject(folder, i + 1, array);
835                     }
836
837                     @Override
838                     public void onError(Throwable t) {
839                         GWT.log("", t);
840                                                 setError(t);
841                         displayError("System error unable to delete folder: " + t.getMessage());
842                     }
843
844                                 @Override
845                                 protected void onUnauthorized(Response response) {
846                                         sessionExpired();
847                                 }
848                 };
849                 delete.setHeader("X-Auth-Token", getToken());
850                 Scheduler.get().scheduleDeferred(delete);
851             }
852             else if (o != null) {
853                 String subdir = o.get("subdir").isString().stringValue();
854                 subdir = subdir.substring(0, subdir.length() - 1);
855                 String path = getApiPath() + getUsername() + "/" + folder.getContainer() + "?format=json&delimiter=/&prefix=" + URL.encodeQueryString(subdir) + "&t=" + System.currentTimeMillis();
856                 RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, path);
857                 builder.setHeader("X-Auth-Token", getToken());
858                 try {
859                     builder.sendRequest("", new RequestCallback() {
860                         @Override
861                         public void onResponseReceived(Request request, Response response) {
862                             if (response.getStatusCode() == Response.SC_OK) {
863                                 JSONValue json = JSONParser.parseStrict(response.getText());
864                                 JSONArray array2 = json.isArray();
865                                 if (array2 != null) {
866                                     int l = array.size();
867                                     for (int j=0; j<array2.size(); j++) {
868                                         array.set(l++, array2.get(j));
869                                     }
870                                 }
871                                 deleteObject(folder, i + 1, array);
872                             }
873                         }
874
875                         @Override
876                         public void onError(Request request, Throwable exception) {
877                                 setError(exception);
878                             displayError("System error unable to delete folder: " + exception.getMessage());
879                         }
880                     });
881                 }
882                 catch (RequestException e) {
883                 }
884             }
885         }
886         else {
887             String path = folder.getUri();
888             DeleteRequest deleteFolder = new DeleteRequest(getApiPath(), getUsername(), URL.encode(path)) {
889                 @Override
890                 public void onSuccess(Resource result) {
891                     updateFolder(folder.getParent(), true, new Command() {
892                                                 
893                                                 @Override
894                                                 public void execute() {
895                                                         updateStatistics();
896                                                 }
897                                         });
898                 }
899
900                 @Override
901                 public void onError(Throwable t) {
902                     GWT.log("", t);
903                                         setError(t);
904                     if (t instanceof RestException) {
905                         if (((RestException) t).getHttpStatusCode() != Response.SC_NOT_FOUND)
906                                 displayError("Unable to delete folder: "+((RestException) t).getHttpStatusText());
907                         else
908                                 onSuccess(null);
909                     }
910                     else
911                         displayError("System error unable to delete folder: " + t.getMessage());
912                 }
913
914                                 @Override
915                                 protected void onUnauthorized(Response response) {
916                                         sessionExpired();
917                                 }
918             };
919             deleteFolder.setHeader("X-Auth-Token", getToken());
920             Scheduler.get().scheduleDeferred(deleteFolder);
921         }
922     }
923
924     public FolderTreeView getFolderTreeView() {
925         return folderTreeView;
926     }
927
928     public void copyFiles(final Iterator<File> iter, final String targetUsername, final String targetUri, final Command callback) {
929         if (iter.hasNext()) {
930             File file = iter.next();
931             String path = targetUri + "/" + file.getName();
932             PutRequest copyFile = new PutRequest(getApiPath(), targetUsername, path) {
933                 @Override
934                 public void onSuccess(Resource result) {
935                     copyFiles(iter, targetUsername, targetUri, callback);
936                 }
937
938                 @Override
939                 public void onError(Throwable t) {
940                     GWT.log("", t);
941                                         setError(t);
942                     if (t instanceof RestException) {
943                         displayError("Unable to copy file: " + ((RestException) t).getHttpStatusText());
944                     }
945                     else
946                         displayError("System error unable to copy file: "+t.getMessage());
947                 }
948
949                                 @Override
950                                 protected void onUnauthorized(Response response) {
951                                         sessionExpired();
952                                 }
953             };
954             copyFile.setHeader("X-Auth-Token", getToken());
955             copyFile.setHeader("X-Copy-From", URL.encodePathSegment(file.getUri()));
956             if (!file.getOwner().equals(targetUsername))
957                 copyFile.setHeader("X-Source-Account", URL.encodePathSegment(file.getOwner()));
958             copyFile.setHeader("Content-Type", file.getContentType());
959             Scheduler.get().scheduleDeferred(copyFile);
960         }
961         else  if (callback != null) {
962             callback.execute();
963         }
964     }
965
966     public void copySubfolders(final Iterator<Folder> iter, final String targetUsername, final String targetUri, final Command callback) {
967         if (iter.hasNext()) {
968             final Folder f = iter.next();
969             copyFolder(f, targetUsername, targetUri, new Command() {
970                                 
971                                 @Override
972                                 public void execute() {
973                                         copySubfolders(iter, targetUsername, targetUri, callback);
974                                 }
975                         });
976         }
977         else  if (callback != null) {
978             callback.execute();
979         }
980     }
981
982     public void copyFolder(final Folder f, final String targetUsername, final String targetUri, final Command callback) {
983         String path = targetUri + "/" + f.getName();
984         PutRequest createFolder = new PutRequest(getApiPath(), targetUsername, path) {
985             @Override
986             public void onSuccess(Resource result) {
987                 GetRequest<Folder> getFolder = new GetRequest<Folder>(Folder.class, getApiPath(), f.getOwner(), "/" + f.getContainer() + "?format=json&delimiter=/&prefix=" + URL.encodeQueryString(f.getPrefix()), f) {
988
989                                         @Override
990                                         public void onSuccess(final Folder _f) {
991                                 Iterator<File> iter = _f.getFiles().iterator();
992                                 copyFiles(iter, targetUsername, targetUri + "/" + _f.getName(), new Command() {
993                                     @Override
994                                     public void execute() {
995                                         Iterator<Folder> iterf = _f.getSubfolders().iterator();
996                                         copySubfolders(iterf, targetUsername, targetUri + "/" + _f.getName(), callback);
997                                     }
998                                 });
999                                         }
1000
1001                                         @Override
1002                                         public void onError(Throwable t) {
1003                                 GWT.log("", t);
1004                                                 setError(t);
1005                                 if (t instanceof RestException) {
1006                                     displayError("Unable to get folder: " + ((RestException) t).getHttpStatusText());
1007                                 }
1008                                 else
1009                                     displayError("System error getting folder: " + t.getMessage());
1010                                         }
1011
1012                                         @Override
1013                                         protected void onUnauthorized(Response response) {
1014                                                 sessionExpired();
1015                                         }
1016                                 };
1017                                 getFolder.setHeader("X-Auth-Token", getToken());
1018                                 Scheduler.get().scheduleDeferred(getFolder);
1019             }
1020
1021             @Override
1022             public void onError(Throwable t) {
1023                 GWT.log("", t);
1024                                 setError(t);
1025                if (t instanceof RestException) {
1026                     displayError("Unable to create folder: " + ((RestException) t).getHttpStatusText());
1027                 }
1028                 else
1029                     displayError("System error creating folder: " + t.getMessage());
1030             }
1031
1032                         @Override
1033                         protected void onUnauthorized(Response response) {
1034                                 sessionExpired();
1035                         }
1036         };
1037         createFolder.setHeader("X-Auth-Token", getToken());
1038         createFolder.setHeader("Accept", "*/*");
1039         createFolder.setHeader("Content-Length", "0");
1040         createFolder.setHeader("Content-Type", "application/folder");
1041         Scheduler.get().scheduleDeferred(createFolder);
1042     }
1043     
1044     public void addSelectionModel(@SuppressWarnings("rawtypes") SingleSelectionModel model) {
1045         selectionModels.add(model);
1046     }
1047
1048         public OtherSharedTreeView getOtherSharedTreeView() {
1049                 return otherSharedTreeView;
1050         }
1051
1052         public void updateTrash(boolean showFiles, Command callback) {
1053                 updateFolder(trash, showFiles, callback);
1054         }
1055
1056         public void updateGroupsNode() {
1057                 groupTreeView.updateGroupNode(null);
1058         }
1059
1060         public void addGroup(String groupname) {
1061                 Group newGroup = new Group(groupname);
1062                 account.addGroup(newGroup);
1063                 groupTreeView.updateGroupNode(null);
1064         }
1065
1066         public void removeGroup(Group group) {
1067                 account.removeGroup(group);
1068                 updateGroupsNode();
1069         }
1070
1071         public TreeView getSelectedTree() {
1072                 return selectedTree;
1073         }
1074         
1075         public Folder getSelection() {
1076                 return selectedTree.getSelection();
1077         }
1078
1079         public void showFolderStatistics(int folderFileCount) {
1080                 numOfFiles.setHTML(String.valueOf(folderFileCount));
1081         }
1082
1083         public GroupTreeView getGroupTreeView() {
1084                 return groupTreeView;
1085         }
1086
1087         public void sessionExpired() {
1088                 new SessionExpiredDialog(this).center();
1089         }
1090
1091         public void updateRootFolder(Command callback) {
1092                 updateFolder(account.getPithos(), false, callback);
1093         }
1094
1095         void createMySharedTree() {
1096                 mysharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1097                 mysharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1098                     @Override
1099                     public void onSelectionChange(SelectionChangeEvent event) {
1100                         if (mysharedTreeSelectionModel.getSelectedObject() != null) {
1101                             deselectOthers(mysharedTreeView, mysharedTreeSelectionModel);
1102                             upload.setEnabled(false);
1103                             updateSharedFolder(mysharedTreeSelectionModel.getSelectedObject(), true);
1104                         }
1105                         showRelevantToolbarButtons();
1106                     }
1107                 });
1108                 selectionModels.add(mysharedTreeSelectionModel);
1109                 mysharedTreeViewModel = new MysharedTreeViewModel(Pithos.this, mysharedTreeSelectionModel);
1110                 mysharedTreeViewModel.initialize(new Command() {
1111                         
1112                         @Override
1113                         public void execute() {
1114                             mysharedTreeView = new MysharedTreeView(mysharedTreeViewModel);
1115                                 trees.insert(mysharedTreeView, 2);
1116                                 treeViews.add(mysharedTreeView);
1117                                 createOtherSharedTree();
1118                         }
1119                 });
1120         }
1121
1122         void createOtherSharedTree() {
1123                 otherSharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1124                 otherSharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1125                     @Override
1126                     public void onSelectionChange(SelectionChangeEvent event) {
1127                         if (otherSharedTreeSelectionModel.getSelectedObject() != null) {
1128                             deselectOthers(otherSharedTreeView, otherSharedTreeSelectionModel);
1129                             otherSharedTreeView.addStyleName("cellTreeWidget-selectedTree");
1130                             applyPermissions(otherSharedTreeSelectionModel.getSelectedObject());
1131                             updateOtherSharedFolder(otherSharedTreeSelectionModel.getSelectedObject(), true);
1132                         }
1133                         showRelevantToolbarButtons();
1134                     }
1135                 });
1136                 selectionModels.add(otherSharedTreeSelectionModel);
1137                 otherSharedTreeViewModel = new OtherSharedTreeViewModel(Pithos.this, otherSharedTreeSelectionModel);
1138                 otherSharedTreeViewModel.initialize(new Command() {
1139                         
1140                         @Override
1141                         public void execute() {
1142                             otherSharedTreeView = new OtherSharedTreeView(otherSharedTreeViewModel);
1143                                 trees.insert(otherSharedTreeView, 3);
1144                                 treeViews.add(otherSharedTreeView);
1145                         }
1146                 });
1147         }
1148
1149         public void logoff() {
1150                 Cookies.removeCookie(config.authCookie(), "/");
1151                 Cookies.removeCookie(config.authTokenCookie(), "/");
1152                 for (String s: Cookies.getCookieNames())
1153                         if (s.startsWith(config.shibSessionCookiePrefix()))
1154                                 Cookies.removeCookie(s, "/");
1155                 Window.Location.assign(config.logoutUrl());
1156         }
1157         
1158         public native void log1(String message)/*-{
1159                 $wnd.console.log(message);
1160         }-*/;
1161
1162         public String getErrorData() {
1163                 if (error != null)
1164                         return error.toString();
1165                 return "";
1166         }
1167         
1168         public void setError(Throwable t) {
1169                 error = t;
1170         }
1171         
1172         public void showRelevantToolbarButtons() {
1173                 toolbar.showRelevantButtons();
1174         }
1175
1176         public FileUploadDialog getFileUploadDialog() {
1177                 if (fileUploadDialog == null)
1178                         fileUploadDialog = new FileUploadDialog(this);
1179                 return fileUploadDialog;
1180         }
1181
1182         public void hideUploadIndicator() {
1183                 upload.removeStyleName("pithos-uploadButton-loading");
1184                 upload.setTitle("");
1185         }
1186         
1187         public void showUploadIndicator() {
1188                 upload.addStyleName("pithos-uploadButton-loading");
1189                 upload.setTitle("Upload in progress. Click for details.");
1190         }
1191 }