View Javadoc
1   /*
2    * Copyright (C) 2003-2011 eXo Platform SAS.
3    *
4    * This program is free software: you can redistribute it and/or modify
5    * it under the terms of the GNU Affero General Public License as published by
6    * the Free Software Foundation, either version 3 of the License, or
7    * (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 Affero General Public License for more details.
13   *
14   * You should have received a copy of the GNU Affero General Public License
15   * along with this program. If not, see <http://www.gnu.org/licenses/>.
16   */
17  
18  package org.exoplatform.social.core.storage.impl;
19  
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.HashMap;
25  import java.util.HashSet;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  import java.util.StringTokenizer;
31  
32  import javax.jcr.PropertyType;
33  import javax.jcr.RepositoryException;
34  import javax.jcr.Session;
35  import javax.jcr.nodetype.NodeType;
36  import javax.jcr.nodetype.NodeTypeManager;
37  import javax.jcr.nodetype.PropertyDefinition;
38  
39  import org.apache.commons.lang.StringEscapeUtils;
40  import org.chromattic.api.UndeclaredRepositoryException;
41  import org.chromattic.api.query.Ordering;
42  import org.chromattic.api.query.QueryBuilder;
43  import org.chromattic.api.query.QueryResult;
44  import org.chromattic.core.query.QueryImpl;
45  import org.chromattic.ext.ntdef.NTFile;
46  import org.chromattic.ext.ntdef.Resource;
47  import org.exoplatform.commons.utils.CommonsUtils;
48  import org.exoplatform.commons.utils.ListAccess;
49  import org.exoplatform.container.PortalContainer;
50  import org.exoplatform.services.log.ExoLogger;
51  import org.exoplatform.services.log.Log;
52  import org.exoplatform.services.organization.MembershipTypeHandler;
53  import org.exoplatform.services.organization.OrganizationService;
54  import org.exoplatform.services.organization.User;
55  import org.exoplatform.services.user.UserStateModel;
56  import org.exoplatform.services.user.UserStateService;
57  import org.exoplatform.social.core.chromattic.entity.ActivityProfileEntity;
58  import org.exoplatform.social.core.chromattic.entity.DisabledEntity;
59  import org.exoplatform.social.core.chromattic.entity.IdentityEntity;
60  import org.exoplatform.social.core.chromattic.entity.ProfileEntity;
61  import org.exoplatform.social.core.chromattic.entity.ProfileXpEntity;
62  import org.exoplatform.social.core.chromattic.entity.ProviderEntity;
63  import org.exoplatform.social.core.chromattic.entity.RelationshipEntity;
64  import org.exoplatform.social.core.chromattic.entity.RelationshipListEntity;
65  import org.exoplatform.social.core.chromattic.entity.SpaceRef;
66  import org.exoplatform.social.core.identity.IdentityResult;
67  import org.exoplatform.social.core.identity.SpaceMemberFilterListAccess.Type;
68  import org.exoplatform.social.core.identity.model.ActiveIdentityFilter;
69  import org.exoplatform.social.core.identity.model.Identity;
70  import org.exoplatform.social.core.identity.model.IdentityWithRelationship;
71  import org.exoplatform.social.core.identity.model.Profile;
72  import org.exoplatform.social.core.identity.model.Profile.AttachedActivityType;
73  import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider;
74  import org.exoplatform.social.core.identity.provider.SpaceIdentityProvider;
75  import org.exoplatform.social.core.model.AvatarAttachment;
76  import org.exoplatform.social.core.profile.ProfileFilter;
77  import org.exoplatform.social.core.relationship.model.Relationship;
78  import org.exoplatform.social.core.search.Sorting;
79  import org.exoplatform.social.core.service.LinkProvider;
80  import org.exoplatform.social.core.space.SpaceUtils;
81  import org.exoplatform.social.core.space.model.Space;
82  import org.exoplatform.social.core.storage.IdentityStorageException;
83  import org.exoplatform.social.core.storage.api.IdentityStorage;
84  import org.exoplatform.social.core.storage.api.RelationshipStorage;
85  import org.exoplatform.social.core.storage.api.SpaceStorage;
86  import org.exoplatform.social.core.storage.exception.NodeAlreadyExistsException;
87  import org.exoplatform.social.core.storage.exception.NodeNotFoundException;
88  import org.exoplatform.social.core.storage.query.JCRProperties;
89  import org.exoplatform.social.core.storage.query.WhereExpression;
90  
91  /**
92   * IdentityStorage implementation.
93   *
94   * @author <a href="mailto:alain.defrance@exoplatform.com">Alain Defrance</a>
95   * @version $Revision$
96   */
97  public class IdentityStorageImpl extends AbstractStorage implements IdentityStorage {
98  
99    /** Logger */
100   private static final Log LOG = ExoLogger.getLogger(IdentityStorageImpl.class);
101 
102   private IdentityStorage identityStorage;
103   private RelationshipStorage relationshipStorage;
104   private SpaceStorage spaceStorage;
105   private OrganizationService organizationService;
106   
107 
108   static enum PropNs {
109 
110     VOID("void"),
111     IM("im"),
112     PHONE("phone"),
113     URL("url"),
114     INDEX("index");
115 
116     private String prefix;
117     private static final String SEPARATOR = "-";
118 
119     private PropNs(final String prefix) {
120       this.prefix = prefix;
121     }
122 
123     public String nameOf(String prop) {
124       return String.format("%s%s%s", this.prefix, SEPARATOR, prop);
125     }
126 
127     public static PropNs nsOf(String fullName) {
128       int index = fullName.indexOf(SEPARATOR);
129       String prefix = (index >= 0 ? fullName.substring(0, index) : fullName);
130       return valueOf(prefix.toUpperCase());
131     }
132 
133     public static String cleanPrefix(String name) {
134       int index = name.indexOf(SEPARATOR) + 1;
135       return (index >= 0 ? name.substring(index) : name);
136     }
137 
138   }
139 
140   private Map<String, List<String>> createEntityParamMap(Object value) {
141 
142     Map<String, List<String>> params = new HashMap<String, List<String>>();
143     List<Map<String, String>> map = (List<Map<String, String>>) value;
144 
145     for (Map<String, String> data : map) {
146       
147       List<String> got = params.get(data.get("key"));
148       if (got == null) {
149         got = new ArrayList<String>();
150       }
151 
152       got.add(data.get("value"));
153       params.put(data.get("key"), got);
154 
155     }
156 
157     return params;
158   }
159 
160   private void fillProfileParam(ProfileEntity profileEntity, List<Map<String, String>> dataList, String name) {
161     for (String currentValue : profileEntity.getProperty(name)) {
162       Map<String, String> map = new HashMap<String, String>();
163       map.put("key", PropNs.cleanPrefix(name));
164       map.put("value", currentValue);
165       dataList.add(map);
166     }
167   }
168 
169   private void putParam(ProfileEntity profileEntity, Object value, PropNs propNs) {
170     Map<String, List<String>> params = createEntityParamMap(value);
171     for (String paramKey : params.keySet()) {
172       profileEntity.setProperty(propNs.nameOf(paramKey), params.get(paramKey));
173     }
174   }
175 
176   private void clearPropertyForPrefix(ProfileEntity profileEntity, String prefix) {
177     for (String key : profileEntity.getProperties().keySet()) {
178       if (key.startsWith(prefix)) {
179         profileEntity.setProperty(key, null);
180       }
181     }
182   }
183 
184   private void putParam(ProfileEntity profileEntity, Object value, String key) {
185     Map<String, List<String>> params = createEntityParamMap(value);
186     Iterator<List<String>> valuesItr = params.values().iterator();
187     List<String> values = null;
188     if (valuesItr.hasNext()) {
189       values = (List<String>) valuesItr.next();
190     }
191     profileEntity.setProperty(key, values);
192   }
193 
194   private IdentityStorage getStorage() {
195     return (identityStorage != null ? identityStorage : this);
196   }
197   
198   private RelationshipStorage getRelationshipStorage() {
199     if (relationshipStorage == null) {
200       relationshipStorage = (RelationshipStorage) PortalContainer.getInstance().
201                                                                   getComponentInstanceOfType(RelationshipStorage.class);
202     }
203 
204     return relationshipStorage;
205   }
206   
207   private OrganizationService getOrganizationService() {
208     if (organizationService == null) {
209       organizationService = (OrganizationService) PortalContainer.getInstance().getComponentInstanceOfType(OrganizationService.class);
210     }
211 
212     return organizationService;
213   }
214 
215   private SpaceStorage getSpaceStorage() {
216     if (spaceStorage == null) {
217       spaceStorage = (SpaceStorage) PortalContainer.getInstance().getComponentInstanceOfType(SpaceStorage.class);
218     }
219 
220     return spaceStorage;
221   }
222 
223   private QueryResult<ProfileEntity> getSpaceMemberIdentitiesByProfileFilterQueryBuilder(Space space,
224       final ProfileFilter profileFilter, Type type,  long offset, long limit, boolean count)
225       throws IdentityStorageException {
226 
227     if (offset < 0) {
228       offset = 0;
229     }
230 
231     String inputName = profileFilter.getName().replace(StorageUtils.ASTERISK_STR, StorageUtils.PERCENT_STR);
232     StorageUtils.processUsernameSearchPattern(inputName.trim());
233     List<Identity> excludedIdentityList = profileFilter.getExcludedIdentityList();
234 
235     QueryBuilder<ProfileEntity> builder = getSession().createQueryBuilder(ProfileEntity.class);
236     WhereExpression whereExpression = new WhereExpression();
237 
238     whereExpression.startGroup();
239     whereExpression
240         .like(JCRProperties.path, getProviderRoot().getProviders().get(
241                                 OrganizationIdentityProvider.NAME).getPath() + StorageUtils.SLASH_STR + StorageUtils.PERCENT_STR)
242         .and()
243         .not().equals(ProfileEntity.deleted, "true");;
244 
245     StorageUtils.applyExcludes(whereExpression, excludedIdentityList);
246     StorageUtils.applyFilter(whereExpression, profileFilter);
247 
248     List<Identity> relations = new ArrayList<Identity>();
249 
250     try {
251       Space gotSpace = getSpaceStorage().getSpaceById(space.getId());
252 
253       String[] members = null;
254       switch (type) {
255         case MEMBER:
256           members = gotSpace.getMembers();
257           break;
258         case MANAGER:
259           members = gotSpace.getManagers();
260           break;
261       }
262 
263       for (int i = 0; i <  members.length; i++){
264         Identity identity = findIdentity(OrganizationIdentityProvider.NAME, members[i]);
265         if (!relations.contains(identity)) {
266           relations.add(identity);
267         }
268       }
269 
270     } catch (IdentityStorageException e){
271       throw new IdentityStorageException(IdentityStorageException.Type.FAIL_TO_FIND_IDENTITY);
272     }
273     whereExpression.endGroup();
274     whereExpression.and();
275     StorageUtils.applyWhereFromIdentity(whereExpression, relations);
276 
277     builder.where(whereExpression.toString());
278     applyOrder(builder, profileFilter);
279 
280     if(count){
281      return builder.where(whereExpression.toString()).get().objects();
282     } else {
283      return builder.where(whereExpression.toString()).get().objects(offset, limit);
284     }
285 
286   }
287 
288   private void applyOrder(QueryBuilder builder, ProfileFilter profileFilter) {
289 
290     //
291     Sorting sorting;
292     if (profileFilter == null) {
293       sorting = new Sorting(Sorting.SortBy.TITLE, Sorting.OrderBy.ASC);
294     } else {
295       sorting = profileFilter.getSorting();
296     }
297 
298     //
299     Ordering ordering = Ordering.valueOf(sorting.orderBy.toString());
300     switch (sorting.sortBy) {
301       case DATE:
302         builder.orderBy(ProfileEntity.createdTime.getName(), ordering);
303         break;
304       case RELEVANCY:
305         builder.orderBy(JCRProperties.JCR_RELEVANCY.getName(), ordering);
306       case TITLE:        
307         builder.orderBy(ProfileEntity.lastName.getName(), ordering).orderBy(ProfileEntity.firstName.getName(), ordering);
308         break;
309     }
310   }
311 
312   /*
313    * Internal
314    */
315 
316   protected IdentityEntity _createIdentity(final Identity identity) throws NodeAlreadyExistsException {
317 
318     // Get provider
319     ProviderEntity providerEntity = getProviderRoot().getProvider(identity.getProviderId());
320 
321     // Create Identity
322     if (providerEntity.getIdentities().containsKey(identity.getRemoteId())) {
323       throw new NodeAlreadyExistsException("Identity " + identity.getRemoteId() + " already exists");
324     }
325 
326     IdentityEntity identityEntity = providerEntity.createIdentity();
327     providerEntity.getIdentities().put(identity.getRemoteId(), identityEntity);
328     identityEntity.setProviderId(identity.getProviderId());
329     identityEntity.setRemoteId(identity.getRemoteId());
330     identityEntity.setDeleted(identity.isDeleted());
331     identity.setId(identityEntity.getId());
332 
333     //
334     getSession().save();
335 
336     //
337     LOG.debug(String.format(
338         "Identity %s:%s (%s) created",
339         identity.getProviderId(),
340         identity.getRemoteId(),
341         identity.getId()
342     ));
343 
344     //
345     return identityEntity;
346   }
347 
348   protected void _saveIdentity(final Identity identity) throws NodeAlreadyExistsException, NodeNotFoundException {
349 
350     IdentityEntity identityEntity;
351 
352     identityEntity = _findById(IdentityEntity.class, identity.getId());
353 
354     // change name
355     if (!identityEntity.getName().equals(identity.getRemoteId())) {
356       identityEntity.setName(identity.getRemoteId());
357     }
358 
359     if (!identityEntity.getProviderId().equals(identity.getProviderId())) {
360 
361       // Get provider
362       ProviderEntity providerEntity = getProviderRoot().getProvider(identity.getProviderId());
363 
364       // Move identity
365       providerEntity.getIdentities().put(identity.getRemoteId(), identityEntity);
366     }
367 
368     //
369     identityEntity.setProviderId(identity.getProviderId());
370     identityEntity.setRemoteId(identity.getRemoteId());
371     identityEntity.setDeleted(identity.isDeleted());
372     identity.setId(identityEntity.getId());
373 
374     //
375     getSession().save();
376 
377 
378     //
379     LOG.debug(String.format(
380         "Identity %s:%s (%s) saved",
381         identity.getProviderId(),
382         identity.getRemoteId(),
383         identity.getId()
384     ));
385   }
386 
387   protected void _deleteIdentity(final Identity identity) throws NodeNotFoundException {
388 
389     //
390     if (identity == null || identity.getId() == null) {
391       throw new IllegalArgumentException();
392     }
393 
394     //
395     IdentityEntity identityEntity = _findById(IdentityEntity.class, identity.getId());
396     identity.setProviderId(identityEntity.getProviderId());
397     identity.setRemoteId(identityEntity.getRemoteId());
398 
399     //
400     getSession().remove(identityEntity);
401 
402     //
403     getSession().save();
404 
405     //
406     LOG.debug(String.format(
407         "Identity %s:%s (%s) deleted",
408         identity.getProviderId(),
409         identity.getRemoteId(),
410         identity.getId()
411     ));
412   }
413 
414   protected void _hardDeleteIdentity(final Identity identity) throws NodeNotFoundException {
415 
416     //
417     if (identity == null || identity.getId() == null) {
418       throw new IllegalArgumentException();
419     }
420 
421     IdentityEntity identityEntity = _findById(IdentityEntity.class, identity.getId());
422 
423     // Remove relationships
424     _removeRelationshipList(identityEntity.getSender());
425     _removeRelationshipList(identityEntity.getReceiver());
426     _removeRelationshipList(identityEntity.getRelationship());
427     _removeRelationshipList(identityEntity.getIgnore());
428     _removeRelationshipList(identityEntity.getIgnored());
429 
430     // Remove space membership
431     _removeSpaceMembership(SpaceStorageImpl.RefType.MANAGER, identityEntity);
432     _removeSpaceMembership(SpaceStorageImpl.RefType.MEMBER, identityEntity);
433     _removeSpaceMembership(SpaceStorageImpl.RefType.PENDING, identityEntity);
434     _removeSpaceMembership(SpaceStorageImpl.RefType.INVITED, identityEntity);
435 
436     // Remove identity
437     identity.setProviderId(identityEntity.getProviderId());
438     identity.setRemoteId(identityEntity.getRemoteId());
439     //
440     identityEntity.setDeleted(Boolean.TRUE);
441     Profile profile = loadProfile(new Profile(new Identity(identityEntity.getId())));
442     profile.setProperty(Profile.DELETED, "true");
443     saveProfile(profile);
444 
445     //
446     getSession().save();
447 
448     //
449     LOG.debug(String.format(
450         "Identity %s:%s (%s) deleted",
451         identity.getProviderId(),
452         identity.getRemoteId(),
453         identity.getId()
454     ));
455     
456   }
457 
458   protected void _removeRelationshipList(RelationshipListEntity listEntity) {
459 
460     for (RelationshipEntity relationshipEntity : listEntity.getRelationships().values()) {
461       getRelationshipStorage().removeRelationship(getRelationshipStorage().getRelationship(relationshipEntity.getId()));
462     }
463 
464   }
465 
466   protected void _removeSpaceMembership(SpaceStorageImpl.RefType refType, IdentityEntity identity) {
467 
468     for(SpaceRef ref : refType.refsOf(identity).getRefs().values()) {
469       Space space = getSpaceStorage().getSpaceById(ref.getSpaceRef().getId());
470       String[] ids = refType.idsOf(space);
471       if (ids == null || ids.length == 0) {
472         continue;
473       }
474       List<String> idList = new ArrayList<String>(Arrays.asList(ids));
475       idList.remove(identity.getRemoteId());
476       refType.setIds(space, idList.toArray(new String[]{}));
477       getSpaceStorage().saveSpace(space, false);
478     }
479 
480   }
481 
482   protected Profile _createProfile(final Profile profile) throws NodeNotFoundException {
483 
484     //
485     Identity identity = profile.getIdentity();
486     if (identity.getId() == null) {
487       throw new IllegalArgumentException();
488     }
489 
490     //
491     IdentityEntity identityEntity = _findById(IdentityEntity.class, identity.getId());
492     ProfileEntity profileEntity = identityEntity.createProfile();
493     
494     //
495     identityEntity.setProfile(profileEntity);
496     profile.setId(profileEntity.getId());
497     
498     //
499     ActivityProfileEntity activityPEntity = profileEntity.createActivityProfile();
500     profileEntity.setActivityProfile(activityPEntity);
501     
502     profile.setCreatedTime(System.currentTimeMillis());
503 
504     //
505     getSession().save();
506 
507     //
508     LOG.debug(String.format(
509         "Profile '%s' for %s:%s (%s) created",
510         profile.getId(),
511         identity.getProviderId(),
512         identity.getRemoteId(),
513         identity.getId()
514     ));
515 
516     _saveProfile(profile);
517 
518     return profile;
519 
520   }
521 
522   protected Profile _loadProfile(final Profile profile) throws NodeNotFoundException {
523 
524     //
525     if (profile.getIdentity().getId() == null) {
526       throw new IllegalArgumentException();
527     }
528 
529     //
530     String identityId = profile.getIdentity().getId();
531     IdentityEntity identityEntity = _findById(IdentityEntity.class, identityId);
532     ProfileEntity profileEntity = identityEntity.getProfile();
533     if (profileEntity == null) {
534       throw new NodeNotFoundException("The identity " + identityId + " has no profile");
535     }
536     profile.setId(profileEntity.getId());
537     populateProfile(profile, profileEntity);
538 
539     //
540     LOG.debug(String.format(
541         "Profile '%s' for %s:%s (%s) loaded",
542         profile.getId(),
543         identityEntity.getProviderId(),
544         identityEntity.getRemoteId(),
545         identityEntity.getId()
546     ));
547 
548     return profile;
549   }
550 
551   protected void _saveProfile(final Profile profile) throws NodeNotFoundException {
552 
553     if (profile.getIdentity().getId() == null || profile.getId() == null) {
554       throw new NullPointerException();
555     }
556 
557     //
558     ProfileEntity profileEntity = _findById(ProfileEntity.class, profile.getId());
559     String providerId = profile.getIdentity().getProviderId();
560 
561     Map<String, List<String>> phonesData = new HashMap<String, List<String>>();
562 
563     //
564     for (String key : profile.getProperties().keySet()) {
565       if (isJcrProperty(key)) {
566         Object value = profile.getProperty(key);
567         if (Profile.CONTACT_IMS.equals(key)) {
568           clearPropertyForPrefix(profileEntity, PropNs.IM.prefix);
569           putParam(profileEntity, value, PropNs.IM);
570         }
571         else if (Profile.CONTACT_PHONES.equals(key)) {
572           clearPropertyForPrefix(profileEntity, PropNs.PHONE.prefix);
573           putParam(profileEntity, value, PropNs.PHONE);
574         }
575         else if (Profile.CONTACT_URLS.equals(key)) {
576           clearPropertyForPrefix(profileEntity, PropNs.URL.prefix);
577           putParam(profileEntity, value, PropNs.URL.toString().toLowerCase());
578         }
579         else if (Profile.EXPERIENCES.equals(key)) {
580           // TODO : update instead of remove/add
581           for (ProfileXpEntity xpEntity : profileEntity.getXps().values()) {
582             _removeById(ProfileXpEntity.class, xpEntity.getId());
583           }
584 
585           // create
586           List<String> skills = new ArrayList<String>();
587           List<String> organizations = new ArrayList<String>();
588           List<String> jobsDescription = new ArrayList<String>();
589           for (Map<String, String> currentXp : (List<Map<String, String>>) value) {
590 
591             ProfileXpEntity xpEntity = profileEntity.createXp();
592             profileEntity.getXps().put(String.valueOf(System.currentTimeMillis()), xpEntity);
593             xpEntity.setSkills(currentXp.get(Profile.EXPERIENCES_SKILLS));
594             xpEntity.setPosition(currentXp.get(Profile.EXPERIENCES_POSITION));
595             xpEntity.setStartDate(currentXp.get(Profile.EXPERIENCES_START_DATE));
596             xpEntity.setEndDate(currentXp.get(Profile.EXPERIENCES_END_DATE));
597             xpEntity.setCompany(currentXp.get(Profile.EXPERIENCES_COMPANY));
598             xpEntity.setDescription(currentXp.get(Profile.EXPERIENCES_DESCRIPTION));
599 
600             //
601             if (xpEntity.getSkills() != null) {
602               skills.add(xpEntity.getSkills());
603             }
604             //
605             if (xpEntity.getCompany() != null) {
606               organizations.add(xpEntity.getCompany());
607             }
608             //
609             if (xpEntity.getDescription() != null) {
610               jobsDescription.add(xpEntity.getDescription());
611             }
612 
613           }
614           profileEntity.setProperty(PropNs.INDEX.nameOf(Profile.EXPERIENCES_SKILLS), skills);
615           profileEntity.setProperty(PropNs.INDEX.nameOf(Profile.EXPERIENCES_COMPANY), organizations);
616           profileEntity.setProperty(PropNs.INDEX.nameOf(Profile.EXPERIENCES_DESCRIPTION), jobsDescription);
617 
618         }
619         else if (Profile.AVATAR.equals(key)) {
620           AvatarAttachment attachement = (AvatarAttachment) value;
621           NTFile avatar = profileEntity.getAvatar();
622           if (avatar == null) {
623             avatar = profileEntity.createAvatar();
624             profileEntity.setAvatar(avatar);
625           }
626           avatar.setContentResource(new Resource(attachement.getMimeType(), null, attachement.getImageBytes()));
627         }
628         else {
629           //need to check here to avoid to set property with name: "void-skills"
630           if (Profile.EXPERIENCES_SKILLS.equals(key) == false) {
631             if (value != null) {
632               List<String> lvalue = new ArrayList<String>();
633               lvalue.add((String) value);
634               profileEntity.setProperty(PropNs.VOID.nameOf(key), lvalue);
635             } else {
636               profileEntity.setProperty(PropNs.VOID.nameOf(key), null);
637             }
638           }
639         }
640       }
641     }
642     
643     // TODO : find better
644     profileEntity.setParentId(profile.getIdentity().getId());
645     profileEntity.setCreatedTime(profile.getCreatedTime());
646 
647     // External profile
648     if (!OrganizationIdentityProvider.NAME.equals(providerId) && !SpaceIdentityProvider.NAME.equals(providerId)) {
649       profileEntity.setExternalUrl(profile.getUrl());
650       profileEntity.setExternalAvatarUrl(profile.getAvatarUrl());
651     }
652     
653     getSession().save();
654 
655     //
656     LOG.debug(String.format(
657         "Profile '%s' for %s:%s (%s) saved",
658         profile.getId(),
659         profileEntity.getIdentity().getProviderId(),
660         profileEntity.getIdentity().getRemoteId(),
661         profileEntity.getIdentity().getId()
662     ));
663   }
664 
665   protected Identity _findIdentity(final String providerId, final String remoteId) throws NodeNotFoundException {
666 
667     IdentityEntity identityEntity = _findIdentityEntity(providerId, remoteId);
668 
669     Identity identity = new Identity(providerId, remoteId);
670     identity.setDeleted(identityEntity.isDeleted());
671     identity.setEnable(_getMixin(identityEntity, DisabledEntity.class, false) == null);
672     identity.setId(identityEntity.getId());
673 
674     try {
675       _loadProfile(identity.getProfile());
676     } catch (NodeNotFoundException e) {
677       LOG.debug(e.getMessage(), e);
678     }
679 
680     //
681     LOG.debug(String.format(
682         "Identity  %s:%s (%s) found",
683         identity.getProviderId(),
684         identity.getRemoteId(),
685         identity.getId()
686     ));
687 
688     return identity;
689   }
690   /**
691    * Gets just identity information without profile
692    * Improve performance in the case only needs Identity's Id
693    * for create Activity and Comment also
694    * 
695    * @param providerId
696    * @param remoteId
697    * @param forceLoadProfile
698    * @return
699    */
700   protected Identity _findIdentityEntity(final String providerId, final String remoteId, boolean forceLoadProfile) {
701     IdentityEntity identityEntity;
702     Identity identity = null;
703     try {
704       if (!forceLoadProfile) {
705         identityEntity = _findIdentityEntity(providerId, remoteId);
706 
707         identity = new Identity(OrganizationIdentityProvider.NAME, remoteId);
708         identity.setId(identityEntity.getId());
709         identity.setEnable(_getMixin(identityEntity, DisabledEntity.class, false) == null);
710       } else {
711         identity = _findIdentity(providerId, remoteId);
712       }
713       
714     } catch (NodeNotFoundException e) {
715       LOG.warn(e.getMessage());
716       LOG.debug(e.getMessage(), e);
717     }
718     return identity;
719   }
720 
721   protected IdentityEntity _findIdentityEntity(final String providerId, final String remoteId) throws NodeNotFoundException {
722     ProviderEntity providerEntity;
723     try {
724       providerEntity = getProviderRoot().getProviders().get(providerId);
725     } catch (Exception ex) {
726       lifeCycle.getProviderRoot().set(null);
727       providerEntity = getProviderRoot().getProviders().get(providerId);
728     }
729 
730     if (providerEntity == null) {
731       throw new NodeNotFoundException("The node " + providerId + " doesn't exist");
732     }
733 
734     IdentityEntity identityEntity = providerEntity.getIdentities().get(remoteId);
735 
736     if (identityEntity == null) {
737       throw new NodeNotFoundException("The node " + providerId + "/" + remoteId + " doesn't exist");
738     }
739 
740     return identityEntity;
741 
742   }
743 
744   /*
745    * Private
746    */
747 
748   private Identity createIdentityFromEntity(final IdentityEntity identityEntity) {
749 
750     //
751     return getStorage().findIdentityById(identityEntity.getId());
752     
753   }
754 
755   private void populateProfile(final Profile profile, final ProfileEntity profileEntity) {
756 
757     IdentityEntity identity = profileEntity.getIdentity();
758 
759     String providerId = identity.getProviderId();
760     String remoteId = identity.getRemoteId();
761 
762     profile.setId(profileEntity.getId());
763     profile.setCreatedTime(profileEntity.getCreatedTime());
764 
765     List<Map<String, String>> phones = new ArrayList<Map<String,String>>();
766     List<Map<String, String>> ims = new ArrayList<Map<String,String>>();
767     List<Map<String, String>> urls = new ArrayList<Map<String,String>>();
768 
769     //handle try/catch here with test_test user on intranet. Makes sure it isn't broken process.
770     //The Problem: property definition is NULL /production/soc:providers/soc:organization/soc:test_test/soc:profile/soc:externalAvatarUrl []
771     try {
772       //
773       for (String name : profileEntity.getProperties().keySet()) {
774         if (isJcrProperty(name)) {
775           switch(PropNs.nsOf(name)) {
776             case VOID:
777             case INDEX:
778               profile.setProperty(PropNs.cleanPrefix(name), profileEntity.getProperty(name).get(0));
779               break;
780             case PHONE:
781               fillProfileParam(profileEntity, phones, name);
782               break;
783             case IM:
784               fillProfileParam(profileEntity, ims, name);
785               break;
786             case URL:
787               fillProfileParam(profileEntity, urls, name);
788               break;
789           }
790         }
791       }
792       
793     } catch(UndeclaredRepositoryException e) {
794       LOG.warn(e.getMessage()); 
795     }
796     
797     if (OrganizationIdentityProvider.NAME.equals(providerId) || SpaceIdentityProvider.NAME.equals(providerId)) {
798 
799       //
800       if (OrganizationIdentityProvider.NAME.equals(providerId)) {
801         profile.setUrl(LinkProvider.getUserProfileUri(remoteId));
802       } else if (SpaceIdentityProvider.NAME.equals(providerId)) {
803         spaceStorage = getSpaceStorage();
804         if (spaceStorage.getSpaceByPrettyName(remoteId) != null) {
805           profile.setUrl(LinkProvider.getSpaceUri(remoteId));
806         }
807       }
808       
809       //
810       NTFile avatar = profileEntity.getAvatar();
811       if (avatar != null) {
812         try {
813           String avatarPath = getSession().getPath(avatar);
814           long lastModified = avatar.getLastModified().getTime();
815           profile.setAvatarLastUpdated(lastModified);
816           // workaround: as dot character (.) breaks generated url (Ref: SOC-2283)
817           String avatarUrl = StorageUtils.encodeUrl(avatarPath) + "/?upd=" + lastModified;
818           profile.setAvatarUrl(LinkProvider.escapeJCRSpecialCharacters(avatarUrl));
819         } catch (Exception e) {
820           LOG.warn("Failed to build file url from fileResource: " + e.getMessage());
821         }
822       }
823 
824     }
825     // External profile
826     else {
827       profile.setUrl(profileEntity.getExternalUrl());
828       profile.setAvatarUrl(profileEntity.getExternalAvatarUrl());
829     }
830 
831     //
832     if (phones.size() > 0) {
833       profile.setProperty(Profile.CONTACT_PHONES, phones);
834     }
835     if (ims.size() > 0) {
836       profile.setProperty(Profile.CONTACT_IMS, ims);
837     }
838     if (urls.size() > 0) {
839       profile.setProperty(Profile.CONTACT_URLS, urls);
840     }
841 
842     //
843     List<Map<String, Object>> xpData = new ArrayList<Map<String, Object>>();
844     for (ProfileXpEntity xpEntity : profileEntity.getXps().values()){
845       Map<String, Object> xpMap = new HashMap<String, Object>();
846       xpMap.put(Profile.EXPERIENCES_SKILLS, xpEntity.getSkills());
847       xpMap.put(Profile.EXPERIENCES_POSITION, xpEntity.getPosition());
848       xpMap.put(Profile.EXPERIENCES_START_DATE, xpEntity.getStartDate());
849       xpMap.put(Profile.EXPERIENCES_END_DATE, xpEntity.getEndDate());
850       xpMap.put(Profile.EXPERIENCES_COMPANY, xpEntity.getCompany());
851       xpMap.put(Profile.EXPERIENCES_DESCRIPTION, xpEntity.getDescription());
852       xpMap.put(Profile.EXPERIENCES_IS_CURRENT, xpEntity.isCurrent());
853       xpData.add(xpMap);
854     }
855 
856     profile.setProperty(Profile.EXPERIENCES, xpData);
857   }
858 
859   /*
860    * Public
861    */
862 
863   /**
864    * {@inheritDoc}
865    */
866   public void saveIdentity(final Identity identity) throws IdentityStorageException {
867 
868     try {
869       try {
870         _findById(IdentityEntity.class, identity.getId());
871         _saveIdentity(identity);
872       }
873       catch (NodeNotFoundException e) {
874         _createIdentity(identity);
875         _saveIdentity(identity);
876       }
877     }
878     catch (NodeAlreadyExistsException e1) {
879       throw new IdentityStorageException(IdentityStorageException.Type.FAIL_TO_SAVE_IDENTITY, e1.getMessage(), e1);
880     }
881     catch (NodeNotFoundException e1) {
882       throw new IdentityStorageException(IdentityStorageException.Type.FAIL_TO_SAVE_IDENTITY, e1.getMessage(), e1);
883     }
884   }
885 
886   /**
887    * {@inheritDoc}
888    */
889   public Identity updateIdentity(final Identity identity) throws IdentityStorageException {
890 
891     //
892     saveIdentity(identity);
893 
894     //
895     return findIdentityById(identity.getId());
896 
897   }
898   
899   /**
900    * {@inheritDoc}
901    */
902   public void updateIdentityMembership(final String remoteId) throws IdentityStorageException {
903     //only clear Identity Caching when user updated Group, what raised by Organization Service
904   }
905 
906   public void hardDeleteIdentity(final Identity identity) throws IdentityStorageException {
907     try {
908       _hardDeleteIdentity(identity);
909     }
910     catch (NodeNotFoundException e) {
911       throw new IdentityStorageException(IdentityStorageException.Type.FAIL_TO_DELETE_IDENTITY, e.getMessage(), e);
912     }
913   }
914 
915   /**
916    * {@inheritDoc}
917    */
918   public Identity findIdentityById(final String nodeId) throws IdentityStorageException {
919 
920     try {
921 
922       //
923       IdentityEntity identityEntity = _findById(IdentityEntity.class, nodeId);
924       Identity identity = new Identity(nodeId);
925       identity.setDeleted(identityEntity.isDeleted());
926       identity.setRemoteId(identityEntity.getRemoteId());
927       identity.setProviderId(identityEntity.getProviderId());
928       identity.setEnable(_getMixin(identityEntity, DisabledEntity.class, false) == null);
929       //
930       return identity;
931     }
932     catch (NodeNotFoundException e) {
933       return null;
934     }
935   }
936 
937   /**
938    * {@inheritDoc}
939    */
940   public void deleteIdentity(final Identity identity) throws IdentityStorageException {
941     try {
942       _deleteIdentity(identity);
943     }
944     catch (NodeNotFoundException e) {
945       throw new IdentityStorageException(IdentityStorageException.Type.FAIL_TO_DELETE_IDENTITY, e.getMessage(), e);
946     }
947   }
948 
949   /**
950    * {@inheritDoc}
951    */
952   public Profile loadProfile(Profile profile) throws IdentityStorageException {
953     try {
954       profile = _loadProfile(profile);
955     }
956     catch (NodeNotFoundException e) {
957       try {
958         profile = _createProfile(profile);
959       }
960       catch (NodeNotFoundException e1) {
961         throw new IdentityStorageException(
962             IdentityStorageException.Type.FAIL_TO_FIND_IDENTITY_BY_NODE_ID,
963             e1.getMessage(), e1);
964       }
965     }
966 
967     profile.clearHasChanged();
968 
969     return profile;
970   }
971 
972   /**
973    * {@inheritDoc}
974    */
975   public Identity findIdentity(final String providerId, final String remoteId) throws IdentityStorageException {
976     try {
977       return _findIdentity(providerId, remoteId);
978     }
979     catch (NodeNotFoundException e) {
980       return null;
981     }
982   }
983 
984   /**
985    * {@inheritDoc}
986    */
987   public void saveProfile(final Profile profile) throws IdentityStorageException {
988 
989     try {
990       if (profile.getId() == null) {
991         _createProfile(profile);
992       }
993       else {
994         _saveProfile(profile);
995       }
996     }
997     catch (NodeNotFoundException e) {
998       LOG.debug(e.getMessage(), e); // should never be thrown
999     }
1000     profile.clearHasChanged();
1001   }
1002 
1003   /**
1004    * {@inheritDoc}
1005    */
1006   public void updateProfile(final Profile profile) throws IdentityStorageException {
1007     saveProfile(profile);
1008   }
1009 
1010   /**
1011    * {@inheritDoc}
1012    */
1013   public int getIdentitiesCount(final String providerId) throws IdentityStorageException {
1014     ProviderEntity providerEntity = getProviderRoot().getProviders().get(providerId);
1015     Iterator<IdentityEntity> iter = providerEntity.getIdentities().values().iterator();
1016     int number = 0;
1017     while (iter.hasNext()) {
1018       if (_getMixin(iter.next(), DisabledEntity.class, false) == null) {
1019         ++number;
1020       }
1021     }
1022     return number;
1023   }
1024 
1025   @Override
1026   public List<Identity> getIdentities(String providerId, char firstCharacterOfName, long offset, long limit) {
1027     if (firstCharacterOfName == '\u0000') {
1028       return this.getIdentities(providerId, offset, limit);
1029     } else {
1030       ProfileFilter filter = new ProfileFilter();
1031       filter.setFirstCharacterOfName(firstCharacterOfName);
1032       return getIdentitiesByFirstCharacterOfName(providerId, filter, offset, limit, true);
1033     }
1034   }
1035 
1036   @Override
1037   public List<Identity> getIdentities(String providerId, long offset, long limit) {
1038     // this is a placeholder impl to run tests
1039     return getIdentitiesByProfileFilter(providerId, new ProfileFilter(), offset, limit, true);
1040   }
1041 
1042   /**
1043    * {@inheritDoc}
1044    */
1045   public List<Identity> getIdentitiesByProfileFilter(
1046       final String providerId, final ProfileFilter profileFilter, long offset, long limit,
1047       boolean forceLoadOrReloadProfile)
1048       throws IdentityStorageException {
1049 
1050     if (offset < 0) {
1051       offset = 0;
1052     }
1053 
1054     String inputName = profileFilter.getName().replace(StorageUtils.ASTERISK_STR, StorageUtils.PERCENT_STR);
1055     StorageUtils.processUsernameSearchPattern(inputName.trim());
1056     List<Identity> excludedIdentityList = profileFilter.getExcludedIdentityList();
1057     List<Identity> listIdentity = new ArrayList<Identity>();
1058 
1059     QueryBuilder<ProfileEntity> builder = getSession().createQueryBuilder(ProfileEntity.class);
1060     WhereExpression whereExpression = new WhereExpression();
1061 
1062     whereExpression
1063         .like(JCRProperties.path, getProviderRoot().getProviders().get(
1064                                                     providerId).getPath() + StorageUtils.SLASH_STR + StorageUtils.PERCENT_STR)
1065         .and()
1066         .not().equals(ProfileEntity.deleted, "true");
1067 
1068     StorageUtils.applyExcludes(whereExpression, excludedIdentityList);
1069     StorageUtils.applyFilter(whereExpression, profileFilter);
1070 
1071     builder.where(whereExpression.toString());
1072     applyOrder(builder, profileFilter);
1073 
1074 
1075     QueryImpl<ProfileEntity> queryImpl = (QueryImpl<ProfileEntity>) builder.get();
1076     ((org.exoplatform.services.jcr.impl.core.query.QueryImpl) queryImpl.getNativeQuery()).setCaseInsensitiveOrder(true);
1077     
1078     QueryResult<ProfileEntity> results = queryImpl.objects(offset, limit);
1079     while (results.hasNext()) {
1080 
1081       ProfileEntity profileEntity = results.next();
1082       Identity identity = createIdentityFromEntity(profileEntity.getIdentity());
1083       if (! identity.isEnable()) {
1084         continue;
1085       }
1086       Profile profile = getStorage().loadProfile(new Profile(identity));
1087       identity.setProfile(profile);
1088       listIdentity.add(identity);
1089 
1090     }
1091 
1092     return listIdentity;
1093   }
1094   
1095   /**
1096    * {@inheritDoc}
1097    */
1098   @Override
1099   public List<Identity> getIdentitiesForMentions(
1100       final String providerId, final ProfileFilter profileFilter,
1101       org.exoplatform.social.core.relationship.model.Relationship.Type type,
1102       long offset, long limit,
1103       boolean forceLoadOrReloadProfile)
1104       throws IdentityStorageException {
1105     if(type != null) {
1106       switch (type) {
1107       case CONFIRMED:
1108         return getRelationshipStorage().getConnectionsByFilter(null, profileFilter, offset, limit);
1109       case INCOMING:
1110         return getRelationshipStorage().getIncomingByFilter(null, profileFilter, offset, limit);
1111       case OUTGOING:
1112         return getRelationshipStorage().getOutgoingByFilter(null, profileFilter, offset, limit);
1113       default:
1114         break;
1115       }
1116     }
1117 
1118     if (offset < 0) {
1119       offset = 0;
1120     }
1121 
1122     QueryResult<ProfileEntity> results = getFilteredProfiles(providerId, profileFilter);
1123 
1124     //QueryResult<ProfileEntity> results = builder.get().objects();
1125     
1126     //
1127     IdentityResult identityResult = new IdentityResult(offset, limit, results.size());
1128     
1129     //
1130     while (results.hasNext()) {
1131 
1132       ProfileEntity profileEntity = results.next();
1133       Identity identity = createIdentityFromEntity(profileEntity.getIdentity());
1134       if (! identity.isEnable()) {
1135         continue;
1136       }
1137       Profile profile = getStorage().loadProfile(new Profile(identity));
1138       identity.setProfile(profile);
1139       
1140       identityResult.add(identity);
1141       
1142       //
1143       if (identityResult.addMore() == false) {
1144         break;
1145       }
1146       
1147 
1148     }
1149 
1150     return identityResult.result();
1151   }
1152 
1153   @Override
1154   public int getIdentitiesForMentionsCount(String providerId,
1155                                            ProfileFilter profileFilter,
1156                                            org.exoplatform.social.core.relationship.model.Relationship.Type type) throws IdentityStorageException {
1157     if(type != null) {
1158       switch (type) {
1159       case CONFIRMED:
1160         return getRelationshipStorage().getConnectionsCountByFilter(null, profileFilter);
1161       case INCOMING:
1162         return getRelationshipStorage().getIncomingCountByFilter(null, profileFilter);
1163       case OUTGOING:
1164         return getRelationshipStorage().getOutgoingCountByFilter(null, profileFilter);
1165       default:
1166         break;
1167       }
1168     }
1169 
1170     return getFilteredProfiles(providerId, profileFilter).size();
1171   }
1172 
1173   private QueryResult<ProfileEntity> getFilteredProfiles(final String providerId, final ProfileFilter profileFilter) {
1174     QueryBuilder<ProfileEntity> builder = getSession().createQueryBuilder(ProfileEntity.class);
1175     WhereExpression whereExpression = new WhereExpression();
1176 
1177     whereExpression
1178         .like(JCRProperties.path, getProviderRoot().getProviders().get(
1179                                                     providerId).getPath() + StorageUtils.SLASH_STR + StorageUtils.PERCENT_STR)
1180         .and()
1181         .not().equals(ProfileEntity.deleted, "true");
1182 
1183     if (profileFilter != null) {
1184       List<Identity> excludedIdentityList = profileFilter.getExcludedIdentityList();
1185       StorageUtils.applyExcludes(whereExpression, excludedIdentityList);
1186       StorageUtils.applyFilter(whereExpression, profileFilter);
1187     }
1188 
1189     builder.where(whereExpression.toString());
1190     applyOrder(builder, profileFilter);
1191 
1192     QueryImpl<ProfileEntity> queryImpl = (QueryImpl<ProfileEntity>) builder.get();
1193     ((org.exoplatform.services.jcr.impl.core.query.QueryImpl) queryImpl.getNativeQuery()).setCaseInsensitiveOrder(true);
1194     
1195     QueryResult<ProfileEntity> results = queryImpl.objects();
1196     return results;
1197   }
1198   
1199 
1200   /**
1201    * {@inheritDoc}
1202    */
1203   public List<Identity> getIdentitiesForUnifiedSearch(final String providerId, 
1204                                                       ProfileFilter profileFilter,
1205                                                       long offset, long limit) throws IdentityStorageException {
1206     if (offset < 0) {
1207       offset = 0;
1208     }
1209     
1210     List<Identity> listIdentity = new ArrayList<Identity>();
1211 
1212     QueryBuilder<ProfileEntity> builder = getSession().createQueryBuilder(ProfileEntity.class);
1213     WhereExpression whereExpression = new WhereExpression();
1214     //
1215     whereExpression
1216         .like(JCRProperties.path, getProviderRoot().getProviders().get(providerId).getPath() + StorageUtils.SLASH_STR + StorageUtils.PERCENT_STR)
1217         .and()
1218         .not().equals(ProfileEntity.deleted, "true");
1219     
1220     //apply unified search
1221     _applyUnifiedSearchFilter(whereExpression, profileFilter);
1222     
1223     builder.where(whereExpression.toString());
1224     applyOrder(builder, profileFilter);
1225 
1226     QueryImpl<ProfileEntity> queryImpl = (QueryImpl<ProfileEntity>) builder.get();
1227     
1228     QueryResult<ProfileEntity> results = queryImpl.objects(offset, limit);
1229     while (results.hasNext()) {
1230 
1231       ProfileEntity profileEntity = results.next();
1232       Identity identity = createIdentityFromEntity(profileEntity.getIdentity());
1233       if (! identity.isEnable()) {
1234         continue;
1235       }
1236       Profile profile = getStorage().loadProfile(new Profile(identity));
1237       identity.setProfile(profile);
1238       listIdentity.add(identity);
1239 
1240     }
1241 
1242     return listIdentity;
1243   }
1244   
1245   /**
1246    * {@inheritDoc}
1247    */
1248   public int getIdentitiesByProfileFilterCount(final String providerId, final ProfileFilter profileFilter)
1249       throws IdentityStorageException {
1250 
1251     List<Identity> excludedIdentityList = profileFilter.getExcludedIdentityList();
1252 
1253     QueryBuilder<ProfileEntity> builder = getSession().createQueryBuilder(ProfileEntity.class);
1254     WhereExpression whereExpression = new WhereExpression();
1255 
1256     whereExpression
1257         .like(JCRProperties.path, getProviderRoot().getProviders().get(
1258                                                     providerId).getPath() + StorageUtils.SLASH_STR + StorageUtils.PERCENT_STR)
1259         .and()
1260         .not().equals(ProfileEntity.deleted, "true");
1261 
1262     StorageUtils.applyExcludes(whereExpression, excludedIdentityList);
1263     StorageUtils.applyFilter(whereExpression, profileFilter);
1264 
1265     builder.where(whereExpression.toString());
1266 
1267     QueryResult<ProfileEntity> results = builder.get().objects();
1268 
1269     return getCountFromQueryResult(results);
1270 
1271   }
1272 
1273   private int getCountFromQueryResult(QueryResult<ProfileEntity> results) {
1274     int count = 0;
1275     while (results.hasNext()) {
1276       ProfileEntity profileEntity = results.next();
1277       
1278       //ignore if the user is disabled
1279       if (_getMixin(profileEntity.getIdentity(), DisabledEntity.class, false) != null) {
1280         continue;
1281       }
1282       
1283       count++;
1284     }
1285     return count;
1286   }
1287   
1288   /**
1289    * {@inheritDoc}
1290    */
1291   public int getIdentitiesByFirstCharacterOfNameCount(final String providerId, final ProfileFilter profileFilter)
1292       throws IdentityStorageException {
1293 
1294     List<Identity> excludedIdentityList = profileFilter.getExcludedIdentityList();
1295 
1296     QueryBuilder<ProfileEntity> builder = getSession().createQueryBuilder(ProfileEntity.class);
1297     WhereExpression whereExpression = new WhereExpression();
1298 
1299     whereExpression
1300         .like(JCRProperties.path, getProviderRoot().getProviders().get(
1301                                                     providerId).getPath() + StorageUtils.SLASH_STR + StorageUtils.PERCENT_STR)
1302         .and().not().equals(ProfileEntity.deleted, "true");
1303 
1304     StorageUtils.applyExcludes(whereExpression, excludedIdentityList);
1305     StorageUtils.applyFilter(whereExpression, profileFilter);
1306 
1307     builder.where(whereExpression.toString());
1308     
1309     QueryResult<ProfileEntity> results = builder.get().objects();
1310     
1311     return getCountFromQueryResult(results);
1312   }
1313 
1314   /**
1315    * {@inheritDoc}
1316    */
1317   public List<Identity> getIdentitiesByFirstCharacterOfName(final String providerId, final ProfileFilter profileFilter,
1318       long offset, long limit, boolean forceLoadOrReloadProfile) throws IdentityStorageException {
1319 
1320 
1321     QueryResult<ProfileEntity> results = getFilteredProfiles(providerId, profileFilter);
1322     
1323     //
1324     IdentityResult identityResult = new IdentityResult(offset, limit, results.size());
1325     
1326     //
1327     while (results.hasNext()) {
1328 
1329       ProfileEntity profileEntity = results.next();
1330       Identity identity = createIdentityFromEntity(profileEntity.getIdentity());
1331       if (! identity.isEnable()) {
1332         continue;
1333       }
1334       Profile profile = getStorage().loadProfile(new Profile(identity));
1335       identity.setProfile(profile);
1336       
1337       identityResult.add(identity);
1338       
1339       //
1340       if (identityResult.addMore() == false) {
1341         break;
1342       }
1343 
1344     }
1345 
1346     return identityResult.result();
1347 
1348   }
1349   
1350   
1351 
1352   /**
1353    * {@inheritDoc}
1354    */
1355   public String getType(final String nodetype, final String property) {
1356 
1357     // TODO : move to appropriate classe
1358 
1359     Session jcrSession = getSession().getJCRSession();
1360     try {
1361 
1362       NodeTypeManager ntManager = jcrSession.getWorkspace().getNodeTypeManager();
1363       NodeType nt = ntManager.getNodeType(nodetype);
1364       PropertyDefinition[] pDefs = nt.getDeclaredPropertyDefinitions();
1365 
1366       for (PropertyDefinition pDef : pDefs) {
1367         if (pDef.getName().equals(property)) {
1368           return PropertyType.nameFromValue(pDef.getRequiredType());
1369         }
1370       }
1371 
1372     }
1373     catch (RepositoryException e) {
1374       return null;
1375     }
1376 
1377     return null;
1378   }
1379 
1380   /**
1381    * {@inheritDoc}
1382    */
1383   public void addOrModifyProfileProperties(final Profile profile) throws IdentityStorageException {
1384     getStorage().updateProfile(profile);
1385   }
1386 
1387   /**
1388    * {@inheritDoc}
1389    */
1390   public void setStorage(IdentityStorage storage) {
1391     this.identityStorage = storage;
1392   }
1393 
1394   /**
1395    * {@inheritDoc}
1396    */
1397   public List<Identity> getSpaceMemberIdentitiesByProfileFilter(
1398       Space space,
1399       ProfileFilter profileFilter,
1400       Type type,
1401       long offset, long limit) throws IdentityStorageException {
1402 
1403 
1404     List<Identity> listIdentity = new ArrayList<Identity>();
1405     QueryResult<ProfileEntity> results = getSpaceMemberIdentitiesByProfileFilterQueryBuilder(space, profileFilter, type, offset,
1406                                                                                              limit, false);
1407 
1408     while (results.hasNext()) {
1409       ProfileEntity profileEntity = results.next();
1410       Identity identity = createIdentityFromEntity(profileEntity.getIdentity());
1411       if (! identity.isEnable()) {
1412         continue;
1413       }
1414       Profile profile = getStorage().loadProfile(new Profile(identity));
1415       identity.setProfile(profile);
1416       listIdentity.add(identity);
1417     }
1418 
1419     return listIdentity;
1420   }
1421 
1422   /**
1423    * {@inheritDoc}
1424    */
1425   public int getSpaceMemberIdentitiesByProfileFilterCount(
1426       Space space,
1427       ProfileFilter profileFilter,
1428       Type type,
1429       long offset, long limit) throws IdentityStorageException {
1430     
1431     return getSpaceMemberIdentitiesByProfileFilterQueryBuilder(space, profileFilter, type, offset, limit, true).size();
1432 
1433   }
1434 
1435   /**
1436    * {@inheritDoc}
1437    */
1438   public void updateProfileActivityId(Identity identity, String activityId, AttachedActivityType type) {
1439     try {
1440       ProfileEntity profileEntity = _findById(ProfileEntity.class, identity.getProfile().getId());
1441       ActivityProfileEntity activityPEntity = profileEntity.getActivityProfile();
1442       if (activityPEntity == null) {
1443         activityPEntity = profileEntity.createActivityProfile();
1444       }
1445       profileEntity.setActivityProfile(activityPEntity);
1446       type.setActivityId(activityPEntity, activityId);
1447     } catch (NodeNotFoundException e) {
1448       LOG.debug(e.getMessage(), e);
1449     }
1450   }
1451   
1452   /**
1453    * {@inheritDoc}
1454    */
1455   public String getProfileActivityId(Profile profile, AttachedActivityType type) {
1456     try {
1457       ProfileEntity profileEntity = _findById(ProfileEntity.class, profile.getId());
1458       ActivityProfileEntity activityPEntity = profileEntity.getActivityProfile();
1459       return type.getActivityId(activityPEntity);
1460     } catch (Exception e) {
1461       return null;
1462     }
1463   }
1464   
1465   /**
1466    * {@inheritDoc}
1467    */
1468   public void processEnabledIdentity(Identity identity, boolean isEnable) {
1469     try {
1470       IdentityEntity identityEntity = _findById(IdentityEntity.class, identity.getId());
1471       if (isEnable) {
1472         _removeMixin(identityEntity, DisabledEntity.class);
1473       } else {
1474         _getMixin(identityEntity, DisabledEntity.class, true);
1475       }
1476       getSession().save();
1477     } catch (Exception e) {
1478       LOG.warn(String.format("Process enable identity of user %s unsuccessfully.", identity.getRemoteId()));
1479       LOG.debug(e.getMessage(), e);
1480     }
1481   }
1482   
1483   private void _applyUnifiedSearchFilter(WhereExpression whereExpression, ProfileFilter profileFilter) {
1484 
1485     if (profileFilter == null) return;
1486 
1487     String searchCondition = StorageUtils.escapeSpecialCharacter(profileFilter.getAll());
1488     if (searchCondition != null && searchCondition.length() != 0) {
1489       if (this.isValidInput(searchCondition)) {
1490 
1491         List<String> unifiedSearchConditions = StorageUtils.processUnifiedSearchCondition(searchCondition);
1492         if (unifiedSearchConditions.size() > 0) {
1493           whereExpression.and().startGroup();
1494         }
1495         boolean first = true;
1496         for(String condition : unifiedSearchConditions) {
1497           //
1498           if (first == false) {
1499             whereExpression.or();
1500           }
1501 
1502             String conditionEscapeHtml = StringEscapeUtils.escapeHtml(condition).toLowerCase();
1503             whereExpression.startGroup();
1504             whereExpression
1505                 .contains(ProfileEntity.fullName, condition.toLowerCase())
1506                 .or().contains(ProfileEntity.firstName, condition.toLowerCase())
1507                 .or().contains(ProfileEntity.lastName, condition.toLowerCase())
1508                 .or().contains(ProfileEntity.position, conditionEscapeHtml)
1509                 .or().contains(ProfileEntity.skills, conditionEscapeHtml)
1510                 .or().contains(ProfileEntity.positions, conditionEscapeHtml)
1511                 .or().contains(ProfileEntity.organizations, conditionEscapeHtml)
1512                 .or().contains(ProfileEntity.jobsDescription, conditionEscapeHtml);
1513             whereExpression.endGroup();
1514 
1515 
1516           first = false;
1517         } //end for
1518         
1519         if (unifiedSearchConditions.size() > 0) {
1520           whereExpression.endGroup();
1521         }
1522       }
1523     }
1524   }
1525   
1526   private boolean isValidInput(String input) {
1527     if (input == null || input.length() == 0) {
1528       return false;
1529     }
1530     String cleanString = input.replaceAll("\\*", "");
1531     cleanString = cleanString.replaceAll("\\%", "");
1532     if (cleanString.length() == 0) {
1533        return false;
1534     }
1535     return true;
1536   }
1537 
1538   @Override
1539   public Set<String> getActiveUsers(ActiveIdentityFilter filter) {
1540     Set<String> activeUsers = new HashSet<String>();
1541     //by userGroups
1542     if (filter.getUserGroups() != null) {
1543       StringTokenizer stringToken = new StringTokenizer(filter.getUserGroups(), ActiveIdentityFilter.COMMA_SEPARATOR);
1544       try {
1545         while(stringToken.hasMoreTokens()) {
1546           try {
1547             ListAccess<User> listAccess = getOrganizationService().getUserHandler().findUsersByGroupId(stringToken.nextToken().trim());
1548             User[] users = listAccess.load(0, listAccess.getSize());
1549             //
1550             for(User u : users) {
1551               activeUsers.add(u.getUserName());
1552             }
1553           } catch (Exception e) {
1554             LOG.error(e.getMessage(), e);
1555           }
1556         }
1557       } catch (Exception e) {
1558         LOG.error(e.getMessage());
1559       }
1560     }
1561     
1562     //by N days
1563     if (filter.getDays() > 0) {
1564       activeUsers = StorageUtils.getLastLogin(filter.getDays());
1565     }
1566 
1567     //Gets online users and push to activate users
1568     if (CommonsUtils.getService(UserStateService.class) != null) {
1569       List<UserStateModel> onlines = CommonsUtils.getService(UserStateService.class).online();
1570       for (UserStateModel user : onlines) {
1571         activeUsers.add(user.getUserId());
1572       }
1573     }
1574     
1575     
1576     return activeUsers;
1577   }
1578 
1579   /**
1580    * {@inheritDoc}
1581    */
1582   @Override
1583   public List<IdentityWithRelationship> getIdentitiesWithRelationships(String identityId, int offset, int limit) {
1584     Identity currentUserIdentity = findIdentity(OrganizationIdentityProvider.NAME, identityId);
1585 
1586     ProfileFilter profileFilter = new ProfileFilter();
1587     profileFilter.setViewerIdentity(currentUserIdentity);
1588     List<Identity> identitiesByProfileFilter = getIdentitiesByProfileFilter(OrganizationIdentityProvider.NAME,
1589                                                                             profileFilter,
1590                                                                             offset,
1591                                                                             limit,
1592                                                                             false);
1593 
1594     List<IdentityWithRelationship> identities = new ArrayList<IdentityWithRelationship>();
1595     for (Identity identity : identitiesByProfileFilter) {
1596       if (identity.getRemoteId().equals(currentUserIdentity.getRemoteId())) {
1597         continue;
1598       }
1599       IdentityWithRelationship identityWithRelationship = new IdentityWithRelationship(identity);
1600       Relationship relationship = relationshipStorage.getRelationship(identity, currentUserIdentity);
1601       identityWithRelationship.setRelationship(relationship);
1602       identities.add(identityWithRelationship);
1603     }
1604     return identities;
1605   }
1606 
1607   /**
1608    * {@inheritDoc}
1609    */
1610   @Override
1611   public int countIdentitiesWithRelationships(String identityId) throws Exception {
1612     Identity currentUserIdentity = findIdentity(OrganizationIdentityProvider.NAME, identityId);
1613 
1614     ProfileFilter profileFilter = new ProfileFilter();
1615     profileFilter.setViewerIdentity(currentUserIdentity);
1616     return getIdentitiesByProfileFilterCount(OrganizationIdentityProvider.NAME, profileFilter);
1617   }
1618   
1619   /**
1620    * Gets a the avatar stream for a given identity
1621    *
1622    * @param identity
1623    * @return
1624    */
1625   @Override
1626   public InputStream getAvatarInputStreamById(Identity identity) throws IOException {
1627     throw new UnsupportedOperationException("JCR implementation is no more used");
1628   }
1629 
1630   @Override
1631   public InputStream getBannerInputStreamById(Identity identity) throws IOException {
1632     throw new UnsupportedOperationException("JCR implementation is no more used");
1633   }
1634 
1635   public int countSpaceMemberIdentitiesByProfileFilter(Space space, ProfileFilter profileFilter, Type type) {
1636     return getSpaceMemberIdentitiesByProfileFilter(space, profileFilter, type, 0, Integer.MAX_VALUE).size();
1637   }
1638 
1639   @Override
1640   public List<String> sortIdentities(List<String> identityRemoteIds, String sortField) {
1641     throw new UnsupportedOperationException("JCR implementation is no more used");
1642   }
1643 
1644 }