View Javadoc
1   /*
2    * Copyright (C) 2003-2007 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   */
17  package org.exoplatform.social.opensocial.service;
18  
19  import java.net.URLEncoder;
20  import java.util.ArrayList;
21  import java.util.Collections;
22  import java.util.Comparator;
23  import java.util.HashMap;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Locale;
27  import java.util.Map;
28  import java.util.ResourceBundle;
29  import java.util.Set;
30  import java.util.concurrent.CompletableFuture;
31  import java.util.concurrent.Future;
32  
33  import javax.servlet.http.HttpServletResponse;
34  
35  import org.apache.shindig.auth.AnonymousSecurityToken;
36  import org.apache.shindig.auth.SecurityToken;
37  import org.apache.shindig.protocol.DataCollection;
38  import org.apache.shindig.protocol.ProtocolException;
39  import org.apache.shindig.protocol.RestfulCollection;
40  import org.apache.shindig.protocol.model.SortOrder;
41  import org.apache.shindig.social.core.model.ListFieldImpl;
42  import org.apache.shindig.social.core.model.NameImpl;
43  import org.apache.shindig.social.core.model.UrlImpl;
44  import org.apache.shindig.social.opensocial.model.ListField;
45  import org.apache.shindig.social.opensocial.model.Person;
46  import org.apache.shindig.social.opensocial.model.Url;
47  import org.apache.shindig.social.opensocial.spi.AppDataService;
48  import org.apache.shindig.social.opensocial.spi.CollectionOptions;
49  import org.apache.shindig.social.opensocial.spi.GroupId;
50  import org.apache.shindig.social.opensocial.spi.PersonService;
51  import org.apache.shindig.social.opensocial.spi.UserId;
52  import org.exoplatform.commons.utils.ListAccess;
53  import org.exoplatform.container.PortalContainer;
54  import org.exoplatform.container.component.RequestLifeCycle;
55  import org.exoplatform.portal.config.UserPortalConfig;
56  import org.exoplatform.portal.config.UserPortalConfigService;
57  import org.exoplatform.portal.mop.SiteKey;
58  import org.exoplatform.portal.mop.navigation.Scope;
59  import org.exoplatform.portal.mop.user.UserNavigation;
60  import org.exoplatform.portal.mop.user.UserNode;
61  import org.exoplatform.portal.mop.user.UserPortal;
62  import org.exoplatform.portal.mop.user.UserPortalContext;
63  import org.exoplatform.services.log.ExoLogger;
64  import org.exoplatform.services.log.Log;
65  import org.exoplatform.social.core.identity.model.Identity;
66  import org.exoplatform.social.core.identity.model.Profile;
67  import org.exoplatform.social.core.service.LinkProvider;
68  import org.exoplatform.social.core.space.SpaceException;
69  import org.exoplatform.social.core.space.model.Space;
70  import org.exoplatform.social.core.space.spi.SpaceService;
71  import org.exoplatform.social.opensocial.auth.ExoBlobCrypterSecurityToken;
72  import org.exoplatform.social.opensocial.model.ExoPersonImpl;
73  import org.exoplatform.social.opensocial.model.SpaceImpl;
74  
75  import com.google.common.collect.Lists;
76  import com.google.inject.Inject;
77  import com.google.inject.Injector;
78  
79  /**
80   * The Class ExoPeopleService.
81   */
82  public class ExoPeopleService extends ExoService implements PersonService, AppDataService {
83  
84    /**
85     * The injector.
86     */
87    private Injector injector;
88  
89    /**
90     * The Logger.
91     */
92    private static final Log LOG = ExoLogger.getLogger(ExoPeopleService.class);
93    
94    /**
95     * Instantiates a new exo people service.
96     *
97     * @param injector the injector
98     */
99    @Inject
100   public ExoPeopleService(Injector injector) {
101     this.injector = injector;
102   }
103 
104   /**
105    * The Constant NAME_COMPARATOR.
106    */
107   private static final Comparator<Person> NAME_COMPARATOR = new Comparator<Person>() {
108     public int compare(Person person, Person person1) {
109       String name = person.getName().getFormatted();
110       String name1 = person1.getName().getFormatted();
111       return name.compareTo(name1);
112     }
113   };
114 
115 
116   /**
117    * {@inheritDoc}
118    */
119   public Future<RestfulCollection<Person>> getPeople(Set<UserId> userIds, GroupId groupId,
120                                                      CollectionOptions collectionOptions, Set<String> fields,
121                                                      SecurityToken token) throws ProtocolException {
122     List<Person> result = Lists.newArrayList();
123     try {
124       Set<Identity> idSet = getIdSet(userIds, groupId, token);
125 
126       Iterator<Identity> it = idSet.iterator();
127 
128       while (it.hasNext()) {
129         Identity id = it.next();
130         if (id != null) {
131           result.add(convertToPerson(id, fields, token));
132         }
133       }
134 
135       // We can pretend that by default the people are in top friends order
136       if (collectionOptions.getSortBy().equals(Person.Field.NAME.toString())) {
137         Collections.sort(result, NAME_COMPARATOR);
138       }
139 
140       if (collectionOptions.getSortOrder().equals(SortOrder.descending)) {
141         Collections.reverse(result);
142       }
143 
144       // TODO: The eXocontainer doesn't  have the concept of HAS_APP so
145       // we can't support any filters yet. We should fix this.
146 
147       int totalSize = result.size();
148       int fromIndex = collectionOptions.getFirst();
149       int toIndex = fromIndex + collectionOptions.getMax();
150       fromIndex = fromIndex < 0 ? 0 : fromIndex;
151       toIndex = totalSize < toIndex ? totalSize : toIndex;
152       toIndex = toIndex < fromIndex ? fromIndex : toIndex;
153       result = result.subList(fromIndex, toIndex);
154 
155       return CompletableFuture.completedFuture(new RestfulCollection<Person>(
156               result, collectionOptions.getFirst(), totalSize));
157     } catch (Exception je) {
158       throw new ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, je.getMessage(), je);
159     }
160   }
161 
162   /**
163    * {@inheritDoc}
164    */
165   public Future<Person> getPerson(UserId id, Set<String> fields, SecurityToken token) throws ProtocolException {
166     try {
167 
168       if (token instanceof AnonymousSecurityToken) {
169         throw new Exception(Integer.toString(HttpServletResponse.SC_FORBIDDEN));
170       }
171 
172       Identity identity = getIdentity(id.getUserId(token), true, token);
173 
174       return CompletableFuture.completedFuture(convertToPerson(identity, fields, token));
175     } catch (Exception e) {
176       throw new ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage(), e);
177     }
178   }
179 
180   @Override
181   public Future<Person> updatePerson(UserId userId, Person person, SecurityToken securityToken) throws ProtocolException {
182     throw new UnsupportedOperationException();
183   }
184 
185   /**
186    * Converts to person.
187    *
188    * @param identity the identity
189    * @param fields   the fields
190    * @return the person
191    * @throws Exception
192    */
193   private Person convertToPerson(Identity identity, Set<String> fields, SecurityToken st) throws Exception {
194     Person p = new ExoPersonImpl();
195     Profile pro = identity.getProfile();
196     PortalContainer container = getPortalContainer(st);
197     String host = getHost(st);
198     for (String field : fields) {
199       if (Person.Field.DISPLAY_NAME.toString().equals(field)) {
200         p.setDisplayName(pro.getFullName());
201       } else if (Person.Field.EMAILS.toString().equals(field)) {
202         p.setEmails(convertToListFields((List<Map>) pro.getProperty("emails")));
203       } else if (Person.Field.URLS.toString().equals(field)) {
204         p.setUrls(convertToURLListFields((List<Map>) pro.getProperty("urls")));
205       } else if (Person.Field.IMS.toString().equals(field)) {
206         p.setIms(convertToListFields((List<Map>) pro.getProperty("ims")));
207       } else if (Person.Field.ID.toString().equals(field)) {
208         p.setId(identity.getId());
209       } else if (Person.Field.NAME.toString().equals(field)) {
210         NameImpl name = new NameImpl();
211         name.setFamilyName((String) pro.getProperty(Profile.LAST_NAME));
212         name.setGivenName((String) pro.getProperty(Profile.FIRST_NAME));
213         name.setFormatted(name.getGivenName() + " " + name.getFamilyName());
214         p.setName(name);
215       } else if (Person.Field.PROFILE_URL.toString().equals(field)) {
216         String portalOwner = getPortalOwner(st);
217         String portalName = getPortalContainer(st).getName();
218         p.setProfileUrl(LinkProvider.getAbsoluteProfileUrl(identity.getRemoteId(), portalName, portalOwner, host));
219       } else if (Person.Field.GENDER.toString().equals(field)) {
220         String gender = (String) pro.getProperty("gender");
221         if (gender != null && gender.equals("female")) {
222           p.setGender(Person.Gender.female);
223         } else {
224           p.setGender(Person.Gender.male);
225         }
226       } else if (ExoPersonImpl.Field.SPACES.toString().equals(field)) {
227         List<org.exoplatform.social.opensocial.model.Space> spaces =
228                 new ArrayList<org.exoplatform.social.opensocial.model.Space>();
229         //TODO: dang.tung: improve space to person, it will auto convert field by shindig
230         SpaceService spaceService = (SpaceService) (container.getComponentInstanceOfType(SpaceService.class));
231         try {
232           ListAccess<Space> memberSpaceListAccess = spaceService.getMemberSpaces(identity.getRemoteId());
233           //Load 100 maximum only for performance gain
234           Space[] spaceArray = memberSpaceListAccess.load(0, 100);
235           SpaceImpl space = new SpaceImpl();
236           for(Space spaceObj : spaceArray) {
237               space.setId(spaceObj.getId());
238               space.setDisplayName(spaceObj.getDisplayName());
239               spaces.add(space);
240           }
241           ((ExoPersonImpl) p).setSpaces(spaces);
242         } catch (SpaceException e) {
243           LOG.warn("Failed to convert spaces!");
244         }
245       } else if (Person.Field.THUMBNAIL_URL.toString().equals(field)) {
246         String avatarUrl = pro.getAvatarUrl();
247         if (avatarUrl != null) {
248           p.setThumbnailUrl(host + avatarUrl);          
249         } else {
250           p.setThumbnailUrl(host + LinkProvider.PROFILE_DEFAULT_AVATAR_URL);
251         }
252       } else if (ExoPersonImpl.Field.PORTAL_CONTAINER.toString().equals(field)) {
253         ((ExoPersonImpl) p).setPortalName(container.getName());
254       } else if (ExoPersonImpl.Field.REST_CONTEXT.toString().equals(field)) {
255         ((ExoPersonImpl) p).setRestContextName(container.getRestContextName());
256       } else if (ExoPersonImpl.Field.HOST.toString().equals(field)) {
257         ((ExoPersonImpl) p).setHostName(getHost(st));
258       } else if (ExoPersonImpl.Field.PEOPLE_URI.toString().equals(field)) {
259         ((ExoPersonImpl) p).setPeopleUri(getURIForPeople(container, identity.getRemoteId()));
260       }
261     }
262     return p;
263   }
264 
265   /**
266    * Convert to list fields.
267    *
268    * @param fields the fields
269    * @return the list
270    */
271   private List<ListField> convertToListFields(List<Map> fields) {
272     List<ListField> l = new ArrayList<ListField>();
273     if (fields == null) {
274       return null;
275     }
276     for (Map field : fields) {
277       l.add(new ListFieldImpl((String) field.get("key"), (String) field.get("value")));
278     }
279     return l;
280   }
281 
282   /**
283    * Convert to url list fields.
284    *
285    * @param fields the fields
286    * @return the list
287    */
288   private List<Url> convertToURLListFields(List<Map> fields) {
289     List<Url> l = new ArrayList<Url>();
290     if (fields == null) {
291       return null;
292     }
293     for (Map field : fields) {
294       l.add(new UrlImpl((String) field.get("value"), (String) field.get("key"), (String) field.get("key")));
295     }
296     return l;
297   }
298 
299   /**
300    * {@inheritDoc}
301    */
302   public Future<DataCollection> getPersonData(Set<UserId> userIds, GroupId groupId, String appId,
303                                               Set<String> fields, SecurityToken token) throws ProtocolException {
304     try {
305       Set<Identity> idSet = getIdSet(userIds, groupId, token);
306       Map<String, Map<String, Object>> idToData = new HashMap<>();
307       Iterator<Identity> it = idSet.iterator();
308 
309       String gadgetId = clean(appId);
310       String instanceId = "" + token.getModuleId();
311       while (it.hasNext()) {
312         Identity id = it.next();
313         idToData.put(id.getId(), getPreferences(id.getRemoteId(), gadgetId, instanceId, fields));
314       }
315       return CompletableFuture.completedFuture(new DataCollection(idToData));
316     } catch (Exception e) {
317       throw new ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage(), e);
318     }
319   }
320 
321   /**
322    * Clean.
323    *
324    * @param url the url
325    * @return the string
326    */
327   private String clean(String url) {
328     url = URLEncoder.encode(url);
329     url = url.replaceAll(":", "");
330     return url;
331   }
332 
333   /**
334    * Gets the preferences.
335    *
336    * @param userID     the user id
337    * @param gadgetId   the gadget id
338    * @param instanceID the instance id
339    * @param fields     the fields
340    * @return the preferences
341    * @throws Exception the exception
342    */
343   private Map<String, Object> getPreferences(String userID, String gadgetId, String instanceID,
344                                              Set<String> fields) throws Exception {
345 //      PortalContainer pc = RootContainer.getInstance().getPortalContainer("portal");
346 //      UserGadgetStorage userGadgetStorage = (UserGadgetStorage) pc.getComponentInstanceOfType(UserGadgetStorage.class);
347 //
348 //      Map<String, String> values = userGadgetStorage.get(userID, gadgetId, instanceID, fields);
349     Map<String, Object> values = null;
350     return values;
351   }
352 
353   /**
354    * Save preferences.
355    *
356    * @param userID     the user id
357    * @param gadgetId   the gadget id
358    * @param instanceID the instance id
359    * @param values     the values
360    * @throws Exception the exception
361    */
362   private void savePreferences(String userID, String gadgetId, String instanceID,
363                                Map<String, Object> values) throws Exception {
364 //    PortalContainer pc = RootContainer.getInstance().getPortalContainer("portal");
365 //    UserGadgetStorage userGadgetStorage = (UserGadgetStorage) pc.getComponentInstanceOfType(UserGadgetStorage.class);
366 //
367 //    userGadgetStorage.save(userID, gadgetId, instanceID, values);
368   }
369 
370   /**
371    * Delete preferences.
372    *
373    * @param userID     the user id
374    * @param gadgetId   the gadget id
375    * @param instanceID the instance id
376    * @param keys       the keys
377    * @throws Exception the exception
378    */
379   private void deletePreferences(String userID, String gadgetId, String instanceID, Set<String> keys) throws Exception {
380 //    PortalContainer pc = RootContainer.getInstance().getPortalContainer("portal");
381 //    UserGadgetStorage userGadgetStorage = (UserGadgetStorage) pc.getComponentInstanceOfType(UserGadgetStorage.class);
382 //
383 //    userGadgetStorage.delete(userID, gadgetId, instanceID, keys);
384   }
385 
386   /**
387    * {@inheritDoc}
388    */
389   public Future<Void> deletePersonData(UserId user, GroupId groupId, String appId,
390                                        Set<String> fields, SecurityToken token) throws ProtocolException {
391     try {
392       if (token instanceof AnonymousSecurityToken) {
393         throw new Exception(Integer.toString(HttpServletResponse.SC_FORBIDDEN));
394       }
395       String userId = user.getUserId(token);
396 
397       String portalName = PortalContainer.getCurrentPortalContainerName();
398       if (token instanceof ExoBlobCrypterSecurityToken) {
399         portalName = ((ExoBlobCrypterSecurityToken) token).getPortalContainer();
400       }
401 
402       Identity id = getIdentity(userId, true, token);
403       String gadgetId = clean(appId);
404       String instanceId = "" + token.getModuleId();
405 
406       deletePreferences(id.getRemoteId(), gadgetId, instanceId, fields);
407     } catch (Exception e) {
408       throw new ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage(), e);
409     }
410     return CompletableFuture.completedFuture(null);
411   }
412 
413   /**
414    * {@inheritDoc}
415    */
416   public Future<Void> updatePersonData(UserId user, GroupId groupId, String appId, Set<String> fields,
417                                        Map<String, Object> values, SecurityToken token) throws ProtocolException {
418     //TODO: remove the fields that are in the fields list and not in the values map
419     try {
420       if (token instanceof AnonymousSecurityToken) {
421         throw new Exception(Integer.toString(HttpServletResponse.SC_FORBIDDEN));
422       }
423       String userId = user.getUserId(token);
424 
425       Identity id = getIdentity(userId, true, token);
426       String gadgetId = clean(appId);
427       String instanceId = "" + token.getModuleId();
428 
429       savePreferences(id.getRemoteId(), gadgetId, instanceId, values);
430     } catch (Exception e) {
431       throw new ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage(), e);
432     }
433     return CompletableFuture.completedFuture(null);
434   }
435 
436   /**
437    * Get the uri people.
438    * 
439    * @param portalContainer
440    * @param remoteId
441    * @return
442    * @throws Exception
443    */
444   private String getURIForPeople(PortalContainer portalContainer, String remoteId) throws Exception {
445     UserPortalConfigService userPortalConfigSer = (UserPortalConfigService)
446                                                   portalContainer.getComponentInstanceOfType(UserPortalConfigService.class);
447     
448     UserPortalContext NULL_CONTEXT = new UserPortalContext() {
449       public ResourceBundle getBundle(UserNavigation navigation) {
450         return null;
451       }
452 
453       public Locale getUserLocale() {
454         return Locale.ENGLISH;
455       }
456     };
457     StringBuffer stringBuffer = new StringBuffer();
458     RequestLifeCycle.begin(portalContainer);
459     try {
460       UserPortalConfig userPortalCfg = userPortalConfigSer.
461                                        getUserPortalConfig(userPortalConfigSer.getDefaultPortal(), remoteId, NULL_CONTEXT);
462       UserPortal userPortal = userPortalCfg.getUserPortal();
463       
464       SiteKey siteKey = SiteKey.portal(userPortalConfigSer.getDefaultPortal());
465       UserNavigation userNav = userPortal.getNavigation(siteKey);
466       UserNode rootNode = userPortal.getNode(userNav, Scope.ALL, null, null);
467       UserNode peopleNode = rootNode.getChild("people");
468       UserNode iteratorNode = peopleNode;
469       
470       if(iteratorNode != null){
471         while(iteratorNode !=null && iteratorNode.getParent()!=null){
472           stringBuffer.insert(0, iteratorNode.getName());
473           stringBuffer.insert(0, "/");
474           iteratorNode = iteratorNode.getParent();
475         }
476         stringBuffer.insert(0, userPortalConfigSer.getDefaultPortal());
477         stringBuffer.insert(0, "/");
478         stringBuffer.insert(0, portalContainer.getName());
479         stringBuffer.insert(0, "/");
480       }
481     } catch (Exception e){
482       LOG.debug("Could not get the people page node.");
483     }
484     finally{
485       RequestLifeCycle.end();
486     }
487     return stringBuffer.toString();
488   }
489 }