1. Project Clover database Sat Feb 2 2019 06:45:20 CET
  2. Package com.xpn.xwiki.user.impl.xwiki

File XWikiGroupServiceImpl.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart8.png
56% of files have more coverage

Code metrics

82
223
31
1
828
518
93
0.42
7.19
31
3

Classes

Class Line # Actions
XWikiGroupServiceImpl 63 223 0% 93 94
0.720238172%
 

Contributing tests

This file is covered by 3 tests. .

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