/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package com.xpn.xwiki.user.impl.xwiki;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xwiki.bridge.event.DocumentCreatedEvent;
import org.xwiki.bridge.event.DocumentDeletedEvent;
import org.xwiki.bridge.event.DocumentUpdatedEvent;
import org.xwiki.cache.Cache;
import org.xwiki.cache.CacheException;
import org.xwiki.cache.CacheManager;
import org.xwiki.cache.config.CacheConfiguration;
import org.xwiki.cache.eviction.EntryEvictionConfiguration;
import org.xwiki.cache.eviction.LRUEvictionConfiguration;
import org.xwiki.model.EntityType;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.DocumentReferenceResolver;
import org.xwiki.model.reference.EntityReference;
import org.xwiki.model.reference.EntityReferenceSerializer;
import org.xwiki.observation.EventListener;
import org.xwiki.observation.ObservationManager;
import org.xwiki.observation.event.Event;
import org.xwiki.query.Query;
import org.xwiki.query.QueryException;
import org.xwiki.query.QueryManager;
import com.xpn.xwiki.XWiki;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.objects.BaseObject;
import com.xpn.xwiki.store.XWikiStoreInterface;
import com.xpn.xwiki.user.api.XWikiGroupService;
import com.xpn.xwiki.user.api.XWikiRightService;
import com.xpn.xwiki.util.Util;
import com.xpn.xwiki.web.Utils;
/**
* Default implementation of {@link XWikiGroupService} users and groups manager.
*
* @version $Id: 5789cfc1ac224bdf8412a48c0557ba125e3f3d63 $
*/
public class XWikiGroupServiceImpl implements XWikiGroupService, EventListener
{
public static final EntityReference GROUPCLASS_REFERENCE = new EntityReference("XWikiGroups", EntityType.DOCUMENT,
new EntityReference("XWiki", EntityType.SPACE));
private static final Logger LOGGER = LoggerFactory.getLogger(XWikiDocument.class);
/**
* Name of the "XWiki.XWikiGroups" class without the space name.
*/
private static final String CLASS_SUFFIX_XWIKIGROUPS = "XWikiGroups";
/**
* Name of the "XWiki.XWikiUsers" class without the space name.
*/
private static final String CLASS_SUFFIX_XWIKIUSERS = "XWikiUsers";
/**
* Name of the "XWiki.XWikiGroups" class.
*/
private static final String CLASS_XWIKIGROUPS = "XWiki." + CLASS_SUFFIX_XWIKIGROUPS;
/**
* Name of the "XWiki.XWikiGroupTemplate" class sheet.
*/
private static final String CLASSTEMPLATE_XWIKIGROUPS = "XWiki.XWikiGroupTemplate";
/**
* Name of the "XWiki.XWikiUserTemplate" class sheet.
*/
private static final String CLASSTEMPLATE_XWIKIUSERS = "XWiki.XWikiUserTemplate";
/**
* Name of the field of class XWiki.XWikiGroups where group's members names are inserted.
*/
private static final String FIELD_XWIKIGROUPS_MEMBER = "member";
/**
* Default space name for a group or a user.
*/
private static final String DEFAULT_MEMBER_SPACE = "XWiki";
/**
* String between wiki name and full name in document path.
*/
private static final String WIKI_FULLNAME_SEP = ":";
/**
* String between space and name in document full name.
*/
private static final String SPACE_NAME_SEP = ".";
/**
* Symbol use in HQL "like" command that means "all characters".
*/
private static final String HQLLIKE_ALL_SYMBOL = "%";
private static final String NAME = "groupservice";
private static final List<Event> EVENTS = new ArrayList<Event>()
{
{
add(new DocumentCreatedEvent());
add(new DocumentUpdatedEvent());
add(new DocumentDeletedEvent());
}
};
protected Cache<Collection<DocumentReference>> memberGroupsCache;
/**
* Used to convert a string into a proper Document Reference.
*/
private DocumentReferenceResolver<String> currentMixedDocumentReferenceResolver = Utils.getComponent(
DocumentReferenceResolver.TYPE_STRING, "currentmixed");
private EntityReferenceSerializer<String> entityReferenceSerializer = Utils
.getComponent(EntityReferenceSerializer.TYPE_STRING);
private EntityReferenceSerializer<String> localWikiEntityReferenceSerializer = Utils.getComponent(
EntityReferenceSerializer.TYPE_STRING, "local");
@Override
public synchronized void init(XWiki xwiki, XWikiContext context) throws XWikiException
{
initCache(context);
Utils.getComponent(ObservationManager.class).addListener(this);
}
@Override
public synchronized void initCache(XWikiContext context) throws XWikiException
{
int iCapacity = 100;
try {
String capacity = context.getWiki().Param("xwiki.authentication.group.cache.capacity");
if (capacity != null) {
iCapacity = Integer.parseInt(capacity);
}
} catch (Exception e) {
}
initCache(iCapacity, context);
}
@Override
public synchronized void initCache(int iCapacity, XWikiContext context) throws XWikiException
{
try {
CacheConfiguration configuration = new CacheConfiguration();
configuration.setConfigurationId("xwiki.groupservice.usergroups");
LRUEvictionConfiguration lru = new LRUEvictionConfiguration();
lru.setMaxEntries(iCapacity);
configuration.put(EntryEvictionConfiguration.CONFIGURATIONID, lru);
this.memberGroupsCache = Utils.getComponent(CacheManager.class).createNewCache(configuration);
} catch (CacheException e) {
throw new XWikiException(XWikiException.MODULE_XWIKI_CACHE, XWikiException.ERROR_CACHE_INITIALIZING,
"Failed to initialize cache", e);
}
}
@Override
public void flushCache()
{
if (this.memberGroupsCache != null) {
this.memberGroupsCache.removeAll();
}
}
/**
* Check whether the configuration specifies that every user is implicitly in XWikiAllGroup. Configured by the
* {@code xwiki.authentication.group.allgroupimplicit} parameter in {@code xwiki.cfg}.
*
* @param context the current XWiki context
* @return {@code true} if the group is implicit and all users should be by default in it, {@code false} if the
* group behaves as all other groups, containing only the users/subgroups that are explicitly listed inside
* the document.
*/
protected boolean isAllGroupImplicit(XWikiContext context)
{
return context.getWiki().isAllGroupImplicit();
}
@Override
@Deprecated
public Collection<String> listGroupsForUser(String member, XWikiContext context) throws XWikiException
{
return getAllGroupsNamesForMember(member, -1, 0, context);
}
@Override
@Deprecated
public void addUserToGroup(String username, String database, String group, XWikiContext context)
throws XWikiException
{
// Don't do anything and let the listener do its job if there is a need
}
/**
* Check if provided member is equal to member name found in XWiki.XWikiGroups object.
*
* @param currentMember the member name found in XWiki.XWikiGroups object.
* @param memberWiki the name of the wiki of the member.
* @param memberSpace the name of the space of the member.
* @param memberName the name of the member.
* @param context the XWiki context.
* @return true if equals, false if not.
*/
private boolean isMemberEquals(String currentMember, String memberWiki, String memberSpace, String memberName,
XWikiContext context)
{
boolean equals = false;
if (memberWiki != null) {
equals |= currentMember.equals(memberWiki + WIKI_FULLNAME_SEP + memberSpace + SPACE_NAME_SEP + memberName);
if (memberSpace == null || memberSpace.equals(DEFAULT_MEMBER_SPACE)) {
equals |= currentMember.equals(memberSpace + SPACE_NAME_SEP + memberName);
}
}
if (context.getWikiId() == null || context.getWikiId().equalsIgnoreCase(memberWiki)) {
equals |= currentMember.equals(memberName);
if (memberSpace == null || memberSpace.equals(DEFAULT_MEMBER_SPACE)) {
equals |= currentMember.equals(memberSpace + SPACE_NAME_SEP + memberName);
}
}
return equals;
}
/**
* Remove user or group name from a group {@link XWikiDocument}.
*
* @param groupDocument the {@link XWikiDocument} containing group object.
* @param memberWiki the name of the wiki of the member.
* @param memberSpace the name of the space of the member.
* @param memberName the name of the member.
* @param context the XWiki context.
* @return true if at least one member has been removed from the list.
*/
private boolean removeUserOrGroupFromGroup(XWikiDocument groupDocument, String memberWiki, String memberSpace,
String memberName, XWikiContext context)
{
boolean needUpdate = false;
List<BaseObject> groups = groupDocument.getXObjects(GROUPCLASS_REFERENCE);
if (groups != null) {
for (BaseObject bobj : groups) {
if (bobj != null) {
String member = bobj.getStringValue(FIELD_XWIKIGROUPS_MEMBER);
if (isMemberEquals(member, memberWiki, memberSpace, memberName, context)) {
needUpdate = groupDocument.removeXObject(bobj);
}
}
}
}
return needUpdate;
}
@Override
public void removeUserOrGroupFromAllGroups(String memberWiki, String memberSpace, String memberName,
XWikiContext context) throws XWikiException
{
List<Object> parameterValues = new ArrayList<Object>();
StringBuilder where =
new StringBuilder(
", BaseObject as obj, StringProperty as prop where doc.fullName=obj.name and obj.className=?");
parameterValues.add(CLASS_XWIKIGROUPS);
where.append(" and obj.id=prop.id.id");
where.append(" and prop.name=?");
parameterValues.add(FIELD_XWIKIGROUPS_MEMBER);
where.append(" and prop.value like ?");
if (context.getWikiId() == null || context.getWikiId().equalsIgnoreCase(memberWiki)) {
if (memberSpace == null || memberSpace.equals(DEFAULT_MEMBER_SPACE)) {
parameterValues.add(HQLLIKE_ALL_SYMBOL + memberName + HQLLIKE_ALL_SYMBOL);
} else {
parameterValues.add(HQLLIKE_ALL_SYMBOL + memberSpace + SPACE_NAME_SEP + memberName + HQLLIKE_ALL_SYMBOL);
}
} else {
parameterValues.add(HQLLIKE_ALL_SYMBOL + memberWiki + WIKI_FULLNAME_SEP + memberSpace + SPACE_NAME_SEP
+ memberName + HQLLIKE_ALL_SYMBOL);
}
List<XWikiDocument> documentList =
context.getWiki().getStore().searchDocuments(where.toString(), parameterValues, context);
for (XWikiDocument groupDocument : documentList) {
if (removeUserOrGroupFromGroup(groupDocument, memberWiki, memberSpace, memberName, context)) {
context.getWiki().saveDocument(groupDocument, context);
}
}
}
@Override
@Deprecated
public List<String> listMemberForGroup(String group, XWikiContext context) throws XWikiException
{
List<String> list = new ArrayList<String>();
try {
if (group == null) {
try {
return context.getWiki().getStore().getQueryManager().getNamedQuery("getAllUsers").execute();
} catch (QueryException ex) {
throw new XWikiException(0, 0, ex.getMessage(), ex);
}
} else {
String gshortname = Util.getName(group, context);
XWikiDocument docgroup = context.getWiki().getDocument(gshortname, context);
List<BaseObject> groups = docgroup.getXObjects(GROUPCLASS_REFERENCE);
if (groups != null) {
for (BaseObject bobj : groups) {
if (bobj != null) {
String member = bobj.getStringValue(FIELD_XWIKIGROUPS_MEMBER);
if (StringUtils.isNotEmpty(member)) {
list.add(member);
}
}
}
}
return list;
}
} catch (XWikiException e) {
LOGGER.error("Failed to get group document", e);
}
return null;
}
@Override
@Deprecated
public List<String> listAllGroups(XWikiContext context) throws XWikiException
{
if (context.getWiki().getHibernateStore() != null) {
String sql = ", BaseObject as obj where obj.name=doc.fullName and obj.className='XWiki.XWikiGroups'";
return context.getWiki().getStore().searchDocumentsNames(sql, context);
} else {
return null;
}
}
@Override
public String getName()
{
return NAME;
}
@Override
public List<Event> getEvents()
{
return EVENTS;
}
@Override
public void onEvent(Event event, Object source, Object data)
{
XWikiDocument document = (XWikiDocument) source;
XWikiDocument oldDocument = document.getOriginalDocument();
// if there is any chance some group changed, flush the group cache
if (document.getXObject(GROUPCLASS_REFERENCE) != null || oldDocument.getXObject(GROUPCLASS_REFERENCE) != null) {
flushCache();
}
}
@Override
public List<?> getAllMatchedUsers(Object[][] matchFields, boolean withdetails, int nb, int start,
Object[][] order, XWikiContext context) throws XWikiException
{
return getAllMatchedUsersOrGroups(true, matchFields, withdetails, nb, start, order, context);
}
@Override
public List<?> getAllMatchedGroups(Object[][] matchFields, boolean withdetails, int nb, int start,
Object[][] order, XWikiContext context) throws XWikiException
{
return getAllMatchedUsersOrGroups(false, matchFields, withdetails, nb, start, order, context);
}
/**
* Create a "where clause" to use with {@link XWikiStoreInterface} searchDocuments and searchDocumentsNames methods.
*
* @param user if true search for users, otherwise search for groups.
* @param matchFields the field to math with values. It is a table of table with :
* <ul>
* <li>fiedname : the name of the field</li>
* <li>fieldtype : for example StringProperty. If null the field is considered as document field</li>
* <li>pattern matching : based on HQL "like" command</li>
* </ul>
* @param order the field to order from. It is a table of table with :
* <ul>
* <li>fieldname : the name of the field</li>
* <li>fieldtype : for example StringProperty. If null the field is considered as document field</li>
* <li>asc : a Boolean, if true the order is ascendent</li>
* </ul>
* @param parameterValues the list of values to fill for use with HQL named request.
* @return the formated HQL named request.
*/
protected String createMatchUserOrGroupWhereClause(boolean user, Object[][] matchFields, Object[][] order,
List<Object> parameterValues)
{
String documentClass = user ? CLASS_SUFFIX_XWIKIUSERS : CLASS_SUFFIX_XWIKIGROUPS;
String classtemplate = user ? CLASSTEMPLATE_XWIKIUSERS : CLASSTEMPLATE_XWIKIGROUPS;
StringBuilder from = new StringBuilder(", BaseObject as obj");
StringBuilder where = new StringBuilder(" where doc.fullName=obj.name and doc.fullName<>? and obj.className=?");
parameterValues.add(classtemplate);
parameterValues.add("XWiki." + documentClass);
Map<String, String> fieldMap = new HashMap<String, String>();
int fieldIndex = 0;
// Manage object match strings
if (matchFields != null) {
for (Object[] matchField : matchFields) {
String fieldName = (String) matchField[0];
String type = (String) matchField[1];
String value = (String) matchField[2];
if (type != null) {
String fieldPrefix;
if (!fieldMap.containsKey(fieldName)) {
fieldPrefix = "field" + fieldIndex;
from.append(", " + type + " as " + fieldPrefix);
where.append(" and obj.id=" + fieldPrefix + ".id.id");
where.append(" and " + fieldPrefix + ".name=?");
parameterValues.add(fieldName);
++fieldIndex;
} else {
fieldPrefix = fieldMap.get(fieldName);
}
where.append(" and lower(" + fieldPrefix + ".value) like ?");
parameterValues.add(HQLLIKE_ALL_SYMBOL + value.toLowerCase() + HQLLIKE_ALL_SYMBOL);
fieldMap.put(fieldName, fieldPrefix);
} else {
where.append(" and lower(doc." + fieldName + ") like ?");
parameterValues.add(HQLLIKE_ALL_SYMBOL + value.toLowerCase() + HQLLIKE_ALL_SYMBOL);
}
}
}
StringBuilder orderString = new StringBuilder();
// Manage order
if (order != null && order.length > 0) {
orderString.append(" order by");
for (int i = 0; i < order.length; ++i) {
String fieldName = (String) order[i][0];
String type = (String) order[i][1];
Boolean asc = (Boolean) order[i][2];
if (i > 0) {
orderString.append(",");
}
if (type != null) {
String fieldPrefix;
if (!fieldMap.containsKey(fieldName)) {
fieldPrefix = "field" + fieldIndex;
from.append(", " + type + " as " + fieldPrefix);
where.append(" and obj.id=" + fieldPrefix + ".id.id");
where.append(" and " + fieldPrefix + ".name=?");
parameterValues.add(fieldName);
++fieldIndex;
} else {
fieldPrefix = fieldMap.get(fieldName);
}
orderString.append(" " + fieldPrefix + ".value");
} else {
orderString.append(" doc." + fieldName);
}
orderString.append(asc == null || asc.booleanValue() ? " asc" : " desc");
}
}
return from.append(where).append(orderString).toString();
}
/**
* Search for all users or group with provided constraints and in a provided order.
*
* @param user if true search for users, otherwise search for groups.
* @param matchFields the field to math with values. It is a table of table with :
* <ul>
* <li>fiedname : the name of the field</li>
* <li>fieldtype : for example StringProperty. If null the field is considered as document field</li>
* <li>pattern matching : based on HQL "like" command</li>
* </ul>
* @param withdetails indicate if a {@link List} containing {@link String} names is returned or {@link List}
* containing {@link XWikiDocument}.
* @param nb the maximum number of results to return. Infinite if 0.
* @param start the index of the first found user or group to return.
* @param order the field to order from. It is a table of table with :
* <ul>
* <li>fieldname : the name of the field</li>
* <li>fieldtype : for example StringProperty. If null the field is considered as document field</li>
* </ul>
* @param context the {@link XWikiContext}.
* @return the list of users or groups.
* @throws XWikiException error when calling for
* {@link XWikiStoreInterface#searchDocuments(String, int, int, List, XWikiContext)} or
* {@link XWikiStoreInterface#searchDocumentsNames(String, int, int, List, XWikiContext)}
*/
protected List<?> getAllMatchedUsersOrGroups(boolean user, Object[][] matchFields, boolean withdetails, int nb,
int start, Object[][] order, XWikiContext context) throws XWikiException
{
List<?> groups = null;
if (context.getWiki().getHibernateStore() != null) {
List<Object> parameterValues = new ArrayList<Object>();
String where = createMatchUserOrGroupWhereClause(user, matchFields, order, parameterValues);
if (withdetails) {
groups =
context.getWiki().getStore().searchDocuments(where, false, nb, start, parameterValues, context);
} else {
groups = context.getWiki().getStore().searchDocumentsNames(where, nb, start, parameterValues, context);
}
} else if (context.getWiki().getStore().getQueryManager().hasLanguage(Query.XPATH)) {
// TODO : fully implement this methods for XPATH platform
if ((matchFields != null && matchFields.length > 0) || withdetails) {
throw new UnsupportedOperationException(
"The current storage engine does not support advanced group queries");
}
try {
groups =
context
.getWiki()
.getStore()
.getQueryManager()
.createQuery(
"/*/*[obj/XWiki/" + (user ? CLASS_SUFFIX_XWIKIUSERS : CLASS_SUFFIX_XWIKIGROUPS)
+ "]/@fullName", Query.XPATH).setLimit(nb).setOffset(start).execute();
} catch (QueryException ex) {
throw new XWikiException(0, 0, ex.getMessage(), ex);
}
}
return groups;
}
/**
* Return number of users or groups with provided constraints.
*
* @param user if true search for users, otherwise search for groups.
* @param matchFields the field to math with values. It is a table of table with :
* <ul>
* <li>fiedname : the name of the field</li>
* <li>fieldtype : for example StringProperty. If null the field is considered as document field</li>
* <li>pattern matching : based on HQL "like" command</li>
* </ul>
* @param context the {@link XWikiContext}.
* @return the of found users or groups.
* @throws XWikiException error when calling for
* {@link XWikiStoreInterface#search(String, int, int, List, XWikiContext)}
*/
protected int countAllMatchedUsersOrGroups(boolean user, Object[][] matchFields, XWikiContext context)
throws XWikiException
{
List<Object> parameterValues = new ArrayList<Object>();
String where = createMatchUserOrGroupWhereClause(user, matchFields, null, parameterValues);
String sql = "select count(distinct doc) from XWikiDocument doc" + where;
List<?> list = context.getWiki().getStore().search(sql, 0, 0, parameterValues, context);
if (list == null || list.size() == 0) {
return 0;
}
return ((Number) list.get(0)).intValue();
}
@Override
public int countAllMatchedUsers(Object[][] matchFields, XWikiContext context) throws XWikiException
{
return countAllMatchedUsersOrGroups(true, matchFields, context);
}
@Override
public int countAllMatchedGroups(Object[][] matchFields, XWikiContext context) throws XWikiException
{
return countAllMatchedUsersOrGroups(false, matchFields, context);
}
// ///////////////////////////////////////////////////////////////////
// Group members and member groups
/**
* Create a query to filter provided group members. Generate the entire HQL query except the select part.
*
* @param groupFullName the fill wiki name of the group.
* @param matchField a string to search in result to filter.
* @param orderAsc if true, the result is ordered ascendent, if false it descendant. If null no order is applied.
* @param parameterValues the values to insert in names query.
* @return the HQL query.
* @since 1.6M1
*/
protected String createMatchGroupMembersWhereClause(String groupFullName, String matchField, Boolean orderAsc,
Map<String, Object> parameterValues)
{
StringBuilder queryString = new StringBuilder();
// Add from clause
queryString.append(" FROM BaseObject as obj, StringProperty as field");
// Add where clause
queryString.append(" WHERE obj.name=:groupdocname "
+ "and obj.className=:groupclassname and obj.id=field.id.id");
parameterValues.put("groupdocname", groupFullName);
parameterValues.put("groupclassname", CLASS_XWIKIGROUPS);
// Note: We should normally be able to use the 3-argument trim() function which defaults to the whitesapce
// char to be trimmed. However because of https://hibernate.atlassian.net/browse/HHH-8295 this raises a
// warning in the XWiki console. Once this is fixed in Hibernate we can start using the 3-argument function
// again.
queryString.append(" and (trim(both ' ' from field.value)<>'' or "
+ "(trim(both ' ' from field.value) is not null and '' is null))");
if (matchField != null) {
queryString.append(" and lower(field.value) like :matchfield");
parameterValues.put("matchfield", HQLLIKE_ALL_SYMBOL + matchField.toLowerCase() + HQLLIKE_ALL_SYMBOL);
}
// Add order by clause
if (orderAsc != null) {
queryString.append(" ORDER BY field.value ").append(orderAsc ? "asc" : "desc");
}
return queryString.toString();
}
@Override
public Collection<String> getAllGroupsNamesForMember(String member, int nb, int start, XWikiContext context)
throws XWikiException
{
List<String> groupNames = null;
DocumentReference memberReference = this.currentMixedDocumentReferenceResolver.resolve(member);
String currentWiki = context.getWikiId();
try {
context.setWikiId(memberReference.getWikiReference().getName());
Collection<DocumentReference> groupReferences =
getAllGroupsReferencesForMember(memberReference, nb, start, context);
groupNames = new ArrayList<String>(groupReferences.size());
for (DocumentReference groupReference : groupReferences) {
groupNames.add(this.localWikiEntityReferenceSerializer.serialize(groupReference));
}
} finally {
context.setWikiId(currentWiki);
}
return groupNames;
}
@Override
public Collection<DocumentReference> getAllGroupsReferencesForMember(DocumentReference memberReference, int limit,
int offset, XWikiContext context) throws XWikiException
{
Collection<DocumentReference> groupReferences = null;
String prefixedFullName = this.entityReferenceSerializer.serialize(memberReference);
String key = context.getWikiId() + "/" + prefixedFullName;
synchronized (key) {
if (this.memberGroupsCache == null) {
initCache(context);
}
// TODO: add cache support for customized limit/offset ?
boolean supportCache = limit <= 0 && offset <= 0;
if (supportCache) {
groupReferences = this.memberGroupsCache.get(key);
}
if (groupReferences == null) {
List<String> groupNames;
try {
Query query;
if (memberReference.getWikiReference().getName().equals(context.getWikiId())
|| (memberReference.getLastSpaceReference().getName().equals("XWiki") && memberReference
.getName().equals(XWikiRightService.GUEST_USER))) {
query =
context
.getWiki()
.getStore()
.getQueryManager()
.getNamedQuery("listGroupsForUser")
.bindValue("username", prefixedFullName)
.bindValue("shortname",
this.localWikiEntityReferenceSerializer.serialize(memberReference))
.bindValue("veryshortname", memberReference.getName());
} else {
query =
context.getWiki().getStore().getQueryManager()
.getNamedQuery("listGroupsForUserInOtherWiki")
.bindValue("prefixedmembername", prefixedFullName);
}
query.setOffset(offset);
query.setLimit(limit);
groupNames = query.execute();
} catch (QueryException ex) {
throw new XWikiException(0, 0, ex.getMessage(), ex);
}
groupReferences = new HashSet<DocumentReference>(groupNames.size());
for (String groupName : groupNames) {
groupReferences.add(this.currentMixedDocumentReferenceResolver.resolve(groupName));
}
// If the 'XWiki.XWikiAllGroup' is implicit, all users/groups except XWikiGuest and XWikiAllGroup
// itself are part of it.
if (isAllGroupImplicit(context)
&& memberReference.getWikiReference().getName().equals(context.getWikiId())
&& !memberReference.getName().equals(XWikiRightService.GUEST_USER)) {
DocumentReference currentXWikiAllGroup =
new DocumentReference(context.getWikiId(), "XWiki", XWikiRightService.ALLGROUP_GROUP);
if (!currentXWikiAllGroup.equals(memberReference)) {
groupReferences.add(currentXWikiAllGroup);
}
}
if (supportCache) {
this.memberGroupsCache.set(key, groupReferences);
}
}
}
return groupReferences;
}
@Override
public Collection<String> getAllMembersNamesForGroup(String group, int nb, int start, XWikiContext context)
throws XWikiException
{
return getAllMatchedMembersNamesForGroup(group, null, nb, start, null, context);
}
@Override
public Collection<String> getAllMatchedMembersNamesForGroup(String group, String matchField, int nb, int start,
Boolean orderAsc, XWikiContext context) throws XWikiException
{
// TODO: add cache mechanism.
XWikiDocument groupDocument = new XWikiDocument(this.currentMixedDocumentReferenceResolver.resolve(group));
Map<String, Object> parameterValues = new HashMap<String, Object>();
// //////////////////////////////////////
// Create the query string
StringBuilder queryString = new StringBuilder("SELECT field.value");
queryString.append(' ').append(
createMatchGroupMembersWhereClause(groupDocument.getFullName(), matchField, orderAsc, parameterValues));
try {
// //////////////////////////////////////
// Create the query
QueryManager qm = context.getWiki().getStore().getQueryManager();
Query query = qm.createQuery(queryString.toString(), Query.HQL);
for (Map.Entry<String, Object> entry : parameterValues.entrySet()) {
query.bindValue(entry.getKey(), entry.getValue());
}
query.setOffset(start);
query.setLimit(nb);
query.setWiki(groupDocument.getDocumentReference().getWikiReference().getName());
return query.execute();
} catch (QueryException ex) {
throw new XWikiException(0, 0, ex.getMessage(), ex);
}
}
@Override
public int countAllGroupsNamesForMember(String member, XWikiContext context) throws XWikiException
{
if (member == null) {
return 0;
}
// TODO: improve using real request
return getAllGroupsNamesForMember(member, 0, 0, context).size();
}
@Override
public int countAllMembersNamesForGroup(String group, XWikiContext context) throws XWikiException
{
if (group == null) {
return 0;
}
// TODO: improve using real request
return getAllMembersNamesForGroup(group, 0, 0, context).size();
}
}