Initialization code for tag views
[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                 Folder f = folderTreeSelectionModel.getSelectedObject();
310                 updateFolder(f);
311             }
312         });
313
314         folderTreeViewModel = new FolderTreeViewModel(folderTreeSelectionModel);
315         folderTreeView = new FolderTreeView(folderTreeViewModel);
316
317         fileList = new FileList(images, folderTreeView);
318         inner.add(fileList, createHeaderHTML(AbstractImagePrototype.create(images.folders()), "Files"), true);
319
320         tagTreeSelectionModel = new SingleSelectionModel<Tag>();
321         tagTreeSelectionModel.addSelectionChangeHandler(new Handler() {
322             @Override
323             public void onSelectionChange(SelectionChangeEvent event) {
324                 Tag t = tagTreeSelectionModel.getSelectedObject();
325                 updateTag(t);
326             }
327         });
328         tagTreeViewModel = new TagTreeViewModel(tagTreeSelectionModel);
329         tagTreeView = new TagTreeView(tagTreeViewModel);
330
331         VerticalPanel trees = new VerticalPanel();
332         trees.add(folderTreeView);
333         trees.add(tagTreeView);
334         // Add the left and right panels to the split panel.
335         splitPanel.setLeftWidget(trees);
336         splitPanel.setRightWidget(inner);
337         splitPanel.setSplitPosition("25%");
338         splitPanel.setSize("100%", "100%");
339         splitPanel.addStyleName("pithos-splitPanel");
340
341         // Create a dock panel that will contain the menu bar at the top,
342         // the shortcuts to the left, the status bar at the bottom and the
343         // right panel taking the rest.
344         VerticalPanel outer = new VerticalPanel();
345         outer.add(topPanel);
346         outer.add(messagePanel);
347         outer.add(splitPanel);
348         statusPanel = new StatusPanel(Pithos.images);
349         outer.add(statusPanel);
350         outer.setWidth("100%");
351         outer.setCellHorizontalAlignment(messagePanel, HasHorizontalAlignment.ALIGN_CENTER);
352
353         outer.setSpacing(4);
354
355         // Hook the window resize event, so that we can adjust the UI.
356         Window.addResizeHandler(this);
357         // Clear out the window's built-in margin, because we want to take
358         // advantage of the entire client area.
359         Window.setMargin("0px");
360         // Finally, add the outer panel to the RootPanel, so that it will be
361         // displayed.
362         RootPanel.get().add(outer);
363         // Call the window resized handler to get the initial sizes setup. Doing
364         // this in a deferred command causes it to occur after all widgets'
365         // sizes have been computed by the browser.
366         Scheduler.get().scheduleDeferred(new ScheduledCommand() {
367
368             @Override
369             public void execute() {
370                 onWindowResized(Window.getClientHeight());
371             }
372         });
373
374         Scheduler.get().scheduleDeferred(new ScheduledCommand() {
375             @Override
376             public void execute() {
377                 fetchAccount();
378             }
379         });
380     }
381
382     public void showFiles(Folder f) {
383         inner.selectTab(0);
384         if (f.isTrash()) {
385             fileList.showTrash();
386         }
387         else
388             fileList.showFiles();
389         Set<File> files = f.getFiles();
390         Iterator<File> iter = files.iterator();
391         fetchFile(iter, files);
392     }
393
394     private void fetchFile(final Iterator<File> iter, final Set<File> files) {
395         if (iter.hasNext()) {
396             File file = iter.next();
397             String path = getApiPath() + username + "/" + file.getContainer() + "/" + file.getPath() + "?format=json";
398             GetRequest<File> getFile = new GetRequest<File>(File.class, path, file) {
399                 @Override
400                 public void onSuccess(File result) {
401                     fetchFile(iter, files);
402                 }
403
404                 @Override
405                 public void onError(Throwable t) {
406                     GWT.log("Error getting file", t);
407                     if (t instanceof RestException)
408                         Pithos.get().displayError("Error getting file: " + ((RestException) t).getHttpStatusText());
409                     else
410                         Pithos.get().displayError("System error fetching file: " + t.getMessage());
411                 }
412             };
413             getFile.setHeader("X-Auth-Token", "0000");
414             Scheduler.get().scheduleDeferred(getFile);
415         }
416         else
417             fileList.setFiles(new ArrayList<File>(files));
418     }
419
420     /**
421          * Parse and store the user credentials to the appropriate fields.
422          */
423         private boolean parseUserCredentials() {
424                 Configuration conf = (Configuration) GWT.create(Configuration.class);
425                 String cookie = conf.authCookie();
426                 String auth = Cookies.getCookie(cookie);
427                 if (auth == null) {
428                         authenticateUser();
429             return false;
430         }
431         else {
432             String[] authSplit = auth.split("\\" + conf.cookieSeparator(), 2);
433             if (authSplit.length != 2) {
434                 authenticateUser();
435                 return false;
436             }
437             else {
438                 username = authSplit[0];
439                 token = authSplit[1];
440                 return true;
441             }
442         }
443         }
444
445     /**
446          * Redirect the user to the login page for authentication.
447          */
448         protected void authenticateUser() {
449                 Configuration conf = (Configuration) GWT.create(Configuration.class);
450
451 //        Window.Location.assign(GWT.getModuleBaseURL() + conf.loginUrl() + "?next=" + Window.Location.getHref());
452         Cookies.setCookie(conf.authCookie(), "test" + conf.cookieSeparator() + "0000");
453         Window.Location.assign(GWT.getModuleBaseURL() + "Pithos.html");
454         }
455
456     private void fetchAccount() {
457         String path = getApiPath() + username + "?format=json";
458
459         GetRequest<AccountResource> getAccount = new GetRequest<AccountResource>(AccountResource.class, path) {
460             @Override
461             public void onSuccess(AccountResource result) {
462                 account = result;
463                 statusPanel.displayStats(account);
464                 folderTreeViewModel.initialize(account);
465                 inner.selectTab(0);
466             }
467
468             @Override
469             public void onError(Throwable t) {
470                 GWT.log("Error getting account", t);
471                 if (t instanceof RestException)
472                     Pithos.get().displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
473                 else
474                     Pithos.get().displayError("System error fetching user data: " + t.getMessage());
475             }
476         };
477         getAccount.setHeader("X-Auth-Token", token);
478         Scheduler.get().scheduleDeferred(getAccount);
479     }
480
481         /**
482          * Clear the cookie and redirect the user to the logout page.
483          */
484         void logout() {
485                 Configuration conf = (Configuration) GWT.create(Configuration.class);
486                 String cookie = conf.authCookie();
487                 String domain = Window.Location.getHostName();
488                 String path = Window.Location.getPath();
489                 Cookies.setCookie(cookie, "", null, domain, path, false);
490         String baseUrl = GWT.getModuleBaseURL();
491         String homeUrl = baseUrl.substring(0, baseUrl.indexOf(path));
492                 Window.Location.assign(homeUrl + conf.logoutUrl());
493         }
494
495         /**
496          * Creates an HTML fragment that places an image & caption together, for use
497          * in a group header.
498          *
499          * @param imageProto an image prototype for an image
500          * @param caption the group caption
501          * @return the header HTML fragment
502          */
503         private String createHeaderHTML(AbstractImagePrototype imageProto, String caption) {
504                 String captionHTML = "<table class='caption' cellpadding='0' " 
505                 + "cellspacing='0'>" + "<tr><td class='lcaption'>" + imageProto.getHTML() 
506                 + "</td><td id =" + caption +" class='rcaption'><b style='white-space:nowrap'>&nbsp;" 
507                 + caption + "</b></td></tr></table>";
508                 return captionHTML;
509         }
510
511         private void onWindowResized(int height) {
512                 // Adjust the split panel to take up the available room in the window.
513                 int newHeight = height - splitPanel.getAbsoluteTop() - 44;
514                 if (newHeight < 1)
515                         newHeight = 1;
516                 splitPanel.setHeight("" + newHeight);
517                 inner.setHeight("" + newHeight);
518         }
519
520         @Override
521         public void onResize(ResizeEvent event) {
522                 int height = event.getHeight();
523                 onWindowResized(height);
524         }
525
526         public boolean isFileListShowing() {
527                 int tab = inner.getTabBar().getSelectedTab();
528                 if (tab == 0)
529                         return true;
530                 return false;
531         }
532
533         public boolean isSearchResultsShowing() {
534                 int tab = inner.getTabBar().getSelectedTab();
535                 if (tab == 2)
536                         return true;
537                 return false;
538         }
539
540         /**
541          * Make the file list visible.
542          *
543          * @param update
544          */
545         public void showFileList(boolean update) {
546                 if(update){
547                         getTreeView().refreshCurrentNode(true);
548                 }
549                 else{
550                         RestResource currentFolder = getTreeView().getSelection();
551                         if(currentFolder!=null){
552                                 showFileList(currentFolder);
553                 }
554                 }
555
556         }
557         
558         public void showFileList(RestResource r) {
559                 showFileList(r,true);
560         }
561         
562         public void showFileList(RestResource r, boolean clearSelection) {
563                 RestResource currentFolder = r;
564                 if(currentFolder!=null){
565                         List<FileResource> files = null;
566                         if (currentFolder instanceof RestResourceWrapper) {
567                                 RestResourceWrapper folder = (RestResourceWrapper) currentFolder;
568                                 files = folder.getResource().getFiles();
569                         }
570                 }
571                 inner.selectTab(0);
572         }
573
574         /**
575          * Display the 'loading' indicator.
576          */
577         public void showLoadingIndicator(String message, String path) {
578                 if(path!=null){
579                         String[] split = path.split("/");
580                         message = message +" "+URL.decode(split[split.length-1]);
581                 }
582                 topPanel.getLoading().show(message);
583         }
584
585         /**
586          * Hide the 'loading' indicator.
587          */
588         public void hideLoadingIndicator() {
589                 topPanel.getLoading().hide();
590         }
591
592         /**
593          * A native JavaScript method to reach out to the browser's window and
594          * invoke its resizeTo() method.
595          *
596          * @param x the new width
597          * @param y the new height
598          */
599         public static native void resizeTo(int x, int y) /*-{
600                 $wnd.resizeTo(x,y);
601         }-*/;
602
603         /**
604          * A helper method that returns true if the user's list is currently visible
605          * and false if it is hidden.
606          *
607          * @return true if the user list is visible
608          */
609         public boolean isUserListVisible() {
610                 return inner.getTabBar().getSelectedTab() == 1;
611         }
612
613         /**
614          * Display an error message.
615          *
616          * @param msg the message to display
617          */
618         public void displayError(String msg) {
619                 messagePanel.displayError(msg);
620         }
621
622         /**
623          * Display a warning message.
624          *
625          * @param msg the message to display
626          */
627         public void displayWarning(String msg) {
628                 messagePanel.displayWarning(msg);
629         }
630
631         /**
632          * Display an informational message.
633          *
634          * @param msg the message to display
635          */
636         public void displayInformation(String msg) {
637                 messagePanel.displayInformation(msg);
638         }
639
640         /**
641          * Retrieve the folders.
642          *
643          * @return the folders
644          
645         public Folders getFolders() {
646                 return folders;
647         }*/
648
649         /**
650          * Retrieve the currentSelection.
651          *
652          * @return the currentSelection
653          */
654         public Object getCurrentSelection() {
655                 return currentSelection;
656         }
657
658         /**
659          * Modify the currentSelection.
660          *
661          * @param newCurrentSelection the currentSelection to set
662          */
663         public void setCurrentSelection(Object newCurrentSelection) {
664                 currentSelection = newCurrentSelection;
665         }
666
667         /**
668          * Retrieve the fileList.
669          *
670          * @return the fileList
671          */
672         public FileList getFileList() {
673                 return fileList;
674         }
675
676         /**
677          * Retrieve the topPanel.
678          *
679          * @return the topPanel
680          */
681         TopPanel getTopPanel() {
682                 return topPanel;
683         }
684
685         /**
686          * Retrieve the clipboard.
687          *
688          * @return the clipboard
689          */
690         public Clipboard getClipboard() {
691                 return clipboard;
692         }
693
694         public StatusPanel getStatusPanel() {
695                 return statusPanel;
696         }
697
698         /**
699          * Retrieve the userDetailsPanel.
700          *
701          * @return the userDetailsPanel
702          */
703         public UserDetailsPanel getUserDetailsPanel() {
704                 return userDetailsPanel;
705         }
706
707         
708
709         public String getToken() {
710                 return token;
711         }
712
713         public String getWebDAVPassword() {
714                 return webDAVPassword;
715         }
716
717         /**
718          * Retrieve the currentUserResource.
719          *
720          * @return the currentUserResource
721          */
722         public UserResource getCurrentUserResource() {
723                 return currentUserResource;
724         }
725
726         /**
727          * Modify the currentUserResource.
728          *
729          * @param newUser the new currentUserResource
730          */
731         public void setCurrentUserResource(UserResource newUser) {
732                 currentUserResource = newUser;
733         }
734
735         public static native void preventIESelection() /*-{
736                 $doc.body.onselectstart = function () { return false; };
737         }-*/;
738
739         public static native void enableIESelection() /*-{
740                 if ($doc.body.onselectstart != null)
741                 $doc.body.onselectstart = null;
742         }-*/;
743
744         /**
745          * @return the absolute path of the API root URL
746          */
747         public String getApiPath() {
748                 Configuration conf = (Configuration) GWT.create(Configuration.class);
749                 return conf.apiPath();
750         }
751
752         /**
753          * Convert server date to local time according to browser timezone
754          * and format it according to localized pattern.
755          * Time is always formatted to 24hr format.
756          * NB: This assumes that server runs in UTC timezone. Otherwise
757          * we would need to adjust for server time offset as well.
758          *
759          * @param date
760          * @return String
761          */
762         public static String formatLocalDateTime(Date date) {
763                 Date convertedDate = new Date(date.getTime() - date.getTimezoneOffset());
764                 final DateTimeFormat dateFormatter = DateTimeFormat.getShortDateFormat();
765                 final DateTimeFormat timeFormatter = DateTimeFormat.getFormat("HH:mm");
766                 String datePart = dateFormatter.format(convertedDate);
767                 String timePart = timeFormatter.format(convertedDate);
768                 return datePart + " " + timePart;
769         }
770         
771         /**
772          * History support for folder navigation
773          * adds a new browser history entry
774          *
775          * @param key
776          */
777         public void updateHistory(String key){
778 //              Replace any whitespace of the initial string to "+"
779 //              String result = key.replaceAll("\\s","+");
780 //              Add a new browser history entry.
781 //              History.newItem(result);
782                 History.newItem(key);
783         }
784
785         /**
786          * This method examines the token input and add a "/" at the end in case it's omitted.
787          * This happens only in Files/trash/, Files/shared/, Files/others.
788          *
789          * @param tokenInput
790          * @return the formated token with a "/" at the end or the same tokenInput parameter
791          */
792
793         private String handleSpecialFolderNames(String tokenInput){
794                 List<String> pathsToCheck = Arrays.asList("Files/trash", "Files/shared", "Files/others");
795                 if(pathsToCheck.contains(tokenInput))
796                         return tokenInput + "/";
797                 return tokenInput;
798
799         }
800
801         /**
802          * Reject illegal resource names, like '.' or '..' or slashes '/'.
803          */
804         static boolean isValidResourceName(String name) {
805                 if (".".equals(name) || "..".equals(name) || name.contains("/"))
806                         return false;
807                 return true;
808         }
809
810         public void putUserToMap(String _userName, String _userFullName){
811                 userFullNameMap.put(_userName, _userFullName);
812         }
813
814         public String findUserFullName(String _userName){
815                 return userFullNameMap.get(_userName);
816         }
817         public String getUserFullName(String _userName) {
818                 
819         if (Pithos.get().findUserFullName(_userName) == null)
820                 //if there is no userFullName found then the map fills with the given _userName,
821                 //so userFullName = _userName
822                 Pithos.get().putUserToMap(_userName, _userName);
823         else if(Pithos.get().findUserFullName(_userName).indexOf('@') != -1){
824                 //if the userFullName = _userName the GetUserCommand updates the userFullName in the map
825                 GetUserCommand guc = new GetUserCommand(_userName);
826                 guc.execute();
827         }
828         return Pithos.get().findUserFullName(_userName);
829         }
830         /**
831          * Retrieve the treeView.
832          *
833          * @return the treeView
834          */
835         public CellTreeView getTreeView() {
836                 return treeView;
837         }
838         
839         public void onResourceUpdate(RestResource resource,boolean clearSelection){
840                 if(resource instanceof RestResourceWrapper || resource instanceof OtherUserResource || resource instanceof TrashResource || resource instanceof SharedResource){
841                         if(getTreeView().getSelection()!=null&&getTreeView().getSelection().getUri().equals(resource.getUri()))
842                                 showFileList(resource,clearSelection);
843                 }
844                 
845         }
846
847     public void deleteFolder(final Folder folder) {
848         String path = getApiPath() + getUsername() + "/" + folder.getContainer() + "?format=json&delimiter=/&prefix=" + folder.getPrefix();
849         RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, path);
850         builder.setHeader("If-Modified-Since", "0");
851         builder.setHeader("X-Auth-Token", getToken());
852         try {
853             builder.sendRequest("", new RequestCallback() {
854                 @Override
855                 public void onResponseReceived(Request request, Response response) {
856                     if (response.getStatusCode() == Response.SC_OK) {
857                         JSONValue json = JSONParser.parseStrict(response.getText());
858                         JSONArray array = json.isArray();
859                         int i = 0;
860                         if (array != null) {
861                             deleteObject(folder, i, array);
862                         }
863                     }
864                 }
865
866                 @Override
867                 public void onError(Request request, Throwable exception) {
868                     Pithos.get().displayError("System error unable to delete folder: " + exception.getMessage());
869                 }
870             });
871         }
872         catch (RequestException e) {
873         }
874     }
875
876     public void deleteObject(final Folder folder, final int i, final JSONArray array) {
877         if (i < array.size()) {
878             JSONObject o = array.get(i).isObject();
879             if (o != null && !o.containsKey("subdir")) {
880                 JSONString name = o.get("name").isString();
881                 String path = getApiPath() + getUsername() + "/" + folder.getContainer() + "/" + name.stringValue();
882                 DeleteRequest delete = new DeleteRequest(path) {
883                     @Override
884                     public void onSuccess(Resource result) {
885                         deleteObject(folder, i + 1, array);
886                     }
887
888                     @Override
889                     public void onError(Throwable t) {
890                         GWT.log("", t);
891                         Pithos.get().displayError("System error unable to delete folder: " + t.getMessage());
892                     }
893                 };
894                 delete.setHeader("X-Auth-Token", getToken());
895                 Scheduler.get().scheduleDeferred(delete);
896             }
897             else {
898                 String subdir = o.get("subdir").isString().stringValue();
899                 subdir = subdir.substring(0, subdir.length() - 1);
900                 String path = getApiPath() + getUsername() + "/" + folder.getContainer() + "?format=json&delimiter=/&prefix=" + subdir;
901                 RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, path);
902                 builder.setHeader("If-Modified-Since", "0");
903                 builder.setHeader("X-Auth-Token", getToken());
904                 try {
905                     builder.sendRequest("", new RequestCallback() {
906                         @Override
907                         public void onResponseReceived(Request request, Response response) {
908                             if (response.getStatusCode() == Response.SC_OK) {
909                                 JSONValue json = JSONParser.parseStrict(response.getText());
910                                 JSONArray array2 = json.isArray();
911                                 if (array2 != null) {
912                                     int l = array.size();
913                                     for (int j=0; j<array2.size(); j++) {
914                                         array.set(l++, array2.get(j));
915                                     }
916                                 }
917                                 deleteObject(folder, i + 1, array);
918                             }
919                         }
920
921                         @Override
922                         public void onError(Request request, Throwable exception) {
923                             Pithos.get().displayError("System error unable to delete folder: " + exception.getMessage());
924                         }
925                     });
926                 }
927                 catch (RequestException e) {
928                 }
929             }
930         }
931         else {
932             String prefix = folder.getPrefix();
933             String path = getApiPath() + getUsername() + "/" + folder.getContainer() + (prefix.length() == 0 ? "" : "/" + prefix);
934             DeleteRequest deleteFolder = new DeleteRequest(path) {
935                 @Override
936                 public void onSuccess(Resource result) {
937                     updateFolder(folder.getParent());
938                 }
939
940                 @Override
941                 public void onError(Throwable t) {
942                     GWT.log("", t);
943                     if (t instanceof RestException) {
944                         displayError("Unable to delete folder: "+((RestException) t).getHttpStatusText());
945                     }
946                     else
947                         Pithos.get().displayError("System error unable to delete folder: " + t.getMessage());
948                 }
949             };
950             deleteFolder.setHeader("X-Auth-Token", getToken());
951             Scheduler.get().scheduleDeferred(deleteFolder);
952         }
953     }
954
955     public FolderTreeView getFolderTreeView() {
956         return folderTreeView;
957     }
958
959     public void copyFiles(final Iterator<File> iter, final String targetUri, final Command callback) {
960         if (iter.hasNext()) {
961             File file = iter.next();
962             String path = getApiPath() + getUsername() + targetUri + "/" + file.getName();
963             PutRequest copyFile = new PutRequest(path) {
964                 @Override
965                 public void onSuccess(Resource result) {
966                     copyFiles(iter, targetUri, callback);
967                 }
968
969                 @Override
970                 public void onError(Throwable t) {
971                     GWT.log("", t);
972                     if (t instanceof RestException) {
973                         Pithos.get().displayError("Unable to copy file: " + ((RestException) t).getHttpStatusText());
974                     }
975                     else
976                         Pithos.get().displayError("System error unable to copy file: "+t.getMessage());
977                 }
978             };
979             copyFile.setHeader("X-Auth-Token", getToken());
980             copyFile.setHeader("X-Copy-From", file.getUri());
981             Scheduler.get().scheduleDeferred(copyFile);
982         }
983         else  if (callback != null) {
984             callback.execute();
985         }
986     }
987
988     public void copySubfolders(final Iterator<Folder> iter, final String targetUri, final Command callback) {
989         if (iter.hasNext()) {
990             final Folder f = iter.next();
991             copyFolder(f, targetUri, callback);
992         }
993         else  if (callback != null) {
994             callback.execute();
995         }
996     }
997
998     public void copyFolder(final Folder f, final String targetUri, final Command callback) {
999         String path = getApiPath() + getUsername() + targetUri + "/" + f.getName();
1000         PutRequest createFolder = new PutRequest(path) {
1001             @Override
1002             public void onSuccess(Resource result) {
1003                 Iterator<File> iter = f.getFiles().iterator();
1004                 copyFiles(iter, targetUri + "/" + f.getName(), new Command() {
1005                     @Override
1006                     public void execute() {
1007                         Iterator<Folder> iterf = f.getSubfolders().iterator();
1008                         copySubfolders(iterf, targetUri + "/" + f.getName(), new Command() {
1009                             @Override
1010                             public void execute() {
1011                                 callback.execute();
1012                             }
1013                         });
1014                     }
1015                 });
1016             }
1017
1018             @Override
1019             public void onError(Throwable t) {
1020                 GWT.log("", t);
1021                 if (t instanceof RestException) {
1022                     displayError("Unable to create folder:" + ((RestException) t).getHttpStatusText());
1023                 }
1024                 else
1025                     displayError("System error creating folder:" + t.getMessage());
1026             }
1027         };
1028         createFolder.setHeader("X-Auth-Token", getToken());
1029         createFolder.setHeader("Accept", "*/*");
1030         createFolder.setHeader("Content-Length", "0");
1031         createFolder.setHeader("Content-Type", "application/folder");
1032         Scheduler.get().scheduleDeferred(createFolder);
1033     }
1034 }