Skip to content

Commit abddfb1

Browse files
authored
Add a last seen widget #102 (#114)
1 parent 51cd2b4 commit abddfb1

36 files changed

+3341
-14
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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+
21+
package com.xwiki.analytics;
22+
23+
import java.util.List;
24+
25+
import org.xwiki.component.annotation.Role;
26+
import org.xwiki.stability.Unstable;
27+
28+
/**
29+
* Provides APIs for aggregating data from multiple Matomo endpoints to make custom tables.
30+
*
31+
* @version $Id$
32+
* @since 1.1
33+
*/
34+
@Role
35+
@Unstable
36+
public interface Aggregator
37+
{
38+
/**
39+
* Aggregates the data from multiple endpoints.
40+
*/
41+
void aggregateData();
42+
43+
/**
44+
* Will get the hint of the aggregator.
45+
*
46+
* @return hint of the aggregator
47+
*/
48+
String getHint();
49+
50+
/**
51+
* Returns the name of the page where the data is saved.
52+
*
53+
* @return name of the page where the data is saved.
54+
*/
55+
String getPage();
56+
57+
/**
58+
* Returns the space where the page storing the aggregated data is located.
59+
*
60+
* @return a list of space names as strings.
61+
*/
62+
List<String> getSpace();
63+
}

application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,16 @@
2020
package com.xwiki.analytics;
2121

2222
import java.io.IOException;
23+
import java.util.List;
2324
import java.util.Map;
2425

26+
import org.apache.commons.lang3.tuple.Pair;
2527
import org.xwiki.component.annotation.Role;
2628
import org.xwiki.stability.Unstable;
2729

30+
import com.fasterxml.jackson.core.JsonProcessingException;
2831
import com.fasterxml.jackson.databind.JsonNode;
32+
import com.xpn.xwiki.XWikiException;
2933

3034
/**
3135
* The interface for the AnalyticManger.
@@ -47,4 +51,27 @@ public interface AnalyticsManager
4751
*/
4852
JsonNode requestData(Map<String, String> parameters, Map<String, String> filters, String jsonNormaliserHint)
4953
throws IOException;
54+
55+
/**
56+
* Aggregate data from multiple endpoints using a specific aggregator.
57+
*
58+
* @param hint which aggregator you want to use.
59+
*/
60+
void aggregate(String hint);
61+
62+
/**
63+
* Handles data aggregated from the analytics applications and stored directly in XWiki.
64+
*
65+
* @param hint for the aggregator used
66+
* @param asc if you want the data to be in ascending or descending order
67+
* @param sortField the field that you want to sort after
68+
* @param filters map where the keys are the fields and the values are the text that you want to filter after
69+
* @param pageSize how many elements are on a page
70+
* @param pageCount current page offset
71+
* @return a pair where the first element is the total number of entries that meet the criteria(filter), and the
72+
* second element is a slice of the original list paginated using pageSize and pageCount
73+
*/
74+
Pair<Integer, List<JsonNode>> handleData(String hint, String asc, String sortField,
75+
Map<String, String> filters, int pageSize, int pageCount) throws JsonProcessingException, XWikiException;
76+
5077
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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.xwiki.analytics.configuration;
21+
22+
import org.xwiki.component.annotation.Role;
23+
import org.xwiki.stability.Unstable;
24+
25+
/**
26+
* Provides a common interface for all the aggregator configurations.
27+
*
28+
* @version $Id$
29+
* @since 1.0
30+
*/
31+
@Role
32+
@Unstable
33+
public interface AggregatorConfiguration
34+
{
35+
/**
36+
* Time interval for the aggregated statistics: e.g. current year,month, year or custom.
37+
* @return time interval to compute aggregated statistics.
38+
*/
39+
String getTimeIntervalForStatistics();
40+
41+
/**
42+
* Start date of the time interval in case the user selected custom.
43+
* @return stat date of the interval
44+
*/
45+
String getStartDate();
46+
47+
/**
48+
* End date of the time interval in case the user selected custom.
49+
* @return stat date of the interval
50+
*/
51+
String getEndDate();
52+
}
53+

application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
* Configuration for the Analytics Application.
2727
*
2828
* @version $Id$
29-
* @since 1.0
29+
* @since 1.2
3030
*/
3131
@Role
3232
@Unstable

application-analytics-default/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@
4949
<!-- Testing dependencies -->
5050
<!-- UriBuilder in JAX-RS is abstract implementation and requiring an implementation like Jersey for unit testing.
5151
-->
52+
<dependency>
53+
<groupId>org.xwiki.platform</groupId>
54+
<artifactId>xwiki-platform-rest-server</artifactId>
55+
<version>${platform.version}</version>
56+
</dependency>
5257
<dependency>
5358
<groupId>org.xwiki.commons</groupId>
5459
<artifactId>xwiki-commons-tool-test-component</artifactId>
@@ -61,6 +66,11 @@
6166
<artifactId>jersey-server</artifactId>
6267
<version>2.30.1</version> <!-- You can update to the latest version available -->
6368
</dependency>
69+
<dependency>
70+
<groupId>org.xwiki.platform</groupId>
71+
<artifactId>xwiki-platform-scheduler-api</artifactId>
72+
<version>${platform.version}</version>
73+
</dependency>
6474
<dependency>
6575
<groupId>org.glassfish.jersey.core</groupId>
6676
<artifactId>jersey-client</artifactId>
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
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.xwiki.analytics.internal;
21+
22+
import java.util.ArrayList;
23+
import java.util.Arrays;
24+
import java.util.List;
25+
26+
import javax.inject.Inject;
27+
import javax.inject.Named;
28+
import javax.inject.Provider;
29+
import javax.inject.Singleton;
30+
31+
import org.quartz.SchedulerException;
32+
import org.quartz.Trigger;
33+
import org.slf4j.Logger;
34+
import org.xwiki.component.annotation.Component;
35+
import org.xwiki.component.phase.Initializable;
36+
import org.xwiki.component.phase.InitializationException;
37+
import org.xwiki.extension.event.ExtensionEvent;
38+
import org.xwiki.extension.event.ExtensionInstalledEvent;
39+
import org.xwiki.extension.event.ExtensionUpgradedEvent;
40+
import org.xwiki.model.reference.LocalDocumentReference;
41+
import org.xwiki.observation.AbstractEventListener;
42+
import org.xwiki.observation.event.Event;
43+
44+
import com.xpn.xwiki.XWikiContext;
45+
import com.xpn.xwiki.XWikiException;
46+
import com.xpn.xwiki.doc.XWikiDocument;
47+
import com.xpn.xwiki.objects.BaseObject;
48+
import com.xpn.xwiki.plugin.scheduler.JobState;
49+
import com.xpn.xwiki.plugin.scheduler.SchedulerPlugin;
50+
51+
/**
52+
* Schedules the jobs for the aggregators macros on install and reschedules them on upgrade to make sure that everything
53+
* works properly.
54+
*
55+
* @version $Id$
56+
* @since 1.2
57+
*/
58+
@Component
59+
@Named(AnalyticsApplicationListener.ROLE_HINT)
60+
@Singleton
61+
public class AnalyticsApplicationListener extends AbstractEventListener implements Initializable
62+
{
63+
protected static final String ROLE_HINT = "AnalyticsApplicationListener";
64+
65+
protected static final List<String> CODE_SPACE = Arrays.asList("Analytics", "Code", "Jobs");
66+
67+
protected static final List<String> JOB_PAGES = List.of("JobLastSeenUser");
68+
69+
private static final String ANALYTICS_APPLICATION_ID = "com.xwiki.analytics:application-analytics-ui";
70+
71+
@Inject
72+
private Logger logger;
73+
74+
@Inject
75+
private Provider<XWikiContext> contextProvider;
76+
77+
/**
78+
* Default constructor.
79+
*/
80+
public AnalyticsApplicationListener()
81+
{
82+
//TODO: Filter by ExtensionInstallEvent with the help of the extension id when XCOMMONS-2526 is added to
83+
// the platform and the analytics application depends on that parent.
84+
85+
super(ROLE_HINT,
86+
Arrays.<Event>asList(new ExtensionUpgradedEvent(ANALYTICS_APPLICATION_ID), new ExtensionInstalledEvent()));
87+
}
88+
89+
/**
90+
* The migration should be done at ExtensionUpgradedEvent, but for avoiding XCOMMONS-751: Getting wrong component
91+
* instance during JAR extension upgrade, it is done also at initialization step, since when an extension is
92+
* upgraded its listeners are initialized too. After the issue is fixed and the application starts depending on a
93+
* version of XWiki >= the version where is fixed, then only the migration from inside the event should be executed.
94+
*/
95+
@Override
96+
public void initialize() throws InitializationException
97+
{
98+
99+
// Don't schedule jobs at xwiki start up time.
100+
if (this.contextProvider.get() != null) {
101+
prepareJobs(true);
102+
}
103+
}
104+
105+
@Override
106+
public void onEvent(Event event, Object source, Object data)
107+
{
108+
109+
if (event instanceof ExtensionUpgradedEvent) {
110+
// Unscheduled and reschedule the jobs
111+
prepareJobs(true);
112+
} else if (isAnalyticsInstallEvent(event)) {
113+
prepareJobs(false);
114+
}
115+
}
116+
117+
/**
118+
* Will gather all the jobs pages that are bundled in the analytics app.
119+
* @return list with document references of all the jobs that are brought by the analytics app.
120+
*/
121+
private List<LocalDocumentReference> getJobPages()
122+
{
123+
List<LocalDocumentReference> jobPages = new ArrayList<>();
124+
for (String jobPage : JOB_PAGES) {
125+
jobPages.add(new LocalDocumentReference(CODE_SPACE, jobPage));
126+
}
127+
return jobPages;
128+
}
129+
130+
private void prepareJobs(boolean reschedule)
131+
{
132+
List<LocalDocumentReference> jobPages = getJobPages();
133+
try {
134+
135+
for (LocalDocumentReference jobPage : jobPages) {
136+
scheduleJob(reschedule, jobPage);
137+
}
138+
} catch (SchedulerException | XWikiException e) {
139+
logger.error("Failed to schedule jobs", e);
140+
}
141+
}
142+
143+
private static boolean isAnalyticsInstallEvent(Event event)
144+
{
145+
return event instanceof ExtensionInstalledEvent && ANALYTICS_APPLICATION_ID.equals(
146+
((ExtensionEvent) event).getExtensionId().getId());
147+
}
148+
149+
private void scheduleJob(boolean doReschedule, LocalDocumentReference jobDocReference)
150+
throws XWikiException, SchedulerException
151+
{
152+
XWikiContext xcontext = contextProvider.get();
153+
154+
SchedulerPlugin schedulerPlugin =
155+
(SchedulerPlugin) xcontext.getWiki().getPluginManager().getPlugin("scheduler");
156+
XWikiDocument jobDoc = xcontext.getWiki().getDocument(jobDocReference, xcontext);
157+
BaseObject job = jobDoc.getXObject(SchedulerPlugin.XWIKI_JOB_CLASSREFERENCE);
158+
JobState jobState = schedulerPlugin.getJobStatus(job, xcontext);
159+
160+
if (doReschedule && jobState.getQuartzState().equals(Trigger.TriggerState.NORMAL)) {
161+
schedulerPlugin.unscheduleJob(job, xcontext);
162+
schedulerPlugin.scheduleJob(job, xcontext);
163+
logger.info("Job, [{}], was rescheduled successfully", jobDocReference);
164+
} else if (jobState.getQuartzState().equals(Trigger.TriggerState.NONE)) {
165+
schedulerPlugin.scheduleJob(job, xcontext);
166+
logger.info("Job, [{}], was scheduled successfully", jobDocReference);
167+
}
168+
}
169+
}

0 commit comments

Comments
 (0)