f66e75c0326661413436dbf3e7ea65e443ab2c48
[pithos-web-client] / src / gr / grnet / pithos / web / client / othersharedtree / OtherSharedTreeViewModel.java
1 /*
2  * Copyright 2011-2012 GRNET S.A. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or
5  * without modification, are permitted provided that the following
6  * conditions are met:
7  *
8  *   1. Redistributions of source code must retain the above
9  *      copyright notice, this list of conditions and the following
10  *      disclaimer.
11  *
12  *   2. Redistributions in binary form must reproduce the above
13  *      copyright notice, this list of conditions and the following
14  *      disclaimer in the documentation and/or other materials
15  *      provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  * The views and conclusions contained in the software and
31  * documentation are those of the authors and should not be
32  * interpreted as representing official policies, either expressed
33  * or implied, of GRNET S.A.
34  */
35
36 package gr.grnet.pithos.web.client.othersharedtree;
37
38 import com.google.gwt.cell.client.AbstractCell;
39 import com.google.gwt.cell.client.Cell;
40 import com.google.gwt.cell.client.TextCell;
41 import com.google.gwt.cell.client.ValueUpdater;
42 import com.google.gwt.core.client.GWT;
43 import com.google.gwt.core.client.Scheduler;
44 import com.google.gwt.event.dom.client.ContextMenuEvent;
45 import com.google.gwt.http.client.Request;
46 import com.google.gwt.http.client.Response;
47 import com.google.gwt.http.client.URL;
48 import com.google.gwt.safehtml.shared.SafeHtml;
49 import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
50 import com.google.gwt.text.shared.SafeHtmlRenderer;
51 import com.google.gwt.user.client.Command;
52 import com.google.gwt.user.client.ui.AbstractImagePrototype;
53 import com.google.gwt.view.client.ListDataProvider;
54 import com.google.gwt.view.client.SingleSelectionModel;
55 import com.google.gwt.view.client.TreeViewModel;
56 import gr.grnet.pithos.web.client.FolderContextMenu;
57 import gr.grnet.pithos.web.client.Pithos;
58 import gr.grnet.pithos.web.client.SharingUsers;
59 import gr.grnet.pithos.web.client.catalog.UpdateUserCatalogs;
60 import gr.grnet.pithos.web.client.catalog.UserCatalogs;
61 import gr.grnet.pithos.web.client.foldertree.AccountResource;
62 import gr.grnet.pithos.web.client.foldertree.Folder;
63 import gr.grnet.pithos.web.client.othersharedtree.OtherSharedTreeView.Templates;
64 import gr.grnet.pithos.web.client.rest.GetRequest;
65 import gr.grnet.pithos.web.client.rest.RestException;
66
67 import java.util.HashMap;
68 import java.util.Iterator;
69 import java.util.List;
70 import java.util.Map;
71
72 public class OtherSharedTreeViewModel implements TreeViewModel {
73
74     protected Pithos app;
75
76     final String dummy = "No shares by others yet";
77
78     private Cell<Folder> folderCell = new AbstractCell<Folder>(ContextMenuEvent.getType().getName()) {
79
80         @Override
81         public void render(Context context, Folder folder, SafeHtmlBuilder safeHtmlBuilder) {
82             String html = AbstractImagePrototype.create(OtherSharedTreeView.images.folderYellow()).getHTML();
83             safeHtmlBuilder.appendHtmlConstant(html).appendHtmlConstant("&nbsp;");
84             safeHtmlBuilder.append(Templates.INSTANCE.nameSpan(folder.getName()));
85         }
86
87         @Override
88         public void onBrowserEvent(Context context, com.google.gwt.dom.client.Element parent, final Folder folder, com.google.gwt.dom.client.NativeEvent event, ValueUpdater<Folder> valueUpdater) {
89             if(event.getType().equals(ContextMenuEvent.getType().getName())) {
90                 final int x = event.getClientX();
91                 final int y = event.getClientY();
92                 OtherSharedTreeViewModel.this.selectionModel.setSelected(folder, true);
93                 app.scheduleFolderHeadCommand(folder, new Command() {
94
95                     @Override
96                     public void execute() {
97                         FolderContextMenu menu = new FolderContextMenu(app, OtherSharedTreeView.images, app.getSelectedTree(), folder);
98                         menu.setPopupPosition(x, y);
99                         menu.show();
100                     }
101                 });
102             }
103         }
104     };
105
106     protected ListDataProvider<String> userLevelDataProviderForIDs = new ListDataProvider<String>();
107 //    protected ListDataProvider<String> userLevelDataProviderForDisplayNames = new ListDataProvider<String>();
108
109     protected Map<String, ListDataProvider<Folder>> userDataProviderMap = new HashMap<String, ListDataProvider<Folder>>();
110     private Map<Folder, ListDataProvider<Folder>> dataProviderMap = new HashMap<Folder, ListDataProvider<Folder>>();
111
112     protected SingleSelectionModel<Folder> selectionModel;
113
114     public OtherSharedTreeViewModel(Pithos _app, SingleSelectionModel<Folder> selectionModel) {
115         app = _app;
116         this.selectionModel = selectionModel;
117     }
118
119     @Override
120     public <T> NodeInfo<?> getNodeInfo(T value) {
121         if(value == null) {
122 //            LOG("getNodeInfo(null), calling fetchSharingUsers(null)");
123             fetchSharingUsers(null);
124 //            LOG("getNodeInfo(null), called fetchSharingUsers(null)");
125             return new DefaultNodeInfo<String>(userLevelDataProviderForIDs, new TextCell(new SafeHtmlRenderer<String>() {
126                 @Override
127                 public SafeHtml render(String object) {
128                     final String displayName = app.getDisplayNameForUserID(object);
129 //                    LOG("render(userID = "+object+"), displayName = " + displayName);
130                     SafeHtmlBuilder builder = new SafeHtmlBuilder();
131                     render(displayName, builder);
132                     return builder.toSafeHtml();
133                 }
134
135                 @Override
136                 public void render(String object, SafeHtmlBuilder builder) {
137                     if(!object.equals(dummy)) {
138 //                        LOG("render(userID [not dummy] = "+object+")");
139                         String html = AbstractImagePrototype.create(OtherSharedTreeView.images.myShared()).getHTML();
140                         builder.appendHtmlConstant(html).appendHtmlConstant("&nbsp;");
141                     }
142                     final String displayName = app.getDisplayNameForUserID(object);
143                     builder.append(OtherSharedTreeView.Templates.INSTANCE.nameSpan(object));
144                 }
145             }), null, null);
146         }
147         else if(value instanceof String) {
148 //            LOG("getNodeInfo(String "+value+"), called with userID");
149             final String userID = (String) value;
150
151             if(userDataProviderMap.get(userID) == null) {
152                 userDataProviderMap.put(userID, new ListDataProvider<Folder>());
153             }
154             final ListDataProvider<Folder> userIDDataProvider = userDataProviderMap.get(userID);
155             fetchSharedContainers(userID, userIDDataProvider, null);
156
157             return new DefaultNodeInfo<Folder>(userIDDataProvider, folderCell, selectionModel, null);
158         }
159         else {
160 //            LOG("getNodeInfo(Folder "+value+"), called with a folder");
161             final Folder f = (Folder) value;
162             if(dataProviderMap.get(f) == null) {
163                 dataProviderMap.put(f, new ListDataProvider<Folder>());
164             }
165             final ListDataProvider<Folder> dataProvider = dataProviderMap.get(f);
166             fetchFolder(f, dataProvider, false, null);
167             return new DefaultNodeInfo<Folder>(dataProvider, folderCell, selectionModel, null);
168         }
169     }
170
171 //    native void LOG(String message) /*-{
172 //      console.log(message );
173 //    }-*/;
174
175     private void fetchSharingUsers(final Command callback) {
176         GetRequest<SharingUsers> getSharingUsers = new GetRequest<SharingUsers>(SharingUsers.class, app.getApiPath(), "", "?format=json") {
177             @Override
178             public void onSuccess(final SharingUsers _result) {
179                 userLevelDataProviderForIDs.getList().clear();
180
181                 final List<String> userIDs = _result.getUserIDs();
182 //                for(String userID : userIDs) {
183 //                    LOG("fetchSharingUsers(): userID =" + userID);
184 //                }
185                 final List<String> userIDsWithUnknownDisplayNames = app.filterUserIDsWithUnknownDisplayName(userIDs);
186 //                for(String userID : userIDsWithUnknownDisplayNames) {
187 //                    LOG("fetchSharingUsers(): userID (with unknown name) =" + userID);
188 //                }
189
190                 userLevelDataProviderForIDs.getList().addAll(userIDs);
191                 if(userLevelDataProviderForIDs.getList().isEmpty()) {
192                     userLevelDataProviderForIDs.getList().add(dummy);
193 //                    userLevelDataProviderForDisplayNames.getList().add(dummy);
194                 } else {
195 //                    userLevelDataProviderForDisplayNames.getList().addAll(app.getUserDisplayNamesForIDs(userIDs));
196                 }
197
198                 if(userIDsWithUnknownDisplayNames.size() == 0) {
199                     // All display names are known, so we proceed directly
200                     fetchSharedContainers(userLevelDataProviderForIDs.getList().iterator(), callback);
201                 } else {
202                     // First fetch unknown display names and then proceed
203                     new UpdateUserCatalogs(app, userIDsWithUnknownDisplayNames) {
204                         @Override
205                         public void onSuccess(UserCatalogs requestedUserCatalogs, UserCatalogs updatedUserCatalogs) {
206                             fetchSharedContainers(userLevelDataProviderForIDs.getList().iterator(), callback);
207                         }
208
209                         @Override
210                         public void onError(Request request, Throwable t) {
211                             super.onError(request, t);
212                             app.setError(t);
213                         }
214                     }.scheduleDeferred();
215                 }
216             }
217
218             @Override
219             public void onError(Throwable t) {
220                 GWT.log("Error getting folder", t);
221                 app.setError(t);
222                 if(t instanceof RestException) {
223                     app.displayError("Error getting folder: " + ((RestException) t).getHttpStatusText());
224                 }
225                 else {
226                     app.displayError("System error fetching folder: " + t.getMessage());
227                 }
228             }
229
230             @Override
231             protected void onUnauthorized(Response response) {
232                 app.sessionExpired();
233             }
234         };
235         getSharingUsers.setHeader("X-Auth-Token", app.getUserToken());
236         Scheduler.get().scheduleDeferred(getSharingUsers);
237     }
238
239     protected void fetchSharedContainers(final Iterator<String> userIDsIterator, final Command callback) {
240         if(userIDsIterator.hasNext()) {
241             String userID = userIDsIterator.next();
242             if(userDataProviderMap.get(userID) == null) {
243                 userDataProviderMap.put(userID, new ListDataProvider<Folder>());
244             }
245             final ListDataProvider<Folder> dataProvider = userDataProviderMap.get(userID);
246             fetchSharedContainers(userID, dataProvider, new Command() {
247
248                 @Override
249                 public void execute() {
250                     fetchSharedContainers(userIDsIterator, callback);
251
252                 }
253             });
254         }
255         else if(callback != null) {
256             callback.execute();
257         }
258     }
259
260     @Override
261     public boolean isLeaf(Object o) {
262         if(o == null) {
263             boolean result =  userLevelDataProviderForIDs.getList().isEmpty();
264 //            LOG("isLeaf(null) ==> " + result);
265             return result;
266         }
267         else if(o instanceof Folder) {
268             Folder folder = (Folder) o;
269             boolean result = folder.getSubfolders().isEmpty();
270 //            LOG("isLeaf(Folder "+folder+") ==> " + result);
271             return result;
272         }
273         else {
274             ListDataProvider<Folder> dp = userDataProviderMap.get(o);
275             if(dp != null) {
276                 boolean result =  dp.getList().isEmpty();
277 //                LOG("isLeaf("+o.getClass().getName()+" "+o+") ==> " + result);
278                 return result;
279             }
280 //            LOG("isLeaf("+o.getClass().getName()+" "+o+") ==> (true)");
281             return true;
282         }
283     }
284
285     private void fetchSharedContainers(final String userID, final ListDataProvider<Folder> dataProvider, final Command callback) {
286         GetRequest<AccountResource> getUserSharedContainers = new GetRequest<AccountResource>(AccountResource.class, app.getApiPath(), userID, "?format=json") {
287
288             @Override
289             public void onSuccess(AccountResource _result) {
290                 final ListDataProvider<Folder> tempProvider = new ListDataProvider<Folder>();
291                 Iterator<Folder> folderIterator = _result.getContainers().iterator();
292                 fetchFolder(userID, folderIterator, tempProvider, new Command() {
293
294                     @Override
295                     public void execute() {
296                         dataProvider.getList().clear();
297                         dataProvider.getList().addAll(tempProvider.getList());
298                         if(callback != null) {
299                             callback.execute();
300                         }
301                     }
302                 });
303             }
304
305             @Override
306             public void onError(Throwable t) {
307                 GWT.log("Error getting account", t);
308                 app.setError(t);
309                 if(t instanceof RestException) {
310                     app.displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
311                 }
312                 else {
313                     app.displayError("System error fetching user data: " + t.getMessage());
314                 }
315             }
316
317             @Override
318             protected void onUnauthorized(Response response) {
319                 app.sessionExpired();
320             }
321         };
322         getUserSharedContainers.setHeader("X-Auth-Token", app.getUserToken());
323         Scheduler.get().scheduleDeferred(getUserSharedContainers);
324     }
325
326     protected void fetchFolder(final String userID, final Iterator<Folder> folderIterator, final ListDataProvider<Folder> dataProvider, final Command callback) {
327         if(folderIterator.hasNext()) {
328             final Folder f = folderIterator.next();
329
330             String path = "/" + f.getContainer() + "?format=json&delimiter=/&prefix=" + URL.encodeQueryString(f.getPrefix());
331             GetRequest<Folder> getFolder = new GetRequest<Folder>(Folder.class, app.getApiPath(), userID, path, f) {
332                 @Override
333                 public void onSuccess(Folder _result) {
334                     dataProvider.getList().add(_result);
335                     fetchFolder(userID, folderIterator, dataProvider, callback);
336                 }
337
338                 @Override
339                 public void onError(Throwable t) {
340                     GWT.log("Error getting folder", t);
341                     app.setError(t);
342                     if(t instanceof RestException) {
343                         app.displayError("Error getting folder: " + ((RestException) t).getHttpStatusText());
344                     }
345                     else {
346                         app.displayError("System error fetching folder: " + t.getMessage());
347                     }
348                 }
349
350                 @Override
351                 protected void onUnauthorized(Response response) {
352                     app.sessionExpired();
353                 }
354             };
355             getFolder.setHeader("X-Auth-Token", app.getUserToken());
356             Scheduler.get().scheduleDeferred(getFolder);
357         }
358         else if(callback != null) {
359             callback.execute();
360         }
361     }
362
363     public Folder getSelection() {
364         return selectionModel.getSelectedObject();
365     }
366
367     public void updateFolder(Folder folder, boolean showfiles, Command callback) {
368         if(dataProviderMap.get(folder) == null) {
369             dataProviderMap.put(folder, new ListDataProvider<Folder>());
370         }
371         final ListDataProvider<Folder> dataProvider = dataProviderMap.get(folder);
372         fetchFolder(folder, dataProvider, showfiles, callback);
373     }
374
375     public void fetchFolder(final Folder f, final ListDataProvider<Folder> dataProvider, final boolean showfiles, final Command callback) {
376         String path = "/" + f.getContainer() + "?format=json&delimiter=/&prefix=" + URL.encodeQueryString(f.getPrefix());
377         GetRequest<Folder> getFolder = new GetRequest<Folder>(Folder.class, app.getApiPath(), f.getOwnerID(), path, f) {
378             @Override
379             public void onSuccess(final Folder _result) {
380                 if(showfiles) {
381                     app.showFiles(_result);
382                 }
383                 Iterator<Folder> iter = _result.getSubfolders().iterator();
384                 fetchFolder(_result.getOwnerID(), iter, dataProvider, new Command() {
385                     @Override
386                     public void execute() {
387                         dataProvider.getList().clear();
388                         dataProvider.getList().addAll(_result.getSubfolders());
389                         app.getOtherSharedTreeView().updateChildren(f);
390                         if(callback != null) {
391                             callback.execute();
392                         }
393                     }
394                 });
395             }
396
397             @Override
398             public void onError(Throwable t) {
399                 GWT.log("Error getting folder", t);
400                 app.setError(t);
401                 if(t instanceof RestException) {
402                     app.displayError("Error getting folder: " + ((RestException) t).getHttpStatusText());
403                 }
404                 else {
405                     app.displayError("System error fetching folder: " + t.getMessage());
406                 }
407             }
408
409             @Override
410             protected void onUnauthorized(Response response) {
411                 app.sessionExpired();
412             }
413         };
414         getFolder.setHeader("X-Auth-Token", app.getUserToken());
415         Scheduler.get().scheduleDeferred(getFolder);
416     }
417
418     public void initialize(Command callback) {
419         fetchSharingUsers(callback);
420     }
421 }