Finished folder permissions dialog
[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 com.google.gwt.core.client.Scheduler;
38 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
39 import com.google.gwt.http.client.Request;
40 import com.google.gwt.http.client.RequestBuilder;
41 import com.google.gwt.http.client.RequestCallback;
42 import com.google.gwt.http.client.RequestException;
43 import com.google.gwt.http.client.Response;
44 import com.google.gwt.json.client.JSONArray;
45 import com.google.gwt.json.client.JSONObject;
46 import com.google.gwt.json.client.JSONParser;
47 import com.google.gwt.json.client.JSONString;
48 import com.google.gwt.json.client.JSONValue;
49 import com.google.gwt.user.client.Command;
50 import com.google.gwt.view.client.SelectionChangeEvent;
51 import com.google.gwt.view.client.SelectionChangeEvent.Handler;
52 import com.google.gwt.view.client.SingleSelectionModel;
53 import gr.grnet.pithos.web.client.commands.GetUserCommand;
54 import gr.grnet.pithos.web.client.foldertree.AccountResource;
55 import gr.grnet.pithos.web.client.foldertree.File;
56 import gr.grnet.pithos.web.client.foldertree.Folder;
57 import gr.grnet.pithos.web.client.foldertree.FolderTreeView;
58 import gr.grnet.pithos.web.client.foldertree.FolderTreeViewModel;
59 import gr.grnet.pithos.web.client.foldertree.Resource;
60 import gr.grnet.pithos.web.client.rest.DeleteRequest;
61 import gr.grnet.pithos.web.client.rest.GetRequest;
62 import gr.grnet.pithos.web.client.rest.PutRequest;
63 import gr.grnet.pithos.web.client.rest.RestException;
64 import gr.grnet.pithos.web.client.rest.resource.FileResource;
65 import gr.grnet.pithos.web.client.rest.resource.OtherUserResource;
66 import gr.grnet.pithos.web.client.rest.resource.RestResource;
67 import gr.grnet.pithos.web.client.rest.resource.RestResourceWrapper;
68 import gr.grnet.pithos.web.client.rest.resource.SharedResource;
69 import gr.grnet.pithos.web.client.rest.resource.TrashResource;
70 import gr.grnet.pithos.web.client.rest.resource.UserResource;
71
72 import gr.grnet.pithos.web.client.tagtree.Tag;
73 import gr.grnet.pithos.web.client.tagtree.TagTreeView;
74 import gr.grnet.pithos.web.client.tagtree.TagTreeViewModel;
75 import java.util.ArrayList;
76 import java.util.Arrays;
77 import java.util.Collection;
78 import java.util.Date;
79 import java.util.HashMap;
80 import java.util.Iterator;
81 import java.util.List;
82
83 import com.google.gwt.core.client.EntryPoint;
84 import com.google.gwt.core.client.GWT;
85 import com.google.gwt.event.logical.shared.ResizeEvent;
86 import com.google.gwt.event.logical.shared.ResizeHandler;
87 import com.google.gwt.event.logical.shared.SelectionEvent;
88 import com.google.gwt.event.logical.shared.SelectionHandler;
89 import com.google.gwt.http.client.URL;
90 import com.google.gwt.i18n.client.DateTimeFormat;
91 import com.google.gwt.resources.client.ClientBundle;
92 import com.google.gwt.resources.client.ImageResource;
93 import com.google.gwt.user.client.Cookies;
94 import com.google.gwt.user.client.Event;
95 import com.google.gwt.user.client.History;
96 import com.google.gwt.user.client.Window;
97 import com.google.gwt.user.client.ui.AbstractImagePrototype;
98 import com.google.gwt.user.client.ui.DecoratedTabPanel;
99 import com.google.gwt.user.client.ui.HasHorizontalAlignment;
100 import com.google.gwt.user.client.ui.HorizontalSplitPanel;
101 import com.google.gwt.user.client.ui.RootPanel;
102 import com.google.gwt.user.client.ui.TabPanel;
103 import com.google.gwt.user.client.ui.VerticalPanel;
104 import java.util.Set;
105
106 /**
107  * Entry point classes define <code>onModuleLoad()</code>.
108  */
109 public class Pithos implements EntryPoint, ResizeHandler {
110
111         /**
112          * A constant that denotes the completion of an IncrementalCommand.
113          */
114         public static final boolean DONE = false;
115
116         public static final int VISIBLE_FILE_COUNT = 25;
117
118         /**
119          * Instantiate an application-level image bundle. This object will provide
120          * programmatic access to all the images needed by widgets.
121          */
122         private static Images images = (Images) GWT.create(Images.class);
123
124     public String getUsername() {
125         return username;
126     }
127
128     public void setAccount(AccountResource acct) {
129         account = acct;
130     }
131
132     public AccountResource getAccount() {
133         return account;
134     }
135
136     public void updateFolder(Folder f, boolean showfiles) {
137         folderTreeView.updateFolder(f, showfiles);
138     }
139
140     public void updateTag(Tag t) {
141         tagTreeView.updateTag(t);
142     }
143
144     public void updateTags() {
145         tagTreeViewModel.initialize(getAllTags());
146     }
147
148     public List<Tag> getAllTags() {
149         List<Tag> tagList = new ArrayList<Tag>();
150         for (Folder f : account.getContainers()) {
151             for (String t : f.getTags()) {
152                 tagList.add(new Tag(t));
153             }
154         }
155         return tagList;
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 ClientBundle, TopPanel.Images, StatusPanel.Images, FileMenu.Images, EditMenu.Images, FilePropertiesDialog.Images, MessagePanel.Images, FileList.Images, Search.Images, CellTreeView.Images {
163
164                 @Source("gr/grnet/pithos/resources/document.png")
165                 ImageResource folders();
166
167                 @Source("gr/grnet/pithos/resources/edit_group_22.png")
168                 ImageResource groups();
169
170                 @Source("gr/grnet/pithos/resources/search.png")
171                 ImageResource search();
172         }
173
174         /**
175          * The Application Clipboard implementation;
176          */
177         private Clipboard clipboard = new Clipboard();
178
179         private UserResource currentUserResource;
180
181         /**
182          * The top panel that contains the menu bar.
183          */
184         private TopPanel topPanel;
185
186         /**
187          * The panel that contains the various system messages.
188          */
189         private MessagePanel messagePanel = new MessagePanel(Pithos.images);
190
191         /**
192          * The bottom panel that contains the status bar.
193          */
194         private StatusPanel statusPanel = null;
195
196         /**
197          * The top right panel that displays the logged in user details
198          */
199         private UserDetailsPanel userDetailsPanel = new UserDetailsPanel(this);
200
201         /**
202          * The file list widget.
203          */
204         private FileList fileList;
205
206         /**
207          * The tab panel that occupies the right side of the screen.
208          */
209         private TabPanel inner = new DecoratedTabPanel(){
210                 
211 //              public void onBrowserEvent(com.google.gwt.user.client.Event event) {
212 //                      if (DOM.eventGetType(event) == Event.ONCONTEXTMENU){
213 //                              if(isFileListShowing()){
214 //                                      getFileList().showContextMenu(event);
215 //                              }
216 //                      }
217 //              };
218         };
219
220
221         /**
222          * The split panel that will contain the left and right panels.
223          */
224         private HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
225
226         /**
227          * The widget that displays the tree of folders.
228          */
229         
230         private CellTreeView treeView = null;
231         /**
232          * The currently selected item in the application, for use by the Edit menu
233          * commands. Potential types are Folder, File, User and Group.
234          */
235         private Object currentSelection;
236
237
238         /**
239          * The WebDAV password of the current user
240          */
241         private String webDAVPassword;
242
243         public HashMap<String, String> userFullNameMap = new HashMap<String, String>();
244
245     private String username = null;
246
247     /**
248      * The authentication token of the current user.
249      */
250     private String token;
251
252     private SingleSelectionModel<Folder> folderTreeSelectionModel;
253     private FolderTreeViewModel folderTreeViewModel;
254     private FolderTreeView folderTreeView;
255
256     private SingleSelectionModel<Tag> tagTreeSelectionModel;
257     private TagTreeViewModel tagTreeViewModel;
258     private TagTreeView tagTreeView;
259
260     private AccountResource account;
261
262         @Override
263         public void onModuleLoad() {
264                 if (parseUserCredentials())
265             initialize();
266         }
267
268     private void initialize() {
269         topPanel = new TopPanel(this, Pithos.images);
270         topPanel.setWidth("100%");
271
272         messagePanel.setWidth("100%");
273         messagePanel.setVisible(false);
274
275
276         // Inner contains the various lists.
277         inner.sinkEvents(Event.ONCONTEXTMENU);
278         inner.setAnimationEnabled(true);
279         inner.getTabBar().addStyleName("pithos-MainTabBar");
280         inner.getDeckPanel().addStyleName("pithos-MainTabPanelBottom");
281
282         inner.setWidth("100%");
283
284         inner.addSelectionHandler(new SelectionHandler<Integer>() {
285
286             @Override
287             public void onSelection(SelectionEvent<Integer> event) {
288                 int tabIndex = event.getSelectedItem();
289                 switch (tabIndex) {
290                     case 0:
291                         fileList.updateCurrentlyShowingStats();
292                         break;
293                 }
294             }
295         });
296
297         folderTreeSelectionModel = new SingleSelectionModel<Folder>();
298         folderTreeSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
299             @Override
300             public void onSelectionChange(SelectionChangeEvent event) {
301                 if (folderTreeSelectionModel.getSelectedObject() != null) {
302                     tagTreeSelectionModel.setSelected(tagTreeSelectionModel.getSelectedObject(), false);
303                     Folder f = folderTreeSelectionModel.getSelectedObject();
304                     updateFolder(f, true);
305                 }
306             }
307         });
308
309         folderTreeViewModel = new FolderTreeViewModel(this, folderTreeSelectionModel);
310         folderTreeView = new FolderTreeView(folderTreeViewModel);
311
312         fileList = new FileList(this, images, folderTreeView);
313         inner.add(fileList, createHeaderHTML(AbstractImagePrototype.create(images.folders()), "Files"), true);
314
315         tagTreeSelectionModel = new SingleSelectionModel<Tag>();
316         tagTreeSelectionModel.addSelectionChangeHandler(new Handler() {
317             @Override
318             public void onSelectionChange(SelectionChangeEvent event) {
319                 if (tagTreeSelectionModel.getSelectedObject() != null) {
320                     folderTreeSelectionModel.setSelected(folderTreeSelectionModel.getSelectedObject(), false);
321                     Tag t = tagTreeSelectionModel.getSelectedObject();
322                     updateTag(t);
323                 }
324             }
325         });
326         tagTreeViewModel = new TagTreeViewModel(this, tagTreeSelectionModel);
327         tagTreeView = new TagTreeView(tagTreeViewModel);
328
329         VerticalPanel trees = new VerticalPanel();
330         trees.add(folderTreeView);
331         trees.add(tagTreeView);
332         // Add the left and right panels to the split panel.
333         splitPanel.setLeftWidget(trees);
334         splitPanel.setRightWidget(inner);
335         splitPanel.setSplitPosition("25%");
336         splitPanel.setSize("100%", "100%");
337         splitPanel.addStyleName("pithos-splitPanel");
338
339         // Create a dock panel that will contain the menu bar at the top,
340         // the shortcuts to the left, the status bar at the bottom and the
341         // right panel taking the rest.
342         VerticalPanel outer = new VerticalPanel();
343         outer.add(topPanel);
344         outer.add(messagePanel);
345         outer.add(splitPanel);
346         statusPanel = new StatusPanel(this, Pithos.images);
347         outer.add(statusPanel);
348         outer.setWidth("100%");
349         outer.setCellHorizontalAlignment(messagePanel, HasHorizontalAlignment.ALIGN_CENTER);
350
351         outer.setSpacing(4);
352
353         // Hook the window resize event, so that we can adjust the UI.
354         Window.addResizeHandler(this);
355         // Clear out the window's built-in margin, because we want to take
356         // advantage of the entire client area.
357         Window.setMargin("0px");
358         // Finally, add the outer panel to the RootPanel, so that it will be
359         // displayed.
360         RootPanel.get().add(outer);
361         // Call the window resized handler to get the initial sizes setup. Doing
362         // this in a deferred command causes it to occur after all widgets'
363         // sizes have been computed by the browser.
364         Scheduler.get().scheduleDeferred(new ScheduledCommand() {
365
366             @Override
367             public void execute() {
368                 onWindowResized(Window.getClientHeight());
369             }
370         });
371
372         Scheduler.get().scheduleDeferred(new ScheduledCommand() {
373             @Override
374             public void execute() {
375                 fetchAccount();
376             }
377         });
378     }
379
380     public void showFiles(Folder f) {
381         inner.selectTab(0);
382         if (f.isTrash()) {
383             fileList.showTrash();
384         }
385         else
386             fileList.showFiles();
387         Set<File> files = f.getFiles();
388         showFiles(files);
389     }
390
391     public void showFiles(Set<File> files) {
392         //Iterator<File> iter = files.iterator();
393         //fetchFile(iter, files);
394         fileList.setFiles(new ArrayList<File>(files));
395     }
396
397     private void fetchFile(final Iterator<File> iter, final Set<File> files) {
398         if (iter.hasNext()) {
399             File file = iter.next();
400             String path = file.getUri() + "?format=json";
401             GetRequest<File> getFile = new GetRequest<File>(File.class, getApiPath(), username, path, file) {
402                 @Override
403                 public void onSuccess(File result) {
404                     fetchFile(iter, files);
405                 }
406
407                 @Override
408                 public void onError(Throwable t) {
409                     GWT.log("Error getting file", t);
410                     if (t instanceof RestException)
411                         displayError("Error getting file: " + ((RestException) t).getHttpStatusText());
412                     else
413                         displayError("System error fetching file: " + t.getMessage());
414                 }
415             };
416             getFile.setHeader("X-Auth-Token", "0000");
417             Scheduler.get().scheduleDeferred(getFile);
418         }
419         else
420             fileList.setFiles(new ArrayList<File>(files));
421     }
422
423     /**
424          * Parse and store the user credentials to the appropriate fields.
425          */
426         private boolean parseUserCredentials() {
427                 Configuration conf = (Configuration) GWT.create(Configuration.class);
428                 String cookie = conf.authCookie();
429                 String auth = Cookies.getCookie(cookie);
430                 if (auth == null) {
431                         authenticateUser();
432             return false;
433         }
434         else {
435             String[] authSplit = auth.split("\\" + conf.cookieSeparator(), 2);
436             if (authSplit.length != 2) {
437                 authenticateUser();
438                 return false;
439             }
440             else {
441                 username = authSplit[0];
442                 token = authSplit[1];
443                 return true;
444             }
445         }
446         }
447
448     /**
449          * Redirect the user to the login page for authentication.
450          */
451         protected void authenticateUser() {
452                 Configuration conf = (Configuration) GWT.create(Configuration.class);
453
454 //        Window.Location.assign(GWT.getModuleBaseURL() + conf.loginUrl() + "?next=" + Window.Location.getHref());
455         Cookies.setCookie(conf.authCookie(), "test" + conf.cookieSeparator() + "0000");
456         Window.Location.assign(GWT.getModuleBaseURL() + "pithos.html");
457         }
458
459     private void fetchAccount() {
460         String path = "?format=json";
461
462         GetRequest<AccountResource> getAccount = new GetRequest<AccountResource>(AccountResource.class, getApiPath(), username, path) {
463             @Override
464             public void onSuccess(AccountResource result) {
465                 account = result;
466                 statusPanel.displayStats(account);
467                 inner.selectTab(0);
468                 if (account.getContainers().isEmpty())
469                     createHomeContainers();
470                 else
471                     folderTreeViewModel.initialize(account);
472             }
473
474             @Override
475             public void onError(Throwable t) {
476                 GWT.log("Error getting account", t);
477                 if (t instanceof RestException)
478                     displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
479                 else
480                     displayError("System error fetching user data: " + t.getMessage());
481             }
482         };
483         getAccount.setHeader("X-Auth-Token", token);
484         Scheduler.get().scheduleDeferred(getAccount);
485     }
486
487     private void createHomeContainers() {
488         String path = "/pithos";
489         PutRequest createPithos = new PutRequest(getApiPath(), getUsername(), path) {
490             @Override
491             public void onSuccess(Resource result) {
492                 fetchAccount();
493             }
494
495             @Override
496             public void onError(Throwable t) {
497                 GWT.log("Error creating pithos", t);
498                 if (t instanceof RestException)
499                     displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
500                 else
501                     displayError("System error Error creating pithos: " + t.getMessage());
502             }
503         };
504         createPithos.setHeader("X-Auth-Token", getToken());
505         Scheduler.get().scheduleDeferred(createPithos);
506     }
507
508         /**
509          * Clear the cookie and redirect the user to the logout page.
510          */
511         void logout() {
512                 Configuration conf = (Configuration) GWT.create(Configuration.class);
513                 String cookie = conf.authCookie();
514                 String domain = Window.Location.getHostName();
515                 String path = Window.Location.getPath();
516                 Cookies.setCookie(cookie, "", null, domain, path, false);
517         String baseUrl = GWT.getModuleBaseURL();
518         String homeUrl = baseUrl.substring(0, baseUrl.indexOf(path));
519                 Window.Location.assign(homeUrl + conf.logoutUrl());
520         }
521
522         /**
523          * Creates an HTML fragment that places an image & caption together, for use
524          * in a group header.
525          *
526          * @param imageProto an image prototype for an image
527          * @param caption the group caption
528          * @return the header HTML fragment
529          */
530         private String createHeaderHTML(AbstractImagePrototype imageProto, String caption) {
531                 String captionHTML = "<table class='caption' cellpadding='0' " 
532                 + "cellspacing='0'>" + "<tr><td class='lcaption'>" + imageProto.getHTML() 
533                 + "</td><td id =" + caption +" class='rcaption'><b style='white-space:nowrap'>&nbsp;" 
534                 + caption + "</b></td></tr></table>";
535                 return captionHTML;
536         }
537
538         private void onWindowResized(int height) {
539                 // Adjust the split panel to take up the available room in the window.
540                 int newHeight = height - splitPanel.getAbsoluteTop() - 44;
541                 if (newHeight < 1)
542                         newHeight = 1;
543                 splitPanel.setHeight("" + newHeight);
544                 inner.setHeight("" + newHeight);
545         }
546
547         @Override
548         public void onResize(ResizeEvent event) {
549                 int height = event.getHeight();
550                 onWindowResized(height);
551         }
552
553         public boolean isFileListShowing() {
554                 int tab = inner.getTabBar().getSelectedTab();
555                 if (tab == 0)
556                         return true;
557                 return false;
558         }
559
560         public boolean isSearchResultsShowing() {
561                 int tab = inner.getTabBar().getSelectedTab();
562                 if (tab == 2)
563                         return true;
564                 return false;
565         }
566
567         /**
568          * Make the file list visible.
569          *
570          * @param update
571          */
572         public void showFileList(boolean update) {
573                 if(update){
574                         getTreeView().refreshCurrentNode(true);
575                 }
576                 else{
577                         RestResource currentFolder = getTreeView().getSelection();
578                         if(currentFolder!=null){
579                                 showFileList(currentFolder);
580                 }
581                 }
582
583         }
584         
585         public void showFileList(RestResource r) {
586                 showFileList(r,true);
587         }
588         
589         public void showFileList(RestResource r, boolean clearSelection) {
590                 RestResource currentFolder = r;
591                 if(currentFolder!=null){
592                         List<FileResource> files = null;
593                         if (currentFolder instanceof RestResourceWrapper) {
594                                 RestResourceWrapper folder = (RestResourceWrapper) currentFolder;
595                                 files = folder.getResource().getFiles();
596                         }
597                 }
598                 inner.selectTab(0);
599         }
600
601         /**
602          * Display the 'loading' indicator.
603          */
604         public void showLoadingIndicator(String message, String path) {
605                 if(path!=null){
606                         String[] split = path.split("/");
607                         message = message +" "+URL.decode(split[split.length-1]);
608                 }
609                 topPanel.getLoading().show(message);
610         }
611
612         /**
613          * Hide the 'loading' indicator.
614          */
615         public void hideLoadingIndicator() {
616                 topPanel.getLoading().hide();
617         }
618
619         /**
620          * A native JavaScript method to reach out to the browser's window and
621          * invoke its resizeTo() method.
622          *
623          * @param x the new width
624          * @param y the new height
625          */
626         public static native void resizeTo(int x, int y) /*-{
627                 $wnd.resizeTo(x,y);
628         }-*/;
629
630         /**
631          * A helper method that returns true if the user's list is currently visible
632          * and false if it is hidden.
633          *
634          * @return true if the user list is visible
635          */
636         public boolean isUserListVisible() {
637                 return inner.getTabBar().getSelectedTab() == 1;
638         }
639
640         /**
641          * Display an error message.
642          *
643          * @param msg the message to display
644          */
645         public void displayError(String msg) {
646                 messagePanel.displayError(msg);
647         }
648
649         /**
650          * Display a warning message.
651          *
652          * @param msg the message to display
653          */
654         public void displayWarning(String msg) {
655                 messagePanel.displayWarning(msg);
656         }
657
658         /**
659          * Display an informational message.
660          *
661          * @param msg the message to display
662          */
663         public void displayInformation(String msg) {
664                 messagePanel.displayInformation(msg);
665         }
666
667         /**
668          * Retrieve the folders.
669          *
670          * @return the folders
671          
672         public Folders getFolders() {
673                 return folders;
674         }*/
675
676         /**
677          * Retrieve the currentSelection.
678          *
679          * @return the currentSelection
680          */
681         public Object getCurrentSelection() {
682                 return currentSelection;
683         }
684
685         /**
686          * Modify the currentSelection.
687          *
688          * @param newCurrentSelection the currentSelection to set
689          */
690         public void setCurrentSelection(Object newCurrentSelection) {
691                 currentSelection = newCurrentSelection;
692         }
693
694         /**
695          * Retrieve the fileList.
696          *
697          * @return the fileList
698          */
699         public FileList getFileList() {
700                 return fileList;
701         }
702
703         /**
704          * Retrieve the topPanel.
705          *
706          * @return the topPanel
707          */
708         TopPanel getTopPanel() {
709                 return topPanel;
710         }
711
712         /**
713          * Retrieve the clipboard.
714          *
715          * @return the clipboard
716          */
717         public Clipboard getClipboard() {
718                 return clipboard;
719         }
720
721         public StatusPanel getStatusPanel() {
722                 return statusPanel;
723         }
724
725         /**
726          * Retrieve the userDetailsPanel.
727          *
728          * @return the userDetailsPanel
729          */
730         public UserDetailsPanel getUserDetailsPanel() {
731                 return userDetailsPanel;
732         }
733
734         
735
736         public String getToken() {
737                 return token;
738         }
739
740         public String getWebDAVPassword() {
741                 return webDAVPassword;
742         }
743
744         /**
745          * Retrieve the currentUserResource.
746          *
747          * @return the currentUserResource
748          */
749         public UserResource getCurrentUserResource() {
750                 return currentUserResource;
751         }
752
753         /**
754          * Modify the currentUserResource.
755          *
756          * @param newUser the new currentUserResource
757          */
758         public void setCurrentUserResource(UserResource newUser) {
759                 currentUserResource = newUser;
760         }
761
762         public static native void preventIESelection() /*-{
763                 $doc.body.onselectstart = function () { return false; };
764         }-*/;
765
766         public static native void enableIESelection() /*-{
767                 if ($doc.body.onselectstart != null)
768                 $doc.body.onselectstart = null;
769         }-*/;
770
771         /**
772          * @return the absolute path of the API root URL
773          */
774         public String getApiPath() {
775                 Configuration conf = (Configuration) GWT.create(Configuration.class);
776                 return conf.apiPath();
777         }
778
779         /**
780          * Convert server date to local time according to browser timezone
781          * and format it according to localized pattern.
782          * Time is always formatted to 24hr format.
783          * NB: This assumes that server runs in UTC timezone. Otherwise
784          * we would need to adjust for server time offset as well.
785          *
786          * @param date
787          * @return String
788          */
789         public static String formatLocalDateTime(Date date) {
790                 Date convertedDate = new Date(date.getTime() - date.getTimezoneOffset());
791                 final DateTimeFormat dateFormatter = DateTimeFormat.getShortDateFormat();
792                 final DateTimeFormat timeFormatter = DateTimeFormat.getFormat("HH:mm");
793                 String datePart = dateFormatter.format(convertedDate);
794                 String timePart = timeFormatter.format(convertedDate);
795                 return datePart + " " + timePart;
796         }
797         
798         /**
799          * History support for folder navigation
800          * adds a new browser history entry
801          *
802          * @param key
803          */
804         public void updateHistory(String key){
805 //              Replace any whitespace of the initial string to "+"
806 //              String result = key.replaceAll("\\s","+");
807 //              Add a new browser history entry.
808 //              History.newItem(result);
809                 History.newItem(key);
810         }
811
812         /**
813          * This method examines the token input and add a "/" at the end in case it's omitted.
814          * This happens only in Files/trash/, Files/shared/, Files/others.
815          *
816          * @param tokenInput
817          * @return the formated token with a "/" at the end or the same tokenInput parameter
818          */
819
820         private String handleSpecialFolderNames(String tokenInput){
821                 List<String> pathsToCheck = Arrays.asList("Files/trash", "Files/shared", "Files/others");
822                 if(pathsToCheck.contains(tokenInput))
823                         return tokenInput + "/";
824                 return tokenInput;
825
826         }
827
828         /**
829          * Reject illegal resource names, like '.' or '..' or slashes '/'.
830          */
831         static boolean isValidResourceName(String name) {
832                 if (".".equals(name) || "..".equals(name) || name.contains("/"))
833                         return false;
834                 return true;
835         }
836
837         public void putUserToMap(String _userName, String _userFullName){
838                 userFullNameMap.put(_userName, _userFullName);
839         }
840
841         public String findUserFullName(String _userName){
842                 return userFullNameMap.get(_userName);
843         }
844
845         /**
846          * Retrieve the treeView.
847          *
848          * @return the treeView
849          */
850         public CellTreeView getTreeView() {
851                 return treeView;
852         }
853         
854         public void onResourceUpdate(RestResource resource,boolean clearSelection){
855                 if(resource instanceof RestResourceWrapper || resource instanceof OtherUserResource || resource instanceof TrashResource || resource instanceof SharedResource){
856                         if(getTreeView().getSelection()!=null&&getTreeView().getSelection().getUri().equals(resource.getUri()))
857                                 showFileList(resource,clearSelection);
858                 }
859                 
860         }
861
862     public void deleteFolder(final Folder folder) {
863         String path = getApiPath() + getUsername() + "/" + folder.getContainer() + "?format=json&delimiter=/&prefix=" + folder.getPrefix();
864         RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, path);
865         builder.setHeader("If-Modified-Since", "0");
866         builder.setHeader("X-Auth-Token", getToken());
867         try {
868             builder.sendRequest("", new RequestCallback() {
869                 @Override
870                 public void onResponseReceived(Request request, Response response) {
871                     if (response.getStatusCode() == Response.SC_OK) {
872                         JSONValue json = JSONParser.parseStrict(response.getText());
873                         JSONArray array = json.isArray();
874                         int i = 0;
875                         if (array != null) {
876                             deleteObject(folder, i, array);
877                         }
878                     }
879                 }
880
881                 @Override
882                 public void onError(Request request, Throwable exception) {
883                     displayError("System error unable to delete folder: " + exception.getMessage());
884                 }
885             });
886         }
887         catch (RequestException e) {
888         }
889     }
890
891     public void deleteObject(final Folder folder, final int i, final JSONArray array) {
892         if (i < array.size()) {
893             JSONObject o = array.get(i).isObject();
894             if (o != null && !o.containsKey("subdir")) {
895                 JSONString name = o.get("name").isString();
896                 String path = "/" + folder.getContainer() + "/" + name.stringValue();
897                 DeleteRequest delete = new DeleteRequest(getApiPath(), getUsername(), path) {
898                     @Override
899                     public void onSuccess(Resource result) {
900                         deleteObject(folder, i + 1, array);
901                     }
902
903                     @Override
904                     public void onError(Throwable t) {
905                         GWT.log("", t);
906                         displayError("System error unable to delete folder: " + t.getMessage());
907                     }
908                 };
909                 delete.setHeader("X-Auth-Token", getToken());
910                 Scheduler.get().scheduleDeferred(delete);
911             }
912             else {
913                 String subdir = o.get("subdir").isString().stringValue();
914                 subdir = subdir.substring(0, subdir.length() - 1);
915                 String path = getApiPath() + getUsername() + "/" + folder.getContainer() + "?format=json&delimiter=/&prefix=" + subdir;
916                 RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, path);
917                 builder.setHeader("If-Modified-Since", "0");
918                 builder.setHeader("X-Auth-Token", getToken());
919                 try {
920                     builder.sendRequest("", new RequestCallback() {
921                         @Override
922                         public void onResponseReceived(Request request, Response response) {
923                             if (response.getStatusCode() == Response.SC_OK) {
924                                 JSONValue json = JSONParser.parseStrict(response.getText());
925                                 JSONArray array2 = json.isArray();
926                                 if (array2 != null) {
927                                     int l = array.size();
928                                     for (int j=0; j<array2.size(); j++) {
929                                         array.set(l++, array2.get(j));
930                                     }
931                                 }
932                                 deleteObject(folder, i + 1, array);
933                             }
934                         }
935
936                         @Override
937                         public void onError(Request request, Throwable exception) {
938                             displayError("System error unable to delete folder: " + exception.getMessage());
939                         }
940                     });
941                 }
942                 catch (RequestException e) {
943                 }
944             }
945         }
946         else {
947             String path = folder.getUri();
948             DeleteRequest deleteFolder = new DeleteRequest(getApiPath(), getUsername(), path) {
949                 @Override
950                 public void onSuccess(Resource result) {
951                     updateFolder(folder.getParent(), true);
952                 }
953
954                 @Override
955                 public void onError(Throwable t) {
956                     GWT.log("", t);
957                     if (t instanceof RestException) {
958                         displayError("Unable to delete folder: "+((RestException) t).getHttpStatusText());
959                     }
960                     else
961                         displayError("System error unable to delete folder: " + t.getMessage());
962                 }
963             };
964             deleteFolder.setHeader("X-Auth-Token", getToken());
965             Scheduler.get().scheduleDeferred(deleteFolder);
966         }
967     }
968
969     public FolderTreeView getFolderTreeView() {
970         return folderTreeView;
971     }
972
973     public void copyFiles(final Iterator<File> iter, final String targetUri, final Command callback) {
974         if (iter.hasNext()) {
975             File file = iter.next();
976             String path = targetUri + "/" + file.getName();
977             PutRequest copyFile = new PutRequest(getApiPath(), getUsername(), path) {
978                 @Override
979                 public void onSuccess(Resource result) {
980                     copyFiles(iter, targetUri, callback);
981                 }
982
983                 @Override
984                 public void onError(Throwable t) {
985                     GWT.log("", t);
986                     if (t instanceof RestException) {
987                         displayError("Unable to copy file: " + ((RestException) t).getHttpStatusText());
988                     }
989                     else
990                         displayError("System error unable to copy file: "+t.getMessage());
991                 }
992             };
993             copyFile.setHeader("X-Auth-Token", getToken());
994             copyFile.setHeader("X-Copy-From", file.getUri());
995             Scheduler.get().scheduleDeferred(copyFile);
996         }
997         else  if (callback != null) {
998             callback.execute();
999         }
1000     }
1001
1002     public void copySubfolders(final Iterator<Folder> iter, final String targetUri, final Command callback) {
1003         if (iter.hasNext()) {
1004             final Folder f = iter.next();
1005             copyFolder(f, targetUri, callback);
1006         }
1007         else  if (callback != null) {
1008             callback.execute();
1009         }
1010     }
1011
1012     public void copyFolder(final Folder f, final String targetUri, final Command callback) {
1013         String path = targetUri + "/" + f.getName();
1014         PutRequest createFolder = new PutRequest(getApiPath(), getUsername(), path) {
1015             @Override
1016             public void onSuccess(Resource result) {
1017                 Iterator<File> iter = f.getFiles().iterator();
1018                 copyFiles(iter, targetUri + "/" + f.getName(), new Command() {
1019                     @Override
1020                     public void execute() {
1021                         Iterator<Folder> iterf = f.getSubfolders().iterator();
1022                         copySubfolders(iterf, targetUri + "/" + f.getName(), new Command() {
1023                             @Override
1024                             public void execute() {
1025                                 callback.execute();
1026                             }
1027                         });
1028                     }
1029                 });
1030             }
1031
1032             @Override
1033             public void onError(Throwable t) {
1034                 GWT.log("", t);
1035                 if (t instanceof RestException) {
1036                     displayError("Unable to create folder:" + ((RestException) t).getHttpStatusText());
1037                 }
1038                 else
1039                     displayError("System error creating folder:" + t.getMessage());
1040             }
1041         };
1042         createFolder.setHeader("X-Auth-Token", getToken());
1043         createFolder.setHeader("Accept", "*/*");
1044         createFolder.setHeader("Content-Length", "0");
1045         createFolder.setHeader("Content-Type", "application/folder");
1046         Scheduler.get().scheduleDeferred(createFolder);
1047     }
1048 }