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.Timer;
102 import com.google.gwt.user.client.Window;
103 import com.google.gwt.user.client.ui.AbstractImagePrototype;
104 import com.google.gwt.user.client.ui.Button;
105 import com.google.gwt.user.client.ui.Composite;
106 import com.google.gwt.user.client.ui.FlowPanel;
107 import com.google.gwt.user.client.ui.HTML;
108 import com.google.gwt.user.client.ui.HasHorizontalAlignment;
109 import com.google.gwt.user.client.ui.HasVerticalAlignment;
110 import com.google.gwt.user.client.ui.HorizontalPanel;
111 import com.google.gwt.user.client.ui.HorizontalSplitPanel;
112 import com.google.gwt.user.client.ui.PopupPanel;
113 import com.google.gwt.user.client.ui.RootPanel;
114 import com.google.gwt.user.client.ui.VerticalPanel;
115 import com.google.gwt.view.client.SelectionChangeEvent;
116 import com.google.gwt.view.client.SelectionChangeEvent.Handler;
117 import com.google.gwt.view.client.SingleSelectionModel;
120 * Entry point classes define <code>onModuleLoad()</code>.
122 public class Pithos implements EntryPoint, ResizeHandler {
124 public static final String HOME_CONTAINER = "pithos";
126 public static final String TRASH_CONTAINER = "trash";
128 public static final Configuration config = GWT.create(Configuration.class);
130 public interface Style extends CssResource {
131 String commandAnchor();
135 @ClassName("gwt-HTML")
138 String uploadAlert();
140 String uploadAlertLink();
142 String uploadAlertProgress();
144 String uploadAlertPercent();
146 String uploadAlertClose();
149 public interface Resources extends ClientBundle {
150 @Source("Pithos.css")
153 @Source("gr/grnet/pithos/resources/close-popup.png")
154 ImageResource closePopup();
157 public static Resources resources = GWT.create(Resources.class);
160 * Instantiate an application-level image bundle. This object will provide
161 * programmatic access to all the images needed by widgets.
163 static Images images = (Images) GWT.create(Images.class);
165 public String getUsername() {
169 public void setAccount(AccountResource acct) {
173 public AccountResource getAccount() {
177 public void updateFolder(Folder f, boolean showfiles, Command callback, final boolean openParent) {
178 folderTreeView.updateFolder(f, showfiles, callback, openParent);
181 public void updateGroupNode(Group group) {
182 groupTreeView.updateGroupNode(group);
185 public void updateMySharedRoot() {
186 mysharedTreeView.updateRoot();
189 public void updateSharedFolder(Folder f, boolean showfiles, Command callback) {
190 mysharedTreeView.updateFolder(f, showfiles, callback);
193 public void updateSharedFolder(Folder f, boolean showfiles) {
194 updateSharedFolder(f, showfiles, null);
197 public void updateOtherSharedFolder(Folder f, boolean showfiles, Command callback) {
198 otherSharedTreeView.updateFolder(f, showfiles, callback);
201 public MysharedTreeView getMySharedTreeView() {
202 return mysharedTreeView;
206 * An aggregate image bundle that pulls together all the images for this
207 * application into a single bundle.
209 public interface Images extends TopPanel.Images, FileList.Images, ToolsMenu.Images {
211 @Source("gr/grnet/pithos/resources/document.png")
212 ImageResource folders();
214 @Source("gr/grnet/pithos/resources/advancedsettings.png")
215 @ImageOptions(width=32, height=32)
216 ImageResource tools();
219 private Throwable error;
222 * The Application Clipboard implementation;
224 private Clipboard clipboard = new Clipboard();
227 * The top panel that contains the menu bar.
229 private TopPanel topPanel;
232 * The panel that contains the various system messages.
234 private MessagePanel messagePanel = new MessagePanel(this, Pithos.images);
237 * The bottom panel that contains the status bar.
239 StatusPanel statusPanel = null;
242 * The file list widget.
244 private FileList fileList;
247 * The tab panel that occupies the right side of the screen.
249 private VerticalPanel inner = new VerticalPanel();
253 * The split panel that will contain the left and right panels.
255 private HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
258 * The currently selected item in the application, for use by the Edit menu
259 * commands. Potential types are Folder, File, User and Group.
261 private Object currentSelection;
263 public HashMap<String, String> userFullNameMap = new HashMap<String, String>();
265 private String username = null;
268 * The authentication token of the current user.
270 private String token;
274 SingleSelectionModel<Folder> folderTreeSelectionModel;
275 FolderTreeViewModel folderTreeViewModel;
276 FolderTreeView folderTreeView;
278 SingleSelectionModel<Folder> mysharedTreeSelectionModel;
279 MysharedTreeViewModel mysharedTreeViewModel;
280 MysharedTreeView mysharedTreeView = null;;
282 protected SingleSelectionModel<Folder> otherSharedTreeSelectionModel;
283 OtherSharedTreeViewModel otherSharedTreeViewModel;
284 OtherSharedTreeView otherSharedTreeView = null;
286 GroupTreeViewModel groupTreeViewModel;
287 GroupTreeView groupTreeView;
289 TreeView selectedTree;
290 protected AccountResource account;
294 List<Composite> treeViews = new ArrayList<Composite>();
296 @SuppressWarnings("rawtypes") List<SingleSelectionModel> selectionModels = new ArrayList<SingleSelectionModel>();
298 public Button upload;
300 private HTML numOfFiles;
302 private Toolbar toolbar;
304 private FileUploadDialog fileUploadDialog = new FileUploadDialog(this);
306 UploadAlert uploadAlert;
311 public void onModuleLoad() {
312 if (parseUserCredentials())
316 private void initialize() {
317 lastModified = new Date(); //Initialize if-modified-since value with now.
318 resources.pithosCss().ensureInjected();
319 boolean bareContent = Window.Location.getParameter("noframe") != null;
320 String contentWidth = bareContent ? "100%" : "75%";
322 VerticalPanel outer = new VerticalPanel();
323 outer.setWidth("100%");
325 outer.addStyleName("pithos-outer");
329 topPanel = new TopPanel(this, Pithos.images);
330 topPanel.setWidth("100%");
332 outer.setCellHorizontalAlignment(topPanel, HasHorizontalAlignment.ALIGN_CENTER);
335 messagePanel.setVisible(false);
336 outer.add(messagePanel);
337 outer.setCellHorizontalAlignment(messagePanel, HasHorizontalAlignment.ALIGN_CENTER);
338 outer.setCellVerticalAlignment(messagePanel, HasVerticalAlignment.ALIGN_MIDDLE);
340 HorizontalPanel header = new HorizontalPanel();
341 header.addStyleName("pithos-header");
342 header.setWidth(contentWidth);
344 header.addStyleName("pithos-header-noframe");
345 upload = new Button("Upload", new ClickHandler() {
347 public void onClick(ClickEvent event) {
348 if (getSelection() != null)
349 new UploadFileCommand(Pithos.this, null, getSelection()).execute();
352 upload.addStyleName("pithos-uploadButton");
354 header.setCellHorizontalAlignment(upload, HasHorizontalAlignment.ALIGN_LEFT);
355 header.setCellVerticalAlignment(upload, HasVerticalAlignment.ALIGN_MIDDLE);
357 toolbar = new Toolbar(this);
359 header.setCellHorizontalAlignment(toolbar, HasHorizontalAlignment.ALIGN_CENTER);
360 header.setCellVerticalAlignment(toolbar, HasVerticalAlignment.ALIGN_MIDDLE);
362 HorizontalPanel folderStatistics = new HorizontalPanel();
363 folderStatistics.addStyleName("pithos-folderStatistics");
364 numOfFiles = new HTML();
365 folderStatistics.add(numOfFiles);
366 folderStatistics.setCellVerticalAlignment(numOfFiles, HasVerticalAlignment.ALIGN_MIDDLE);
367 HTML numOfFilesLabel = new HTML(" Files");
368 folderStatistics.add(numOfFilesLabel);
369 folderStatistics.setCellVerticalAlignment(numOfFilesLabel, HasVerticalAlignment.ALIGN_MIDDLE);
370 header.add(folderStatistics);
371 header.setCellHorizontalAlignment(folderStatistics, HasHorizontalAlignment.ALIGN_RIGHT);
372 header.setCellVerticalAlignment(folderStatistics, HasVerticalAlignment.ALIGN_MIDDLE);
373 header.setCellWidth(folderStatistics, "40px");
375 outer.setCellHorizontalAlignment(header, HasHorizontalAlignment.ALIGN_CENTER);
376 // Inner contains the various lists
377 inner.sinkEvents(Event.ONCONTEXTMENU);
378 inner.setWidth("100%");
380 folderTreeSelectionModel = new SingleSelectionModel<Folder>();
381 folderTreeSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
383 public void onSelectionChange(SelectionChangeEvent event) {
384 if (folderTreeSelectionModel.getSelectedObject() != null) {
385 deselectOthers(folderTreeView, folderTreeSelectionModel);
386 applyPermissions(folderTreeSelectionModel.getSelectedObject());
387 Folder f = folderTreeSelectionModel.getSelectedObject();
388 updateFolder(f, true, new Command() {
391 public void execute() {
395 showRelevantToolbarButtons();
398 if (getSelectedTree().equals(folderTreeView))
399 setSelectedTree(null);
400 if (getSelectedTree() == null)
401 showRelevantToolbarButtons();
405 selectionModels.add(folderTreeSelectionModel);
407 folderTreeViewModel = new FolderTreeViewModel(this, folderTreeSelectionModel);
408 folderTreeView = new FolderTreeView(folderTreeViewModel);
409 treeViews.add(folderTreeView);
411 fileList = new FileList(this, images);
414 trees = new VerticalPanel();
415 trees.setWidth("100%");
417 // Add the left and right panels to the split panel.
418 splitPanel.setLeftWidget(trees);
419 FlowPanel right = new FlowPanel();
420 right.getElement().setId("rightPanel");
422 splitPanel.setRightWidget(right);
423 splitPanel.setSplitPosition("219px");
424 splitPanel.setSize("100%", "100%");
425 splitPanel.addStyleName("pithos-splitPanel");
426 splitPanel.setWidth(contentWidth);
427 outer.add(splitPanel);
428 outer.setCellHorizontalAlignment(splitPanel, HasHorizontalAlignment.ALIGN_CENTER);
431 statusPanel = new StatusPanel();
432 statusPanel.setWidth("100%");
433 outer.add(statusPanel);
434 outer.setCellHorizontalAlignment(statusPanel, HasHorizontalAlignment.ALIGN_CENTER);
437 splitPanel.addStyleName("pithos-splitPanel-noframe");
439 // Hook the window resize event, so that we can adjust the UI.
440 Window.addResizeHandler(this);
441 // Clear out the window's built-in margin, because we want to take
442 // advantage of the entire client area.
443 Window.setMargin("0px");
444 // Finally, add the outer panel to the RootPanel, so that it will be
446 RootPanel.get().add(outer);
447 // Call the window resized handler to get the initial sizes setup. Doing
448 // this in a deferred command causes it to occur after all widgets'
449 // sizes have been computed by the browser.
450 Scheduler.get().scheduleIncremental(new RepeatingCommand() {
453 public boolean execute() {
454 if (!isCloudbarReady())
456 onWindowResized(Window.getClientHeight());
461 Scheduler.get().scheduleDeferred(new ScheduledCommand() {
463 public void execute() {
464 fetchAccount(new Command() {
467 public void execute() {
468 if (!account.hasHomeContainer())
469 createHomeContainer(account, this);
470 else if (!account.hasTrashContainer())
471 createTrashContainer(this);
473 for (Folder f : account.getContainers())
474 if (f.getName().equals(Pithos.TRASH_CONTAINER)) {
478 trees.add(folderTreeView);
479 folderTreeViewModel.initialize(account, new Command() {
482 public void execute() {
483 createMySharedTree();
487 HorizontalPanel separator = new HorizontalPanel();
488 separator.addStyleName("pithos-statisticsSeparator");
489 separator.add(new HTML(""));
490 trees.add(separator);
492 groupTreeViewModel = new GroupTreeViewModel(Pithos.this);
493 groupTreeView = new GroupTreeView(groupTreeViewModel);
494 treeViews.add(groupTreeView);
495 trees.add(groupTreeView);
496 folderTreeView.showStatistics(account);
504 public void scheduleResfresh() {
505 Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
508 public boolean execute() {
509 final Folder f = getSelection();
513 HeadRequest<Folder> head = new HeadRequest<Folder>(Folder.class, getApiPath(), getUsername(), "/" + f.getContainer()) {
516 public void onSuccess(Folder _result) {
517 lastModified = new Date();
518 if (getSelectedTree().equals(folderTreeView))
519 updateFolder(f, true, new Command() {
522 public void execute() {
527 else if (getSelectedTree().equals(mysharedTreeView))
528 updateSharedFolder(f, true, new Command() {
531 public void execute() {
538 public void onError(Throwable t) {
539 if (t instanceof RestException && ((RestException) t).getHttpStatusCode() == HttpStatus.SC_NOT_MODIFIED)
541 else if (retries >= MAX_RETRIES) {
542 GWT.log("Error heading folder", t);
544 if (t instanceof RestException)
545 displayError("Error heading folder: " + ((RestException) t).getHttpStatusText());
547 displayError("System error heading folder: " + t.getMessage());
550 GWT.log("Retry " + retries);
551 Scheduler.get().scheduleDeferred(this);
556 protected void onUnauthorized(Response response) {
557 if (retries >= MAX_RETRIES)
560 Scheduler.get().scheduleDeferred(this);
563 head.setHeader("X-Auth-Token", getToken());
564 head.setHeader("If-Modified-Since", DateTimeFormat.getFormat("EEE, dd MMM yyyy HH:mm:ss").format(lastModified, TimeZone.createTimeZone(0)) + " GMT");
565 Scheduler.get().scheduleDeferred(head);
572 public void applyPermissions(Folder f) {
575 upload.setEnabled(false);
579 Boolean[] perms = f.getPermissions().get(username);
580 if (f.getOwner().equals(username) || (perms != null && perms[1] != null && perms[1])) {
581 upload.setEnabled(true);
585 upload.setEnabled(false);
591 upload.setEnabled(false);
596 @SuppressWarnings({ "rawtypes", "unchecked" })
597 public void deselectOthers(TreeView _selectedTree, SingleSelectionModel model) {
598 selectedTree = _selectedTree;
600 for (SingleSelectionModel s : selectionModels)
601 if (!s.equals(model) && s.getSelectedObject() != null)
602 s.setSelected(s.getSelectedObject(), false);
605 public void showFiles(final Folder f) {
606 Set<File> files = f.getFiles();
610 public void showFiles(Set<File> files) {
611 fileList.setFiles(new ArrayList<File>(files));
615 * Parse and store the user credentials to the appropriate fields.
617 private boolean parseUserCredentials() {
618 Configuration conf = (Configuration) GWT.create(Configuration.class);
619 Dictionary otherProperties = Dictionary.getDictionary("otherProperties");
620 String cookie = otherProperties.get("authCookie");
621 String auth = Cookies.getCookie(cookie);
626 if (auth.startsWith("\""))
627 auth = auth.substring(1);
628 if (auth.endsWith("\""))
629 auth = auth.substring(0, auth.length() - 1);
630 String[] authSplit = auth.split("\\" + conf.cookieSeparator(), 2);
631 if (authSplit.length != 2) {
635 username = authSplit[0];
636 token = authSplit[1];
638 String gotoUrl = Window.Location.getParameter("goto");
639 if (gotoUrl != null && gotoUrl.length() > 0) {
640 Window.Location.assign(gotoUrl);
647 * Redirect the user to the login page for authentication.
649 protected void authenticateUser() {
650 Dictionary otherProperties = Dictionary.getDictionary("otherProperties");
651 Window.Location.assign(otherProperties.get("loginUrl") + Window.Location.getHref());
654 public void fetchAccount(final Command callback) {
655 String path = "?format=json";
657 GetRequest<AccountResource> getAccount = new GetRequest<AccountResource>(AccountResource.class, getApiPath(), username, path) {
659 public void onSuccess(AccountResource _result) {
661 if (callback != null)
666 public void onError(Throwable t) {
667 GWT.log("Error getting account", t);
669 if (t instanceof RestException)
670 displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
672 displayError("System error fetching user data: " + t.getMessage());
676 protected void onUnauthorized(Response response) {
680 getAccount.setHeader("X-Auth-Token", token);
681 Scheduler.get().scheduleDeferred(getAccount);
684 public void updateStatistics() {
685 HeadRequest<AccountResource> headAccount = new HeadRequest<AccountResource>(AccountResource.class, getApiPath(), username, "", account) {
688 public void onSuccess(AccountResource _result) {
689 folderTreeView.showStatistics(account);
693 public void onError(Throwable t) {
694 GWT.log("Error getting account", t);
696 if (t instanceof RestException)
697 displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
699 displayError("System error fetching user data: " + t.getMessage());
703 protected void onUnauthorized(Response response) {
707 headAccount.setHeader("X-Auth-Token", token);
708 Scheduler.get().scheduleDeferred(headAccount);
711 protected void createHomeContainer(final AccountResource _account, final Command callback) {
712 String path = "/" + Pithos.HOME_CONTAINER;
713 PutRequest createPithos = new PutRequest(getApiPath(), getUsername(), path) {
715 public void onSuccess(Resource result) {
716 if (!_account.hasTrashContainer())
717 createTrashContainer(callback);
719 fetchAccount(callback);
723 public void onError(Throwable t) {
724 GWT.log("Error creating pithos", t);
726 if (t instanceof RestException)
727 displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
729 displayError("System error Error creating pithos: " + t.getMessage());
733 protected void onUnauthorized(Response response) {
737 createPithos.setHeader("X-Auth-Token", getToken());
738 Scheduler.get().scheduleDeferred(createPithos);
741 protected void createTrashContainer(final Command callback) {
742 String path = "/" + Pithos.TRASH_CONTAINER;
743 PutRequest createPithos = new PutRequest(getApiPath(), getUsername(), path) {
745 public void onSuccess(Resource result) {
746 fetchAccount(callback);
750 public void onError(Throwable t) {
751 GWT.log("Error creating pithos", t);
753 if (t instanceof RestException)
754 displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
756 displayError("System error Error creating pithos: " + t.getMessage());
760 protected void onUnauthorized(Response response) {
764 createPithos.setHeader("X-Auth-Token", getToken());
765 Scheduler.get().scheduleDeferred(createPithos);
769 * Creates an HTML fragment that places an image & caption together, for use
772 * @param imageProto an image prototype for an image
773 * @param caption the group caption
774 * @return the header HTML fragment
776 private String createHeaderHTML(AbstractImagePrototype imageProto, String caption) {
777 String captionHTML = "<table class='caption' cellpadding='0' "
778 + "cellspacing='0'>" + "<tr><td class='lcaption'>" + imageProto.getHTML()
779 + "</td><td id =" + caption +" class='rcaption'><b style='white-space:nowrap'> "
780 + caption + "</b></td></tr></table>";
784 protected void onWindowResized(int height) {
785 // Adjust the split panel to take up the available room in the window.
786 int newHeight = height - splitPanel.getAbsoluteTop() - 153;
789 splitPanel.setHeight("" + newHeight);
790 inner.setHeight("" + newHeight);
793 native boolean isCloudbarReady()/*-{
794 if ($wnd.$("div.servicesbar") && $wnd.$("div.servicesbar").height() > 0)
800 public void onResize(ResizeEvent event) {
801 int height = event.getHeight();
802 onWindowResized(height);
806 * Display an error message.
808 * @param msg the message to display
810 public void displayError(String msg) {
811 messagePanel.displayError(msg);
812 onWindowResized(Window.getClientHeight());
816 * Display a warning message.
818 * @param msg the message to display
820 public void displayWarning(String msg) {
821 messagePanel.displayWarning(msg);
822 onWindowResized(Window.getClientHeight());
826 * Display an informational message.
828 * @param msg the message to display
830 public void displayInformation(String msg) {
831 messagePanel.displayInformation(msg);
832 onWindowResized(Window.getClientHeight());
836 * Retrieve the fileList.
838 * @return the fileList
840 public FileList getFileList() {
845 * Retrieve the topPanel.
847 * @return the topPanel
849 TopPanel getTopPanel() {
854 * Retrieve the clipboard.
856 * @return the clipboard
858 public Clipboard getClipboard() {
862 public StatusPanel getStatusPanel() {
866 public String getToken() {
870 public static native void preventIESelection() /*-{
871 $doc.body.onselectstart = function() {
876 public static native void enableIESelection() /*-{
877 if ($doc.body.onselectstart != null)
878 $doc.body.onselectstart = null;
882 * @return the absolute path of the API root URL
884 public String getApiPath() {
885 Configuration conf = (Configuration) GWT.create(Configuration.class);
886 return conf.apiPath();
890 * History support for folder navigation
891 * adds a new browser history entry
895 public void updateHistory(String key){
896 // Replace any whitespace of the initial string to "+"
897 // String result = key.replaceAll("\\s","+");
898 // Add a new browser history entry.
899 // History.newItem(result);
900 History.newItem(key);
903 public void deleteFolder(final Folder folder, final Command callback) {
904 final PleaseWaitPopup pwp = new PleaseWaitPopup();
906 String path = "/" + folder.getContainer() + "/" + folder.getPrefix() + "?delimiter=/" + "&t=" + System.currentTimeMillis();
907 DeleteRequest deleteFolder = new DeleteRequest(getApiPath(), folder.getOwner(), path) {
910 protected void onUnauthorized(Response response) {
916 public void onSuccess(Resource result) {
917 updateFolder(folder.getParent(), true, new Command() {
920 public void execute() {
921 folderTreeSelectionModel.setSelected(folder.getParent(), true);
923 if (callback != null)
925 Timer t = new Timer() {
938 public void onError(Throwable t) {
941 if (t instanceof RestException) {
942 if (((RestException) t).getHttpStatusCode() != Response.SC_NOT_FOUND)
943 displayError("Unable to delete folder: "+((RestException) t).getHttpStatusText());
948 displayError("System error unable to delete folder: " + t.getMessage());
952 deleteFolder.setHeader("X-Auth-Token", getToken());
953 Scheduler.get().scheduleDeferred(deleteFolder);
956 public FolderTreeView getFolderTreeView() {
957 return folderTreeView;
960 public void copyFiles(final Iterator<File> iter, final String targetUsername, final String targetUri, final Command callback) {
961 if (iter.hasNext()) {
962 File file = iter.next();
963 String path = targetUri + "/" + file.getName();
964 PutRequest copyFile = new PutRequest(getApiPath(), targetUsername, path) {
966 public void onSuccess(Resource result) {
967 copyFiles(iter, targetUsername, targetUri, callback);
971 public void onError(Throwable t) {
974 if (t instanceof RestException) {
975 displayError("Unable to copy file: " + ((RestException) t).getHttpStatusText());
978 displayError("System error unable to copy file: "+t.getMessage());
982 protected void onUnauthorized(Response response) {
986 copyFile.setHeader("X-Auth-Token", getToken());
987 copyFile.setHeader("X-Copy-From", URL.encodePathSegment(file.getUri()));
988 if (!file.getOwner().equals(targetUsername))
989 copyFile.setHeader("X-Source-Account", URL.encodePathSegment(file.getOwner()));
990 copyFile.setHeader("Content-Type", file.getContentType());
991 Scheduler.get().scheduleDeferred(copyFile);
993 else if (callback != null) {
998 public void copyFolder(final Folder f, final String targetUsername, final String targetUri, boolean move, final Command callback) {
999 String path = targetUri + "?delimiter=/";
1000 PutRequest copyFolder = new PutRequest(getApiPath(), targetUsername, path) {
1002 public void onSuccess(Resource result) {
1003 if (callback != null)
1008 public void onError(Throwable t) {
1011 if (t instanceof RestException) {
1012 displayError("Unable to copy folder: " + ((RestException) t).getHttpStatusText());
1015 displayError("System error copying folder: " + t.getMessage());
1019 protected void onUnauthorized(Response response) {
1023 copyFolder.setHeader("X-Auth-Token", getToken());
1024 copyFolder.setHeader("Accept", "*/*");
1025 copyFolder.setHeader("Content-Length", "0");
1026 copyFolder.setHeader("Content-Type", "application/directory");
1027 if (!f.getOwner().equals(targetUsername))
1028 copyFolder.setHeader("X-Source-Account", f.getOwner());
1030 copyFolder.setHeader("X-Move-From", URL.encodePathSegment(f.getUri()));
1032 copyFolder.setHeader("X-Copy-From", URL.encodePathSegment(f.getUri()));
1033 Scheduler.get().scheduleDeferred(copyFolder);
1036 public void addSelectionModel(@SuppressWarnings("rawtypes") SingleSelectionModel model) {
1037 selectionModels.add(model);
1040 public OtherSharedTreeView getOtherSharedTreeView() {
1041 return otherSharedTreeView;
1044 public void updateTrash(boolean showFiles, Command callback) {
1045 updateFolder(trash, showFiles, callback, true);
1048 public void updateGroupsNode() {
1049 groupTreeView.updateGroupNode(null);
1052 public Group addGroup(String groupname) {
1053 Group newGroup = new Group(groupname);
1054 account.addGroup(newGroup);
1055 groupTreeView.updateGroupNode(null);
1059 public void removeGroup(Group group) {
1060 account.removeGroup(group);
1064 public TreeView getSelectedTree() {
1065 return selectedTree;
1068 public void setSelectedTree(TreeView selected) {
1069 selectedTree = selected;
1072 public Folder getSelection() {
1073 if (selectedTree != null)
1074 return selectedTree.getSelection();
1078 public void showFolderStatistics(int folderFileCount) {
1079 numOfFiles.setHTML(String.valueOf(folderFileCount));
1082 public GroupTreeView getGroupTreeView() {
1083 return groupTreeView;
1086 public void sessionExpired() {
1087 new SessionExpiredDialog(this).center();
1090 public void updateRootFolder(Command callback) {
1091 updateFolder(account.getPithos(), false, callback, true);
1094 void createMySharedTree() {
1095 mysharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1096 mysharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1098 public void onSelectionChange(SelectionChangeEvent event) {
1099 if (mysharedTreeSelectionModel.getSelectedObject() != null) {
1100 deselectOthers(mysharedTreeView, mysharedTreeSelectionModel);
1101 upload.setEnabled(false);
1102 disableUploadArea();
1103 updateSharedFolder(mysharedTreeSelectionModel.getSelectedObject(), true);
1104 showRelevantToolbarButtons();
1107 if (getSelectedTree().equals(mysharedTreeView))
1108 setSelectedTree(null);
1109 if (getSelectedTree() == null)
1110 showRelevantToolbarButtons();
1114 selectionModels.add(mysharedTreeSelectionModel);
1115 mysharedTreeViewModel = new MysharedTreeViewModel(Pithos.this, mysharedTreeSelectionModel);
1116 mysharedTreeViewModel.initialize(new Command() {
1119 public void execute() {
1120 mysharedTreeView = new MysharedTreeView(mysharedTreeViewModel);
1121 trees.insert(mysharedTreeView, 2);
1122 treeViews.add(mysharedTreeView);
1123 createOtherSharedTree();
1128 void createOtherSharedTree() {
1129 otherSharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1130 otherSharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1132 public void onSelectionChange(SelectionChangeEvent event) {
1133 if (otherSharedTreeSelectionModel.getSelectedObject() != null) {
1134 deselectOthers(otherSharedTreeView, otherSharedTreeSelectionModel);
1135 applyPermissions(otherSharedTreeSelectionModel.getSelectedObject());
1136 updateOtherSharedFolder(otherSharedTreeSelectionModel.getSelectedObject(), true, null);
1137 showRelevantToolbarButtons();
1140 if (getSelectedTree().equals(otherSharedTreeView))
1141 setSelectedTree(null);
1142 if (getSelectedTree() == null)
1143 showRelevantToolbarButtons();
1147 selectionModels.add(otherSharedTreeSelectionModel);
1148 otherSharedTreeViewModel = new OtherSharedTreeViewModel(Pithos.this, otherSharedTreeSelectionModel);
1149 otherSharedTreeViewModel.initialize(new Command() {
1152 public void execute() {
1153 otherSharedTreeView = new OtherSharedTreeView(otherSharedTreeViewModel);
1154 trees.insert(otherSharedTreeView, 1);
1155 treeViews.add(otherSharedTreeView);
1161 public native void log1(String message)/*-{
1162 $wnd.console.log(message);
1165 public String getErrorData() {
1167 return error.toString();
1171 public void setError(Throwable t) {
1175 public void showRelevantToolbarButtons() {
1176 toolbar.showRelevantButtons();
1179 public FileUploadDialog getFileUploadDialog() {
1180 if (fileUploadDialog == null)
1181 fileUploadDialog = new FileUploadDialog(this);
1182 return fileUploadDialog;
1185 public void hideUploadIndicator() {
1186 upload.removeStyleName("pithos-uploadButton-loading");
1187 upload.setTitle("");
1190 public void showUploadIndicator() {
1191 upload.addStyleName("pithos-uploadButton-loading");
1192 upload.setTitle("Upload in progress. Click for details.");
1195 public void scheduleFolderHeadCommand(final Folder folder, final Command callback) {
1196 if (folder == null) {
1197 if (callback != null)
1201 HeadRequest<Folder> headFolder = new HeadRequest<Folder>(Folder.class, getApiPath(), folder.getOwner(), folder.getUri(), folder) {
1204 public void onSuccess(Folder _result) {
1205 if (callback != null)
1210 public void onError(Throwable t) {
1211 if (t instanceof RestException) {
1212 if (((RestException) t).getHttpStatusCode() == Response.SC_NOT_FOUND) {
1213 final String path = folder.getUri();
1214 PutRequest newFolder = new PutRequest(getApiPath(), folder.getOwner(), path) {
1216 public void onSuccess(Resource _result) {
1217 scheduleFolderHeadCommand(folder, callback);
1221 public void onError(Throwable _t) {
1224 if(_t instanceof RestException){
1225 displayError("Unable to create folder: " + ((RestException) _t).getHttpStatusText());
1228 displayError("System error creating folder: " + _t.getMessage());
1232 protected void onUnauthorized(Response response) {
1236 newFolder.setHeader("X-Auth-Token", getToken());
1237 newFolder.setHeader("Content-Type", "application/folder");
1238 newFolder.setHeader("Accept", "*/*");
1239 newFolder.setHeader("Content-Length", "0");
1240 Scheduler.get().scheduleDeferred(newFolder);
1243 displayError("Error heading folder: " + ((RestException) t).getHttpStatusText());
1246 displayError("System error heading folder: " + t.getMessage());
1248 GWT.log("Error heading folder", t);
1253 protected void onUnauthorized(Response response) {
1257 headFolder.setHeader("X-Auth-Token", getToken());
1258 Scheduler.get().scheduleDeferred(headFolder);
1262 public void scheduleFileHeadCommand(File f, final Command callback) {
1263 HeadRequest<File> headFile = new HeadRequest<File>(File.class, getApiPath(), f.getOwner(), f.getUri(), f) {
1266 public void onSuccess(File _result) {
1267 if (callback != null)
1272 public void onError(Throwable t) {
1273 GWT.log("Error heading file", t);
1275 if (t instanceof RestException)
1276 displayError("Error heading file: " + ((RestException) t).getHttpStatusText());
1278 displayError("System error heading file: " + t.getMessage());
1282 protected void onUnauthorized(Response response) {
1286 headFile.setHeader("X-Auth-Token", getToken());
1287 Scheduler.get().scheduleDeferred(headFile);
1290 public boolean isMySharedSelected() {
1291 return getSelectedTree().equals(getMySharedTreeView());
1294 private Folder getUploadFolder() {
1295 if (folderTreeView.equals(getSelectedTree()) || otherSharedTreeView.equals(getSelectedTree())) {
1296 return getSelection();
1301 private void updateUploadFolder() {
1302 updateUploadFolder(null);
1305 private void updateUploadFolder(final JsArrayString urls) {
1306 if (folderTreeView.equals(getSelectedTree()) || otherSharedTreeView.equals(getSelectedTree())) {
1307 Folder f = getSelection();
1308 if (getSelectedTree().equals(getFolderTreeView()))
1309 updateFolder(f, true, new Command() {
1312 public void execute() {
1315 selectUploadedFiles(urls);
1319 updateOtherSharedFolder(f, true, null);
1323 public native void disableUploadArea() /*-{
1324 var uploader = $wnd.$("#uploader").pluploadQueue();
1325 var dropElm = $wnd.document.getElementById('rightPanel');
1326 $wnd.plupload.removeAllEvents(dropElm, uploader.id);
1329 public native void enableUploadArea() /*-{
1330 var uploader = $wnd.$("#uploader").pluploadQueue();
1331 var dropElm = $wnd.document.getElementById('rightPanel');
1332 $wnd.plupload.removeAllEvents(dropElm, uploader.id);
1333 if (uploader.runtime == 'html5') {
1334 uploader.settings.drop_element = 'rightPanel';
1335 uploader.trigger('PostInit');
1339 public void showUploadAlert(int nOfFiles) {
1340 if (uploadAlert == null)
1341 uploadAlert = new UploadAlert(this, nOfFiles);
1342 if (!uploadAlert.isShowing())
1343 uploadAlert.setPopupPositionAndShow(new PopupPanel.PositionCallback() {
1346 public void setPosition(int offsetWidth, int offsetHeight) {
1347 uploadAlert.setPopupPosition((Window.getClientWidth() - offsetWidth)/2, statusPanel.getAbsoluteTop() - offsetHeight);
1350 uploadAlert.setNumOfFiles(nOfFiles);
1353 public void hideUploadAlert() {
1354 if (uploadAlert != null && uploadAlert.isShowing())
1358 public void selectUploadedFiles(JsArrayString urls) {
1359 List<String> selectedUrls = new ArrayList<String>();
1360 for (int i=0; i<urls.length(); i++)
1361 selectedUrls.add(urls.get(i));
1362 fileList.selectByUrl(selectedUrls);
1365 public void deleteAndCreateTrash() {
1366 DeleteRequest delete = new DeleteRequest(getApiPath(), getUsername(), "/trash") {
1369 protected void onUnauthorized(Response response) {
1374 public void onSuccess(Resource result) {
1375 createTrashContainer(null);
1379 public void onError(Throwable t) {
1380 GWT.log("Error deleting trash", t);
1382 if (t instanceof RestException)
1383 displayError("Error deleting trash: " + ((RestException) t).getHttpStatusText());
1385 displayError("System error deleting trash: " + t.getMessage());
1388 delete.setHeader("X-Auth-Token", getToken());
1389 Scheduler.get().scheduleDeferred(delete);