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());
534 getFile.setHeader("X-Auth-Token", "0000");
535 Scheduler.get().scheduleDeferred(getFile);
538 fileList.setFiles(new ArrayList<File>(files));
542 * Parse and store the user credentials to the appropriate fields.
544 private boolean parseUserCredentials() {
545 username = Window.Location.getParameter("user");
546 token = Window.Location.getParameter("token");
547 Configuration conf = (Configuration) GWT.create(Configuration.class);
548 if (username == null || username.length() == 0 || token == null || token.length() == 0) {
549 String cookie = conf.authCookie();
550 String auth = Cookies.getCookie(cookie);
555 String[] authSplit = auth.split("\\" + conf.cookieSeparator(), 2);
556 if (authSplit.length != 2) {
560 username = authSplit[0];
561 token = authSplit[1];
564 Cookies.setCookie(conf.authCookie(), username + conf.cookieSeparator() + token);
569 * Redirect the user to the login page for authentication.
571 protected void authenticateUser() {
572 Configuration conf = (Configuration) GWT.create(Configuration.class);
573 Window.Location.assign(conf.loginUrl() + "?next=" + Window.Location.getHref());
576 protected void fetchAccount(final Command callback) {
577 String path = "?format=json";
579 GetRequest<AccountResource> getAccount = new GetRequest<AccountResource>(AccountResource.class, getApiPath(), username, path) {
581 public void onSuccess(AccountResource _result) {
583 if (callback != null)
588 public void onError(Throwable t) {
589 GWT.log("Error getting account", t);
590 if (t instanceof RestException)
591 displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
593 displayError("System error fetching user data: " + t.getMessage());
596 getAccount.setHeader("X-Auth-Token", token);
597 Scheduler.get().scheduleDeferred(getAccount);
600 public void updateStatistics() {
601 HeadRequest<AccountResource> headAccount = new HeadRequest<AccountResource>(AccountResource.class, getApiPath(), username, "", account) {
604 public void onSuccess(@SuppressWarnings("unused") AccountResource _result) {
609 public void onError(Throwable t) {
610 GWT.log("Error getting account", t);
611 if (t instanceof RestException)
612 displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
614 displayError("System error fetching user data: " + t.getMessage());
617 headAccount.setHeader("X-Auth-Token", token);
618 Scheduler.get().scheduleDeferred(headAccount);
621 protected void showStatistics() {
622 totalFiles.setHTML(String.valueOf(account.getNumberOfObjects()));
623 usedBytes.setHTML(String.valueOf(account.getFileSizeAsString()));
624 totalBytes.setHTML(String.valueOf(account.getQuotaAsString()));
625 usedPercent.setHTML(String.valueOf(account.getUsedPercentage()));
628 protected void createHomeContainer(final AccountResource _account, final Command callback) {
629 String path = "/" + Pithos.HOME_CONTAINER;
630 PutRequest createPithos = new PutRequest(getApiPath(), getUsername(), path) {
632 public void onSuccess(@SuppressWarnings("unused") Resource result) {
633 if (!_account.hasTrashContainer())
634 createTrashContainer(callback);
636 fetchAccount(callback);
640 public void onError(Throwable t) {
641 GWT.log("Error creating pithos", t);
642 if (t instanceof RestException)
643 displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
645 displayError("System error Error creating pithos: " + t.getMessage());
648 createPithos.setHeader("X-Auth-Token", getToken());
649 Scheduler.get().scheduleDeferred(createPithos);
652 protected void createTrashContainer(final Command callback) {
653 String path = "/" + Pithos.TRASH_CONTAINER;
654 PutRequest createPithos = new PutRequest(getApiPath(), getUsername(), path) {
656 public void onSuccess(@SuppressWarnings("unused") Resource result) {
657 fetchAccount(callback);
661 public void onError(Throwable t) {
662 GWT.log("Error creating pithos", t);
663 if (t instanceof RestException)
664 displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
666 displayError("System error Error creating pithos: " + t.getMessage());
669 createPithos.setHeader("X-Auth-Token", getToken());
670 Scheduler.get().scheduleDeferred(createPithos);
674 * Creates an HTML fragment that places an image & caption together, for use
677 * @param imageProto an image prototype for an image
678 * @param caption the group caption
679 * @return the header HTML fragment
681 private String createHeaderHTML(AbstractImagePrototype imageProto, String caption) {
682 String captionHTML = "<table class='caption' cellpadding='0' "
683 + "cellspacing='0'>" + "<tr><td class='lcaption'>" + imageProto.getHTML()
684 + "</td><td id =" + caption +" class='rcaption'><b style='white-space:nowrap'> "
685 + caption + "</b></td></tr></table>";
689 protected void onWindowResized(int height) {
690 // Adjust the split panel to take up the available room in the window.
691 int newHeight = height - splitPanel.getAbsoluteTop() - 60;
694 splitPanel.setHeight("" + newHeight);
695 inner.setHeight("" + newHeight);
699 public void onResize(ResizeEvent event) {
700 int height = event.getHeight();
701 onWindowResized(height);
705 * Display an error message.
707 * @param msg the message to display
709 public void displayError(String msg) {
710 messagePanel.displayError(msg);
714 * Display a warning message.
716 * @param msg the message to display
718 public void displayWarning(String msg) {
719 messagePanel.displayWarning(msg);
723 * Display an informational message.
725 * @param msg the message to display
727 public void displayInformation(String msg) {
728 messagePanel.displayInformation(msg);
732 * Retrieve the fileList.
734 * @return the fileList
736 public FileList getFileList() {
741 * Retrieve the topPanel.
743 * @return the topPanel
745 TopPanel getTopPanel() {
750 * Retrieve the clipboard.
752 * @return the clipboard
754 public Clipboard getClipboard() {
758 public StatusPanel getStatusPanel() {
762 public String getToken() {
766 public static native void preventIESelection() /*-{
767 $doc.body.onselectstart = function () { return false; };
770 public static native void enableIESelection() /*-{
771 if ($doc.body.onselectstart != null)
772 $doc.body.onselectstart = null;
776 * @return the absolute path of the API root URL
778 public String getApiPath() {
779 Configuration conf = (Configuration) GWT.create(Configuration.class);
780 return conf.apiPath();
784 * History support for folder navigation
785 * adds a new browser history entry
789 public void updateHistory(String key){
790 // Replace any whitespace of the initial string to "+"
791 // String result = key.replaceAll("\\s","+");
792 // Add a new browser history entry.
793 // History.newItem(result);
794 History.newItem(key);
797 public void deleteFolder(final Folder folder) {
798 String path = getApiPath() + folder.getOwner() + "/" + folder.getContainer() + "?format=json&delimiter=/&prefix=" + folder.getPrefix();
799 RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, path);
800 builder.setHeader("If-Modified-Since", "0");
801 builder.setHeader("X-Auth-Token", getToken());
803 builder.sendRequest("", new RequestCallback() {
805 public void onResponseReceived(@SuppressWarnings("unused") Request request, Response response) {
806 if (response.getStatusCode() == Response.SC_OK) {
807 JSONValue json = JSONParser.parseStrict(response.getText());
808 JSONArray array = json.isArray();
811 deleteObject(folder, i, array);
817 public void onError(@SuppressWarnings("unused") Request request, Throwable exception) {
818 displayError("System error unable to delete folder: " + exception.getMessage());
822 catch (RequestException e) {
826 void deleteObject(final Folder folder, final int i, final JSONArray array) {
827 if (i < array.size()) {
828 JSONObject o = array.get(i).isObject();
829 if (o != null && !o.containsKey("subdir")) {
830 JSONString name = o.get("name").isString();
831 String path = "/" + folder.getContainer() + "/" + name.stringValue();
832 DeleteRequest delete = new DeleteRequest(getApiPath(), folder.getOwner(), path) {
834 public void onSuccess(@SuppressWarnings("unused") Resource result) {
835 deleteObject(folder, i + 1, array);
839 public void onError(Throwable t) {
841 displayError("System error unable to delete folder: " + t.getMessage());
844 delete.setHeader("X-Auth-Token", getToken());
845 Scheduler.get().scheduleDeferred(delete);
847 else if (o != null) {
848 String subdir = o.get("subdir").isString().stringValue();
849 subdir = subdir.substring(0, subdir.length() - 1);
850 String path = getApiPath() + getUsername() + "/" + folder.getContainer() + "?format=json&delimiter=/&prefix=" + subdir;
851 RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, path);
852 builder.setHeader("If-Modified-Since", "0");
853 builder.setHeader("X-Auth-Token", getToken());
855 builder.sendRequest("", new RequestCallback() {
857 public void onResponseReceived(@SuppressWarnings("unused") Request request, Response response) {
858 if (response.getStatusCode() == Response.SC_OK) {
859 JSONValue json = JSONParser.parseStrict(response.getText());
860 JSONArray array2 = json.isArray();
861 if (array2 != null) {
862 int l = array.size();
863 for (int j=0; j<array2.size(); j++) {
864 array.set(l++, array2.get(j));
867 deleteObject(folder, i + 1, array);
872 public void onError(@SuppressWarnings("unused") Request request, Throwable exception) {
873 displayError("System error unable to delete folder: " + exception.getMessage());
877 catch (RequestException e) {
882 String path = folder.getUri();
883 DeleteRequest deleteFolder = new DeleteRequest(getApiPath(), getUsername(), path) {
885 public void onSuccess(@SuppressWarnings("unused") Resource result) {
886 updateFolder(folder.getParent(), true, new Command() {
889 public void execute() {
896 public void onError(Throwable t) {
898 if (t instanceof RestException) {
899 if (((RestException) t).getHttpStatusCode() != Response.SC_NOT_FOUND)
900 displayError("Unable to delete folder: "+((RestException) t).getHttpStatusText());
905 displayError("System error unable to delete folder: " + t.getMessage());
908 deleteFolder.setHeader("X-Auth-Token", getToken());
909 Scheduler.get().scheduleDeferred(deleteFolder);
913 public FolderTreeView getFolderTreeView() {
914 return folderTreeView;
917 public void copyFiles(final Iterator<File> iter, final String targetUsername, final String targetUri, final Command callback) {
918 if (iter.hasNext()) {
919 File file = iter.next();
920 String path = targetUri + "/" + file.getName();
921 PutRequest copyFile = new PutRequest(getApiPath(), targetUsername, path) {
923 public void onSuccess(@SuppressWarnings("unused") Resource result) {
924 copyFiles(iter, targetUsername, targetUri, callback);
928 public void onError(Throwable t) {
930 if (t instanceof RestException) {
931 displayError("Unable to copy file: " + ((RestException) t).getHttpStatusText());
934 displayError("System error unable to copy file: "+t.getMessage());
937 copyFile.setHeader("X-Auth-Token", getToken());
938 copyFile.setHeader("X-Copy-From", file.getUri());
939 Scheduler.get().scheduleDeferred(copyFile);
941 else if (callback != null) {
946 public void copySubfolders(final Iterator<Folder> iter, final String targetUsername, final String targetUri, final Command callback) {
947 if (iter.hasNext()) {
948 final Folder f = iter.next();
949 copyFolder(f, targetUsername, targetUri, new Command() {
952 public void execute() {
953 copySubfolders(iter, targetUsername, targetUri, callback);
957 else if (callback != null) {
962 public void copyFolder(final Folder f, final String targetUsername, final String targetUri, final Command callback) {
963 String path = targetUri + "/" + f.getName();
964 PutRequest createFolder = new PutRequest(getApiPath(), targetUsername, path) {
966 public void onSuccess(@SuppressWarnings("unused") Resource result) {
967 GetRequest<Folder> getFolder = new GetRequest<Folder>(Folder.class, getApiPath(), f.getOwner(), "/" + f.getContainer() + "?format=json&delimiter=/&prefix=" + f.getPrefix(), f) {
970 public void onSuccess(final Folder _f) {
971 Iterator<File> iter = _f.getFiles().iterator();
972 copyFiles(iter, targetUsername, targetUri + "/" + _f.getName(), new Command() {
974 public void execute() {
975 Iterator<Folder> iterf = _f.getSubfolders().iterator();
976 copySubfolders(iterf, targetUsername, targetUri + "/" + _f.getName(), callback);
982 public void onError(Throwable t) {
984 if (t instanceof RestException) {
985 displayError("Unable to get folder: " + ((RestException) t).getHttpStatusText());
988 displayError("System error getting folder: " + t.getMessage());
991 getFolder.setHeader("X-Auth-Token", getToken());
992 Scheduler.get().scheduleDeferred(getFolder);
996 public void onError(Throwable t) {
998 if (t instanceof RestException) {
999 displayError("Unable to create folder: " + ((RestException) t).getHttpStatusText());
1002 displayError("System error creating folder: " + t.getMessage());
1005 createFolder.setHeader("X-Auth-Token", getToken());
1006 createFolder.setHeader("Accept", "*/*");
1007 createFolder.setHeader("Content-Length", "0");
1008 createFolder.setHeader("Content-Type", "application/folder");
1009 Scheduler.get().scheduleDeferred(createFolder);
1012 public void addSelectionModel(@SuppressWarnings("rawtypes") SingleSelectionModel model) {
1013 selectionModels.add(model);
1016 public OtherSharedTreeView getOtherSharedTreeView() {
1017 return otherSharedTreeView;
1020 public void updateTrash(boolean showFiles, Command callback) {
1021 updateFolder(trash, showFiles, callback);
1024 public void updateGroupsNode() {
1025 groupTreeView.updateGroupNode(null);
1028 public void addGroup(String groupname) {
1029 Group newGroup = new Group(groupname);
1030 account.addGroup(newGroup);
1031 groupTreeView.updateGroupNode(null);
1034 public void removeGroup(Group group) {
1035 account.removeGroup(group);
1039 public TreeView getSelectedTree() {
1040 return selectedTree;
1043 public Folder getSelection() {
1044 return selectedTree.getSelection();
1047 public void showFolderStatistics(int folderFileCount) {
1048 numOfFiles.setHTML(String.valueOf(folderFileCount));
1051 public GroupTreeView getGroupTreeView() {
1052 return groupTreeView;