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

File StatsUtil.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart4.png
78% of files have more coverage

Code metrics

68
174
25
2
807
376
67
0.39
6.96
12.5
2.68

Classes

Class Line # Actions
StatsUtil 54 174 0% 67 169
0.367041236.7%
StatsUtil.PeriodType 190 0 - 0 0
-1.0 -
 

Contributing tests

This file is covered by 2 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.stats.impl;
21   
22    import java.net.MalformedURLException;
23    import java.net.URL;
24    import java.util.ArrayList;
25    import java.util.Calendar;
26    import java.util.Collection;
27    import java.util.Collections;
28    import java.util.Date;
29    import java.util.List;
30   
31    import javax.servlet.http.Cookie;
32    import javax.servlet.http.HttpSession;
33   
34    import org.apache.commons.lang3.RandomStringUtils;
35    import org.apache.commons.lang3.StringUtils;
36    import org.slf4j.Logger;
37    import org.slf4j.LoggerFactory;
38    import org.xwiki.model.reference.DocumentReference;
39    import org.xwiki.query.Query;
40    import org.xwiki.query.QueryManager;
41   
42    import com.xpn.xwiki.XWikiContext;
43    import com.xpn.xwiki.XWikiException;
44    import com.xpn.xwiki.plugin.rightsmanager.RightsManager;
45    import com.xpn.xwiki.user.api.XWikiRightService;
46    import com.xpn.xwiki.util.Util;
47    import com.xpn.xwiki.web.XWikiRequest;
48   
49    /**
50    * Utility class for statistics.
51    *
52    * @version $Id: 5f076ea496b853c7595c9e6ffca5bfb66991f04e $
53    */
 
54    public final class StatsUtil
55    {
56    /**
57    * Logging tools.
58    */
59    private static final Logger LOGGER = LoggerFactory.getLogger(StatsUtil.class);
60   
61    /**
62    * Default separator for a list.
63    */
64    private static final String LIST_SEPARATOR = ",";
65   
66    /**
67    * Default separator for a list in escaped form.
68    */
69    private static final String ESCAPED_LIST_SEPARATOR = "\\,";
70   
71    /**
72    * The name of the property in XWiki configuration file containing the list of cookie domains.
73    */
74    private static final String CFGPROP_COOKIEDOMAINS = "xwiki.authentication.cookiedomains";
75   
76    /**
77    * Separator for the property in XWiki configuration file containing the list of cookie domains.
78    */
79    private static final char CFGPROP_COOKIEDOMAINS_SEP = ',';
80   
81    /**
82    * The name of the property in XWiki configuration file indicating if statistics are enabled.
83    */
84    private static final String CFGPROP_STATS = "xwiki.stats";
85   
86    /**
87    * The name of the property in XWiki configuration file indicating if a virtual wiki store statistics by default.
88    */
89    private static final String CFGPROP_STATS_DEFAULT = "xwiki.stats.default";
90   
91    /**
92    * See {@link #CFGPROP_STATS_EXCLUDEDUSERSANDGROUPS_REQUEST}.
93    */
94    @Deprecated
95    private static final String DEPRECATED_CFGPROP_STATS_EXCLUDEDUSERSANDGROUPS = "xwiki.stats.excludedUsersAndGroups";
96   
97    /**
98    * The name of the property in XWiki configuration file containing the list of users and group to filter in
99    * statistics view requests.
100    */
101    private static final String CFGPROP_STATS_EXCLUDEDUSERSANDGROUPS_REQUEST =
102    "xwiki.stats.request.excludedUsersAndGroups";
103   
104    /**
105    * The name of the property in XWiki configuration file containing the list of users and group to filter in
106    * statistics storage.
107    */
108    private static final String CFGPROP_STATS_EXCLUDEDUSERSANDGROUPS_STORAGE =
109    "xwiki.stats.storage.excludedUsersAndGroups";
110   
111    /**
112    * The prefix name of the session property containing recent statistics actions.
113    */
114    private static final String SESSPROP_RECENT_PREFFIX = "recent_";
115   
116    /**
117    * The prefix name of the session property containing the current visit object.
118    */
119    private static final String SESSPROP_VISITOBJECT = "visitObject";
120   
121    /**
122    * The name of the session property containing the size of the recent list of visit statistics actions.
123    */
124    private static final String PREFPROP_RECENT_VISITS_SIZE = "recent_visits_size";
125   
126    /**
127    * The name of the XWiki preferences property indicating if current wiki store statistics.
128    */
129    private static final String PREFPROP_STATISTICS = "statistics";
130   
131    /**
132    * See {@link #PREFPROP_EXCLUDEDUSERSANDGROUPS_REQUEST}.
133    */
134    @Deprecated
135    private static final String DEPRECATED_PREFPROP_EXCLUDEDUSERSANDGROUPS = "statistics_excludedUsersAndGroups";
136   
137    /**
138    * The name of the XWiki preferences property containing the list of users and group to filter in statistics view
139    * requests.
140    */
141    private static final String PREFPROP_EXCLUDEDUSERSANDGROUPS_REQUEST = "statistics_request_excludedUsersAndGroups";
142   
143    /**
144    * The name of the XWiki preferences property containing the list of users and group to filter in statistics
145    * storage.
146    */
147    private static final String PREFPROP_EXCLUDEDUSERSANDGROUPS_STORAGE = "statistics_storage_excludedUsersAndGroups";
148   
149    /**
150    * The name of the request property containing the referer.
151    */
152    private static final String REQPROP_REFERER = "referer";
153   
154    /**
155    * The name of the request property containing the user agent.
156    */
157    private static final String REQPROP_USERAGENT = "User-Agent";
158   
159    /**
160    * The name of the context property containing the statistics cookie name.
161    */
162    private static final String CONTPROP_STATS_COOKIE = "stats_cookie";
163   
164    /**
165    * The name of the context property indicating if the cookie in the context is new.
166    */
167    private static final String CONTPROP_STATS_NEWCOOKIE = "stats_newcookie";
168   
169    /**
170    * The name of the cookie property containing the unique id of the visit object.
171    */
172    private static final String COOKPROP_VISITID = "visitid";
173   
174    /**
175    * The list of cookie domains.
176    */
177    private static String[] cookieDomains;
178   
179    /**
180    * The expiration date of the cookie.
181    */
182    private static Date cookieExpirationDate;
183   
184    /**
185    * The type of the period.
186    *
187    * @version $Id: 5f076ea496b853c7595c9e6ffca5bfb66991f04e $
188    * @since 1.4M1
189    */
 
190    public enum PeriodType
191    {
192    /**
193    * Based on month.
194    */
195    MONTH,
196    /**
197    * Based on day.
198    */
199    DAY
200    }
201   
202    /**
203    * Default {@link StatsUtil} constructor.
204    */
 
205  0 toggle private StatsUtil()
206    {
207    }
208   
209    /**
210    * Computes an integer representation of the passed date using the following format:
211    * <ul>
212    * <li>"yyyMMdd" for {@link PeriodType#DAY}</li>
213    * <li>"yyyMM" for {@link PeriodType#MONTH}</li>
214    * </ul>
215    * .
216    *
217    * @param date the date for which to return an integer representation.
218    * @param type the date type. It can be {@link PeriodType#DAY} or {@link PeriodType#MONTH}.
219    * @return the integer representation of the specified date.
220    * @see java.text.SimpleDateFormat
221    * @since 1.4M1
222    */
 
223  2 toggle public static int getPeriodAsInt(Date date, PeriodType type)
224    {
225  2 int period;
226   
227  2 Calendar cal = Calendar.getInstance();
228  2 if (date != null) {
229  2 cal.setTime(date);
230    }
231   
232  2 if (type == PeriodType.MONTH) {
233    // The first month of the year is JANUARY which is 0
234  1 period = cal.get(Calendar.YEAR) * 100 + (cal.get(Calendar.MONTH) + 1);
235    } else {
236    // The first day of the month has value 1
237  1 period =
238    cal.get(Calendar.YEAR) * 10000 + (cal.get(Calendar.MONTH) + 1) * 100 + cal.get(Calendar.DAY_OF_MONTH);
239    }
240   
241  2 return period;
242    }
243   
244    /**
245    * @param context the XWiki context.
246    * @return the list of cookie domains.
247    * @since 1.4M1
248    */
 
249  922 toggle public static String[] getCookieDomains(XWikiContext context)
250    {
251  921 if (cookieDomains == null) {
252  29 cookieDomains =
253    StringUtils.split(context.getWiki().Param(CFGPROP_COOKIEDOMAINS), CFGPROP_COOKIEDOMAINS_SEP);
254    }
255   
256  925 return cookieDomains;
257    }
258   
259    /**
260    * @return the expiration date of the cookie.
261    * @since 1.4M1
262    */
 
263  923 toggle public static Date getCookieExpirationDate()
264    {
265    // Let's init the expirationDate for the cookie
266  923 Calendar cal = Calendar.getInstance();
267  922 cal.set(2030, 0, 0);
268  924 cookieExpirationDate = cal.getTime();
269   
270  924 return cookieExpirationDate;
271    }
272   
273    /**
274    * @param context the XWiki context from where to get the HTTP session session.
275    * @param action the action id.
276    * @return the recent statistics actions stored in the session.
277    * @since 1.4M1
278    */
 
279  744 toggle public static Collection<?> getRecentActionFromSessions(XWikiContext context, String action)
280    {
281  744 return (Collection<?>) context.getRequest().getSession().getAttribute(SESSPROP_RECENT_PREFFIX + action);
282    }
283   
284    /**
285    * Store the recent statistics actions in the session.
286    *
287    * @param context the XWiki context from where to get the HTTP session session.
288    * @param action the action id.
289    * @param actions the actions.
290    * @since 1.4M1
291    */
 
292  108 toggle public static void setRecentActionsFromSession(XWikiContext context, String action, Collection<?> actions)
293    {
294  108 context.getRequest().getSession().setAttribute(SESSPROP_RECENT_PREFFIX + action, actions);
295    }
296   
297    /**
298    * @param context the XWiki context.
299    * @return the size of the recent list of visit statistics actions.
300    * @since 1.4M1
301    */
 
302  108 toggle public static int getRecentVisitSize(XWikiContext context)
303    {
304  108 return context.getWiki().getXWikiPreferenceAsInt(PREFPROP_RECENT_VISITS_SIZE, 20, context);
305    }
306   
307    /**
308    * @param session the session.
309    * @return the visit object stored in the session.
310    * @since 1.4M1
311    */
 
312  0 toggle public static VisitStats getVisitFromSession(HttpSession session)
313    {
314  0 return (VisitStats) session.getAttribute(SESSPROP_VISITOBJECT);
315    }
316   
317    /**
318    * Store the visit object in the session.
319    *
320    * @param session the session.
321    * @param visitStat the visit object.
322    * @since 1.4M1
323    */
 
324  0 toggle public static void setVisitInSession(HttpSession session, VisitStats visitStat)
325    {
326  0 session.setAttribute(SESSPROP_VISITOBJECT, visitStat);
327    }
328   
329    /**
330    * @param context the XWiki context.
331    * @return true if statistics are enabled, false otherwise.
332    * @since 1.4M1
333    */
 
334  60 toggle public static boolean isStatsEnabled(XWikiContext context)
335    {
336  60 return "1".equals(context.getWiki().Param(CFGPROP_STATS, "1"));
337    }
338   
339    /**
340    * @param context the XWiki context
341    * @return true if statistics are enabled for this wiki, false otherwise.
342    * @since 1.4M1
343    */
 
344  925 toggle public static boolean isWikiStatsEnabled(XWikiContext context)
345    {
346  925 String statsdefault = context.getWiki().Param(CFGPROP_STATS_DEFAULT);
347  925 String statsactive = context.getWiki().getXWikiPreference(PREFPROP_STATISTICS, "", context);
348   
349  925 return "1".equals(statsactive) || (("".equals(statsactive)) && ("1".equals(statsdefault)));
350    }
351   
352    /**
353    * Try to find the visiting session of the current request, or create a new one if this request is not part of a
354    * visit. The session is searched in the following way:
355    * <ol>
356    * <li>the java session is searched for the visit object</li>
357    * <li>try to find the stored session using the cookie</li>
358    * <li>try to find the session by matching the IP and User Agent</li>
359    * </ol>
360    * The session is invalidated if:
361    * <ul>
362    * <li>the cookie is not the same as the stored cookie</li>
363    * <li>more than 30 minutes have elapsed from the previous request</li>
364    * <li>the user is not the same</li>
365    * </ul>
366    *
367    * @param context The context of this request.
368    * @return The visiting session, retrieved from the database or created.
369    * @since 1.4M1
370    */
 
371  0 toggle public static VisitStats findVisit(XWikiContext context)
372    {
373  0 XWikiRequest request = context.getRequest();
374  0 HttpSession session = request.getSession(true);
375   
376  0 VisitStats visitObject = StatsUtil.getVisitFromSession(session);
377   
378  0 Cookie cookie = (Cookie) context.get(CONTPROP_STATS_COOKIE);
379  0 boolean newcookie = ((Boolean) context.get(CONTPROP_STATS_NEWCOOKIE)).booleanValue();
380   
381  0 if (visitObject == null) {
382  0 visitObject = findVisitByCookieOrIPUA(context);
383    }
384   
385  0 if (visitObject == null || !isVisitObjectValid(visitObject, context)) {
386  0 visitObject = createNewVisit(context);
387    } else {
388  0 if (!newcookie) {
389    // If the cookie is not yet the unique ID we need to change that
390  0 String uniqueID = visitObject.getUniqueID();
391  0 String oldcookie = visitObject.getCookie();
392   
393  0 if (!uniqueID.equals(oldcookie)) {
394    // We need to store the oldID so that we can remove the older entry
395    // since the entry identifiers are changing
396  0 VisitStats newVisitObject = (VisitStats) visitObject.clone();
397  0 newVisitObject.rememberOldObject(visitObject);
398  0 newVisitObject.setUniqueID(cookie.getValue());
399  0 visitObject = newVisitObject;
400    }
401    }
402   
403  0 if ((!context.getUser().equals(XWikiRightService.GUEST_USER_FULLNAME))
404    && (visitObject.getUser().equals(XWikiRightService.GUEST_USER_FULLNAME))) {
405    // The user has changed from guest to an authenticated user
406    // We want to record this
407  0 VisitStats newVisitObject = visitObject;
408  0 newVisitObject.rememberOldObject(visitObject);
409  0 newVisitObject.setName(context.getUser());
410  0 visitObject = newVisitObject;
411    }
412    }
413   
414    // Keep the visit object in the session
415  0 StatsUtil.setVisitInSession(session, visitObject);
416   
417  0 return visitObject;
418    }
419   
420    /**
421    * Try to find the visit object in the database from cookie if it's not a new cookie, search by unique id otherwise.
422    *
423    * @param context the XWiki context.
424    * @return the visit statistics object found.
425    * @since 1.4M1
426    */
 
427  0 toggle private static VisitStats findVisitByCookieOrIPUA(XWikiContext context)
428    {
429  0 VisitStats visitStats = null;
430   
431  0 XWikiRequest request = context.getRequest();
432   
433  0 Cookie cookie = (Cookie) context.get(CONTPROP_STATS_COOKIE);
434  0 boolean newcookie = ((Boolean) context.get(CONTPROP_STATS_NEWCOOKIE)).booleanValue();
435   
436  0 if (!newcookie) {
437  0 try {
438  0 visitStats = findVisitByCookie(cookie.getValue(), context);
439    } catch (XWikiException e) {
440  0 LOGGER.error("Failed to find visit by cookie", e);
441    }
442    } else {
443  0 try {
444  0 String ip = request.getRemoteAddr();
445  0 String ua = request.getHeader(REQPROP_USERAGENT);
446  0 visitStats = findVisitByIPUA(computeUniqueID(ip, ua), context);
447    } catch (XWikiException e) {
448  0 LOGGER.error("Failed to find visit by unique id", e);
449    }
450    }
451   
452  0 return visitStats;
453    }
454   
455    /**
456    * Indicate of the provided visit object has to be recreated.
457    *
458    * @param visitObject the visit object to validate.
459    * @param context the XWiki context.
460    * @return false if the visit object has to be recreated, true otherwise.
461    * @since 1.4M1
462    */
 
463  0 toggle private static boolean isVisitObjectValid(VisitStats visitObject, XWikiContext context)
464    {
465  0 boolean valid = true;
466   
467  0 XWikiRequest request = context.getRequest();
468  0 HttpSession session = request.getSession(true);
469  0 Cookie cookie = (Cookie) context.get(CONTPROP_STATS_COOKIE);
470  0 Date nowDate = new Date();
471   
472  0 if (visitObject != null) {
473    // Let's verify if the session is valid
474    // If the cookie is not the same
475  0 if (!visitObject.getCookie().equals(cookie.getValue())) {
476    // Let's log a message here
477    // Since the session is also maintained using a cookie
478    // then there is something wrong here
479  0 if (LOGGER.isDebugEnabled()) {
480  0 LOGGER.debug("Found visit with cookie " + visitObject.getCookie() + " in session "
481    + session.getId() + " for request with cookie " + cookie.getValue());
482    }
483   
484  0 valid = false;
485  0 } else if ((nowDate.getTime() - visitObject.getEndDate().getTime()) > 30 * 60 * 1000) {
486    // If session is longer than 30 minutes we should invalidate it
487    // and create a new one
488  0 valid = false;
489  0 } else if (!context.getUser().equals(visitObject.getName())) {
490    // If the user is not the same, we should invalidate the session
491    // and create a new one
492  0 valid = false;
493    }
494    }
495   
496  0 return valid;
497    }
498   
499    /**
500    * Create and initialize a new visit statistics object.
501    *
502    * @param context the XWiki context.
503    * @return the new visit statistics object.
504    * @since 1.4M1
505    */
 
506  0 toggle private static VisitStats createNewVisit(XWikiContext context)
507    {
508  0 VisitStats visitStats = null;
509   
510  0 XWikiRequest request = context.getRequest();
511   
512  0 Date nowDate = new Date();
513  0 Cookie cookie = (Cookie) context.get(CONTPROP_STATS_COOKIE);
514  0 boolean newcookie = ((Boolean) context.get(CONTPROP_STATS_NEWCOOKIE)).booleanValue();
515   
516    // we need to create the session
517  0 String ip = request.getRemoteAddr();
518  0 String ua = request.getHeader(REQPROP_USERAGENT);
519  0 if (ua == null) {
520  0 ua = "";
521    }
522   
523  0 String uniqueID;
524  0 if (newcookie) {
525    // We cannot yet ID the user using the cookie
526    // we need to use the IP and UA
527  0 uniqueID = computeUniqueID(ip, ua);
528    } else {
529    // In this case we got the cookie from the request
530    // so we id the user using the cookie
531  0 uniqueID = cookie.getValue();
532    }
533   
534  0 visitStats = new VisitStats(context.getUser(), uniqueID, cookie.getValue(), ip, ua, nowDate, PeriodType.MONTH);
535  0 visitStats.setEndDate(nowDate);
536   
537  0 return visitStats;
538    }
539   
540    /**
541    * Compute the unique id for stat visits.
542    * <p>
543    * TODO: In the future, replace this with a unique random number since this algorithm is not good enough; for
544    * example users in the same company behind a company firewall may get the same IP and same user agent...
545    *
546    * @param ip the IP address of the user
547    * @param ua the user agent of the user
548    * @return the unique ID, limited to 255 characters since that's the max size of the field in the DB
549    */
 
550  0 toggle private static String computeUniqueID(String ip, String ua)
551    {
552  0 return StringUtils.substring(ip + ua, 0, 255);
553    }
554   
555    /**
556    * Search visit statistics object in the database based on cookie name.
557    *
558    * @param fieldName the field name.
559    * @param fieldValue the field value.
560    * @param context the XWiki context.
561    * @return the visit object, null if no object was found.
562    * @throws XWikiException error when searching for visit object.
563    * @since 1.4M1
564    */
 
565  0 toggle protected static VisitStats findVisitByField(String fieldName, String fieldValue, XWikiContext context)
566    throws XWikiException
567    {
568  0 VisitStats visitStats = null;
569   
570  0 Date currentDate = new Date(new Date().getTime() - 30 * 60 * 1000);
571   
572  0 QueryManager qm = context.getWiki().getStore().getQueryManager();
573  0 List<VisitStats> solist = null;
574  0 final String sfieldValue = "fieldValue";
575  0 final String sdate = "date";
576  0 if (qm.hasLanguage(Query.XPATH)) {
577  0 try {
578  0 solist =
579    qm.createQuery(
580    "//element(*, xwiki:object)[@:{fieldName}=:{fieldValue}"
581    + " and @endDate>:{date}] order by @endDate descending", Query.XPATH)
582    .bindValue("fieldName", fieldName).bindValue(sfieldValue, fieldValue)
583    .bindValue(sdate, currentDate).execute();
584    } catch (Exception e) {
585  0 LOGGER.error("Failed to search visit object in the jcr store from cookie name", e);
586    }
587  0 } else if (qm.hasLanguage(Query.HQL)) {
588  0 try {
589  0 solist =
590    qm.createQuery(
591    "from VisitStats as obj " + "where obj." + fieldName + "=:fieldValue and obj.endDate > :date"
592    + " order by obj.endDate desc", Query.HQL).bindValue(sfieldValue, fieldValue)
593    .bindValue(sdate, currentDate).execute();
594    } catch (Exception e) {
595  0 LOGGER.error("Failed to search visit object in the database from " + fieldName, e);
596    }
597    } else {
598  0 throw new UnsupportedOperationException("The current storage engine does not support querying statistics");
599    }
600  0 if (solist != null && solist.size() > 0) {
601  0 visitStats = solist.get(0);
602    }
603   
604  0 return visitStats;
605    }
606   
607    /**
608    * Search visit statistics object in the database based on cookie name.
609    *
610    * @param cookie the cookie name.
611    * @param context the XWiki context.
612    * @return the visit object, null if no object was found.
613    * @throws XWikiException error when searching for visit object.
614    * @since 1.4M1
615    */
 
616  0 toggle protected static VisitStats findVisitByCookie(String cookie, XWikiContext context) throws XWikiException
617    {
618  0 return findVisitByField("cookie", cookie, context);
619    }
620   
621    /**
622    * Search visit statistics object in the database based on visit unique id.
623    *
624    * @param uniqueID the visit unique id.
625    * @param context the XWiki context.
626    * @return the visit object.
627    * @throws XWikiException error when searching for visit object.
628    * @since 1.4M1
629    */
 
630  0 toggle protected static VisitStats findVisitByIPUA(String uniqueID, XWikiContext context) throws XWikiException
631    {
632  0 return findVisitByField("uniqueID", uniqueID, context);
633    }
634   
635    /**
636    * Create a new visit cookie and return it.
637    *
638    * @param context the XWiki context.
639    * @return the newly created cookie.
640    * @since 1.4M1
641    */
 
642  924 toggle protected static Cookie addCookie(XWikiContext context)
643    {
644  925 Cookie cookie = new Cookie(COOKPROP_VISITID, RandomStringUtils.randomAlphanumeric(32).toUpperCase());
645  924 cookie.setPath("/");
646   
647  921 int time = (int) (getCookieExpirationDate().getTime() - (new Date()).getTime()) / 1000;
648  922 cookie.setMaxAge(time);
649   
650  920 String cookieDomain = null;
651  921 getCookieDomains(context);
652  923 if (cookieDomains != null) {
653  921 String servername = context.getRequest().getServerName();
654  922 for (String cookieDomain2 : cookieDomains) {
655  0 if (servername.indexOf(cookieDomain2) != -1) {
656  0 cookieDomain = cookieDomain2;
657  0 break;
658    }
659    }
660    }
661   
662  921 if (cookieDomain != null) {
663  0 cookie.setDomain(cookieDomain);
664    }
665   
666  921 if (LOGGER.isDebugEnabled()) {
667  0 LOGGER.debug("Setting cookie " + cookie.getValue() + " for name " + cookie.getName() + " with domain "
668    + cookie.getDomain() + " and path " + cookie.getPath() + " and maxage " + cookie.getMaxAge());
669    }
670   
671  923 context.getResponse().addCookie(cookie);
672   
673  925 return cookie;
674    }
675   
676    /**
677    * Try to find the cookie of the current request or create it.
678    *
679    * @param context The context of this request.
680    * @return true if the cookie is created.
681    * @since 1.4M1
682    */
 
683  925 toggle public static boolean findCookie(XWikiContext context)
684    {
685  923 if (context.get(CONTPROP_STATS_COOKIE) != null) {
686  0 return false;
687    }
688   
689  922 Cookie cookie = Util.getCookie(COOKPROP_VISITID, context);
690  924 boolean newcookie = false;
691   
692    // If the cookie does not exist we need to set it
693  922 if (cookie == null) {
694  922 cookie = addCookie(context);
695  924 newcookie = true;
696    }
697   
698  924 context.put(CONTPROP_STATS_COOKIE, cookie);
699  925 context.put(CONTPROP_STATS_NEWCOOKIE, Boolean.valueOf(newcookie));
700   
701  925 return true;
702    }
703   
704    /**
705    * @param context the XWiki context.
706    * @return the referer.
707    * @since 1.4M1
708    */
 
709  0 toggle public static String getReferer(XWikiContext context)
710    {
711  0 String referer = context.getRequest().getHeader(REQPROP_REFERER);
712   
713  0 try {
714  0 URL url = new URL(referer);
715  0 URL baseurl = context.getURL();
716  0 if (baseurl.getHost().equals(url.getHost())) {
717  0 referer = null;
718    }
719    } catch (MalformedURLException e) {
720  0 referer = null;
721    }
722   
723  0 return referer;
724    }
725   
726    /**
727    * The list of users.
728    *
729    * @param pref field name in XWikiPreference
730    * @param cfg field name in xwiki.cfg file
731    * @param context the XWiki context
732    * @return the list of users references
733    * @throws XWikiException error when trying to resolve users
734    */
 
735  7 toggle private static Collection<DocumentReference> getFilteredUsers(String pref, String cfg, XWikiContext context)
736    throws XWikiException
737    {
738  7 List<String> userList;
739   
740  7 String users = context.getWiki().getXWikiPreference(pref, "", context);
741   
742  7 if (StringUtils.isEmpty(users)) {
743  7 users = context.getWiki().Param(cfg);
744    }
745   
746  7 if (!StringUtils.isBlank(users)) {
747  6 userList = new ArrayList<String>();
748   
749  6 int begin = 0;
750  6 boolean escaped = false;
751  121 for (int i = 0; i < users.length(); ++i) {
752  115 char c = users.charAt(i);
753   
754  115 if (!escaped) {
755  115 if (c == '\\') {
756  0 escaped = true;
757  115 } else if (c == ',') {
758  3 userList.add(users.substring(begin, i).replace(ESCAPED_LIST_SEPARATOR, LIST_SEPARATOR));
759  3 begin = i + 1;
760    }
761    } else {
762  0 escaped = false;
763    }
764    }
765   
766  6 if (begin < users.length()) {
767  6 userList.add(users.substring(begin).replace(ESCAPED_LIST_SEPARATOR, LIST_SEPARATOR));
768    }
769    } else {
770  1 userList = Collections.emptyList();
771    }
772   
773  7 return RightsManager.getInstance().resolveUsers(userList, context);
774    }
775   
776    /**
777    * The list of users to filter when storing statistics.
778    *
779    * @param context the XWiki context
780    * @return the list of users references
781    * @throws XWikiException error when trying to resolve users
782    */
 
783  0 toggle public static Collection<DocumentReference> getStorageFilteredUsers(XWikiContext context) throws XWikiException
784    {
785    // TODO: cache
786  0 return getFilteredUsers(PREFPROP_EXCLUDEDUSERSANDGROUPS_STORAGE, CFGPROP_STATS_EXCLUDEDUSERSANDGROUPS_STORAGE,
787    context);
788    }
789   
790    /**
791    * The list of users to filter in view request.
792    *
793    * @param context the XWiki context
794    * @return the list of users references
795    * @throws XWikiException error when trying to resolve users
796    */
 
797  7 toggle public static Collection<DocumentReference> getRequestFilteredUsers(XWikiContext context) throws XWikiException
798    {
799    // TODO: cache
800  7 Collection<DocumentReference> users =
801    getFilteredUsers(PREFPROP_EXCLUDEDUSERSANDGROUPS_REQUEST, CFGPROP_STATS_EXCLUDEDUSERSANDGROUPS_REQUEST,
802    context);
803   
804  7 return users != null ? users : getFilteredUsers(DEPRECATED_PREFPROP_EXCLUDEDUSERSANDGROUPS,
805    DEPRECATED_CFGPROP_STATS_EXCLUDEDUSERSANDGROUPS, context);
806    }
807    }