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