1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package com.xpn.xwiki.user.impl.xwiki

File XWikiGroupServiceImpl.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart6.png
69% of files have more coverage

Code metrics

94
237
30
1
856
543
99
0.42
7.9
30
3.3

Classes

Class Line # Actions
XWikiGroupServiceImpl 69 237 0% 99 170
0.529085952.9%
 

Contributing tests

This file is covered by 1 test. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package com.xpn.xwiki.user.impl.xwiki;
21   
22    import java.util.ArrayList;
23    import java.util.Collection;
24    import java.util.HashMap;
25    import java.util.HashSet;
26    import java.util.List;
27    import java.util.Map;
28   
29    import org.apache.commons.lang3.StringUtils;
30    import org.slf4j.Logger;
31    import org.slf4j.LoggerFactory;
32    import org.xwiki.bridge.event.DocumentCreatedEvent;
33    import org.xwiki.bridge.event.DocumentDeletedEvent;
34    import org.xwiki.bridge.event.DocumentUpdatedEvent;
35    import org.xwiki.cache.Cache;
36    import org.xwiki.cache.CacheException;
37    import org.xwiki.cache.CacheManager;
38    import org.xwiki.cache.config.CacheConfiguration;
39    import org.xwiki.cache.eviction.EntryEvictionConfiguration;
40    import org.xwiki.cache.eviction.LRUEvictionConfiguration;
41    import org.xwiki.model.EntityType;
42    import org.xwiki.model.reference.DocumentReference;
43    import org.xwiki.model.reference.DocumentReferenceResolver;
44    import org.xwiki.model.reference.EntityReference;
45    import org.xwiki.model.reference.EntityReferenceSerializer;
46    import org.xwiki.observation.EventListener;
47    import org.xwiki.observation.ObservationManager;
48    import org.xwiki.observation.event.Event;
49    import org.xwiki.query.Query;
50    import org.xwiki.query.QueryException;
51    import org.xwiki.query.QueryManager;
52   
53    import com.xpn.xwiki.XWiki;
54    import com.xpn.xwiki.XWikiContext;
55    import com.xpn.xwiki.XWikiException;
56    import com.xpn.xwiki.doc.XWikiDocument;
57    import com.xpn.xwiki.objects.BaseObject;
58    import com.xpn.xwiki.store.XWikiStoreInterface;
59    import com.xpn.xwiki.user.api.XWikiGroupService;
60    import com.xpn.xwiki.user.api.XWikiRightService;
61    import com.xpn.xwiki.util.Util;
62    import com.xpn.xwiki.web.Utils;
63   
64    /**
65    * Default implementation of {@link XWikiGroupService} users and groups manager.
66    *
67    * @version $Id: d2c7ce5c25875a7a17ceff3db497673c6255ef71 $
68    */
 
69    public class XWikiGroupServiceImpl implements XWikiGroupService, EventListener
70    {
71    public static final EntityReference GROUPCLASS_REFERENCE = new EntityReference("XWikiGroups", EntityType.DOCUMENT,
72    new EntityReference("XWiki", EntityType.SPACE));
73   
74    private static final Logger LOGGER = LoggerFactory.getLogger(XWikiDocument.class);
75   
76    /**
77    * Name of the "XWiki.XWikiGroups" class without the space name.
78    */
79    private static final String CLASS_SUFFIX_XWIKIGROUPS = "XWikiGroups";
80   
81    /**
82    * Name of the "XWiki.XWikiUsers" class without the space name.
83    */
84    private static final String CLASS_SUFFIX_XWIKIUSERS = "XWikiUsers";
85   
86    /**
87    * Name of the "XWiki.XWikiGroups" class.
88    */
89    private static final String CLASS_XWIKIGROUPS = "XWiki." + CLASS_SUFFIX_XWIKIGROUPS;
90   
91    /**
92    * Name of the "XWiki.XWikiGroupTemplate" class sheet.
93    */
94    private static final String CLASSTEMPLATE_XWIKIGROUPS = "XWiki.XWikiGroupTemplate";
95   
96    /**
97    * Name of the "XWiki.XWikiUserTemplate" class sheet.
98    */
99    private static final String CLASSTEMPLATE_XWIKIUSERS = "XWiki.XWikiUserTemplate";
100   
101    /**
102    * Name of the field of class XWiki.XWikiGroups where group's members names are inserted.
103    */
104    private static final String FIELD_XWIKIGROUPS_MEMBER = "member";
105   
106    /**
107    * Default space name for a group or a user.
108    */
109    private static final String DEFAULT_MEMBER_SPACE = "XWiki";
110   
111    /**
112    * String between wiki name and full name in document path.
113    */
114    private static final String WIKI_FULLNAME_SEP = ":";
115   
116    /**
117    * String between space and name in document full name.
118    */
119    private static final String SPACE_NAME_SEP = ".";
120   
121    /**
122    * Symbol use in HQL "like" command that means "all characters".
123    */
124    private static final String HQLLIKE_ALL_SYMBOL = "%";
125   
126    private static final String NAME = "groupservice";
127   
128    private static final List<Event> EVENTS = new ArrayList<Event>()
129    {
 
130  29 toggle {
131  29 add(new DocumentCreatedEvent());
132  29 add(new DocumentUpdatedEvent());
133  29 add(new DocumentDeletedEvent());
134    }
135    };
136   
137    protected Cache<Collection<DocumentReference>> memberGroupsCache;
138   
139    /**
140    * Used to convert a string into a proper Document Reference.
141    */
142    private DocumentReferenceResolver<String> currentMixedDocumentReferenceResolver = Utils.getComponent(
143    DocumentReferenceResolver.TYPE_STRING, "currentmixed");
144   
145    private EntityReferenceSerializer<String> entityReferenceSerializer = Utils
146    .getComponent(EntityReferenceSerializer.TYPE_STRING);
147   
148    private EntityReferenceSerializer<String> localWikiEntityReferenceSerializer = Utils.getComponent(
149    EntityReferenceSerializer.TYPE_STRING, "local");
150   
 
151  50 toggle @Override
152    public synchronized void init(XWiki xwiki, XWikiContext context) throws XWikiException
153    {
154  50 initCache(context);
155   
156  50 Utils.getComponent(ObservationManager.class).addListener(this);
157    }
158   
 
159  50 toggle @Override
160    public synchronized void initCache(XWikiContext context) throws XWikiException
161    {
162  50 int iCapacity = 100;
163  50 try {
164  50 String capacity = context.getWiki().Param("xwiki.authentication.group.cache.capacity");
165  50 if (capacity != null) {
166  0 iCapacity = Integer.parseInt(capacity);
167    }
168    } catch (Exception e) {
169    }
170  50 initCache(iCapacity, context);
171    }
172   
 
173  50 toggle @Override
174    public synchronized void initCache(int iCapacity, XWikiContext context) throws XWikiException
175    {
176  50 try {
177  50 CacheConfiguration configuration = new CacheConfiguration();
178  50 configuration.setConfigurationId("xwiki.groupservice.usergroups");
179  50 LRUEvictionConfiguration lru = new LRUEvictionConfiguration();
180  50 lru.setMaxEntries(iCapacity);
181  50 configuration.put(EntryEvictionConfiguration.CONFIGURATIONID, lru);
182   
183  50 this.memberGroupsCache = Utils.getComponent(CacheManager.class).createNewCache(configuration);
184    } catch (CacheException e) {
185  0 throw new XWikiException(XWikiException.MODULE_XWIKI_CACHE, XWikiException.ERROR_CACHE_INITIALIZING,
186    "Failed to initialize cache", e);
187    }
188    }
189   
 
190  44 toggle @Override
191    public void flushCache()
192    {
193  44 if (this.memberGroupsCache != null) {
194  44 this.memberGroupsCache.removeAll();
195    }
196    }
197   
198    /**
199    * Check whether the configuration specifies that every user is implicitly in XWikiAllGroup. Configured by the
200    * {@code xwiki.authentication.group.allgroupimplicit} parameter in {@code xwiki.cfg}.
201    *
202    * @param context the current XWiki context
203    * @return {@code true} if the group is implicit and all users should be by default in it, {@code false} if the
204    * group behaves as all other groups, containing only the users/subgroups that are explicitly listed inside
205    * the document.
206    */
 
207  180 toggle protected boolean isAllGroupImplicit(XWikiContext context)
208    {
209  180 long implicit = context.getWiki().ParamAsLong("xwiki.authentication.group.allgroupimplicit", 0);
210  180 return (implicit == 1);
211    }
212   
 
213  0 toggle @Override
214    @Deprecated
215    public Collection<String> listGroupsForUser(String member, XWikiContext context) throws XWikiException
216    {
217  0 return getAllGroupsNamesForMember(member, -1, 0, context);
218    }
219   
 
220  0 toggle @Override
221    @Deprecated
222    public void addUserToGroup(String username, String database, String group, XWikiContext context)
223    throws XWikiException
224    {
225    // Don't do anything and let the listener do its job if there is a need
226    }
227   
228    /**
229    * Check if provided member is equal to member name found in XWiki.XWikiGroups object.
230    *
231    * @param currentMember the member name found in XWiki.XWikiGroups object.
232    * @param memberWiki the name of the wiki of the member.
233    * @param memberSpace the name of the space of the member.
234    * @param memberName the name of the member.
235    * @param context the XWiki context.
236    * @return true if equals, false if not.
237    */
 
238  0 toggle private boolean isMemberEquals(String currentMember, String memberWiki, String memberSpace, String memberName,
239    XWikiContext context)
240    {
241  0 boolean equals = false;
242   
243  0 if (memberWiki != null) {
244  0 equals |= currentMember.equals(memberWiki + WIKI_FULLNAME_SEP + memberSpace + SPACE_NAME_SEP + memberName);
245   
246  0 if (memberSpace == null || memberSpace.equals(DEFAULT_MEMBER_SPACE)) {
247  0 equals |= currentMember.equals(memberSpace + SPACE_NAME_SEP + memberName);
248    }
249    }
250   
251  0 if (context.getWikiId() == null || context.getWikiId().equalsIgnoreCase(memberWiki)) {
252  0 equals |= currentMember.equals(memberName);
253   
254  0 if (memberSpace == null || memberSpace.equals(DEFAULT_MEMBER_SPACE)) {
255  0 equals |= currentMember.equals(memberSpace + SPACE_NAME_SEP + memberName);
256    }
257    }
258   
259  0 return equals;
260    }
261   
262    /**
263    * Remove user or group name from a group {@link XWikiDocument}.
264    *
265    * @param groupDocument the {@link XWikiDocument} containing group object.
266    * @param memberWiki the name of the wiki of the member.
267    * @param memberSpace the name of the space of the member.
268    * @param memberName the name of the member.
269    * @param context the XWiki context.
270    * @return true if at least one member has been removed from the list.
271    */
 
272  0 toggle private boolean removeUserOrGroupFromGroup(XWikiDocument groupDocument, String memberWiki, String memberSpace,
273    String memberName, XWikiContext context)
274    {
275  0 boolean needUpdate = false;
276   
277  0 List<BaseObject> groups = groupDocument.getXObjects(GROUPCLASS_REFERENCE);
278   
279  0 if (groups != null) {
280  0 for (BaseObject bobj : groups) {
281  0 if (bobj != null) {
282  0 String member = bobj.getStringValue(FIELD_XWIKIGROUPS_MEMBER);
283   
284  0 if (isMemberEquals(member, memberWiki, memberSpace, memberName, context)) {
285  0 needUpdate = groupDocument.removeXObject(bobj);
286    }
287    }
288    }
289    }
290   
291  0 return needUpdate;
292    }
293   
 
294  0 toggle @Override
295    public void removeUserOrGroupFromAllGroups(String memberWiki, String memberSpace, String memberName,
296    XWikiContext context) throws XWikiException
297    {
298  0 List<Object> parameterValues = new ArrayList<Object>();
299   
300  0 StringBuilder where =
301    new StringBuilder(
302    ", BaseObject as obj, StringProperty as prop where doc.fullName=obj.name and obj.className=?");
303  0 parameterValues.add(CLASS_XWIKIGROUPS);
304   
305  0 where.append(" and obj.id=prop.id.id");
306   
307  0 where.append(" and prop.name=?");
308  0 parameterValues.add(FIELD_XWIKIGROUPS_MEMBER);
309   
310  0 where.append(" and prop.value like ?");
311   
312  0 if (context.getWikiId() == null || context.getWikiId().equalsIgnoreCase(memberWiki)) {
313  0 if (memberSpace == null || memberSpace.equals(DEFAULT_MEMBER_SPACE)) {
314  0 parameterValues.add(HQLLIKE_ALL_SYMBOL + memberName + HQLLIKE_ALL_SYMBOL);
315    } else {
316  0 parameterValues.add(HQLLIKE_ALL_SYMBOL + memberSpace + SPACE_NAME_SEP + memberName + HQLLIKE_ALL_SYMBOL);
317    }
318    } else {
319  0 parameterValues.add(HQLLIKE_ALL_SYMBOL + memberWiki + WIKI_FULLNAME_SEP + memberSpace + SPACE_NAME_SEP
320    + memberName + HQLLIKE_ALL_SYMBOL);
321    }
322   
323  0 List<XWikiDocument> documentList =
324    context.getWiki().getStore().searchDocuments(where.toString(), parameterValues, context);
325   
326  0 for (XWikiDocument groupDocument : documentList) {
327  0 if (removeUserOrGroupFromGroup(groupDocument, memberWiki, memberSpace, memberName, context)) {
328  0 context.getWiki().saveDocument(groupDocument, context);
329    }
330    }
331    }
332   
 
333  2 toggle @Override
334    @Deprecated
335    public List<String> listMemberForGroup(String group, XWikiContext context) throws XWikiException
336    {
337  2 List<String> list = new ArrayList<String>();
338   
339  2 try {
340  2 if (group == null) {
341  0 try {
342  0 return context.getWiki().getStore().getQueryManager().getNamedQuery("getAllUsers").execute();
343    } catch (QueryException ex) {
344  0 throw new XWikiException(0, 0, ex.getMessage(), ex);
345    }
346    } else {
347  2 String gshortname = Util.getName(group, context);
348  2 XWikiDocument docgroup = context.getWiki().getDocument(gshortname, context);
349   
350  2 List<BaseObject> groups = docgroup.getXObjects(GROUPCLASS_REFERENCE);
351  2 if (groups != null) {
352  2 for (BaseObject bobj : groups) {
353  2 if (bobj != null) {
354  2 String member = bobj.getStringValue(FIELD_XWIKIGROUPS_MEMBER);
355  2 if (StringUtils.isNotEmpty(member)) {
356  2 list.add(member);
357    }
358    }
359    }
360    }
361   
362  2 return list;
363    }
364    } catch (XWikiException e) {
365  0 LOGGER.error("Failed to get group document", e);
366    }
367   
368  0 return null;
369    }
370   
 
371  0 toggle @Override
372    @Deprecated
373    public List<String> listAllGroups(XWikiContext context) throws XWikiException
374    {
375  0 if (context.getWiki().getHibernateStore() != null) {
376  0 String sql = ", BaseObject as obj where obj.name=doc.fullName and obj.className='XWiki.XWikiGroups'";
377  0 return context.getWiki().getStore().searchDocumentsNames(sql, context);
378    } else {
379  0 return null;
380    }
381    }
382   
 
383  400 toggle @Override
384    public String getName()
385    {
386  400 return NAME;
387    }
388   
 
389  50 toggle @Override
390    public List<Event> getEvents()
391    {
392  50 return EVENTS;
393    }
394   
 
395  3295 toggle @Override
396    public void onEvent(Event event, Object source, Object data)
397    {
398  3295 XWikiDocument document = (XWikiDocument) source;
399  3295 XWikiDocument oldDocument = document.getOriginalDocument();
400   
401    // if there is any chance some group changed, flush the group cache
402  3295 if (document.getXObject(GROUPCLASS_REFERENCE) != null || oldDocument.getXObject(GROUPCLASS_REFERENCE) != null) {
403  44 flushCache();
404    }
405    }
406   
 
407  2 toggle @Override
408    public List<?> getAllMatchedUsers(Object[][] matchFields, boolean withdetails, int nb, int start,
409    Object[][] order, XWikiContext context) throws XWikiException
410    {
411  2 return getAllMatchedUsersOrGroups(true, matchFields, withdetails, nb, start, order, context);
412    }
413   
 
414  5 toggle @Override
415    public List<?> getAllMatchedGroups(Object[][] matchFields, boolean withdetails, int nb, int start,
416    Object[][] order, XWikiContext context) throws XWikiException
417    {
418  5 return getAllMatchedUsersOrGroups(false, matchFields, withdetails, nb, start, order, context);
419    }
420   
421    /**
422    * Create a "where clause" to use with {@link XWikiStoreInterface} searchDocuments and searchDocumentsNames methods.
423    *
424    * @param user if true search for users, otherwise search for groups.
425    * @param matchFields the field to math with values. It is a table of table with :
426    * <ul>
427    * <li>fiedname : the name of the field</li>
428    * <li>fieldtype : for example StringProperty. If null the field is considered as document field</li>
429    * <li>pattern matching : based on HQL "like" command</li>
430    * </ul>
431    * @param order the field to order from. It is a table of table with :
432    * <ul>
433    * <li>fieldname : the name of the field</li>
434    * <li>fieldtype : for example StringProperty. If null the field is considered as document field</li>
435    * <li>asc : a Boolean, if true the order is ascendent</li>
436    * </ul>
437    * @param parameterValues the list of values to fill for use with HQL named request.
438    * @return the formated HQL named request.
439    */
 
440  12 toggle protected String createMatchUserOrGroupWhereClause(boolean user, Object[][] matchFields, Object[][] order,
441    List<Object> parameterValues)
442    {
443  12 String documentClass = user ? CLASS_SUFFIX_XWIKIUSERS : CLASS_SUFFIX_XWIKIGROUPS;
444  12 String classtemplate = user ? CLASSTEMPLATE_XWIKIUSERS : CLASSTEMPLATE_XWIKIGROUPS;
445   
446  12 StringBuilder from = new StringBuilder(", BaseObject as obj");
447   
448  12 StringBuilder where = new StringBuilder(" where doc.fullName=obj.name and doc.fullName<>? and obj.className=?");
449  12 parameterValues.add(classtemplate);
450  12 parameterValues.add("XWiki." + documentClass);
451   
452  12 Map<String, String> fieldMap = new HashMap<String, String>();
453  12 int fieldIndex = 0;
454   
455    // Manage object match strings
456  12 if (matchFields != null) {
457  0 for (Object[] matchField : matchFields) {
458  0 String fieldName = (String) matchField[0];
459  0 String type = (String) matchField[1];
460  0 String value = (String) matchField[2];
461   
462  0 if (type != null) {
463  0 String fieldPrefix;
464   
465  0 if (!fieldMap.containsKey(fieldName)) {
466  0 fieldPrefix = "field" + fieldIndex;
467  0 from.append(", " + type + " as " + fieldPrefix);
468   
469  0 where.append(" and obj.id=" + fieldPrefix + ".id.id");
470  0 where.append(" and " + fieldPrefix + ".name=?");
471  0 parameterValues.add(fieldName);
472  0 ++fieldIndex;
473    } else {
474  0 fieldPrefix = fieldMap.get(fieldName);
475    }
476   
477  0 where.append(" and lower(" + fieldPrefix + ".value) like ?");
478  0 parameterValues.add(HQLLIKE_ALL_SYMBOL + value.toLowerCase() + HQLLIKE_ALL_SYMBOL);
479   
480  0 fieldMap.put(fieldName, fieldPrefix);
481    } else {
482  0 where.append(" and lower(doc." + fieldName + ") like ?");
483  0 parameterValues.add(HQLLIKE_ALL_SYMBOL + value.toLowerCase() + HQLLIKE_ALL_SYMBOL);
484    }
485    }
486    }
487   
488  12 StringBuilder orderString = new StringBuilder();
489   
490    // Manage order
491  12 if (order != null && order.length > 0) {
492  5 orderString.append(" order by");
493   
494  10 for (int i = 0; i < order.length; ++i) {
495  5 String fieldName = (String) order[i][0];
496  5 String type = (String) order[i][1];
497  5 Boolean asc = (Boolean) order[i][2];
498   
499  5 if (i > 0) {
500  0 orderString.append(",");
501    }
502   
503  5 if (type != null) {
504  0 String fieldPrefix;
505   
506  0 if (!fieldMap.containsKey(fieldName)) {
507  0 fieldPrefix = "field" + fieldIndex;
508  0 from.append(", " + type + " as " + fieldPrefix);
509   
510  0 where.append(" and obj.id=" + fieldPrefix + ".id.id");
511  0 where.append(" and " + fieldPrefix + ".name=?");
512  0 parameterValues.add(fieldName);
513  0 ++fieldIndex;
514    } else {
515  0 fieldPrefix = fieldMap.get(fieldName);
516    }
517   
518  0 orderString.append(" " + fieldPrefix + ".value");
519    } else {
520  5 orderString.append(" doc." + fieldName);
521    }
522   
523  5 orderString.append(asc == null || asc.booleanValue() ? " asc" : " desc");
524    }
525    }
526   
527  12 return from.append(where).append(orderString).toString();
528    }
529   
530    /**
531    * Search for all users or group with provided constraints and in a provided order.
532    *
533    * @param user if true search for users, otherwise search for groups.
534    * @param matchFields the field to math with values. It is a table of table with :
535    * <ul>
536    * <li>fiedname : the name of the field</li>
537    * <li>fieldtype : for example StringProperty. If null the field is considered as document field</li>
538    * <li>pattern matching : based on HQL "like" command</li>
539    * </ul>
540    * @param withdetails indicate if a {@link List} containing {@link String} names is returned or {@link List}
541    * containing {@link XWikiDocument}.
542    * @param nb the maximum number of results to return. Infinite if 0.
543    * @param start the index of the first found user or group to return.
544    * @param order the field to order from. It is a table of table with :
545    * <ul>
546    * <li>fieldname : the name of the field</li>
547    * <li>fieldtype : for example StringProperty. If null the field is considered as document field</li>
548    * </ul>
549    * @param context the {@link XWikiContext}.
550    * @return the list of users or groups.
551    * @throws XWikiException error when calling for
552    * {@link XWikiStoreInterface#searchDocuments(String, int, int, List, XWikiContext)} or
553    * {@link XWikiStoreInterface#searchDocumentsNames(String, int, int, List, XWikiContext)}
554    */
 
555  7 toggle protected List<?> getAllMatchedUsersOrGroups(boolean user, Object[][] matchFields, boolean withdetails, int nb,
556    int start, Object[][] order, XWikiContext context) throws XWikiException
557    {
558  7 List<?> groups = null;
559   
560  7 if (context.getWiki().getHibernateStore() != null) {
561  7 List<Object> parameterValues = new ArrayList<Object>();
562  7 String where = createMatchUserOrGroupWhereClause(user, matchFields, order, parameterValues);
563   
564  7 if (withdetails) {
565  5 groups =
566    context.getWiki().getStore().searchDocuments(where, false, nb, start, parameterValues, context);
567    } else {
568  2 groups = context.getWiki().getStore().searchDocumentsNames(where, nb, start, parameterValues, context);
569    }
570  0 } else if (context.getWiki().getStore().getQueryManager().hasLanguage(Query.XPATH)) {
571    // TODO : fully implement this methods for XPATH platform
572  0 if ((matchFields != null && matchFields.length > 0) || withdetails) {
573  0 throw new UnsupportedOperationException(
574    "The current storage engine does not support advanced group queries");
575    }
576   
577  0 try {
578  0 groups =
579    context
580    .getWiki()
581    .getStore()
582    .getQueryManager()
583    .createQuery(
584  0 "/*/*[obj/XWiki/" + (user ? CLASS_SUFFIX_XWIKIUSERS : CLASS_SUFFIX_XWIKIGROUPS)
585    + "]/@fullName", Query.XPATH).setLimit(nb).setOffset(start).execute();
586    } catch (QueryException ex) {
587  0 throw new XWikiException(0, 0, ex.getMessage(), ex);
588    }
589    }
590   
591  7 return groups;
592    }
593   
594    /**
595    * Return number of users or groups with provided constraints.
596    *
597    * @param user if true search for users, otherwise search for groups.
598    * @param matchFields the field to math with values. It is a table of table with :
599    * <ul>
600    * <li>fiedname : the name of the field</li>
601    * <li>fieldtype : for example StringProperty. If null the field is considered as document field</li>
602    * <li>pattern matching : based on HQL "like" command</li>
603    * </ul>
604    * @param context the {@link XWikiContext}.
605    * @return the of found users or groups.
606    * @throws XWikiException error when calling for
607    * {@link XWikiStoreInterface#search(String, int, int, List, XWikiContext)}
608    */
 
609  5 toggle protected int countAllMatchedUsersOrGroups(boolean user, Object[][] matchFields, XWikiContext context)
610    throws XWikiException
611    {
612  5 List<Object> parameterValues = new ArrayList<Object>();
613  5 String where = createMatchUserOrGroupWhereClause(user, matchFields, null, parameterValues);
614   
615  5 String sql = "select count(distinct doc) from XWikiDocument doc" + where;
616   
617  5 List<?> list = context.getWiki().getStore().search(sql, 0, 0, parameterValues, context);
618   
619  5 if (list == null || list.size() == 0) {
620  0 return 0;
621    }
622   
623  5 return ((Number) list.get(0)).intValue();
624    }
625   
 
626  1 toggle @Override
627    public int countAllMatchedUsers(Object[][] matchFields, XWikiContext context) throws XWikiException
628    {
629  1 return countAllMatchedUsersOrGroups(true, matchFields, context);
630    }
631   
 
632  4 toggle @Override
633    public int countAllMatchedGroups(Object[][] matchFields, XWikiContext context) throws XWikiException
634    {
635  4 return countAllMatchedUsersOrGroups(false, matchFields, context);
636    }
637   
638    // ///////////////////////////////////////////////////////////////////
639    // Group members and member groups
640   
641    /**
642    * Create a query to filter provided group members. Generate the entire HQL query except the select part.
643    *
644    * @param groupFullName the fill wiki name of the group.
645    * @param matchField a string to search in result to filter.
646    * @param orderAsc if true, the result is ordered ascendent, if false it descendant. If null no order is applied.
647    * @param parameterValues the values to insert in names query.
648    * @return the HQL query.
649    * @since 1.6M1
650    */
 
651  100 toggle protected String createMatchGroupMembersWhereClause(String groupFullName, String matchField, Boolean orderAsc,
652    Map<String, Object> parameterValues)
653    {
654  100 StringBuilder queryString = new StringBuilder();
655   
656    // Add from clause
657  100 queryString.append(" FROM BaseObject as obj, StringProperty as field");
658   
659    // Add where clause
660  100 queryString.append(" WHERE obj.name=:groupdocname "
661    + "and obj.className=:groupclassname and obj.id=field.id.id");
662  100 parameterValues.put("groupdocname", groupFullName);
663  100 parameterValues.put("groupclassname", CLASS_XWIKIGROUPS);
664   
665    // Note: We should normally be able to use the 3-argument trim() function which defaults to the whitesapce
666    // char to be trimmed. However because of https://hibernate.atlassian.net/browse/HHH-8295 this raises a
667    // warning in the XWiki console. Once this is fixed in Hibernate we can start using the 3-argument function
668    // again.
669  100 queryString.append(" and (trim(both ' ' from field.value)<>'' or "
670    + "(trim(both ' ' from field.value) is not null and '' is null))");
671   
672  100 if (matchField != null) {
673  4 queryString.append(" and lower(field.value) like :matchfield");
674  4 parameterValues.put("matchfield", HQLLIKE_ALL_SYMBOL + matchField.toLowerCase() + HQLLIKE_ALL_SYMBOL);
675    }
676   
677    // Add order by clause
678  100 if (orderAsc != null) {
679  7 queryString.append(" ORDER BY field.value ").append(orderAsc ? "asc" : "desc");
680    }
681   
682  100 return queryString.toString();
683    }
684   
 
685  0 toggle @Override
686    public Collection<String> getAllGroupsNamesForMember(String member, int nb, int start, XWikiContext context)
687    throws XWikiException
688    {
689  0 List<String> groupNames = null;
690   
691  0 DocumentReference memberReference = this.currentMixedDocumentReferenceResolver.resolve(member);
692   
693  0 String currentWiki = context.getWikiId();
694  0 try {
695  0 context.setWikiId(memberReference.getWikiReference().getName());
696   
697  0 Collection<DocumentReference> groupReferences =
698    getAllGroupsReferencesForMember(memberReference, nb, start, context);
699   
700  0 groupNames = new ArrayList<String>(groupReferences.size());
701  0 for (DocumentReference groupReference : groupReferences) {
702  0 groupNames.add(this.localWikiEntityReferenceSerializer.serialize(groupReference));
703    }
704    } finally {
705  0 context.setWikiId(currentWiki);
706    }
707   
708  0 return groupNames;
709    }
710   
 
711  179 toggle @Override
712    public Collection<DocumentReference> getAllGroupsReferencesForMember(DocumentReference memberReference, int limit,
713    int offset, XWikiContext context) throws XWikiException
714    {
715  180 Collection<DocumentReference> groupReferences = null;
716   
717  179 String prefixedFullName = this.entityReferenceSerializer.serialize(memberReference);
718   
719  180 String key = context.getWikiId() + "/" + prefixedFullName;
720  180 synchronized (key) {
721  180 if (this.memberGroupsCache == null) {
722  0 initCache(context);
723    }
724   
725    // TODO: add cache support for customized limit/offset ?
726  180 boolean supportCache = limit <= 0 && offset <= 0;
727   
728  180 if (supportCache) {
729  1 groupReferences = this.memberGroupsCache.get(key);
730    }
731   
732  180 if (groupReferences == null) {
733  180 List<String> groupNames;
734  180 try {
735  180 Query query;
736  180 if (memberReference.getWikiReference().getName().equals(context.getWikiId())
737    || (memberReference.getLastSpaceReference().getName().equals("XWiki") && memberReference
738    .getName().equals(XWikiRightService.GUEST_USER))) {
739  180 query =
740    context
741    .getWiki()
742    .getStore()
743    .getQueryManager()
744    .getNamedQuery("listGroupsForUser")
745    .bindValue("username", prefixedFullName)
746    .bindValue("shortname",
747    this.localWikiEntityReferenceSerializer.serialize(memberReference))
748    .bindValue("veryshortname", memberReference.getName());
749    } else {
750  0 query =
751    context.getWiki().getStore().getQueryManager()
752    .getNamedQuery("listGroupsForUserInOtherWiki")
753    .bindValue("prefixedmembername", prefixedFullName);
754    }
755   
756  180 query.setOffset(offset);
757  178 query.setLimit(limit);
758   
759  175 groupNames = query.execute();
760    } catch (QueryException ex) {
761  0 throw new XWikiException(0, 0, ex.getMessage(), ex);
762    }
763   
764  180 groupReferences = new HashSet<DocumentReference>(groupNames.size());
765  180 for (String groupName : groupNames) {
766  62 groupReferences.add(this.currentMixedDocumentReferenceResolver.resolve(groupName));
767    }
768   
769    // If the 'XWiki.XWikiAllGroup' is implicit, all users/groups except XWikiGuest and XWikiAllGroup
770    // itself are part of it.
771  180 if (isAllGroupImplicit(context)
772    && memberReference.getWikiReference().getName().equals(context.getWikiId())
773    && !memberReference.getName().equals(XWikiRightService.GUEST_USER)) {
774  0 DocumentReference currentXWikiAllGroup =
775    new DocumentReference(context.getWikiId(), "XWiki", XWikiRightService.ALLGROUP_GROUP);
776   
777  0 if (!currentXWikiAllGroup.equals(memberReference)) {
778  0 groupReferences.add(currentXWikiAllGroup);
779    }
780    }
781   
782  180 if (supportCache) {
783  1 this.memberGroupsCache.set(key, groupReferences);
784    }
785    }
786    }
787   
788  180 return groupReferences;
789    }
790   
 
791  93 toggle @Override
792    public Collection<String> getAllMembersNamesForGroup(String group, int nb, int start, XWikiContext context)
793    throws XWikiException
794    {
795  93 return getAllMatchedMembersNamesForGroup(group, null, nb, start, null, context);
796    }
797   
 
798  100 toggle @Override
799    public Collection<String> getAllMatchedMembersNamesForGroup(String group, String matchField, int nb, int start,
800    Boolean orderAsc, XWikiContext context) throws XWikiException
801    {
802    // TODO: add cache mechanism.
803  100 XWikiDocument groupDocument = new XWikiDocument(this.currentMixedDocumentReferenceResolver.resolve(group));
804   
805  100 Map<String, Object> parameterValues = new HashMap<String, Object>();
806    // //////////////////////////////////////
807    // Create the query string
808  100 StringBuilder queryString = new StringBuilder("SELECT field.value");
809   
810  100 queryString.append(' ').append(
811    createMatchGroupMembersWhereClause(groupDocument.getFullName(), matchField, orderAsc, parameterValues));
812   
813  100 try {
814    // //////////////////////////////////////
815    // Create the query
816  100 QueryManager qm = context.getWiki().getStore().getQueryManager();
817   
818  100 Query query = qm.createQuery(queryString.toString(), Query.HQL);
819   
820  100 for (Map.Entry<String, Object> entry : parameterValues.entrySet()) {
821  204 query.bindValue(entry.getKey(), entry.getValue());
822    }
823   
824  100 query.setOffset(start);
825  100 query.setLimit(nb);
826   
827  100 query.setWiki(groupDocument.getDocumentReference().getWikiReference().getName());
828   
829  100 return query.execute();
830    } catch (QueryException ex) {
831  0 throw new XWikiException(0, 0, ex.getMessage(), ex);
832    }
833    }
834   
 
835  0 toggle @Override
836    public int countAllGroupsNamesForMember(String member, XWikiContext context) throws XWikiException
837    {
838  0 if (member == null) {
839  0 return 0;
840    }
841   
842    // TODO: improve using real request
843  0 return getAllGroupsNamesForMember(member, 0, 0, context).size();
844    }
845   
 
846  9 toggle @Override
847    public int countAllMembersNamesForGroup(String group, XWikiContext context) throws XWikiException
848    {
849  9 if (group == null) {
850  0 return 0;
851    }
852   
853    // TODO: improve using real request
854  9 return getAllMembersNamesForGroup(group, 0, 0, context).size();
855    }
856    }