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