package org.alfresco.repo.favourites; import java.io.Serializable; import java.text.Collator; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import java.util.TreeMap; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.query.PageDetails; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; import org.alfresco.repo.favourites.PersonFavourite.PersonFavouriteKey; import org.alfresco.repo.policy.ClassPolicy; import org.alfresco.repo.policy.ClassPolicyDelegate; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.security.authentication.AuthenticationContext; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.site.SiteModel; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.favourites.FavouritesService; import org.alfresco.service.cmr.preference.PreferenceService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.site.SiteInfo; import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.ISO8601DateFormat; import org.alfresco.util.Pair; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; /** * Favourites service implementation that uses the PreferencesService for persisting favourites. * * Unfortunately, we are tied to the PreferencesService and to the preference names and data structure because Share uses the PreferenceService directly. * * @author steveglover */ public class FavouritesServiceImpl implements FavouritesService, InitializingBean { private static final Log logger = LogFactory.getLog(FavouritesServiceImpl.class); private Map prefKeys; private PreferenceService preferenceService; private NodeService nodeService; private DictionaryService dictionaryService; private SiteService siteService; private PolicyComponent policyComponent; private PermissionService permissionService; private PersonService personService; /** Authentication Service */ private AuthenticationContext authenticationContext; private ClassPolicyDelegate onAddFavouriteDelegate; private ClassPolicyDelegate onRemoveFavouriteDelegate; private Collator collator = Collator.getInstance(); public interface OnAddFavouritePolicy extends ClassPolicy { public static final QName QNAME = QName.createQName(NamespaceService.ALFRESCO_URI, "onAddfavourite"); /** * Called after a node has been favourited * * @param userName the username of the person who favourited the node * @param nodeRef the node which was favourited */ public void onAddFavourite(String userName, NodeRef nodeRef); } public interface OnRemoveFavouritePolicy extends ClassPolicy { public static final QName QNAME = QName.createQName(NamespaceService.ALFRESCO_URI, "onRemovefavourite"); /** * Called after a favourite has been removed * * @param userName the username of the person who favourited the node * @param nodeRef the node that was un-favourited */ public void onRemoveFavourite(String userName, NodeRef nodeRef); } public void setPermissionService(PermissionService permissionService) { this.permissionService = permissionService; } public void setPersonService(PersonService personService) { this.personService = personService; } public void setAuthenticationContext(AuthenticationContext authenticationContext) { this.authenticationContext = authenticationContext; } public void setPolicyComponent(PolicyComponent policyComponent) { this.policyComponent = policyComponent; } public void setSiteService(SiteService siteService) { this.siteService = siteService; } public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } public void setDictionaryService(DictionaryService dictionaryService) { this.dictionaryService = dictionaryService; } public void setPreferenceService(PreferenceService preferenceService) { this.preferenceService = preferenceService; } private static class PrefKeys { private String sharePrefKey; private String alfrescoPrefKey; public PrefKeys(String sharePrefKey, String alfrescoPrefKey) { super(); this.sharePrefKey = sharePrefKey; this.alfrescoPrefKey = alfrescoPrefKey; } public String getSharePrefKey() { return sharePrefKey; } public String getAlfrescoPrefKey() { return alfrescoPrefKey; } } @Override public void afterPropertiesSet() throws Exception { this.prefKeys = new HashMap(); this.prefKeys.put(Type.SITE, new PrefKeys("org.alfresco.share.sites.favourites.", "org.alfresco.ext.sites.favourites.")); this.prefKeys.put(Type.FILE, new PrefKeys("org.alfresco.share.documents.favourites", "org.alfresco.ext.documents.favourites.")); this.prefKeys.put(Type.FOLDER, new PrefKeys("org.alfresco.share.folders.favourites", "org.alfresco.ext.folders.favourites.")); } public void init() { this.onAddFavouriteDelegate = policyComponent.registerClassPolicy(OnAddFavouritePolicy.class); this.onRemoveFavouriteDelegate = policyComponent.registerClassPolicy(OnRemoveFavouritePolicy.class); } private PrefKeys getPrefKeys(Type type) { PrefKeys prefKey = prefKeys.get(type); return prefKey; } private boolean removeFavouriteSite(String userName, NodeRef nodeRef) { PrefKeys prefKeys = getPrefKeys(Type.SITE); boolean exists = false; SiteInfo siteInfo = siteService.getSite(nodeRef); if(siteInfo != null) { StringBuilder sitePrefKeyBuilder = new StringBuilder(prefKeys.getSharePrefKey()); sitePrefKeyBuilder.append(siteInfo.getShortName()); String sitePrefKey = sitePrefKeyBuilder.toString(); String siteFavouritedKey = siteFavouritedKey(siteInfo); exists = preferenceService.getPreference(userName, siteFavouritedKey) != null; preferenceService.clearPreferences(userName, sitePrefKey); } else { throw new IllegalArgumentException("NodeRef " + nodeRef + " is not a site"); } return exists; } private String siteFavouritedKey(SiteInfo siteInfo) { PrefKeys prefKeys = getPrefKeys(Type.SITE); StringBuilder sitePrefKeyBuilder = new StringBuilder(prefKeys.getSharePrefKey()); sitePrefKeyBuilder.append(siteInfo.getShortName()); String sitePrefKey = sitePrefKeyBuilder.toString(); String favouritedKey = sitePrefKey; return favouritedKey; } private String siteCreatedAtKey(SiteInfo siteInfo) { PrefKeys prefKeys = getPrefKeys(Type.SITE); StringBuilder sitePrefKeyBuilder = new StringBuilder(prefKeys.getAlfrescoPrefKey()); sitePrefKeyBuilder.append(siteInfo.getShortName()); String sitePrefKey = sitePrefKeyBuilder.toString(); StringBuilder createdAtKeyBuilder = new StringBuilder(sitePrefKey); createdAtKeyBuilder.append(".createdAt"); String createdAtKey = createdAtKeyBuilder.toString(); return createdAtKey; } private Comparator getComparator(final List> sortProps) { Comparator comparator = new Comparator() { @Override public int compare(PersonFavouriteKey o1, PersonFavouriteKey o2) { int ret = 0; for(Pair sort : sortProps) { FavouritesService.SortFields field = sort.getFirst(); Boolean ascending = sort.getSecond(); if(field.equals(FavouritesService.SortFields.username)) { if(ascending) { ret = collator.compare(o1.getUserName(), o2.getUserName()); } else { ret = o2.getUserName().compareTo(o1.getUserName()); } if(ret != 0) { break; } } else if(field.equals(FavouritesService.SortFields.type)) { if(ascending) { ret = o1.getType().compareTo(o2.getType()); } else { ret = o2.getType().compareTo(o1.getType()); } if(ret != 0) { break; } } else if(field.equals(FavouritesService.SortFields.createdAt)) { if(ascending) { if(o1.getCreatedAt() != null && o2.getCreatedAt() != null) { ret = o1.getCreatedAt().compareTo(o2.getCreatedAt()); } } else { if(o1.getCreatedAt() != null && o2.getCreatedAt() != null) { ret = o2.getCreatedAt().compareTo(o1.getCreatedAt()); } } if(ret != 0) { break; } } else if(field.equals(FavouritesService.SortFields.title)) { if(ascending) { if(o1.getTitle() != null && o2.getTitle() != null) { ret = collator.compare(o1.getTitle(), o2.getTitle()); } } else { if(o1.getTitle() != null && o2.getTitle() != null) { ret = collator.compare(o2.getTitle(), o1.getTitle()); } } if(ret != 0) { break; } } } if(ret == 0) { // some favourites may not have a createdAt value, rendering this comparator less selective. // If the favourites are still regarded as the same, differentiate on nodeRef. ret = o1.getNodeRef().toString().compareTo(o2.getNodeRef().toString()); } return ret; } }; return comparator; } private PersonFavourite addFavouriteSite(String userName, NodeRef nodeRef) { PersonFavourite favourite = null; SiteInfo siteInfo = siteService.getSite(nodeRef); if(siteInfo != null) { favourite = getFavouriteSite(userName, siteInfo); if(favourite == null) { Map preferences = new HashMap(1); String siteFavouritedKey = siteFavouritedKey(siteInfo); preferences.put(siteFavouritedKey, Boolean.TRUE); // ISO8601 string format: PreferenceService works with strings only for dates it seems String siteCreatedAtKey = siteCreatedAtKey(siteInfo); Date createdAt = new Date(); String createdAtStr = ISO8601DateFormat.format(createdAt); preferences.put(siteCreatedAtKey, createdAtStr); preferenceService.setPreferences(userName, preferences); favourite = new PersonFavourite(userName, siteInfo.getNodeRef(), Type.SITE, siteInfo.getTitle(), createdAt); QName nodeClass = nodeService.getType(nodeRef); OnAddFavouritePolicy policy = onAddFavouriteDelegate.get(nodeRef, nodeClass); policy.onAddFavourite(userName, nodeRef); } } else { // shouldn't happen, getType recognizes it as a site or subtype logger.warn("Unable to get site for " + nodeRef); } return favourite; } private PersonFavourite getFavouriteSite(String userName, SiteInfo siteInfo) { PersonFavourite favourite = null; String siteFavouritedKey = siteFavouritedKey(siteInfo); String siteCreatedAtKey = siteCreatedAtKey(siteInfo); Boolean isFavourited = false; Serializable s = preferenceService.getPreference(userName, siteFavouritedKey); if(s != null) { if(s instanceof String) { isFavourited = Boolean.valueOf((String)s); } else if(s instanceof Boolean) { isFavourited = (Boolean)s; } else { throw new AlfrescoRuntimeException("Unexpected favourites preference value"); } } if(isFavourited) { String createdAtStr = (String)preferenceService.getPreference(userName, siteCreatedAtKey); Date createdAt = (createdAtStr == null ? null : ISO8601DateFormat.parse(createdAtStr)); favourite = new PersonFavourite(userName, siteInfo.getNodeRef(), Type.SITE, siteInfo.getTitle(), createdAt); } return favourite; } private boolean isFavouriteSite(String userName, NodeRef nodeRef) { Boolean isFavourited = Boolean.FALSE; SiteInfo siteInfo = siteService.getSite(nodeRef); if(siteInfo != null) { String favouritedPrefKey = siteFavouritedKey(siteInfo); Serializable value = preferenceService.getPreference(userName, favouritedPrefKey); if(value != null) { if(value instanceof String) { isFavourited = Boolean.valueOf((String)value); } else if(value instanceof String) { isFavourited = (Boolean)value; } else { throw new AlfrescoRuntimeException("Unexpected favourites preference value"); } } } else { throw new IllegalArgumentException("NodeRef " + nodeRef + " is not a site"); } return isFavourited.booleanValue(); } private void updateFavouriteNodes(String userName, Type type, Map favouriteNodes) { PrefKeys prefKeys = getPrefKeys(type); Map preferences = new HashMap(1); StringBuilder values = new StringBuilder(); for(PersonFavourite node : favouriteNodes.values()) { NodeRef nodeRef = node.getNodeRef(); values.append(nodeRef.toString()); values.append(","); // ISO8601 string format: PreferenceService works with strings only for dates it seems StringBuilder sb = new StringBuilder(prefKeys.getAlfrescoPrefKey()); sb.append(nodeRef.toString()); sb.append(".createdAt"); String createdAtKey = sb.toString(); Date createdAt = node.getCreatedAt(); if(createdAt != null) { String createdAtStr = ISO8601DateFormat.format(createdAt); preferences.put(createdAtKey, createdAtStr); } } if(values.length() > 1) { values.delete(values.length() - 1, values.length()); } preferences.put(prefKeys.getSharePrefKey(), values.toString()); preferenceService.setPreferences(userName, preferences); } private Map getFavouriteNodes(String userName, Type type) { PrefKeys prefKeys = getPrefKeys(type); Map favouriteNodes = Collections.emptyMap(); Map prefs = preferenceService.getPreferences(userName, prefKeys.getSharePrefKey()); String nodes = (String)prefs.get(prefKeys.getSharePrefKey()); if(nodes != null) { favouriteNodes = extractFavouriteNodes(userName, type, nodes); } else { favouriteNodes = new HashMap(); } return favouriteNodes; } /* * Extract favourite nodes of the given type from the comma-separated list in "nodes". */ private Map extractFavouriteNodes(String userName, Type type, String nodes) { PrefKeys prefKeys = getPrefKeys(type); Map favouriteNodes = new HashMap(); StringTokenizer st = new StringTokenizer(nodes, ","); while(st.hasMoreTokens()) { String nodeRefStr = st.nextToken(); nodeRefStr = nodeRefStr.trim(); if(!NodeRef.isNodeRef((String)nodeRefStr)) { continue; } NodeRef nodeRef = new NodeRef((String)nodeRefStr); if(!nodeService.exists(nodeRef)) { continue; } if(permissionService.hasPermission(nodeRef, PermissionService.READ_PROPERTIES) == AccessStatus.DENIED) { continue; } // get createdAt for this favourited node // use ISO8601 StringBuilder builder = new StringBuilder(prefKeys.getAlfrescoPrefKey()); builder.append(nodeRef.toString()); builder.append(".createdAt"); String prefKey = builder.toString(); String createdAtStr = (String)preferenceService.getPreference(userName, prefKey); Date createdAt = (createdAtStr != null ? ISO8601DateFormat.parse(createdAtStr): null); String name = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); PersonFavourite personFavourite = new PersonFavourite(userName, nodeRef, type, name, createdAt); PersonFavouriteKey key = personFavourite.getKey(); favouriteNodes.put(key, personFavourite); } return favouriteNodes; } private void extractFavouriteSite(String userName, Type type, Map sortedFavouriteNodes, Map preferences, String key) { // preference value indicates whether the site has been favourited Serializable pref = preferences.get(key); Boolean isFavourite = (Boolean)pref; if(isFavourite) { PrefKeys sitePrefKeys = getPrefKeys(Type.SITE); int length = sitePrefKeys.getSharePrefKey().length(); String siteId = key.substring(length); try { SiteInfo siteInfo = siteService.getSite(siteId); if(siteInfo != null) { StringBuilder builder = new StringBuilder(sitePrefKeys.getAlfrescoPrefKey()); builder.append(siteId); builder.append(".createdAt"); String createdAtPrefKey = builder.toString(); String createdAtStr = (String)preferences.get(createdAtPrefKey); Date createdAt = null; if(createdAtStr != null) { createdAt = (createdAtStr != null ? ISO8601DateFormat.parse(createdAtStr): null); } PersonFavourite personFavourite = new PersonFavourite(userName, siteInfo.getNodeRef(), Type.SITE, siteId, createdAt); sortedFavouriteNodes.put(personFavourite.getKey(), personFavourite); } } catch(AccessDeniedException ex) { // the user no longer has access to this site, skip over the favourite // TODO remove the favourite preference return; } } } private PersonFavourite getFavouriteDocumentOrFolder(String userName, Type type, NodeRef nodeRef) { PersonFavourite favourite = null; PrefKeys prefKeys = getPrefKeys(type); Map preferences = preferenceService.getPreferences(userName, prefKeys.getSharePrefKey()); String nodes = (String)preferences.get(prefKeys.getSharePrefKey()); if(nodes != null) { Map favouriteNodes = extractFavouriteNodes(userName, type, nodes); String title = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_TITLE); PersonFavouriteKey key = new PersonFavouriteKey(userName, title, type, nodeRef); favourite = favouriteNodes.get(key); } return favourite; } private PersonFavourite getPersonFavourite(String userName, Type type, NodeRef nodeRef) { PersonFavourite ret = null; if(type.equals(Type.SITE)) { SiteInfo siteInfo = siteService.getSite(nodeRef); if(siteInfo != null) { ret = getFavouriteSite(userName, siteInfo); } else { // shouldn't happen, getType recognizes it as a site or subtype logger.warn("Unable to get site for " + nodeRef); } } else if(type.equals(Type.FILE)) { ret = getFavouriteDocumentOrFolder(userName, type, nodeRef); } else if(type.equals(Type.FOLDER)) { ret = getFavouriteDocumentOrFolder(userName, type, nodeRef); } else { // shouldn't happen throw new AlfrescoRuntimeException("Unexpected favourite type"); } return ret; } private PersonFavourite addFavouriteDocumentOrFolder(String userName, Type type, NodeRef nodeRef) { Map personFavourites = getFavouriteNodes(userName, type); PersonFavourite personFavourite = getPersonFavourite(userName, type, nodeRef); if(personFavourite == null) { Date createdAt = new Date(); String name = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); personFavourite = new PersonFavourite(userName, nodeRef, type, name, createdAt); personFavourites.put(personFavourite.getKey(), personFavourite); updateFavouriteNodes(userName, type, personFavourites); QName nodeClass = nodeService.getType(nodeRef); OnAddFavouritePolicy policy = onAddFavouriteDelegate.get(nodeRef, nodeClass); policy.onAddFavourite(userName, nodeRef); } return personFavourite; } private boolean isFavouriteNode(String userName, Type type, NodeRef nodeRef) { Map personFavourites = getFavouriteNodes(userName, type); PersonFavouriteKey personFavouriteKey = new PersonFavouriteKey(userName, null, type, nodeRef); boolean isFavourite = personFavourites.containsKey(personFavouriteKey); return isFavourite; } public Type getType(NodeRef nodeRef) { Type favouriteType = null; QName type = nodeService.getType(nodeRef); boolean isSite = dictionaryService.isSubClass(type, SiteModel.TYPE_SITE); if(isSite) { favouriteType = Type.SITE; } else { boolean isContainer = (dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDER) && !dictionaryService.isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER)); if(isContainer) { favouriteType = Type.FOLDER; } else { boolean isFile = dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT); if(isFile) { favouriteType = Type.FILE; } } } return favouriteType; } private boolean removeFavouriteNode(String userName, Type type, NodeRef nodeRef) { boolean exists = false; Map personFavourites = getFavouriteNodes(userName, type); PersonFavouriteKey personFavouriteKey = new PersonFavouriteKey(userName, null, type, nodeRef); PersonFavourite personFavourite = personFavourites.remove(personFavouriteKey); exists = personFavourite != null; updateFavouriteNodes(userName, type, personFavourites); QName nodeClass = nodeService.getType(nodeRef); OnRemoveFavouritePolicy policy = onRemoveFavouriteDelegate.get(nodeRef, nodeClass); policy.onRemoveFavourite(userName, nodeRef); return exists; } @Override public PersonFavourite addFavourite(String userName, NodeRef nodeRef) { PersonFavourite personFavourite = null; Type type = getType(nodeRef); if(type == null) { throw new IllegalArgumentException("Cannot favourite this node"); } else if(type.equals(Type.FILE)) { personFavourite = addFavouriteDocumentOrFolder(userName, Type.FILE, nodeRef); } else if(type.equals(Type.FOLDER)) { personFavourite = addFavouriteDocumentOrFolder(userName, Type.FOLDER, nodeRef); } else if(type.equals(Type.SITE)) { personFavourite = addFavouriteSite(userName, nodeRef); } else { throw new IllegalArgumentException("Cannot favourite this node"); } return personFavourite; } @Override public boolean removeFavourite(String userName, NodeRef nodeRef) { boolean exists = false; Type type = getType(nodeRef); if(type == null) { throw new IllegalArgumentException("Cannot un-favourite this node"); } else if(type.equals(Type.FILE)) { exists = removeFavouriteNode(userName, type, nodeRef); } else if(type.equals(Type.FOLDER)) { exists = removeFavouriteNode(userName, type, nodeRef); } else if(type.equals(Type.SITE)) { exists = removeFavouriteSite(userName, nodeRef); } else { throw new IllegalArgumentException("Cannot un-favourite this node"); } return exists; } @Override public boolean isFavourite(String userName, NodeRef nodeRef) { boolean isFavourite = false; Type type = getType(nodeRef); if(type == null) { throw new IllegalArgumentException("Unsupported node type"); } else if(type.equals(Type.FILE)) { isFavourite = isFavouriteNode(userName, type, nodeRef); } else if(type.equals(Type.FOLDER)) { isFavourite = isFavouriteNode(userName, type, nodeRef); } else if(type.equals(Type.SITE)) { isFavourite = isFavouriteSite(userName, nodeRef); } else { throw new IllegalArgumentException("Unsupported node type"); } return isFavourite; } @Override public PagingResults getPagedFavourites(String userName, Set types, List> sortProps, PagingRequest pagingRequest) { // Get the user node reference final NodeRef personNodeRef = personService.getPerson(userName); if(personNodeRef == null) { throw new AlfrescoRuntimeException("Can not get preferences for " + userName + " because he/she does not exist."); } boolean includeFiles = types.contains(Type.FILE); boolean includeFolders = types.contains(Type.FOLDER); boolean includeSites = types.contains(Type.SITE); String currentUserName = AuthenticationUtil.getFullyAuthenticatedUser(); if (authenticationContext.isSystemUserName(currentUserName) == true || permissionService.hasPermission(personNodeRef, PermissionService.WRITE) == AccessStatus.ALLOWED || userName.equals(currentUserName) == true) { // we may have more than one favourite that is considered the same w.r.t. the PersonFavourite comparator final Map sortedFavouriteNodes = new TreeMap(getComparator(sortProps)); PrefKeys sitePrefKeys = getPrefKeys(Type.SITE); PrefKeys documentsPrefKeys = getPrefKeys(Type.FILE); PrefKeys foldersPrefKeys = getPrefKeys(Type.FOLDER); Map preferences = preferenceService.getPreferences(userName); for(String key : preferences.keySet()) { if(includeFiles && key.startsWith(documentsPrefKeys.sharePrefKey)) { String nodes = (String)preferences.get(key); if(nodes != null) { sortedFavouriteNodes.putAll(extractFavouriteNodes(userName, Type.FILE, nodes)); } } else if(includeFolders && key.startsWith(foldersPrefKeys.sharePrefKey)) { String nodes = (String)preferences.get(key); if(nodes != null) { sortedFavouriteNodes.putAll(extractFavouriteNodes(userName, Type.FOLDER, nodes)); } } else if(includeSites && key.startsWith(sitePrefKeys.getSharePrefKey()) && !key.endsWith(".createdAt")) { // key is either of the form org.alfresco.share.sites.favourites..favourited or // org.alfresco.share.sites.favourites. extractFavouriteSite(userName, Type.SITE, sortedFavouriteNodes, preferences, key); } } int totalSize = sortedFavouriteNodes.size(); final PageDetails pageDetails = PageDetails.getPageDetails(pagingRequest, totalSize); final List page = new ArrayList(pageDetails.getPageSize()); Iterator it = sortedFavouriteNodes.values().iterator(); for(int counter = 0; counter < pageDetails.getEnd() && it.hasNext(); counter++) { PersonFavourite favouriteNode = it.next(); if(counter < pageDetails.getSkipCount()) { continue; } if(counter > pageDetails.getEnd() - 1) { break; } page.add(favouriteNode); } return new PagingResults() { @Override public List getPage() { return page; } @Override public boolean hasMoreItems() { return pageDetails.hasMoreItems(); } @Override public Pair getTotalResultCount() { Integer total = Integer.valueOf(sortedFavouriteNodes.size()); return new Pair(total, total); } @Override public String getQueryExecutionId() { return null; } }; } else { // The current user does not have sufficient permissions to update the preferences for this user throw new AccessDeniedException("The current user " + currentUserName + " does not have sufficient permissions to get the favourites of the user " + userName); } } public PersonFavourite getFavourite(String userName, NodeRef nodeRef) { Type type = getType(nodeRef); return getPersonFavourite(userName, type, nodeRef); } }