2 * Copyright 2011-2012 GRNET S.A. All rights reserved.
4 * Redistribution and use in source and binary forms, with or
5 * without modification, are permitted provided that the following
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
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.
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.
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.
35 package gr.grnet.pithos.web.client;
37 import gr.grnet.pithos.web.client.commands.UploadFileCommand;
38 import gr.grnet.pithos.web.client.foldertree.AccountResource;
39 import gr.grnet.pithos.web.client.foldertree.File;
40 import gr.grnet.pithos.web.client.foldertree.Folder;
41 import gr.grnet.pithos.web.client.foldertree.FolderTreeView;
42 import gr.grnet.pithos.web.client.foldertree.FolderTreeViewModel;
43 import gr.grnet.pithos.web.client.foldertree.Resource;
44 import gr.grnet.pithos.web.client.grouptree.Group;
45 import gr.grnet.pithos.web.client.grouptree.GroupTreeView;
46 import gr.grnet.pithos.web.client.grouptree.GroupTreeViewModel;
47 import gr.grnet.pithos.web.client.mysharedtree.MysharedTreeView;
48 import gr.grnet.pithos.web.client.mysharedtree.MysharedTreeViewModel;
49 import gr.grnet.pithos.web.client.othersharedtree.OtherSharedTreeView;
50 import gr.grnet.pithos.web.client.othersharedtree.OtherSharedTreeViewModel;
51 import gr.grnet.pithos.web.client.rest.DeleteRequest;
52 import gr.grnet.pithos.web.client.rest.GetRequest;
53 import gr.grnet.pithos.web.client.rest.HeadRequest;
54 import gr.grnet.pithos.web.client.rest.PutRequest;
55 import gr.grnet.pithos.web.client.rest.RestException;
57 import java.util.ArrayList;
58 import java.util.Date;
59 import java.util.HashMap;
60 import java.util.Iterator;
61 import java.util.List;
64 import org.apache.http.HttpStatus;
66 import com.google.gwt.core.client.EntryPoint;
67 import com.google.gwt.core.client.GWT;
68 import com.google.gwt.core.client.JsArrayString;
69 import com.google.gwt.core.client.Scheduler;
70 import com.google.gwt.core.client.Scheduler.RepeatingCommand;
71 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
72 import com.google.gwt.event.dom.client.ClickEvent;
73 import com.google.gwt.event.dom.client.ClickHandler;
74 import com.google.gwt.event.logical.shared.ResizeEvent;
75 import com.google.gwt.event.logical.shared.ResizeHandler;
76 import com.google.gwt.http.client.Request;
77 import com.google.gwt.http.client.RequestBuilder;
78 import com.google.gwt.http.client.RequestCallback;
79 import com.google.gwt.http.client.RequestException;
80 import com.google.gwt.http.client.Response;
81 import com.google.gwt.http.client.URL;
82 import com.google.gwt.i18n.client.DateTimeFormat;
83 import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
84 import com.google.gwt.i18n.client.Dictionary;
85 import com.google.gwt.i18n.client.TimeZone;
86 import com.google.gwt.i18n.client.TimeZoneInfo;
87 import com.google.gwt.i18n.client.constants.TimeZoneConstants;
88 import com.google.gwt.json.client.JSONArray;
89 import com.google.gwt.json.client.JSONObject;
90 import com.google.gwt.json.client.JSONParser;
91 import com.google.gwt.json.client.JSONString;
92 import com.google.gwt.json.client.JSONValue;
93 import com.google.gwt.resources.client.ClientBundle;
94 import com.google.gwt.resources.client.CssResource;
95 import com.google.gwt.resources.client.ImageResource;
96 import com.google.gwt.resources.client.ImageResource.ImageOptions;
97 import com.google.gwt.user.client.Command;
98 import com.google.gwt.user.client.Cookies;
99 import com.google.gwt.user.client.Event;
100 import com.google.gwt.user.client.History;
101 import com.google.gwt.user.client.Window;
102 import com.google.gwt.user.client.ui.AbstractImagePrototype;
103 import com.google.gwt.user.client.ui.Button;
104 import com.google.gwt.user.client.ui.Composite;
105 import com.google.gwt.user.client.ui.FlowPanel;
106 import com.google.gwt.user.client.ui.HTML;
107 import com.google.gwt.user.client.ui.HasHorizontalAlignment;
108 import com.google.gwt.user.client.ui.HasVerticalAlignment;
109 import com.google.gwt.user.client.ui.HorizontalPanel;
110 import com.google.gwt.user.client.ui.HorizontalSplitPanel;
111 import com.google.gwt.user.client.ui.PopupPanel;
112 import com.google.gwt.user.client.ui.RootPanel;
113 import com.google.gwt.user.client.ui.VerticalPanel;
114 import com.google.gwt.view.client.SelectionChangeEvent;
115 import com.google.gwt.view.client.SelectionChangeEvent.Handler;
116 import com.google.gwt.view.client.SingleSelectionModel;
119 * Entry point classes define <code>onModuleLoad()</code>.
121 public class Pithos implements EntryPoint, ResizeHandler {
123 public static final String HOME_CONTAINER = "pithos";
125 public static final String TRASH_CONTAINER = "trash";
127 public static final Configuration config = GWT.create(Configuration.class);
129 public interface Style extends CssResource {
130 String commandAnchor();
134 @ClassName("gwt-HTML")
137 String uploadAlert();
139 String uploadAlertLink();
141 String uploadAlertProgress();
143 String uploadAlertPercent();
145 String uploadAlertClose();
148 public interface Resources extends ClientBundle {
149 @Source("Pithos.css")
152 @Source("gr/grnet/pithos/resources/close-popup.png")
153 ImageResource closePopup();
156 public static Resources resources = GWT.create(Resources.class);
159 * Instantiate an application-level image bundle. This object will provide
160 * programmatic access to all the images needed by widgets.
162 static Images images = (Images) GWT.create(Images.class);
164 public String getUsername() {
168 public void setAccount(AccountResource acct) {
172 public AccountResource getAccount() {
176 public void updateFolder(Folder f, boolean showfiles, Command callback, final boolean openParent) {
177 folderTreeView.updateFolder(f, showfiles, callback, openParent);
180 public void updateGroupNode(Group group) {
181 groupTreeView.updateGroupNode(group);
184 public void updateMySharedRoot() {
185 mysharedTreeView.updateRoot();
188 public void updateSharedFolder(Folder f, boolean showfiles, Command callback) {
189 mysharedTreeView.updateFolder(f, showfiles, callback);
192 public void updateSharedFolder(Folder f, boolean showfiles) {
193 updateSharedFolder(f, showfiles, null);
196 public void updateOtherSharedFolder(Folder f, boolean showfiles, Command callback) {
197 otherSharedTreeView.updateFolder(f, showfiles, callback);
200 public MysharedTreeView getMySharedTreeView() {
201 return mysharedTreeView;
205 * An aggregate image bundle that pulls together all the images for this
206 * application into a single bundle.
208 public interface Images extends TopPanel.Images, FileList.Images, ToolsMenu.Images {
210 @Source("gr/grnet/pithos/resources/document.png")
211 ImageResource folders();
213 @Source("gr/grnet/pithos/resources/advancedsettings.png")
214 @ImageOptions(width=32, height=32)
215 ImageResource tools();
218 private Throwable error;
221 * The Application Clipboard implementation;
223 private Clipboard clipboard = new Clipboard();
226 * The top panel that contains the menu bar.
228 private TopPanel topPanel;
231 * The panel that contains the various system messages.
233 private MessagePanel messagePanel = new MessagePanel(this, Pithos.images);
236 * The bottom panel that contains the status bar.
238 StatusPanel statusPanel = null;
241 * The file list widget.
243 private FileList fileList;
246 * The tab panel that occupies the right side of the screen.
248 private VerticalPanel inner = new VerticalPanel();
252 * The split panel that will contain the left and right panels.
254 private HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
257 * The currently selected item in the application, for use by the Edit menu
258 * commands. Potential types are Folder, File, User and Group.
260 private Object currentSelection;
262 public HashMap<String, String> userFullNameMap = new HashMap<String, String>();
264 private String username = null;
267 * The authentication token of the current user.
269 private String token;
273 SingleSelectionModel<Folder> folderTreeSelectionModel;
274 FolderTreeViewModel folderTreeViewModel;
275 FolderTreeView folderTreeView;
277 SingleSelectionModel<Folder> mysharedTreeSelectionModel;
278 MysharedTreeViewModel mysharedTreeViewModel;
279 MysharedTreeView mysharedTreeView = null;;
281 protected SingleSelectionModel<Folder> otherSharedTreeSelectionModel;
282 OtherSharedTreeViewModel otherSharedTreeViewModel;
283 OtherSharedTreeView otherSharedTreeView = null;
285 GroupTreeViewModel groupTreeViewModel;
286 GroupTreeView groupTreeView;
288 TreeView selectedTree;
289 protected AccountResource account;
293 List<Composite> treeViews = new ArrayList<Composite>();
295 @SuppressWarnings("rawtypes") List<SingleSelectionModel> selectionModels = new ArrayList<SingleSelectionModel>();
297 public Button upload;
299 private HTML numOfFiles;
301 private Toolbar toolbar;
303 private FileUploadDialog fileUploadDialog = new FileUploadDialog(this);
305 UploadAlert uploadAlert;
310 public void onModuleLoad() {
311 if (parseUserCredentials())
315 private void initialize() {
316 lastModified = new Date(); //Initialize if-modified-since value with now.
317 resources.pithosCss().ensureInjected();
318 boolean bareContent = Window.Location.getParameter("noframe") != null;
319 String contentWidth = bareContent ? "100%" : "75%";
321 VerticalPanel outer = new VerticalPanel();
322 outer.setWidth("100%");
324 outer.addStyleName("pithos-outer");
328 topPanel = new TopPanel(this, Pithos.images);
329 topPanel.setWidth("100%");
331 outer.setCellHorizontalAlignment(topPanel, HasHorizontalAlignment.ALIGN_CENTER);
334 messagePanel.setVisible(false);
335 outer.add(messagePanel);
336 outer.setCellHorizontalAlignment(messagePanel, HasHorizontalAlignment.ALIGN_CENTER);
337 outer.setCellVerticalAlignment(messagePanel, HasVerticalAlignment.ALIGN_MIDDLE);
339 HorizontalPanel header = new HorizontalPanel();
340 header.addStyleName("pithos-header");
341 header.setWidth(contentWidth);
343 header.addStyleName("pithos-header-noframe");
344 upload = new Button("Upload", new ClickHandler() {
346 public void onClick(ClickEvent event) {
347 if (getSelection() != null)
348 new UploadFileCommand(Pithos.this, null, getSelection()).execute();
351 upload.addStyleName("pithos-uploadButton");
353 header.setCellHorizontalAlignment(upload, HasHorizontalAlignment.ALIGN_LEFT);
354 header.setCellVerticalAlignment(upload, HasVerticalAlignment.ALIGN_MIDDLE);
356 toolbar = new Toolbar(this);
358 header.setCellHorizontalAlignment(toolbar, HasHorizontalAlignment.ALIGN_CENTER);
359 header.setCellVerticalAlignment(toolbar, HasVerticalAlignment.ALIGN_MIDDLE);
361 HorizontalPanel folderStatistics = new HorizontalPanel();
362 folderStatistics.addStyleName("pithos-folderStatistics");
363 numOfFiles = new HTML();
364 folderStatistics.add(numOfFiles);
365 folderStatistics.setCellVerticalAlignment(numOfFiles, HasVerticalAlignment.ALIGN_MIDDLE);
366 HTML numOfFilesLabel = new HTML(" Files");
367 folderStatistics.add(numOfFilesLabel);
368 folderStatistics.setCellVerticalAlignment(numOfFilesLabel, HasVerticalAlignment.ALIGN_MIDDLE);
369 header.add(folderStatistics);
370 header.setCellHorizontalAlignment(folderStatistics, HasHorizontalAlignment.ALIGN_RIGHT);
371 header.setCellVerticalAlignment(folderStatistics, HasVerticalAlignment.ALIGN_MIDDLE);
372 header.setCellWidth(folderStatistics, "40px");
374 outer.setCellHorizontalAlignment(header, HasHorizontalAlignment.ALIGN_CENTER);
375 // Inner contains the various lists
376 inner.sinkEvents(Event.ONCONTEXTMENU);
377 inner.setWidth("100%");
379 folderTreeSelectionModel = new SingleSelectionModel<Folder>();
380 folderTreeSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
382 public void onSelectionChange(SelectionChangeEvent event) {
383 if (folderTreeSelectionModel.getSelectedObject() != null) {
384 deselectOthers(folderTreeView, folderTreeSelectionModel);
385 applyPermissions(folderTreeSelectionModel.getSelectedObject());
386 Folder f = folderTreeSelectionModel.getSelectedObject();
387 updateFolder(f, true, new Command() {
390 public void execute() {
394 showRelevantToolbarButtons();
397 if (getSelectedTree().equals(folderTreeView))
398 setSelectedTree(null);
399 if (getSelectedTree() == null)
400 showRelevantToolbarButtons();
404 selectionModels.add(folderTreeSelectionModel);
406 folderTreeViewModel = new FolderTreeViewModel(this, folderTreeSelectionModel);
407 folderTreeView = new FolderTreeView(folderTreeViewModel);
408 treeViews.add(folderTreeView);
410 fileList = new FileList(this, images);
413 trees = new VerticalPanel();
414 trees.setWidth("100%");
416 // Add the left and right panels to the split panel.
417 splitPanel.setLeftWidget(trees);
418 FlowPanel right = new FlowPanel();
419 right.getElement().setId("rightPanel");
421 splitPanel.setRightWidget(right);
422 splitPanel.setSplitPosition("219px");
423 splitPanel.setSize("100%", "100%");
424 splitPanel.addStyleName("pithos-splitPanel");
425 splitPanel.setWidth(contentWidth);
426 outer.add(splitPanel);
427 outer.setCellHorizontalAlignment(splitPanel, HasHorizontalAlignment.ALIGN_CENTER);
430 statusPanel = new StatusPanel();
431 statusPanel.setWidth("100%");
432 outer.add(statusPanel);
433 outer.setCellHorizontalAlignment(statusPanel, HasHorizontalAlignment.ALIGN_CENTER);
436 splitPanel.addStyleName("pithos-splitPanel-noframe");
438 // Hook the window resize event, so that we can adjust the UI.
439 Window.addResizeHandler(this);
440 // Clear out the window's built-in margin, because we want to take
441 // advantage of the entire client area.
442 Window.setMargin("0px");
443 // Finally, add the outer panel to the RootPanel, so that it will be
445 RootPanel.get().add(outer);
446 // Call the window resized handler to get the initial sizes setup. Doing
447 // this in a deferred command causes it to occur after all widgets'
448 // sizes have been computed by the browser.
449 Scheduler.get().scheduleIncremental(new RepeatingCommand() {
452 public boolean execute() {
453 if (!isCloudbarReady())
455 onWindowResized(Window.getClientHeight());
460 Scheduler.get().scheduleDeferred(new ScheduledCommand() {
462 public void execute() {
463 fetchAccount(new Command() {
466 public void execute() {
467 if (!account.hasHomeContainer())
468 createHomeContainer(account, this);
469 else if (!account.hasTrashContainer())
470 createTrashContainer(this);
472 for (Folder f : account.getContainers())
473 if (f.getName().equals(Pithos.TRASH_CONTAINER)) {
477 trees.add(folderTreeView);
478 folderTreeViewModel.initialize(account, new Command() {
481 public void execute() {
482 createMySharedTree();
486 HorizontalPanel separator = new HorizontalPanel();
487 separator.addStyleName("pithos-statisticsSeparator");
488 separator.add(new HTML(""));
489 trees.add(separator);
491 groupTreeViewModel = new GroupTreeViewModel(Pithos.this);
492 groupTreeView = new GroupTreeView(groupTreeViewModel);
493 treeViews.add(groupTreeView);
494 trees.add(groupTreeView);
495 folderTreeView.showStatistics(account);
503 public void scheduleResfresh() {
504 Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
507 public boolean execute() {
508 final Folder f = getSelection();
512 HeadRequest<Folder> head = new HeadRequest<Folder>(Folder.class, getApiPath(), getUsername(), "/" + f.getContainer()) {
515 public void onSuccess(Folder _result) {
516 lastModified = new Date();
517 if (getSelectedTree().equals(folderTreeView))
518 updateFolder(f, true, new Command() {
521 public void execute() {
526 else if (getSelectedTree().equals(mysharedTreeView))
527 updateSharedFolder(f, true, new Command() {
530 public void execute() {
537 public void onError(Throwable t) {
538 if (t instanceof RestException && ((RestException) t).getHttpStatusCode() == HttpStatus.SC_NOT_MODIFIED)
540 else if (retries >= MAX_RETRIES) {
541 GWT.log("Error heading folder", t);
543 if (t instanceof RestException)
544 displayError("Error heading folder: " + ((RestException) t).getHttpStatusText());
546 displayError("System error heading folder: " + t.getMessage());
549 GWT.log("Retry " + retries);
550 Scheduler.get().scheduleDeferred(this);
555 protected void onUnauthorized(Response response) {
556 if (retries >= MAX_RETRIES)
559 Scheduler.get().scheduleDeferred(this);
562 head.setHeader("X-Auth-Token", getToken());
563 head.setHeader("If-Modified-Since", DateTimeFormat.getFormat("EEE, dd MMM yyyy HH:mm:ss").format(lastModified, TimeZone.createTimeZone(0)) + " GMT");
564 Scheduler.get().scheduleDeferred(head);
571 public void applyPermissions(Folder f) {
574 upload.setEnabled(false);
578 Boolean[] perms = f.getPermissions().get(username);
579 if (f.getOwner().equals(username) || (perms != null && perms[1] != null && perms[1])) {
580 upload.setEnabled(true);
584 upload.setEnabled(false);
590 upload.setEnabled(false);
595 @SuppressWarnings({ "rawtypes", "unchecked" })
596 public void deselectOthers(TreeView _selectedTree, SingleSelectionModel model) {
597 selectedTree = _selectedTree;
599 for (SingleSelectionModel s : selectionModels)
600 if (!s.equals(model) && s.getSelectedObject() != null)
601 s.setSelected(s.getSelectedObject(), false);
604 public void showFiles(final Folder f) {
605 Set<File> files = f.getFiles();
609 public void showFiles(Set<File> files) {
610 fileList.setFiles(new ArrayList<File>(files));
614 * Parse and store the user credentials to the appropriate fields.
616 private boolean parseUserCredentials() {
617 Configuration conf = (Configuration) GWT.create(Configuration.class);
618 Dictionary otherProperties = Dictionary.getDictionary("otherProperties");
619 String cookie = otherProperties.get("authCookie");
620 String auth = Cookies.getCookie(cookie);
625 if (auth.startsWith("\""))
626 auth = auth.substring(1);
627 if (auth.endsWith("\""))
628 auth = auth.substring(0, auth.length() - 1);
629 String[] authSplit = auth.split("\\" + conf.cookieSeparator(), 2);
630 if (authSplit.length != 2) {
634 username = authSplit[0];
635 token = authSplit[1];
637 String gotoUrl = Window.Location.getParameter("goto");
638 if (gotoUrl != null && gotoUrl.length() > 0) {
639 Window.Location.assign(gotoUrl);
646 * Redirect the user to the login page for authentication.
648 protected void authenticateUser() {
649 Dictionary otherProperties = Dictionary.getDictionary("otherProperties");
650 Window.Location.assign(otherProperties.get("loginUrl") + Window.Location.getHref());
653 public void fetchAccount(final Command callback) {
654 String path = "?format=json";
656 GetRequest<AccountResource> getAccount = new GetRequest<AccountResource>(AccountResource.class, getApiPath(), username, path) {
658 public void onSuccess(AccountResource _result) {
660 if (callback != null)
665 public void onError(Throwable t) {
666 GWT.log("Error getting account", t);
668 if (t instanceof RestException)
669 displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
671 displayError("System error fetching user data: " + t.getMessage());
675 protected void onUnauthorized(Response response) {
679 getAccount.setHeader("X-Auth-Token", token);
680 Scheduler.get().scheduleDeferred(getAccount);
683 public void updateStatistics() {
684 HeadRequest<AccountResource> headAccount = new HeadRequest<AccountResource>(AccountResource.class, getApiPath(), username, "", account) {
687 public void onSuccess(AccountResource _result) {
688 folderTreeView.showStatistics(account);
692 public void onError(Throwable t) {
693 GWT.log("Error getting account", t);
695 if (t instanceof RestException)
696 displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
698 displayError("System error fetching user data: " + t.getMessage());
702 protected void onUnauthorized(Response response) {
706 headAccount.setHeader("X-Auth-Token", token);
707 Scheduler.get().scheduleDeferred(headAccount);
710 protected void createHomeContainer(final AccountResource _account, final Command callback) {
711 String path = "/" + Pithos.HOME_CONTAINER;
712 PutRequest createPithos = new PutRequest(getApiPath(), getUsername(), path) {
714 public void onSuccess(Resource result) {
715 if (!_account.hasTrashContainer())
716 createTrashContainer(callback);
718 fetchAccount(callback);
722 public void onError(Throwable t) {
723 GWT.log("Error creating pithos", t);
725 if (t instanceof RestException)
726 displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
728 displayError("System error Error creating pithos: " + t.getMessage());
732 protected void onUnauthorized(Response response) {
736 createPithos.setHeader("X-Auth-Token", getToken());
737 Scheduler.get().scheduleDeferred(createPithos);
740 protected void createTrashContainer(final Command callback) {
741 String path = "/" + Pithos.TRASH_CONTAINER;
742 PutRequest createPithos = new PutRequest(getApiPath(), getUsername(), path) {
744 public void onSuccess(Resource result) {
745 fetchAccount(callback);
749 public void onError(Throwable t) {
750 GWT.log("Error creating pithos", t);
752 if (t instanceof RestException)
753 displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
755 displayError("System error Error creating pithos: " + t.getMessage());
759 protected void onUnauthorized(Response response) {
763 createPithos.setHeader("X-Auth-Token", getToken());
764 Scheduler.get().scheduleDeferred(createPithos);
768 * Creates an HTML fragment that places an image & caption together, for use
771 * @param imageProto an image prototype for an image
772 * @param caption the group caption
773 * @return the header HTML fragment
775 private String createHeaderHTML(AbstractImagePrototype imageProto, String caption) {
776 String captionHTML = "<table class='caption' cellpadding='0' "
777 + "cellspacing='0'>" + "<tr><td class='lcaption'>" + imageProto.getHTML()
778 + "</td><td id =" + caption +" class='rcaption'><b style='white-space:nowrap'> "
779 + caption + "</b></td></tr></table>";
783 protected void onWindowResized(int height) {
784 // Adjust the split panel to take up the available room in the window.
785 int newHeight = height - splitPanel.getAbsoluteTop() - 153;
788 splitPanel.setHeight("" + newHeight);
789 inner.setHeight("" + newHeight);
792 native boolean isCloudbarReady()/*-{
793 if ($wnd.$("div.servicesbar") && $wnd.$("div.servicesbar").height() > 0)
799 public void onResize(ResizeEvent event) {
800 int height = event.getHeight();
801 onWindowResized(height);
805 * Display an error message.
807 * @param msg the message to display
809 public void displayError(String msg) {
810 messagePanel.displayError(msg);
811 onWindowResized(Window.getClientHeight());
815 * Display a warning message.
817 * @param msg the message to display
819 public void displayWarning(String msg) {
820 messagePanel.displayWarning(msg);
821 onWindowResized(Window.getClientHeight());
825 * Display an informational message.
827 * @param msg the message to display
829 public void displayInformation(String msg) {
830 messagePanel.displayInformation(msg);
831 onWindowResized(Window.getClientHeight());
835 * Retrieve the fileList.
837 * @return the fileList
839 public FileList getFileList() {
844 * Retrieve the topPanel.
846 * @return the topPanel
848 TopPanel getTopPanel() {
853 * Retrieve the clipboard.
855 * @return the clipboard
857 public Clipboard getClipboard() {
861 public StatusPanel getStatusPanel() {
865 public String getToken() {
869 public static native void preventIESelection() /*-{
870 $doc.body.onselectstart = function() {
875 public static native void enableIESelection() /*-{
876 if ($doc.body.onselectstart != null)
877 $doc.body.onselectstart = null;
881 * @return the absolute path of the API root URL
883 public String getApiPath() {
884 Configuration conf = (Configuration) GWT.create(Configuration.class);
885 return conf.apiPath();
889 * History support for folder navigation
890 * adds a new browser history entry
894 public void updateHistory(String key){
895 // Replace any whitespace of the initial string to "+"
896 // String result = key.replaceAll("\\s","+");
897 // Add a new browser history entry.
898 // History.newItem(result);
899 History.newItem(key);
902 public void deleteFolder(final Folder folder, final Command callback) {
903 String path = "/" + folder.getContainer() + "/" + folder.getPrefix() + "?delimiter=/" + "&t=" + System.currentTimeMillis();
904 DeleteRequest deleteFolder = new DeleteRequest(getApiPath(), folder.getOwner(), path) {
907 protected void onUnauthorized(Response response) {
912 public void onSuccess(Resource result) {
913 updateFolder(folder.getParent(), true, new Command() {
916 public void execute() {
917 folderTreeSelectionModel.setSelected(folder.getParent(), true);
919 if (callback != null)
926 public void onError(Throwable t) {
929 if (t instanceof RestException) {
930 if (((RestException) t).getHttpStatusCode() != Response.SC_NOT_FOUND)
931 displayError("Unable to delete folder: "+((RestException) t).getHttpStatusText());
936 displayError("System error unable to delete folder: " + t.getMessage());
939 deleteFolder.setHeader("X-Auth-Token", getToken());
940 Scheduler.get().scheduleDeferred(deleteFolder);
943 public FolderTreeView getFolderTreeView() {
944 return folderTreeView;
947 public void copyFiles(final Iterator<File> iter, final String targetUsername, final String targetUri, final Command callback) {
948 if (iter.hasNext()) {
949 File file = iter.next();
950 String path = targetUri + "/" + file.getName();
951 PutRequest copyFile = new PutRequest(getApiPath(), targetUsername, path) {
953 public void onSuccess(Resource result) {
954 copyFiles(iter, targetUsername, targetUri, callback);
958 public void onError(Throwable t) {
961 if (t instanceof RestException) {
962 displayError("Unable to copy file: " + ((RestException) t).getHttpStatusText());
965 displayError("System error unable to copy file: "+t.getMessage());
969 protected void onUnauthorized(Response response) {
973 copyFile.setHeader("X-Auth-Token", getToken());
974 copyFile.setHeader("X-Copy-From", URL.encodePathSegment(file.getUri()));
975 if (!file.getOwner().equals(targetUsername))
976 copyFile.setHeader("X-Source-Account", URL.encodePathSegment(file.getOwner()));
977 copyFile.setHeader("Content-Type", file.getContentType());
978 Scheduler.get().scheduleDeferred(copyFile);
980 else if (callback != null) {
985 public void copyFolder(final Folder f, final String targetUsername, final String targetUri, boolean move, final Command callback) {
986 String path = targetUri + "?delimiter=/";
987 PutRequest copyFolder = new PutRequest(getApiPath(), targetUsername, path) {
989 public void onSuccess(Resource result) {
990 if (callback != null)
995 public void onError(Throwable t) {
998 if (t instanceof RestException) {
999 displayError("Unable to copy folder: " + ((RestException) t).getHttpStatusText());
1002 displayError("System error copying folder: " + t.getMessage());
1006 protected void onUnauthorized(Response response) {
1010 copyFolder.setHeader("X-Auth-Token", getToken());
1011 copyFolder.setHeader("Accept", "*/*");
1012 copyFolder.setHeader("Content-Length", "0");
1013 copyFolder.setHeader("Content-Type", "application/directory");
1014 if (!f.getOwner().equals(targetUsername))
1015 copyFolder.setHeader("X-Source-Account", f.getOwner());
1017 copyFolder.setHeader("X-Move-From", URL.encodePathSegment(f.getUri()));
1019 copyFolder.setHeader("X-Copy-From", URL.encodePathSegment(f.getUri()));
1020 Scheduler.get().scheduleDeferred(copyFolder);
1023 public void addSelectionModel(@SuppressWarnings("rawtypes") SingleSelectionModel model) {
1024 selectionModels.add(model);
1027 public OtherSharedTreeView getOtherSharedTreeView() {
1028 return otherSharedTreeView;
1031 public void updateTrash(boolean showFiles, Command callback) {
1032 updateFolder(trash, showFiles, callback, true);
1035 public void updateGroupsNode() {
1036 groupTreeView.updateGroupNode(null);
1039 public Group addGroup(String groupname) {
1040 Group newGroup = new Group(groupname);
1041 account.addGroup(newGroup);
1042 groupTreeView.updateGroupNode(null);
1046 public void removeGroup(Group group) {
1047 account.removeGroup(group);
1051 public TreeView getSelectedTree() {
1052 return selectedTree;
1055 public void setSelectedTree(TreeView selected) {
1056 selectedTree = selected;
1059 public Folder getSelection() {
1060 if (selectedTree != null)
1061 return selectedTree.getSelection();
1065 public void showFolderStatistics(int folderFileCount) {
1066 numOfFiles.setHTML(String.valueOf(folderFileCount));
1069 public GroupTreeView getGroupTreeView() {
1070 return groupTreeView;
1073 public void sessionExpired() {
1074 new SessionExpiredDialog(this).center();
1077 public void updateRootFolder(Command callback) {
1078 updateFolder(account.getPithos(), false, callback, true);
1081 void createMySharedTree() {
1082 mysharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1083 mysharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1085 public void onSelectionChange(SelectionChangeEvent event) {
1086 if (mysharedTreeSelectionModel.getSelectedObject() != null) {
1087 deselectOthers(mysharedTreeView, mysharedTreeSelectionModel);
1088 upload.setEnabled(false);
1089 disableUploadArea();
1090 updateSharedFolder(mysharedTreeSelectionModel.getSelectedObject(), true);
1091 showRelevantToolbarButtons();
1094 if (getSelectedTree().equals(mysharedTreeView))
1095 setSelectedTree(null);
1096 if (getSelectedTree() == null)
1097 showRelevantToolbarButtons();
1101 selectionModels.add(mysharedTreeSelectionModel);
1102 mysharedTreeViewModel = new MysharedTreeViewModel(Pithos.this, mysharedTreeSelectionModel);
1103 mysharedTreeViewModel.initialize(new Command() {
1106 public void execute() {
1107 mysharedTreeView = new MysharedTreeView(mysharedTreeViewModel);
1108 trees.insert(mysharedTreeView, 2);
1109 treeViews.add(mysharedTreeView);
1110 createOtherSharedTree();
1115 void createOtherSharedTree() {
1116 otherSharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1117 otherSharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1119 public void onSelectionChange(SelectionChangeEvent event) {
1120 if (otherSharedTreeSelectionModel.getSelectedObject() != null) {
1121 deselectOthers(otherSharedTreeView, otherSharedTreeSelectionModel);
1122 applyPermissions(otherSharedTreeSelectionModel.getSelectedObject());
1123 updateOtherSharedFolder(otherSharedTreeSelectionModel.getSelectedObject(), true, null);
1124 showRelevantToolbarButtons();
1127 if (getSelectedTree().equals(otherSharedTreeView))
1128 setSelectedTree(null);
1129 if (getSelectedTree() == null)
1130 showRelevantToolbarButtons();
1134 selectionModels.add(otherSharedTreeSelectionModel);
1135 otherSharedTreeViewModel = new OtherSharedTreeViewModel(Pithos.this, otherSharedTreeSelectionModel);
1136 otherSharedTreeViewModel.initialize(new Command() {
1139 public void execute() {
1140 otherSharedTreeView = new OtherSharedTreeView(otherSharedTreeViewModel);
1141 trees.insert(otherSharedTreeView, 1);
1142 treeViews.add(otherSharedTreeView);
1148 public native void log1(String message)/*-{
1149 $wnd.console.log(message);
1152 public String getErrorData() {
1154 return error.toString();
1158 public void setError(Throwable t) {
1162 public void showRelevantToolbarButtons() {
1163 toolbar.showRelevantButtons();
1166 public FileUploadDialog getFileUploadDialog() {
1167 if (fileUploadDialog == null)
1168 fileUploadDialog = new FileUploadDialog(this);
1169 return fileUploadDialog;
1172 public void hideUploadIndicator() {
1173 upload.removeStyleName("pithos-uploadButton-loading");
1174 upload.setTitle("");
1177 public void showUploadIndicator() {
1178 upload.addStyleName("pithos-uploadButton-loading");
1179 upload.setTitle("Upload in progress. Click for details.");
1182 public void scheduleFolderHeadCommand(final Folder folder, final Command callback) {
1183 if (folder == null) {
1184 if (callback != null)
1188 HeadRequest<Folder> headFolder = new HeadRequest<Folder>(Folder.class, getApiPath(), folder.getOwner(), folder.getUri(), folder) {
1191 public void onSuccess(Folder _result) {
1192 if (callback != null)
1197 public void onError(Throwable t) {
1198 if (t instanceof RestException) {
1199 if (((RestException) t).getHttpStatusCode() == Response.SC_NOT_FOUND) {
1200 final String path = folder.getUri();
1201 PutRequest newFolder = new PutRequest(getApiPath(), folder.getOwner(), path) {
1203 public void onSuccess(Resource _result) {
1204 scheduleFolderHeadCommand(folder, callback);
1208 public void onError(Throwable _t) {
1211 if(_t instanceof RestException){
1212 displayError("Unable to create folder: " + ((RestException) _t).getHttpStatusText());
1215 displayError("System error creating folder: " + _t.getMessage());
1219 protected void onUnauthorized(Response response) {
1223 newFolder.setHeader("X-Auth-Token", getToken());
1224 newFolder.setHeader("Content-Type", "application/folder");
1225 newFolder.setHeader("Accept", "*/*");
1226 newFolder.setHeader("Content-Length", "0");
1227 Scheduler.get().scheduleDeferred(newFolder);
1230 displayError("Error heading folder: " + ((RestException) t).getHttpStatusText());
1233 displayError("System error heading folder: " + t.getMessage());
1235 GWT.log("Error heading folder", t);
1240 protected void onUnauthorized(Response response) {
1244 headFolder.setHeader("X-Auth-Token", getToken());
1245 Scheduler.get().scheduleDeferred(headFolder);
1249 public void scheduleFileHeadCommand(File f, final Command callback) {
1250 HeadRequest<File> headFile = new HeadRequest<File>(File.class, getApiPath(), f.getOwner(), f.getUri(), f) {
1253 public void onSuccess(File _result) {
1254 if (callback != null)
1259 public void onError(Throwable t) {
1260 GWT.log("Error heading file", t);
1262 if (t instanceof RestException)
1263 displayError("Error heading file: " + ((RestException) t).getHttpStatusText());
1265 displayError("System error heading file: " + t.getMessage());
1269 protected void onUnauthorized(Response response) {
1273 headFile.setHeader("X-Auth-Token", getToken());
1274 Scheduler.get().scheduleDeferred(headFile);
1277 public boolean isMySharedSelected() {
1278 return getSelectedTree().equals(getMySharedTreeView());
1281 private Folder getUploadFolder() {
1282 if (folderTreeView.equals(getSelectedTree()) || otherSharedTreeView.equals(getSelectedTree())) {
1283 return getSelection();
1288 private void updateUploadFolder() {
1289 updateUploadFolder(null);
1292 private void updateUploadFolder(final JsArrayString urls) {
1293 if (folderTreeView.equals(getSelectedTree()) || otherSharedTreeView.equals(getSelectedTree())) {
1294 Folder f = getSelection();
1295 if (getSelectedTree().equals(getFolderTreeView()))
1296 updateFolder(f, true, new Command() {
1299 public void execute() {
1302 selectUploadedFiles(urls);
1306 updateOtherSharedFolder(f, true, null);
1310 public native void disableUploadArea() /*-{
1311 var uploader = $wnd.$("#uploader").pluploadQueue();
1312 var dropElm = $wnd.document.getElementById('rightPanel');
1313 $wnd.plupload.removeAllEvents(dropElm, uploader.id);
1316 public native void enableUploadArea() /*-{
1317 var uploader = $wnd.$("#uploader").pluploadQueue();
1318 var dropElm = $wnd.document.getElementById('rightPanel');
1319 $wnd.plupload.removeAllEvents(dropElm, uploader.id);
1320 if (uploader.runtime == 'html5') {
1321 uploader.settings.drop_element = 'rightPanel';
1322 uploader.trigger('PostInit');
1326 public void showUploadAlert(int nOfFiles) {
1327 if (uploadAlert == null)
1328 uploadAlert = new UploadAlert(this, nOfFiles);
1329 if (!uploadAlert.isShowing())
1330 uploadAlert.setPopupPositionAndShow(new PopupPanel.PositionCallback() {
1333 public void setPosition(int offsetWidth, int offsetHeight) {
1334 uploadAlert.setPopupPosition((Window.getClientWidth() - offsetWidth)/2, statusPanel.getAbsoluteTop() - offsetHeight);
1337 uploadAlert.setNumOfFiles(nOfFiles);
1340 public void hideUploadAlert() {
1341 if (uploadAlert != null && uploadAlert.isShowing())
1345 public void selectUploadedFiles(JsArrayString urls) {
1346 List<String> selectedUrls = new ArrayList<String>();
1347 for (int i=0; i<urls.length(); i++)
1348 selectedUrls.add(urls.get(i));
1349 fileList.selectByUrl(selectedUrls);
1352 public void deleteAndCreateTrash() {
1353 DeleteRequest delete = new DeleteRequest(getApiPath(), getUsername(), "/trash") {
1356 protected void onUnauthorized(Response response) {
1361 public void onSuccess(Resource result) {
1362 createTrashContainer(null);
1366 public void onError(Throwable t) {
1367 GWT.log("Error deleting trash", t);
1369 if (t instanceof RestException)
1370 displayError("Error deleting trash: " + ((RestException) t).getHttpStatusText());
1372 displayError("System error deleting trash: " + t.getMessage());
1375 delete.setHeader("X-Auth-Token", getToken());
1376 Scheduler.get().scheduleDeferred(delete);