2 * Copyright 2011 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;
56 import gr.grnet.pithos.web.client.tagtree.Tag;
57 import gr.grnet.pithos.web.client.tagtree.TagTreeView;
58 import gr.grnet.pithos.web.client.tagtree.TagTreeViewModel;
60 import java.util.ArrayList;
61 import java.util.HashMap;
62 import java.util.Iterator;
63 import java.util.List;
66 import com.google.gwt.core.client.EntryPoint;
67 import com.google.gwt.core.client.GWT;
68 import com.google.gwt.core.client.Scheduler;
69 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
70 import com.google.gwt.event.dom.client.ClickEvent;
71 import com.google.gwt.event.dom.client.ClickHandler;
72 import com.google.gwt.event.logical.shared.ResizeEvent;
73 import com.google.gwt.event.logical.shared.ResizeHandler;
74 import com.google.gwt.http.client.Request;
75 import com.google.gwt.http.client.RequestBuilder;
76 import com.google.gwt.http.client.RequestCallback;
77 import com.google.gwt.http.client.RequestException;
78 import com.google.gwt.http.client.Response;
79 import com.google.gwt.json.client.JSONArray;
80 import com.google.gwt.json.client.JSONObject;
81 import com.google.gwt.json.client.JSONParser;
82 import com.google.gwt.json.client.JSONString;
83 import com.google.gwt.json.client.JSONValue;
84 import com.google.gwt.resources.client.ImageResource;
85 import com.google.gwt.user.client.Command;
86 import com.google.gwt.user.client.Cookies;
87 import com.google.gwt.user.client.Event;
88 import com.google.gwt.user.client.History;
89 import com.google.gwt.user.client.Window;
90 import com.google.gwt.user.client.ui.AbstractImagePrototype;
91 import com.google.gwt.user.client.ui.Button;
92 import com.google.gwt.user.client.ui.HTML;
93 import com.google.gwt.user.client.ui.HasHorizontalAlignment;
94 import com.google.gwt.user.client.ui.HasVerticalAlignment;
95 import com.google.gwt.user.client.ui.HorizontalPanel;
96 import com.google.gwt.user.client.ui.HorizontalSplitPanel;
97 import com.google.gwt.user.client.ui.Image;
98 import com.google.gwt.user.client.ui.RootPanel;
99 import com.google.gwt.user.client.ui.VerticalPanel;
100 import com.google.gwt.view.client.SelectionChangeEvent;
101 import com.google.gwt.view.client.SelectionChangeEvent.Handler;
102 import com.google.gwt.view.client.SingleSelectionModel;
105 * Entry point classes define <code>onModuleLoad()</code>.
107 public class Pithos implements EntryPoint, ResizeHandler {
109 public static final String HOME_CONTAINER = "pithos";
111 public static final String TRASH_CONTAINER = "trash";
114 * Instantiate an application-level image bundle. This object will provide
115 * programmatic access to all the images needed by widgets.
117 static Images images = (Images) GWT.create(Images.class);
119 public String getUsername() {
123 public void setAccount(AccountResource acct) {
127 public AccountResource getAccount() {
131 public void updateFolder(Folder f, boolean showfiles, Command callback) {
132 folderTreeView.updateFolder(f, showfiles, callback);
135 public void updateGroupNode(Group group) {
136 groupTreeView.updateGroupNode(group);
139 public void updateSharedFolder(Folder f, boolean showfiles) {
140 mysharedTreeView.updateFolder(f, showfiles);
143 public void updateOtherSharedFolder(Folder f, boolean showfiles) {
144 otherSharedTreeView.updateFolder(f, showfiles);
147 public void updateTag(Tag t) {
148 tagTreeView.updateTag(t);
151 public void updateTags() {
152 tagTreeViewModel.initialize(getAllTags());
155 public List<Tag> getAllTags() {
156 List<Tag> tagList = new ArrayList<Tag>();
157 for (Folder f : account.getContainers()) {
158 for (String t : f.getTags()) {
159 tagList.add(new Tag(t));
165 public MysharedTreeView getMySharedTreeView() {
166 return mysharedTreeView;
170 * An aggregate image bundle that pulls together all the images for this
171 * application into a single bundle.
173 public interface Images extends TopPanel.Images, FileList.Images, ToolsMenu.Images {
175 @Source("gr/grnet/pithos/resources/document.png")
176 ImageResource folders();
178 @Source("gr/grnet/pithos/resources/edit_group_22.png")
179 ImageResource groups();
181 @Source("gr/grnet/pithos/resources/advancedsettings.png")
182 ImageResource tools();
186 * The Application Clipboard implementation;
188 private Clipboard clipboard = new Clipboard();
191 * The top panel that contains the menu bar.
193 private TopPanel topPanel;
196 * The panel that contains the various system messages.
198 private MessagePanel messagePanel = new MessagePanel(Pithos.images);
201 * The bottom panel that contains the status bar.
203 private StatusPanel statusPanel = null;
206 * The file list widget.
208 private FileList fileList;
211 * The tab panel that occupies the right side of the screen.
213 private VerticalPanel inner = new VerticalPanel();
217 * The split panel that will contain the left and right panels.
219 private HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
222 * The currently selected item in the application, for use by the Edit menu
223 * commands. Potential types are Folder, File, User and Group.
225 private Object currentSelection;
227 public HashMap<String, String> userFullNameMap = new HashMap<String, String>();
229 private String username = null;
232 * The authentication token of the current user.
234 private String token;
236 SingleSelectionModel<Folder> folderTreeSelectionModel;
237 FolderTreeViewModel folderTreeViewModel;
238 FolderTreeView folderTreeView;
240 SingleSelectionModel<Folder> mysharedTreeSelectionModel;
241 private MysharedTreeViewModel mysharedTreeViewModel;
242 MysharedTreeView mysharedTreeView;
244 protected SingleSelectionModel<Folder> otherSharedTreeSelectionModel;
245 private OtherSharedTreeViewModel otherSharedTreeViewModel;
246 OtherSharedTreeView otherSharedTreeView;
248 protected SingleSelectionModel<Tag> tagTreeSelectionModel;
249 private TagTreeViewModel tagTreeViewModel;
250 private TagTreeView tagTreeView;
252 GroupTreeViewModel groupTreeViewModel;
253 private GroupTreeView groupTreeView;
255 private TreeView selectedTree;
256 protected AccountResource account;
260 @SuppressWarnings("rawtypes")
261 private List<SingleSelectionModel> selectionModels = new ArrayList<SingleSelectionModel>();
265 private HTML totalFiles;
267 private HTML usedBytes;
269 private HTML totalBytes;
271 private HTML usedPercent;
273 private HTML numOfFiles;
275 private Button toolsButton;
278 public void onModuleLoad() {
279 if (parseUserCredentials())
283 private void initialize() {
284 VerticalPanel outer = new VerticalPanel();
285 outer.setWidth("100%");
287 topPanel = new TopPanel(this, Pithos.images);
288 topPanel.setWidth("100%");
291 messagePanel.setWidth("100%");
292 messagePanel.setVisible(false);
293 outer.add(messagePanel);
294 outer.setCellHorizontalAlignment(messagePanel, HasHorizontalAlignment.ALIGN_CENTER);
297 // Inner contains the various lists.
298 inner.sinkEvents(Event.ONCONTEXTMENU);
299 inner.setWidth("100%");
301 HorizontalPanel rightside = new HorizontalPanel();
302 rightside.addStyleName("pithos-rightSide");
303 rightside.setSpacing(5);
305 toolsButton = new Button(AbstractImagePrototype.create(images.tools()).getHTML());
306 toolsButton.addClickHandler(new ClickHandler() {
309 public void onClick(ClickEvent event) {
310 ToolsMenu menu = new ToolsMenu(Pithos.this, images, getSelectedTree(), getSelectedTree().getSelection(), getFileList().getSelectedFiles());
311 if (!menu.isEmpty()) {
312 menu.setPopupPosition(event.getClientX(), event.getClientY());
317 rightside.add(toolsButton);
318 rightside.setCellHorizontalAlignment(toolsButton, HasHorizontalAlignment.ALIGN_LEFT);
320 HorizontalPanel folderStatistics = new HorizontalPanel();
321 folderStatistics.addStyleName("pithos-folderStatistics");
322 numOfFiles = new HTML();
323 folderStatistics.add(numOfFiles);
324 HTML numOfFilesLabel = new HTML(" Files");
325 folderStatistics.add(numOfFilesLabel);
326 rightside.add(folderStatistics);
327 rightside.setCellHorizontalAlignment(folderStatistics, HasHorizontalAlignment.ALIGN_RIGHT);
329 inner.add(rightside);
330 inner.setCellVerticalAlignment(rightside, HasVerticalAlignment.ALIGN_MIDDLE);
331 inner.setCellHeight(rightside, "60px");
333 folderTreeSelectionModel = new SingleSelectionModel<Folder>();
334 folderTreeSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
336 public void onSelectionChange(@SuppressWarnings("unused") SelectionChangeEvent event) {
337 if (folderTreeSelectionModel.getSelectedObject() != null) {
338 deselectOthers(folderTreeView, folderTreeSelectionModel);
339 applyPermissions(folderTreeSelectionModel.getSelectedObject());
340 Folder f = folderTreeSelectionModel.getSelectedObject();
341 updateFolder(f, true, null);
345 selectionModels.add(folderTreeSelectionModel);
347 folderTreeViewModel = new FolderTreeViewModel(this, folderTreeSelectionModel);
348 folderTreeView = new FolderTreeView(folderTreeViewModel);
350 fileList = new FileList(this, images, folderTreeView);
353 mysharedTreeSelectionModel = new SingleSelectionModel<Folder>();
354 mysharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
356 public void onSelectionChange(@SuppressWarnings("unused") SelectionChangeEvent event) {
357 if (mysharedTreeSelectionModel.getSelectedObject() != null) {
358 deselectOthers(mysharedTreeView, mysharedTreeSelectionModel);
359 upload.setEnabled(false);
360 updateSharedFolder(mysharedTreeSelectionModel.getSelectedObject(), true);
364 selectionModels.add(mysharedTreeSelectionModel);
365 mysharedTreeViewModel = new MysharedTreeViewModel(this, mysharedTreeSelectionModel);
366 mysharedTreeView = new MysharedTreeView(mysharedTreeViewModel);
368 otherSharedTreeSelectionModel = new SingleSelectionModel<Folder>();
369 otherSharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
371 public void onSelectionChange(@SuppressWarnings("unused") SelectionChangeEvent event) {
372 if (otherSharedTreeSelectionModel.getSelectedObject() != null) {
373 deselectOthers(otherSharedTreeView, otherSharedTreeSelectionModel);
374 applyPermissions(otherSharedTreeSelectionModel.getSelectedObject());
375 updateOtherSharedFolder(otherSharedTreeSelectionModel.getSelectedObject(), true);
379 selectionModels.add(otherSharedTreeSelectionModel);
380 otherSharedTreeViewModel = new OtherSharedTreeViewModel(this, otherSharedTreeSelectionModel);
381 otherSharedTreeView = new OtherSharedTreeView(otherSharedTreeViewModel);
383 groupTreeViewModel = new GroupTreeViewModel(this);
384 groupTreeView = new GroupTreeView(groupTreeViewModel);
386 VerticalPanel trees = new VerticalPanel();
388 upload = new Button("Upload File", new ClickHandler() {
390 public void onClick(@SuppressWarnings("unused") ClickEvent event) {
391 new UploadFileCommand(Pithos.this, null, getSelection()).execute();
394 upload.addStyleName("pithos-uploadButton");
397 HorizontalPanel treeHeader = new HorizontalPanel();
398 treeHeader.addStyleName("pithos-treeHeader");
399 HorizontalPanel statistics = new HorizontalPanel();
400 statistics.add(new HTML("Total Objects: "));
401 totalFiles = new HTML();
402 statistics.add(totalFiles);
403 statistics.add(new HTML(" | Used: "));
404 usedBytes = new HTML();
405 statistics.add(usedBytes);
406 statistics.add(new HTML(" of "));
407 totalBytes = new HTML();
408 statistics.add(totalBytes);
409 statistics.add(new HTML(" ("));
410 usedPercent = new HTML();
411 statistics.add(usedPercent);
412 statistics.add(new HTML("%)"));
413 treeHeader.add(statistics);
414 trees.add(treeHeader);
416 trees.add(folderTreeView);
417 trees.add(mysharedTreeView);
418 trees.add(otherSharedTreeView);
419 // trees.add(tagTreeView);
420 trees.add(groupTreeView);
421 // Add the left and right panels to the split panel.
422 splitPanel.setLeftWidget(trees);
423 splitPanel.setRightWidget(inner);
424 splitPanel.setSplitPosition("25%");
425 splitPanel.setSize("100%", "100%");
426 splitPanel.addStyleName("pithos-splitPanel");
427 outer.add(splitPanel);
429 statusPanel = new StatusPanel();
430 outer.add(statusPanel);
433 // Hook the window resize event, so that we can adjust the UI.
434 Window.addResizeHandler(this);
435 // Clear out the window's built-in margin, because we want to take
436 // advantage of the entire client area.
437 Window.setMargin("0px");
438 // Finally, add the outer panel to the RootPanel, so that it will be
440 RootPanel.get().add(outer);
441 // Call the window resized handler to get the initial sizes setup. Doing
442 // this in a deferred command causes it to occur after all widgets'
443 // sizes have been computed by the browser.
444 Scheduler.get().scheduleDeferred(new ScheduledCommand() {
447 public void execute() {
448 onWindowResized(Window.getClientHeight());
452 Scheduler.get().scheduleDeferred(new ScheduledCommand() {
454 public void execute() {
455 fetchAccount(new Command() {
458 public void execute() {
459 if (!account.hasHomeContainer())
460 createHomeContainer(account, this);
461 else if (!account.hasTrashContainer())
462 createTrashContainer(this);
464 for (Folder f : account.getContainers())
465 if (f.getName().equals(Pithos.TRASH_CONTAINER)) {
469 folderTreeViewModel.initialize(account);
470 groupTreeViewModel.initialize();
479 public void applyPermissions(Folder f) {
482 upload.setEnabled(false);
484 Boolean[] perms = f.getPermissions().get(username);
485 if (f.getOwner().equals(username) || (perms != null && perms[1] != null && perms[1])) {
486 upload.setEnabled(true);
489 upload.setEnabled(false);
493 upload.setEnabled(false);
496 @SuppressWarnings({ "rawtypes", "unchecked" })
497 public void deselectOthers(TreeView _selectedTree, SingleSelectionModel model) {
498 selectedTree = _selectedTree;
499 for (SingleSelectionModel s : selectionModels)
500 if (!s.equals(model))
501 s.setSelected(s.getSelectedObject(), false);
504 public void showFiles(Folder f) {
505 Set<File> files = f.getFiles();
509 public void showFiles(Set<File> files) {
510 //Iterator<File> iter = files.iterator();
511 //fetchFile(iter, files);
512 fileList.setFiles(new ArrayList<File>(files));
515 protected void fetchFile(final Iterator<File> iter, final Set<File> files) {
516 if (iter.hasNext()) {
517 File file = iter.next();
518 String path = file.getUri() + "?format=json";
519 GetRequest<File> getFile = new GetRequest<File>(File.class, getApiPath(), username, path, file) {
521 public void onSuccess(@SuppressWarnings("unused") File _result) {
522 fetchFile(iter, files);
526 public void onError(Throwable t) {
527 GWT.log("Error getting file", t);
528 if (t instanceof RestException)
529 displayError("Error getting file: " + ((RestException) t).getHttpStatusText());
531 displayError("System error fetching file: " + t.getMessage());
535 protected void onUnauthorized(Response response) {
539 getFile.setHeader("X-Auth-Token", "0000");
540 Scheduler.get().scheduleDeferred(getFile);
543 fileList.setFiles(new ArrayList<File>(files));
547 * Parse and store the user credentials to the appropriate fields.
549 private boolean parseUserCredentials() {
550 username = Window.Location.getParameter("user");
551 token = Window.Location.getParameter("token");
552 Configuration conf = (Configuration) GWT.create(Configuration.class);
553 if (username == null || username.length() == 0 || token == null || token.length() == 0) {
554 String cookie = conf.authCookie();
555 String auth = Cookies.getCookie(cookie);
560 String[] authSplit = auth.split("\\" + conf.cookieSeparator(), 2);
561 if (authSplit.length != 2) {
565 username = authSplit[0];
566 token = authSplit[1];
569 Cookies.setCookie(conf.authCookie(), username + conf.cookieSeparator() + token);
574 * Redirect the user to the login page for authentication.
576 protected void authenticateUser() {
577 Configuration conf = (Configuration) GWT.create(Configuration.class);
578 Window.Location.assign(conf.loginUrl() + "?next=" + Window.Location.getHref());
581 protected void fetchAccount(final Command callback) {
582 String path = "?format=json";
584 GetRequest<AccountResource> getAccount = new GetRequest<AccountResource>(AccountResource.class, getApiPath(), username, path) {
586 public void onSuccess(AccountResource _result) {
588 if (callback != null)
593 public void onError(Throwable t) {
594 GWT.log("Error getting account", t);
595 if (t instanceof RestException)
596 displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
598 displayError("System error fetching user data: " + t.getMessage());
602 protected void onUnauthorized(Response response) {
606 getAccount.setHeader("X-Auth-Token", token);
607 Scheduler.get().scheduleDeferred(getAccount);
610 public void updateStatistics() {
611 HeadRequest<AccountResource> headAccount = new HeadRequest<AccountResource>(AccountResource.class, getApiPath(), username, "", account) {
614 public void onSuccess(@SuppressWarnings("unused") AccountResource _result) {
619 public void onError(Throwable t) {
620 GWT.log("Error getting account", t);
621 if (t instanceof RestException)
622 displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
624 displayError("System error fetching user data: " + t.getMessage());
628 protected void onUnauthorized(Response response) {
632 headAccount.setHeader("X-Auth-Token", token);
633 Scheduler.get().scheduleDeferred(headAccount);
636 protected void showStatistics() {
637 totalFiles.setHTML(String.valueOf(account.getNumberOfObjects()));
638 usedBytes.setHTML(String.valueOf(account.getFileSizeAsString()));
639 totalBytes.setHTML(String.valueOf(account.getQuotaAsString()));
640 usedPercent.setHTML(String.valueOf(account.getUsedPercentage()));
643 protected void createHomeContainer(final AccountResource _account, final Command callback) {
644 String path = "/" + Pithos.HOME_CONTAINER;
645 PutRequest createPithos = new PutRequest(getApiPath(), getUsername(), path) {
647 public void onSuccess(@SuppressWarnings("unused") Resource result) {
648 if (!_account.hasTrashContainer())
649 createTrashContainer(callback);
651 fetchAccount(callback);
655 public void onError(Throwable t) {
656 GWT.log("Error creating pithos", t);
657 if (t instanceof RestException)
658 displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
660 displayError("System error Error creating pithos: " + t.getMessage());
664 protected void onUnauthorized(Response response) {
668 createPithos.setHeader("X-Auth-Token", getToken());
669 Scheduler.get().scheduleDeferred(createPithos);
672 protected void createTrashContainer(final Command callback) {
673 String path = "/" + Pithos.TRASH_CONTAINER;
674 PutRequest createPithos = new PutRequest(getApiPath(), getUsername(), path) {
676 public void onSuccess(@SuppressWarnings("unused") Resource result) {
677 fetchAccount(callback);
681 public void onError(Throwable t) {
682 GWT.log("Error creating pithos", t);
683 if (t instanceof RestException)
684 displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
686 displayError("System error Error creating pithos: " + t.getMessage());
690 protected void onUnauthorized(Response response) {
694 createPithos.setHeader("X-Auth-Token", getToken());
695 Scheduler.get().scheduleDeferred(createPithos);
699 * Creates an HTML fragment that places an image & caption together, for use
702 * @param imageProto an image prototype for an image
703 * @param caption the group caption
704 * @return the header HTML fragment
706 private String createHeaderHTML(AbstractImagePrototype imageProto, String caption) {
707 String captionHTML = "<table class='caption' cellpadding='0' "
708 + "cellspacing='0'>" + "<tr><td class='lcaption'>" + imageProto.getHTML()
709 + "</td><td id =" + caption +" class='rcaption'><b style='white-space:nowrap'> "
710 + caption + "</b></td></tr></table>";
714 protected void onWindowResized(int height) {
715 // Adjust the split panel to take up the available room in the window.
716 int newHeight = height - splitPanel.getAbsoluteTop() - 60;
719 splitPanel.setHeight("" + newHeight);
720 inner.setHeight("" + newHeight);
724 public void onResize(ResizeEvent event) {
725 int height = event.getHeight();
726 onWindowResized(height);
730 * Display an error message.
732 * @param msg the message to display
734 public void displayError(String msg) {
735 messagePanel.displayError(msg);
739 * Display a warning message.
741 * @param msg the message to display
743 public void displayWarning(String msg) {
744 messagePanel.displayWarning(msg);
748 * Display an informational message.
750 * @param msg the message to display
752 public void displayInformation(String msg) {
753 messagePanel.displayInformation(msg);
757 * Retrieve the fileList.
759 * @return the fileList
761 public FileList getFileList() {
766 * Retrieve the topPanel.
768 * @return the topPanel
770 TopPanel getTopPanel() {
775 * Retrieve the clipboard.
777 * @return the clipboard
779 public Clipboard getClipboard() {
783 public StatusPanel getStatusPanel() {
787 public String getToken() {
791 public static native void preventIESelection() /*-{
792 $doc.body.onselectstart = function () { return false; };
795 public static native void enableIESelection() /*-{
796 if ($doc.body.onselectstart != null)
797 $doc.body.onselectstart = null;
801 * @return the absolute path of the API root URL
803 public String getApiPath() {
804 Configuration conf = (Configuration) GWT.create(Configuration.class);
805 return conf.apiPath();
809 * History support for folder navigation
810 * adds a new browser history entry
814 public void updateHistory(String key){
815 // Replace any whitespace of the initial string to "+"
816 // String result = key.replaceAll("\\s","+");
817 // Add a new browser history entry.
818 // History.newItem(result);
819 History.newItem(key);
822 public void deleteFolder(final Folder folder) {
823 String path = getApiPath() + folder.getOwner() + "/" + folder.getContainer() + "?format=json&delimiter=/&prefix=" + folder.getPrefix();
824 RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, path);
825 builder.setHeader("If-Modified-Since", "0");
826 builder.setHeader("X-Auth-Token", getToken());
828 builder.sendRequest("", new RequestCallback() {
830 public void onResponseReceived(@SuppressWarnings("unused") Request request, Response response) {
831 if (response.getStatusCode() == Response.SC_OK) {
832 JSONValue json = JSONParser.parseStrict(response.getText());
833 JSONArray array = json.isArray();
836 deleteObject(folder, i, array);
842 public void onError(@SuppressWarnings("unused") Request request, Throwable exception) {
843 displayError("System error unable to delete folder: " + exception.getMessage());
847 catch (RequestException e) {
851 void deleteObject(final Folder folder, final int i, final JSONArray array) {
852 if (i < array.size()) {
853 JSONObject o = array.get(i).isObject();
854 if (o != null && !o.containsKey("subdir")) {
855 JSONString name = o.get("name").isString();
856 String path = "/" + folder.getContainer() + "/" + name.stringValue();
857 DeleteRequest delete = new DeleteRequest(getApiPath(), folder.getOwner(), path) {
859 public void onSuccess(@SuppressWarnings("unused") Resource result) {
860 deleteObject(folder, i + 1, array);
864 public void onError(Throwable t) {
866 displayError("System error unable to delete folder: " + t.getMessage());
870 protected void onUnauthorized(Response response) {
874 delete.setHeader("X-Auth-Token", getToken());
875 Scheduler.get().scheduleDeferred(delete);
877 else if (o != null) {
878 String subdir = o.get("subdir").isString().stringValue();
879 subdir = subdir.substring(0, subdir.length() - 1);
880 String path = getApiPath() + getUsername() + "/" + folder.getContainer() + "?format=json&delimiter=/&prefix=" + subdir;
881 RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, path);
882 builder.setHeader("If-Modified-Since", "0");
883 builder.setHeader("X-Auth-Token", getToken());
885 builder.sendRequest("", new RequestCallback() {
887 public void onResponseReceived(@SuppressWarnings("unused") Request request, Response response) {
888 if (response.getStatusCode() == Response.SC_OK) {
889 JSONValue json = JSONParser.parseStrict(response.getText());
890 JSONArray array2 = json.isArray();
891 if (array2 != null) {
892 int l = array.size();
893 for (int j=0; j<array2.size(); j++) {
894 array.set(l++, array2.get(j));
897 deleteObject(folder, i + 1, array);
902 public void onError(@SuppressWarnings("unused") Request request, Throwable exception) {
903 displayError("System error unable to delete folder: " + exception.getMessage());
907 catch (RequestException e) {
912 String path = folder.getUri();
913 DeleteRequest deleteFolder = new DeleteRequest(getApiPath(), getUsername(), path) {
915 public void onSuccess(@SuppressWarnings("unused") Resource result) {
916 updateFolder(folder.getParent(), true, new Command() {
919 public void execute() {
926 public void onError(Throwable t) {
928 if (t instanceof RestException) {
929 if (((RestException) t).getHttpStatusCode() != Response.SC_NOT_FOUND)
930 displayError("Unable to delete folder: "+((RestException) t).getHttpStatusText());
935 displayError("System error unable to delete folder: " + t.getMessage());
939 protected void onUnauthorized(Response response) {
943 deleteFolder.setHeader("X-Auth-Token", getToken());
944 Scheduler.get().scheduleDeferred(deleteFolder);
948 public FolderTreeView getFolderTreeView() {
949 return folderTreeView;
952 public void copyFiles(final Iterator<File> iter, final String targetUsername, final String targetUri, final Command callback) {
953 if (iter.hasNext()) {
954 File file = iter.next();
955 String path = targetUri + "/" + file.getName();
956 PutRequest copyFile = new PutRequest(getApiPath(), targetUsername, path) {
958 public void onSuccess(@SuppressWarnings("unused") Resource result) {
959 copyFiles(iter, targetUsername, targetUri, callback);
963 public void onError(Throwable t) {
965 if (t instanceof RestException) {
966 displayError("Unable to copy file: " + ((RestException) t).getHttpStatusText());
969 displayError("System error unable to copy file: "+t.getMessage());
973 protected void onUnauthorized(Response response) {
977 copyFile.setHeader("X-Auth-Token", getToken());
978 copyFile.setHeader("X-Copy-From", file.getUri());
979 Scheduler.get().scheduleDeferred(copyFile);
981 else if (callback != null) {
986 public void copySubfolders(final Iterator<Folder> iter, final String targetUsername, final String targetUri, final Command callback) {
987 if (iter.hasNext()) {
988 final Folder f = iter.next();
989 copyFolder(f, targetUsername, targetUri, new Command() {
992 public void execute() {
993 copySubfolders(iter, targetUsername, targetUri, callback);
997 else if (callback != null) {
1002 public void copyFolder(final Folder f, final String targetUsername, final String targetUri, final Command callback) {
1003 String path = targetUri + "/" + f.getName();
1004 PutRequest createFolder = new PutRequest(getApiPath(), targetUsername, path) {
1006 public void onSuccess(@SuppressWarnings("unused") Resource result) {
1007 GetRequest<Folder> getFolder = new GetRequest<Folder>(Folder.class, getApiPath(), f.getOwner(), "/" + f.getContainer() + "?format=json&delimiter=/&prefix=" + f.getPrefix(), f) {
1010 public void onSuccess(final Folder _f) {
1011 Iterator<File> iter = _f.getFiles().iterator();
1012 copyFiles(iter, targetUsername, targetUri + "/" + _f.getName(), new Command() {
1014 public void execute() {
1015 Iterator<Folder> iterf = _f.getSubfolders().iterator();
1016 copySubfolders(iterf, targetUsername, targetUri + "/" + _f.getName(), callback);
1022 public void onError(Throwable t) {
1024 if (t instanceof RestException) {
1025 displayError("Unable to get folder: " + ((RestException) t).getHttpStatusText());
1028 displayError("System error getting folder: " + t.getMessage());
1032 protected void onUnauthorized(Response response) {
1036 getFolder.setHeader("X-Auth-Token", getToken());
1037 Scheduler.get().scheduleDeferred(getFolder);
1041 public void onError(Throwable t) {
1043 if (t instanceof RestException) {
1044 displayError("Unable to create folder: " + ((RestException) t).getHttpStatusText());
1047 displayError("System error creating folder: " + t.getMessage());
1051 protected void onUnauthorized(Response response) {
1055 createFolder.setHeader("X-Auth-Token", getToken());
1056 createFolder.setHeader("Accept", "*/*");
1057 createFolder.setHeader("Content-Length", "0");
1058 createFolder.setHeader("Content-Type", "application/folder");
1059 Scheduler.get().scheduleDeferred(createFolder);
1062 public void addSelectionModel(@SuppressWarnings("rawtypes") SingleSelectionModel model) {
1063 selectionModels.add(model);
1066 public OtherSharedTreeView getOtherSharedTreeView() {
1067 return otherSharedTreeView;
1070 public void updateTrash(boolean showFiles, Command callback) {
1071 updateFolder(trash, showFiles, callback);
1074 public void updateGroupsNode() {
1075 groupTreeView.updateGroupNode(null);
1078 public void addGroup(String groupname) {
1079 Group newGroup = new Group(groupname);
1080 account.addGroup(newGroup);
1081 groupTreeView.updateGroupNode(null);
1084 public void removeGroup(Group group) {
1085 account.removeGroup(group);
1089 public TreeView getSelectedTree() {
1090 return selectedTree;
1093 public Folder getSelection() {
1094 return selectedTree.getSelection();
1097 public void showFolderStatistics(int folderFileCount) {
1098 numOfFiles.setHTML(String.valueOf(folderFileCount));
1101 public GroupTreeView getGroupTreeView() {
1102 return groupTreeView;
1105 public void sessionExpired() {
1106 new SessionExpiredDialog(this).center();