1c1a244a31eeba55264a4d8f417a3206cdb474d1
[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.json.client.JSONObject;
49 import com.google.gwt.safehtml.shared.SafeHtml;
50 import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
51 import com.google.gwt.text.shared.SafeHtmlRenderer;
52 import com.google.gwt.user.client.Command;
53 import com.google.gwt.user.client.ui.AbstractImagePrototype;
54 import com.google.gwt.view.client.ListDataProvider;
55 import com.google.gwt.view.client.SingleSelectionModel;
56 import com.google.gwt.view.client.TreeViewModel;
57 import gr.grnet.pithos.web.client.FolderContextMenu;
58 import gr.grnet.pithos.web.client.Pithos;
59 import gr.grnet.pithos.web.client.SharingUsers;
60 import gr.grnet.pithos.web.client.catalog.GetUserCatalogs;
61 import gr.grnet.pithos.web.client.catalog.UpdateUserCatalogs;
62 import gr.grnet.pithos.web.client.catalog.UserCatalogs;
63 import gr.grnet.pithos.web.client.foldertree.AccountResource;
64 import gr.grnet.pithos.web.client.foldertree.Folder;
65 import gr.grnet.pithos.web.client.othersharedtree.OtherSharedTreeView.Templates;
66 import gr.grnet.pithos.web.client.rest.GetRequest;
67 import gr.grnet.pithos.web.client.rest.RestException;
68
69 import java.util.HashMap;
70 import java.util.Iterator;
71 import java.util.List;
72 import java.util.Map;
73
74 public class OtherSharedTreeViewModel implements TreeViewModel {
75
76     protected Pithos app;
77
78     final String dummy = "No shares by others yet";
79
80     private Cell<Folder> folderCell = new AbstractCell<Folder>(ContextMenuEvent.getType().getName()) {
81
82         @Override
83         public void render(Context context, Folder folder, SafeHtmlBuilder safeHtmlBuilder) {
84             String html = AbstractImagePrototype.create(OtherSharedTreeView.images.folderYellow()).getHTML();
85             safeHtmlBuilder.appendHtmlConstant(html).appendHtmlConstant("&nbsp;");
86             safeHtmlBuilder.append(Templates.INSTANCE.nameSpan(folder.getName()));
87         }
88
89         @Override
90         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) {
91             if(event.getType().equals(ContextMenuEvent.getType().getName())) {
92                 final int x = event.getClientX();
93                 final int y = event.getClientY();
94                 OtherSharedTreeViewModel.this.selectionModel.setSelected(folder, true);
95                 app.scheduleFolderHeadCommand(folder, new Command() {
96
97                     @Override
98                     public void execute() {
99                         FolderContextMenu menu = new FolderContextMenu(app, OtherSharedTreeView.images, app.getSelectedTree(), folder);
100                         menu.setPopupPosition(x, y);
101                         menu.show();
102                     }
103                 });
104             }
105         }
106     };
107
108     protected ListDataProvider<String> userLevelDataProviderForIDs = new ListDataProvider<String>();
109 //    protected ListDataProvider<String> userLevelDataProviderForDisplayNames = new ListDataProvider<String>();
110
111     protected Map<String, ListDataProvider<Folder>> userDataProviderMap = new HashMap<String, ListDataProvider<Folder>>();
112     private Map<Folder, ListDataProvider<Folder>> dataProviderMap = new HashMap<Folder, ListDataProvider<Folder>>();
113
114     protected SingleSelectionModel<Folder> selectionModel;
115
116     public OtherSharedTreeViewModel(Pithos _app, SingleSelectionModel<Folder> selectionModel) {
117         app = _app;
118         this.selectionModel = selectionModel;
119     }
120
121     @Override
122     public <T> NodeInfo<?> getNodeInfo(T value) {
123         if(value == null) {
124 //            LOG("getNodeInfo(null), calling fetchSharingUsers(null)");
125             fetchSharingUsers(null);
126 //            LOG("getNodeInfo(null), called fetchSharingUsers(null)");
127             return new DefaultNodeInfo<String>(userLevelDataProviderForIDs, new TextCell(new SafeHtmlRenderer<String>() {
128                 @Override
129                 public SafeHtml render(String object) {
130                     final String displayName = app.getUserDisplayNameForID(object);
131 //                    LOG("render(userID = "+object+"), displayName = " + displayName);
132                     SafeHtmlBuilder builder = new SafeHtmlBuilder();
133                     render(displayName, builder);
134                     return builder.toSafeHtml();
135                 }
136
137                 @Override
138                 public void render(String object, SafeHtmlBuilder builder) {
139                     if(!object.equals(dummy)) {
140 //                        LOG("render(userID [not dummy] = "+object+")");
141                         String html = AbstractImagePrototype.create(OtherSharedTreeView.images.myShared()).getHTML();
142                         builder.appendHtmlConstant(html).appendHtmlConstant("&nbsp;");
143                     }
144                     final String displayName = app.getUserDisplayNameForID(object);
145                     builder.append(OtherSharedTreeView.Templates.INSTANCE.nameSpan(object));
146                 }
147             }), null, null);
148         }
149         else if(value instanceof String) {
150 //            LOG("getNodeInfo(String "+value+"), called with userID");
151             final String userID = (String) value;
152
153             if(userDataProviderMap.get(userID) == null) {
154                 userDataProviderMap.put(userID, new ListDataProvider<Folder>());
155             }
156             final ListDataProvider<Folder> userIDDataProvider = userDataProviderMap.get(userID);
157             fetchSharedContainers(userID, userIDDataProvider, null);
158
159             return new DefaultNodeInfo<Folder>(userIDDataProvider, folderCell, selectionModel, null);
160         }
161         else {
162 //            LOG("getNodeInfo(Folder "+value+"), called with a folder");
163             final Folder f = (Folder) value;
164             if(dataProviderMap.get(f) == null) {
165                 dataProviderMap.put(f, new ListDataProvider<Folder>());
166             }
167             final ListDataProvider<Folder> dataProvider = dataProviderMap.get(f);
168             fetchFolder(f, dataProvider, false, null);
169             return new DefaultNodeInfo<Folder>(dataProvider, folderCell, selectionModel, null);
170         }
171     }
172
173 //    native void LOG(String message) /*-{
174 //      console.log(message );
175 //    }-*/;
176
177     private void fetchSharingUsers(final Command callback) {
178         GetRequest<SharingUsers> getSharingUsers = new GetRequest<SharingUsers>(app, SharingUsers.class, app.getApiPath(), "", "?format=json") {
179             @Override
180             public void onSuccess(final SharingUsers _result) {
181                 userLevelDataProviderForIDs.getList().clear();
182
183                 final List<String> userIDs = _result.getUserIDs();
184 //                for(String userID : userIDs) {
185 //                    LOG("fetchSharingUsers(): userID =" + userID);
186 //                }
187                 final List<String> userIDsWithUnknownDisplayNames = app.filterUserIDsWithUnknownDisplayName(userIDs);
188 //                for(String userID : userIDsWithUnknownDisplayNames) {
189 //                    LOG("fetchSharingUsers(): userID (with unknown name) =" + userID);
190 //                }
191
192                 userLevelDataProviderForIDs.getList().addAll(userIDs);
193                 if(userLevelDataProviderForIDs.getList().isEmpty()) {
194                     userLevelDataProviderForIDs.getList().add(dummy);
195 //                    userLevelDataProviderForDisplayNames.getList().add(dummy);
196                 } else {
197 //                    userLevelDataProviderForDisplayNames.getList().addAll(app.getUserDisplayNamesForIDs(userIDs));
198                 }
199
200                 if(userIDsWithUnknownDisplayNames.size() == 0) {
201                     // All display names are known, so we proceed directly
202                     fetchSharedContainers(userLevelDataProviderForIDs.getList().iterator(), callback);
203                 } else {
204                     // First fetch unknown display names and then proceed
205                     new UpdateUserCatalogs(app, userIDsWithUnknownDisplayNames) {
206                         @Override
207                         public void onSuccess(UserCatalogs requestedUserCatalogs, UserCatalogs updatedUserCatalogs) {
208                             fetchSharedContainers(userLevelDataProviderForIDs.getList().iterator(), callback);
209                         }
210
211                         @Override
212                         public void onError(Request request, Throwable t) {
213                             super.onError(request, t);
214                             app.setError(t);
215                         }
216                     }.scheduleDeferred();
217                 }
218             }
219
220             @Override
221             public void onError(Throwable t) {
222                 GWT.log("Error getting folder", t);
223                 app.setError(t);
224                 if(t instanceof RestException) {
225                     app.displayError("Error getting folder: " + ((RestException) t).getHttpStatusText());
226                 }
227                 else {
228                     app.displayError("System error fetching folder: " + t.getMessage());
229                 }
230             }
231
232             @Override
233             protected void onUnauthorized(Response response) {
234                 app.sessionExpired();
235             }
236         };
237         getSharingUsers.setHeader("X-Auth-Token", app.getUserToken());
238         Scheduler.get().scheduleDeferred(getSharingUsers);
239     }
240
241     protected void fetchSharedContainers(final Iterator<String> userIDsIterator, final Command callback) {
242         if(userIDsIterator.hasNext()) {
243             String userID = userIDsIterator.next();
244             if(userDataProviderMap.get(userID) == null) {
245                 userDataProviderMap.put(userID, new ListDataProvider<Folder>());
246             }
247             final ListDataProvider<Folder> dataProvider = userDataProviderMap.get(userID);
248             fetchSharedContainers(userID, dataProvider, new Command() {
249
250                 @Override
251                 public void execute() {
252                     fetchSharedContainers(userIDsIterator, callback);
253
254                 }
255             });
256         }
257         else if(callback != null) {
258             callback.execute();
259         }
260     }
261
262     @Override
263     public boolean isLeaf(Object o) {
264         if(o == null) {
265             boolean result =  userLevelDataProviderForIDs.getList().isEmpty();
266 //            LOG("isLeaf(null) ==> " + result);
267             return result;
268         }
269         else if(o instanceof Folder) {
270             Folder folder = (Folder) o;
271             boolean result = folder.getSubfolders().isEmpty();
272 //            LOG("isLeaf(Folder "+folder+") ==> " + result);
273             return result;
274         }
275         else {
276             ListDataProvider<Folder> dp = userDataProviderMap.get(o);
277             if(dp != null) {
278                 boolean result =  dp.getList().isEmpty();
279 //                LOG("isLeaf("+o.getClass().getName()+" "+o+") ==> " + result);
280                 return result;
281             }
282 //            LOG("isLeaf("+o.getClass().getName()+" "+o+") ==> (true)");
283             return true;
284         }
285     }
286
287     private void fetchSharedContainers(final String userID, final ListDataProvider<Folder> dataProvider, final Command callback) {
288         GetRequest<AccountResource> getUserSharedContainers = new GetRequest<AccountResource>(app, AccountResource.class, app.getApiPath(), userID, "?format=json") {
289
290             @Override
291             public void onSuccess(AccountResource _result) {
292                 final ListDataProvider<Folder> tempProvider = new ListDataProvider<Folder>();
293                 Iterator<Folder> folderIterator = _result.getContainers().iterator();
294                 fetchFolder(userID, folderIterator, tempProvider, new Command() {
295
296                     @Override
297                     public void execute() {
298                         dataProvider.getList().clear();
299                         dataProvider.getList().addAll(tempProvider.getList());
300                         if(callback != null) {
301                             callback.execute();
302                         }
303                     }
304                 });
305             }
306
307             @Override
308             public void onError(Throwable t) {
309                 GWT.log("Error getting account", t);
310                 app.setError(t);
311                 if(t instanceof RestException) {
312                     app.displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
313                 }
314                 else {
315                     app.displayError("System error fetching user data: " + t.getMessage());
316                 }
317             }
318
319             @Override
320             protected void onUnauthorized(Response response) {
321                 app.sessionExpired();
322             }
323         };
324         getUserSharedContainers.setHeader("X-Auth-Token", app.getUserToken());
325         Scheduler.get().scheduleDeferred(getUserSharedContainers);
326     }
327
328     protected void fetchFolder(final String userID, final Iterator<Folder> folderIterator, final ListDataProvider<Folder> dataProvider, final Command callback) {
329         if(folderIterator.hasNext()) {
330             final Folder f = folderIterator.next();
331
332             String path = "/" + f.getContainer() + "?format=json&delimiter=/&prefix=" + URL.encodeQueryString(f.getPrefix());
333             GetRequest<Folder> getFolder = new GetRequest<Folder>(app, Folder.class, app.getApiPath(), userID, path, f) {
334                 @Override
335                 public void onSuccess(Folder _result) {
336                     dataProvider.getList().add(_result);
337                     fetchFolder(userID, folderIterator, dataProvider, callback);
338                 }
339
340                 @Override
341                 public void onError(Throwable t) {
342                     GWT.log("Error getting folder", t);
343                     app.setError(t);
344                     if(t instanceof RestException) {
345                         app.displayError("Error getting folder: " + ((RestException) t).getHttpStatusText());
346                     }
347                     else {
348                         app.displayError("System error fetching folder: " + t.getMessage());
349                     }
350                 }
351
352                 @Override
353                 protected void onUnauthorized(Response response) {
354                     app.sessionExpired();
355                 }
356             };
357             getFolder.setHeader("X-Auth-Token", app.getUserToken());
358             Scheduler.get().scheduleDeferred(getFolder);
359         }
360         else if(callback != null) {
361             callback.execute();
362         }
363     }
364
365     public Folder getSelection() {
366         return selectionModel.getSelectedObject();
367     }
368
369     public void updateFolder(Folder folder, boolean showfiles, Command callback) {
370         if(dataProviderMap.get(folder) == null) {
371             dataProviderMap.put(folder, new ListDataProvider<Folder>());
372         }
373         final ListDataProvider<Folder> dataProvider = dataProviderMap.get(folder);
374         fetchFolder(folder, dataProvider, showfiles, callback);
375     }
376
377     public void fetchFolder(final Folder f, final ListDataProvider<Folder> dataProvider, final boolean showfiles, final Command callback) {
378         String path = "/" + f.getContainer() + "?format=json&delimiter=/&prefix=" + URL.encodeQueryString(f.getPrefix());
379         GetRequest<Folder> getFolder = new GetRequest<Folder>(app, Folder.class, app.getApiPath(), f.getOwnerID(), path, f) {
380             @Override
381             public void onSuccess(final Folder _result) {
382                 if(showfiles) {
383                     app.showFiles(_result);
384                 }
385                 Iterator<Folder> iter = _result.getSubfolders().iterator();
386                 fetchFolder(_result.getOwnerID(), iter, dataProvider, new Command() {
387                     @Override
388                     public void execute() {
389                         dataProvider.getList().clear();
390                         dataProvider.getList().addAll(_result.getSubfolders());
391                         app.getOtherSharedTreeView().updateChildren(f);
392                         if(callback != null) {
393                             callback.execute();
394                         }
395                     }
396                 });
397             }
398
399             @Override
400             public void onError(Throwable t) {
401                 GWT.log("Error getting folder", t);
402                 app.setError(t);
403                 if(t instanceof RestException) {
404                     app.displayError("Error getting folder: " + ((RestException) t).getHttpStatusText());
405                 }
406                 else {
407                     app.displayError("System error fetching folder: " + t.getMessage());
408                 }
409             }
410
411             @Override
412             protected void onUnauthorized(Response response) {
413                 app.sessionExpired();
414             }
415         };
416         getFolder.setHeader("X-Auth-Token", app.getUserToken());
417         Scheduler.get().scheduleDeferred(getFolder);
418     }
419
420     public void initialize(Command callback) {
421         fetchSharingUsers(callback);
422     }
423 }