From 17df15d385ae9a9bc5901e14f09657c6e444e124 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Wed, 2 Aug 2023 22:44:27 +0300 Subject: [PATCH 001/105] Move Most Viewed Pages Macro to livetable #10 * I added the pom.xmls --- application-analytics-api/pom.xml | 0 application-analytics-default/pom.xml | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 application-analytics-api/pom.xml create mode 100644 application-analytics-default/pom.xml diff --git a/application-analytics-api/pom.xml b/application-analytics-api/pom.xml new file mode 100644 index 00000000..e69de29b diff --git a/application-analytics-default/pom.xml b/application-analytics-default/pom.xml new file mode 100644 index 00000000..e69de29b From bddfed8ed341dd48e9b48ba503f07578a2e80e6e Mon Sep 17 00:00:00 2001 From: Farcasut Date: Wed, 2 Aug 2023 22:45:48 +0300 Subject: [PATCH 002/105] Move Most Viewed Pages Macro to livetable #10 * I added the pom.xmls --- application-analytics-api/pom.xml | 40 +++++++++++++++++++++++++ application-analytics-default/pom.xml | 42 +++++++++++++++++++++++++++ application-analytics-ui/pom.xml | 5 ++++ pom.xml | 2 ++ 4 files changed, 89 insertions(+) diff --git a/application-analytics-api/pom.xml b/application-analytics-api/pom.xml index e69de29b..611c7c5a 100644 --- a/application-analytics-api/pom.xml +++ b/application-analytics-api/pom.xml @@ -0,0 +1,40 @@ + + + + + 4.0.0 + + com.xwiki.analytics + application-analytics + 1.0-SNAPSHOT + + application-analytics-api + jar + Analytics Connector Application (Pro) - API + Discover unique insights to help you better understand the usage of your instance. Streamline and improve internal processes by having full visibility on the most viewed pages. Understand how you can help your teams boost productivity by using real-time statistics. + + + org.xwiki.platform + xwiki-platform-rest-server + ${platform.version} + + + \ No newline at end of file diff --git a/application-analytics-default/pom.xml b/application-analytics-default/pom.xml index e69de29b..f526e27d 100644 --- a/application-analytics-default/pom.xml +++ b/application-analytics-default/pom.xml @@ -0,0 +1,42 @@ + + + + + 4.0.0 + + com.xwiki.analytics + application-analytics + 1.0-SNAPSHOT + + application-analytics-default + jar + Analytics Application (Pro) - DEFAULT + Discover unique insights to help you better understand the usage of your instance. Streamline and improve internal processes by having full visibility on the most viewed pages. Understand how you can help your teams boost productivity by using real-time statistics. + + + + + com.xwiki.analytics + application-analytics-api + ${project.version} + + + \ No newline at end of file diff --git a/application-analytics-ui/pom.xml b/application-analytics-ui/pom.xml index 087d08b2..7d404764 100644 --- a/application-analytics-ui/pom.xml +++ b/application-analytics-ui/pom.xml @@ -55,6 +55,11 @@ application-licensing-licensor-api ${licensing.version} + + com.xwiki.analytics + application-analytics-default + ${project.version} + diff --git a/pom.xml b/pom.xml index 5a1e5bd8..0dd2fb24 100644 --- a/pom.xml +++ b/pom.xml @@ -48,5 +48,7 @@ application-analytics-ui + application-analytics-api + application-analytics-default \ No newline at end of file From 2225294ba832dbde902a5172ff9165f60cbe23ab Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 3 Aug 2023 11:54:13 +0300 Subject: [PATCH 003/105] Added the structure for the config --- application-analytics-api/pom.xml | 5 +++ .../configuration/AnalyticsConfiguration.java | 30 +++++++++++++++++ .../DefaultAnalyticsConfiguration.java | 33 +++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java create mode 100644 application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java diff --git a/application-analytics-api/pom.xml b/application-analytics-api/pom.xml index 611c7c5a..6bb2394c 100644 --- a/application-analytics-api/pom.xml +++ b/application-analytics-api/pom.xml @@ -35,6 +35,11 @@ org.xwiki.platform xwiki-platform-rest-server ${platform.version} + + + org.xwiki.commons + xwiki-commons-component-api + ${commons.version} \ No newline at end of file diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java b/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java new file mode 100644 index 00000000..31f9c8a9 --- /dev/null +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java @@ -0,0 +1,30 @@ +package com.xwiki.analytics.configuration; + +import org.xwiki.component.annotation.Role; +import org.xwiki.stability.Unstable; + +@Role +@Unstable +/** + * Analytics configuration options. + */ +public interface AnalyticsConfiguration { + + /** + * + * @return Returns the address where the Matomo requests will be made. + */ + String getRequestAddress(); + + /** + * + * @return Returns the id of the site that we want to see the statistics for. + */ + String getIdSite(); + + /** + * + * @return Returns the Authentication Token that permits to access the statistics. + */ + String getAuthenticationToken(); +} diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java new file mode 100644 index 00000000..ceef6c86 --- /dev/null +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java @@ -0,0 +1,33 @@ +package com.xwiki.analytics.internal.configuration; + +import com.xwiki.analytics.configuration.AnalyticsConfiguration; +import groovy.lang.Singleton; +import org.xwiki.component.annotation.Component; +import org.xwiki.configuration.ConfigurationSource; + +import javax.inject.Inject; +import javax.inject.Named; + +/** + * Default implementation of {@AnalyticsConfiguration} + */ +@Component +@Singleton +public class DefaultAnalyticsConfiguration implements AnalyticsConfiguration { + @Inject + private ConfigurationSource configDocument; + @Override + public String getRequestAddress() { + return null; + } + + @Override + public String getIdSite() { + return null; + } + + @Override + public String getAuthenticationToken() { + return null; + } +} From fd80f1c1a80da36027765a7785c209918419e443 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 3 Aug 2023 16:15:48 +0300 Subject: [PATCH 004/105] Implmentation --- application-analytics-api/pom.xml | 2 +- .../analytics/analytics/AnalyticsManager.java | 13 ++++++ application-analytics-default/pom.xml | 5 +++ .../AnalyticsConfigurationSource.java | 41 +++++++++++++++++++ .../DefaultAnalyticsConfiguration.java | 12 ++++-- .../script/AnalyticsScriptService.java | 19 +++++++++ 6 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 application-analytics-api/src/main/java/com/xwiki/analytics/analytics/AnalyticsManager.java create mode 100644 application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/AnalyticsConfigurationSource.java create mode 100644 application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java diff --git a/application-analytics-api/pom.xml b/application-analytics-api/pom.xml index 6bb2394c..400aaecb 100644 --- a/application-analytics-api/pom.xml +++ b/application-analytics-api/pom.xml @@ -33,7 +33,7 @@ org.xwiki.platform - xwiki-platform-rest-server + xwiki-platform-configuration-default ${platform.version} diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/analytics/AnalyticsManager.java b/application-analytics-api/src/main/java/com/xwiki/analytics/analytics/AnalyticsManager.java new file mode 100644 index 00000000..f3d0a980 --- /dev/null +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/analytics/AnalyticsManager.java @@ -0,0 +1,13 @@ +package com.xwiki.analytics.analytics; + +import org.xwiki.component.annotation.Role; +import org.xwiki.stability.Unstable; + +/** + * @version $Id$ + */ +@Role +@Unstable +public class AnalyticsManager { + +} diff --git a/application-analytics-default/pom.xml b/application-analytics-default/pom.xml index f526e27d..0322f99a 100644 --- a/application-analytics-default/pom.xml +++ b/application-analytics-default/pom.xml @@ -38,5 +38,10 @@ application-analytics-api ${project.version} + + org.xwiki.platform + xwiki-platform-configuration-default + ${platform.version} + \ No newline at end of file diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/AnalyticsConfigurationSource.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/AnalyticsConfigurationSource.java new file mode 100644 index 00000000..75223b8e --- /dev/null +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/AnalyticsConfigurationSource.java @@ -0,0 +1,41 @@ +package com.xwiki.analytics.internal.configuration; +import groovy.lang.Singleton; +import jdk.vm.ci.meta.Local; +import org.xwiki.component.annotation.Component; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.configuration.internal.AbstractDocumentConfigurationSource; +import org.xwiki.model.reference.LocalDocumentReference; +import org.xwiki.model.reference.SpaceReference; + +import javax.inject.Named; +import java.util.Arrays; +import java.util.List; + +@Component +@Singleton +@Named("analytics") +public class AnalyticsConfigurationSource extends AbstractDocumentConfigurationSource { + private static final List SPACE_NAMES= Arrays.asList("Analytics", "Code"); + private static final LocalDocumentReference DOCUMENT_REFERENCE= + new LocalDocumentReference(SPACE_NAMES, "Configuration"); + private static final LocalDocumentReference CLASS_REFERENCE= + new LocalDocumentReference(SPACE_NAMES, "ConfigurationClass"); + + + @Override + protected DocumentReference getDocumentReference() + { + return new DocumentReference(DOCUMENT_REFERENCE, this.getCurrentWikiReference()); + } + + @Override + protected LocalDocumentReference getClassReference() + { + return CLASS_REFERENCE; + } + + @Override + protected String getCacheId() { + return "configuration.document.analytics"; + } +} diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java index ceef6c86..f55e278d 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java @@ -15,19 +15,25 @@ @Singleton public class DefaultAnalyticsConfiguration implements AnalyticsConfiguration { @Inject + @Named("analytics") private ConfigurationSource configDocument; @Override public String getRequestAddress() { - return null; + return getProperty("requestAddress", ""); } @Override public String getIdSite() { - return null; + return getProperty("siteId",""); } @Override public String getAuthenticationToken() { - return null; + return getProperty("authToken", ""); + } + + private T getProperty(String key, T defaultValue) + { + return this.configDocument.getProperty(key, defaultValue); } } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java new file mode 100644 index 00000000..42f54334 --- /dev/null +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java @@ -0,0 +1,19 @@ +package com.xwiki.analytics.script; + +import com.xwiki.analytics.configuration.AnalyticsConfiguration; +import org.xwiki.component.annotation.Component; +import org.xwiki.script.service.ScriptService; +import org.xwiki.stability.Unstable; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +@Component +@Named("analytics") +@Unstable +@Singleton +public class AnalyticsScriptService implements ScriptService { + @Inject + private AnalyticsConfiguration configuration; +} From 14d7b5edae6b8d0f0a428393dc95d0b6c88404e8 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 3 Aug 2023 23:41:53 +0300 Subject: [PATCH 005/105] Move Most Viewed Pages Macro to livetable #10 *I added the classes need to interact with the configs from Analytics.Code.COnfiguration and I also added a class that will normalise the jsons from matomo --- application-analytics-api/pom.xml | 2 +- .../com/xwiki/analytics/AnalyticsManager.java | 35 ++++++++ .../com/xwiki/analytics/JsonNormaliser.java | 36 ++++++++ .../analytics/analytics/AnalyticsManager.java | 13 --- .../configuration/AnalyticsConfiguration.java | 28 +++++- application-analytics-default/pom.xml | 2 +- .../xwiki/analytics/MatomoJsonNormaliser.java | 90 +++++++++++++++++++ .../AnalyticsConfigurationSource.java | 38 ++++++-- .../DefaultAnalyticsConfiguration.java | 40 +++++++-- .../script/AnalyticsScriptService.java | 37 +++++++- .../main/resources/META-INF/components.txt | 3 + application-analytics-ui/pom.xml | 2 +- pom.xml | 5 +- 13 files changed, 292 insertions(+), 39 deletions(-) create mode 100644 application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java create mode 100644 application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java delete mode 100644 application-analytics-api/src/main/java/com/xwiki/analytics/analytics/AnalyticsManager.java create mode 100644 application-analytics-default/src/main/java/com/xwiki/analytics/MatomoJsonNormaliser.java create mode 100644 application-analytics-default/src/main/resources/META-INF/components.txt diff --git a/application-analytics-api/pom.xml b/application-analytics-api/pom.xml index 400aaecb..5fac6aa2 100644 --- a/application-analytics-api/pom.xml +++ b/application-analytics-api/pom.xml @@ -24,7 +24,7 @@ com.xwiki.analytics application-analytics - 1.0-SNAPSHOT + 1.11-SNAPSHOT application-analytics-api jar diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java b/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java new file mode 100644 index 00000000..6ced8f07 --- /dev/null +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java @@ -0,0 +1,35 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.analytics; + + +import org.xwiki.component.annotation.Role; +import org.xwiki.stability.Unstable; + +/** + * @version $Id$ + * @since 1.0 + */ +@Role +@Unstable +public class AnalyticsManager +{ + +} diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java new file mode 100644 index 00000000..49b190fe --- /dev/null +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java @@ -0,0 +1,36 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.analytics; + +import com.fasterxml.jackson.core.JsonProcessingException; + +/** + * @version $Id$ + */ +public interface JsonNormaliser +{ + /** + * + * @param jsonString + * @return Returns the json in string format + * @throws JsonProcessingException + */ + String normaliseData(String jsonString) throws JsonProcessingException; +} diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/analytics/AnalyticsManager.java b/application-analytics-api/src/main/java/com/xwiki/analytics/analytics/AnalyticsManager.java deleted file mode 100644 index f3d0a980..00000000 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/analytics/AnalyticsManager.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.xwiki.analytics.analytics; - -import org.xwiki.component.annotation.Role; -import org.xwiki.stability.Unstable; - -/** - * @version $Id$ - */ -@Role -@Unstable -public class AnalyticsManager { - -} diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java b/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java index 31f9c8a9..f3c012e7 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java @@ -1,14 +1,36 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ package com.xwiki.analytics.configuration; import org.xwiki.component.annotation.Role; import org.xwiki.stability.Unstable; -@Role -@Unstable /** + * @version $Id$ + * @since 1.0 * Analytics configuration options. */ -public interface AnalyticsConfiguration { +@Role +@Unstable +public interface AnalyticsConfiguration +{ /** * diff --git a/application-analytics-default/pom.xml b/application-analytics-default/pom.xml index 0322f99a..d3f980b7 100644 --- a/application-analytics-default/pom.xml +++ b/application-analytics-default/pom.xml @@ -24,7 +24,7 @@ com.xwiki.analytics application-analytics - 1.0-SNAPSHOT + 1.11-SNAPSHOT application-analytics-default jar diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoJsonNormaliser.java new file mode 100644 index 00000000..3734100b --- /dev/null +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoJsonNormaliser.java @@ -0,0 +1,90 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.analytics; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import java.util.Iterator; + +/** + * @version $Id$ + */ +public class MatomoJsonNormaliser implements JsonNormaliser +{ + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private static final String DATE = "date"; + + /** + * + * @param jsonString The json provided by Matomo. + * @return The normalised json as a string. + * @throws JsonProcessingException + */ + public String normaliseData(String jsonString) throws JsonProcessingException + { + JsonNode jsonNode = OBJECT_MAPPER.readTree(jsonString); + if (jsonNode.isArray()) { + processArrayNode(jsonNode); + } else { + jsonNode = processObjectNode(jsonNode); + } + return jsonNode.toString(); + } + + /** + * This function will add the date field to all rows when the user uses period="range" and the data is merged. + * @param jsonNode + */ + + private void processArrayNode(JsonNode jsonNode) + { + for (JsonNode objNode : jsonNode) { + if (objNode.isObject()) { + ((ObjectNode) objNode).put(DATE, ""); + } + } + } + + /** + * This function will process the json from Matomo and return a new one with the date field added. + * @param jsonNode + * @return + */ + private ArrayNode processObjectNode(JsonNode jsonNode) + { + ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); + Iterator fieldNames = jsonNode.fieldNames(); + while (fieldNames.hasNext()) { + String date = fieldNames.next(); + JsonNode childNode = jsonNode.get(date); + for (JsonNode objNode : childNode) { + if (objNode.isObject()) { + ((ObjectNode) objNode).put(DATE, date); + arrayNode.add(objNode); + } + } + } + return arrayNode; + } +} diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/AnalyticsConfigurationSource.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/AnalyticsConfigurationSource.java index 75223b8e..b13f3912 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/AnalyticsConfigurationSource.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/AnalyticsConfigurationSource.java @@ -1,24 +1,45 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ package com.xwiki.analytics.internal.configuration; -import groovy.lang.Singleton; -import jdk.vm.ci.meta.Local; +import javax.inject.Singleton; import org.xwiki.component.annotation.Component; import org.xwiki.model.reference.DocumentReference; import org.xwiki.configuration.internal.AbstractDocumentConfigurationSource; import org.xwiki.model.reference.LocalDocumentReference; -import org.xwiki.model.reference.SpaceReference; import javax.inject.Named; import java.util.Arrays; import java.util.List; +/** + * @version $Id$ + */ @Component @Singleton @Named("analytics") -public class AnalyticsConfigurationSource extends AbstractDocumentConfigurationSource { - private static final List SPACE_NAMES= Arrays.asList("Analytics", "Code"); - private static final LocalDocumentReference DOCUMENT_REFERENCE= +public class AnalyticsConfigurationSource extends AbstractDocumentConfigurationSource +{ + private static final List SPACE_NAMES = Arrays.asList("Analytics", "Code"); + private static final LocalDocumentReference DOCUMENT_REFERENCE = new LocalDocumentReference(SPACE_NAMES, "Configuration"); - private static final LocalDocumentReference CLASS_REFERENCE= + private static final LocalDocumentReference CLASS_REFERENCE = new LocalDocumentReference(SPACE_NAMES, "ConfigurationClass"); @@ -35,7 +56,8 @@ protected LocalDocumentReference getClassReference() } @Override - protected String getCacheId() { + protected String getCacheId() + { return "configuration.document.analytics"; } } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java index f55e278d..737ba1df 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java @@ -1,7 +1,26 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ package com.xwiki.analytics.internal.configuration; +import javax.inject.Singleton; import com.xwiki.analytics.configuration.AnalyticsConfiguration; -import groovy.lang.Singleton; import org.xwiki.component.annotation.Component; import org.xwiki.configuration.ConfigurationSource; @@ -9,31 +28,36 @@ import javax.inject.Named; /** - * Default implementation of {@AnalyticsConfiguration} + * Default implementation of {@AnalyticsConfiguration}. + * @version $Id$ */ @Component @Singleton -public class DefaultAnalyticsConfiguration implements AnalyticsConfiguration { +public class DefaultAnalyticsConfiguration implements AnalyticsConfiguration +{ @Inject @Named("analytics") private ConfigurationSource configDocument; @Override - public String getRequestAddress() { + public String getRequestAddress() + { return getProperty("requestAddress", ""); } @Override - public String getIdSite() { - return getProperty("siteId",""); + public String getIdSite() + { + return getProperty("siteId", ""); } @Override - public String getAuthenticationToken() { + public String getAuthenticationToken() + { return getProperty("authToken", ""); } private T getProperty(String key, T defaultValue) { - return this.configDocument.getProperty(key, defaultValue); + return this.configDocument.getProperty(key, defaultValue); } } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java index 42f54334..1aaa80b0 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java @@ -1,3 +1,22 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ package com.xwiki.analytics.script; import com.xwiki.analytics.configuration.AnalyticsConfiguration; @@ -9,11 +28,27 @@ import javax.inject.Named; import javax.inject.Singleton; + +/** + * @version $Id$ + * @since 1.1 + */ @Component @Named("analytics") @Unstable @Singleton -public class AnalyticsScriptService implements ScriptService { +public class AnalyticsScriptService implements ScriptService +{ @Inject private AnalyticsConfiguration configuration; + + /** + * + * @return Will return the configuration + */ + + public AnalyticsConfiguration getConfiguration() + { + return configuration; + } } diff --git a/application-analytics-default/src/main/resources/META-INF/components.txt b/application-analytics-default/src/main/resources/META-INF/components.txt new file mode 100644 index 00000000..c2810bb1 --- /dev/null +++ b/application-analytics-default/src/main/resources/META-INF/components.txt @@ -0,0 +1,3 @@ +com.xwiki.analytics.internal.configuration.AnalyticsConfigurationSource +com.xwiki.analytics.internal.configuration.DefaultAnalyticsConfiguration +com.xwiki.analytics.script.AnalyticsScriptService diff --git a/application-analytics-ui/pom.xml b/application-analytics-ui/pom.xml index 7d404764..f328070f 100644 --- a/application-analytics-ui/pom.xml +++ b/application-analytics-ui/pom.xml @@ -25,7 +25,7 @@ com.xwiki.analytics application-analytics - 1.0-SNAPSHOT + 1.11-SNAPSHOT application-analytics-ui xar diff --git a/pom.xml b/pom.xml index 0dd2fb24..3cc0b2db 100644 --- a/pom.xml +++ b/pom.xml @@ -19,8 +19,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - + 4.0.0 com.xwiki.parent @@ -29,7 +28,7 @@ com.xwiki.analytics application-analytics - 1.0-SNAPSHOT + 1.11-SNAPSHOT pom Analytics Application (Pro) Parent POM Discover unique insights to help you better understand the usage of your instance. Streamline and improve internal processes by having full visibility on the most viewed pages. Understand how you can help your teams boost productivity by using real-time statistics. From 981005c38b3f71b4109508929d4dbbacf434e465 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 4 Aug 2023 15:10:46 +0300 Subject: [PATCH 006/105] Move Most Viewed Pages Macro to livetable #10 * I added the scripting service and the MatomoAnalyticsManager --- application-analytics-api/pom.xml | 2 +- .../com/xwiki/analytics/AnalyticsManager.java | 14 +++- .../com/xwiki/analytics/JsonNormaliser.java | 3 +- application-analytics-default/pom.xml | 14 +++- .../analytics/MatomoAnalyticsManger.java | 79 +++++++++++++++++++ .../xwiki/analytics/MatomoJsonNormaliser.java | 9 ++- .../script/AnalyticsScriptService.java | 28 ++++++- .../main/resources/META-INF/components.txt | 1 + application-analytics-ui/pom.xml | 2 +- pom.xml | 2 +- 10 files changed, 141 insertions(+), 13 deletions(-) create mode 100644 application-analytics-default/src/main/java/com/xwiki/analytics/MatomoAnalyticsManger.java diff --git a/application-analytics-api/pom.xml b/application-analytics-api/pom.xml index 5fac6aa2..06e95b52 100644 --- a/application-analytics-api/pom.xml +++ b/application-analytics-api/pom.xml @@ -24,7 +24,7 @@ com.xwiki.analytics application-analytics - 1.11-SNAPSHOT + 1.16-SNAPSHOT application-analytics-api jar diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java b/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java index 6ced8f07..e3060bd8 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java @@ -20,16 +20,26 @@ package com.xwiki.analytics; +import com.fasterxml.jackson.databind.JsonNode; import org.xwiki.component.annotation.Role; import org.xwiki.stability.Unstable; +import java.io.IOException; +import java.util.Map; + /** * @version $Id$ * @since 1.0 */ @Role @Unstable -public class AnalyticsManager +public interface AnalyticsManager { - + /** + * This function will make the request to get the data. + * @param address The address where the request will be made. + * @param parameterList A list of key, value pairs that will represent the parameters for the request. + * @return A JSON string. + */ + JsonNode requestData(String address, Map parameterList) throws IOException, InterruptedException; } diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java index 49b190fe..bc1ef40d 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java @@ -20,6 +20,7 @@ package com.xwiki.analytics; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; /** * @version $Id$ @@ -32,5 +33,5 @@ public interface JsonNormaliser * @return Returns the json in string format * @throws JsonProcessingException */ - String normaliseData(String jsonString) throws JsonProcessingException; + JsonNode normaliseData(String jsonString) throws JsonProcessingException; } diff --git a/application-analytics-default/pom.xml b/application-analytics-default/pom.xml index d3f980b7..4a610d0b 100644 --- a/application-analytics-default/pom.xml +++ b/application-analytics-default/pom.xml @@ -24,7 +24,7 @@ com.xwiki.analytics application-analytics - 1.11-SNAPSHOT + 1.16-SNAPSHOT application-analytics-default jar @@ -44,4 +44,16 @@ ${platform.version} + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + \ No newline at end of file diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoAnalyticsManger.java b/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoAnalyticsManger.java new file mode 100644 index 00000000..edac7920 --- /dev/null +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoAnalyticsManger.java @@ -0,0 +1,79 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.analytics; + +import com.fasterxml.jackson.databind.JsonNode; +import org.xwiki.component.annotation.Component; + +import javax.inject.Named; +import javax.inject.Singleton; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.util.Map; + + +/** + * @version $Id$ + */ +@Component +@Named("Matomo") +@Singleton + +public class MatomoAnalyticsManger implements AnalyticsManager +{ + @Override + public JsonNode requestData(String address, Map parameterList) + throws IOException, InterruptedException + { + HttpClient client = HttpClient.newHttpClient(); + HttpRequest httpRequest = HttpRequest.newBuilder(URI.create(buildURI(address, parameterList))).build(); + HttpResponse response = client.send(httpRequest, HttpResponse.BodyHandlers.ofString()); + MatomoJsonNormaliser matomoJsonNormaliser = new MatomoJsonNormaliser(); + return matomoJsonNormaliser.normaliseData(response.body()); + } + private String buildURI(String address, Map parameterList) + { + StringBuilder url = new StringBuilder(); + if (!parameterList.isEmpty()) + { + // If anyone knows a better way to build the final url please send it to me. + url.append(address + "index.php?"); + parameterList.forEach((k, v) -> + { + try { + url.append(URLEncoder.encode(k, StandardCharsets.UTF_8.name())) + .append('=') + .append(URLEncoder.encode(v, StandardCharsets.UTF_8.name())) + .append('&'); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + }); + url.deleteCharAt(url.length() - 1); + } + return url.toString(); + } +} diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoJsonNormaliser.java index 3734100b..13ed7753 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoJsonNormaliser.java @@ -36,12 +36,11 @@ public class MatomoJsonNormaliser implements JsonNormaliser private static final String DATE = "date"; /** - * * @param jsonString The json provided by Matomo. - * @return The normalised json as a string. + * @return The normalised json as a string. * @throws JsonProcessingException */ - public String normaliseData(String jsonString) throws JsonProcessingException + public JsonNode normaliseData(String jsonString) throws JsonProcessingException { JsonNode jsonNode = OBJECT_MAPPER.readTree(jsonString); if (jsonNode.isArray()) { @@ -49,11 +48,12 @@ public String normaliseData(String jsonString) throws JsonProcessingException } else { jsonNode = processObjectNode(jsonNode); } - return jsonNode.toString(); + return jsonNode; } /** * This function will add the date field to all rows when the user uses period="range" and the data is merged. + * * @param jsonNode */ @@ -68,6 +68,7 @@ private void processArrayNode(JsonNode jsonNode) /** * This function will process the json from Matomo and return a new one with the date field added. + * * @param jsonNode * @return */ diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java index 1aaa80b0..5cbf9f5d 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java @@ -19,6 +19,8 @@ */ package com.xwiki.analytics.script; +import com.fasterxml.jackson.databind.JsonNode; +import com.xwiki.analytics.AnalyticsManager; import com.xwiki.analytics.configuration.AnalyticsConfiguration; import org.xwiki.component.annotation.Component; import org.xwiki.script.service.ScriptService; @@ -27,6 +29,8 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; +import java.io.IOException; +import java.util.Map; /** @@ -41,14 +45,34 @@ public class AnalyticsScriptService implements ScriptService { @Inject private AnalyticsConfiguration configuration; + @Inject + @Named("Matomo") + private AnalyticsManager analyticsManager; + /** + * @param parameterList A map of the parameters for the url + * @return Will return a json string. The method is temporary + */ + + public JsonNode getDataFromRequest(Map parameterList) + { + parameterList.put("idSite", configuration.getIdSite()); + parameterList.put("token_auth", configuration.getAuthenticationToken()); + try { + return analyticsManager.requestData(configuration.getRequestAddress(), parameterList); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } /** * - * @return Will return the configuration + * @return Will return the Analytics configuration. */ - public AnalyticsConfiguration getConfiguration() { return configuration; } + } diff --git a/application-analytics-default/src/main/resources/META-INF/components.txt b/application-analytics-default/src/main/resources/META-INF/components.txt index c2810bb1..9ac38f9e 100644 --- a/application-analytics-default/src/main/resources/META-INF/components.txt +++ b/application-analytics-default/src/main/resources/META-INF/components.txt @@ -1,3 +1,4 @@ com.xwiki.analytics.internal.configuration.AnalyticsConfigurationSource com.xwiki.analytics.internal.configuration.DefaultAnalyticsConfiguration com.xwiki.analytics.script.AnalyticsScriptService +com.xwiki.analytics.MatomoAnalyticsManger diff --git a/application-analytics-ui/pom.xml b/application-analytics-ui/pom.xml index 8370cf4f..4ded370d 100644 --- a/application-analytics-ui/pom.xml +++ b/application-analytics-ui/pom.xml @@ -25,7 +25,7 @@ com.xwiki.analytics application-analytics - 1.11-SNAPSHOT + 1.16-SNAPSHOT application-analytics-ui xar diff --git a/pom.xml b/pom.xml index 3cc0b2db..aab32079 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ com.xwiki.analytics application-analytics - 1.11-SNAPSHOT + 1.16-SNAPSHOT pom Analytics Application (Pro) Parent POM Discover unique insights to help you better understand the usage of your instance. Streamline and improve internal processes by having full visibility on the most viewed pages. Understand how you can help your teams boost productivity by using real-time statistics. From 1ead5518e4c90534340de7ef0e1e7e0e382de7f0 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Sun, 6 Aug 2023 01:21:22 +0300 Subject: [PATCH 007/105] Move Most Viewed Pages Macro to livetable #10 *I added the livetable and everything seems to work okay for the time being. --- application-analytics-api/pom.xml | 2 +- application-analytics-default/pom.xml | 2 +- application-analytics-ui/pom.xml | 2 +- .../Analytics/Code/AnalyticsTranslations.xml | 14 +- .../Analytics/Code/Macros/MostViewedPage.xml | 472 ++++++++++++++++++ .../Code/Macros/MostViewedPageJSON.xml | 110 ++++ pom.xml | 2 +- 7 files changed, 599 insertions(+), 5 deletions(-) create mode 100644 application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPage.xml create mode 100644 application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml diff --git a/application-analytics-api/pom.xml b/application-analytics-api/pom.xml index 06e95b52..400aaecb 100644 --- a/application-analytics-api/pom.xml +++ b/application-analytics-api/pom.xml @@ -24,7 +24,7 @@ com.xwiki.analytics application-analytics - 1.16-SNAPSHOT + 1.0-SNAPSHOT application-analytics-api jar diff --git a/application-analytics-default/pom.xml b/application-analytics-default/pom.xml index 4a610d0b..618c7de7 100644 --- a/application-analytics-default/pom.xml +++ b/application-analytics-default/pom.xml @@ -24,7 +24,7 @@ com.xwiki.analytics application-analytics - 1.16-SNAPSHOT + 1.0-SNAPSHOT application-analytics-default jar diff --git a/application-analytics-ui/pom.xml b/application-analytics-ui/pom.xml index 4ded370d..5b5871b3 100644 --- a/application-analytics-ui/pom.xml +++ b/application-analytics-ui/pom.xml @@ -25,7 +25,7 @@ com.xwiki.analytics application-analytics - 1.16-SNAPSHOT + 1.0-SNAPSHOT application-analytics-ui xar diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml index f94f5e83..a278b77a 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml @@ -50,7 +50,7 @@ Analytics.Code.AnalyticsDashboardClass_DisplayedStatistics.hint=List of statisti ## Admin section admin.analytics.application=Analytics Analytics.Code.ConfigurationClass_authToken=Authentication token -Analytics.Code.ConfigurationClass_authToken.hint=Token generated by Matomo to be capable of using the API to view the data. This token can be generated from Settings -> Personal -> Security -> Create New Token +Analytics.Code.ConfigurationClass_authToken.hint=Token generated by Matomo to be capable of using the API to view the data. This token can be generated from Settings -> Personal -> Security -> Create New Token Analytics.Code.ConfigurationClass_siteId=Site ID Analytics.Code.ConfigurationClass_siteId.hint=The ID of the site that you want to see the statistics for. This can be taken from Matomo server integration tab Analytics.Code.ConfigurationClass_requestAddress=Request address @@ -58,6 +58,18 @@ Analytics.Code.ConfigurationClass_requestAddress.hint=The Matomo URL. This can b Analytics.Code.ConfigurationClass_trackingCode=Tracking code Analytics.Code.ConfigurationClass_trackingCode.hint=Tracking Code received from Matomo. The 'u' variable might be different than the one that is initially generated by Matomo if the server you want to track is on another machine. To fix this, go to the integration tab, copy the Matomo URL, and replace the value of 'u' with the Matomo URL. +## Most Viewed Pages Macro +analytics.mostViewedPages.header.date=Date +analytics.mostViewedPages.header.bounceRate=Bounce rate +analytics.mostViewedPages.header.entryNbVisits=No. of views that started on this page +analytics.mostViewedPages.header.entryNbActions= No. of actions +analytics.mostViewedPages.header.exitRate=Exit rate +analytics.mostViewedPages.header.hits=Page views +analytics.mostViewedPages.header.timeSpent=Time spent +analytics.mostViewedPages.header.title=Page title +analytics.mostViewedPages.header.visits=Unique visitors +analytics.mostViewedPages.header.year=Year + ## Live table keys analytics.livetable.doc.title=Page Title analytics.livetable.startDate = Start Date diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPage.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPage.xml new file mode 100644 index 00000000..50481203 --- /dev/null +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPage.xml @@ -0,0 +1,472 @@ + + + + + + Analytics.Code.Macros + MostViewedPage + + + 0 + xwiki:XWiki.Admin + Analytics.Code.Macros.WebHome + xwiki:XWiki.Admin + xwiki:XWiki.Admin + 1.1 + MostViewedPage + + false + xwiki/2.1 + true + {{mostViewedPage/}} + + Analytics.Code.Macros.MostViewedPage + 0 + XWiki.WikiMacroClass + dc943965-6af8-4905-8508-9b8c248c3930 + + XWiki.WikiMacroClass + + + + + + + + + 0 + 0 + select + + async_cached + 13 + Cached + 0 + com.xpn.xwiki.objects.classes.BooleanClass + + + 0 + 0 + select + forbidden + 0 + 1 + async_context + 14 + Context elements + 0 + , + |, + 5 + 0 + action=Action|doc.reference=Document|icon.theme=Icon theme|locale=Language|rendering.defaultsyntax=Default syntax|rendering.restricted=Restricted|rendering.targetsyntax=Target syntax|request.base=Request base URL|request.parameters=Request parameters|request.url=Request URL|request.wiki=Request wiki|user=User|wiki=Wiki + com.xpn.xwiki.objects.classes.StaticListClass + + + 0 + 0 + select + + async_enabled + 12 + Asynchronous rendering + 0 + com.xpn.xwiki.objects.classes.BooleanClass + + + 0 + Text + code + 10 + Macro code + 20 + 40 + 0 + com.xpn.xwiki.objects.classes.TextAreaClass + + + PureText + 0 + PureText + contentDescription + 9 + Content description (Not applicable for "No content" type) + 5 + 40 + 0 + com.xpn.xwiki.objects.classes.TextAreaClass + + + 0 + Unknown + 0 + input + allowed + 1 + 0 + contentJavaType + 8 + 1 + Macro content type + 0 + | + | + 1 + 0 + Unknown|Wiki + com.xpn.xwiki.objects.classes.StaticListClass + + + 0 + 0 + select + forbidden + 0 + 0 + contentType + 7 + Macro content availability + 0 + | + | + 1 + 0 + Optional|Mandatory|No content + com.xpn.xwiki.objects.classes.StaticListClass + + + 0 + defaultCategory + 4 + Default category + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + PureText + 0 + PureText + description + 3 + Macro description + 5 + 40 + 0 + com.xpn.xwiki.objects.classes.TextAreaClass + + + 0 + id + 1 + Macro id + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + name + 2 + Macro name + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + priority + 11 + integer + Priority + 10 + 0 + com.xpn.xwiki.objects.classes.NumberClass + + + 0 + select + yesno + supportsInlineMode + 5 + Supports inline mode + 0 + com.xpn.xwiki.objects.classes.BooleanClass + + + 0 + 0 + select + forbidden + 0 + 0 + visibility + 6 + Macro visibility + 0 + | + | + 1 + 0 + Current User|Current Wiki|Global + com.xpn.xwiki.objects.classes.StaticListClass + + + + 0 + + + + + + 0 + + + {{velocity}} +#set ($dashbord = $doc.getObject('Analytics.Code.AnalyticsDashboardClass')) +#set ($period = 'range') +#if ($dashbord) + #set ($startDate = $dashbord.getProperty('startDate')) + #set ($endDate = $dashbord.getProperty('endDate')) +#else + #set ($startDate = $xcontext.macro.params.startDate.toString()) + #set ($endDate = $xcontext.macro.params.endDate.toString()) +#end +#set ($startDateObj = $datetool.toDate('yyyy/MM/dd HH:mm', $startDate)) +#set ($startDate = $datetool.format('yyyy-MM-dd', $startDateObj)) +#set ($endDateObj = $datetool.toDate('yyyyy/MM/dd HH:mm', $endDate)) +#set ($endDate = $datetool.format('yyyy-MM-dd', $endDateObj)) +#if (!$startDate) + #set ($startDate = '2007-01-01') +#end +#if (!$endDate) + #set ($endDate = $datetool.get('yyyy-MM-dd')) +#end +##Prepare the livetable with the fields and properties +#set ($parameters = '&date=' + $startDate + "," + $endDate + '&period=' + $period + '&limitEntries=-1') +#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbVisits', 'entryNbActions', 'bounceRate', 'exitRate']) +#set ($columnsProperties = { +'date' : { 'type' : 'text', 'sortable' : true }, +'title' : { 'type' : 'text', 'sortable' : true }, +'visits' : { 'type' : 'text', 'sortable' : true }, +'hits' : { 'type' : 'text', 'sortable' : true }, +'timeSpent' : { 'type' : 'text', 'sortable' : true }, +'entryNbVisits' : { 'type' : 'text', 'sortable' : true }, +'entryNbActions' : { 'type' : 'text', 'sortable' : true }, +'bounceRate' : { 'type' : 'text', 'sortable' : true }, +'exitRate' : { 'type' : 'text', 'sortable' : true } +}) +#set($options = { + 'translationPrefix': 'analytics.mostViewedPages.header.', + 'resultPage': 'Analytics.Code.Macros.MostViewedPageJSON', + 'extraParams': $parameters +}) +#livetable('mostViwedPages' $columns $columnsProperties $options) +{{/velocity}} + + + + + + + + + No content + + + + + + + + + mostViewedPage + + + Most Viewed Page + + + + + + 1 + + + Current Wiki + + + + Analytics.Code.Macros.MostViewedPage + 0 + XWiki.WikiMacroParameterClass + 6e3cae3f-3127-4fb2-a590-5dce2f4be0d0 + + XWiki.WikiMacroParameterClass + + + + + + + + + 0 + defaultValue + 4 + Parameter default value + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + description + 2 + Parameter description + 5 + 40 + 0 + com.xpn.xwiki.objects.classes.TextAreaClass + + + 0 + select + yesno + mandatory + 3 + Parameter mandatory + 0 + com.xpn.xwiki.objects.classes.BooleanClass + + + 0 + name + 1 + Parameter name + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + type + 5 + Parameter type + 60 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + + + + + + + + 0 + + + startDate + + + java.util.Date + + + + Analytics.Code.Macros.MostViewedPage + 1 + XWiki.WikiMacroParameterClass + 8a9fb4a7-d7e2-4d7d-84b8-8afb71b4606d + + XWiki.WikiMacroParameterClass + + + + + + + + + 0 + defaultValue + 4 + Parameter default value + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + description + 2 + Parameter description + 5 + 40 + 0 + com.xpn.xwiki.objects.classes.TextAreaClass + + + 0 + select + yesno + mandatory + 3 + Parameter mandatory + 0 + com.xpn.xwiki.objects.classes.BooleanClass + + + 0 + name + 1 + Parameter name + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + type + 5 + Parameter type + 60 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + + + + + + + + 0 + + + endDate + + + java.util.Date + + + diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml new file mode 100644 index 00000000..92b1082a --- /dev/null +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml @@ -0,0 +1,110 @@ + + + + + + Analytics.Code.Macros + MostViewedPageJSON + + + 0 + xwiki:XWiki.Admin + WebHome + xwiki:XWiki.Admin + xwiki:XWiki.Admin + 1.1 + MostViewedPageJSON + + false + xwiki/2.1 + true + {{velocity}} +#template('attachment_macros.vm') +#template('hierarchy_macros.vm') +#if ($xcontext.action == 'get') + #set ($offset = $numbertool.toNumber($request.offset).intValue()) + ## The offset sent by the live table starts at 1. + #set ($offset = $offset - 1) + #if (!$offset || $offset < 0) + #set ($offset = 0) + #end + #set ($limit = $numbertool.toNumber($request.limit).intValue()) + #if (!$limit) + #set ($limit = 15) + #end + ## + ## Apply live table filters. + #set ($keys = { + 'date' : 'date', + 'title' : 'label', + 'visits' : 'nb_visits', + 'hits' : 'nb_hits', + 'timeSpent' : 'sum_time_spent', + 'entryNbVisits' : 'entry_nb_visits', + 'entryNbActions' : 'entry_nb_actions', + 'bounceRate' : 'bounce_rate', + 'exitRate' : 'exit_rate' +}) + #set ($sort = 'nb_hits') + #set ($order = 'desc') + #if($request.sort) + #set($sort = $keys.get($request.sort)) + #set($order = $request.dir) + #end + #set ($parameters = + { + 'period' : $request.period, + 'date' : $request.date, + 'module' : 'API', + 'method' : 'Actions.getPageTitles', + 'format' : 'json', + 'filter_limit' : $request.limitEntries, + 'filter_sort_column' : $sort, + 'filter_sort_order' : $order + } + ) + #set ($analyticsResult = $services.analytics.getDataFromRequest($parameters)) + #set ($results = { + "totalrows": $analyticsResult.size(), + "returnedrows": $analyticsResult.size(), + "offset": $mathtool.add($offset, 1), + "reqNo": $numbertool.toNumber($request.reqNo).intValue(), + "rows": [] + }) + #foreach($currentEntry in $analyticsResult) + #set ($discard = $results.rows.add( + { + 'date' : $currentEntry.get('date').asText(), + 'title' : $currentEntry.get('label').asText(), + 'visits' : $currentEntry.get('nb_visits').asText(), + 'hits' : $currentEntry.get('nb_hits').asText(), + 'timeSpent' : $currentEntry.get('sum_time_spent').asText(), + 'entryNbVisits' : $currentEntry.get('entry_nb_visits').asText(), + 'entryNbActions' : $currentEntry.get('entry_nb_actions').asText(), + 'bounceRate' : $currentEntry.get('bounce_rate').asText(), + 'exitRate' : $currentEntry.get('exit_rate').asText() + } + )) + #end + #jsonResponse($results) +#end +{{/velocity}} + diff --git a/pom.xml b/pom.xml index aab32079..cc461f46 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ com.xwiki.analytics application-analytics - 1.16-SNAPSHOT + 1.0-SNAPSHOT pom Analytics Application (Pro) Parent POM Discover unique insights to help you better understand the usage of your instance. Streamline and improve internal processes by having full visibility on the most viewed pages. Understand how you can help your teams boost productivity by using real-time statistics. From 25a3094b7fcab975302289afa35cfee36644a14f Mon Sep 17 00:00:00 2001 From: Farcasut Date: Mon, 7 Aug 2023 09:41:02 +0300 Subject: [PATCH 008/105] [MISC FIX] Indentation --- .../Analytics/Code/Macros/MostViewedPage.xml | 81 ++++++----- .../Code/Macros/MostViewedPageJSON.xml | 137 +++++++++--------- 2 files changed, 108 insertions(+), 110 deletions(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPage.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPage.xml index 50481203..7ebad1ad 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPage.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPage.xml @@ -240,46 +240,47 @@ 0 - {{velocity}} -#set ($dashbord = $doc.getObject('Analytics.Code.AnalyticsDashboardClass')) -#set ($period = 'range') -#if ($dashbord) - #set ($startDate = $dashbord.getProperty('startDate')) - #set ($endDate = $dashbord.getProperty('endDate')) -#else - #set ($startDate = $xcontext.macro.params.startDate.toString()) - #set ($endDate = $xcontext.macro.params.endDate.toString()) -#end -#set ($startDateObj = $datetool.toDate('yyyy/MM/dd HH:mm', $startDate)) -#set ($startDate = $datetool.format('yyyy-MM-dd', $startDateObj)) -#set ($endDateObj = $datetool.toDate('yyyyy/MM/dd HH:mm', $endDate)) -#set ($endDate = $datetool.format('yyyy-MM-dd', $endDateObj)) -#if (!$startDate) - #set ($startDate = '2007-01-01') -#end -#if (!$endDate) - #set ($endDate = $datetool.get('yyyy-MM-dd')) -#end -##Prepare the livetable with the fields and properties -#set ($parameters = '&date=' + $startDate + "," + $endDate + '&period=' + $period + '&limitEntries=-1') -#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbVisits', 'entryNbActions', 'bounceRate', 'exitRate']) -#set ($columnsProperties = { -'date' : { 'type' : 'text', 'sortable' : true }, -'title' : { 'type' : 'text', 'sortable' : true }, -'visits' : { 'type' : 'text', 'sortable' : true }, -'hits' : { 'type' : 'text', 'sortable' : true }, -'timeSpent' : { 'type' : 'text', 'sortable' : true }, -'entryNbVisits' : { 'type' : 'text', 'sortable' : true }, -'entryNbActions' : { 'type' : 'text', 'sortable' : true }, -'bounceRate' : { 'type' : 'text', 'sortable' : true }, -'exitRate' : { 'type' : 'text', 'sortable' : true } -}) -#set($options = { - 'translationPrefix': 'analytics.mostViewedPages.header.', - 'resultPage': 'Analytics.Code.Macros.MostViewedPageJSON', - 'extraParams': $parameters -}) -#livetable('mostViwedPages' $columns $columnsProperties $options) + {{velocity}} +#set ($dashbord = $doc.getObject('Analytics.Code.AnalyticsDashboardClass')) +#set ($period = 'range') +#if ($dashbord) + #set ($startDate = $dashbord.getProperty('startDate')) + #set ($endDate = $dashbord.getProperty('endDate')) +#else + #set ($startDate = $xcontext.macro.params.startDate.toString()) + #set ($endDate = $xcontext.macro.params.endDate.toString()) +#end +#set ($startDateObj = $datetool.toDate('yyyy/MM/dd HH:mm', $startDate)) +#set ($startDate = $datetool.format('yyyy-MM-dd', $startDateObj)) +#set ($endDateObj = $datetool.toDate('yyyyy/MM/dd HH:mm', $endDate)) +#set ($endDate = $datetool.format('yyyy-MM-dd', $endDateObj)) +#if (!$startDate) + #set ($startDate = '2007-01-01') +#end +#if (!$endDate) + #set ($endDate = $datetool.get('yyyy-MM-dd')) +#end +## Prepare the livetable with the fields and properties. +## Any suggestions on how to do this more cleanly? +#set ($parameters = '&date=' + $startDate + "," + $endDate + '&period=' + $period + '&limitEntries=-1') +#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbVisits', 'entryNbActions', 'bounceRate', 'exitRate']) +#set ($columnsProperties = { + 'date' : { 'type' : 'text', 'sortable' : true }, + 'title' : { 'type' : 'text', 'sortable' : true }, + 'visits' : { 'type' : 'text', 'sortable' : true }, + 'hits' : { 'type' : 'text', 'sortable' : true }, + 'timeSpent' : { 'type' : 'text', 'sortable' : true }, + 'entryNbVisits' : { 'type' : 'text', 'sortable' : true }, + 'entryNbActions' : { 'type' : 'text', 'sortable' : true }, + 'bounceRate' : { 'type' : 'text', 'sortable' : true }, + 'exitRate' : { 'type' : 'text', 'sortable' : true } +}) +#set($options = { + 'translationPrefix': 'analytics.mostViewedPages.header.', + 'resultPage': 'Analytics.Code.Macros.MostViewedPageJSON', + 'extraParams': $parameters +}) +#livetable('mostViewedPages' $columns $columnsProperties $options) {{/velocity}} diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml index 92b1082a..28a28119 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml @@ -36,75 +36,72 @@ false xwiki/2.1 true - {{velocity}} -#template('attachment_macros.vm') -#template('hierarchy_macros.vm') -#if ($xcontext.action == 'get') - #set ($offset = $numbertool.toNumber($request.offset).intValue()) - ## The offset sent by the live table starts at 1. - #set ($offset = $offset - 1) - #if (!$offset || $offset < 0) - #set ($offset = 0) - #end - #set ($limit = $numbertool.toNumber($request.limit).intValue()) - #if (!$limit) - #set ($limit = 15) - #end - ## - ## Apply live table filters. - #set ($keys = { - 'date' : 'date', - 'title' : 'label', - 'visits' : 'nb_visits', - 'hits' : 'nb_hits', - 'timeSpent' : 'sum_time_spent', - 'entryNbVisits' : 'entry_nb_visits', - 'entryNbActions' : 'entry_nb_actions', - 'bounceRate' : 'bounce_rate', - 'exitRate' : 'exit_rate' -}) - #set ($sort = 'nb_hits') - #set ($order = 'desc') - #if($request.sort) - #set($sort = $keys.get($request.sort)) - #set($order = $request.dir) - #end - #set ($parameters = - { - 'period' : $request.period, - 'date' : $request.date, - 'module' : 'API', - 'method' : 'Actions.getPageTitles', - 'format' : 'json', - 'filter_limit' : $request.limitEntries, - 'filter_sort_column' : $sort, - 'filter_sort_order' : $order - } - ) - #set ($analyticsResult = $services.analytics.getDataFromRequest($parameters)) - #set ($results = { - "totalrows": $analyticsResult.size(), - "returnedrows": $analyticsResult.size(), - "offset": $mathtool.add($offset, 1), - "reqNo": $numbertool.toNumber($request.reqNo).intValue(), - "rows": [] - }) - #foreach($currentEntry in $analyticsResult) - #set ($discard = $results.rows.add( - { - 'date' : $currentEntry.get('date').asText(), - 'title' : $currentEntry.get('label').asText(), - 'visits' : $currentEntry.get('nb_visits').asText(), - 'hits' : $currentEntry.get('nb_hits').asText(), - 'timeSpent' : $currentEntry.get('sum_time_spent').asText(), - 'entryNbVisits' : $currentEntry.get('entry_nb_visits').asText(), - 'entryNbActions' : $currentEntry.get('entry_nb_actions').asText(), - 'bounceRate' : $currentEntry.get('bounce_rate').asText(), - 'exitRate' : $currentEntry.get('exit_rate').asText() - } - )) - #end - #jsonResponse($results) -#end + {{velocity}} +#template('attachment_macros.vm') +#template('hierarchy_macros.vm') +#if ($xcontext.action == 'get') + #set ($offset = $numbertool.toNumber($request.offset).intValue()) + ## The offset sent by the live table starts at 1. + #set ($offset = $offset - 1) + #if (!$offset || $offset < 0) + #set ($offset = 0) + #end + #set ($limit = $numbertool.toNumber($request.limit).intValue()) + #if (!$limit) + #set ($limit = 15) + #end + ## + #set ($keys = { + 'date' : 'date', + 'title' : 'label', + 'visits' : 'nb_visits', + 'hits' : 'nb_hits', + 'timeSpent' : 'sum_time_spent', + 'entryNbVisits' : 'entry_nb_visits', + 'entryNbActions' : 'entry_nb_actions', + 'bounceRate' : 'bounce_rate', + 'exitRate' : 'exit_rate' + }) + #set ($sort = 'nb_hits') + #set ($order = 'desc') + #if($request.sort && $request.reqNo!='1') + #set($sort = $keys.get($request.sort)) + #set($order = $request.dir) + #end + #set ($parameters = + { + 'period' : $request.period, + 'date' : $request.date, + 'module' : 'API', + 'method' : 'Actions.getPageTitles', + 'format' : 'json', + 'filter_limit' : $request.limitEntries, + 'filter_sort_column' : $sort, + 'filter_sort_order' : $order + }) + #set ($analyticsResult = $services.analytics.getDataFromRequest($parameters)) + #set ($results = { + "totalrows": $analyticsResult.size(), + "returnedrows": $analyticsResult.size(), + "offset": $mathtool.add($offset, 1), + "reqNo": $numbertool.toNumber($request.reqNo).intValue(), + "rows": [] + }) + #foreach($currentEntry in $analyticsResult) + #set ($discard = $results.rows.add( + { + 'date' : $currentEntry.get('date').asText(), + 'title' : $currentEntry.get('label').asText(), + 'visits' : $currentEntry.get('nb_visits').asText(), + 'hits' : $currentEntry.get('nb_hits').asText(), + 'timeSpent' : $currentEntry.get('sum_time_spent').asText(), + 'entryNbVisits' : $currentEntry.get('entry_nb_visits').asText(), + 'entryNbActions' : $currentEntry.get('entry_nb_actions').asText(), + 'bounceRate' : $currentEntry.get('bounce_rate').asText(), + 'exitRate' : $currentEntry.get('exit_rate').asText() + })) + #end + #jsonResponse($results) +#end {{/velocity}} From ab29104c5de05bb2e8f03a248f44c6fe84c6a214 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Mon, 7 Aug 2023 10:23:32 +0300 Subject: [PATCH 009/105] [MISC FIX] Comments --- .../com/xwiki/analytics/JsonNormaliser.java | 6 +++--- .../configuration/AnalyticsConfiguration.java | 2 +- .../xwiki/analytics/MatomoAnalyticsManger.java | 16 ++++++++++++++++ .../xwiki/analytics/MatomoJsonNormaliser.java | 17 +++++++++-------- .../script/AnalyticsScriptService.java | 4 ++-- 5 files changed, 31 insertions(+), 14 deletions(-) diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java index bc1ef40d..abb311ae 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java @@ -28,10 +28,10 @@ public interface JsonNormaliser { /** - * - * @param jsonString + * This function will normalise the data returned to have only one format. + * @param jsonString A string that has a proper json format. * @return Returns the json in string format - * @throws JsonProcessingException + * @throws JsonProcessingException Throws this error when the jsonString param is not a proper json. */ JsonNode normaliseData(String jsonString) throws JsonProcessingException; } diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java b/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java index f3c012e7..44e0ca8b 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java @@ -34,7 +34,7 @@ public interface AnalyticsConfiguration /** * - * @return Returns the address where the Matomo requests will be made. + * @return Returns the address where the requests will be made. */ String getRequestAddress(); diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoAnalyticsManger.java b/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoAnalyticsManger.java index edac7920..74a13fe1 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoAnalyticsManger.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoAnalyticsManger.java @@ -36,6 +36,7 @@ /** + * The Matomo request manager.This class will make request towards Matomo and return the data. * @version $Id$ */ @Component @@ -44,6 +45,14 @@ public class MatomoAnalyticsManger implements AnalyticsManager { + /** + * + * @param address The address where the request will be made. + * @param parameterList A list of key, value pairs that will represent the parameters for the request. + * @return Will return a json with all the data returned by Matomo. + * @throws IOException + * @throws InterruptedException + */ @Override public JsonNode requestData(String address, Map parameterList) throws IOException, InterruptedException @@ -54,6 +63,13 @@ public JsonNode requestData(String address, Map parameterList) MatomoJsonNormaliser matomoJsonNormaliser = new MatomoJsonNormaliser(); return matomoJsonNormaliser.normaliseData(response.body()); } + + /** + * This function will create the URI for the matomo request by appending all the necessary data. + * @param address Address of the Matomo server. + * @param parameterList List of the url parameters. + * @return The final URI in string format. + */ private String buildURI(String address, Map parameterList) { StringBuilder url = new StringBuilder(); diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoJsonNormaliser.java index 13ed7753..916da891 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoJsonNormaliser.java @@ -36,9 +36,10 @@ public class MatomoJsonNormaliser implements JsonNormaliser private static final String DATE = "date"; /** + * This method will normalise the jsons returned by Matomo into a single format. * @param jsonString The json provided by Matomo. * @return The normalised json as a string. - * @throws JsonProcessingException + * @throws JsonProcessingException Throws this error when the jsonString param is not a proper json. */ public JsonNode normaliseData(String jsonString) throws JsonProcessingException { @@ -52,9 +53,9 @@ public JsonNode normaliseData(String jsonString) throws JsonProcessingException } /** - * This function will add the date field to all rows when the user uses period="range" and the data is merged. - * - * @param jsonNode + * This function will add the date field to all rows when the user uses and the data is merged and is returned + * as an array of jsons. + * @param jsonNode The json node created from the Matomo response. */ private void processArrayNode(JsonNode jsonNode) @@ -67,10 +68,10 @@ private void processArrayNode(JsonNode jsonNode) } /** - * This function will process the json from Matomo and return a new one with the date field added. - * - * @param jsonNode - * @return + * This function will process the data returned by Matomo when it returns a json and each field represents a time + * interval. + * @param jsonNode The json node created from the Matomo response. + * @return Will return a new array of jsons. */ private ArrayNode processObjectNode(JsonNode jsonNode) { diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java index 5cbf9f5d..20e2057d 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java @@ -50,7 +50,7 @@ public class AnalyticsScriptService implements ScriptService private AnalyticsManager analyticsManager; /** * @param parameterList A map of the parameters for the url - * @return Will return a json string. The method is temporary + * @return Will return a json string. */ public JsonNode getDataFromRequest(Map parameterList) @@ -67,7 +67,7 @@ public JsonNode getDataFromRequest(Map parameterList) } /** - * + * This method is only for testing purposes; it will be removed before the pull request is merged. * @return Will return the Analytics configuration. */ public AnalyticsConfiguration getConfiguration() From b6ef7ac3a06429e337538245910f055c32c339dc Mon Sep 17 00:00:00 2001 From: Farcasut Date: Tue, 8 Aug 2023 22:17:39 +0300 Subject: [PATCH 010/105] Move Most Viewed Pages Macro to livetable #10 * I modified the implementations of the JsonNormaliser to be a component and I also added the url + the document reference to the returned json. --- README.md | 1 + SECURITY.md | 3 +- application-analytics-api/pom.xml | 9 +- .../com/xwiki/analytics/AnalyticsManager.java | 15 +- .../com/xwiki/analytics/JsonNormaliser.java | 16 +- .../configuration/AnalyticsConfiguration.java | 9 +- application-analytics-default/pom.xml | 14 +- .../analytics/MatomoAnalyticsManger.java | 52 +++--- .../xwiki/analytics/MatomoJsonNormaliser.java | 92 ---------- .../analytics/MostViewedJsonNormaliser.java | 158 ++++++++++++++++++ .../AnalyticsConfigurationSource.java | 19 ++- .../DefaultAnalyticsConfiguration.java | 11 +- .../script/AnalyticsScriptService.java | 32 ++-- .../main/resources/META-INF/components.txt | 1 + application-analytics-ui/pom.xml | 9 +- pom.xml | 7 +- 16 files changed, 281 insertions(+), 167 deletions(-) delete mode 100644 application-analytics-default/src/main/java/com/xwiki/analytics/MatomoJsonNormaliser.java create mode 100644 application-analytics-default/src/main/java/com/xwiki/analytics/MostViewedJsonNormaliser.java diff --git a/README.md b/README.md index dccb8e5e..a0e96d9e 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ Discover unique insights to help you better understand the usage of your instance. Streamline and improve internal processes by having full visibility on the most viewed pages. Understand how you can help your teams boost productivity by using real-time statistics. + * Project Lead [Farcasi Alexandru](https://github.com/Farcasut) * Documentation & Download: N\A * [Issue Tracker](https://github.com/xwikisas/application-analytics/issues) diff --git a/SECURITY.md b/SECURITY.md index 659e4f72..007486bd 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,3 +1,4 @@ # Security Policy -The security policy that applies to all the [XWiki Pro extensions](https://store.xwiki.com) is detailed on the following document: https://store.xwiki.com/xwiki/bin/view/Store/SecurityPolicy/ . \ No newline at end of file +The security policy that applies to all the [XWiki Pro extensions](https://store.xwiki.com) is detailed on the following +document: https://store.xwiki.com/xwiki/bin/view/Store/SecurityPolicy/ . \ No newline at end of file diff --git a/application-analytics-api/pom.xml b/application-analytics-api/pom.xml index 400aaecb..7342863d 100644 --- a/application-analytics-api/pom.xml +++ b/application-analytics-api/pom.xml @@ -24,19 +24,22 @@ com.xwiki.analytics application-analytics - 1.0-SNAPSHOT + 1.6-SNAPSHOT application-analytics-api jar Analytics Connector Application (Pro) - API - Discover unique insights to help you better understand the usage of your instance. Streamline and improve internal processes by having full visibility on the most viewed pages. Understand how you can help your teams boost productivity by using real-time statistics. + Discover unique insights to help you better understand the usage of your instance. Streamline and improve + internal processes by having full visibility on the most viewed pages. Understand how you can help your teams boost + productivity by using real-time statistics. + org.xwiki.platform xwiki-platform-configuration-default ${platform.version} - + org.xwiki.commons xwiki-commons-component-api ${commons.version} diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java b/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java index e3060bd8..df35b7cd 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java @@ -19,13 +19,17 @@ */ package com.xwiki.analytics; +import java.io.IOException; +import java.util.Map; -import com.fasterxml.jackson.databind.JsonNode; import org.xwiki.component.annotation.Role; +import org.xwiki.component.manager.ComponentLookupException; +import org.xwiki.resource.CreateResourceReferenceException; +import org.xwiki.resource.CreateResourceTypeException; +import org.xwiki.resource.UnsupportedResourceReferenceException; import org.xwiki.stability.Unstable; -import java.io.IOException; -import java.util.Map; +import com.fasterxml.jackson.databind.JsonNode; /** * @version $Id$ @@ -37,9 +41,12 @@ public interface AnalyticsManager { /** * This function will make the request to get the data. + * @param jsonNormaliserHint Hint to select the json normaliser. * @param address The address where the request will be made. * @param parameterList A list of key, value pairs that will represent the parameters for the request. * @return A JSON string. */ - JsonNode requestData(String address, Map parameterList) throws IOException, InterruptedException; + JsonNode requestData(String address, Map parameterList, String jsonNormaliserHint) + throws IOException, InterruptedException, ComponentLookupException, UnsupportedResourceReferenceException, + CreateResourceTypeException, CreateResourceReferenceException; } diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java index abb311ae..866bce0b 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java @@ -19,19 +19,33 @@ */ package com.xwiki.analytics; +import java.net.MalformedURLException; + +import org.xwiki.component.annotation.Role; +import org.xwiki.resource.CreateResourceReferenceException; +import org.xwiki.resource.CreateResourceTypeException; +import org.xwiki.resource.UnsupportedResourceReferenceException; +import org.xwiki.stability.Unstable; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; /** * @version $Id$ + * @since 1.0 */ +@Role +@Unstable public interface JsonNormaliser { /** * This function will normalise the data returned to have only one format. + * * @param jsonString A string that has a proper json format. * @return Returns the json in string format * @throws JsonProcessingException Throws this error when the jsonString param is not a proper json. */ - JsonNode normaliseData(String jsonString) throws JsonProcessingException; + JsonNode normaliseData(String jsonString) + throws JsonProcessingException, MalformedURLException, UnsupportedResourceReferenceException, + CreateResourceTypeException, CreateResourceReferenceException; } diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java b/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java index 44e0ca8b..4fab2b9b 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java @@ -24,29 +24,24 @@ /** * @version $Id$ - * @since 1.0 - * Analytics configuration options. + * @since 1.0 Analytics configuration options. */ @Role @Unstable public interface AnalyticsConfiguration { - /** - * * @return Returns the address where the requests will be made. */ String getRequestAddress(); /** - * * @return Returns the id of the site that we want to see the statistics for. */ String getIdSite(); /** - * - * @return Returns the Authentication Token that permits to access the statistics. + * @return Returns the Authentication Token that permits to access the statistics. */ String getAuthenticationToken(); } diff --git a/application-analytics-default/pom.xml b/application-analytics-default/pom.xml index 618c7de7..5b10664b 100644 --- a/application-analytics-default/pom.xml +++ b/application-analytics-default/pom.xml @@ -24,12 +24,15 @@ com.xwiki.analytics application-analytics - 1.0-SNAPSHOT + 1.6-SNAPSHOT application-analytics-default jar - Analytics Application (Pro) - DEFAULT - Discover unique insights to help you better understand the usage of your instance. Streamline and improve internal processes by having full visibility on the most viewed pages. Understand how you can help your teams boost productivity by using real-time statistics. + Analytics Application (Pro) - DEFAULT + Discover unique insights to help you better understand the usage of your instance. Streamline and improve + internal processes by having full visibility on the most viewed pages. Understand how you can help your teams boost + productivity by using real-time statistics. + @@ -43,6 +46,11 @@ xwiki-platform-configuration-default ${platform.version} + + org.xwiki.platform + xwiki-platform-annotation-reference + ${platform.version} + diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoAnalyticsManger.java b/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoAnalyticsManger.java index 74a13fe1..f7019020 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoAnalyticsManger.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoAnalyticsManger.java @@ -19,13 +19,7 @@ */ package com.xwiki.analytics; -import com.fasterxml.jackson.databind.JsonNode; -import org.xwiki.component.annotation.Component; - -import javax.inject.Named; -import javax.inject.Singleton; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLEncoder; import java.net.http.HttpClient; @@ -34,19 +28,34 @@ import java.nio.charset.StandardCharsets; import java.util.Map; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.xwiki.component.annotation.Component; +import org.xwiki.component.manager.ComponentLookupException; +import org.xwiki.component.manager.ComponentManager; +import org.xwiki.resource.CreateResourceReferenceException; +import org.xwiki.resource.CreateResourceTypeException; +import org.xwiki.resource.UnsupportedResourceReferenceException; + +import com.fasterxml.jackson.databind.JsonNode; /** * The Matomo request manager.This class will make request towards Matomo and return the data. + * * @version $Id$ */ @Component @Named("Matomo") @Singleton - public class MatomoAnalyticsManger implements AnalyticsManager { + @Inject + private ComponentManager componentManager; + /** - * + * @param jsonNormaliserHint Hint to select the json normaliser. * @param address The address where the request will be made. * @param parameterList A list of key, value pairs that will represent the parameters for the request. * @return Will return a json with all the data returned by Matomo. @@ -54,18 +63,20 @@ public class MatomoAnalyticsManger implements AnalyticsManager * @throws InterruptedException */ @Override - public JsonNode requestData(String address, Map parameterList) - throws IOException, InterruptedException + public JsonNode requestData(String address, Map parameterList, String jsonNormaliserHint) + throws IOException, InterruptedException, ComponentLookupException, UnsupportedResourceReferenceException, + CreateResourceTypeException, CreateResourceReferenceException { + JsonNormaliser jsonNormaliser = componentManager.getInstance(JsonNormaliser.class, jsonNormaliserHint); HttpClient client = HttpClient.newHttpClient(); HttpRequest httpRequest = HttpRequest.newBuilder(URI.create(buildURI(address, parameterList))).build(); - HttpResponse response = client.send(httpRequest, HttpResponse.BodyHandlers.ofString()); - MatomoJsonNormaliser matomoJsonNormaliser = new MatomoJsonNormaliser(); - return matomoJsonNormaliser.normaliseData(response.body()); + HttpResponse response = client.send(httpRequest, HttpResponse.BodyHandlers.ofString()); + return jsonNormaliser.normaliseData(response.body()); } /** * This function will create the URI for the matomo request by appending all the necessary data. + * * @param address Address of the Matomo server. * @param parameterList List of the url parameters. * @return The final URI in string format. @@ -73,20 +84,15 @@ public JsonNode requestData(String address, Map parameterList) private String buildURI(String address, Map parameterList) { StringBuilder url = new StringBuilder(); - if (!parameterList.isEmpty()) - { + if (!parameterList.isEmpty()) { // If anyone knows a better way to build the final url please send it to me. url.append(address + "index.php?"); parameterList.forEach((k, v) -> { - try { - url.append(URLEncoder.encode(k, StandardCharsets.UTF_8.name())) - .append('=') - .append(URLEncoder.encode(v, StandardCharsets.UTF_8.name())) - .append('&'); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } + url.append(URLEncoder.encode(k, StandardCharsets.UTF_8)) + .append('=') + .append(URLEncoder.encode(v, StandardCharsets.UTF_8)) + .append('&'); }); url.deleteCharAt(url.length() - 1); } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoJsonNormaliser.java deleted file mode 100644 index 916da891..00000000 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/MatomoJsonNormaliser.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * See the NOTICE file distributed with this work for additional - * information regarding copyright ownership. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package com.xwiki.analytics; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; - -import java.util.Iterator; - -/** - * @version $Id$ - */ -public class MatomoJsonNormaliser implements JsonNormaliser -{ - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - private static final String DATE = "date"; - - /** - * This method will normalise the jsons returned by Matomo into a single format. - * @param jsonString The json provided by Matomo. - * @return The normalised json as a string. - * @throws JsonProcessingException Throws this error when the jsonString param is not a proper json. - */ - public JsonNode normaliseData(String jsonString) throws JsonProcessingException - { - JsonNode jsonNode = OBJECT_MAPPER.readTree(jsonString); - if (jsonNode.isArray()) { - processArrayNode(jsonNode); - } else { - jsonNode = processObjectNode(jsonNode); - } - return jsonNode; - } - - /** - * This function will add the date field to all rows when the user uses and the data is merged and is returned - * as an array of jsons. - * @param jsonNode The json node created from the Matomo response. - */ - - private void processArrayNode(JsonNode jsonNode) - { - for (JsonNode objNode : jsonNode) { - if (objNode.isObject()) { - ((ObjectNode) objNode).put(DATE, ""); - } - } - } - - /** - * This function will process the data returned by Matomo when it returns a json and each field represents a time - * interval. - * @param jsonNode The json node created from the Matomo response. - * @return Will return a new array of jsons. - */ - private ArrayNode processObjectNode(JsonNode jsonNode) - { - ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); - Iterator fieldNames = jsonNode.fieldNames(); - while (fieldNames.hasNext()) { - String date = fieldNames.next(); - JsonNode childNode = jsonNode.get(date); - for (JsonNode objNode : childNode) { - if (objNode.isObject()) { - ((ObjectNode) objNode).put(DATE, date); - arrayNode.add(objNode); - } - } - } - return arrayNode; - } -} diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/MostViewedJsonNormaliser.java new file mode 100644 index 00000000..01499d43 --- /dev/null +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/MostViewedJsonNormaliser.java @@ -0,0 +1,158 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.analytics; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collections; +import java.util.Iterator; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.xwiki.component.annotation.Component; +import org.xwiki.resource.CreateResourceReferenceException; +import org.xwiki.resource.CreateResourceTypeException; +import org.xwiki.resource.ResourceReference; +import org.xwiki.resource.ResourceReferenceResolver; +import org.xwiki.resource.ResourceType; +import org.xwiki.resource.ResourceTypeResolver; +import org.xwiki.resource.UnsupportedResourceReferenceException; +import org.xwiki.resource.entity.EntityResourceReference; +import org.xwiki.url.ExtendedURL; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * @version $Id$ + */ +@Component +@Named("MostViewed") +@Singleton +public class MostViewedJsonNormaliser implements JsonNormaliser +{ + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private static final String DATE = "date"; + + private static final String URL = "url"; + + private static final String LABEL = "label"; + + private static final String DOCUMENT_REFERENCE = "documentReference"; + + @Inject + private ResourceReferenceResolver resourceReferenceResolver; + + @Inject + private ResourceTypeResolver resourceTypeResolver; + + /** + * This method will normalise the jsons returned by Matomo into a single format. + * + * @param jsonString The json provided by Matomo. + * @return The normalised json as a string. + * @throws JsonProcessingException Throws this error when the jsonString param is not a proper json. + */ + public JsonNode normaliseData(String jsonString) + throws JsonProcessingException, MalformedURLException, UnsupportedResourceReferenceException, + CreateResourceTypeException, CreateResourceReferenceException + { + JsonNode jsonNode = OBJECT_MAPPER.readTree(jsonString); + if (jsonNode.isArray()) { + processArrayNode(jsonNode); + } else { + jsonNode = processObjectNode(jsonNode); + } + return jsonNode; + } + + /** + * This function will add the date field to all rows when the user uses and the data is merged and is returned as an + * array of jsons. + * + * @param jsonNode The json node created from the Matomo response. + */ + private void processArrayNode(JsonNode jsonNode) + throws MalformedURLException, UnsupportedResourceReferenceException, CreateResourceTypeException, + CreateResourceReferenceException + { + for (JsonNode objNode : jsonNode) { + if (objNode.isObject()) { + ((ObjectNode) objNode).put(DATE, ""); + EntityResourceReference entityResourceReference = + (EntityResourceReference) this.getReference(objNode.get(URL).asText()); + ((ObjectNode) objNode).put(LABEL, + entityResourceReference.getEntityReference().getName()); + ((ObjectNode) objNode).put(DOCUMENT_REFERENCE, + entityResourceReference.getEntityReference().toString()); + } + } + } + + /** + * This function will process the data returned by Matomo when it returns a json and each field represents a time + * interval. + * + * @param jsonNode The json node created from the Matomo response. + * @return Will return a new array of jsons. + */ + private ArrayNode processObjectNode(JsonNode jsonNode) + throws MalformedURLException, UnsupportedResourceReferenceException, CreateResourceTypeException, + CreateResourceReferenceException + { + ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); + Iterator fieldNames = jsonNode.fieldNames(); + while (fieldNames.hasNext()) { + String date = fieldNames.next(); + JsonNode childNode = jsonNode.get(date); + for (JsonNode objNode : childNode) { + if (objNode.isObject()) { + ((ObjectNode) objNode).put(DATE, date); + EntityResourceReference entityResourceReference = + (EntityResourceReference) this.getReference(objNode.get(URL).asText()); + ((ObjectNode) objNode).put(LABEL, + entityResourceReference.getEntityReference().getName()); + arrayNode.add(objNode); + ((ObjectNode) objNode).put(DOCUMENT_REFERENCE, + entityResourceReference.getEntityReference().toString()); + } + } + } + return arrayNode; + } + + private ResourceReference getReference(String url) + throws MalformedURLException, CreateResourceTypeException, UnsupportedResourceReferenceException, + CreateResourceReferenceException + { + ResourceReference result = null; + ExtendedURL extendedURL = new ExtendedURL(new URL(url), null); + ResourceType resourceType = this.resourceTypeResolver.resolve(extendedURL, Collections.emptyMap()); + result = this.resourceReferenceResolver.resolve(extendedURL, resourceType, Collections.emptyMap()); + + return result; + } +} diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/AnalyticsConfigurationSource.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/AnalyticsConfigurationSource.java index b13f3912..a01b80a4 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/AnalyticsConfigurationSource.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/AnalyticsConfigurationSource.java @@ -18,16 +18,18 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package com.xwiki.analytics.internal.configuration; + +import java.util.Arrays; +import java.util.List; + +import javax.inject.Named; import javax.inject.Singleton; + import org.xwiki.component.annotation.Component; -import org.xwiki.model.reference.DocumentReference; import org.xwiki.configuration.internal.AbstractDocumentConfigurationSource; +import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.LocalDocumentReference; -import javax.inject.Named; -import java.util.Arrays; -import java.util.List; - /** * @version $Id$ */ @@ -37,11 +39,12 @@ public class AnalyticsConfigurationSource extends AbstractDocumentConfigurationSource { private static final List SPACE_NAMES = Arrays.asList("Analytics", "Code"); + private static final LocalDocumentReference DOCUMENT_REFERENCE = - new LocalDocumentReference(SPACE_NAMES, "Configuration"); - private static final LocalDocumentReference CLASS_REFERENCE = - new LocalDocumentReference(SPACE_NAMES, "ConfigurationClass"); + new LocalDocumentReference(SPACE_NAMES, "Configuration"); + private static final LocalDocumentReference CLASS_REFERENCE = + new LocalDocumentReference(SPACE_NAMES, "ConfigurationClass"); @Override protected DocumentReference getDocumentReference() diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java index 737ba1df..c9b3a8ca 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java @@ -19,16 +19,18 @@ */ package com.xwiki.analytics.internal.configuration; +import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; -import com.xwiki.analytics.configuration.AnalyticsConfiguration; + import org.xwiki.component.annotation.Component; import org.xwiki.configuration.ConfigurationSource; -import javax.inject.Inject; -import javax.inject.Named; +import com.xwiki.analytics.configuration.AnalyticsConfiguration; /** * Default implementation of {@AnalyticsConfiguration}. + * * @version $Id$ */ @Component @@ -38,6 +40,7 @@ public class DefaultAnalyticsConfiguration implements AnalyticsConfiguration @Inject @Named("analytics") private ConfigurationSource configDocument; + @Override public String getRequestAddress() { @@ -47,7 +50,7 @@ public String getRequestAddress() @Override public String getIdSite() { - return getProperty("siteId", ""); + return getProperty("siteId", ""); } @Override diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java index 20e2057d..28c13689 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java @@ -19,19 +19,19 @@ */ package com.xwiki.analytics.script; -import com.fasterxml.jackson.databind.JsonNode; -import com.xwiki.analytics.AnalyticsManager; -import com.xwiki.analytics.configuration.AnalyticsConfiguration; -import org.xwiki.component.annotation.Component; -import org.xwiki.script.service.ScriptService; -import org.xwiki.stability.Unstable; +import java.util.Map; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; -import java.io.IOException; -import java.util.Map; +import org.xwiki.component.annotation.Component; +import org.xwiki.script.service.ScriptService; +import org.xwiki.stability.Unstable; + +import com.fasterxml.jackson.databind.JsonNode; +import com.xwiki.analytics.AnalyticsManager; +import com.xwiki.analytics.configuration.AnalyticsConfiguration; /** * @version $Id$ @@ -45,34 +45,34 @@ public class AnalyticsScriptService implements ScriptService { @Inject private AnalyticsConfiguration configuration; + @Inject @Named("Matomo") private AnalyticsManager analyticsManager; + /** + * @param jsonNormaliserHint Hint to select the json normaliser. * @param parameterList A map of the parameters for the url - * @return Will return a json string. + * @return Will return a json string. */ - - public JsonNode getDataFromRequest(Map parameterList) + public JsonNode getDataFromRequest(Map parameterList, String jsonNormaliserHint) { parameterList.put("idSite", configuration.getIdSite()); parameterList.put("token_auth", configuration.getAuthenticationToken()); try { - return analyticsManager.requestData(configuration.getRequestAddress(), parameterList); - } catch (IOException e) { - throw new RuntimeException(e); - } catch (InterruptedException e) { + return analyticsManager.requestData(configuration.getRequestAddress(), parameterList, jsonNormaliserHint); + } catch (Exception e) { throw new RuntimeException(e); } } /** * This method is only for testing purposes; it will be removed before the pull request is merged. + * * @return Will return the Analytics configuration. */ public AnalyticsConfiguration getConfiguration() { return configuration; } - } diff --git a/application-analytics-default/src/main/resources/META-INF/components.txt b/application-analytics-default/src/main/resources/META-INF/components.txt index 9ac38f9e..47d1a175 100644 --- a/application-analytics-default/src/main/resources/META-INF/components.txt +++ b/application-analytics-default/src/main/resources/META-INF/components.txt @@ -2,3 +2,4 @@ com.xwiki.analytics.internal.configuration.AnalyticsConfigurationSource com.xwiki.analytics.internal.configuration.DefaultAnalyticsConfiguration com.xwiki.analytics.script.AnalyticsScriptService com.xwiki.analytics.MatomoAnalyticsManger +com.xwiki.analytics.MostViewedJsonNormaliser diff --git a/application-analytics-ui/pom.xml b/application-analytics-ui/pom.xml index 5b5871b3..d379c0a8 100644 --- a/application-analytics-ui/pom.xml +++ b/application-analytics-ui/pom.xml @@ -25,12 +25,15 @@ com.xwiki.analytics application-analytics - 1.0-SNAPSHOT + 1.6-SNAPSHOT application-analytics-ui xar - Analytics Application (Pro) - Discover unique insights to help you better understand the usage of your instance. Streamline and improve internal processes by having full visibility on the most viewed pages. Understand how you can help your teams boost productivity by using real-time statistics. + Analytics Application (Pro) + Discover unique insights to help you better understand the usage of your instance. Streamline and improve + internal processes by having full visibility on the most viewed pages. Understand how you can help your teams boost + productivity by using real-time statistics. + - - - Analytics.Code.Macros - MostViewedPageJSON - - - 0 - xwiki:XWiki.Admin - WebHome - xwiki:XWiki.Admin - xwiki:XWiki.Admin - 1.1 - MostViewedPageJSON - - false - xwiki/2.1 - true - {{velocity}} -#template('attachment_macros.vm') -#template('hierarchy_macros.vm') -#if ($xcontext.action == 'get') - #set ($offset = $numbertool.toNumber($request.offset).intValue()) - ## The offset sent by the live table starts at 1. - #set ($offset = $offset - 1) - #if (!$offset || $offset < 0) - #set ($offset = 0) - #end - #set ($limit = $numbertool.toNumber($request.limit).intValue()) - #if (!$limit) - #set ($limit = 15) - #end - ## - #set ($keys = { - 'date' : 'date', - 'title' : 'label', - 'visits' : 'nb_visits', - 'hits' : 'nb_hits', - 'timeSpent' : 'sum_time_spent', - 'entryNbVisits' : 'entry_nb_visits', - 'entryNbActions' : 'entry_nb_actions', - 'bounceRate' : 'bounce_rate', - 'exitRate' : 'exit_rate' - }) - #set ($sort = 'nb_hits') - #set ($order = 'desc') - #if($request.sort && $request.reqNo!='1') - #set($sort = $keys.get($request.sort)) - #set($order = $request.dir) - #end - #set ($parameters = - { - 'period' : $request.period, - 'date' : $request.date, - 'module' : 'API', - 'method' : 'Actions.getPageTitles', - 'format' : 'json', - 'filter_limit' : $request.limitEntries, - 'filter_sort_column' : $sort, - 'filter_sort_order' : $order - }) - #set ($analyticsResult = $services.analytics.getDataFromRequest($parameters)) - #set ($results = { - "totalrows": $analyticsResult.size(), - "returnedrows": $analyticsResult.size(), - "offset": $mathtool.add($offset, 1), - "reqNo": $numbertool.toNumber($request.reqNo).intValue(), - "rows": [] - }) - #foreach($currentEntry in $analyticsResult) - #set ($discard = $results.rows.add( - { - 'date' : $currentEntry.get('date').asText(), - 'title' : $currentEntry.get('label').asText(), - 'visits' : $currentEntry.get('nb_visits').asText(), - 'hits' : $currentEntry.get('nb_hits').asText(), - 'timeSpent' : $currentEntry.get('sum_time_spent').asText(), - 'entryNbVisits' : $currentEntry.get('entry_nb_visits').asText(), - 'entryNbActions' : $currentEntry.get('entry_nb_actions').asText(), - 'bounceRate' : $currentEntry.get('bounce_rate').asText(), - 'exitRate' : $currentEntry.get('exit_rate').asText() - })) - #end - #jsonResponse($results) -#end -{{/velocity}} - + + + + + + Analytics.Code.Macros + MostViewedPageJSON + + + 0 + xwiki:XWiki.Admin + WebHome + xwiki:XWiki.Admin + xwiki:XWiki.Admin + 1.1 + MostViewedPageJSON + + false + xwiki/2.1 + true + {{include reference='Analytics.Code.Macros.Macros'/}} +{{velocity}} +#template('attachment_macros.vm') +#template('hierarchy_macros.vm') +#if ($xcontext.action == 'get') + #set ($offset = $numbertool.toNumber($request.offset).intValue()) + ## The offset sent by the live table starts at 1. + #set ($offset = $offset - 1) + #if (!$offset || $offset < 0) + #set ($offset = 0) + #end + #set ($limit = $numbertool.toNumber($request.limit).intValue()) + #if (!$limit) + #set ($limit = 15) + #end + ## + #set ($keys = { + 'date' : 'date', + 'title' : 'label', + 'visits' : 'nb_visits', + 'hits' : 'nb_hits', + 'timeSpent' : 'sum_time_spent', + 'entryNbVisits' : 'entry_nb_visits', + 'entryNbActions' : 'entry_nb_actions', + 'bounceRate' : 'bounce_rate', + 'exitRate' : 'exit_rate' + }) + #set ($sort = 'nb_hits') + #set ($order = 'desc') + #if($request.sort && $request.reqNo!='1') + #set($sort = $keys.get($request.sort)) + #set($order = $request.dir) + #end + #set ($parameters = + { + 'period' : $request.period, + 'date' : $request.date, + 'module' : 'API', + 'method' : 'Actions.getPageUrls', + 'format' : 'json', + 'filter_limit' : $request.limitEntries, + 'filter_sort_column' : $sort, + 'filter_sort_order' : $order, + 'expanded' : '1', + 'flat' : '1' + }) + #set ($analyticsResult = $services.analytics.getDataFromRequest($parameters, 'MostViewed')) + #set ($results = { + "totalrows": $analyticsResult.size(), + "returnedrows": $analyticsResult.size(), + "offset": $mathtool.add($offset, 1), + "reqNo": $numbertool.toNumber($request.reqNo).intValue(), + "rows": [] + }) + #foreach($currentEntry in $analyticsResult) + #set ($url = $currentEntry.get('url').asText()) + #set ($discard = $results.rows.add( + { + 'date' : $currentEntry.get('date').asText(), + 'title' : $currentEntry.get('label').asText(), + 'visits' : $currentEntry.get('nb_visits').asText(), + 'hits' : $currentEntry.get('nb_hits').asText(), + 'timeSpent' : $currentEntry.get('sum_time_spent').asText(), + 'entryNbVisits' : $currentEntry.get('entry_nb_visits').asText(), + 'entryNbActions' : $currentEntry.get('entry_nb_actions').asText(), + 'bounceRate' : $currentEntry.get('bounce_rate').asText(), + 'exitRate' : $currentEntry.get('exit_rate').asText() + })) + #end + #jsonResponse($results) +#end +{{/velocity}} + + From a5625b24114159d118a9939cb3d91300ee10e5f5 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Wed, 9 Aug 2023 17:31:56 +0300 Subject: [PATCH 013/105] Move Most Viewed Pages Macro to livetable #10 * I added an if in the case that I switch back to the getPageTitles API --- .../MostViewedJsonNormaliser.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/MostViewedJsonNormaliser.java index 72bccb60..98d43a84 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/MostViewedJsonNormaliser.java @@ -99,12 +99,14 @@ private void processArrayNode(JsonNode jsonNode) for (JsonNode objNode : jsonNode) { if (objNode.isObject()) { ((ObjectNode) objNode).put(DATE, ""); - EntityResourceReference entityResourceReference = - (EntityResourceReference) this.getReference(objNode.get(URL).asText()); - ((ObjectNode) objNode).put(LABEL, - entityResourceReference.getEntityReference().getName()); - ((ObjectNode) objNode).put(DOCUMENT_REFERENCE, - entityResourceReference.getEntityReference().toString()); + if (objNode.has(URL)) { + EntityResourceReference entityResourceReference = + (EntityResourceReference) this.getReference(objNode.get(URL).asText()); + ((ObjectNode) objNode).put(LABEL, + entityResourceReference.getEntityReference().getName()); + ((ObjectNode) objNode).put(DOCUMENT_REFERENCE, + entityResourceReference.getEntityReference().toString()); + } } } } @@ -121,13 +123,15 @@ private ArrayNode processObjectNode(JsonNode jsonNode) for (JsonNode objNode : childNode) { if (objNode.isObject()) { ((ObjectNode) objNode).put(DATE, date); - EntityResourceReference entityResourceReference = - (EntityResourceReference) this.getReference(objNode.get(URL).asText()); - ((ObjectNode) objNode).put(LABEL, - entityResourceReference.getEntityReference().getName()); - arrayNode.add(objNode); - ((ObjectNode) objNode).put(DOCUMENT_REFERENCE, - entityResourceReference.getEntityReference().toString()); + if (objNode.has(URL)) { + EntityResourceReference entityResourceReference = + (EntityResourceReference) this.getReference(objNode.get(URL).asText()); + ((ObjectNode) objNode).put(LABEL, + entityResourceReference.getEntityReference().getName()); + arrayNode.add(objNode); + ((ObjectNode) objNode).put(DOCUMENT_REFERENCE, + entityResourceReference.getEntityReference().toString()); + } } } } From 5418b75085b103b4232d7adf896d51934ef0fc48 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 10 Aug 2023 14:01:59 +0300 Subject: [PATCH 014/105] Move Most Viewed Pages Macro to livetable #10 * I modified the dependencies and removed some parameters. --- .../com/xwiki/analytics/AnalyticsManager.java | 5 +-- application-analytics-default/pom.xml | 5 +-- .../MatomoAnalyticsManager.java | 15 ++++--- .../MostViewedJsonNormaliser.java | 43 +++++++++---------- .../script/AnalyticsScriptService.java | 15 +++---- .../main/resources/META-INF/components.txt | 4 +- 6 files changed, 41 insertions(+), 46 deletions(-) rename application-analytics-default/src/main/java/com/xwiki/analytics/internal/{configuration => }/MatomoAnalyticsManager.java (85%) rename application-analytics-default/src/main/java/com/xwiki/analytics/internal/{configuration => }/MostViewedJsonNormaliser.java (74%) diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java b/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java index e1de1885..f76e70a2 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java @@ -44,11 +44,10 @@ public interface AnalyticsManager /** * This function will make the request to get the data. * @param jsonNormaliserHint Hint to select the json normaliser. - * @param address The address where the request will be made. - * @param parameterList A list of key, value pairs that will represent the parameters for the request. + * @param parameters A list of key, value pairs that will represent the parameters for the request. * @return A JSON string. */ - JsonNode requestData(String address, Map parameterList, String jsonNormaliserHint) + JsonNode requestData(Map parameters, String jsonNormaliserHint) throws IOException, InterruptedException, ComponentLookupException, UnsupportedResourceReferenceException, CreateResourceTypeException, CreateResourceReferenceException; } diff --git a/application-analytics-default/pom.xml b/application-analytics-default/pom.xml index 7990e5e8..801a3131 100644 --- a/application-analytics-default/pom.xml +++ b/application-analytics-default/pom.xml @@ -42,9 +42,8 @@ ${project.version} - jakarta.ws.rs - jakarta.ws.rs-api - 2.1.6 + javax.ws.rs + jsr311-api diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/MatomoAnalyticsManager.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java similarity index 85% rename from application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/MatomoAnalyticsManager.java rename to application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java index 3b1366dd..796737e7 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/MatomoAnalyticsManager.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package com.xwiki.analytics.internal.configuration; +package com.xwiki.analytics.internal; import java.io.IOException; import java.net.URI; @@ -42,6 +42,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.xwiki.analytics.AnalyticsManager; import com.xwiki.analytics.JsonNormaliser; +import com.xwiki.analytics.configuration.AnalyticsConfiguration; /** * The class would handle the Matomo request and return a json with the data. @@ -57,23 +58,27 @@ public class MatomoAnalyticsManager implements AnalyticsManager { @Inject private ComponentManager componentManager; + @Inject + private AnalyticsConfiguration configuration; /** * This method will handle all the request made by the user and return a proper json. * * @param jsonNormaliserHint hint to select the json normaliser. - * @param address the address where the request will be made. - * @param parameterList a list of key, value pairs that will represent the parameters for the request. + * @param parameters a list of key, value pairs that will represent the parameters for the request. * @return will return a json with all the data returned by Matomo. */ @Override - public JsonNode requestData(String address, Map parameterList, String jsonNormaliserHint) + public JsonNode requestData(Map parameters, String jsonNormaliserHint) throws IOException, InterruptedException, ComponentLookupException, UnsupportedResourceReferenceException, CreateResourceTypeException, CreateResourceReferenceException { + parameters.put("idSite", configuration.getIdSite()); + parameters.put("token_auth", configuration.getAuthenticationToken()); JsonNormaliser jsonNormaliser = componentManager.getInstance(JsonNormaliser.class, jsonNormaliserHint); HttpClient client = HttpClient.newHttpClient(); - HttpRequest httpRequest = HttpRequest.newBuilder(buildURI(address, parameterList)).build(); + HttpRequest httpRequest = HttpRequest.newBuilder(buildURI(configuration.getRequestAddress(), parameters)) + .build(); HttpResponse response = client.send(httpRequest, HttpResponse.BodyHandlers.ofString()); return jsonNormaliser.normaliseData(response.body()); } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java similarity index 74% rename from application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/MostViewedJsonNormaliser.java rename to application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index 98d43a84..5a7701c5 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package com.xwiki.analytics.internal.configuration; +package com.xwiki.analytics.internal; import java.net.MalformedURLException; import java.net.URL; @@ -28,6 +28,7 @@ import javax.inject.Named; import javax.inject.Singleton; +import org.slf4j.Logger; import org.xwiki.component.annotation.Component; import org.xwiki.resource.CreateResourceReferenceException; import org.xwiki.resource.CreateResourceTypeException; @@ -70,6 +71,9 @@ public class MostViewedJsonNormaliser implements JsonNormaliser @Inject private ResourceReferenceResolver resourceReferenceResolver; + @Inject + private Logger logger; + @Inject private ResourceTypeResolver resourceTypeResolver; @@ -79,9 +83,7 @@ public class MostViewedJsonNormaliser implements JsonNormaliser * @param jsonString the json provided by Matomo. * @return the normalised json as a string. */ - public JsonNode normaliseData(String jsonString) - throws JsonProcessingException, MalformedURLException, UnsupportedResourceReferenceException, - CreateResourceTypeException, CreateResourceReferenceException + public JsonNode normaliseData(String jsonString) throws JsonProcessingException { JsonNode jsonNode = OBJECT_MAPPER.readTree(jsonString); if (jsonNode.isArray()) { @@ -93,27 +95,21 @@ public JsonNode normaliseData(String jsonString) } private void processArrayNode(JsonNode jsonNode) - throws MalformedURLException, UnsupportedResourceReferenceException, CreateResourceTypeException, - CreateResourceReferenceException { for (JsonNode objNode : jsonNode) { if (objNode.isObject()) { ((ObjectNode) objNode).put(DATE, ""); if (objNode.has(URL)) { EntityResourceReference entityResourceReference = - (EntityResourceReference) this.getReference(objNode.get(URL).asText()); + (EntityResourceReference) this.getResourceReferenceFromStringURL(objNode.get(URL).asText()); ((ObjectNode) objNode).put(LABEL, entityResourceReference.getEntityReference().getName()); - ((ObjectNode) objNode).put(DOCUMENT_REFERENCE, - entityResourceReference.getEntityReference().toString()); } } } } private ArrayNode processObjectNode(JsonNode jsonNode) - throws MalformedURLException, UnsupportedResourceReferenceException, CreateResourceTypeException, - CreateResourceReferenceException { ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); Iterator fieldNames = jsonNode.fieldNames(); @@ -121,16 +117,16 @@ private ArrayNode processObjectNode(JsonNode jsonNode) String date = fieldNames.next(); JsonNode childNode = jsonNode.get(date); for (JsonNode objNode : childNode) { + //This will add the date to the json itself in case the user uses another period=day/week/mont/year + // and will also add change the label to the name of the page. if (objNode.isObject()) { ((ObjectNode) objNode).put(DATE, date); if (objNode.has(URL)) { EntityResourceReference entityResourceReference = - (EntityResourceReference) this.getReference(objNode.get(URL).asText()); + (EntityResourceReference) this.getResourceReferenceFromStringURL(objNode.get(URL).asText()); ((ObjectNode) objNode).put(LABEL, entityResourceReference.getEntityReference().getName()); arrayNode.add(objNode); - ((ObjectNode) objNode).put(DOCUMENT_REFERENCE, - entityResourceReference.getEntityReference().toString()); } } } @@ -138,15 +134,16 @@ private ArrayNode processObjectNode(JsonNode jsonNode) return arrayNode; } - private ResourceReference getReference(String url) - throws MalformedURLException, CreateResourceTypeException, UnsupportedResourceReferenceException, - CreateResourceReferenceException + private ResourceReference getResourceReferenceFromStringURL(String resourceReferenceURL) { - ResourceReference result = null; - ExtendedURL extendedURL = new ExtendedURL(new URL(url), null); - ResourceType resourceType = this.resourceTypeResolver.resolve(extendedURL, Collections.emptyMap()); - result = this.resourceReferenceResolver.resolve(extendedURL, resourceType, Collections.emptyMap()); - - return result; + try { + ExtendedURL extendedURL = new ExtendedURL(new URL(resourceReferenceURL), null); + ResourceType resourceType = this.resourceTypeResolver.resolve(extendedURL, Collections.emptyMap()); + return this.resourceReferenceResolver.resolve(extendedURL, resourceType, Collections.emptyMap()); + } catch (MalformedURLException | CreateResourceReferenceException | CreateResourceTypeException + | UnsupportedResourceReferenceException e) { + logger.warn("Failed to get resource reference from URL: " + resourceReferenceURL, e); + return null; + } } } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java index 6c79acec..346b9182 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java @@ -31,7 +31,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.xwiki.analytics.AnalyticsManager; -import com.xwiki.analytics.configuration.AnalyticsConfiguration; /** * The scripting service for the Analytics App (Pro). @@ -45,26 +44,22 @@ @Singleton public class AnalyticsScriptService implements ScriptService { - @Inject - private AnalyticsConfiguration configuration; @Inject @Named("Matomo") private AnalyticsManager analyticsManager; - /** * This Method is used to interrogate the Matomo API. * - * @param jsonNormaliserHint hint to select the json normaliser. - * @param parameterList a map of the parameters for the url + * @param jsonNormaliserHint A hint to select the JSON normalizer. This hint is needed because Matomo returns + * JSON in various formats. With this hint, I can switch the normalizer at runtime. + * @param parameters a map of the parameters for the url * @return will return a json string. */ - public JsonNode getDataFromRequest(Map parameterList, String jsonNormaliserHint) + public JsonNode getDataFromRequest(Map parameters, String jsonNormaliserHint) { - parameterList.put("idSite", configuration.getIdSite()); - parameterList.put("token_auth", configuration.getAuthenticationToken()); try { - return analyticsManager.requestData(configuration.getRequestAddress(), parameterList, jsonNormaliserHint); + return analyticsManager.requestData(parameters, jsonNormaliserHint); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/application-analytics-default/src/main/resources/META-INF/components.txt b/application-analytics-default/src/main/resources/META-INF/components.txt index ccb47c03..b2eedefd 100644 --- a/application-analytics-default/src/main/resources/META-INF/components.txt +++ b/application-analytics-default/src/main/resources/META-INF/components.txt @@ -1,5 +1,5 @@ com.xwiki.analytics.internal.configuration.AnalyticsConfigurationSource com.xwiki.analytics.internal.configuration.DefaultAnalyticsConfiguration com.xwiki.analytics.script.AnalyticsScriptService -com.xwiki.analytics.internal.configuration.MatomoAnalyticsManager -com.xwiki.analytics.internal.configuration.MostViewedJsonNormaliser +com.xwiki.analytics.internal.MatomoAnalyticsManager +com.xwiki.analytics.internal.MostViewedJsonNormaliser From 737c3c77dea20ea35aa2a1cae610df8a2fd8fb0d Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 10 Aug 2023 14:24:22 +0300 Subject: [PATCH 015/105] Move Most Viewed Pages Macro to livetable #10 * I modified how I get the normaliser --- .../internal/MatomoAnalyticsManager.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java index 796737e7..d91cc09a 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java @@ -56,8 +56,10 @@ @Unstable public class MatomoAnalyticsManager implements AnalyticsManager { + @Inject - private ComponentManager componentManager; + @Named("MostViewed") + private JsonNormaliser mostViwedNormaliser; @Inject private AnalyticsConfiguration configuration; @@ -75,7 +77,7 @@ public JsonNode requestData(Map parameters, String jsonNormalise { parameters.put("idSite", configuration.getIdSite()); parameters.put("token_auth", configuration.getAuthenticationToken()); - JsonNormaliser jsonNormaliser = componentManager.getInstance(JsonNormaliser.class, jsonNormaliserHint); + JsonNormaliser jsonNormaliser = this.getNormaliser(jsonNormaliserHint); HttpClient client = HttpClient.newHttpClient(); HttpRequest httpRequest = HttpRequest.newBuilder(buildURI(configuration.getRequestAddress(), parameters)) .build(); @@ -100,4 +102,14 @@ private URI buildURI(String address, Map parameterList) return uriBuilder.build(); } + private JsonNormaliser getNormaliser(String hint) + { + switch (hint) + { + case "MostViewed": + return this.mostViwedNormaliser; + default: + return null; + } + } } From 6b504947fa1bdac27eccf038da4c0266d09cfc24 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 10 Aug 2023 17:55:40 +0300 Subject: [PATCH 016/105] Move Most Viewed Pages Macro to livetable #10 * I added 2 tests and modified a few typos. --- application-analytics-default/pom.xml | 23 ++++ .../internal/MatomoAnalyticsManager.java | 5 +- .../internal/MostViewedJsonNormaliser.java | 12 +- .../MostViewedJsonNormaliserTest.java | 108 ++++++++++++++++++ .../DefaultAnalyticsConfigurationTest.java | 63 ++++++++++ 5 files changed, 204 insertions(+), 7 deletions(-) create mode 100644 application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java create mode 100644 application-analytics-default/src/test/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfigurationTest.java diff --git a/application-analytics-default/pom.xml b/application-analytics-default/pom.xml index 801a3131..9f2d5143 100644 --- a/application-analytics-default/pom.xml +++ b/application-analytics-default/pom.xml @@ -45,9 +45,32 @@ javax.ws.rs jsr311-api + + + org.xwiki.commons + xwiki-commons-tool-test-component + ${commons.version} + test + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + + org.apache.maven.plugins + maven-jar-plugin + + + **/test/**/*.class + + + org.apache.maven.plugins maven-compiler-plugin diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java index d91cc09a..faff16bc 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java @@ -33,7 +33,6 @@ import org.xwiki.component.annotation.Component; import org.xwiki.component.manager.ComponentLookupException; -import org.xwiki.component.manager.ComponentManager; import org.xwiki.resource.CreateResourceReferenceException; import org.xwiki.resource.CreateResourceTypeException; import org.xwiki.resource.UnsupportedResourceReferenceException; @@ -59,7 +58,7 @@ public class MatomoAnalyticsManager implements AnalyticsManager @Inject @Named("MostViewed") - private JsonNormaliser mostViwedNormaliser; + private JsonNormaliser mostViewedNormaliser; @Inject private AnalyticsConfiguration configuration; @@ -107,7 +106,7 @@ private JsonNormaliser getNormaliser(String hint) switch (hint) { case "MostViewed": - return this.mostViwedNormaliser; + return this.mostViewedNormaliser; default: return null; } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index 5a7701c5..71e7dec2 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -102,8 +102,10 @@ private void processArrayNode(JsonNode jsonNode) if (objNode.has(URL)) { EntityResourceReference entityResourceReference = (EntityResourceReference) this.getResourceReferenceFromStringURL(objNode.get(URL).asText()); - ((ObjectNode) objNode).put(LABEL, - entityResourceReference.getEntityReference().getName()); + if (entityResourceReference != null) { + ((ObjectNode) objNode).put(LABEL, + entityResourceReference.getEntityReference().getName()); + } } } } @@ -124,8 +126,10 @@ private ArrayNode processObjectNode(JsonNode jsonNode) if (objNode.has(URL)) { EntityResourceReference entityResourceReference = (EntityResourceReference) this.getResourceReferenceFromStringURL(objNode.get(URL).asText()); - ((ObjectNode) objNode).put(LABEL, - entityResourceReference.getEntityReference().getName()); + if (entityResourceReference != null) { + ((ObjectNode) objNode).put(LABEL, + entityResourceReference.getEntityReference().getName()); + } arrayNode.add(objNode); } } diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java new file mode 100644 index 00000000..3d4d7ef2 --- /dev/null +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java @@ -0,0 +1,108 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.analytics.internal; + +import java.net.URL; +import java.util.Collections; + +import org.junit.jupiter.api.Test; +import org.xwiki.resource.ResourceReferenceResolver; +import org.xwiki.resource.ResourceType; +import org.xwiki.resource.ResourceTypeResolver; +import org.xwiki.test.junit5.mockito.ComponentTest; +import org.xwiki.test.junit5.mockito.InjectMockComponents; +import org.xwiki.test.junit5.mockito.MockComponent; +import org.xwiki.url.ExtendedURL; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +@ComponentTest +public class MostViewedJsonNormaliserTest +{ + @InjectMockComponents + private MostViewedJsonNormaliser mostViewedJsonNormaliser; + + @MockComponent + private ResourceReferenceResolver resourceReferenceResolver; + + @MockComponent + private ResourceTypeResolver resourceTypeResolver; + + @Test + public void testObjectJSONResponse() throws Exception + { + setupMocks("http://localhost:8080/xwiki/bin/view/Analytics/", + "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage"); + assertEquals(responseObjectJSON(), mostViewedJsonNormaliser.normaliseData(getObjectJSON()).toString()); + } + + @Test + public void testArrayJSONResponse() throws Exception + { + setupMocks("http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages", + "http://localhost:8081/xwiki/bin/view/Main/"); + assertEquals(responseArrayJSON(), mostViewedJsonNormaliser.normaliseData(getArrayJSONS()).toString()); + } + + private void setupMocks(String url1, String url2) throws Exception + { + ExtendedURL extendedURL1 = new ExtendedURL(new URL(url1), null); + ExtendedURL extendedURL2 = new ExtendedURL(new URL(url2), null); + + ResourceType resourceType1 = resourceTypeResolver.resolve(extendedURL1, Collections.emptyMap()); + ResourceType resourceType2 = resourceTypeResolver.resolve(extendedURL2, Collections.emptyMap()); + + when(resourceReferenceResolver.resolve(extendedURL1, resourceType1, Collections.emptyMap())).thenReturn(null); + when(resourceReferenceResolver.resolve(extendedURL2, resourceType2, Collections.emptyMap())).thenReturn(null); + } + + private String getObjectJSON() + { + return "{\"2023-04\":[],\"2023-05\":[],\"2023-06\":[],\"2023-07\":[{\"label\":\"\\/xwiki\\/bin\\/view\\/Analy" + + "tics\\/Code\\/MostViwedPages\",\"url\":\"http:\\/\\/localhost:8080\\/xwiki\\/bin\\/view\\/Analytics\\/" + + "Code\\/MostViwedPages\"}],\"2023-08\":[{\"label\":\"\\/xwiki\\/bin\\/view\\/Analytics\\/Code\\/Macros\\" + + "/MostViewedPage\",\"url\":\"http:\\/\\/localhost:8080\\/xwiki\\/bin\\/view\\/Analytics\\/Code\\/Macros\\" + + "/MostViewedPage\"}]}" + + " "; + } + + private String responseObjectJSON() + { + return "[{\"label\":\"/xwiki/bin/view/Analytics/Code/MostViwedPages\",\"url\":\"http://localhost:8080/xwiki/" + + "bin/view/Analytics/Code/MostViwedPages\",\"date\":\"2023-07\"},{\"label\":\"/xwiki/bin/view/Analytics" + + "/Code/Macros/MostViewedPage\",\"url\":\"http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/Mo" + + "stViewedPage\",\"date\":\"2023-08\"}]"; + } + + private String getArrayJSONS() + { + return "[{\"label\":\"\\/xwiki\\/bin\\/view\\/Analytics\\/Code\\/MostViwedPages\",\"url\":\"http:\\/\\/" + + "localhost:8080\\/xwiki\\/bin\\/view\\/Analytics\\/Code\\/MostViwedPages\"},{\"label\":\"\\/xwiki" + + "\\/bin\\/view\\/Main\\/\",\"url\":\"http:\\/\\/localhost:8081\\/xwiki\\/bin\\/view\\/Main\\/\"}]"; + } + + private String responseArrayJSON() + { + return "[{\"label\":\"/xwiki/bin/view/Analytics/Code/MostViwedPages\"," + + "\"url\":\"http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages\",\"date\":\"\"},{\"label" + + "\":\"/xwiki/bin/view/Main/\",\"url\":\"http://localhost:8081/xwiki/bin/view/Main/\",\"date\":\"\"}]"; + } +} diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfigurationTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfigurationTest.java new file mode 100644 index 00000000..725132a3 --- /dev/null +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfigurationTest.java @@ -0,0 +1,63 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.analytics.internal.configuration; + +import javax.inject.Named; + +import org.junit.jupiter.api.Test; +import org.xwiki.configuration.ConfigurationSource; +import org.xwiki.test.junit5.mockito.ComponentTest; +import org.xwiki.test.junit5.mockito.InjectMockComponents; +import org.xwiki.test.junit5.mockito.MockComponent; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +@ComponentTest +public class DefaultAnalyticsConfigurationTest +{ + @InjectMockComponents + private DefaultAnalyticsConfiguration defaultAnalyticsConfiguration; + + @MockComponent + @Named("analytics") + private ConfigurationSource analyticsConfigurationSource; + + @Test + public void getAuthenticationTokenTest() + { + when(this.analyticsConfigurationSource.getProperty("authToken", "")).thenReturn("token"); + assertEquals("token", this.defaultAnalyticsConfiguration.getAuthenticationToken()); + } + + @Test + public void getSiteIDTokenTest() + { + when(this.analyticsConfigurationSource.getProperty("siteId", "")).thenReturn("3"); + assertEquals("3", this.defaultAnalyticsConfiguration.getIdSite()); + } + + @Test + public void getAddressTokenTest() + { + when(this.analyticsConfigurationSource.getProperty("requestAddress", "")).thenReturn("13.192.200.19"); + assertEquals("13.192.200.19", this.defaultAnalyticsConfiguration.getRequestAddress()); + } +} From 1cba67ed1e4778bd7f1f7c5f08c3ea6ea5cc8a93 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 10 Aug 2023 23:23:40 +0300 Subject: [PATCH 017/105] Move Most Viewed Pages Macro to livetable #10 * I added some functions. --- .../internal/MatomoAnalyticsManager.java | 15 ++++---- .../internal/MostViewedJsonNormaliser.java | 34 +++++++++---------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java index faff16bc..a01f9f48 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java @@ -55,10 +55,10 @@ @Unstable public class MatomoAnalyticsManager implements AnalyticsManager { - @Inject @Named("MostViewed") private JsonNormaliser mostViewedNormaliser; + @Inject private AnalyticsConfiguration configuration; @@ -77,6 +77,9 @@ public JsonNode requestData(Map parameters, String jsonNormalise parameters.put("idSite", configuration.getIdSite()); parameters.put("token_auth", configuration.getAuthenticationToken()); JsonNormaliser jsonNormaliser = this.getNormaliser(jsonNormaliserHint); + if (jsonNormaliser == null) { + throw new RuntimeException("The hint that you provided is invalid."); + } HttpClient client = HttpClient.newHttpClient(); HttpRequest httpRequest = HttpRequest.newBuilder(buildURI(configuration.getRequestAddress(), parameters)) .build(); @@ -101,14 +104,12 @@ private URI buildURI(String address, Map parameterList) return uriBuilder.build(); } + private JsonNormaliser getNormaliser(String hint) { - switch (hint) - { - case "MostViewed": - return this.mostViewedNormaliser; - default: - return null; + if (hint.equals("MostViewed")) { + return this.mostViewedNormaliser; } + return null; } } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index 71e7dec2..6a22e32a 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -85,13 +85,13 @@ public class MostViewedJsonNormaliser implements JsonNormaliser */ public JsonNode normaliseData(String jsonString) throws JsonProcessingException { - JsonNode jsonNode = OBJECT_MAPPER.readTree(jsonString); - if (jsonNode.isArray()) { - processArrayNode(jsonNode); + JsonNode jsonRoot = OBJECT_MAPPER.readTree(jsonString); + if (jsonRoot.isArray()) { + processArrayNode(jsonRoot); } else { - jsonNode = processObjectNode(jsonNode); + jsonRoot = processObjectNode(jsonRoot); } - return jsonNode; + return jsonRoot; } private void processArrayNode(JsonNode jsonNode) @@ -100,12 +100,7 @@ private void processArrayNode(JsonNode jsonNode) if (objNode.isObject()) { ((ObjectNode) objNode).put(DATE, ""); if (objNode.has(URL)) { - EntityResourceReference entityResourceReference = - (EntityResourceReference) this.getResourceReferenceFromStringURL(objNode.get(URL).asText()); - if (entityResourceReference != null) { - ((ObjectNode) objNode).put(LABEL, - entityResourceReference.getEntityReference().getName()); - } + this.handleURLNode((ObjectNode) objNode); } } } @@ -124,12 +119,7 @@ private ArrayNode processObjectNode(JsonNode jsonNode) if (objNode.isObject()) { ((ObjectNode) objNode).put(DATE, date); if (objNode.has(URL)) { - EntityResourceReference entityResourceReference = - (EntityResourceReference) this.getResourceReferenceFromStringURL(objNode.get(URL).asText()); - if (entityResourceReference != null) { - ((ObjectNode) objNode).put(LABEL, - entityResourceReference.getEntityReference().getName()); - } + this.handleURLNode((ObjectNode) objNode); arrayNode.add(objNode); } } @@ -150,4 +140,14 @@ private ResourceReference getResourceReferenceFromStringURL(String resourceRefer return null; } } + + private void handleURLNode(ObjectNode objNode) + { + EntityResourceReference entityResourceReference = + (EntityResourceReference) this.getResourceReferenceFromStringURL(objNode.get(URL).asText()); + if (entityResourceReference != null) { + objNode.put(LABEL, + entityResourceReference.getEntityReference().getName()); + } + } } From bd7ec804065b6b43c8f23f37577fb42ef648b121 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 11 Aug 2023 09:57:52 +0300 Subject: [PATCH 018/105] Move Most Viewed Pages Macro to livetable #10 * Renamed the MostViewed hint to MostViewedPages and modified the comments --- .../internal/MatomoAnalyticsManager.java | 4 ++-- .../internal/MostViewedJsonNormaliser.java | 20 ++++++++++++---- .../script/AnalyticsScriptService.java | 8 +++---- .../Code/Macros/MostViewedPageJSON.xml | 23 +++++++++---------- 4 files changed, 33 insertions(+), 22 deletions(-) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java index a01f9f48..bc0b749e 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java @@ -56,7 +56,7 @@ public class MatomoAnalyticsManager implements AnalyticsManager { @Inject - @Named("MostViewed") + @Named("MostViewedPages") private JsonNormaliser mostViewedNormaliser; @Inject @@ -107,7 +107,7 @@ private URI buildURI(String address, Map parameterList) private JsonNormaliser getNormaliser(String hint) { - if (hint.equals("MostViewed")) { + if (hint.equals("MostViewedPages")) { return this.mostViewedNormaliser; } return null; diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index 6a22e32a..348b5bf9 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -47,6 +47,8 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.xwiki.analytics.JsonNormaliser; +import liquibase.repackaged.org.apache.commons.lang3.exception.ExceptionUtils; + /** * The normaliser for the MostViewedMacro. * @@ -54,7 +56,7 @@ * @since 1.0 */ @Component -@Named("MostViewed") +@Named("MostViewedPages") @Singleton public class MostViewedJsonNormaliser implements JsonNormaliser { @@ -85,7 +87,18 @@ public class MostViewedJsonNormaliser implements JsonNormaliser */ public JsonNode normaliseData(String jsonString) throws JsonProcessingException { + // I need to convert the string returned by matomo in a JSON to easily handle the processing of the nodes. JsonNode jsonRoot = OBJECT_MAPPER.readTree(jsonString); + // Matomo may return several variants of JSON formats. In one scenario, when the period is set to + // day/week/month/year, it returns a JSON object with keys representing dates. The corresponding value for + // each key is an array of JSON objects, each of which represents a page. However, if the user sets the + // period parameter to "range" Matomo returns an array of JSON objects, with each JSON object representing + // a page. Due to these variations, I need to process the result from Matomo to create a normalized format. + // This normalized format is an array of JSON objects and each JSON object in this array will have a new + // field called 'date'. This 'date' field will be set to 'N/A' when Matomo returns an array instead of an + // object. In the 'processArrayNode' and 'processObjectNode' methods, I also modify the 'label' field to + // change it from the raw URL format to the page name. + if (jsonRoot.isArray()) { processArrayNode(jsonRoot); } else { @@ -114,8 +127,6 @@ private ArrayNode processObjectNode(JsonNode jsonNode) String date = fieldNames.next(); JsonNode childNode = jsonNode.get(date); for (JsonNode objNode : childNode) { - //This will add the date to the json itself in case the user uses another period=day/week/mont/year - // and will also add change the label to the name of the page. if (objNode.isObject()) { ((ObjectNode) objNode).put(DATE, date); if (objNode.has(URL)) { @@ -136,7 +147,8 @@ private ResourceReference getResourceReferenceFromStringURL(String resourceRefer return this.resourceReferenceResolver.resolve(extendedURL, resourceType, Collections.emptyMap()); } catch (MalformedURLException | CreateResourceReferenceException | CreateResourceTypeException | UnsupportedResourceReferenceException e) { - logger.warn("Failed to get resource reference from URL: " + resourceReferenceURL, e); + logger.warn("Failed to get resource reference from URL: [{}].", resourceReferenceURL, + ExceptionUtils.getRootCauseMessage(e)); return null; } } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java index 346b9182..3cf1e7e1 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java @@ -44,24 +44,24 @@ @Singleton public class AnalyticsScriptService implements ScriptService { - @Inject @Named("Matomo") private AnalyticsManager analyticsManager; + /** * This Method is used to interrogate the Matomo API. * * @param jsonNormaliserHint A hint to select the JSON normalizer. This hint is needed because Matomo returns - * JSON in various formats. With this hint, I can switch the normalizer at runtime. + * JSON in various formats. With this hint, I can switch the normalizer at runtime. * @param parameters a map of the parameters for the url * @return will return a json string. */ - public JsonNode getDataFromRequest(Map parameters, String jsonNormaliserHint) + public JsonNode getMatomoRequestResult(Map parameters, String jsonNormaliserHint) { try { return analyticsManager.requestData(parameters, jsonNormaliserHint); } catch (Exception e) { - throw new RuntimeException(e); + throw new RuntimeException("Failed to get data for " + jsonNormaliserHint, e); } } } diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml index 05c751e1..f8bf388a 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml @@ -53,15 +53,15 @@ #end ## #set ($keys = { - 'date' : 'date', - 'title' : 'label', - 'visits' : 'nb_visits', - 'hits' : 'nb_hits', - 'timeSpent' : 'sum_time_spent', - 'entryNbVisits' : 'entry_nb_visits', - 'entryNbActions' : 'entry_nb_actions', - 'bounceRate' : 'bounce_rate', - 'exitRate' : 'exit_rate' + 'date' : 'date', + 'title' : 'label', + 'visits' : 'nb_visits', + 'hits' : 'nb_hits', + 'timeSpent' : 'sum_time_spent', + 'entryNbVisits' : 'entry_nb_visits', + 'entryNbActions' : 'entry_nb_actions', + 'bounceRate' : 'bounce_rate', + 'exitRate' : 'exit_rate' }) #set ($sort = 'nb_hits') #set ($order = 'desc') @@ -69,8 +69,7 @@ #set($sort = $keys.get($request.sort)) #set($order = $request.dir) #end - #set ($parameters = - { + #set ($parameters = { 'period' : $request.period, 'date' : $request.date, 'module' : 'API', @@ -82,7 +81,7 @@ 'expanded' : '1', 'flat' : '1' }) - #set ($analyticsResult = $services.analytics.getDataFromRequest($parameters, 'MostViewed')) + #set ($analyticsResult = $services.analytics.getMatomoRequestResult($parameters, 'MostViewedPages')) #set ($results = { "totalrows": $analyticsResult.size(), "returnedrows": $analyticsResult.size(), From 6fcb82796e46dc5732d2d4a003f6f236dad36ef0 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 11 Aug 2023 11:22:36 +0300 Subject: [PATCH 019/105] Move Most Viewed Pages Macro to livetable #10 * Renamed 2 pages and added a test class for MatomoAnalyticsManager --- .../internal/MatomoAnalyticsManagerTest.java | 45 ++++ ...MostViewedPage.xml => MostViewedPages.xml} | 82 +++---- ...edPageJSON.xml => MostViewedPagesJSON.xml} | 222 +++++++++--------- 3 files changed, 197 insertions(+), 152 deletions(-) create mode 100644 application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java rename application-analytics-ui/src/main/resources/Analytics/Code/Macros/{MostViewedPage.xml => MostViewedPages.xml} (99%) rename application-analytics-ui/src/main/resources/Analytics/Code/Macros/{MostViewedPageJSON.xml => MostViewedPagesJSON.xml} (97%) diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java new file mode 100644 index 00000000..b97ea13d --- /dev/null +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java @@ -0,0 +1,45 @@ +package com.xwiki.analytics.internal; + +import java.io.IOException; +import java.util.HashMap; + +import javax.inject.Named; + +import org.junit.jupiter.api.Test; +import org.xwiki.component.manager.ComponentLookupException; +import org.xwiki.resource.CreateResourceReferenceException; +import org.xwiki.resource.CreateResourceTypeException; +import org.xwiki.resource.UnsupportedResourceReferenceException; +import org.xwiki.test.junit5.mockito.ComponentTest; +import org.xwiki.test.junit5.mockito.InjectMockComponents; +import org.xwiki.test.junit5.mockito.MockComponent; + +import com.xwiki.analytics.JsonNormaliser; +import com.xwiki.analytics.configuration.AnalyticsConfiguration; + +import static org.mockito.Mockito.when; + +@ComponentTest +public class MatomoAnalyticsManagerTest +{ + @InjectMockComponents + private MatomoAnalyticsManager matomoAnalyticsManager; + + @MockComponent + @Named("MostViewedPages") + private JsonNormaliser jsonNormaliser; + + @MockComponent + private AnalyticsConfiguration configuration; + + @Test + public void MatomoAnalyticsManagerTest() + throws ComponentLookupException, IOException, UnsupportedResourceReferenceException, InterruptedException, + CreateResourceTypeException, CreateResourceReferenceException + { + when(this.configuration.getAuthenticationToken()).thenReturn("token"); + when(this.configuration.getRequestAddress()).thenReturn("http://130.61.233.19//matomo"); + when(this.configuration.getIdSite()).thenReturn("3"); + this.matomoAnalyticsManager.requestData(new HashMap<>(), "MostViewedPages"); + } +} diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPage.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml similarity index 99% rename from application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPage.xml rename to application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index bdcfd074..04bda91b 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPage.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -240,47 +240,47 @@ 0 - {{velocity}} -#set ($dashbord = $doc.getObject('Analytics.Code.AnalyticsDashboardClass')) -#set ($period = 'range') -#if ($dashbord) - #set ($startDate = $dashbord.getProperty('startDate')) - #set ($endDate = $dashbord.getProperty('endDate')) -#else - #set ($startDate = $xcontext.macro.params.startDate.toString()) - #set ($endDate = $xcontext.macro.params.endDate.toString()) -#end -#set ($startDateObj = $datetool.toDate('yyyy/MM/dd HH:mm', $startDate)) -#set ($startDate = $datetool.format('yyyy-MM-dd', $startDateObj)) -#set ($endDateObj = $datetool.toDate('yyyyy/MM/dd HH:mm', $endDate)) -#set ($endDate = $datetool.format('yyyy-MM-dd', $endDateObj)) -#if (!$startDate) - #set ($startDate = '2007-01-01') -#end -#if (!$endDate) - #set ($endDate = $datetool.get('yyyy-MM-dd')) -#end -## Prepare the livetable with the fields and properties. -## Any suggestions on how to do this more cleanly? -#set ($parameters = '&date=' + $startDate + "," + $endDate + '&period=' + $period + '&limitEntries=-1') -#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbVisits', 'entryNbActions', 'bounceRate', 'exitRate','documentReference', 'URL', 'actions']) -#set ($columnsProperties = { - 'date' : { 'type' : 'text', 'sortable' : true }, - 'title' : { 'type' : 'text', 'sortable' : true }, - 'visits' : { 'type' : 'text', 'sortable' : true }, - 'hits' : { 'type' : 'text', 'sortable' : true }, - 'timeSpent' : { 'type' : 'text', 'sortable' : true }, - 'entryNbVisits' : { 'type' : 'text', 'sortable' : true }, - 'entryNbActions' : { 'type' : 'text', 'sortable' : true }, - 'bounceRate' : { 'type' : 'text', 'sortable' : true }, - 'exitRate' : { 'type' : 'text', 'sortable' : true } -}) -#set($options = { - 'translationPrefix': 'analytics.mostViewedPages.header.', - 'resultPage': 'Analytics.Code.Macros.MostViewedPageJSON', - 'extraParams': $parameters -}) -#livetable('mostViewedPages' $columns $columnsProperties $options) + {{velocity}} +#set ($dashbord = $doc.getObject('Analytics.Code.AnalyticsDashboardClass')) +#set ($period = 'range') +#if ($dashbord) + #set ($startDate = $dashbord.getProperty('startDate')) + #set ($endDate = $dashbord.getProperty('endDate')) +#else + #set ($startDate = $xcontext.macro.params.startDate.toString()) + #set ($endDate = $xcontext.macro.params.endDate.toString()) +#end +#set ($startDateObj = $datetool.toDate('yyyy/MM/dd HH:mm', $startDate)) +#set ($startDate = $datetool.format('yyyy-MM-dd', $startDateObj)) +#set ($endDateObj = $datetool.toDate('yyyyy/MM/dd HH:mm', $endDate)) +#set ($endDate = $datetool.format('yyyy-MM-dd', $endDateObj)) +#if (!$startDate) + #set ($startDate = '2007-01-01') +#end +#if (!$endDate) + #set ($endDate = $datetool.get('yyyy-MM-dd')) +#end +## Prepare the livetable with the fields and properties. +## Any suggestions on how to do this more cleanly? +#set ($parameters = '&date=' + $startDate + "," + $endDate + '&period=' + $period + '&limitEntries=-1') +#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbVisits', 'entryNbActions', 'bounceRate', 'exitRate','documentReference', 'URL', 'actions']) +#set ($columnsProperties = { + 'date' : { 'type' : 'text', 'sortable' : true }, + 'title' : { 'type' : 'text', 'sortable' : true }, + 'visits' : { 'type' : 'text', 'sortable' : true }, + 'hits' : { 'type' : 'text', 'sortable' : true }, + 'timeSpent' : { 'type' : 'text', 'sortable' : true }, + 'entryNbVisits' : { 'type' : 'text', 'sortable' : true }, + 'entryNbActions' : { 'type' : 'text', 'sortable' : true }, + 'bounceRate' : { 'type' : 'text', 'sortable' : true }, + 'exitRate' : { 'type' : 'text', 'sortable' : true } +}) +#set($options = { + 'translationPrefix': 'analytics.mostViewedPages.header.', + 'resultPage': 'Analytics.Code.Macros.MostViewedPageJSON', + 'extraParams': $parameters +}) +#livetable('mostViewedPages' $columns $columnsProperties $options) {{/velocity}} diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml similarity index 97% rename from application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml rename to application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml index f8bf388a..982b8c97 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml @@ -1,111 +1,111 @@ - - - - - - Analytics.Code.Macros - MostViewedPageJSON - - - 0 - xwiki:XWiki.Admin - WebHome - xwiki:XWiki.Admin - xwiki:XWiki.Admin - 1.1 - MostViewedPageJSON - - false - xwiki/2.1 - true - {{include reference='Analytics.Code.Macros.Macros'/}} -{{velocity}} -#template('attachment_macros.vm') -#template('hierarchy_macros.vm') -#if ($xcontext.action == 'get') - #set ($offset = $numbertool.toNumber($request.offset).intValue()) - ## The offset sent by the live table starts at 1. - #set ($offset = $offset - 1) - #if (!$offset || $offset < 0) - #set ($offset = 0) - #end - #set ($limit = $numbertool.toNumber($request.limit).intValue()) - #if (!$limit) - #set ($limit = 15) - #end - ## - #set ($keys = { - 'date' : 'date', - 'title' : 'label', - 'visits' : 'nb_visits', - 'hits' : 'nb_hits', - 'timeSpent' : 'sum_time_spent', - 'entryNbVisits' : 'entry_nb_visits', - 'entryNbActions' : 'entry_nb_actions', - 'bounceRate' : 'bounce_rate', - 'exitRate' : 'exit_rate' - }) - #set ($sort = 'nb_hits') - #set ($order = 'desc') - #if($request.sort && $request.reqNo!='1') - #set($sort = $keys.get($request.sort)) - #set($order = $request.dir) - #end - #set ($parameters = { - 'period' : $request.period, - 'date' : $request.date, - 'module' : 'API', - 'method' : 'Actions.getPageUrls', - 'format' : 'json', - 'filter_limit' : $request.limitEntries, - 'filter_sort_column' : $sort, - 'filter_sort_order' : $order, - 'expanded' : '1', - 'flat' : '1' - }) - #set ($analyticsResult = $services.analytics.getMatomoRequestResult($parameters, 'MostViewedPages')) - #set ($results = { - "totalrows": $analyticsResult.size(), - "returnedrows": $analyticsResult.size(), - "offset": $mathtool.add($offset, 1), - "reqNo": $numbertool.toNumber($request.reqNo).intValue(), - "rows": [] - }) - #foreach($currentEntry in $analyticsResult) - #set ($url = $currentEntry.get('url').asText()) - #set ($discard = $results.rows.add( - { - 'date' : $currentEntry.get('date').asText(), - 'title' : $currentEntry.get('label').asText(), - 'visits' : $currentEntry.get('nb_visits').asText(), - 'hits' : $currentEntry.get('nb_hits').asText(), - 'timeSpent' : $currentEntry.get('sum_time_spent').asText(), - 'entryNbVisits' : $currentEntry.get('entry_nb_visits').asText(), - 'entryNbActions' : $currentEntry.get('entry_nb_actions').asText(), - 'bounceRate' : $currentEntry.get('bounce_rate').asText(), - 'exitRate' : $currentEntry.get('exit_rate').asText() - })) - #end - #jsonResponse($results) -#end -{{/velocity}} - - + + + + + + Analytics.Code.Macros + MostViewedPageJSON + + + 0 + xwiki:XWiki.Admin + WebHome + xwiki:XWiki.Admin + xwiki:XWiki.Admin + 1.1 + MostViewedPageJSON + + false + xwiki/2.1 + true + {{include reference='Analytics.Code.Macros.Macros'/}} +{{velocity}} +#template('attachment_macros.vm') +#template('hierarchy_macros.vm') +#if ($xcontext.action == 'get') + #set ($offset = $numbertool.toNumber($request.offset).intValue()) + ## The offset sent by the live table starts at 1. + #set ($offset = $offset - 1) + #if (!$offset || $offset < 0) + #set ($offset = 0) + #end + #set ($limit = $numbertool.toNumber($request.limit).intValue()) + #if (!$limit) + #set ($limit = 15) + #end + ## + #set ($keys = { + 'date' : 'date', + 'title' : 'label', + 'visits' : 'nb_visits', + 'hits' : 'nb_hits', + 'timeSpent' : 'sum_time_spent', + 'entryNbVisits' : 'entry_nb_visits', + 'entryNbActions' : 'entry_nb_actions', + 'bounceRate' : 'bounce_rate', + 'exitRate' : 'exit_rate' + }) + #set ($sort = 'nb_hits') + #set ($order = 'desc') + #if($request.sort && $request.reqNo!='1') + #set($sort = $keys.get($request.sort)) + #set($order = $request.dir) + #end + #set ($parameters = { + 'period' : $request.period, + 'date' : $request.date, + 'module' : 'API', + 'method' : 'Actions.getPageUrls', + 'format' : 'json', + 'filter_limit' : $request.limitEntries, + 'filter_sort_column' : $sort, + 'filter_sort_order' : $order, + 'expanded' : '1', + 'flat' : '1' + }) + #set ($analyticsResult = $services.analytics.getMatomoRequestResult($parameters, 'MostViewedPages')) + #set ($results = { + "totalrows": $analyticsResult.size(), + "returnedrows": $analyticsResult.size(), + "offset": $mathtool.add($offset, 1), + "reqNo": $numbertool.toNumber($request.reqNo).intValue(), + "rows": [] + }) + #foreach($currentEntry in $analyticsResult) + #set ($url = $currentEntry.get('url').asText()) + #set ($discard = $results.rows.add( + { + 'date' : $currentEntry.get('date').asText(), + 'title' : $currentEntry.get('label').asText(), + 'visits' : $currentEntry.get('nb_visits').asText(), + 'hits' : $currentEntry.get('nb_hits').asText(), + 'timeSpent' : $currentEntry.get('sum_time_spent').asText(), + 'entryNbVisits' : $currentEntry.get('entry_nb_visits').asText(), + 'entryNbActions' : $currentEntry.get('entry_nb_actions').asText(), + 'bounceRate' : $currentEntry.get('bounce_rate').asText(), + 'exitRate' : $currentEntry.get('exit_rate').asText() + })) + #end + #jsonResponse($results) +#end +{{/velocity}} + + From 692ef5c811937a5ca02009eca81bb4f68f66432d Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 11 Aug 2023 12:11:15 +0300 Subject: [PATCH 020/105] [MISC FIX] *Changed the name of a file --- .../Code/Macros/{MostViewedPages.xml => MostViewedPage.xml} | 0 .../Macros/{MostViewedPagesJSON.xml => MostViewedPageJSON.xml} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename application-analytics-ui/src/main/resources/Analytics/Code/Macros/{MostViewedPages.xml => MostViewedPage.xml} (100%) rename application-analytics-ui/src/main/resources/Analytics/Code/Macros/{MostViewedPagesJSON.xml => MostViewedPageJSON.xml} (100%) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPage.xml similarity index 100% rename from application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml rename to application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPage.xml diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml similarity index 100% rename from application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml rename to application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml From f84fdfad5a3b715ed8c3d86edc707c171cfda46c Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 11 Aug 2023 12:14:07 +0300 Subject: [PATCH 021/105] Removed a file --- .../Analytics/Code/Macros/MostViewedPages.xml | 821 ------------------ 1 file changed, 821 deletions(-) delete mode 100644 application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml deleted file mode 100644 index 45f0b529..00000000 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml +++ /dev/null @@ -1,821 +0,0 @@ - - - - - - Analytics.Code.Macros - MostViewedPages - - - 0 - xwiki:XWiki.Admin - Analytics.Code.Macros.WebHome - xwiki:XWiki.Admin - xwiki:XWiki.Admin - 1.1 - MostViewedPages - - false - xwiki/2.1 - true - {{mostViewedPages}} - - Analytics.Code.Macros.MostViewedPages - 0 - XWiki.JavaScriptExtension - 5b6bb6f7-68aa-437e-9c20-a36dcb3ea9de - - XWiki.JavaScriptExtension - - - - - - - - - 0 - long - 0 - select - forbidden - 0 - 0 - cache - 5 - Caching policy - 0 - - |, - 1 - 0 - long|short|default|forbid - com.xpn.xwiki.objects.classes.StaticListClass - - - PureText - 0 - PureText - code - 2 - Code - 20 - 50 - 0 - com.xpn.xwiki.objects.classes.TextAreaClass - - - 0 - name - 1 - Name - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - 0 - select - yesno - parse - 4 - Parse content - 0 - com.xpn.xwiki.objects.classes.BooleanClass - - - 0 - 0 - select - forbidden - 0 - 0 - use - 3 - Use this extension - 0 - - |, - 1 - 0 - currentPage|onDemand|always - com.xpn.xwiki.objects.classes.StaticListClass - - - - long - - - define('mostViewedPagesTranslation', { - prefix: 'analytics.mostViewedPages.header.', - keys: [ - 'year', - 'title', - 'visits', - 'hits', - 'timeSpent', - 'entryNbVisits', - 'entryNbActions', - 'bounceRate', - 'exitRate' - ] -}); - -require(['jquery', 'xwiki-l10n!mostViewedPagesTranslation'], function($, l10n) { - - function constructUrl(element) { - const paramString = $.param({ - module: 'API', - method: 'Actions.getPageTitles', - idSite: element.dataset.idSite, - period: element.dataset.period, - date: element.dataset.date, - format: 'json', - filter_sort_column: 'nb_hits', - filter_sort_order: 'desc', - token_auth: element.dataset.authToken - }); - return element.dataset.ipMatomo + 'index.php?' + paramString; - }; - - function createHeader(headersTranslations) { - let headerRow = $('<tr class="table"></tr>'); - headersTranslations.forEach(header => { - let headerCell = $(`<th class='xwiki-mostviewedpages-header'>${l10n[header]}</th>`); - headerRow.append(headerCell); - }); - return headerRow[0]; - }; - - function createRowFromData(headers, dataItem, index) { - let row = $('<tr></tr>'); - headers.forEach(header => { - let cell = $(`<td class='xwiki-mostviewedpages-cell'> ${dataItem[header] !== undefined ? dataItem[header] : ""} - </td>`); - row.append(cell); - }); - return row[0]; - }; - /** - * This function handles the case where you get the data unified or receive data for a specific year. The function - * iterates through all the data and creates new rows. - */ - function createMergedYearsRows(fragment, headers, data) { - data.forEach((item, index) => { - let row = createRowFromData(headers, item, index); - fragment.appendChild(row); - }); - }; - - /** - * The data is organized as a JSON of JSONs, where each internal JSON represents data for a specific year. - */ - function createMultipleYearsRows(fragment, headers, data) { - for (let year in data) { - data[year].forEach((item, index) => { - // Add 'year' to each item. - // At some point, we might want to unify the data from different years. For the time being, I don't know how - // Matomo handles data from multiple years. - // The fragmentation is caused by matomo when you use the period argument with the value of month, year or day, - // you can unify the data by using period=range. - item.year = year; - let row = createRowFromData(headers, item, index); - fragment.appendChild(row); - }); - } - }; - - function createTable(container) { - let table = document.createElement('table'); - let thead = document.createElement('thead'); - let tbody = document.createElement('tbody'); - let fragment = document.createDocumentFragment(); - - table.classList.add('table'); - table.classList.add('table-hover'); - table.classList.add('table-striped'); - - let url = constructUrl(container); - $.getJSON(url).done(function (data) { - // If the response is a json of jsons then we select the one with the year. - let headers = Array.isArray(data) - ? ['title', 'visits', 'hits', 'timeSpent', 'entryNbVisits', 'entryNbActions', 'bounceRate', 'exitRate'] - : ['year', 'title', 'visits', 'hits', 'timeSpent', 'entryNbVisits', 'entryNbActions', 'bounceRate', 'exitRate']; - let jsonKeys = Array.isArray(data) - //The keys that matomo will return - ? ['label', 'nb_visits', 'nb_hits', 'sum_time_spent', 'entry_nb_visits', 'entry_nb_actions', 'bounce_rate', 'exit_rate'] - : ['year', 'label', 'nb_visits', 'nb_hits', 'sum_time_spent', 'entry_nb_visits', 'entry_nb_actions', 'bounce_rate', 'exit_rate']; - thead.appendChild(createHeader(headers)); - if (Array.isArray(data)) { - createMergedYearsRows(fragment, jsonKeys, data); - } else { - createMultipleYearsRows(fragment, jsonKeys, data); - } - tbody.appendChild(fragment); - table.appendChild(thead); - table.appendChild(tbody); - container.appendChild(table); - }).fail(function(jqxhr, textStatus, error) { - let err = textStatus + ', ' + error; - console.log('Request Failed: ' + err); - new XWiki.widgets.Notification(l10n.get('error', error), 'error'); - }); - }; - - $(function() { - let containers = document.querySelectorAll('.mostViewedPages'); - containers.forEach((container, index) => { - createTable(container); - }); - }); -}); - - - - - - - - - - onDemand - - - - Analytics.Code.Macros.MostViewedPages - 0 - XWiki.StyleSheetExtension - d76078ba-f6aa-498b-9d7b-5b221b9c3e30 - - XWiki.StyleSheetExtension - - - - - - - - - 0 - long - 0 - select - forbidden - 0 - 0 - cache - 5 - Caching policy - 0 - - |, - 1 - 0 - long|short|default|forbid - com.xpn.xwiki.objects.classes.StaticListClass - - - PureText - 0 - PureText - code - 2 - Code - 20 - 50 - 0 - com.xpn.xwiki.objects.classes.TextAreaClass - - - 0 - 0 - select - forbidden - 0 - 0 - contentType - 6 - Content Type - 0 - - |, - 1 - 0 - CSS|LESS - com.xpn.xwiki.objects.classes.StaticListClass - - - 0 - name - 1 - Name - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - 0 - select - yesno - parse - 4 - Parse content - 0 - com.xpn.xwiki.objects.classes.BooleanClass - - - 0 - 0 - select - forbidden - 0 - 0 - use - 3 - Use this extension - 0 - - |, - 1 - 0 - currentPage|onDemand|always - com.xpn.xwiki.objects.classes.StaticListClass - - - - long - - - #template('colorThemeInit.vm') -.xwiki-mostviewedpages-cell { - padding: 10px; - border-bottom: 1px solid #ddd; -} -.xwiki-mostviewedpages-header { - background-color: $theme.menuBackgroundColor; - color: $theme.textColor; - padding:10px; -} - - - - CSS - - - - - - 1 - - - currentPage - - - - Analytics.Code.Macros.MostViewedPages - 0 - XWiki.WikiMacroClass - 46181f3c-348a-441c-8066-514759808b3b - - XWiki.WikiMacroClass - - - - - - - - - 0 - 0 - select - - async_cached - 13 - Cached - 0 - com.xpn.xwiki.objects.classes.BooleanClass - - - 0 - 0 - select - forbidden - 0 - 1 - async_context - 14 - Context elements - 0 - , - |, - 5 - 0 - action=Action|doc.reference=Document|icon.theme=Icon theme|locale=Language|rendering.defaultsyntax=Default syntax|rendering.restricted=Restricted|rendering.targetsyntax=Target syntax|request.base=Request base URL|request.parameters=Request parameters|request.url=Request URL|request.wiki=Request wiki|user=User|wiki=Wiki - com.xpn.xwiki.objects.classes.StaticListClass - - - 0 - 0 - select - - async_enabled - 12 - Asynchronous rendering - 0 - com.xpn.xwiki.objects.classes.BooleanClass - - - 0 - Text - code - 10 - Macro code - 20 - 40 - 0 - com.xpn.xwiki.objects.classes.TextAreaClass - - - PureText - 0 - PureText - contentDescription - 9 - Content description (Not applicable for "No content" type) - 5 - 40 - 0 - com.xpn.xwiki.objects.classes.TextAreaClass - - - 0 - Unknown - 0 - input - allowed - 1 - 0 - contentJavaType - 8 - 1 - Macro content type - 0 - | - | - 1 - 0 - Unknown|Wiki - com.xpn.xwiki.objects.classes.StaticListClass - - - 0 - 0 - select - forbidden - 0 - 0 - contentType - 7 - Macro content availability - 0 - | - | - 1 - 0 - Optional|Mandatory|No content - com.xpn.xwiki.objects.classes.StaticListClass - - - 0 - defaultCategory - 4 - Default category - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - PureText - 0 - PureText - description - 3 - Macro description - 5 - 40 - 0 - com.xpn.xwiki.objects.classes.TextAreaClass - - - 0 - id - 1 - Macro id - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - 0 - name - 2 - Macro name - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - 0 - priority - 11 - integer - Priority - 10 - 0 - com.xpn.xwiki.objects.classes.NumberClass - - - 0 - select - yesno - supportsInlineMode - 5 - Supports inline mode - 0 - com.xpn.xwiki.objects.classes.BooleanClass - - - 0 - 0 - select - forbidden - 0 - 0 - visibility - 6 - Macro visibility - 0 - | - | - 1 - 0 - Current User|Current Wiki|Global - com.xpn.xwiki.objects.classes.StaticListClass - - - - 0 - - - - - - 0 - - - {{velocity}} -#set ($discard = $xwiki.jsx.use('Analytics.Code.Macros.MostViewedPages')) -#set ($object = $xwiki.getDocument('Analytics.Code.Configuration').getObject('Analytics.Code.ConfigurationClass')) -#set ($ip = $object.getProperty('requestAddress').value) -#set ($siteId = $object.getProperty('siteId').value) -#set ($authToken = $object.getProperty('authToken').value) -#set ($dashbord = $doc.getObject('Analytics.Code.AnalyticsDashboardClass')) -#set ($period = 'range') -## A dashboard will have a date inside and that date will take precedence over the date set by the macro. -#if ($dashbord) - #set ($startDate = $dashbord.getProperty('startDate')) - #set ($endTimeDate = $dashbord.getProperty('endDate')) -#else - #set ($startDate = $xcontext.macro.params.startDate.toString()) - #set ($endTimeDate = $xcontext.macro.params.endDate.toString()) -#end -#set ($rows = $xcontext.macro.params.numberOfRows) -#set ($startDateObj = $datetool.toDate('yyyy/MM/dd HH:MM', $startDate)) -#set ($startDate = $datetool.format('yyyy-MM-dd', $startDateObj)) -#set ($endDateObj = $datetool.toDate('yyyy/MM/dd HH:MM', $endDate)) -#set ($endDate = $datetool.format('yyyy-MM-dd', $endDateObj)) -## Check if startDate or endDate are not defined and replace them with some default values, in order to match the end of -## the time interval. For startDate we use '2007-01-01' since it's the oldest one accepted by Matomo. -#if (!$startDate) - #set ($startDate = '2007-01-01') -#end -#if (!$endDate) - #set ($endDate = $datetool.get('yyyy-MM-dd')) -#end -#set ($date = $startDate + ',' + $endDate) - -{{html}} - <div class="mostViewedPages" data-ip-matomo=$ip data-period=$period data-id-site=$siteId data-date=$date - data-auth-token=$authToken> - <div> -{{/html}} - -{{/velocity}} - - - - - - Wiki - - - No content - - - - - - This macro will make a table of the Most Viewed Pages. - - - mostViewedPages - - - Most Viewed Pages - - - - - - 1 - - - Current Wiki - - - - Analytics.Code.Macros.MostViewedPages - 0 - XWiki.WikiMacroParameterClass - a8584a41-4f35-44f7-ab71-7f7e87245f8a - - XWiki.WikiMacroParameterClass - - - - - - - - - 0 - defaultValue - 4 - Parameter default value - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - 0 - description - 2 - Parameter description - 5 - 40 - 0 - com.xpn.xwiki.objects.classes.TextAreaClass - - - 0 - select - yesno - mandatory - 3 - Parameter mandatory - 0 - com.xpn.xwiki.objects.classes.BooleanClass - - - 0 - name - 1 - Parameter name - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - 0 - type - 5 - Parameter type - 60 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - - - - - - - - 0 - - - startDate - - - java.util.Date - - - - Analytics.Code.Macros.MostViewedPages - 1 - XWiki.WikiMacroParameterClass - cf126ff8-9c6a-46f2-bfd0-4885352a960b - - XWiki.WikiMacroParameterClass - - - - - - - - - 0 - defaultValue - 4 - Parameter default value - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - 0 - description - 2 - Parameter description - 5 - 40 - 0 - com.xpn.xwiki.objects.classes.TextAreaClass - - - 0 - select - yesno - mandatory - 3 - Parameter mandatory - 0 - com.xpn.xwiki.objects.classes.BooleanClass - - - 0 - name - 1 - Parameter name - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - 0 - type - 5 - Parameter type - 60 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - - - - - - - - 0 - - - endDate - - - java.util.Date - - - From 301b284a5a54d5063bed61c5d92172478e8eed22 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 11 Aug 2023 13:02:43 +0300 Subject: [PATCH 022/105] Move Most Viewed Pages Macro to livetable #10 * I added a test for the MatomoANalyticsManager --- .../internal/MatomoAnalyticsManagerTest.java | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java index b97ea13d..ae6b3012 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java @@ -1,3 +1,22 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ package com.xwiki.analytics.internal; import java.io.IOException; @@ -18,6 +37,7 @@ import com.xwiki.analytics.configuration.AnalyticsConfiguration; import static org.mockito.Mockito.when; +import static org.junit.jupiter.api.Assertions.assertEquals; @ComponentTest public class MatomoAnalyticsManagerTest @@ -38,8 +58,8 @@ public void MatomoAnalyticsManagerTest() CreateResourceTypeException, CreateResourceReferenceException { when(this.configuration.getAuthenticationToken()).thenReturn("token"); - when(this.configuration.getRequestAddress()).thenReturn("http://130.61.233.19//matomo"); + when(this.configuration.getRequestAddress()).thenReturn("http://130.61.233.19/matomo"); when(this.configuration.getIdSite()).thenReturn("3"); - this.matomoAnalyticsManager.requestData(new HashMap<>(), "MostViewedPages"); + assertEquals(null, this.matomoAnalyticsManager.requestData(new HashMap<>(), "MostViewedPages")); } } From 98e3e5223eac218a8653e385150f17f0fdedeab4 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Mon, 14 Aug 2023 12:20:24 +0300 Subject: [PATCH 023/105] Move Most Viewed Pages Macro to livetable #10 * I added new error messages and I updated the comments ona few functions. --- .../configuration/AnalyticsConfiguration.java | 8 +-- application-analytics-default/pom.xml | 11 ++++ .../internal/MatomoAnalyticsManager.java | 3 +- .../internal/MostViewedJsonNormaliser.java | 29 ++++++++- .../DefaultAnalyticsConfiguration.java | 8 ++- .../script/AnalyticsScriptService.java | 12 ++-- .../internal/MatomoAnalyticsManagerTest.java | 7 ++- .../MostViewedJsonNormaliserTest.java | 60 ++++++++----------- .../DefaultAnalyticsConfigurationTest.java | 9 ++- .../src/test/resources/tests.json | 56 +++++++++++++++++ .../Analytics/Code/AnalyticsTranslations.xml | 17 +----- ...MostViewedPage.xml => MostViewedPages.xml} | 31 ++++++---- ...edPageJSON.xml => MostViewedPagesJSON.xml} | 11 ++-- pom.xml | 2 +- 14 files changed, 180 insertions(+), 84 deletions(-) create mode 100644 application-analytics-default/src/test/resources/tests.json rename application-analytics-ui/src/main/resources/Analytics/Code/Macros/{MostViewedPage.xml => MostViewedPages.xml} (94%) rename application-analytics-ui/src/main/resources/Analytics/Code/Macros/{MostViewedPageJSON.xml => MostViewedPagesJSON.xml} (94%) diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java b/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java index 66fa68f0..c17ed84f 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java @@ -23,7 +23,7 @@ import org.xwiki.stability.Unstable; /** - * Analytics Configuration. + * Configuration for the Analytics Application. * * @version $Id$ * @since 1.0 @@ -33,17 +33,17 @@ public interface AnalyticsConfiguration { /** - * @return Returns the address where the requests will be made. + * @return the address where the requests will be made. */ String getRequestAddress(); /** - * @return Returns the id of the site that we want to see the statistics for. + * @return the id of the site that we want to see the statistics for. */ String getIdSite(); /** - * @return Returns the Authentication Token that permits to access the statistics. + * @return the Authentication Token that permits to access the statistics. */ String getAuthenticationToken(); } diff --git a/application-analytics-default/pom.xml b/application-analytics-default/pom.xml index 9f2d5143..114add86 100644 --- a/application-analytics-default/pom.xml +++ b/application-analytics-default/pom.xml @@ -52,6 +52,17 @@ ${commons.version} test + + + org.glassfish.jersey.core + jersey-server + 2.30.1 + + + org.glassfish.jersey.core + jersey-client + 2.30.1 + diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java index bc0b749e..2f0ebbd0 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java @@ -78,7 +78,8 @@ public JsonNode requestData(Map parameters, String jsonNormalise parameters.put("token_auth", configuration.getAuthenticationToken()); JsonNormaliser jsonNormaliser = this.getNormaliser(jsonNormaliserHint); if (jsonNormaliser == null) { - throw new RuntimeException("The hint that you provided is invalid."); + throw new RuntimeException("Error occurred while retrieving Matomo statistic results. There is no JSON" + + " normalizer associated with the hint you provided."); } HttpClient client = HttpClient.newHttpClient(); HttpRequest httpRequest = HttpRequest.newBuilder(buildURI(configuration.getRequestAddress(), parameters)) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index 348b5bf9..9ad55fdf 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -48,7 +48,6 @@ import com.xwiki.analytics.JsonNormaliser; import liquibase.repackaged.org.apache.commons.lang3.exception.ExceptionUtils; - /** * The normaliser for the MostViewedMacro. * @@ -98,7 +97,6 @@ public JsonNode normaliseData(String jsonString) throws JsonProcessingException // field called 'date'. This 'date' field will be set to 'N/A' when Matomo returns an array instead of an // object. In the 'processArrayNode' and 'processObjectNode' methods, I also modify the 'label' field to // change it from the raw URL format to the page name. - if (jsonRoot.isArray()) { processArrayNode(jsonRoot); } else { @@ -107,6 +105,12 @@ public JsonNode normaliseData(String jsonString) throws JsonProcessingException return jsonRoot; } + /** + * Handle the scenario where Matomo returns an array of JSON objects. This function processes each entry to append + * an empty date to it. + * + * @param jsonNode an array of jsons + */ private void processArrayNode(JsonNode jsonNode) { for (JsonNode objNode : jsonNode) { @@ -119,6 +123,15 @@ private void processArrayNode(JsonNode jsonNode) } } + /** + * Handle the scenario where Matomo returns an object with dates as keys and arrays of JSON objects as values. This + * function extracts the date from the key and adds it to each page. Ultimately, it returns an array of JSON objects + * instead of a single JSON object. + * + * @param jsonNode json object + * @return array of jsons + */ + private ArrayNode processObjectNode(JsonNode jsonNode) { ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); @@ -139,6 +152,13 @@ private ArrayNode processObjectNode(JsonNode jsonNode) return arrayNode; } + /** + * Process the URL of a page to obtain the documentReference. This is necessary to retrieve the document name for + * display when rendering the table. + * + * @param resourceReferenceURL the url of the page + * @return + */ private ResourceReference getResourceReferenceFromStringURL(String resourceReferenceURL) { try { @@ -153,6 +173,11 @@ private ResourceReference getResourceReferenceFromStringURL(String resourceRefer } } + /** + * Will change the label with the actual name of the page. + * + * @param objNode a json object + */ private void handleURLNode(ObjectNode objNode) { EntityResourceReference entityResourceReference = diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java index d237ace9..5d2f8c48 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java @@ -19,6 +19,8 @@ */ package com.xwiki.analytics.internal.configuration; +import java.text.MessageFormat; + import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; @@ -64,6 +66,10 @@ public String getAuthenticationToken() private T getProperty(String key, T defaultValue) { - return this.configDocument.getProperty(key, defaultValue); + T value = this.configDocument.getProperty(key, defaultValue); + if (value.equals(defaultValue)) { + throw new RuntimeException(MessageFormat.format("The {0} is missing", key)); + } + return value; } } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java index 3cf1e7e1..5d7f9041 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java @@ -33,7 +33,7 @@ import com.xwiki.analytics.AnalyticsManager; /** - * The scripting service for the Analytics App (Pro). + * Script service for the Analytics Application. * * @version $Id$ * @since 1.1 @@ -49,12 +49,12 @@ public class AnalyticsScriptService implements ScriptService private AnalyticsManager analyticsManager; /** - * This Method is used to interrogate the Matomo API. + * Get data from Matomo API, in a format specific to the macro that will use it. * - * @param jsonNormaliserHint A hint to select the JSON normalizer. This hint is needed because Matomo returns - * JSON in various formats. With this hint, I can switch the normalizer at runtime. - * @param parameters a map of the parameters for the url - * @return will return a json string. + * @param jsonNormaliserHint hint specific to the component that will normalize the Matomo result response, since + * it's given / resulted format depends on the context were is used. + * @param parameters a map of the parameters needed for this request + * @return response from Matomo API, in a normalized JSON format */ public JsonNode getMatomoRequestResult(Map parameters, String jsonNormaliserHint) { diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java index ae6b3012..03e2c5af 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java @@ -36,9 +36,14 @@ import com.xwiki.analytics.JsonNormaliser; import com.xwiki.analytics.configuration.AnalyticsConfiguration; -import static org.mockito.Mockito.when; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; +/** + * Unit test for {@link MatomoAnalyticsManager} + * + * @version $Id$ + */ @ComponentTest public class MatomoAnalyticsManagerTest { diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java index 3d4d7ef2..75ec5334 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java @@ -19,6 +19,8 @@ */ package com.xwiki.analytics.internal; +import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.util.Collections; @@ -31,9 +33,18 @@ import org.xwiki.test.junit5.mockito.MockComponent; import org.xwiki.url.ExtendedURL; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.stream.JsonReader; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; +/** + * Unit test for {@link MatomoAnalyticsManager} + * + * @version $Id$ + */ @ComponentTest public class MostViewedJsonNormaliserTest { @@ -46,23 +57,29 @@ public class MostViewedJsonNormaliserTest @MockComponent private ResourceTypeResolver resourceTypeResolver; + private static JsonNode node; + @Test public void testObjectJSONResponse() throws Exception { - setupMocks("http://localhost:8080/xwiki/bin/view/Analytics/", + readJSONS(); + setupURLs("http://localhost:8080/xwiki/bin/view/Analytics/", "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage"); - assertEquals(responseObjectJSON(), mostViewedJsonNormaliser.normaliseData(getObjectJSON()).toString()); + assertEquals(node.get("ResponseObjectJSON"), + mostViewedJsonNormaliser.normaliseData(node.get("ObjectJSON").toString())); } @Test public void testArrayJSONResponse() throws Exception { - setupMocks("http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages", + readJSONS(); + setupURLs("http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages", "http://localhost:8081/xwiki/bin/view/Main/"); - assertEquals(responseArrayJSON(), mostViewedJsonNormaliser.normaliseData(getArrayJSONS()).toString()); + assertEquals(node.get("ResponseArrayJSON"), + mostViewedJsonNormaliser.normaliseData(node.get("ArrayJSONS").toString())); } - private void setupMocks(String url1, String url2) throws Exception + private void setupURLs(String url1, String url2) throws Exception { ExtendedURL extendedURL1 = new ExtendedURL(new URL(url1), null); ExtendedURL extendedURL2 = new ExtendedURL(new URL(url2), null); @@ -74,35 +91,10 @@ private void setupMocks(String url1, String url2) throws Exception when(resourceReferenceResolver.resolve(extendedURL2, resourceType2, Collections.emptyMap())).thenReturn(null); } - private String getObjectJSON() - { - return "{\"2023-04\":[],\"2023-05\":[],\"2023-06\":[],\"2023-07\":[{\"label\":\"\\/xwiki\\/bin\\/view\\/Analy" - + "tics\\/Code\\/MostViwedPages\",\"url\":\"http:\\/\\/localhost:8080\\/xwiki\\/bin\\/view\\/Analytics\\/" - + "Code\\/MostViwedPages\"}],\"2023-08\":[{\"label\":\"\\/xwiki\\/bin\\/view\\/Analytics\\/Code\\/Macros\\" - + "/MostViewedPage\",\"url\":\"http:\\/\\/localhost:8080\\/xwiki\\/bin\\/view\\/Analytics\\/Code\\/Macros\\" - + "/MostViewedPage\"}]}" - + " "; - } - - private String responseObjectJSON() - { - return "[{\"label\":\"/xwiki/bin/view/Analytics/Code/MostViwedPages\",\"url\":\"http://localhost:8080/xwiki/" - + "bin/view/Analytics/Code/MostViwedPages\",\"date\":\"2023-07\"},{\"label\":\"/xwiki/bin/view/Analytics" - + "/Code/Macros/MostViewedPage\",\"url\":\"http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/Mo" - + "stViewedPage\",\"date\":\"2023-08\"}]"; - } - - private String getArrayJSONS() - { - return "[{\"label\":\"\\/xwiki\\/bin\\/view\\/Analytics\\/Code\\/MostViwedPages\",\"url\":\"http:\\/\\/" - + "localhost:8080\\/xwiki\\/bin\\/view\\/Analytics\\/Code\\/MostViwedPages\"},{\"label\":\"\\/xwiki" - + "\\/bin\\/view\\/Main\\/\",\"url\":\"http:\\/\\/localhost:8081\\/xwiki\\/bin\\/view\\/Main\\/\"}]"; - } - - private String responseArrayJSON() + private void readJSONS() throws IOException { - return "[{\"label\":\"/xwiki/bin/view/Analytics/Code/MostViwedPages\"," - + "\"url\":\"http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages\",\"date\":\"\"},{\"label" - + "\":\"/xwiki/bin/view/Main/\",\"url\":\"http://localhost:8081/xwiki/bin/view/Main/\",\"date\":\"\"}]"; + ObjectMapper objectMapper = new ObjectMapper(); + InputStream is = JsonReader.class.getResourceAsStream("/tests.json"); + node = objectMapper.readTree(is); } } diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfigurationTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfigurationTest.java index 725132a3..d0fbd6d3 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfigurationTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfigurationTest.java @@ -30,6 +30,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; +/** + * Unit test for {@link DefaultAnalyticsConfiguration} + * + * @version $Id$ + */ @ComponentTest public class DefaultAnalyticsConfigurationTest { @@ -48,14 +53,14 @@ public void getAuthenticationTokenTest() } @Test - public void getSiteIDTokenTest() + public void getSiteIDTest() { when(this.analyticsConfigurationSource.getProperty("siteId", "")).thenReturn("3"); assertEquals("3", this.defaultAnalyticsConfiguration.getIdSite()); } @Test - public void getAddressTokenTest() + public void getAddressTest() { when(this.analyticsConfigurationSource.getProperty("requestAddress", "")).thenReturn("13.192.200.19"); assertEquals("13.192.200.19", this.defaultAnalyticsConfiguration.getRequestAddress()); diff --git a/application-analytics-default/src/test/resources/tests.json b/application-analytics-default/src/test/resources/tests.json new file mode 100644 index 00000000..4c044f95 --- /dev/null +++ b/application-analytics-default/src/test/resources/tests.json @@ -0,0 +1,56 @@ +{ + "ObjectJSON": { + "2023-04": [ + ], + "2023-05": [ + ], + "2023-06": [ + ], + "2023-07": [ + { + "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" + } + ], + "2023-08": [ + { + "label": "/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ] + }, + "ResponseObjectJSON": [ + { + "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages", + "date": "2023-07" + }, + { + "label": "/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "date": "2023-08" + } + ], + "ArrayJSONS": [ + { + "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" + }, + { + "label": "/xwiki/bin/view/Main/", + "url": "http://localhost:8081/xwiki/bin/view/Main/" + } + ], + "ResponseArrayJSON": [ + { + "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages", + "date": "" + }, + { + "label": "/xwiki/bin/view/Main/", + "url": "http://localhost:8081/xwiki/bin/view/Main/", + "date": "" + } + ] +} \ No newline at end of file diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml index aa0e5ba5..477a81b6 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml @@ -48,8 +48,10 @@ Analytics.Code.AnalyticsDashboardClass_startDate.hint=Start time of the date int ## Most Viewed Pages Macro analytics.mostViewedPages.header.bounceRate=Bounce rate +analytics.mostViewedPages.header.date=Date analytics.mostViewedPages.header.entryNbVisits=No. of views that started on this page analytics.mostViewedPages.header.entryNbActions= No. of actions +analytics.mostViewedPages.header.emptyvalue=- analytics.mostViewedPages.header.exitRate=Exit rate analytics.mostViewedPages.header.hits=Page views analytics.mostViewedPages.header.timeSpent=Time spent @@ -70,19 +72,6 @@ Analytics.Code.ConfigurationClass_requestAddress.hint=The Matomo URL. This can b Analytics.Code.ConfigurationClass_trackingCode=Tracking code Analytics.Code.ConfigurationClass_trackingCode.hint=Tracking Code received from Matomo. The 'u' variable might be different than the one that is initially generated by Matomo if the server you want to track is on another machine. To fix this, go to the integration tab, copy the Matomo URL, and replace the value of 'u' with the Matomo URL. -## Most Viewed Pages Macro -analytics.mostViewedPages.header.bounceRate=Bounce rate -analytics.mostViewedPages.header.date=Date -analytics.mostViewedPages.header.entryNbVisits=No. of views that started on this page -analytics.mostViewedPages.header.entryNbActions= No. of actions -analytics.mostViewedPages.header.emptyvalue=- -analytics.mostViewedPages.header.exitRate=Exit rate -analytics.mostViewedPages.header.hits=Page views -analytics.mostViewedPages.header.timeSpent=Time spent -analytics.mostViewedPages.header.title=Page title -analytics.mostViewedPages.header.visits=Unique visitors -analytics.mostViewedPages.header.year=Year - ## Live table keys analytics.livetable.emptyvalue=Not Defined analytics.livetable.endDate=End Date @@ -131,4 +120,4 @@ analytics.extension.title=Analytics WIKI - \ No newline at end of file + diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPage.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml similarity index 94% rename from application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPage.xml rename to application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index 04bda91b..cae53787 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPage.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -20,25 +20,25 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - + Analytics.Code.Macros - MostViewedPage + MostViewedPages 0 xwiki:XWiki.Admin - Analytics.Code.Macros.WebHome + WebHome xwiki:XWiki.Admin xwiki:XWiki.Admin 1.1 - MostViewedPage + MostViewedPages false xwiki/2.1 true - {{mostViewedPage startDate="2023/08/08 16:20" endDate="2023/08/08 16:20"/}} + {{mostViewedPage/}} - Analytics.Code.Macros.MostViewedPage + Analytics.Code.Macros.MostViewedPages 0 XWiki.WikiMacroClass dc943965-6af8-4905-8508-9b8c248c3930 @@ -243,6 +243,7 @@ {{velocity}} #set ($dashbord = $doc.getObject('Analytics.Code.AnalyticsDashboardClass')) #set ($period = 'range') +## A dashboard will have a date inside and that date will take precedence over the date set by the macro. #if ($dashbord) #set ($startDate = $dashbord.getProperty('startDate')) #set ($endDate = $dashbord.getProperty('endDate')) @@ -252,8 +253,10 @@ #end #set ($startDateObj = $datetool.toDate('yyyy/MM/dd HH:mm', $startDate)) #set ($startDate = $datetool.format('yyyy-MM-dd', $startDateObj)) -#set ($endDateObj = $datetool.toDate('yyyyy/MM/dd HH:mm', $endDate)) +#set ($endDateObj = $datetool.toDate('yyyy/MM/dd HH:mm', $endDate)) #set ($endDate = $datetool.format('yyyy-MM-dd', $endDateObj)) +## Check if startDate or endDate are not defined and replace them with some default values, in order to match the end of +## the time interval. For startDate we use '2007-01-01' since it's the oldest one accepted by Matomo. #if (!$startDate) #set ($startDate = '2007-01-01') #end @@ -261,8 +264,12 @@ #set ($endDate = $datetool.get('yyyy-MM-dd')) #end ## Prepare the livetable with the fields and properties. -## Any suggestions on how to do this more cleanly? -#set ($parameters = '&date=' + $startDate + "," + $endDate + '&period=' + $period + '&limitEntries=-1') +#set ($date = $startDate + ',' + $endDate) +#set ($parameters = $escapetool.url({ + 'date': $date, + 'period': $$period, + 'limitEntries' : '-1' +})) #set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbVisits', 'entryNbActions', 'bounceRate', 'exitRate','documentReference', 'URL', 'actions']) #set ($columnsProperties = { 'date' : { 'type' : 'text', 'sortable' : true }, @@ -277,7 +284,7 @@ }) #set($options = { 'translationPrefix': 'analytics.mostViewedPages.header.', - 'resultPage': 'Analytics.Code.Macros.MostViewedPageJSON', + 'resultPage': 'Analytics.Code.Macros.MostViewedPagesJSON', 'extraParams': $parameters }) #livetable('mostViewedPages' $columns $columnsProperties $options) @@ -315,7 +322,7 @@ - Analytics.Code.Macros.MostViewedPage + Analytics.Code.Macros.MostViewedPages 0 XWiki.WikiMacroParameterClass 6e3cae3f-3127-4fb2-a590-5dce2f4be0d0 @@ -393,7 +400,7 @@ - Analytics.Code.Macros.MostViewedPage + Analytics.Code.Macros.MostViewedPages 1 XWiki.WikiMacroParameterClass 8a9fb4a7-d7e2-4d7d-84b8-8afb71b4606d diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml similarity index 94% rename from application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml rename to application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml index 982b8c97..71206d28 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPageJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml @@ -20,9 +20,9 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - + Analytics.Code.Macros - MostViewedPageJSON + MostViewedPagesJSON 0 @@ -31,12 +31,12 @@ xwiki:XWiki.Admin xwiki:XWiki.Admin 1.1 - MostViewedPageJSON + MostViewedPagesJSON false xwiki/2.1 true - {{include reference='Analytics.Code.Macros.Macros'/}} + {{velocity}} #template('attachment_macros.vm') #template('hierarchy_macros.vm') @@ -91,8 +91,7 @@ }) #foreach($currentEntry in $analyticsResult) #set ($url = $currentEntry.get('url').asText()) - #set ($discard = $results.rows.add( - { + #set ($discard = $results.rows.add({ 'date' : $currentEntry.get('date').asText(), 'title' : $currentEntry.get('label').asText(), 'visits' : $currentEntry.get('nb_visits').asText(), diff --git a/pom.xml b/pom.xml index 315f80d2..4b19451b 100644 --- a/pom.xml +++ b/pom.xml @@ -49,8 +49,8 @@ HEAD - application-analytics-ui application-analytics-api application-analytics-default + application-analytics-ui \ No newline at end of file From b440066b000d72a465a06ffa38871c5b36f08230 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Mon, 14 Aug 2023 13:03:52 +0300 Subject: [PATCH 024/105] Move Most Viewed Pages Macro to livetable #10 * Removed a template --- .../resources/Analytics/Code/Macros/MostViewedPagesJSON.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml index 71206d28..70539303 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml @@ -38,8 +38,6 @@ true {{velocity}} -#template('attachment_macros.vm') -#template('hierarchy_macros.vm') #if ($xcontext.action == 'get') #set ($offset = $numbertool.toNumber($request.offset).intValue()) ## The offset sent by the live table starts at 1. From c658148c7d60766c68c49107d7de123ff0972752 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Mon, 14 Aug 2023 19:03:38 +0300 Subject: [PATCH 025/105] Row Evolution Feature for the Most Viewed Pages #15 * I added the JSON normalizer to transform the Matomo result into a format that can be easily processed in JavaScript for the Chart.js library. I also added two new Velocity macros and the JS code for displaying the evolution. --- .../internal/MatomoAnalyticsManager.java | 14 +- .../internal/RowEvolutionJsonNormaliser.java | 92 +++++++ .../main/resources/META-INF/components.txt | 1 + .../Analytics/Code/AnalyticsTranslations.xml | 2 + .../Analytics/Code/Macros/MostViewedPages.xml | 226 +++++++++++++++++- .../Code/Macros/MostViewedPagesJSON.xml | 8 +- .../Code/Macros/RowEvolutionJSON.xml | 52 ++++ .../Analytics/Code/Macros/VelocityMacros.xml | 116 +++++++++ 8 files changed, 501 insertions(+), 10 deletions(-) create mode 100644 application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java create mode 100644 application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJSON.xml create mode 100644 application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java index 2f0ebbd0..674b84a5 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java @@ -59,6 +59,10 @@ public class MatomoAnalyticsManager implements AnalyticsManager @Named("MostViewedPages") private JsonNormaliser mostViewedNormaliser; + @Inject + @Named("RowEvolution") + private JsonNormaliser rowEvolution; + @Inject private AnalyticsConfiguration configuration; @@ -108,9 +112,13 @@ private URI buildURI(String address, Map parameterList) private JsonNormaliser getNormaliser(String hint) { - if (hint.equals("MostViewedPages")) { - return this.mostViewedNormaliser; + switch (hint) { + case "MostViewedPages": + return this.mostViewedNormaliser; + case "RowEvolution": + return this.rowEvolution; + default: + return null; } - return null; } } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java new file mode 100644 index 00000000..4caa03a9 --- /dev/null +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java @@ -0,0 +1,92 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.analytics.internal; + +import java.net.MalformedURLException; +import java.util.Iterator; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.xwiki.component.annotation.Component; +import org.xwiki.stability.Unstable; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.xwiki.analytics.JsonNormaliser; + +/** + * The normaliser for the RowEvolution. + * + * @version $Id$ + * @since 1.0 + */ +@Component +@Named("RowEvolution") +@Unstable +@Singleton +public class RowEvolutionJsonNormaliser implements JsonNormaliser +{ + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private static final String DATE = "date"; + + /** + * This method will normalise the jsons returned by Matomo into a single format. + * + * @param jsonString the json provided by Matomo. + * @return the normalised json as a JsonNode. + */ + public JsonNode normaliseData(String jsonString) throws JsonProcessingException, MalformedURLException + { + JsonNode jsonNode = OBJECT_MAPPER.readTree(jsonString); + return processObjectNode(jsonNode); + } + + /** + * Transforming the json object returned by Matomo into an array of jsons to make it easier to use in javascript. + * + * @param jsonNode the JSON node to be processed. + * @return the processed and transformed JSON node. + * @throws JsonProcessingException if any error occurs during JSON processing. + */ + private ArrayNode processObjectNode(JsonNode jsonNode) throws JsonProcessingException + { + ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); + Iterator fieldNames = jsonNode.fieldNames(); + + while (fieldNames.hasNext()) { + String date = fieldNames.next(); + JsonNode childNode = jsonNode.get(date); + JsonNode node = childNode.get(0); + + if (node == null) { + node = OBJECT_MAPPER.readTree(String.format("{\"%s\" : \"%s\"}", DATE, date)); + } else { + ((ObjectNode) node).put(DATE, date); + } + arrayNode.add(node); + } + return arrayNode; + } +} diff --git a/application-analytics-default/src/main/resources/META-INF/components.txt b/application-analytics-default/src/main/resources/META-INF/components.txt index b2eedefd..0dfe6d69 100644 --- a/application-analytics-default/src/main/resources/META-INF/components.txt +++ b/application-analytics-default/src/main/resources/META-INF/components.txt @@ -3,3 +3,4 @@ com.xwiki.analytics.internal.configuration.DefaultAnalyticsConfiguration com.xwiki.analytics.script.AnalyticsScriptService com.xwiki.analytics.internal.MatomoAnalyticsManager com.xwiki.analytics.internal.MostViewedJsonNormaliser +com.xwiki.analytics.internal.RowEvolutionJsonNormaliser diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml index 477a81b6..f44612f5 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml @@ -47,6 +47,7 @@ Analytics.Code.AnalyticsDashboardClass_startDate=Start date Analytics.Code.AnalyticsDashboardClass_startDate.hint=Start time of the date interval used on all statistics of this dashboard. ## Most Viewed Pages Macro +analytics.mostViewedPages.header.actions=Actions analytics.mostViewedPages.header.bounceRate=Bounce rate analytics.mostViewedPages.header.date=Date analytics.mostViewedPages.header.entryNbVisits=No. of views that started on this page @@ -54,6 +55,7 @@ analytics.mostViewedPages.header.entryNbActions= No. of actions analytics.mostViewedPages.header.emptyvalue=- analytics.mostViewedPages.header.exitRate=Exit rate analytics.mostViewedPages.header.hits=Page views +analytics.mostViwedMacros.action.action.label.rowEvolution=RowEvolution analytics.mostViewedPages.header.timeSpent=Time spent analytics.mostViewedPages.header.title=Page title analytics.mostViewedPages.header.visits=Unique visitors diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index cae53787..f7d7202b 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -37,6 +37,221 @@ xwiki/2.1 true {{mostViewedPage/}} + + Analytics.Code.Macros.MostViewedPages + 0 + XWiki.JavaScriptExtension + c906186c-ff5f-40d4-bdbb-95ab83de3344 + + XWiki.JavaScriptExtension + + + + + + + + + 0 + long + 0 + select + forbidden + 0 + 0 + cache + 5 + Caching policy + 0 + + |, + 1 + 0 + long|short|default|forbid + com.xpn.xwiki.objects.classes.StaticListClass + + + PureText + 0 + PureText + code + 2 + Code + 20 + 50 + 0 + com.xpn.xwiki.objects.classes.TextAreaClass + + + 0 + name + 1 + Name + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + select + yesno + parse + 4 + Parse content + 0 + com.xpn.xwiki.objects.classes.BooleanClass + + + 0 + 0 + select + forbidden + 0 + 0 + use + 3 + Use this extension + 0 + + |, + 1 + 0 + currentPage|onDemand|always + com.xpn.xwiki.objects.classes.StaticListClass + + + + long + + + require.config({ + paths: { + 'chartjs': 'https://cdn.jsdelivr.net/npm/chart.js/dist/Chart.min', + } +}); + +require(['jquery', 'chartjs'], ($) => { + const PERIODS = ["Day", "Week", "Month", "Year"]; + let chart; + + const renderChart = (data) => { + const dates = data.map(item => item.date); + const nbHits = data.map(item => item[$('#field').val()] || 0); + const ctx = $('#chart-display'); + chart = new Chart(ctx, { + type: 'line', + data: { + labels: dates, + datasets: [{ + label: '', + data: nbHits, + borderColor: 'rgba(75, 192, 192, 1)', + backgroundColor: 'rgba(75, 192, 192, 0.2)', + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + } + } + } + }); + $('#chartModal').modal('show'); + }; + + const fetchChartData = (url) => { + $('#chartModal').data('url', url); + const period = $('#periodSelect').val().toLowerCase(); + const date = $('#date').data('date'); + console.log(date); + $.getJSON(`RowEvolutionJSON?url=${url}&period=${period}&date=last${date}`) + .done((data) => { + window.sessionStorage.setItem('analyticsLastRequestCache', JSON.stringify(data)); + renderChart(data); + }) + .fail((jqxhr, textStatus, error) => { + const err = `${textStatus}, ${error}`; + console.error(`Request Failed: ${err}`); + }); + }; + + const updateVisibility = (period) => { + PERIODS.forEach(p => { + p === period ? $(`#periodDuration${p}`).show() : $(`#periodDuration${p}`).hide(); + }); + $('#date').data('date', $(`#periodDuration${period}`).val()); + }; + + const updateOnChange = () => { + PERIODS.forEach(p => { + $(`#periodDuration${p}`).on('change', function () { + $('#date').data('date', $(`#periodDuration${p}`).val()); + fetchChartData($('#chartModal').data('url')); + }); + }); + }; + + const restOptions = () => { + $('#chartModal').on('hidden.bs.modal', function () { + PERIODS.forEach(p => { + $(`#periodDuration${p}`).prop('selectedIndex', 0); + }); + $('#field').prop('selectedIndex', 0); + }); + }; + + const displayStatisitcs = () => { + $('#field').on('change', function () { + renderChart(JSON.parse(window.sessionStorage.getItem('analyticsLastRequestCache'))); + }); + }; + + const bindRowEvolutionDisplay = () => { + $(document).on('click', '.displayMostViewedPagesActions', function () { + updateVisibility('Day'); + const url = $(this).data('url'); + $('#periodSelect').prop('selectedIndex', 0); + $('#date').data('date', '30'); + fetchChartData(url); + }); + }; + + const bindPeriodChangeEvent = () => { + $('#periodSelect').on('change', function () { + const period = $(this).val(); + restOptions(); + updateVisibility(period); + fetchChartData($('#chartModal').data('url')); + }); + }; + + const bindEvents = () => { + updateOnChange(); + restOptions(); + displayStatisitcs(); + bindPeriodChangeEvent(); + bindRowEvolutionDisplay(); + }; + + $(document).ready(() => { + updateVisibility('Day'); + bindEvents(); + }); +}); + + + + + + + 0 + + + onDemand + + Analytics.Code.Macros.MostViewedPages 0 @@ -240,7 +455,10 @@ 0 - {{velocity}} + {{include reference='Analytics.Code.Macros.VelocityMacros'/}} +{{velocity}} +#set ($discard =$xwiki.jsx.use('Analytics.Code.Macros.MostViewedPages')) +#set ($discard =$xwiki.ssx.use('Analytics.Code.Macros.MostViewedPages')) #set ($dashbord = $doc.getObject('Analytics.Code.AnalyticsDashboardClass')) #set ($period = 'range') ## A dashboard will have a date inside and that date will take precedence over the date set by the macro. @@ -270,7 +488,7 @@ 'period': $$period, 'limitEntries' : '-1' })) -#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbVisits', 'entryNbActions', 'bounceRate', 'exitRate','documentReference', 'URL', 'actions']) +#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbVisits', 'entryNbActions', 'bounceRate', 'exitRate', 'actions']) #set ($columnsProperties = { 'date' : { 'type' : 'text', 'sortable' : true }, 'title' : { 'type' : 'text', 'sortable' : true }, @@ -280,7 +498,8 @@ 'entryNbVisits' : { 'type' : 'text', 'sortable' : true }, 'entryNbActions' : { 'type' : 'text', 'sortable' : true }, 'bounceRate' : { 'type' : 'text', 'sortable' : true }, - 'exitRate' : { 'type' : 'text', 'sortable' : true } + 'exitRate' : { 'type' : 'text', 'sortable' : true }, + 'actions' : {'type': 'text', 'html':'true', 'sortable' : false, 'filterable' : false} }) #set($options = { 'translationPrefix': 'analytics.mostViewedPages.header.', @@ -288,6 +507,7 @@ 'extraParams': $parameters }) #livetable('mostViewedPages' $columns $columnsProperties $options) +#modalRowEvolution() {{/velocity}} diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml index 71206d28..2bf75da2 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml @@ -36,10 +36,8 @@ false xwiki/2.1 true - + {{include reference='Analytics.Code.Macros.VelocityMacros'/}} {{velocity}} -#template('attachment_macros.vm') -#template('hierarchy_macros.vm') #if ($xcontext.action == 'get') #set ($offset = $numbertool.toNumber($request.offset).intValue()) ## The offset sent by the live table starts at 1. @@ -90,6 +88,7 @@ "rows": [] }) #foreach($currentEntry in $analyticsResult) + #set ($url = $currentEntry.get('url').asText()) #set ($url = $currentEntry.get('url').asText()) #set ($discard = $results.rows.add({ 'date' : $currentEntry.get('date').asText(), @@ -100,7 +99,8 @@ 'entryNbVisits' : $currentEntry.get('entry_nb_visits').asText(), 'entryNbActions' : $currentEntry.get('entry_nb_actions').asText(), 'bounceRate' : $currentEntry.get('bounce_rate').asText(), - 'exitRate' : $currentEntry.get('exit_rate').asText() + 'exitRate' : $currentEntry.get('exit_rate').asText(), + 'actions' : "#displayMostViewedPageActions($url)" })) #end #jsonResponse($results) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJSON.xml new file mode 100644 index 00000000..ca539983 --- /dev/null +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJSON.xml @@ -0,0 +1,52 @@ + + + + + + Analytics.Code.Macros + RowEvolutionJSON + + + 0 + xwiki:XWiki.Admin + Analytics.Code.Macros.VelocityMacros + xwiki:XWiki.Admin + xwiki:XWiki.Admin + 1.1 + RowEvolutionJSON + + false + xwiki/2.1 + true + {{velocity}} +#set ($parameters ={ + 'period' : $request.period, + 'date' : $request.date, + 'module' : 'API', + 'method' : 'Actions.getPageUrl', + 'format' : 'json', + 'pageUrl' : $request.url +}) +#set ($analyticsResult = $services.analytics.getMatomoRequestResult($parameters, 'RowEvolution')) +#jsonResponse($analyticsResult) +{{/velocity}} + + diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml new file mode 100644 index 00000000..87261c42 --- /dev/null +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -0,0 +1,116 @@ + + + + + + Analytics.Code.Macros + VelocityMacros + + + 0 + xwiki:XWiki.Admin + Analytics.WebHome + xwiki:XWiki.Admin + xwiki:XWiki.Admin + 1.1 + VelocityMacros + + false + xwiki/2.1 + true + {{velocity}} +#macro(displayMostViewedPageActions $url) +<div class="displayMostViewedPagesActions" data-url=$escapetool.url($url)> + <a class="move-attachment action" title="$escapetool.xml($services.localization.render('analytics.mostViwedMacros.action.rowEvolution'))" href="#"> + <span class="action-icon">$services.icon.renderHTML('eye')</span><span class="action-label">$escapetool.xml($services.localization.render('analytics.mostViwedMacros.action.action.label.rowEvolution'))</span> + </a> +</div> +#end + +#macro(modalRowEvolution) +{{html clean=false}} +<div class="modal fade" id="chartModal" tabindex="-1" role="dialog" aria-labelledby="chartModalLabel" aria-hidden="true"> + <div class="modal-dialog modal-lg"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">&times;</span> + </button> + <h4 class="modal-title" id="chartModalLabel">Row Evolution</h4> + </div> + <div class="modal-body"> + <label for="periodSelect">Period</label> + <select id="periodSelect" class="form-control"> + <option value="Day">Day</option> + <option value="Week">Week</option> + <option value="Month">Month</option> + <option value="Year">Year</option> + </select> + <div id="date"> + <select id="periodDurationDay" class="form-control"> + <option value="30">30</option> + <option value="8">8</option> + <option value="60">60</option> + <option value="90">90</option> + <option value="180">180</option> + <option value="365">365</option> + <option value="500">500</option> + </select> + <select id="periodDurationWeek" class="form-control"> + <option value="4">4</option> + <option value="12">12</option> + <option value="26">26</option> + <option value="52">52</option> + <option value="104">104</option> + <option value="500">500</option> + </select> + <select id="periodDurationMonth" class="form-control"> + <option value="3">3</option> + <option value="6">6</option> + <option value="12">12</option> + <option value="24">24</option> + <option value="36">36</option> + <option value="120">120</option> + </select> + <select id="periodDurationYear" class="form-control"> + <option value="3">3</option> + <option value="5">5</option> + <option value="10">10</option> + </select> + </div> + <label for="field">Field</label> + <select id="field" class="form-control"> + <option value="nb_hits">Pageviews</option> + <option value="nb_visits">Unique Pageviewes</option> + <option value="avg_time_on_page">Avg. time on page</option> + </select> + <canvas id="chart-display"></canvas> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> + </div> + </div> + </div> +</div> +{{/html}} +#end +{{/velocity}} + From 18b7f1731c6ac112e2f951f02cc01387095db9bf Mon Sep 17 00:00:00 2001 From: Farcasut Date: Wed, 16 Aug 2023 11:30:03 +0300 Subject: [PATCH 026/105] Row Evolution Feature for the Most Viewed Pages #15 * I added a test for the RowEvolutionJsonNormaliser and added comments to the javascript code. --- .../RowEvolutionJsonNormaliserTest.java | 43 ++++ .../src/test/resources/tests.json | 200 +++++++++++++----- .../Analytics/Code/Macros/MostViewedPages.xml | 25 ++- 3 files changed, 217 insertions(+), 51 deletions(-) create mode 100644 application-analytics-default/src/test/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliserTest.java diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliserTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliserTest.java new file mode 100644 index 00000000..271c6e1f --- /dev/null +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliserTest.java @@ -0,0 +1,43 @@ +package com.xwiki.analytics.internal; + +import java.io.IOException; +import java.io.InputStream; + +import org.junit.jupiter.api.Test; +import org.xwiki.test.junit5.mockito.ComponentTest; +import org.xwiki.test.junit5.mockito.InjectMockComponents; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.stream.JsonReader; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Unit test for {@link RowEvolutionJsonNormaliserTest} + * + * @version $Id$ + */ +@ComponentTest +public class RowEvolutionJsonNormaliserTest +{ + @InjectMockComponents + private RowEvolutionJsonNormaliser rowEvolutionJsonNormaliser; + private static JsonNode node; + + private void readJSONS() throws IOException + { + ObjectMapper objectMapper = new ObjectMapper(); + InputStream is = JsonReader.class.getResourceAsStream("/tests.json"); + node = objectMapper.readTree(is); + } + + @Test + public void testNormalisation() throws IOException + { + readJSONS(); + assertEquals(node.get("RowEvolutionTestObjectResponse"), + rowEvolutionJsonNormaliser.normaliseData(node.get("RowEvolutionTestObject").toString())); + + } +} diff --git a/application-analytics-default/src/test/resources/tests.json b/application-analytics-default/src/test/resources/tests.json index 4c044f95..1a41f536 100644 --- a/application-analytics-default/src/test/resources/tests.json +++ b/application-analytics-default/src/test/resources/tests.json @@ -1,56 +1,156 @@ { - "ObjectJSON": { - "2023-04": [ - ], - "2023-05": [ - ], - "2023-06": [ - ], - "2023-07": [ + "ObjectJSON":{ + "2023-04":[ + + ], + "2023-05":[ + + ], + "2023-06":[ + + ], + "2023-07":[ + { + "label":"/xwiki/bin/view/Analytics/Code/MostViwedPages", + "url":"http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" + } + ], + "2023-08":[ + { + "label":"/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "url":"http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ] + }, + "ResponseObjectJSON":[ { - "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", - "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" + "label":"/xwiki/bin/view/Analytics/Code/MostViwedPages", + "url":"http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages", + "date":"2023-07" + }, + { + "label":"/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "url":"http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "date":"2023-08" } - ], - "2023-08": [ + ], + "ArrayJSONS":[ + { + "label":"/xwiki/bin/view/Analytics/Code/MostViwedPages", + "url":"http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" + }, + { + "label":"/xwiki/bin/view/Main/", + "url":"http://localhost:8081/xwiki/bin/view/Main/" + } + ], + "ResponseArrayJSON":[ + { + "label":"/xwiki/bin/view/Analytics/Code/MostViwedPages", + "url":"http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages", + "date":"" + }, { - "label": "/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", - "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + "label":"/xwiki/bin/view/Main/", + "url":"http://localhost:8081/xwiki/bin/view/Main/", + "date":"" } - ] - }, - "ResponseObjectJSON": [ - { - "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", - "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages", - "date": "2023-07" - }, - { - "label": "/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", - "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", - "date": "2023-08" - } - ], - "ArrayJSONS": [ - { - "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", - "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" - }, - { - "label": "/xwiki/bin/view/Main/", - "url": "http://localhost:8081/xwiki/bin/view/Main/" - } - ], - "ResponseArrayJSON": [ - { - "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", - "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages", - "date": "" - }, - { - "label": "/xwiki/bin/view/Main/", - "url": "http://localhost:8081/xwiki/bin/view/Main/", - "date": "" - } - ] + ], + "RowEvolutionTestObject":{ + "2023-08-07":[ + { + "label":"/MostViewedPage", + "nb_visits":2, + "nb_uniq_visitors":1, + "nb_hits":2, + "sum_time_spent":96, + "avg_time_on_page":48, + "bounce_rate":"0%", + "exit_rate":"0%", + "url":"http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-08":[ + { + "label":"/MostViewedPage", + "nb_visits":3, + "nb_uniq_visitors":1, + "nb_hits":67, + "sum_time_spent":10263, + "nb_hits_following_search":"1", + "exit_nb_uniq_visitors":1, + "exit_nb_visits":"1", + "avg_time_on_page":153, + "bounce_rate":"0%", + "exit_rate":"33%", + "url":"http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-09":[ + { + "label":"/MostViewedPage", + "nb_visits":2, + "nb_uniq_visitors":1, + "nb_hits":76, + "sum_time_spent":5822, + "exit_nb_uniq_visitors":1, + "exit_nb_visits":"2", + "avg_time_on_page":77, + "bounce_rate":"0%", + "exit_rate":"100%", + "url":"http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-10":[ + { + "label":"/MostViewedPage", + "nb_visits":1, + "nb_uniq_visitors":1, + "nb_hits":16, + "sum_time_spent":1794, + "avg_time_on_page":112, + "bounce_rate":"0%", + "exit_rate":"0%", + "url":"http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-11":[ + + ], + "2023-08-12":[ + + ], + "2023-08-13":[ + { + "label":"/MostViewedPage", + "nb_visits":1, + "nb_uniq_visitors":1, + "nb_hits":1, + "sum_time_spent":0, + "entry_nb_uniq_visitors":1, + "entry_nb_visits":"1", + "entry_nb_actions":"1", + "entry_sum_visit_length":"0", + "entry_bounce_count":"1", + "exit_nb_uniq_visitors":1, + "exit_nb_visits":"1", + "avg_time_on_page":0, + "bounce_rate":"100%", + "exit_rate":"100%", + "url":"http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-14":[ + + ], + "2023-08-15":[ + + ], + "2023-08-16":[ + + ] + }, + "RowEvolutionTestObjectResponse": + [{"label":"/MostViewedPage","nb_visits":2,"nb_uniq_visitors":1,"nb_hits":2,"sum_time_spent":96,"avg_time_on_page":48,"bounce_rate":"0%","exit_rate":"0%","url":"http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage","date":"2023-08-07"},{"label":"/MostViewedPage","nb_visits":3,"nb_uniq_visitors":1,"nb_hits":67,"sum_time_spent":10263,"nb_hits_following_search":"1","exit_nb_uniq_visitors":1,"exit_nb_visits":"1","avg_time_on_page":153,"bounce_rate":"0%","exit_rate":"33%","url":"http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage","date":"2023-08-08"},{"label":"/MostViewedPage","nb_visits":2,"nb_uniq_visitors":1,"nb_hits":76,"sum_time_spent":5822,"exit_nb_uniq_visitors":1,"exit_nb_visits":"2","avg_time_on_page":77,"bounce_rate":"0%","exit_rate":"100%","url":"http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage","date":"2023-08-09"},{"label":"/MostViewedPage","nb_visits":1,"nb_uniq_visitors":1,"nb_hits":16,"sum_time_spent":1794,"avg_time_on_page":112,"bounce_rate":"0%","exit_rate":"0%","url":"http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage","date":"2023-08-10"},{"date":"2023-08-11"},{"date":"2023-08-12"},{"label":"/MostViewedPage","nb_visits":1,"nb_uniq_visitors":1,"nb_hits":1,"sum_time_spent":0,"entry_nb_uniq_visitors":1,"entry_nb_visits":"1","entry_nb_actions":"1","entry_sum_visit_length":"0","entry_bounce_count":"1","exit_nb_uniq_visitors":1,"exit_nb_visits":"1","avg_time_on_page":0,"bounce_rate":"100%","exit_rate":"100%","url":"http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage","date":"2023-08-13"},{"date":"2023-08-14"},{"date":"2023-08-15"},{"date":"2023-08-16"}] + } \ No newline at end of file diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index f7d7202b..95123ad9 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -134,6 +134,11 @@ require(['jquery', 'chartjs'], ($) => { const PERIODS = ["Day", "Week", "Month", "Year"]; let chart; + /** + * Creates the chart + * + * @param data json with the dataset for the current page. + */ const renderChart = (data) => { const dates = data.map(item => item.date); const nbHits = data.map(item => item[$('#field').val()] || 0); @@ -161,6 +166,12 @@ require(['jquery', 'chartjs'], ($) => { $('#chartModal').modal('show'); }; + /** + * Fetches the data from Matomo using the XWiki server as a proxy through the RowEvolutionJSON page. It then saves the data in session + * storage for easy access. This allows the user to display a different field for the same time interval without making a new request and updates the chart. + * + * @param url the page url. + */ const fetchChartData = (url) => { $('#chartModal').data('url', url); const period = $('#periodSelect').val().toLowerCase(); @@ -184,6 +195,9 @@ require(['jquery', 'chartjs'], ($) => { $('#date').data('date', $(`#periodDuration${period}`).val()); }; + /** + * Updates the chart when the time is changed. + */ const updateOnChange = () => { PERIODS.forEach(p => { $(`#periodDuration${p}`).on('change', function () { @@ -193,6 +207,9 @@ require(['jquery', 'chartjs'], ($) => { }); }; + /** + * Resets to the default option for all the selectos. + */ const restOptions = () => { $('#chartModal').on('hidden.bs.modal', function () { PERIODS.forEach(p => { @@ -208,6 +225,9 @@ require(['jquery', 'chartjs'], ($) => { }); }; + /** + * Binds the event that will open the modal and display the data when the 'Row Evolution' button is clicked. + */ const bindRowEvolutionDisplay = () => { $(document).on('click', '.displayMostViewedPagesActions', function () { updateVisibility('Day'); @@ -218,6 +238,10 @@ require(['jquery', 'chartjs'], ($) => { }); }; + /** + * Bind the event that will change the options for the time when the period is changed. Will also update the graph with + * the new period and the default time for that period. + */ const bindPeriodChangeEvent = () => { $('#periodSelect').on('change', function () { const period = $(this).val(); @@ -458,7 +482,6 @@ require(['jquery', 'chartjs'], ($) => { {{include reference='Analytics.Code.Macros.VelocityMacros'/}} {{velocity}} #set ($discard =$xwiki.jsx.use('Analytics.Code.Macros.MostViewedPages')) -#set ($discard =$xwiki.ssx.use('Analytics.Code.Macros.MostViewedPages')) #set ($dashbord = $doc.getObject('Analytics.Code.AnalyticsDashboardClass')) #set ($period = 'range') ## A dashboard will have a date inside and that date will take precedence over the date set by the macro. From 5b91874d00e75eb4efb4910d6358079f5c37c3d7 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Wed, 16 Aug 2023 13:08:12 +0300 Subject: [PATCH 027/105] Move Most Viewed Pages Macro to livetable #10 * Removed unused variables --- .../src/main/java/com/xwiki/analytics/JsonNormaliser.java | 8 +------- .../analytics/internal/MostViewedJsonNormaliser.java | 1 - 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java index 496d8d8e..8fca97ea 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java @@ -19,12 +19,7 @@ */ package com.xwiki.analytics; -import java.net.MalformedURLException; - import org.xwiki.component.annotation.Role; -import org.xwiki.resource.CreateResourceReferenceException; -import org.xwiki.resource.CreateResourceTypeException; -import org.xwiki.resource.UnsupportedResourceReferenceException; import org.xwiki.stability.Unstable; import com.fasterxml.jackson.core.JsonProcessingException; @@ -48,6 +43,5 @@ public interface JsonNormaliser * @throws JsonProcessingException Throws this error when the jsonString param is not a proper json. */ JsonNode normaliseData(String jsonString) - throws JsonProcessingException, MalformedURLException, UnsupportedResourceReferenceException, - CreateResourceTypeException, CreateResourceReferenceException; + throws JsonProcessingException; } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index 9ad55fdf..b021cd3f 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -67,7 +67,6 @@ public class MostViewedJsonNormaliser implements JsonNormaliser private static final String LABEL = "label"; - private static final String DOCUMENT_REFERENCE = "documentReference"; @Inject private ResourceReferenceResolver resourceReferenceResolver; From 0cfb0cd783aea4104b4b1698bac5742bb42150eb Mon Sep 17 00:00:00 2001 From: Farcasut Date: Wed, 16 Aug 2023 14:07:04 +0300 Subject: [PATCH 028/105] Move Most Viewed Pages Macro to livetable #10 * Removed unused livetable fields --- .../main/resources/Analytics/Code/Macros/MostViewedPages.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index cae53787..e328ba97 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -270,7 +270,7 @@ 'period': $$period, 'limitEntries' : '-1' })) -#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbVisits', 'entryNbActions', 'bounceRate', 'exitRate','documentReference', 'URL', 'actions']) +#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbVisits', 'entryNbActions', 'bounceRate', 'exitRate']) #set ($columnsProperties = { 'date' : { 'type' : 'text', 'sortable' : true }, 'title' : { 'type' : 'text', 'sortable' : true }, From ef662cfba0452eb323078503a72cad7d2c8abf96 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Wed, 16 Aug 2023 15:11:01 +0300 Subject: [PATCH 029/105] Move Most Viewed Pages Macro to livetable #10 * Moved the code that checks the date to macro to be able to reuse it in other pages --- .../Analytics/Code/Macros/MostViewedPages.xml | 27 +------- .../Analytics/Code/Macros/VelocityMacros.xml | 66 +++++++++++++++++++ 2 files changed, 69 insertions(+), 24 deletions(-) create mode 100644 application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index e328ba97..1e7b4a69 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -240,31 +240,10 @@ 0 - {{velocity}} -#set ($dashbord = $doc.getObject('Analytics.Code.AnalyticsDashboardClass')) + {{include reference='Analytics.Code.Macros.VelocityMacros'/}} +{{velocity}} +#getDate() #set ($period = 'range') -## A dashboard will have a date inside and that date will take precedence over the date set by the macro. -#if ($dashbord) - #set ($startDate = $dashbord.getProperty('startDate')) - #set ($endDate = $dashbord.getProperty('endDate')) -#else - #set ($startDate = $xcontext.macro.params.startDate.toString()) - #set ($endDate = $xcontext.macro.params.endDate.toString()) -#end -#set ($startDateObj = $datetool.toDate('yyyy/MM/dd HH:mm', $startDate)) -#set ($startDate = $datetool.format('yyyy-MM-dd', $startDateObj)) -#set ($endDateObj = $datetool.toDate('yyyy/MM/dd HH:mm', $endDate)) -#set ($endDate = $datetool.format('yyyy-MM-dd', $endDateObj)) -## Check if startDate or endDate are not defined and replace them with some default values, in order to match the end of -## the time interval. For startDate we use '2007-01-01' since it's the oldest one accepted by Matomo. -#if (!$startDate) - #set ($startDate = '2007-01-01') -#end -#if (!$endDate) - #set ($endDate = $datetool.get('yyyy-MM-dd')) -#end -## Prepare the livetable with the fields and properties. -#set ($date = $startDate + ',' + $endDate) #set ($parameters = $escapetool.url({ 'date': $date, 'period': $$period, diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml new file mode 100644 index 00000000..d7b560b9 --- /dev/null +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -0,0 +1,66 @@ + + + + + + Analytics.Code.Macros + VelocityMacros + + + 0 + xwiki:XWiki.Admin + WebHome + xwiki:XWiki.Admin + xwiki:XWiki.Admin + 1.1 + + <comment/> + <minorEdit>false</minorEdit> + <syntaxId>xwiki/2.1</syntaxId> + <hidden>true</hidden> + <content>{{velocity}} +#macro (getDate) + #set ($dashbord = $doc.getObject('Analytics.Code.AnalyticsDashboardClass')) + ## A dashboard will have a date inside and that date will take precedence over the date set by the macro. + #if ($dashbord) + #set ($startDate = $dashbord.getProperty('startDate')) + #set ($endDate = $dashbord.getProperty('endDate')) + #else + #set ($startDate = $xcontext.macro.params.startDate.toString()) + #set ($endDate = $xcontext.macro.params.endDate.toString()) + #end + #set ($startDateObj = $datetool.toDate('yyyy/MM/dd HH:mm', $startDate)) + #set ($startDate = $datetool.format('yyyy-MM-dd', $startDateObj)) + #set ($endDateObj = $datetool.toDate('yyyy/MM/dd HH:mm', $endDate)) + #set ($endDate = $datetool.format('yyyy-MM-dd', $endDateObj)) + ## Check if startDate or endDate are not defined and replace them with some default values, in order to match the end of + ## the time interval. For startDate we use '2007-01-01' since it's the oldest one accepted by Matomo. + #if (!$startDate) + #set ($startDate = '2007-01-01') + #end + #if (!$endDate) + #set ($endDate = $datetool.get('yyyy-MM-dd')) + #end + ## Prepare the livetable with the fields and properties. + #set ($date = $startDate + ',' + $endDate) +#end +{{/velocity}}</content> +</xwikidoc> From 9ec0fe38f30f94a1aa57cccc922aa3072f385b7e Mon Sep 17 00:00:00 2001 From: Farcasut <92780090+Farcasut@users.noreply.github.com> Date: Wed, 16 Aug 2023 18:10:54 +0300 Subject: [PATCH 030/105] Delete RowEvolution.xml --- .../Analytics/Code/Macros/RowEvolution.xml | 279 ------------------ 1 file changed, 279 deletions(-) delete mode 100644 application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml deleted file mode 100644 index 60347496..00000000 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml +++ /dev/null @@ -1,279 +0,0 @@ -<?xml version="1.1" encoding="UTF-8"?> - -<!-- - * See the NOTICE file distributed with this work for additional - * information regarding copyright ownership. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. ---> - -<xwikidoc version="1.4" reference="Analytics.Code.Macros.RowEvolution" locale=""> - <web>Analytics.Code.Macros</web> - <name>RowEvolution</name> - <language/> - <defaultLanguage/> - <translation>0</translation> - <creator>xwiki:XWiki.Admin</creator> - <author>xwiki:XWiki.Admin</author> - <contentAuthor>xwiki:XWiki.Admin</contentAuthor> - <version>1.1</version> - <title/> - <comment/> - <minorEdit>false</minorEdit> - <syntaxId>xwiki/2.1</syntaxId> - <hidden>true</hidden> - <content/> - <object> - <name>Analytics.Code.Macros.RowEvolution</name> - <number>0</number> - <className>XWiki.JavaScriptExtension</className> - <guid>9fc4c61e-9ad5-4ca3-9cc7-41d424a82a3a</guid> - <class> - <name>XWiki.JavaScriptExtension</name> - <customClass/> - <customMapping/> - <defaultViewSheet/> - <defaultEditSheet/> - <defaultWeb/> - <nameField/> - <validationScript/> - <cache> - <cache>0</cache> - <defaultValue>long</defaultValue> - <disabled>0</disabled> - <displayType>select</displayType> - <freeText>forbidden</freeText> - <largeStorage>0</largeStorage> - <multiSelect>0</multiSelect> - <name>cache</name> - <number>5</number> - <prettyName>Caching policy</prettyName> - <relationalStorage>0</relationalStorage> - <separator> </separator> - <separators>|, </separators> - <size>1</size> - <unmodifiable>0</unmodifiable> - <values>long|short|default|forbid</values> - <classType>com.xpn.xwiki.objects.classes.StaticListClass</classType> - </cache> - <code> - <contenttype>PureText</contenttype> - <disabled>0</disabled> - <editor>PureText</editor> - <name>code</name> - <number>2</number> - <prettyName>Code</prettyName> - <rows>20</rows> - <size>50</size> - <unmodifiable>0</unmodifiable> - <classType>com.xpn.xwiki.objects.classes.TextAreaClass</classType> - </code> - <name> - <disabled>0</disabled> - <name>name</name> - <number>1</number> - <prettyName>Name</prettyName> - <size>30</size> - <unmodifiable>0</unmodifiable> - <classType>com.xpn.xwiki.objects.classes.StringClass</classType> - </name> - <parse> - <disabled>0</disabled> - <displayFormType>select</displayFormType> - <displayType>yesno</displayType> - <name>parse</name> - <number>4</number> - <prettyName>Parse content</prettyName> - <unmodifiable>0</unmodifiable> - <classType>com.xpn.xwiki.objects.classes.BooleanClass</classType> - </parse> - <use> - <cache>0</cache> - <disabled>0</disabled> - <displayType>select</displayType> - <freeText>forbidden</freeText> - <largeStorage>0</largeStorage> - <multiSelect>0</multiSelect> - <name>use</name> - <number>3</number> - <prettyName>Use this extension</prettyName> - <relationalStorage>0</relationalStorage> - <separator> </separator> - <separators>|, </separators> - <size>1</size> - <unmodifiable>0</unmodifiable> - <values>currentPage|onDemand|always</values> - <classType>com.xpn.xwiki.objects.classes.StaticListClass</classType> - </use> - </class> - <property> - <cache>long</cache> - </property> - <property> - <code>require.config({ - paths: { - 'chartjs': 'https://cdn.jsdelivr.net/npm/chart.js/dist/Chart.min', - } -}); - -require(['jquery', 'chartjs'], ($) => { - const PERIODS = ["Day", "Week", "Month", "Year"]; - let chart; - - /** - * Creates the chart - * - * @param data json with the dataset for the current page. - */ - const renderChart = (data) => { - const dates = data.map(item => item.date); - const nbHits = data.map(item => item[$('#field').val()] || 0); - const ctx = $('#chart-display'); - chart = new Chart(ctx, { - type: 'line', - data: { - labels: dates, - datasets: [{ - label: '', - data: nbHits, - borderColor: 'rgba(75, 192, 192, 1)', - backgroundColor: 'rgba(75, 192, 192, 0.2)', - fill: false - }] - }, - options: { - scales: { - y: { - beginAtZero: true - } - } - } - }); - $('#chartModal').modal('show'); - }; - - /** - * Fetches the data from Matomo using the XWiki server as a proxy through the RowEvolutionJSON page. It then saves the data in session - * storage for easy access. This allows the user to display a different field for the same time interval without making a new request and updates the chart. - * - * @param parameter parameter need for the request - */ - const fetchChartData = (parameter) => { - $('#chartModal').data('parameter', parameter); - const period = $('#periodSelect').val().toLowerCase(); - const options = $('#options') - const date = options.data('date'); - const requestPage = options.data('requestPage') - $.getJSON(`${requestPage}?parameter=${parameter}&period=${period}&date=last${date}`) - .done((data) => { - window.sessionStorage.setItem('analyticsLastRequestCache', JSON.stringify(data)); - renderChart(data); - }) - .fail((jqxhr, textStatus, error) => { - const err = `${textStatus}, ${error}`; - console.error(`Request Failed: ${err}`); - }); - }; - - const updateVisibility = (period) => { - PERIODS.forEach(p => { - p === period ? $(`#periodDuration${p}`).show() : $(`#periodDuration${p}`).hide(); - }); - $('#options').data('date', $(`#periodDuration${period}`).val()); - }; - - /** - * Updates the chart when the time is changed. - */ - const updateOnChange = () => { - PERIODS.forEach(p => { - $(`#periodDuration${p}`).on('change', function () { - $('#options').data('date', $(`#periodDuration${p}`).val()); - fetchChartData($('#chartModal').data('parameter')); - }); - }); - }; - - /** - * Resets to the default option for all the selectos. - */ - const restOptions = () => { - $('#chartModal').on('hidden.bs.modal', function () { - PERIODS.forEach(p => { - $(`#periodDuration${p}`).prop('selectedIndex', 0); - }); - $('#field').prop('selectedIndex', 0); - }); - }; - - const displayStatisitcs = () => { - $('#field').on('change', function () { - renderChart(JSON.parse(window.sessionStorage.getItem('analyticsLastRequestCache'))); - }); - }; - - /** - * Binds the event that will open the modal and display the data when the 'Row Evolution' button is clicked. - */ - const bindRowEvolutionDisplay = () => { - $(document).on('click', '.displayMostViewedPagesActions', function () { - updateVisibility('Day'); - const parameter = $(this).data('parameter'); - $('#periodSelect').prop('selectedIndex', 0); - $('#options').data('date', '30'); - fetchChartData(parameter); - }); - }; - - /** - * Bind the event that will change the options for the time when the period is changed. Will also update the graph with - * the new period and the default time for that period. - */ - const bindPeriodChangeEvent = () => { - $('#periodSelect').on('change', function () { - const period = $(this).val(); - restOptions(); - updateVisibility(period); - fetchChartData($('#chartModal').data('parameter')); - }); - }; - - const bindEvents = () => { - updateOnChange(); - restOptions(); - displayStatisitcs(); - bindPeriodChangeEvent(); - bindRowEvolutionDisplay(); - }; - - $(document).ready(() => { - updateVisibility('Day'); - bindEvents(); - }); -}); -</code> - </property> - <property> - <name/> - </property> - <property> - <parse>0</parse> - </property> - <property> - <use>onDemand</use> - </property> - </object> -</xwikidoc> From 7861833eff95d7eb3a66f6391270a2dd13c095bd Mon Sep 17 00:00:00 2001 From: Farcasut <farcasialexandrububu@gmail.com> Date: Wed, 16 Aug 2023 18:17:44 +0300 Subject: [PATCH 031/105] Row Evolution feature for the MostViewedPages #15 * Changed the name of a macro --- .../resources/Analytics/Code/Macros/MostViewedPagesJSON.xml | 2 +- .../src/main/resources/Analytics/Code/Macros/VelocityMacros.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml index 2bf75da2..e3b6b72e 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml @@ -100,7 +100,7 @@ 'entryNbActions' : $currentEntry.get('entry_nb_actions').asText(), 'bounceRate' : $currentEntry.get('bounce_rate').asText(), 'exitRate' : $currentEntry.get('exit_rate').asText(), - 'actions' : "#displayMostViewedPageActions($url)" + 'actions' : "#setParameterOnAction($url)" })) #end #jsonResponse($results) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index 23616bb8..7ba3d2ea 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -63,7 +63,7 @@ #set ($date = $startDate + ',' + $endDate) #end -#macro(displayMostViewedPageActions $parameter) +#macro(setParameterOnAction $parameter) <div class="displayMostViewedPagesActions" data-parameter=$escapetool.url($parameter)> <a class="move-attachment action" title="$escapetool.xml($services.localization.render('analytics.mostViwedMacros.action.rowEvolution'))" href="#"> <span class="action-icon">$services.icon.renderHTML('eye')</span><span class="action-label">$escapetool.xml($services.localization.render('analytics.mostViwedMacros.action.action.label.rowEvolution'))</span> From 421cede51456b82a03f009bcd6a7932278e79f20 Mon Sep 17 00:00:00 2001 From: Farcasut <farcasialexandrububu@gmail.com> Date: Wed, 16 Aug 2023 18:49:54 +0300 Subject: [PATCH 032/105] Move Most Viewed Pages Macro to livetable #10 * Missing indentation --- .../resources/Analytics/Code/Macros/MostViewedPagesJSON.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml index 70539303..2358f046 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml @@ -63,7 +63,7 @@ }) #set ($sort = 'nb_hits') #set ($order = 'desc') - #if($request.sort && $request.reqNo!='1') + #if ($request.sort && $request.reqNo!='1') #set($sort = $keys.get($request.sort)) #set($order = $request.dir) #end @@ -87,7 +87,7 @@ "reqNo": $numbertool.toNumber($request.reqNo).intValue(), "rows": [] }) - #foreach($currentEntry in $analyticsResult) + #foreach ($currentEntry in $analyticsResult) #set ($url = $currentEntry.get('url').asText()) #set ($discard = $results.rows.add({ 'date' : $currentEntry.get('date').asText(), From fb389117a1e5d6caa509596e12952716bd318f80 Mon Sep 17 00:00:00 2001 From: Farcasut <farcasialexandrububu@gmail.com> Date: Wed, 16 Aug 2023 20:53:40 +0300 Subject: [PATCH 033/105] Row Evolution feature for the MostViewedPages #15 * I added a file that I deleted by mistake --- .../Analytics/Code/Macros/RowEvolution.xml | 279 ++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml new file mode 100644 index 00000000..60347496 --- /dev/null +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml @@ -0,0 +1,279 @@ +<?xml version="1.1" encoding="UTF-8"?> + +<!-- + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. +--> + +<xwikidoc version="1.4" reference="Analytics.Code.Macros.RowEvolution" locale=""> + <web>Analytics.Code.Macros</web> + <name>RowEvolution</name> + <language/> + <defaultLanguage/> + <translation>0</translation> + <creator>xwiki:XWiki.Admin</creator> + <author>xwiki:XWiki.Admin</author> + <contentAuthor>xwiki:XWiki.Admin</contentAuthor> + <version>1.1</version> + <title/> + <comment/> + <minorEdit>false</minorEdit> + <syntaxId>xwiki/2.1</syntaxId> + <hidden>true</hidden> + <content/> + <object> + <name>Analytics.Code.Macros.RowEvolution</name> + <number>0</number> + <className>XWiki.JavaScriptExtension</className> + <guid>9fc4c61e-9ad5-4ca3-9cc7-41d424a82a3a</guid> + <class> + <name>XWiki.JavaScriptExtension</name> + <customClass/> + <customMapping/> + <defaultViewSheet/> + <defaultEditSheet/> + <defaultWeb/> + <nameField/> + <validationScript/> + <cache> + <cache>0</cache> + <defaultValue>long</defaultValue> + <disabled>0</disabled> + <displayType>select</displayType> + <freeText>forbidden</freeText> + <largeStorage>0</largeStorage> + <multiSelect>0</multiSelect> + <name>cache</name> + <number>5</number> + <prettyName>Caching policy</prettyName> + <relationalStorage>0</relationalStorage> + <separator> </separator> + <separators>|, </separators> + <size>1</size> + <unmodifiable>0</unmodifiable> + <values>long|short|default|forbid</values> + <classType>com.xpn.xwiki.objects.classes.StaticListClass</classType> + </cache> + <code> + <contenttype>PureText</contenttype> + <disabled>0</disabled> + <editor>PureText</editor> + <name>code</name> + <number>2</number> + <prettyName>Code</prettyName> + <rows>20</rows> + <size>50</size> + <unmodifiable>0</unmodifiable> + <classType>com.xpn.xwiki.objects.classes.TextAreaClass</classType> + </code> + <name> + <disabled>0</disabled> + <name>name</name> + <number>1</number> + <prettyName>Name</prettyName> + <size>30</size> + <unmodifiable>0</unmodifiable> + <classType>com.xpn.xwiki.objects.classes.StringClass</classType> + </name> + <parse> + <disabled>0</disabled> + <displayFormType>select</displayFormType> + <displayType>yesno</displayType> + <name>parse</name> + <number>4</number> + <prettyName>Parse content</prettyName> + <unmodifiable>0</unmodifiable> + <classType>com.xpn.xwiki.objects.classes.BooleanClass</classType> + </parse> + <use> + <cache>0</cache> + <disabled>0</disabled> + <displayType>select</displayType> + <freeText>forbidden</freeText> + <largeStorage>0</largeStorage> + <multiSelect>0</multiSelect> + <name>use</name> + <number>3</number> + <prettyName>Use this extension</prettyName> + <relationalStorage>0</relationalStorage> + <separator> </separator> + <separators>|, </separators> + <size>1</size> + <unmodifiable>0</unmodifiable> + <values>currentPage|onDemand|always</values> + <classType>com.xpn.xwiki.objects.classes.StaticListClass</classType> + </use> + </class> + <property> + <cache>long</cache> + </property> + <property> + <code>require.config({ + paths: { + 'chartjs': 'https://cdn.jsdelivr.net/npm/chart.js/dist/Chart.min', + } +}); + +require(['jquery', 'chartjs'], ($) => { + const PERIODS = ["Day", "Week", "Month", "Year"]; + let chart; + + /** + * Creates the chart + * + * @param data json with the dataset for the current page. + */ + const renderChart = (data) => { + const dates = data.map(item => item.date); + const nbHits = data.map(item => item[$('#field').val()] || 0); + const ctx = $('#chart-display'); + chart = new Chart(ctx, { + type: 'line', + data: { + labels: dates, + datasets: [{ + label: '', + data: nbHits, + borderColor: 'rgba(75, 192, 192, 1)', + backgroundColor: 'rgba(75, 192, 192, 0.2)', + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + } + } + } + }); + $('#chartModal').modal('show'); + }; + + /** + * Fetches the data from Matomo using the XWiki server as a proxy through the RowEvolutionJSON page. It then saves the data in session + * storage for easy access. This allows the user to display a different field for the same time interval without making a new request and updates the chart. + * + * @param parameter parameter need for the request + */ + const fetchChartData = (parameter) => { + $('#chartModal').data('parameter', parameter); + const period = $('#periodSelect').val().toLowerCase(); + const options = $('#options') + const date = options.data('date'); + const requestPage = options.data('requestPage') + $.getJSON(`${requestPage}?parameter=${parameter}&period=${period}&date=last${date}`) + .done((data) => { + window.sessionStorage.setItem('analyticsLastRequestCache', JSON.stringify(data)); + renderChart(data); + }) + .fail((jqxhr, textStatus, error) => { + const err = `${textStatus}, ${error}`; + console.error(`Request Failed: ${err}`); + }); + }; + + const updateVisibility = (period) => { + PERIODS.forEach(p => { + p === period ? $(`#periodDuration${p}`).show() : $(`#periodDuration${p}`).hide(); + }); + $('#options').data('date', $(`#periodDuration${period}`).val()); + }; + + /** + * Updates the chart when the time is changed. + */ + const updateOnChange = () => { + PERIODS.forEach(p => { + $(`#periodDuration${p}`).on('change', function () { + $('#options').data('date', $(`#periodDuration${p}`).val()); + fetchChartData($('#chartModal').data('parameter')); + }); + }); + }; + + /** + * Resets to the default option for all the selectos. + */ + const restOptions = () => { + $('#chartModal').on('hidden.bs.modal', function () { + PERIODS.forEach(p => { + $(`#periodDuration${p}`).prop('selectedIndex', 0); + }); + $('#field').prop('selectedIndex', 0); + }); + }; + + const displayStatisitcs = () => { + $('#field').on('change', function () { + renderChart(JSON.parse(window.sessionStorage.getItem('analyticsLastRequestCache'))); + }); + }; + + /** + * Binds the event that will open the modal and display the data when the 'Row Evolution' button is clicked. + */ + const bindRowEvolutionDisplay = () => { + $(document).on('click', '.displayMostViewedPagesActions', function () { + updateVisibility('Day'); + const parameter = $(this).data('parameter'); + $('#periodSelect').prop('selectedIndex', 0); + $('#options').data('date', '30'); + fetchChartData(parameter); + }); + }; + + /** + * Bind the event that will change the options for the time when the period is changed. Will also update the graph with + * the new period and the default time for that period. + */ + const bindPeriodChangeEvent = () => { + $('#periodSelect').on('change', function () { + const period = $(this).val(); + restOptions(); + updateVisibility(period); + fetchChartData($('#chartModal').data('parameter')); + }); + }; + + const bindEvents = () => { + updateOnChange(); + restOptions(); + displayStatisitcs(); + bindPeriodChangeEvent(); + bindRowEvolutionDisplay(); + }; + + $(document).ready(() => { + updateVisibility('Day'); + bindEvents(); + }); +}); +</code> + </property> + <property> + <name/> + </property> + <property> + <parse>0</parse> + </property> + <property> + <use>onDemand</use> + </property> + </object> +</xwikidoc> From 46243d005e642ceab068679994bcffd68d57cad0 Mon Sep 17 00:00:00 2001 From: Farcasut <farcasialexandrububu@gmail.com> Date: Thu, 17 Aug 2023 09:56:08 +0300 Subject: [PATCH 034/105] Move Most Viewed Pages Macro to livetable #10 * I updated the MatomoANalyticsMangerTest class --- .../analytics/internal/MatomoAnalyticsManagerTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java index 03e2c5af..032696d8 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java @@ -20,6 +20,9 @@ package com.xwiki.analytics.internal; import java.io.IOException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.util.HashMap; import javax.inject.Named; @@ -37,6 +40,8 @@ import com.xwiki.analytics.configuration.AnalyticsConfiguration; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** @@ -65,6 +70,10 @@ public void MatomoAnalyticsManagerTest() when(this.configuration.getAuthenticationToken()).thenReturn("token"); when(this.configuration.getRequestAddress()).thenReturn("http://130.61.233.19/matomo"); when(this.configuration.getIdSite()).thenReturn("3"); + HttpClient client = mock(HttpClient.class); + HttpRequest httpRequest = mock(HttpRequest.class); + HttpResponse mockedResponse = mock(HttpResponse.class); + when(client.send(eq(httpRequest), any())).thenReturn(mockedResponse); assertEquals(null, this.matomoAnalyticsManager.requestData(new HashMap<>(), "MostViewedPages")); } } From 4ffb9090c209e3176daf5bfc602beb73ffd9549c Mon Sep 17 00:00:00 2001 From: Farcasut <farcasialexandrububu@gmail.com> Date: Thu, 17 Aug 2023 12:06:05 +0300 Subject: [PATCH 035/105] Move Most Viewed Pages Macro to livetable #10 * I modified the comments from the JsonNormaliser and added a waring for the developer for when the normaliser hint is not valid --- .../com/xwiki/analytics/JsonNormaliser.java | 5 ++-- .../internal/MatomoAnalyticsManager.java | 15 +++++------ .../internal/MostViewedJsonNormaliser.java | 27 +++++++------------ .../internal/MatomoAnalyticsManagerTest.java | 14 +++++----- .../Code/Macros/MostViewedPagesJSON.xml | 2 +- 5 files changed, 28 insertions(+), 35 deletions(-) diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java index 8fca97ea..bd4f975a 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java @@ -26,7 +26,8 @@ import com.fasterxml.jackson.databind.JsonNode; /** - * The interface for the json normalisation classes. + * The interface for the json normalisation classes. Matomo may return several variants of JSON formats for easy of + * use in javascript and velocity I need to process them. * * @version $Id$ * @since 1.0 @@ -36,7 +37,7 @@ public interface JsonNormaliser { /** - * This function will normalise the data returned to have only one format. + * Normalise the data returned to have only one format. * * @param jsonString A string that has a proper json format. * @return Returns the json in string format diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java index 2f0ebbd0..3715211a 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java @@ -31,11 +31,8 @@ import javax.inject.Singleton; import javax.ws.rs.core.UriBuilder; +import org.slf4j.Logger; import org.xwiki.component.annotation.Component; -import org.xwiki.component.manager.ComponentLookupException; -import org.xwiki.resource.CreateResourceReferenceException; -import org.xwiki.resource.CreateResourceTypeException; -import org.xwiki.resource.UnsupportedResourceReferenceException; import org.xwiki.stability.Unstable; import com.fasterxml.jackson.databind.JsonNode; @@ -55,6 +52,9 @@ @Unstable public class MatomoAnalyticsManager implements AnalyticsManager { + @Inject + private Logger logger; + @Inject @Named("MostViewedPages") private JsonNormaliser mostViewedNormaliser; @@ -71,15 +71,14 @@ public class MatomoAnalyticsManager implements AnalyticsManager */ @Override public JsonNode requestData(Map<String, String> parameters, String jsonNormaliserHint) - throws IOException, InterruptedException, ComponentLookupException, UnsupportedResourceReferenceException, - CreateResourceTypeException, CreateResourceReferenceException + throws IOException, InterruptedException { parameters.put("idSite", configuration.getIdSite()); parameters.put("token_auth", configuration.getAuthenticationToken()); JsonNormaliser jsonNormaliser = this.getNormaliser(jsonNormaliserHint); if (jsonNormaliser == null) { - throw new RuntimeException("Error occurred while retrieving Matomo statistic results. There is no JSON" - + " normalizer associated with the hint you provided."); + logger.warn("There is no JSON normalizer associated with the [{}] hint you provided.", jsonNormaliserHint); + throw new RuntimeException("Error occurred while retrieving Matomo statistic results."); } HttpClient client = HttpClient.newHttpClient(); HttpRequest httpRequest = HttpRequest.newBuilder(buildURI(configuration.getRequestAddress(), parameters)) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index b021cd3f..9687c4df 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -48,6 +48,7 @@ import com.xwiki.analytics.JsonNormaliser; import liquibase.repackaged.org.apache.commons.lang3.exception.ExceptionUtils; + /** * The normaliser for the MostViewedMacro. * @@ -67,7 +68,6 @@ public class MostViewedJsonNormaliser implements JsonNormaliser private static final String LABEL = "label"; - @Inject private ResourceReferenceResolver<ExtendedURL> resourceReferenceResolver; @@ -77,25 +77,19 @@ public class MostViewedJsonNormaliser implements JsonNormaliser @Inject private ResourceTypeResolver<ExtendedURL> resourceTypeResolver; - /** - * This method will normalise the jsons returned by Matomo into a single format. - * - * @param jsonString the json provided by Matomo. - * @return the normalised json as a string. - */ + @Override public JsonNode normaliseData(String jsonString) throws JsonProcessingException { // I need to convert the string returned by matomo in a JSON to easily handle the processing of the nodes. JsonNode jsonRoot = OBJECT_MAPPER.readTree(jsonString); - // Matomo may return several variants of JSON formats. In one scenario, when the period is set to - // day/week/month/year, it returns a JSON object with keys representing dates. The corresponding value for - // each key is an array of JSON objects, each of which represents a page. However, if the user sets the - // period parameter to "range" Matomo returns an array of JSON objects, with each JSON object representing - // a page. Due to these variations, I need to process the result from Matomo to create a normalized format. - // This normalized format is an array of JSON objects and each JSON object in this array will have a new - // field called 'date'. This 'date' field will be set to 'N/A' when Matomo returns an array instead of an - // object. In the 'processArrayNode' and 'processObjectNode' methods, I also modify the 'label' field to - // change it from the raw URL format to the page name. + // In one scenario, when the period is set to day/week/month/year, it returns a JSON object with keys + // representing dates. The corresponding value for each key is an array of JSON objects, each of which + // represents a page. However, if the user sets the period parameter to "range" Matomo returns an array of + // JSON objects, with each JSON object representing a page. Due to these variations, I need to process the + // result from Matomo to create a normalized format. This normalized format is an array of JSON objects and + // each JSON object in this array will have a new field called 'date'. This 'date' field will be set to 'N/A' + // when Matomo returns an array instead of an object. In the 'processArrayNode' and 'processObjectNode' + // methods, I also modify the 'label' field to change it from the raw URL format to the page name. if (jsonRoot.isArray()) { processArrayNode(jsonRoot); } else { @@ -130,7 +124,6 @@ private void processArrayNode(JsonNode jsonNode) * @param jsonNode json object * @return array of jsons */ - private ArrayNode processObjectNode(JsonNode jsonNode) { ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java index 032696d8..c821f3c0 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java @@ -28,10 +28,6 @@ import javax.inject.Named; import org.junit.jupiter.api.Test; -import org.xwiki.component.manager.ComponentLookupException; -import org.xwiki.resource.CreateResourceReferenceException; -import org.xwiki.resource.CreateResourceTypeException; -import org.xwiki.resource.UnsupportedResourceReferenceException; import org.xwiki.test.junit5.mockito.ComponentTest; import org.xwiki.test.junit5.mockito.InjectMockComponents; import org.xwiki.test.junit5.mockito.MockComponent; @@ -63,9 +59,7 @@ public class MatomoAnalyticsManagerTest private AnalyticsConfiguration configuration; @Test - public void MatomoAnalyticsManagerTest() - throws ComponentLookupException, IOException, UnsupportedResourceReferenceException, InterruptedException, - CreateResourceTypeException, CreateResourceReferenceException + public void requestDataWithCorrectHintForNormaliser() throws IOException, InterruptedException { when(this.configuration.getAuthenticationToken()).thenReturn("token"); when(this.configuration.getRequestAddress()).thenReturn("http://130.61.233.19/matomo"); @@ -74,6 +68,12 @@ public void MatomoAnalyticsManagerTest() HttpRequest httpRequest = mock(HttpRequest.class); HttpResponse mockedResponse = mock(HttpResponse.class); when(client.send(eq(httpRequest), any())).thenReturn(mockedResponse); + when(mockedResponse.body()).thenReturn("test"); assertEquals(null, this.matomoAnalyticsManager.requestData(new HashMap<>(), "MostViewedPages")); } + @Test + public void requestDataWithInvalidNormaliser() + { + + } } diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml index 2358f046..f8800bd0 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml @@ -87,7 +87,7 @@ "reqNo": $numbertool.toNumber($request.reqNo).intValue(), "rows": [] }) - #foreach ($currentEntry in $analyticsResult) + #foreach ($currentEntry in $analyticsResult) #set ($url = $currentEntry.get('url').asText()) #set ($discard = $results.rows.add({ 'date' : $currentEntry.get('date').asText(), From 82505222fa446844b2129cea2872353a14cfb2f8 Mon Sep 17 00:00:00 2001 From: Farcasut <farcasialexandrububu@gmail.com> Date: Thu, 17 Aug 2023 12:47:43 +0300 Subject: [PATCH 036/105] Move Most Viewed Pages Macro to livetable #10 * I added a test for when the Normaliser hint is invalid --- .../internal/MatomoAnalyticsManagerTest.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java index c821f3c0..a40381a8 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java @@ -28,6 +28,7 @@ import javax.inject.Named; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; import org.xwiki.test.junit5.mockito.ComponentTest; import org.xwiki.test.junit5.mockito.InjectMockComponents; import org.xwiki.test.junit5.mockito.MockComponent; @@ -38,6 +39,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** @@ -58,6 +60,9 @@ public class MatomoAnalyticsManagerTest @MockComponent private AnalyticsConfiguration configuration; + @MockComponent + private Logger logger; + @Test public void requestDataWithCorrectHintForNormaliser() throws IOException, InterruptedException { @@ -72,8 +77,20 @@ public void requestDataWithCorrectHintForNormaliser() throws IOException, Interr assertEquals(null, this.matomoAnalyticsManager.requestData(new HashMap<>(), "MostViewedPages")); } @Test - public void requestDataWithInvalidNormaliser() + public void requestDataWithInvalidNormaliser() throws IOException, InterruptedException { + when(this.configuration.getAuthenticationToken()).thenReturn("token"); + when(this.configuration.getRequestAddress()).thenReturn("http://130.61.233.19/matomo"); + when(this.configuration.getIdSite()).thenReturn("3"); + try { + when(this.matomoAnalyticsManager.requestData(new HashMap<>(), "RANDOM_NORMALISER")).thenThrow( + new RuntimeException()); + verify(this.logger).warn("There is no JSON normalizer associated with the [{}] hint you provided.","RANDOM_NORMALISER"); + } + catch (RuntimeException e) + { + assertEquals(e.getMessage(), "Error occurred while retrieving Matomo statistic results."); + } } } From 03276f6a479331e32de17742e557eb7eebff78b6 Mon Sep 17 00:00:00 2001 From: Farcasut <farcasialexandrububu@gmail.com> Date: Thu, 17 Aug 2023 14:02:32 +0300 Subject: [PATCH 037/105] Move Most Viewed Pages Macro to livetable #10 * Update test --- .../internal/MatomoAnalyticsManagerTest.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java index a40381a8..5afb7257 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java @@ -20,9 +20,6 @@ package com.xwiki.analytics.internal; import java.io.IOException; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; import java.util.HashMap; import javax.inject.Named; @@ -37,8 +34,7 @@ import com.xwiki.analytics.configuration.AnalyticsConfiguration; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.mock; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -69,12 +65,8 @@ public void requestDataWithCorrectHintForNormaliser() throws IOException, Interr when(this.configuration.getAuthenticationToken()).thenReturn("token"); when(this.configuration.getRequestAddress()).thenReturn("http://130.61.233.19/matomo"); when(this.configuration.getIdSite()).thenReturn("3"); - HttpClient client = mock(HttpClient.class); - HttpRequest httpRequest = mock(HttpRequest.class); - HttpResponse mockedResponse = mock(HttpResponse.class); - when(client.send(eq(httpRequest), any())).thenReturn(mockedResponse); - when(mockedResponse.body()).thenReturn("test"); - assertEquals(null, this.matomoAnalyticsManager.requestData(new HashMap<>(), "MostViewedPages")); + this.matomoAnalyticsManager.requestData(new HashMap<>(), "MostViewedPages"); + verify(this.jsonNormaliser).normaliseData(any(String.class)); } @Test public void requestDataWithInvalidNormaliser() throws IOException, InterruptedException From 5aa4790e0748cc1cbde29336ce46a3d0693febcc Mon Sep 17 00:00:00 2001 From: Farcasut <farcasialexandrububu@gmail.com> Date: Thu, 17 Aug 2023 16:37:09 +0300 Subject: [PATCH 038/105] Row Evolution feature for the MostViewedPages xwikisas#15 * Updated the javascript code to remove some bugs --- .../Analytics/Code/Macros/MostViewedPages.xml | 3 +- .../Analytics/Code/Macros/RowEvolution.xml | 61 +++++++++++-------- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index f81e40f6..3857ae82 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -269,7 +269,8 @@ 'extraParams': $parameters }) #livetable('mostViewedPages' $columns $columnsProperties $options) -#modalRowEvolution({'Pageviews':'nb_hits','Pageviewes':'nb_visits', "Avg. time on page":'avg_time_on_page'}, 'RowEvolutionMostViwedPagesJSON') +#modalRowEvolution({'Pageviews':'nb_hits','Unique Views':'nb_visits', "Avg. time on page":'avg_time_on_page'}, + 'RowEvolutionMostViwedPagesJSON') {{/velocity}}</code> </property> <property> diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml index 60347496..a09d9b76 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml @@ -140,15 +140,18 @@ require(['jquery', 'chartjs'], ($) => { */ const renderChart = (data) => { const dates = data.map(item => item.date); - const nbHits = data.map(item => item[$('#field').val()] || 0); + const field = data.map(item => item[$('#field').val()] || 0); const ctx = $('#chart-display'); + if (chart) { + chart.destroy(); + } chart = new Chart(ctx, { type: 'line', data: { labels: dates, datasets: [{ label: '', - data: nbHits, + data: field, borderColor: 'rgba(75, 192, 192, 1)', backgroundColor: 'rgba(75, 192, 192, 0.2)', fill: false @@ -172,9 +175,9 @@ require(['jquery', 'chartjs'], ($) => { * @param parameter parameter need for the request */ const fetchChartData = (parameter) => { - $('#chartModal').data('parameter', parameter); - const period = $('#periodSelect').val().toLowerCase(); + $('#options').data('parameter', parameter); const options = $('#options') + const period = options.data('period') const date = options.data('date'); const requestPage = options.data('requestPage') $.getJSON(`${requestPage}?parameter=${parameter}&period=${period}&date=last${date}`) @@ -188,27 +191,21 @@ require(['jquery', 'chartjs'], ($) => { }); }; - const updateVisibility = (period) => { - PERIODS.forEach(p => { - p === period ? $(`#periodDuration${p}`).show() : $(`#periodDuration${p}`).hide(); - }); - $('#options').data('date', $(`#periodDuration${period}`).val()); - }; - /** - * Updates the chart when the time is changed. + * Will show only the time options available for the period given as a parameter and updates the options + * + * @param period Day/Week/Month/Year */ - const updateOnChange = () => { + const displayPeriodDurations = (period) => { PERIODS.forEach(p => { - $(`#periodDuration${p}`).on('change', function () { - $('#options').data('date', $(`#periodDuration${p}`).val()); - fetchChartData($('#chartModal').data('parameter')); - }); + p === period ? $(`#periodDuration${p}`).show() : $(`#periodDuration${p}`).hide(); }); + $('#options').data('period', period.toLowerCase()); + $('#options').data('date', $(`#periodDuration${period}`).val()); }; /** - * Resets to the default option for all the selectos. + * Resets to the default option for all the selectos when the modal is closed. */ const restOptions = () => { $('#chartModal').on('hidden.bs.modal', function () { @@ -230,29 +227,41 @@ require(['jquery', 'chartjs'], ($) => { */ const bindRowEvolutionDisplay = () => { $(document).on('click', '.displayMostViewedPagesActions', function () { - updateVisibility('Day'); + displayPeriodDurations('Day'); const parameter = $(this).data('parameter'); $('#periodSelect').prop('selectedIndex', 0); $('#options').data('date', '30'); fetchChartData(parameter); }); }; - + + /** + * Updates the chart when the time + */ + const updateOnPeriodChange = () => { + PERIODS.forEach(p => { + $(`#periodDuration${p}`).on('change', function () { + //updates the time intervals to that period options + $('#options').data('date', $(`#periodDuration${p}`).val()); + fetchChartData($('#options').data('parameter')); + }); + }); + }; + /** - * Bind the event that will change the options for the time when the period is changed. Will also update the graph with - * the new period and the default time for that period. + * Changes the chart when the period (day/week/month/year) changes. */ const bindPeriodChangeEvent = () => { $('#periodSelect').on('change', function () { const period = $(this).val(); restOptions(); - updateVisibility(period); - fetchChartData($('#chartModal').data('parameter')); + displayPeriodDurations(period); + fetchChartData($('#options').data('parameter')); }); }; const bindEvents = () => { - updateOnChange(); + updateOnPeriodChange(); restOptions(); displayStatisitcs(); bindPeriodChangeEvent(); @@ -260,7 +269,7 @@ require(['jquery', 'chartjs'], ($) => { }; $(document).ready(() => { - updateVisibility('Day'); + displayPeriodDurations('Day'); bindEvents(); }); }); From 0222c08c53b4967a33cdc9382800c0e8961e71cf Mon Sep 17 00:00:00 2001 From: Farcasut <farcasialexandrububu@gmail.com> Date: Fri, 18 Aug 2023 11:37:20 +0300 Subject: [PATCH 039/105] Move Most Viewed Pages Macro to livetable #10 * Modified the structure of the files --- .../internal/MatomoAnalyticsManagerTest.java | 6 +- .../MostViewedPagesMacro/MostViewedPages.xml | 459 ++++++++++++++++++ .../MostViewedPagesJSON.xml | 108 +++++ 3 files changed, 572 insertions(+), 1 deletion(-) create mode 100644 application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml create mode 100644 application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPagesJSON.xml diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java index 5afb7257..0f4659fd 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java @@ -25,6 +25,7 @@ import javax.inject.Named; import org.junit.jupiter.api.Test; +import org.mockito.Mock; import org.slf4j.Logger; import org.xwiki.test.junit5.mockito.ComponentTest; import org.xwiki.test.junit5.mockito.InjectMockComponents; @@ -32,6 +33,7 @@ import com.xwiki.analytics.JsonNormaliser; import com.xwiki.analytics.configuration.AnalyticsConfiguration; +import org.xwiki.component.util.ReflectionUtils; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; @@ -71,13 +73,15 @@ public void requestDataWithCorrectHintForNormaliser() throws IOException, Interr @Test public void requestDataWithInvalidNormaliser() throws IOException, InterruptedException { + ReflectionUtils.setFieldValue(this.matomoAnalyticsManager, "logger", this.logger); when(this.configuration.getAuthenticationToken()).thenReturn("token"); when(this.configuration.getRequestAddress()).thenReturn("http://130.61.233.19/matomo"); when(this.configuration.getIdSite()).thenReturn("3"); try { when(this.matomoAnalyticsManager.requestData(new HashMap<>(), "RANDOM_NORMALISER")).thenThrow( new RuntimeException()); - verify(this.logger).warn("There is no JSON normalizer associated with the [{}] hint you provided.","RANDOM_NORMALISER"); + verify(this.logger).warn("There is no JSON normalizer associated with the [{}] hint you provided.", + "RANDOM_NORMALISER"); } catch (RuntimeException e) { diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml new file mode 100644 index 00000000..adee71d2 --- /dev/null +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml @@ -0,0 +1,459 @@ +<?xml version="1.1" encoding="UTF-8"?> + +<!-- + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. +--> + +<xwikidoc version="1.4" reference="Analytics.Code.Macros.MostViewedPagesMacro.MostViewedPages" locale=""> + <web>Analytics.Code.Macros.MostViewedPagesMacro</web> + <name>MostViewedPages</name> + <language/> + <defaultLanguage/> + <translation>0</translation> + <creator>xwiki:XWiki.Admin</creator> + <parent>WebHome</parent> + <author>xwiki:XWiki.Admin</author> + <contentAuthor>xwiki:XWiki.Admin</contentAuthor> + <version>1.1</version> + <title>MostViewedPages + + false + xwiki/2.1 + true + {{mostViewedPage/}} + + Analytics.Code.Macros.MostViewedPagesMacro.MostViewedPages + 0 + XWiki.WikiMacroClass + dc943965-6af8-4905-8508-9b8c248c3930 + + XWiki.WikiMacroClass + + + + + + + + + 0 + 0 + select + + async_cached + 13 + Cached + 0 + com.xpn.xwiki.objects.classes.BooleanClass + + + 0 + 0 + select + forbidden + 0 + 1 + async_context + 14 + Context elements + 0 + , + |, + 5 + 0 + action=Action|doc.reference=Document|icon.theme=Icon theme|locale=Language|rendering.defaultsyntax=Default syntax|rendering.restricted=Restricted|rendering.targetsyntax=Target syntax|request.base=Request base URL|request.parameters=Request parameters|request.url=Request URL|request.wiki=Request wiki|user=User|wiki=Wiki + com.xpn.xwiki.objects.classes.StaticListClass + + + 0 + 0 + select + + async_enabled + 12 + Asynchronous rendering + 0 + com.xpn.xwiki.objects.classes.BooleanClass + + + 0 + Text + code + 10 + Macro code + 20 + 40 + 0 + com.xpn.xwiki.objects.classes.TextAreaClass + + + PureText + 0 + PureText + contentDescription + 9 + Content description (Not applicable for "No content" type) + 5 + 40 + 0 + com.xpn.xwiki.objects.classes.TextAreaClass + + + 0 + Unknown + 0 + input + allowed + 1 + 0 + contentJavaType + 8 + 1 + Macro content type + 0 + | + | + 1 + 0 + Unknown|Wiki + com.xpn.xwiki.objects.classes.StaticListClass + + + 0 + 0 + select + forbidden + 0 + 0 + contentType + 7 + Macro content availability + 0 + | + | + 1 + 0 + Optional|Mandatory|No content + com.xpn.xwiki.objects.classes.StaticListClass + + + 0 + defaultCategory + 4 + Default category + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + PureText + 0 + PureText + description + 3 + Macro description + 5 + 40 + 0 + com.xpn.xwiki.objects.classes.TextAreaClass + + + 0 + id + 1 + Macro id + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + name + 2 + Macro name + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + priority + 11 + integer + Priority + 10 + 0 + com.xpn.xwiki.objects.classes.NumberClass + + + 0 + select + yesno + supportsInlineMode + 5 + Supports inline mode + 0 + com.xpn.xwiki.objects.classes.BooleanClass + + + 0 + 0 + select + forbidden + 0 + 0 + visibility + 6 + Macro visibility + 0 + | + | + 1 + 0 + Current User|Current Wiki|Global + com.xpn.xwiki.objects.classes.StaticListClass + + + + 0 + + + + + + 0 + + + {{include reference='Analytics.Code.Macros.VelocityMacros'/}} +{{velocity}} +#getDate() +#set ($period = 'range') +#set ($parameters = $escapetool.url({ + 'date': $date, + 'period': $$period, + 'limitEntries' : '-1' +})) +#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbVisits', 'entryNbActions', 'bounceRate', 'exitRate']) +#set ($columnsProperties = { + 'date' : { 'type' : 'text', 'sortable' : true }, + 'title' : { 'type' : 'text', 'sortable' : true }, + 'visits' : { 'type' : 'text', 'sortable' : true }, + 'hits' : { 'type' : 'text', 'sortable' : true }, + 'timeSpent' : { 'type' : 'text', 'sortable' : true }, + 'entryNbVisits' : { 'type' : 'text', 'sortable' : true }, + 'entryNbActions' : { 'type' : 'text', 'sortable' : true }, + 'bounceRate' : { 'type' : 'text', 'sortable' : true }, + 'exitRate' : { 'type' : 'text', 'sortable' : true } +}) +#set($options = { + 'translationPrefix': 'analytics.mostViewedPages.header.', + 'resultPage': 'Analytics.Code.Macros.MostViewedPagesMacro.MostViewedPagesJSON', + 'extraParams': $parameters +}) +#livetable('mostViewedPages' $columns $columnsProperties $options) +{{/velocity}} + + + + + + + + + No content + + + + + + + + + mostViewedPage + + + Most Viewed Page + + + + + + 1 + + + Current Wiki + + + + Analytics.Code.Macros.MostViewedPagesMacro.MostViewedPages + 0 + XWiki.WikiMacroParameterClass + 6e3cae3f-3127-4fb2-a590-5dce2f4be0d0 + + XWiki.WikiMacroParameterClass + + + + + + + + + 0 + defaultValue + 4 + Parameter default value + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + description + 2 + Parameter description + 5 + 40 + 0 + com.xpn.xwiki.objects.classes.TextAreaClass + + + 0 + select + yesno + mandatory + 3 + Parameter mandatory + 0 + com.xpn.xwiki.objects.classes.BooleanClass + + + 0 + name + 1 + Parameter name + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + type + 5 + Parameter type + 60 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + + + + + + + + 0 + + + startDate + + + java.util.Date + + + + Analytics.Code.Macros.MostViewedPagesMacro.MostViewedPages + 1 + XWiki.WikiMacroParameterClass + 8a9fb4a7-d7e2-4d7d-84b8-8afb71b4606d + + XWiki.WikiMacroParameterClass + + + + + + + + + 0 + defaultValue + 4 + Parameter default value + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + description + 2 + Parameter description + 5 + 40 + 0 + com.xpn.xwiki.objects.classes.TextAreaClass + + + 0 + select + yesno + mandatory + 3 + Parameter mandatory + 0 + com.xpn.xwiki.objects.classes.BooleanClass + + + 0 + name + 1 + Parameter name + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + type + 5 + Parameter type + 60 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + + + + + + + + 0 + + + endDate + + + java.util.Date + + + diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPagesJSON.xml new file mode 100644 index 00000000..4e3ac6f5 --- /dev/null +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPagesJSON.xml @@ -0,0 +1,108 @@ + + + + + + Analytics.Code.Macros.MostViewedPagesMacro + MostViewedPagesJSON + + + 0 + xwiki:XWiki.Admin + WebHome + xwiki:XWiki.Admin + xwiki:XWiki.Admin + 1.1 + MostViewedPagesJSON + + false + xwiki/2.1 + true + +{{velocity}} +#if ($xcontext.action == 'get') + #set ($offset = $numbertool.toNumber($request.offset).intValue()) + ## The offset sent by the live table starts at 1. + #set ($offset = $offset - 1) + #if (!$offset || $offset < 0) + #set ($offset = 0) + #end + #set ($limit = $numbertool.toNumber($request.limit).intValue()) + #if (!$limit) + #set ($limit = 15) + #end + ## + #set ($keys = { + 'date' : 'date', + 'title' : 'label', + 'visits' : 'nb_visits', + 'hits' : 'nb_hits', + 'timeSpent' : 'sum_time_spent', + 'entryNbVisits' : 'entry_nb_visits', + 'entryNbActions' : 'entry_nb_actions', + 'bounceRate' : 'bounce_rate', + 'exitRate' : 'exit_rate' + }) + #set ($sort = 'nb_hits') + #set ($order = 'desc') + #if ($request.sort && $request.reqNo!='1') + #set($sort = $keys.get($request.sort)) + #set($order = $request.dir) + #end + #set ($parameters = { + 'period' : $request.period, + 'date' : $request.date, + 'module' : 'API', + 'method' : 'Actions.getPageUrls', + 'format' : 'json', + 'filter_limit' : $request.limitEntries, + 'filter_sort_column' : $sort, + 'filter_sort_order' : $order, + 'expanded' : '1', + 'flat' : '1' + }) + #set ($analyticsResult = $services.analytics.getMatomoRequestResult($parameters, 'MostViewedPages')) + #set ($results = { + "totalrows": $analyticsResult.size(), + "returnedrows": $analyticsResult.size(), + "offset": $mathtool.add($offset, 1), + "reqNo": $numbertool.toNumber($request.reqNo).intValue(), + "rows": [] + }) + #foreach ($currentEntry in $analyticsResult) + #set ($url = $currentEntry.get('url').asText()) + #set ($discard = $results.rows.add({ + 'date' : $currentEntry.get('date').asText(), + 'title' : $currentEntry.get('label').asText(), + 'visits' : $currentEntry.get('nb_visits').asText(), + 'hits' : $currentEntry.get('nb_hits').asText(), + 'timeSpent' : $currentEntry.get('sum_time_spent').asText(), + 'entryNbVisits' : $currentEntry.get('entry_nb_visits').asText(), + 'entryNbActions' : $currentEntry.get('entry_nb_actions').asText(), + 'bounceRate' : $currentEntry.get('bounce_rate').asText(), + 'exitRate' : $currentEntry.get('exit_rate').asText() + })) + #end + #jsonResponse($results) +#end +{{/velocity}} + + From b1a5c78e0b397daf56044628893d014129052dc9 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 18 Aug 2023 11:40:42 +0300 Subject: [PATCH 040/105] Move Most Viewed Pages Macro to livetable #10 * Modified the structure of the files --- .../Analytics/Code/Macros/MostViewedPages.xml | 459 ------------------ .../Code/Macros/MostViewedPagesJSON.xml | 108 ----- 2 files changed, 567 deletions(-) delete mode 100644 application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml delete mode 100644 application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml deleted file mode 100644 index 1e7b4a69..00000000 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml +++ /dev/null @@ -1,459 +0,0 @@ - - - - - - Analytics.Code.Macros - MostViewedPages - - - 0 - xwiki:XWiki.Admin - WebHome - xwiki:XWiki.Admin - xwiki:XWiki.Admin - 1.1 - MostViewedPages - - false - xwiki/2.1 - true - {{mostViewedPage/}} - - Analytics.Code.Macros.MostViewedPages - 0 - XWiki.WikiMacroClass - dc943965-6af8-4905-8508-9b8c248c3930 - - XWiki.WikiMacroClass - - - - - - - - - 0 - 0 - select - - async_cached - 13 - Cached - 0 - com.xpn.xwiki.objects.classes.BooleanClass - - - 0 - 0 - select - forbidden - 0 - 1 - async_context - 14 - Context elements - 0 - , - |, - 5 - 0 - action=Action|doc.reference=Document|icon.theme=Icon theme|locale=Language|rendering.defaultsyntax=Default syntax|rendering.restricted=Restricted|rendering.targetsyntax=Target syntax|request.base=Request base URL|request.parameters=Request parameters|request.url=Request URL|request.wiki=Request wiki|user=User|wiki=Wiki - com.xpn.xwiki.objects.classes.StaticListClass - - - 0 - 0 - select - - async_enabled - 12 - Asynchronous rendering - 0 - com.xpn.xwiki.objects.classes.BooleanClass - - - 0 - Text - code - 10 - Macro code - 20 - 40 - 0 - com.xpn.xwiki.objects.classes.TextAreaClass - - - PureText - 0 - PureText - contentDescription - 9 - Content description (Not applicable for "No content" type) - 5 - 40 - 0 - com.xpn.xwiki.objects.classes.TextAreaClass - - - 0 - Unknown - 0 - input - allowed - 1 - 0 - contentJavaType - 8 - 1 - Macro content type - 0 - | - | - 1 - 0 - Unknown|Wiki - com.xpn.xwiki.objects.classes.StaticListClass - - - 0 - 0 - select - forbidden - 0 - 0 - contentType - 7 - Macro content availability - 0 - | - | - 1 - 0 - Optional|Mandatory|No content - com.xpn.xwiki.objects.classes.StaticListClass - - - 0 - defaultCategory - 4 - Default category - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - PureText - 0 - PureText - description - 3 - Macro description - 5 - 40 - 0 - com.xpn.xwiki.objects.classes.TextAreaClass - - - 0 - id - 1 - Macro id - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - 0 - name - 2 - Macro name - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - 0 - priority - 11 - integer - Priority - 10 - 0 - com.xpn.xwiki.objects.classes.NumberClass - - - 0 - select - yesno - supportsInlineMode - 5 - Supports inline mode - 0 - com.xpn.xwiki.objects.classes.BooleanClass - - - 0 - 0 - select - forbidden - 0 - 0 - visibility - 6 - Macro visibility - 0 - | - | - 1 - 0 - Current User|Current Wiki|Global - com.xpn.xwiki.objects.classes.StaticListClass - - - - 0 - - - - - - 0 - - - {{include reference='Analytics.Code.Macros.VelocityMacros'/}} -{{velocity}} -#getDate() -#set ($period = 'range') -#set ($parameters = $escapetool.url({ - 'date': $date, - 'period': $$period, - 'limitEntries' : '-1' -})) -#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbVisits', 'entryNbActions', 'bounceRate', 'exitRate']) -#set ($columnsProperties = { - 'date' : { 'type' : 'text', 'sortable' : true }, - 'title' : { 'type' : 'text', 'sortable' : true }, - 'visits' : { 'type' : 'text', 'sortable' : true }, - 'hits' : { 'type' : 'text', 'sortable' : true }, - 'timeSpent' : { 'type' : 'text', 'sortable' : true }, - 'entryNbVisits' : { 'type' : 'text', 'sortable' : true }, - 'entryNbActions' : { 'type' : 'text', 'sortable' : true }, - 'bounceRate' : { 'type' : 'text', 'sortable' : true }, - 'exitRate' : { 'type' : 'text', 'sortable' : true } -}) -#set($options = { - 'translationPrefix': 'analytics.mostViewedPages.header.', - 'resultPage': 'Analytics.Code.Macros.MostViewedPagesJSON', - 'extraParams': $parameters -}) -#livetable('mostViewedPages' $columns $columnsProperties $options) -{{/velocity}} - - - - - - - - - No content - - - - - - - - - mostViewedPage - - - Most Viewed Page - - - - - - 1 - - - Current Wiki - - - - Analytics.Code.Macros.MostViewedPages - 0 - XWiki.WikiMacroParameterClass - 6e3cae3f-3127-4fb2-a590-5dce2f4be0d0 - - XWiki.WikiMacroParameterClass - - - - - - - - - 0 - defaultValue - 4 - Parameter default value - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - 0 - description - 2 - Parameter description - 5 - 40 - 0 - com.xpn.xwiki.objects.classes.TextAreaClass - - - 0 - select - yesno - mandatory - 3 - Parameter mandatory - 0 - com.xpn.xwiki.objects.classes.BooleanClass - - - 0 - name - 1 - Parameter name - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - 0 - type - 5 - Parameter type - 60 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - - - - - - - - 0 - - - startDate - - - java.util.Date - - - - Analytics.Code.Macros.MostViewedPages - 1 - XWiki.WikiMacroParameterClass - 8a9fb4a7-d7e2-4d7d-84b8-8afb71b4606d - - XWiki.WikiMacroParameterClass - - - - - - - - - 0 - defaultValue - 4 - Parameter default value - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - 0 - description - 2 - Parameter description - 5 - 40 - 0 - com.xpn.xwiki.objects.classes.TextAreaClass - - - 0 - select - yesno - mandatory - 3 - Parameter mandatory - 0 - com.xpn.xwiki.objects.classes.BooleanClass - - - 0 - name - 1 - Parameter name - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - 0 - type - 5 - Parameter type - 60 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - - - - - - - - 0 - - - endDate - - - java.util.Date - - - diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml deleted file mode 100644 index f8800bd0..00000000 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - Analytics.Code.Macros - MostViewedPagesJSON - - - 0 - xwiki:XWiki.Admin - WebHome - xwiki:XWiki.Admin - xwiki:XWiki.Admin - 1.1 - MostViewedPagesJSON - - false - xwiki/2.1 - true - -{{velocity}} -#if ($xcontext.action == 'get') - #set ($offset = $numbertool.toNumber($request.offset).intValue()) - ## The offset sent by the live table starts at 1. - #set ($offset = $offset - 1) - #if (!$offset || $offset < 0) - #set ($offset = 0) - #end - #set ($limit = $numbertool.toNumber($request.limit).intValue()) - #if (!$limit) - #set ($limit = 15) - #end - ## - #set ($keys = { - 'date' : 'date', - 'title' : 'label', - 'visits' : 'nb_visits', - 'hits' : 'nb_hits', - 'timeSpent' : 'sum_time_spent', - 'entryNbVisits' : 'entry_nb_visits', - 'entryNbActions' : 'entry_nb_actions', - 'bounceRate' : 'bounce_rate', - 'exitRate' : 'exit_rate' - }) - #set ($sort = 'nb_hits') - #set ($order = 'desc') - #if ($request.sort && $request.reqNo!='1') - #set($sort = $keys.get($request.sort)) - #set($order = $request.dir) - #end - #set ($parameters = { - 'period' : $request.period, - 'date' : $request.date, - 'module' : 'API', - 'method' : 'Actions.getPageUrls', - 'format' : 'json', - 'filter_limit' : $request.limitEntries, - 'filter_sort_column' : $sort, - 'filter_sort_order' : $order, - 'expanded' : '1', - 'flat' : '1' - }) - #set ($analyticsResult = $services.analytics.getMatomoRequestResult($parameters, 'MostViewedPages')) - #set ($results = { - "totalrows": $analyticsResult.size(), - "returnedrows": $analyticsResult.size(), - "offset": $mathtool.add($offset, 1), - "reqNo": $numbertool.toNumber($request.reqNo).intValue(), - "rows": [] - }) - #foreach ($currentEntry in $analyticsResult) - #set ($url = $currentEntry.get('url').asText()) - #set ($discard = $results.rows.add({ - 'date' : $currentEntry.get('date').asText(), - 'title' : $currentEntry.get('label').asText(), - 'visits' : $currentEntry.get('nb_visits').asText(), - 'hits' : $currentEntry.get('nb_hits').asText(), - 'timeSpent' : $currentEntry.get('sum_time_spent').asText(), - 'entryNbVisits' : $currentEntry.get('entry_nb_visits').asText(), - 'entryNbActions' : $currentEntry.get('entry_nb_actions').asText(), - 'bounceRate' : $currentEntry.get('bounce_rate').asText(), - 'exitRate' : $currentEntry.get('exit_rate').asText() - })) - #end - #jsonResponse($results) -#end -{{/velocity}} - - From b1415fad240977b5e1b3f854e772ef9b3af45d5d Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 18 Aug 2023 12:30:49 +0300 Subject: [PATCH 041/105] Row Evolution feature for the MostViewedPages xwikisas#15 * Updated the structure. --- .../RowEvolutionMostViwedPagesJSON.xml | 52 +++++++++++++++++++ .../Analytics/Code/Macros/RowEvolution.xml | 1 + 2 files changed, 53 insertions(+) create mode 100644 application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/RowEvolutionMostViwedPagesJSON.xml diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/RowEvolutionMostViwedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/RowEvolutionMostViwedPagesJSON.xml new file mode 100644 index 00000000..58c5ce1c --- /dev/null +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/RowEvolutionMostViwedPagesJSON.xml @@ -0,0 +1,52 @@ + + + + + + Analytics.Code.Macros.MostViewedPagesMacro + RowEvolutionMostViwedPagesJSON + + + 0 + xwiki:XWiki.Admin + WebHome + xwiki:XWiki.Admin + xwiki:XWiki.Admin + 1.1 + RowEvolutionMostViwedPagesJSON + + false + xwiki/2.1 + true + {{velocity}} +#set ($parameters ={ + 'period' : $request.period, + 'date' : $request.date, + 'module' : 'API', + 'method' : 'Actions.getPageUrl', + 'format' : 'json', + 'pageUrl' : $request.parameter +}) +#set ($analyticsResult = $services.analytics.getMatomoRequestResult($parameters, 'RowEvolution')) +#jsonResponse($analyticsResult) +{{/velocity}} + + diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml index a09d9b76..b7a021fa 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml @@ -27,6 +27,7 @@ 0 xwiki:XWiki.Admin + WebHome xwiki:XWiki.Admin xwiki:XWiki.Admin 1.1 From 29b2bb9fe2791e65dfc3aa134815d1a8e52b6336 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Mon, 21 Aug 2023 13:36:33 +0300 Subject: [PATCH 042/105] Move Most Viewed Pages Macro to livetable #10 * I removed a field form the live table and updated the naming to avoid duplicates --- .../internal/MostViewedJsonNormaliser.java | 23 ++++++++++++++----- .../Analytics/Code/AnalyticsTranslations.xml | 3 +-- .../Analytics/Code/Macros/MostViewedPages.xml | 3 +-- .../Code/Macros/MostViewedPagesJSON.xml | 2 -- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index b021cd3f..b0b0eedd 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -20,6 +20,8 @@ package com.xwiki.analytics.internal; import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.util.Collections; import java.util.Iterator; @@ -30,6 +32,7 @@ import org.slf4j.Logger; import org.xwiki.component.annotation.Component; +import org.xwiki.model.reference.EntityReferenceSerializer; import org.xwiki.resource.CreateResourceReferenceException; import org.xwiki.resource.CreateResourceTypeException; import org.xwiki.resource.ResourceReference; @@ -48,6 +51,7 @@ import com.xwiki.analytics.JsonNormaliser; import liquibase.repackaged.org.apache.commons.lang3.exception.ExceptionUtils; + /** * The normaliser for the MostViewedMacro. * @@ -67,7 +71,6 @@ public class MostViewedJsonNormaliser implements JsonNormaliser private static final String LABEL = "label"; - @Inject private ResourceReferenceResolver resourceReferenceResolver; @@ -77,6 +80,10 @@ public class MostViewedJsonNormaliser implements JsonNormaliser @Inject private ResourceTypeResolver resourceTypeResolver; + @Inject + @Named("compactwiki") + private EntityReferenceSerializer serializer; + /** * This method will normalise the jsons returned by Matomo into a single format. * @@ -130,7 +137,6 @@ private void processArrayNode(JsonNode jsonNode) * @param jsonNode json object * @return array of jsons */ - private ArrayNode processObjectNode(JsonNode jsonNode) { ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); @@ -161,11 +167,17 @@ private ArrayNode processObjectNode(JsonNode jsonNode) private ResourceReference getResourceReferenceFromStringURL(String resourceReferenceURL) { try { - ExtendedURL extendedURL = new ExtendedURL(new URL(resourceReferenceURL), null); + // The URL provided by Matomo is not encoded and is in string format. To use the resourceTypeResolver, + // I need the URL to be properly encoded. To achieve this, I create a URL object to split the URL into its + // components, and then use the URI constructor to encode the URL. + URL url = new URL(resourceReferenceURL); + URL encodedUrl = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), + url.getQuery(), url.getRef()).toURL(); + ExtendedURL extendedURL = new ExtendedURL(encodedUrl, null); ResourceType resourceType = this.resourceTypeResolver.resolve(extendedURL, Collections.emptyMap()); return this.resourceReferenceResolver.resolve(extendedURL, resourceType, Collections.emptyMap()); } catch (MalformedURLException | CreateResourceReferenceException | CreateResourceTypeException - | UnsupportedResourceReferenceException e) { + | UnsupportedResourceReferenceException | URISyntaxException e) { logger.warn("Failed to get resource reference from URL: [{}].", resourceReferenceURL, ExceptionUtils.getRootCauseMessage(e)); return null; @@ -182,8 +194,7 @@ private void handleURLNode(ObjectNode objNode) EntityResourceReference entityResourceReference = (EntityResourceReference) this.getResourceReferenceFromStringURL(objNode.get(URL).asText()); if (entityResourceReference != null) { - objNode.put(LABEL, - entityResourceReference.getEntityReference().getName()); + objNode.put(LABEL, this.serializer.serialize(entityResourceReference.getEntityReference())); } } } diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml index 477a81b6..5cac1ee0 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml @@ -49,12 +49,11 @@ Analytics.Code.AnalyticsDashboardClass_startDate.hint=Start time of the date int ## Most Viewed Pages Macro analytics.mostViewedPages.header.bounceRate=Bounce rate analytics.mostViewedPages.header.date=Date -analytics.mostViewedPages.header.entryNbVisits=No. of views that started on this page analytics.mostViewedPages.header.entryNbActions= No. of actions analytics.mostViewedPages.header.emptyvalue=- analytics.mostViewedPages.header.exitRate=Exit rate analytics.mostViewedPages.header.hits=Page views -analytics.mostViewedPages.header.timeSpent=Time spent +analytics.mostViewedPages.header.timeSpent=Time spent(s) analytics.mostViewedPages.header.title=Page title analytics.mostViewedPages.header.visits=Unique visitors analytics.mostViewedPages.header.year=Year diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index 1e7b4a69..00527d5e 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -249,14 +249,13 @@ 'period': $$period, 'limitEntries' : '-1' })) -#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbVisits', 'entryNbActions', 'bounceRate', 'exitRate']) +#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbActions', 'bounceRate', 'exitRate']) #set ($columnsProperties = { 'date' : { 'type' : 'text', 'sortable' : true }, 'title' : { 'type' : 'text', 'sortable' : true }, 'visits' : { 'type' : 'text', 'sortable' : true }, 'hits' : { 'type' : 'text', 'sortable' : true }, 'timeSpent' : { 'type' : 'text', 'sortable' : true }, - 'entryNbVisits' : { 'type' : 'text', 'sortable' : true }, 'entryNbActions' : { 'type' : 'text', 'sortable' : true }, 'bounceRate' : { 'type' : 'text', 'sortable' : true }, 'exitRate' : { 'type' : 'text', 'sortable' : true } diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml index 70539303..940c256d 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml @@ -56,7 +56,6 @@ 'visits' : 'nb_visits', 'hits' : 'nb_hits', 'timeSpent' : 'sum_time_spent', - 'entryNbVisits' : 'entry_nb_visits', 'entryNbActions' : 'entry_nb_actions', 'bounceRate' : 'bounce_rate', 'exitRate' : 'exit_rate' @@ -95,7 +94,6 @@ 'visits' : $currentEntry.get('nb_visits').asText(), 'hits' : $currentEntry.get('nb_hits').asText(), 'timeSpent' : $currentEntry.get('sum_time_spent').asText(), - 'entryNbVisits' : $currentEntry.get('entry_nb_visits').asText(), 'entryNbActions' : $currentEntry.get('entry_nb_actions').asText(), 'bounceRate' : $currentEntry.get('bounce_rate').asText(), 'exitRate' : $currentEntry.get('exit_rate').asText() From 809805d9749840e72252210a870e42fbdea6ea60 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Mon, 21 Aug 2023 13:49:03 +0300 Subject: [PATCH 043/105] Move Most Viewed Pages Macro to livetable #10 * I removed a field form the livetable and modified how the title is processed. --- .../internal/MostViewedJsonNormaliser.java | 44 +++++++++++++------ .../MostViewedPagesMacro/MostViewedPages.xml | 3 +- .../MostViewedPagesJSON.xml | 2 - 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index 9687c4df..b0b0eedd 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -20,6 +20,8 @@ package com.xwiki.analytics.internal; import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.util.Collections; import java.util.Iterator; @@ -30,6 +32,7 @@ import org.slf4j.Logger; import org.xwiki.component.annotation.Component; +import org.xwiki.model.reference.EntityReferenceSerializer; import org.xwiki.resource.CreateResourceReferenceException; import org.xwiki.resource.CreateResourceTypeException; import org.xwiki.resource.ResourceReference; @@ -77,19 +80,29 @@ public class MostViewedJsonNormaliser implements JsonNormaliser @Inject private ResourceTypeResolver resourceTypeResolver; - @Override + @Inject + @Named("compactwiki") + private EntityReferenceSerializer serializer; + + /** + * This method will normalise the jsons returned by Matomo into a single format. + * + * @param jsonString the json provided by Matomo. + * @return the normalised json as a string. + */ public JsonNode normaliseData(String jsonString) throws JsonProcessingException { // I need to convert the string returned by matomo in a JSON to easily handle the processing of the nodes. JsonNode jsonRoot = OBJECT_MAPPER.readTree(jsonString); - // In one scenario, when the period is set to day/week/month/year, it returns a JSON object with keys - // representing dates. The corresponding value for each key is an array of JSON objects, each of which - // represents a page. However, if the user sets the period parameter to "range" Matomo returns an array of - // JSON objects, with each JSON object representing a page. Due to these variations, I need to process the - // result from Matomo to create a normalized format. This normalized format is an array of JSON objects and - // each JSON object in this array will have a new field called 'date'. This 'date' field will be set to 'N/A' - // when Matomo returns an array instead of an object. In the 'processArrayNode' and 'processObjectNode' - // methods, I also modify the 'label' field to change it from the raw URL format to the page name. + // Matomo may return several variants of JSON formats. In one scenario, when the period is set to + // day/week/month/year, it returns a JSON object with keys representing dates. The corresponding value for + // each key is an array of JSON objects, each of which represents a page. However, if the user sets the + // period parameter to "range" Matomo returns an array of JSON objects, with each JSON object representing + // a page. Due to these variations, I need to process the result from Matomo to create a normalized format. + // This normalized format is an array of JSON objects and each JSON object in this array will have a new + // field called 'date'. This 'date' field will be set to 'N/A' when Matomo returns an array instead of an + // object. In the 'processArrayNode' and 'processObjectNode' methods, I also modify the 'label' field to + // change it from the raw URL format to the page name. if (jsonRoot.isArray()) { processArrayNode(jsonRoot); } else { @@ -154,11 +167,17 @@ private ArrayNode processObjectNode(JsonNode jsonNode) private ResourceReference getResourceReferenceFromStringURL(String resourceReferenceURL) { try { - ExtendedURL extendedURL = new ExtendedURL(new URL(resourceReferenceURL), null); + // The URL provided by Matomo is not encoded and is in string format. To use the resourceTypeResolver, + // I need the URL to be properly encoded. To achieve this, I create a URL object to split the URL into its + // components, and then use the URI constructor to encode the URL. + URL url = new URL(resourceReferenceURL); + URL encodedUrl = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), + url.getQuery(), url.getRef()).toURL(); + ExtendedURL extendedURL = new ExtendedURL(encodedUrl, null); ResourceType resourceType = this.resourceTypeResolver.resolve(extendedURL, Collections.emptyMap()); return this.resourceReferenceResolver.resolve(extendedURL, resourceType, Collections.emptyMap()); } catch (MalformedURLException | CreateResourceReferenceException | CreateResourceTypeException - | UnsupportedResourceReferenceException e) { + | UnsupportedResourceReferenceException | URISyntaxException e) { logger.warn("Failed to get resource reference from URL: [{}].", resourceReferenceURL, ExceptionUtils.getRootCauseMessage(e)); return null; @@ -175,8 +194,7 @@ private void handleURLNode(ObjectNode objNode) EntityResourceReference entityResourceReference = (EntityResourceReference) this.getResourceReferenceFromStringURL(objNode.get(URL).asText()); if (entityResourceReference != null) { - objNode.put(LABEL, - entityResourceReference.getEntityReference().getName()); + objNode.put(LABEL, this.serializer.serialize(entityResourceReference.getEntityReference())); } } } diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml index adee71d2..b784d783 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml @@ -249,14 +249,13 @@ 'period': $$period, 'limitEntries' : '-1' })) -#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbVisits', 'entryNbActions', 'bounceRate', 'exitRate']) +#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbActions', 'bounceRate', 'exitRate']) #set ($columnsProperties = { 'date' : { 'type' : 'text', 'sortable' : true }, 'title' : { 'type' : 'text', 'sortable' : true }, 'visits' : { 'type' : 'text', 'sortable' : true }, 'hits' : { 'type' : 'text', 'sortable' : true }, 'timeSpent' : { 'type' : 'text', 'sortable' : true }, - 'entryNbVisits' : { 'type' : 'text', 'sortable' : true }, 'entryNbActions' : { 'type' : 'text', 'sortable' : true }, 'bounceRate' : { 'type' : 'text', 'sortable' : true }, 'exitRate' : { 'type' : 'text', 'sortable' : true } diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPagesJSON.xml index 4e3ac6f5..3a706aa3 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPagesJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPagesJSON.xml @@ -56,7 +56,6 @@ 'visits' : 'nb_visits', 'hits' : 'nb_hits', 'timeSpent' : 'sum_time_spent', - 'entryNbVisits' : 'entry_nb_visits', 'entryNbActions' : 'entry_nb_actions', 'bounceRate' : 'bounce_rate', 'exitRate' : 'exit_rate' @@ -95,7 +94,6 @@ 'visits' : $currentEntry.get('nb_visits').asText(), 'hits' : $currentEntry.get('nb_hits').asText(), 'timeSpent' : $currentEntry.get('sum_time_spent').asText(), - 'entryNbVisits' : $currentEntry.get('entry_nb_visits').asText(), 'entryNbActions' : $currentEntry.get('entry_nb_actions').asText(), 'bounceRate' : $currentEntry.get('bounce_rate').asText(), 'exitRate' : $currentEntry.get('exit_rate').asText() From c8f1cf878e97dd3c64ea356170d866c3c9681a60 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Tue, 22 Aug 2023 11:19:01 +0300 Subject: [PATCH 044/105] Move Most Viewed Pages Macro to livetable #10 * Modified some comments and fixed the indentation --- .../com/xwiki/analytics/AnalyticsManager.java | 4 +- .../com/xwiki/analytics/JsonNormaliser.java | 11 +++--- .../configuration/AnalyticsConfiguration.java | 6 +-- application-analytics-default/pom.xml | 10 +---- .../internal/MatomoAnalyticsManager.java | 29 +++++++------- .../internal/MostViewedJsonNormaliser.java | 38 +++++++++++-------- .../script/AnalyticsScriptService.java | 2 +- .../internal/MatomoAnalyticsManagerTest.java | 6 +-- .../MostViewedPagesMacro/MostViewedPages.xml | 6 +-- .../Analytics/Code/Macros/VelocityMacros.xml | 1 - 10 files changed, 56 insertions(+), 57 deletions(-) diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java b/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java index f76e70a2..92745043 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java @@ -43,8 +43,8 @@ public interface AnalyticsManager { /** * This function will make the request to get the data. - * @param jsonNormaliserHint Hint to select the json normaliser. - * @param parameters A list of key, value pairs that will represent the parameters for the request. + * @param jsonNormaliserHint Hint to select the json normaliser + * @param parameters A list of key, value pairs that will represent the parameters for the request * @return A JSON string. */ JsonNode requestData(Map parameters, String jsonNormaliserHint) diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java index bd4f975a..38fdc47f 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java @@ -26,8 +26,8 @@ import com.fasterxml.jackson.databind.JsonNode; /** - * The interface for the json normalisation classes. Matomo may return several variants of JSON formats for easy of - * use in javascript and velocity I need to process them. + * Provides APIs for normalizing data received from Matomo, for easing the afterwards manipulations. Depending on what + * was requested from Matomo, the response can have various formats, which are not consistent or need enhancement. * * @version $Id$ * @since 1.0 @@ -39,10 +39,9 @@ public interface JsonNormaliser /** * Normalise the data returned to have only one format. * - * @param jsonString A string that has a proper json format. + * @param jsonString A string that has a proper json format * @return Returns the json in string format - * @throws JsonProcessingException Throws this error when the jsonString param is not a proper json. + * @throws JsonProcessingException Throws this error when the jsonString param is not a proper json */ - JsonNode normaliseData(String jsonString) - throws JsonProcessingException; + JsonNode normaliseData(String jsonString) throws JsonProcessingException; } diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java b/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java index c17ed84f..ec808b5b 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/configuration/AnalyticsConfiguration.java @@ -33,17 +33,17 @@ public interface AnalyticsConfiguration { /** - * @return the address where the requests will be made. + * @return the address where the requests will be made */ String getRequestAddress(); /** - * @return the id of the site that we want to see the statistics for. + * @return the id of the site that we want to see the statistics for */ String getIdSite(); /** - * @return the Authentication Token that permits to access the statistics. + * @return the authentication token that permits to access the statistics */ String getAuthenticationToken(); } diff --git a/application-analytics-default/pom.xml b/application-analytics-default/pom.xml index 114add86..95ce7b76 100644 --- a/application-analytics-default/pom.xml +++ b/application-analytics-default/pom.xml @@ -46,6 +46,8 @@ jsr311-api + org.xwiki.commons xwiki-commons-tool-test-component @@ -82,14 +84,6 @@ - - org.apache.maven.plugins - maven-compiler-plugin - - 11 - 11 - - \ No newline at end of file diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java index 3715211a..117dc633 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java @@ -56,18 +56,18 @@ public class MatomoAnalyticsManager implements AnalyticsManager private Logger logger; @Inject - @Named("MostViewedPages") + @Named(MostViewedJsonNormaliser.HINT) private JsonNormaliser mostViewedNormaliser; @Inject private AnalyticsConfiguration configuration; /** - * This method will handle all the request made by the user and return a proper json. + * Request specific data from Matomo and return an enhanced response. * - * @param jsonNormaliserHint hint to select the json normaliser. - * @param parameters a list of key, value pairs that will represent the parameters for the request. - * @return will return a json with all the data returned by Matomo. + * @param jsonNormaliserHint hint for the component that will alter the returned response + * @param parameters a list of key, value pairs that will represent the parameters for the request + * @return the altered Matomo request response, as JSON format */ @Override public JsonNode requestData(Map parameters, String jsonNormaliserHint) @@ -81,25 +81,26 @@ public JsonNode requestData(Map parameters, String jsonNormalise throw new RuntimeException("Error occurred while retrieving Matomo statistic results."); } HttpClient client = HttpClient.newHttpClient(); - HttpRequest httpRequest = HttpRequest.newBuilder(buildURI(configuration.getRequestAddress(), parameters)) + HttpRequest httpRequest = HttpRequest.newBuilder(buildURI(parameters)) .build(); HttpResponse response = client.send(httpRequest, HttpResponse.BodyHandlers.ofString()); return jsonNormaliser.normaliseData(response.body()); } /** - * This function will create the URI for the Matomo request. + * Create URI for Matomo request. * - * @param address Address of the Matomo server. - * @param parameterList List of the url parameters. - * @return The final URI in string format. + * @param parameterList List of the url parameters + * @return The final URI in string format */ - private URI buildURI(String address, Map parameterList) + private URI buildURI(Map parameterList) { - UriBuilder uriBuilder = UriBuilder.fromUri(address).path("index.php"); + UriBuilder uriBuilder = UriBuilder.fromUri(configuration.getRequestAddress()).path("index.php"); if (parameterList != null && !parameterList.isEmpty()) { - parameterList.forEach((k, v) -> uriBuilder.queryParam(k, v)); + for (Map.Entry entry : parameterList.entrySet()) { + uriBuilder.queryParam(entry.getKey(), entry.getValue()); + } } return uriBuilder.build(); @@ -107,7 +108,7 @@ private URI buildURI(String address, Map parameterList) private JsonNormaliser getNormaliser(String hint) { - if (hint.equals("MostViewedPages")) { + if (hint.equals(MostViewedJsonNormaliser.HINT)) { return this.mostViewedNormaliser; } return null; diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index b0b0eedd..ccaaa228 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -53,7 +53,7 @@ import liquibase.repackaged.org.apache.commons.lang3.exception.ExceptionUtils; /** - * The normaliser for the MostViewedMacro. + * Implementation for {@link JsonNormaliser}. * * @version $Id$ * @since 1.0 @@ -63,6 +63,10 @@ @Singleton public class MostViewedJsonNormaliser implements JsonNormaliser { + /** + * Hint for the MostViewedJsonNormaliser. + */ + public static final String HINT = "MostViewedPages"; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final String DATE = "date"; @@ -85,24 +89,23 @@ public class MostViewedJsonNormaliser implements JsonNormaliser private EntityReferenceSerializer serializer; /** - * This method will normalise the jsons returned by Matomo into a single format. + * Normalize Matomo response for format consistency and add extra information needed by XWiki. * - * @param jsonString the json provided by Matomo. - * @return the normalised json as a string. + * @param jsonString the Matomo JSON response, in {@code String} format + * @return the normalised json */ public JsonNode normaliseData(String jsonString) throws JsonProcessingException { - // I need to convert the string returned by matomo in a JSON to easily handle the processing of the nodes. + // Convert the string returned by Matomo in a JSON format to easily handle the processing of the nodes. JsonNode jsonRoot = OBJECT_MAPPER.readTree(jsonString); // Matomo may return several variants of JSON formats. In one scenario, when the period is set to // day/week/month/year, it returns a JSON object with keys representing dates. The corresponding value for // each key is an array of JSON objects, each of which represents a page. However, if the user sets the // period parameter to "range" Matomo returns an array of JSON objects, with each JSON object representing - // a page. Due to these variations, I need to process the result from Matomo to create a normalized format. + // a page. Due to these variations, the result needs to be processed to create a single format. // This normalized format is an array of JSON objects and each JSON object in this array will have a new // field called 'date'. This 'date' field will be set to 'N/A' when Matomo returns an array instead of an - // object. In the 'processArrayNode' and 'processObjectNode' methods, I also modify the 'label' field to - // change it from the raw URL format to the page name. + // object. For both type of formats, the label field is also altered in order to contain the full page name if (jsonRoot.isArray()) { processArrayNode(jsonRoot); } else { @@ -112,8 +115,7 @@ public JsonNode normaliseData(String jsonString) throws JsonProcessingException } /** - * Handle the scenario where Matomo returns an array of JSON objects. This function processes each entry to append - * an empty date to it. + * This method processes each entry to append an empty date to it. * * @param jsonNode an array of jsons */ @@ -122,6 +124,7 @@ private void processArrayNode(JsonNode jsonNode) for (JsonNode objNode : jsonNode) { if (objNode.isObject()) { ((ObjectNode) objNode).put(DATE, ""); + // Just in case that this normalsier wil be used in the future for other macros that do not have an url. if (objNode.has(URL)) { this.handleURLNode((ObjectNode) objNode); } @@ -147,6 +150,8 @@ private ArrayNode processObjectNode(JsonNode jsonNode) for (JsonNode objNode : childNode) { if (objNode.isObject()) { ((ObjectNode) objNode).put(DATE, date); + // Just in case that this normalsier wil be used in the future for other macros that do not have an + // url. if (objNode.has(URL)) { this.handleURLNode((ObjectNode) objNode); arrayNode.add(objNode); @@ -161,15 +166,16 @@ private ArrayNode processObjectNode(JsonNode jsonNode) * Process the URL of a page to obtain the documentReference. This is necessary to retrieve the document name for * display when rendering the table. * - * @param resourceReferenceURL the url of the page - * @return + * @param resourceReferenceURL the URL of the page + * @return the reference associated to the given URL, or {@code null} in case it couldn't be resolved */ private ResourceReference getResourceReferenceFromStringURL(String resourceReferenceURL) { try { - // The URL provided by Matomo is not encoded and is in string format. To use the resourceTypeResolver, - // I need the URL to be properly encoded. To achieve this, I create a URL object to split the URL into its - // components, and then use the URI constructor to encode the URL. + + // The URL provided by Matomo is an unencoded string. For utilization with the resourceTypeResolver, + // this URL needs proper encoding. This is achieved by creating a URL object to split the URL into its + // respective components and after that the URL is encoded using the URI constructor. URL url = new URL(resourceReferenceURL); URL encodedUrl = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef()).toURL(); @@ -185,7 +191,7 @@ private ResourceReference getResourceReferenceFromStringURL(String resourceRefer } /** - * Will change the label with the actual name of the page. + * Change the label node to contain the actual page name instead of an URL. * * @param objNode a json object */ diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java index 5d7f9041..46289922 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java @@ -61,7 +61,7 @@ public JsonNode getMatomoRequestResult(Map parameters, String js try { return analyticsManager.requestData(parameters, jsonNormaliserHint); } catch (Exception e) { - throw new RuntimeException("Failed to get data for " + jsonNormaliserHint, e); + throw new RuntimeException(String.format("Failed to get data for [%s]", jsonNormaliserHint), e); } } } diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java index 0f4659fd..8b487da9 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java @@ -25,15 +25,14 @@ import javax.inject.Named; import org.junit.jupiter.api.Test; -import org.mockito.Mock; import org.slf4j.Logger; +import org.xwiki.component.util.ReflectionUtils; import org.xwiki.test.junit5.mockito.ComponentTest; import org.xwiki.test.junit5.mockito.InjectMockComponents; import org.xwiki.test.junit5.mockito.MockComponent; import com.xwiki.analytics.JsonNormaliser; import com.xwiki.analytics.configuration.AnalyticsConfiguration; -import org.xwiki.component.util.ReflectionUtils; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; @@ -67,9 +66,10 @@ public void requestDataWithCorrectHintForNormaliser() throws IOException, Interr when(this.configuration.getAuthenticationToken()).thenReturn("token"); when(this.configuration.getRequestAddress()).thenReturn("http://130.61.233.19/matomo"); when(this.configuration.getIdSite()).thenReturn("3"); - this.matomoAnalyticsManager.requestData(new HashMap<>(), "MostViewedPages"); + this.matomoAnalyticsManager.requestData(new HashMap<>(), MostViewedJsonNormaliser.HINT); verify(this.jsonNormaliser).normaliseData(any(String.class)); } + @Test public void requestDataWithInvalidNormaliser() throws IOException, InterruptedException { diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml index b784d783..67db4a97 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml @@ -245,9 +245,9 @@ #getDate() #set ($period = 'range') #set ($parameters = $escapetool.url({ - 'date': $date, - 'period': $$period, - 'limitEntries' : '-1' + 'date': $date, + 'period': $period, + 'limitEntries': '-1' })) #set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbActions', 'bounceRate', 'exitRate']) #set ($columnsProperties = { diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index d7b560b9..acdd076e 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -59,7 +59,6 @@ #if (!$endDate) #set ($endDate = $datetool.get('yyyy-MM-dd')) #end - ## Prepare the livetable with the fields and properties. #set ($date = $startDate + ',' + $endDate) #end {{/velocity}} From 1223dd29f313a9deaa0eb7ef005a8d0ee64a9800 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Tue, 22 Aug 2023 21:56:28 +0300 Subject: [PATCH 045/105] Move Most Viewed Pages Macro to livetable #10 * Modified the classes used for the http request to be compatible with java 8. --- .../internal/MatomoAnalyticsManager.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java index 117dc633..b63d4484 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java @@ -21,9 +21,7 @@ import java.io.IOException; import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; + import java.util.Map; import javax.inject.Inject; @@ -31,6 +29,11 @@ import javax.inject.Singleton; import javax.ws.rs.core.UriBuilder; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.xwiki.component.annotation.Component; import org.xwiki.stability.Unstable; @@ -80,11 +83,11 @@ public JsonNode requestData(Map parameters, String jsonNormalise logger.warn("There is no JSON normalizer associated with the [{}] hint you provided.", jsonNormaliserHint); throw new RuntimeException("Error occurred while retrieving Matomo statistic results."); } - HttpClient client = HttpClient.newHttpClient(); - HttpRequest httpRequest = HttpRequest.newBuilder(buildURI(parameters)) - .build(); - HttpResponse response = client.send(httpRequest, HttpResponse.BodyHandlers.ofString()); - return jsonNormaliser.normaliseData(response.body()); + HttpClient client = HttpClients.createDefault(); + HttpGet request = new HttpGet(buildURI(parameters)); + HttpResponse response = client.execute(request); + String responseBody = EntityUtils.toString(response.getEntity()); + return jsonNormaliser.normaliseData(responseBody); } /** From d5bb87053c8d1d64822afea248d0c406d5cf4d1a Mon Sep 17 00:00:00 2001 From: Farcasut Date: Tue, 22 Aug 2023 22:21:35 +0300 Subject: [PATCH 046/105] Move Most Viewed Pages Macro to livetable #10 * [MISC] Typo --- .../com/xwiki/analytics/internal/MostViewedJsonNormaliser.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index ccaaa228..4e89629a 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -154,8 +154,9 @@ private ArrayNode processObjectNode(JsonNode jsonNode) // url. if (objNode.has(URL)) { this.handleURLNode((ObjectNode) objNode); - arrayNode.add(objNode); } + arrayNode.add(objNode); + } } } From f48007bccb90115e74497b6fa09c59bf806058e0 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Tue, 22 Aug 2023 22:23:47 +0300 Subject: [PATCH 047/105] Row Evolution feature for the MostViewedPages xwikisas#15 * Bug fix --- .../com/xwiki/analytics/internal/MostViewedJsonNormaliser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index ccaaa228..fa2beab5 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -154,8 +154,8 @@ private ArrayNode processObjectNode(JsonNode jsonNode) // url. if (objNode.has(URL)) { this.handleURLNode((ObjectNode) objNode); - arrayNode.add(objNode); } + arrayNode.add(objNode); } } } From 7c21c511d7b1679e870f3eb0adc52890e8dddc02 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Wed, 23 Aug 2023 15:08:36 +0300 Subject: [PATCH 048/105] Row Evolution feature for the MostViewedPages #15 * [MISC] Typo --- .../internal/MatomoAnalyticsManager.java | 6 ++- .../internal/RowEvolutionJsonNormaliser.java | 4 ++ .../MostViewedPagesMacro/MostViewedPages.xml | 2 +- .../Macros/RowEvolutionMostViwedPagesJSON.xml | 52 ------------------- 4 files changed, 10 insertions(+), 54 deletions(-) delete mode 100644 application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionMostViwedPagesJSON.xml diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java index 79a69294..6a17dc04 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java @@ -61,6 +61,10 @@ public class MatomoAnalyticsManager implements AnalyticsManager @Named(MostViewedJsonNormaliser.HINT) private JsonNormaliser mostViewedNormaliser; + @Inject + @Named(RowEvolutionJsonNormaliser.HINT) + private JsonNormaliser rowEvolution; + @Inject private AnalyticsConfiguration configuration; @@ -112,7 +116,7 @@ private JsonNormaliser getNormaliser(String hint) switch (hint) { case MostViewedJsonNormaliser.HINT: return this.mostViewedNormaliser; - case "RowEvolution": + case RowEvolutionJsonNormaliser.HINT: return this.rowEvolution; default: return null; diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java index bbfe1ebf..2f76e069 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java @@ -46,6 +46,10 @@ @Singleton public class RowEvolutionJsonNormaliser implements JsonNormaliser { + /** + * Hint for the RowEvolution. + */ + public static final String HINT = "RowEvolution"; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final String DATE = "date"; diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml index 9e47926b..2a2103e7 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml @@ -250,7 +250,7 @@ 'period': $period, 'limitEntries': '-1' })) -#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbActions', 'bounceRate', 'exitRate']) +#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbActions', 'bounceRate', 'exitRate','actions']) #set ($columnsProperties = { 'date' : { 'type' : 'text', 'sortable' : true }, 'title' : { 'type' : 'text', 'sortable' : true }, diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionMostViwedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionMostViwedPagesJSON.xml deleted file mode 100644 index 52594fff..00000000 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionMostViwedPagesJSON.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - Analytics.Code.Macros - RowEvolutionMostViwedPagesJSON - - - 0 - xwiki:XWiki.Admin - WebHome - xwiki:XWiki.Admin - xwiki:XWiki.Admin - 1.1 - RowEvolutionMostViwedPagesJSON - - false - xwiki/2.1 - true - {{velocity}} -#set ($parameters ={ - 'period' : $request.period, - 'date' : $request.date, - 'module' : 'API', - 'method' : 'Actions.getPageUrl', - 'format' : 'json', - 'pageUrl' : $request.parameter -}) -#set ($analyticsResult = $services.analytics.getMatomoRequestResult($parameters, 'RowEvolution')) -#jsonResponse($analyticsResult) -{{/velocity}} - - From c35b398452e4bfde1b3400ac67f2a5f34c27f2e7 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Wed, 23 Aug 2023 16:17:27 +0300 Subject: [PATCH 049/105] Row Evolution feature for the MostViewedPages #15 * Added translations for all the fields --- .../Analytics/Code/AnalyticsTranslations.xml | 14 ++ .../MostViewedPagesMacro/MostViewedPages.xml | 2 +- .../Analytics/Code/Macros/VelocityMacros.xml | 146 ++++++++++-------- 3 files changed, 99 insertions(+), 63 deletions(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml index 07ff25cb..39b74a54 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml @@ -62,6 +62,20 @@ analytics.mostViewedPages.header.year=Year analytics.mostViewedPages.parameter.label.startDate=Start time of the date interval used for this macro analytics.mostViewedPages.parameter.label.endDate=End time of the date interval used for this macro +## Row Evolution +analytics.action.label.rowEvolution=Row Evolution +analytics.rowEvolution.label=Row Evolution +analytics.rowEvolution.close=Close +analytics.rowEvolution.period=Period +analytics.rowEvolution.field=Field +analytics.rowEvolution.day=Day +analytics.rowEvolution.week=Week +analytics.rowEvolution.month=Month +analytics.rowEvolution.year=Year +analytics.rowEvolution.field.pageviews=Pageviews +analytics.rowEvolution.field.uniqueViews=Unique views +analytics.rowEvolution.field.avgTime=Avg. Time on page + ## Admin section admin.analytics.application=Analytics Analytics.Code.ConfigurationClass_authToken=Authentication token diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml index 2a2103e7..f1d3f2b8 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml @@ -268,7 +268,7 @@ 'extraParams': $parameters }) #livetable('mostViewedPages' $columns $columnsProperties $options) -#modalRowEvolution({'Pageviews':'nb_hits','Unique Views':'nb_visits', "Avg. time on page":'avg_time_on_page'}, +#modalRowEvolution({'pageviews':'nb_hits','uniqueViews':'nb_visits', "avgTime":'avg_time_on_page'}, 'RowEvolutionMostViwedPagesJSON') {{/velocity}} diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index af8c5f24..63d91fac 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -64,77 +64,99 @@ #macro(setParameterOnAction $parameter) <div class="displayMostViewedPagesActions" data-parameter=$escapetool.url($parameter)> - <a class="move-attachment action" title="$escapetool.xml($services.localization.render('analytics.mostViwedMacros.action.rowEvolution'))" href="#"> - <span class="action-icon">$services.icon.renderHTML('eye')</span><span class="action-label">$escapetool.xml($services.localization.render('analytics.mostViwedMacros.action.action.label.rowEvolution'))</span> + <a class="action" title="$escapetool.xml($services.localization.render( + 'analytics.mostViwedMacros.action.rowEvolution'))" href="#"> + <span class="action-icon">$services.icon.renderHTML('eye')</span><span class="action-label"> + $escapetool.xml($services.localization.render('analytics.action.label.rowEvolution'))</span> </a> </div> #end #macro(modalRowEvolution $fields $requestPage) {{html clean=false}} -<div class="modal fade" id="chartModal" tabindex="-1" role="dialog" aria-labelledby="chartModalLabel" aria-hidden="true"> - <div class="modal-dialog modal-lg"> - <div class="modal-content"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> - <span aria-hidden="true">&times;</span> - </button> - <h4 class="modal-title" id="chartModalLabel">Row Evolution</h4> - </div> - <div class="modal-body"> - <label for="periodSelect">Period</label> - <select id="periodSelect" class="form-control"> - <option value="Day">Day</option> - <option value="Week">Week</option> - <option value="Month">Month</option> - <option value="Year">Year</option> - </select> - <div id="options" data-request-page=$requestPage> - <select id="periodDurationDay" class="form-control"> - <option value="30">30</option> - <option value="8">8</option> - <option value="60">60</option> - <option value="90">90</option> - <option value="180">180</option> - <option value="365">365</option> - <option value="500">500</option> - </select> - <select id="periodDurationWeek" class="form-control"> - <option value="4">4</option> - <option value="12">12</option> - <option value="26">26</option> - <option value="52">52</option> - <option value="104">104</option> - <option value="500">500</option> - </select> - <select id="periodDurationMonth" class="form-control"> - <option value="3">3</option> - <option value="6">6</option> - <option value="12">12</option> - <option value="24">24</option> - <option value="36">36</option> - <option value="120">120</option> - </select> - <select id="periodDurationYear" class="form-control"> - <option value="3">3</option> - <option value="5">5</option> - <option value="10">10</option> - </select> - </div> - <label for="field">Field</label> - <select id="field" class="form-control"> - #foreach($key in $fields.keySet()) - <option value="$fields.get($key)">$key</option> - #end - </select> - <canvas id="chart-display"></canvas> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> - </div> +<div class="modal fade" id="chartModal" tabindex="-1" role="dialog" aria-labelledby="chartModalLabel" + aria-hidden="true"> + <div class="modal-dialog modal-lg"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">&times;</span> + </button> + <h4 class="modal-title" id="chartModalLabel"> + $services.localization.render('analytics.rowEvolution.label') + </h4> + </div> + <div class="modal-body"> + <label for="periodSelect"> + $services.localization.render('analytics.rowEvolution.period') + </label> + <select id="periodSelect" class="form-control"> + <option value="Day"> + $services.localization.render('analytics.rowEvolution.day') + </option> + <option value="Week"> + $services.localization.render('analytics.rowEvolution.week') + </option> + <option value="Month"> + $services.localization.render('analytics.rowEvolution.month') + </option> + <option value="Year"> + $services.localization.render('analytics.rowEvolution.year') + </option> + </select> + <div id="options" data-request-page=$requestPage> + <select id="periodDurationDay" class="form-control"> + <option value="30">30</option> + <option value="8">8</option> + <option value="60">60</option> + <option value="90">90</option> + <option value="180">180</option> + <option value="365">365</option> + <option value="500">500</option> + </select> + <select id="periodDurationWeek" class="form-control"> + <option value="4">4</option> + <option value="12">12</option> + <option value="26">26</option> + <option value="52">52</option> + <option value="104">104</option> + <option value="500">500</option> + </select> + <select id="periodDurationMonth" class="form-control"> + <option value="3">3</option> + <option value="6">6</option> + <option value="12">12</option> + <option value="24">24</option> + <option value="36">36</option> + <option value="120">120</option> + </select> + <select id="periodDurationYear" class="form-control"> + <option value="3">3</option> + <option value="5">5</option> + <option value="10">10</option> + </select> </div> + <label for="field"> + $services.localization.render('analytics.rowEvolution.field') + </label> + <select id="field" class="form-control"> + #foreach($key in $fields.keySet()) + <option value="$fields.get($key)"> + $services.localization.render('analytics.rowEvolution.field.' + $key) + </option> + #end + </select> + <canvas id="chart-display"></canvas> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-default" data-dismiss="modal"> + $services.localization.render('analytics.rowEvolution.close') + </button> + </div> </div> + </div> </div> + {{/html}} #end {{/velocity}} From e665ed58dada56c0b1c2b50ab85c60ea52d98d95 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Wed, 23 Aug 2023 16:19:52 +0300 Subject: [PATCH 050/105] Row Evolution feature for the MostViewedPages #15 * [MISC] Typo --- .../Code/Macros/MostViewedPagesMacro/MostViewedPages.xml | 2 +- .../MostViewedPagesMacro/RowEvolutionMostViwedPagesJSON.xml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml index f1d3f2b8..20c77ba3 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml @@ -269,7 +269,7 @@ }) #livetable('mostViewedPages' $columns $columnsProperties $options) #modalRowEvolution({'pageviews':'nb_hits','uniqueViews':'nb_visits', "avgTime":'avg_time_on_page'}, - 'RowEvolutionMostViwedPagesJSON') + 'RowEvolutionMostViewedPagesJSON') {{/velocity}} diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/RowEvolutionMostViwedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/RowEvolutionMostViwedPagesJSON.xml index 58c5ce1c..14a219f7 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/RowEvolutionMostViwedPagesJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/RowEvolutionMostViwedPagesJSON.xml @@ -20,9 +20,9 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - + Analytics.Code.Macros.MostViewedPagesMacro - RowEvolutionMostViwedPagesJSON + RowEvolutionMostViewedPagesJSON 0 @@ -31,7 +31,7 @@ xwiki:XWiki.Admin xwiki:XWiki.Admin 1.1 - RowEvolutionMostViwedPagesJSON + RowEvolutionMostViewedPagesJSON false xwiki/2.1 From 0a3e6f9986c7799f7711a869f1d825dca7ef782b Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 24 Aug 2023 15:24:07 +0300 Subject: [PATCH 051/105] Row Evolution feature for the MostViewedPages #15 * Added some translations --- .../internal/RowEvolutionJsonNormaliser.java | 12 ++++-------- .../Analytics/Code/AnalyticsTranslations.xml | 9 ++++----- .../Macros/MostViewedPagesMacro/MostViewedPages.xml | 8 ++++++-- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java index 2f76e069..a1908d7f 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java @@ -35,7 +35,7 @@ import com.xwiki.analytics.JsonNormaliser; /** - * The normaliser for the RowEvolution. + * Normalizes the response needed by the RowEvolution feature. * * @version $Id$ * @since 1.0 @@ -46,20 +46,16 @@ @Singleton public class RowEvolutionJsonNormaliser implements JsonNormaliser { - /** + /** * Hint for the RowEvolution. */ public static final String HINT = "RowEvolution"; + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final String DATE = "date"; - /** - * This method will normalise the jsons returned by Matomo into a single format. - * - * @param jsonString the json provided by Matomo. - * @return the normalised json as a JsonNode. - */ + @Override public JsonNode normaliseData(String jsonString) throws JsonProcessingException { JsonNode jsonNode = OBJECT_MAPPER.readTree(jsonString); diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml index 39b74a54..d90766be 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml @@ -54,7 +54,6 @@ analytics.mostViewedPages.header.entryNbActions= No. of actions analytics.mostViewedPages.header.emptyvalue=- analytics.mostViewedPages.header.exitRate=Exit rate analytics.mostViewedPages.header.hits=Page views -analytics.mostViwedMacros.action.action.label.rowEvolution=RowEvolution analytics.mostViewedPages.header.timeSpent=Time spent(s) analytics.mostViewedPages.header.title=Page title analytics.mostViewedPages.header.visits=Unique visitors @@ -68,10 +67,10 @@ analytics.rowEvolution.label=Row Evolution analytics.rowEvolution.close=Close analytics.rowEvolution.period=Period analytics.rowEvolution.field=Field -analytics.rowEvolution.day=Day -analytics.rowEvolution.week=Week -analytics.rowEvolution.month=Month -analytics.rowEvolution.year=Year +analytics.rowEvolution.modal.day.label=Day +analytics.rowEvolution.modal.week.label=Week +analytics.rowEvolution.modal.month.label=Month +analytics.rowEvolution.modal.year.label=Year analytics.rowEvolution.field.pageviews=Pageviews analytics.rowEvolution.field.uniqueViews=Unique views analytics.rowEvolution.field.avgTime=Avg. Time on page diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml index 20c77ba3..fb2f90ef 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml @@ -268,8 +268,12 @@ 'extraParams': $parameters }) #livetable('mostViewedPages' $columns $columnsProperties $options) -#modalRowEvolution({'pageviews':'nb_hits','uniqueViews':'nb_visits', "avgTime":'avg_time_on_page'}, - 'RowEvolutionMostViewedPagesJSON') +#set ($parameters = { + 'pageviews':'nb_hits', + 'uniqueViews':'nb_visits', + "avgTime":'avg_time_on_page' +}) +#modalRowEvolution('mostViewedPagesModal', $parameters, 'RowEvolutionMostViewedPagesJSON') {{/velocity}} From ec6ea6fa01d5d1b521a5d136ecf257ecb567a60d Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 24 Aug 2023 15:44:30 +0300 Subject: [PATCH 052/105] Row Evolution feature for the MostViewedPages #15 * Modified the tracking code and added new macros to create the modal --- .../MostViewedPages.xml | 17 +-- .../MostViewedPagesJSON.xml | 5 +- .../Analytics/Code/Macros/RowEvolution.xml | 109 ++++++---------- ...ml => RowEvolutionMostViewedPagesJSON.xml} | 5 +- .../Analytics/Code/Macros/VelocityMacros.xml | 122 +++++++++--------- 5 files changed, 117 insertions(+), 141 deletions(-) rename application-analytics-ui/src/main/resources/Analytics/Code/Macros/{MostViewedPagesMacro => }/MostViewedPages.xml (96%) rename application-analytics-ui/src/main/resources/Analytics/Code/Macros/{MostViewedPagesMacro => }/MostViewedPagesJSON.xml (96%) rename application-analytics-ui/src/main/resources/Analytics/Code/Macros/{MostViewedPagesMacro/RowEvolutionMostViwedPagesJSON.xml => RowEvolutionMostViewedPagesJSON.xml} (90%) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml similarity index 96% rename from application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml rename to application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index fb2f90ef..05ffd41c 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -20,8 +20,8 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - - Analytics.Code.Macros.MostViewedPagesMacro + + Analytics.Code.Macros MostViewedPages @@ -38,7 +38,7 @@ true {{mostViewedPage/}} - Analytics.Code.Macros.MostViewedPagesMacro.MostViewedPages + Analytics.Code.Macros.MostViewedPages 0 XWiki.WikiMacroClass dc943965-6af8-4905-8508-9b8c248c3930 @@ -264,16 +264,17 @@ }) #set($options = { 'translationPrefix': 'analytics.mostViewedPages.header.', - 'resultPage': 'Analytics.Code.Macros.MostViewedPagesMacro.MostViewedPagesJSON', + 'resultPage': 'Analytics.Code.Macros.MostViewedPagesJSON', 'extraParams': $parameters }) -#livetable('mostViewedPages' $columns $columnsProperties $options) +#getAnalyticsMacroId('mostViwedMacro') +#livetable($macroId $columns $columnsProperties $options) #set ($parameters = { 'pageviews':'nb_hits', 'uniqueViews':'nb_visits', "avgTime":'avg_time_on_page' }) -#modalRowEvolution('mostViewedPagesModal', $parameters, 'RowEvolutionMostViewedPagesJSON') +#modalRowEvolution($parameters, 'RowEvolutionMostViewedPagesJSON') {{/velocity}} @@ -308,7 +309,7 @@ - Analytics.Code.Macros.MostViewedPagesMacro.MostViewedPages + Analytics.Code.Macros.MostViewedPages 0 XWiki.WikiMacroParameterClass 6e3cae3f-3127-4fb2-a590-5dce2f4be0d0 @@ -386,7 +387,7 @@ - Analytics.Code.Macros.MostViewedPagesMacro.MostViewedPages + Analytics.Code.Macros.MostViewedPages 1 XWiki.WikiMacroParameterClass 8a9fb4a7-d7e2-4d7d-84b8-8afb71b4606d diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml similarity index 96% rename from application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPagesJSON.xml rename to application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml index 71bf1c6e..c9609c46 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPagesJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml @@ -20,8 +20,8 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - - Analytics.Code.Macros.MostViewedPagesMacro + + Analytics.Code.Macros MostViewedPagesJSON @@ -87,7 +87,6 @@ "rows": [] }) #foreach ($currentEntry in $analyticsResult) - #set ($url = $currentEntry.get('url').asText()) #set ($url = $currentEntry.get('url').asText()) #set ($discard = $results.rows.add({ 'date' : $currentEntry.get('date').asText(), diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml index b7a021fa..dd267d64 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml @@ -133,7 +133,8 @@ require(['jquery', 'chartjs'], ($) => { const PERIODS = ["Day", "Week", "Month", "Year"]; let chart; - + let currentModal; + let currentOptions; /** * Creates the chart * @@ -141,7 +142,7 @@ require(['jquery', 'chartjs'], ($) => { */ const renderChart = (data) => { const dates = data.map(item => item.date); - const field = data.map(item => item[$('#field').val()] || 0); + const field = data.map(item => item[currentModal.find('.analyticsApplicationRowEvolutionField').val()] || 0); const ctx = $('#chart-display'); if (chart) { chart.destroy(); @@ -166,7 +167,7 @@ require(['jquery', 'chartjs'], ($) => { } } }); - $('#chartModal').modal('show'); + currentModal.modal('show'); }; /** @@ -176,11 +177,10 @@ require(['jquery', 'chartjs'], ($) => { * @param parameter parameter need for the request */ const fetchChartData = (parameter) => { - $('#options').data('parameter', parameter); - const options = $('#options') - const period = options.data('period') - const date = options.data('date'); - const requestPage = options.data('requestPage') + currentOptions.data('parameter', parameter); + const period = currentOptions.data('period') + const date = currentOptions.data('date'); + const requestPage = currentOptions.data('requestPage') $.getJSON(`${requestPage}?parameter=${parameter}&period=${period}&date=last${date}`) .done((data) => { window.sessionStorage.setItem('analyticsLastRequestCache', JSON.stringify(data)); @@ -198,81 +198,56 @@ require(['jquery', 'chartjs'], ($) => { * @param period Day/Week/Month/Year */ const displayPeriodDurations = (period) => { - PERIODS.forEach(p => { - p === period ? $(`#periodDuration${p}`).show() : $(`#periodDuration${p}`).hide(); - }); - $('#options').data('period', period.toLowerCase()); - $('#options').data('date', $(`#periodDuration${period}`).val()); + currentModal.find('[class^="analyticsApplicationRowEvolutionDuration"]').not(`.analyticsRowEvolutionModal .analyticsApplicationRowEvolutionDuration${period}`).hide(); + currentModal.find(`.analyticsApplicationRowEvolutionDuration${period}`).show(); + currentOptions.data('period', period.toLowerCase()); + currentOptions.data('date', $(`.analyticsApplicationRowEvolutionDuration${period}`).val()); }; /** * Resets to the default option for all the selectos when the modal is closed. */ - const restOptions = () => { - $('#chartModal').on('hidden.bs.modal', function () { - PERIODS.forEach(p => { - $(`#periodDuration${p}`).prop('selectedIndex', 0); - }); - $('#field').prop('selectedIndex', 0); - }); - }; - const displayStatisitcs = () => { - $('#field').on('change', function () { - renderChart(JSON.parse(window.sessionStorage.getItem('analyticsLastRequestCache'))); - }); - }; + $(document).on('show.bs.modal', '.analyticsRowEvolutionModal', function (e) + { + console.log('test'); + let modal=$(e.target); + modal.find('[class^="analyticsApplicationRowEvolutionDuration"]').each(function(){$(this).prop('selectedIndex', 0)}) + modal.find('[class^="analyticsApplicationRowEvolutionDuration"]').not('.analyticsApplicationRowEvolutionDurationDay').hide(); + modal.find('analyticsApplicationRowEvolutionDurationDay').show(); + currentOptions.data('period', 'day'); + currentOptions.find('.analyticsRowEvolutionOptions').data('date', $('.analyticsApplicationRowEvolutionDurationDay').val()); + }); + $(document).on('keyup change', '.analyticsRowEvolutionModal .analyticsApplicationRowEvolutionField', function() + { + renderChart(JSON.parse(window.sessionStorage.getItem('analyticsLastRequestCache'))); + }); /** * Binds the event that will open the modal and display the data when the 'Row Evolution' button is clicked. */ - const bindRowEvolutionDisplay = () => { - $(document).on('click', '.displayMostViewedPagesActions', function () { - displayPeriodDurations('Day'); - const parameter = $(this).data('parameter'); - $('#periodSelect').prop('selectedIndex', 0); - $('#options').data('date', '30'); - fetchChartData(parameter); - }); - }; + $(document).on('click', '.xwiki-livetable-container .analyticsActions .analyticsRowEvolution', function () { + const parameter = $(this).data('parameter'); + currentModal = $(this).closest('.xwiki-livetable-container').next('.analyticsRowEvolutionModal'); + currentOptions = currentModal.find('.analyticsRowEvolutionOptions'); + //setting the parameters for the first request to be period=day&date=30 + console.log(currentOptions); + fetchChartData(parameter); + }); /** * Updates the chart when the time */ - const updateOnPeriodChange = () => { - PERIODS.forEach(p => { - $(`#periodDuration${p}`).on('change', function () { - //updates the time intervals to that period options - $('#options').data('date', $(`#periodDuration${p}`).val()); - fetchChartData($('#options').data('parameter')); - }); - }); - }; - - /** - * Changes the chart when the period (day/week/month/year) changes. - */ - const bindPeriodChangeEvent = () => { - $('#periodSelect').on('change', function () { + $(document).on('keyup change', '.analyticsRowEvolutionModal [class^="analyticsApplicationRowEvolutionDuration"]', function() { + //updates the time intervals to that period options + currentOptions.data('date', $(this).val()); + fetchChartData(currentOptions.data('parameter')); + }); + $(document).on("keyup change", '.analyticsRowEvolutionModal .analyticsApplicationModalPeriodSelect',function() { const period = $(this).val(); - restOptions(); displayPeriodDurations(period); - fetchChartData($('#options').data('parameter')); - }); - }; - - const bindEvents = () => { - updateOnPeriodChange(); - restOptions(); - displayStatisitcs(); - bindPeriodChangeEvent(); - bindRowEvolutionDisplay(); - }; - - $(document).ready(() => { - displayPeriodDurations('Day'); - bindEvents(); - }); + fetchChartData(currentOptions.data('parameter')); + }) }); diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/RowEvolutionMostViwedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionMostViewedPagesJSON.xml similarity index 90% rename from application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/RowEvolutionMostViwedPagesJSON.xml rename to application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionMostViewedPagesJSON.xml index 14a219f7..14a1406e 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/RowEvolutionMostViwedPagesJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionMostViewedPagesJSON.xml @@ -20,8 +20,8 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - - Analytics.Code.Macros.MostViewedPagesMacro + + Analytics.Code.Macros RowEvolutionMostViewedPagesJSON @@ -45,6 +45,7 @@ 'format' : 'json', 'pageUrl' : $request.parameter }) + #set ($analyticsResult = $services.analytics.getMatomoRequestResult($parameters, 'RowEvolution')) #jsonResponse($analyticsResult) {{/velocity}} diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index 63d91fac..ffd2c7c7 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -63,18 +63,53 @@ #end #macro(setParameterOnAction $parameter) -<div class="displayMostViewedPagesActions" data-parameter=$escapetool.url($parameter)> - <a class="action" title="$escapetool.xml($services.localization.render( - 'analytics.mostViwedMacros.action.rowEvolution'))" href="#"> - <span class="action-icon">$services.icon.renderHTML('eye')</span><span class="action-label"> - $escapetool.xml($services.localization.render('analytics.action.label.rowEvolution'))</span> - </a> +<div class="analyticsActions"> + <div class="analyticsRowEvolution" data-parameter=$escapetool.url($parameter)> + <a class="action" title="$escapetool.xml($services.localization.render( + 'analytics.mostViwedMacros.action.rowEvolution'))" href="#"> + <span class="action-icon">$services.icon.renderHTML('eye')</span><span class="action-label"> + $escapetool.xml($services.localization.render('analytics.action.label.rowEvolution'))</span> + </a> + </div> </div> #end +#macro(durationSelect $className $options) +<select class="$className form-control"> + #foreach($option in $options) + <option value="$option">$option</option> + #end +</select> +#end + + +#macro(durationOption $value) + <option value="$value"> + $services.localization.render('analytics.rowEvolution.modal.' + $value.toLowerCase() + '.label') + </option> +#end + +#macro(durationSelectList $class $options) + <select class="$class form-control"> + #foreach($option in $options) + <option value="$option">$option</option> + #end + </select> +#end + +#macro(fieldsSelect $fields) + <select class="form-control analyticsApplicationRowEvolutionField"> + #foreach($key in $fields.keySet()) + <option value="$fields.get($key)"> + $services.localization.render('analytics.rowEvolution.field.' + $key) + </option> + #end + </select> +#end + #macro(modalRowEvolution $fields $requestPage) {{html clean=false}} -<div class="modal fade" id="chartModal" tabindex="-1" role="dialog" aria-labelledby="chartModalLabel" +<div class="modal fade analyticsRowEvolutionModal" id="chartModal" tabindex="-1" role="dialog" aria-labelledby="chartModalLabel" aria-hidden="true"> <div class="modal-dialog modal-lg"> <div class="modal-content"> @@ -87,65 +122,25 @@ </h4> </div> <div class="modal-body"> - <label for="periodSelect"> + <label for="analyticsApplicationModalPeriodSelect"> $services.localization.render('analytics.rowEvolution.period') </label> - <select id="periodSelect" class="form-control"> - <option value="Day"> - $services.localization.render('analytics.rowEvolution.day') - </option> - <option value="Week"> - $services.localization.render('analytics.rowEvolution.week') - </option> - <option value="Month"> - $services.localization.render('analytics.rowEvolution.month') - </option> - <option value="Year"> - $services.localization.render('analytics.rowEvolution.year') - </option> + <select class="analyticsApplicationModalPeriodSelect form-control"> + #durationOption("Day") + #durationOption("Week") + #durationOption("Month") + #durationOption("Year") </select> - <div id="options" data-request-page=$requestPage> - <select id="periodDurationDay" class="form-control"> - <option value="30">30</option> - <option value="8">8</option> - <option value="60">60</option> - <option value="90">90</option> - <option value="180">180</option> - <option value="365">365</option> - <option value="500">500</option> - </select> - <select id="periodDurationWeek" class="form-control"> - <option value="4">4</option> - <option value="12">12</option> - <option value="26">26</option> - <option value="52">52</option> - <option value="104">104</option> - <option value="500">500</option> - </select> - <select id="periodDurationMonth" class="form-control"> - <option value="3">3</option> - <option value="6">6</option> - <option value="12">12</option> - <option value="24">24</option> - <option value="36">36</option> - <option value="120">120</option> - </select> - <select id="periodDurationYear" class="form-control"> - <option value="3">3</option> - <option value="5">5</option> - <option value="10">10</option> - </select> + <div class="analyticsRowEvolutionOptions" data-request-page=$requestPage data-period='day' data-date='30'> + #durationSelectList("analyticsApplicationRowEvolutionDurationDay" [30, 8, 60, 90, 180, 365, 500]) + #durationSelectList("analyticsApplicationRowEvolutionDurationWeek" [4, 12, 26, 52, 104, 500]) + #durationSelectList("analyticsApplicationRowEvolutionDurationMonth" [3, 6, 12, 24, 36, 120]) + #durationSelectList("analyticsApplicationRowEvolutionDurationYear" [3, 5, 10]) </div> - <label for="field"> + <label for="analyticsApplicationRowEvolutionField"> $services.localization.render('analytics.rowEvolution.field') </label> - <select id="field" class="form-control"> - #foreach($key in $fields.keySet()) - <option value="$fields.get($key)"> - $services.localization.render('analytics.rowEvolution.field.' + $key) - </option> - #end - </select> + #fieldsSelect($fields) <canvas id="chart-display"></canvas> </div> <div class="modal-footer"> @@ -156,8 +151,13 @@ </div> </div> </div> - {{/html}} #end + +#set ($analyticsId = -1) +#macro (getAnalyticsMacroId $macro) + #set ($analyticsId = $analyticsId + 1) + #set ($macroId = $macro + '_' + $analyticsId) +#end {{/velocity}} From d02c05a8fa8b5f06cdf9e87e1e9766e8e0dfa04a Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 24 Aug 2023 16:06:44 +0300 Subject: [PATCH 053/105] Row Evolution feature for the MostViewedPages #15 * Renamed the RowEvolutionMostViewedPagesJSON.xml to RowEvolutionJson.xml and solved the request url problem. --- .../src/main/resources/Analytics/Code/Configuration.xml | 3 +-- .../resources/Analytics/Code/Macros/MostViewedPages.xml | 2 +- ...volutionMostViewedPagesJSON.xml => RowEvolutionJson.xml} | 6 +++--- .../main/resources/Analytics/Code/Macros/VelocityMacros.xml | 5 +++-- 4 files changed, 8 insertions(+), 8 deletions(-) rename application-analytics-ui/src/main/resources/Analytics/Code/Macros/{RowEvolutionMostViewedPagesJSON.xml => RowEvolutionJson.xml} (92%) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Configuration.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Configuration.xml index f51f3a21..3cf784f5 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Configuration.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Configuration.xml @@ -114,8 +114,7 @@ - - + diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index 05ffd41c..28055694 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -274,7 +274,7 @@ 'uniqueViews':'nb_visits', "avgTime":'avg_time_on_page' }) -#modalRowEvolution($parameters, 'RowEvolutionMostViewedPagesJSON') +#modalRowEvolution($parameters, 'Analytics.Code.Macros.RowEvolutionJson') {{/velocity}} diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionMostViewedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJson.xml similarity index 92% rename from application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionMostViewedPagesJSON.xml rename to application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJson.xml index 14a1406e..72570245 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionMostViewedPagesJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJson.xml @@ -20,9 +20,9 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - + Analytics.Code.Macros - RowEvolutionMostViewedPagesJSON + RowEvolutionJson 0 @@ -31,7 +31,7 @@ xwiki:XWiki.Admin xwiki:XWiki.Admin 1.1 - RowEvolutionMostViewedPagesJSON + RowEvolutionJson false xwiki/2.1 diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index ffd2c7c7..23080262 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -107,7 +107,8 @@ </select> #end -#macro(modalRowEvolution $fields $requestPage) +#macro(modalRowEvolution $fields $requestPageName) +#set ($requestPageURL = $xwiki.getURL($requestPageName)) {{html clean=false}} <div class="modal fade analyticsRowEvolutionModal" id="chartModal" tabindex="-1" role="dialog" aria-labelledby="chartModalLabel" aria-hidden="true"> @@ -131,7 +132,7 @@ #durationOption("Month") #durationOption("Year") </select> - <div class="analyticsRowEvolutionOptions" data-request-page=$requestPage data-period='day' data-date='30'> + <div class="analyticsRowEvolutionOptions" data-request-page=$requestPageURL data-period='day' data-date='30'> #durationSelectList("analyticsApplicationRowEvolutionDurationDay" [30, 8, 60, 90, 180, 365, 500]) #durationSelectList("analyticsApplicationRowEvolutionDurationWeek" [4, 12, 26, 52, 104, 500]) #durationSelectList("analyticsApplicationRowEvolutionDurationMonth" [3, 6, 12, 24, 36, 120]) From e3e267d530a24ae27500803fa747d4c73db777d9 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 24 Aug 2023 21:52:18 +0300 Subject: [PATCH 054/105] Row Evolution feature for the MostViewedPages #15 * Removed an ID that was left and solved a bug --- .../Analytics/Code/Macros/RowEvolution.xml | 15 +++++++++------ .../Analytics/Code/Macros/VelocityMacros.xml | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml index dd267d64..68f929d6 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml @@ -143,7 +143,7 @@ require(['jquery', 'chartjs'], ($) => { const renderChart = (data) => { const dates = data.map(item => item.date); const field = data.map(item => item[currentModal.find('.analyticsApplicationRowEvolutionField').val()] || 0); - const ctx = $('#chart-display'); + const ctx = currentModal.find('.analyticsChartisplay'); if (chart) { chart.destroy(); } @@ -210,11 +210,11 @@ require(['jquery', 'chartjs'], ($) => { $(document).on('show.bs.modal', '.analyticsRowEvolutionModal', function (e) { - console.log('test'); let modal=$(e.target); modal.find('[class^="analyticsApplicationRowEvolutionDuration"]').each(function(){$(this).prop('selectedIndex', 0)}) + modal.find('.analyticsApplicationRowEvolutionField').prop('selectedIndex', 0); modal.find('[class^="analyticsApplicationRowEvolutionDuration"]').not('.analyticsApplicationRowEvolutionDurationDay').hide(); - modal.find('analyticsApplicationRowEvolutionDurationDay').show(); + modal.find('.analyticsApplicationRowEvolutionDurationDay').show(); currentOptions.data('period', 'day'); currentOptions.find('.analyticsRowEvolutionOptions').data('date', $('.analyticsApplicationRowEvolutionDurationDay').val()); }); @@ -226,7 +226,7 @@ require(['jquery', 'chartjs'], ($) => { /** * Binds the event that will open the modal and display the data when the 'Row Evolution' button is clicked. */ - $(document).on('click', '.xwiki-livetable-container .analyticsActions .analyticsRowEvolution', function () { + $(document).on('click', '.analyticsActions .analyticsRowEvolution', function () { const parameter = $(this).data('parameter'); currentModal = $(this).closest('.xwiki-livetable-container').next('.analyticsRowEvolutionModal'); currentOptions = currentModal.find('.analyticsRowEvolutionOptions'); @@ -241,15 +241,18 @@ require(['jquery', 'chartjs'], ($) => { $(document).on('keyup change', '.analyticsRowEvolutionModal [class^="analyticsApplicationRowEvolutionDuration"]', function() { //updates the time intervals to that period options currentOptions.data('date', $(this).val()); - fetchChartData(currentOptions.data('parameter')); + fetchChartData(currentOptions.data('parameter')); }); $(document).on("keyup change", '.analyticsRowEvolutionModal .analyticsApplicationModalPeriodSelect',function() { const period = $(this).val(); displayPeriodDurations(period); fetchChartData(currentOptions.data('parameter')); }) + + $(document).ready(() => { + }); }); - + diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index 23080262..1730bbcc 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -118,7 +118,7 @@ <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">&times;</span> </button> - <h4 class="modal-title" id="chartModalLabel"> + <h4 class="modal-title" class="chartModalLabel"> $services.localization.render('analytics.rowEvolution.label') </h4> </div> @@ -142,7 +142,7 @@ $services.localization.render('analytics.rowEvolution.field') </label> #fieldsSelect($fields) - <canvas id="chart-display"></canvas> + <canvas class="analyticsApplicationRowEvolutionField"></canvas> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal"> From b64347a7a153b7c1ae0456abe8d007ae51f0d93f Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 24 Aug 2023 21:55:51 +0300 Subject: [PATCH 055/105] Row Evolution feature for the MostViewedPages #15 * [MISC] removed an id --- .../main/resources/Analytics/Code/Macros/VelocityMacros.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index 1730bbcc..027069fa 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -110,7 +110,7 @@ #macro(modalRowEvolution $fields $requestPageName) #set ($requestPageURL = $xwiki.getURL($requestPageName)) {{html clean=false}} -<div class="modal fade analyticsRowEvolutionModal" id="chartModal" tabindex="-1" role="dialog" aria-labelledby="chartModalLabel" +<div class="modal fade analyticsRowEvolutionModal" tabindex="-1" role="dialog" aria-labelledby="chartModalLabel" aria-hidden="true"> <div class="modal-dialog modal-lg"> <div class="modal-content"> @@ -142,7 +142,7 @@ $services.localization.render('analytics.rowEvolution.field') </label> #fieldsSelect($fields) - <canvas class="analyticsApplicationRowEvolutionField"></canvas> + <canvas class="analyticsChartisplay"></canvas> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal"> From 075aa9986b24d4603b7c926449d74fd3beead78a Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 25 Aug 2023 10:31:19 +0300 Subject: [PATCH 056/105] Row Evolution feature for the MostViewedPages #15 * I added comments and modified the json page to be more general --- .../Analytics/Code/Macros/MostViewedPages.xml | 2 +- .../Analytics/Code/Macros/RowEvolution.xml | 60 ++++++++++--------- .../Code/Macros/RowEvolutionJson.xml | 7 ++- .../Analytics/Code/Macros/VelocityMacros.xml | 6 +- 4 files changed, 39 insertions(+), 36 deletions(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index 28055694..d5f2fac2 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -274,7 +274,7 @@ 'uniqueViews':'nb_visits', "avgTime":'avg_time_on_page' }) -#modalRowEvolution($parameters, 'Analytics.Code.Macros.RowEvolutionJson') +#modalRowEvolution($parameters, 'MostViewedMacro') {{/velocity}} diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml index 68f929d6..7e40ba86 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml @@ -136,7 +136,7 @@ require(['jquery', 'chartjs'], ($) => { let currentModal; let currentOptions; /** - * Creates the chart + * Creates the chart for displaying the data. * * @param data json with the dataset for the current page. */ @@ -174,14 +174,15 @@ require(['jquery', 'chartjs'], ($) => { * Fetches the data from Matomo using the XWiki server as a proxy through the RowEvolutionJSON page. It then saves the data in session * storage for easy access. This allows the user to display a different field for the same time interval without making a new request and updates the chart. * - * @param parameter parameter need for the request + * @param parameter that will identify the row in the matomo table */ const fetchChartData = (parameter) => { currentOptions.data('parameter', parameter); - const period = currentOptions.data('period') + const period = currentOptions.data('period'); const date = currentOptions.data('date'); - const requestPage = currentOptions.data('requestPage') - $.getJSON(`${requestPage}?parameter=${parameter}&period=${period}&date=last${date}`) + const requestPage = currentOptions.data('requestPage'); + const macroName=currentOptions.data('macroName'); + $.getJSON(`${requestPage}?parameter=${parameter}&period=${period}&date=last${date}&macroName=${macroName}`) .done((data) => { window.sessionStorage.setItem('analyticsLastRequestCache', JSON.stringify(data)); renderChart(data); @@ -193,9 +194,9 @@ require(['jquery', 'chartjs'], ($) => { }; /** - * Will show only the time options available for the period given as a parameter and updates the options + * Would search for the element that matches the current period, display its time, and hide the other periods time. * - * @param period Day/Week/Month/Year + * @param period period day/week/month/year */ const displayPeriodDurations = (period) => { currentModal.find('[class^="analyticsApplicationRowEvolutionDuration"]').not(`.analyticsRowEvolutionModal .analyticsApplicationRowEvolutionDuration${period}`).hide(); @@ -205,54 +206,55 @@ require(['jquery', 'chartjs'], ($) => { }; /** - * Resets to the default option for all the selectos when the modal is closed. + * Resets all the selectors for the current modal when opened. */ - $(document).on('show.bs.modal', '.analyticsRowEvolutionModal', function (e) - { - let modal=$(e.target); - modal.find('[class^="analyticsApplicationRowEvolutionDuration"]').each(function(){$(this).prop('selectedIndex', 0)}) + $(document).on('show.bs.modal', '.analyticsRowEvolutionModal', function (e) { + let modal = $(e.target); + modal.find('[class^="analyticsApplicationRowEvolutionDuration"]').each(function () { $(this).prop('selectedIndex', 0) }) modal.find('.analyticsApplicationRowEvolutionField').prop('selectedIndex', 0); modal.find('[class^="analyticsApplicationRowEvolutionDuration"]').not('.analyticsApplicationRowEvolutionDurationDay').hide(); modal.find('.analyticsApplicationRowEvolutionDurationDay').show(); currentOptions.data('period', 'day'); currentOptions.find('.analyticsRowEvolutionOptions').data('date', $('.analyticsApplicationRowEvolutionDurationDay').val()); }); - $(document).on('keyup change', '.analyticsRowEvolutionModal .analyticsApplicationRowEvolutionField', function() - { + $(document).on('keyup change', '.analyticsRowEvolutionModal .analyticsApplicationRowEvolutionField', function () { renderChart(JSON.parse(window.sessionStorage.getItem('analyticsLastRequestCache'))); }); /** - * Binds the event that will open the modal and display the data when the 'Row Evolution' button is clicked. + * Handles the click event on the RowEvolution buttons. It will save the current modal and current options div for easy of use and + * fetches the data for the default parameters. */ $(document).on('click', '.analyticsActions .analyticsRowEvolution', function () { const parameter = $(this).data('parameter'); - currentModal = $(this).closest('.xwiki-livetable-container').next('.analyticsRowEvolutionModal'); + currentModal = $(this).closest('.xwiki-livetable-container').next('.analyticsRowEvolutionModal'); currentOptions = currentModal.find('.analyticsRowEvolutionOptions'); - //setting the parameters for the first request to be period=day&date=30 - console.log(currentOptions); fetchChartData(parameter); }); - + /** - * Updates the chart when the time + * Handles the update of the chart when the time is changed. + * */ - $(document).on('keyup change', '.analyticsRowEvolutionModal [class^="analyticsApplicationRowEvolutionDuration"]', function() { - //updates the time intervals to that period options + $(document).on('keyup change', '.analyticsRowEvolutionModal [class^="analyticsApplicationRowEvolutionDuration"]', function () { currentOptions.data('date', $(this).val()); - fetchChartData(currentOptions.data('parameter')); + fetchChartData(currentOptions.data('parameter')); }); - $(document).on("keyup change", '.analyticsRowEvolutionModal .analyticsApplicationModalPeriodSelect',function() { - const period = $(this).val(); - displayPeriodDurations(period); - fetchChartData(currentOptions.data('parameter')); - }) + + /** + * Handles the update of the chart when the period is changed. + */ + $(document).on("keyup change", '.analyticsRowEvolutionModal .analyticsApplicationModalPeriodSelect', function () { + const period = $(this).val(); + displayPeriodDurations(period); + fetchChartData(currentOptions.data('parameter')); + }) $(document).ready(() => { }); }); - + diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJson.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJson.xml index 72570245..c97ce1a7 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJson.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJson.xml @@ -42,10 +42,11 @@ 'date' : $request.date, 'module' : 'API', 'method' : 'Actions.getPageUrl', - 'format' : 'json', - 'pageUrl' : $request.parameter + 'format' : 'json' }) - +#if ($request.macroName=='MostViewedMacro') + $parameters.put('pageUrl', $request.parameter) +#end #set ($analyticsResult = $services.analytics.getMatomoRequestResult($parameters, 'RowEvolution')) #jsonResponse($analyticsResult) {{/velocity}} diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index 027069fa..ceda526e 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -107,8 +107,8 @@ </select> #end -#macro(modalRowEvolution $fields $requestPageName) -#set ($requestPageURL = $xwiki.getURL($requestPageName)) +#macro(modalRowEvolution $fields $macroName) +#set ($requestPageURL = $xwiki.getURL('Analytics.Code.Macros.RowEvolutionJson')) {{html clean=false}} <div class="modal fade analyticsRowEvolutionModal" tabindex="-1" role="dialog" aria-labelledby="chartModalLabel" aria-hidden="true"> @@ -132,7 +132,7 @@ #durationOption("Month") #durationOption("Year") </select> - <div class="analyticsRowEvolutionOptions" data-request-page=$requestPageURL data-period='day' data-date='30'> + <div class="analyticsRowEvolutionOptions" data-macro-name=$macroName data-request-page=$requestPageURL data-period='day' data-date='30'> #durationSelectList("analyticsApplicationRowEvolutionDurationDay" [30, 8, 60, 90, 180, 365, 500]) #durationSelectList("analyticsApplicationRowEvolutionDurationWeek" [4, 12, 26, 52, 104, 500]) #durationSelectList("analyticsApplicationRowEvolutionDurationMonth" [3, 6, 12, 24, 36, 120]) From 46543a8e7601bd2772414bf7ab9b14e40ad68ffe Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 25 Aug 2023 13:06:57 +0300 Subject: [PATCH 057/105] Row Evolution feature for the MostViewedPages #15 * Added a loading animation and solved a bug where the period wouldn't reset. --- .../Analytics/Code/Macros/RowEvolution.xml | 24 +++++++++++-------- .../Analytics/Code/Macros/VelocityMacros.xml | 4 +++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml index 7e40ba86..1de3b497 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml @@ -135,6 +135,7 @@ require(['jquery', 'chartjs'], ($) => { let chart; let currentModal; let currentOptions; + let currentLoadingAnimation; /** * Creates the chart for displaying the data. * @@ -167,21 +168,21 @@ require(['jquery', 'chartjs'], ($) => { } } }); - currentModal.modal('show'); + currentLoadingAnimation.removeClass('loading'); }; /** * Fetches the data from Matomo using the XWiki server as a proxy through the RowEvolutionJSON page. It then saves the data in session * storage for easy access. This allows the user to display a different field for the same time interval without making a new request and updates the chart. * - * @param parameter that will identify the row in the matomo table + * @param parameter parameter need for the request */ const fetchChartData = (parameter) => { currentOptions.data('parameter', parameter); const period = currentOptions.data('period'); const date = currentOptions.data('date'); const requestPage = currentOptions.data('requestPage'); - const macroName=currentOptions.data('macroName'); + const macroName = currentOptions.data('macroName'); $.getJSON(`${requestPage}?parameter=${parameter}&period=${period}&date=last${date}&macroName=${macroName}`) .done((data) => { window.sessionStorage.setItem('analyticsLastRequestCache', JSON.stringify(data)); @@ -212,24 +213,30 @@ require(['jquery', 'chartjs'], ($) => { $(document).on('show.bs.modal', '.analyticsRowEvolutionModal', function (e) { let modal = $(e.target); modal.find('[class^="analyticsApplicationRowEvolutionDuration"]').each(function () { $(this).prop('selectedIndex', 0) }) + modal.find('.analyticsRowEvolutionOptions').data('date', 30); + modal.find('.analyticsRowEvolutionOptions').data('period', 'day'); modal.find('.analyticsApplicationRowEvolutionField').prop('selectedIndex', 0); + modal.find('.analyticsApplicationModalPeriodSelect').prop('selectedIndex', 0); modal.find('[class^="analyticsApplicationRowEvolutionDuration"]').not('.analyticsApplicationRowEvolutionDurationDay').hide(); modal.find('.analyticsApplicationRowEvolutionDurationDay').show(); - currentOptions.data('period', 'day'); - currentOptions.find('.analyticsRowEvolutionOptions').data('date', $('.analyticsApplicationRowEvolutionDurationDay').val()); }); $(document).on('keyup change', '.analyticsRowEvolutionModal .analyticsApplicationRowEvolutionField', function () { renderChart(JSON.parse(window.sessionStorage.getItem('analyticsLastRequestCache'))); }); - + $(document).on('hide.bs.modal', '.analyticsRowEvolutionModal', function (e) { + chart.destroy(); + currentLoadingAnimation.addClass('loading'); + }); /** * Handles the click event on the RowEvolution buttons. It will save the current modal and current options div for easy of use and - * fetches the data for the default parameters. + * fetchs the data for the default parameters. */ $(document).on('click', '.analyticsActions .analyticsRowEvolution', function () { const parameter = $(this).data('parameter'); currentModal = $(this).closest('.xwiki-livetable-container').next('.analyticsRowEvolutionModal'); currentOptions = currentModal.find('.analyticsRowEvolutionOptions'); + currentLoadingAnimation = currentModal.find('.loading'); + currentModal.modal('show'); fetchChartData(parameter); }); @@ -250,9 +257,6 @@ require(['jquery', 'chartjs'], ($) => { displayPeriodDurations(period); fetchChartData(currentOptions.data('parameter')); }) - - $(document).ready(() => { - }); }); diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index ceda526e..32c7e5b1 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -142,7 +142,9 @@ $services.localization.render('analytics.rowEvolution.field') </label> #fieldsSelect($fields) - <canvas class="analyticsChartisplay"></canvas> + <div class='loading'> + <canvas class="analyticsChartisplay"></canvas> + </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal"> From f08de2e85c1f043b50159443e7cb9abfc2b5d130 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 25 Aug 2023 16:36:43 +0300 Subject: [PATCH 058/105] Row Evolution feature for the MostViewedPages #15 * Modified the name of the css classes. --- .../Analytics/Code/AnalyticsTranslations.xml | 3 +- .../Analytics/Code/Macros/MostViewedPages.xml | 2 +- .../Code/Macros/MostViewedPagesJSON.xml | 2 +- .../Analytics/Code/Macros/RowEvolution.xml | 31 ++++---- .../Analytics/Code/Macros/VelocityMacros.xml | 73 +++++++++++-------- 5 files changed, 63 insertions(+), 48 deletions(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml index d90766be..73c83e6b 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsTranslations.xml @@ -67,13 +67,14 @@ analytics.rowEvolution.label=Row Evolution analytics.rowEvolution.close=Close analytics.rowEvolution.period=Period analytics.rowEvolution.field=Field +analytics.rowEvolution.periodLength=Period length analytics.rowEvolution.modal.day.label=Day analytics.rowEvolution.modal.week.label=Week analytics.rowEvolution.modal.month.label=Month analytics.rowEvolution.modal.year.label=Year analytics.rowEvolution.field.pageviews=Pageviews analytics.rowEvolution.field.uniqueViews=Unique views -analytics.rowEvolution.field.avgTime=Avg. Time on page +analytics.rowEvolution.field.avgTime=Avg. Time on ## Admin section admin.analytics.application=Analytics diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index d5f2fac2..0ae6e06d 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -267,7 +267,7 @@ 'resultPage': 'Analytics.Code.Macros.MostViewedPagesJSON', 'extraParams': $parameters }) -#getAnalyticsMacroId('mostViwedMacro') +#getAnalyticsMacroId('mostViwedMacro', $macroId ) #livetable($macroId $columns $columnsProperties $options) #set ($parameters = { 'pageviews':'nb_hits', diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml index c9609c46..9548cf06 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml @@ -97,7 +97,7 @@ 'entryNbActions' : $currentEntry.get('entry_nb_actions').asText(), 'bounceRate' : $currentEntry.get('bounce_rate').asText(), 'exitRate' : $currentEntry.get('exit_rate').asText(), - 'actions' : "#setParameterOnAction($url)" + 'actions' : "#analyticsActions($url)" })) #end #jsonResponse($results) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml index 1de3b497..2279723a 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml @@ -136,6 +136,7 @@ require(['jquery', 'chartjs'], ($) => { let currentModal; let currentOptions; let currentLoadingAnimation; + /** * Creates the chart for displaying the data. * @@ -143,8 +144,8 @@ require(['jquery', 'chartjs'], ($) => { */ const renderChart = (data) => { const dates = data.map(item => item.date); - const field = data.map(item => item[currentModal.find('.analyticsApplicationRowEvolutionField').val()] || 0); - const ctx = currentModal.find('.analyticsChartisplay'); + const field = data.map(item => item[currentModal.find('.rowEvolutionField').val()] || 0); + const ctx = currentModal.find('.chartDisplay'); if (chart) { chart.destroy(); } @@ -181,9 +182,9 @@ require(['jquery', 'chartjs'], ($) => { currentOptions.data('parameter', parameter); const period = currentOptions.data('period'); const date = currentOptions.data('date'); - const requestPage = currentOptions.data('requestPage'); + const requestPageUrl = currentOptions.data('requestPageUrl'); const macroName = currentOptions.data('macroName'); - $.getJSON(`${requestPage}?parameter=${parameter}&period=${period}&date=last${date}&macroName=${macroName}`) + $.getJSON(`${requestPageUrl}?parameter=${parameter}&period=${period}&date=last${date}&macroName=${macroName}`) .done((data) => { window.sessionStorage.setItem('analyticsLastRequestCache', JSON.stringify(data)); renderChart(data); @@ -200,10 +201,10 @@ require(['jquery', 'chartjs'], ($) => { * @param period period day/week/month/year */ const displayPeriodDurations = (period) => { - currentModal.find('[class^="analyticsApplicationRowEvolutionDuration"]').not(`.analyticsRowEvolutionModal .analyticsApplicationRowEvolutionDuration${period}`).hide(); - currentModal.find(`.analyticsApplicationRowEvolutionDuration${period}`).show(); + currentModal.find('[class^="rowEvolutionPeriodLength"]').not(`.analyticsRowEvolutionModal .rowEvolutionPeriodLength${period}`).hide(); + currentModal.find(`.rowEvolutionPeriodLength${period}`).show(); currentOptions.data('period', period.toLowerCase()); - currentOptions.data('date', $(`.analyticsApplicationRowEvolutionDuration${period}`).val()); + currentOptions.data('date', $(`.rowEvolutionPeriodLength${period}`).val()); }; /** @@ -212,15 +213,15 @@ require(['jquery', 'chartjs'], ($) => { $(document).on('show.bs.modal', '.analyticsRowEvolutionModal', function (e) { let modal = $(e.target); - modal.find('[class^="analyticsApplicationRowEvolutionDuration"]').each(function () { $(this).prop('selectedIndex', 0) }) + modal.find('[class^="rowEvolutionPeriodLength"]').each(function () { $(this).prop('selectedIndex', 0) }) modal.find('.analyticsRowEvolutionOptions').data('date', 30); modal.find('.analyticsRowEvolutionOptions').data('period', 'day'); - modal.find('.analyticsApplicationRowEvolutionField').prop('selectedIndex', 0); - modal.find('.analyticsApplicationModalPeriodSelect').prop('selectedIndex', 0); - modal.find('[class^="analyticsApplicationRowEvolutionDuration"]').not('.analyticsApplicationRowEvolutionDurationDay').hide(); - modal.find('.analyticsApplicationRowEvolutionDurationDay').show(); + modal.find('.rowEvolutionField').prop('selectedIndex', 0); + modal.find('.rowEvolutionPeriod').prop('selectedIndex', 0); + modal.find('[class^="rowEvolutionPeriodLength"]').not('.rowEvolutionPeriodLengthDay').hide(); + modal.find('.rowEvolutionPeriodLengthDay').show(); }); - $(document).on('keyup change', '.analyticsRowEvolutionModal .analyticsApplicationRowEvolutionField', function () { + $(document).on('keyup change', '.analyticsRowEvolutionModal .rowEvolutionField', function () { renderChart(JSON.parse(window.sessionStorage.getItem('analyticsLastRequestCache'))); }); $(document).on('hide.bs.modal', '.analyticsRowEvolutionModal', function (e) { @@ -244,7 +245,7 @@ require(['jquery', 'chartjs'], ($) => { * Handles the update of the chart when the time is changed. * */ - $(document).on('keyup change', '.analyticsRowEvolutionModal [class^="analyticsApplicationRowEvolutionDuration"]', function () { + $(document).on('keyup change', '.analyticsRowEvolutionModal [class^="rowEvolutionPeriodLength"]', function () { currentOptions.data('date', $(this).val()); fetchChartData(currentOptions.data('parameter')); }); @@ -252,7 +253,7 @@ require(['jquery', 'chartjs'], ($) => { /** * Handles the update of the chart when the period is changed. */ - $(document).on("keyup change", '.analyticsRowEvolutionModal .analyticsApplicationModalPeriodSelect', function () { + $(document).on("keyup change", '.analyticsRowEvolutionModal .rowEvolutionPeriod', function () { const period = $(this).val(); displayPeriodDurations(period); fetchChartData(currentOptions.data('parameter')); diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index 32c7e5b1..6a341094 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -62,7 +62,7 @@ #set ($date = $startDate + ',' + $endDate) #end -#macro(setParameterOnAction $parameter) +#macro(analyticsActions $parameter) <div class="analyticsActions"> <div class="analyticsRowEvolution" data-parameter=$escapetool.url($parameter)> <a class="action" title="$escapetool.xml($services.localization.render( @@ -90,28 +90,28 @@ #end #macro(durationSelectList $class $options) - <select class="$class form-control"> + <select class="$class form-control" aria-label='duration'> #foreach($option in $options) <option value="$option">$option</option> #end </select> #end -#macro(fieldsSelect $fields) - <select class="form-control analyticsApplicationRowEvolutionField"> - #foreach($key in $fields.keySet()) - <option value="$fields.get($key)"> +#macro(rowEvolutionFieldSelect $matomoFields) + <select class="form-control rowEvolutionField"> + #foreach($key in $matomoFields.keySet()) + <option value="$matomoFields.get($key)"> $services.localization.render('analytics.rowEvolution.field.' + $key) </option> #end </select> #end -#macro(modalRowEvolution $fields $macroName) +#macro(modalRowEvolution $matomoFields $macroName) #set ($requestPageURL = $xwiki.getURL('Analytics.Code.Macros.RowEvolutionJson')) {{html clean=false}} <div class="modal fade analyticsRowEvolutionModal" tabindex="-1" role="dialog" aria-labelledby="chartModalLabel" - aria-hidden="true"> + aria-hidden="true"> <div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="modal-header"> @@ -122,29 +122,42 @@ $services.localization.render('analytics.rowEvolution.label') </h4> </div> - <div class="modal-body"> - <label for="analyticsApplicationModalPeriodSelect"> - $services.localization.render('analytics.rowEvolution.period') - </label> - <select class="analyticsApplicationModalPeriodSelect form-control"> - #durationOption("Day") - #durationOption("Week") - #durationOption("Month") - #durationOption("Year") - </select> - <div class="analyticsRowEvolutionOptions" data-macro-name=$macroName data-request-page=$requestPageURL data-period='day' data-date='30'> - #durationSelectList("analyticsApplicationRowEvolutionDurationDay" [30, 8, 60, 90, 180, 365, 500]) - #durationSelectList("analyticsApplicationRowEvolutionDurationWeek" [4, 12, 26, 52, 104, 500]) - #durationSelectList("analyticsApplicationRowEvolutionDurationMonth" [3, 6, 12, 24, 36, 120]) - #durationSelectList("analyticsApplicationRowEvolutionDurationYear" [3, 5, 10]) + <div class="analyticsRowEvolutionOptions" data-macro-name=$macroName data-request-page-url=$requestPageURL> + <div class="modal-body"> + <div class=".container-fluid"> + <div class="row"> + <div class="col-md-3"> + <label> + $services.localization.render('analytics.rowEvolution.period') + <select class="rowEvolutionPeriod form-control"> + #durationOption("Day") + #durationOption("Week") + #durationOption("Month") + #durationOption("Year") + </select> + </label> + </div> + <div class="col-md-3"> + <label> + $services.localization.render('analytics.rowEvolution.periodLength') + #durationSelectList("rowEvolutionPeriodLengthDay" [30, 8, 60, 90, 180, 365, 500]) + #durationSelectList("rowEvolutionPeriodLengthWeek" [4, 12, 26, 52, 104, 500]) + #durationSelectList("rowEvolutionPeriodLengthMonth" [3, 6, 12, 24, 36, 120]) + #durationSelectList("rowEvolutionPeriodLengthYear" [3, 5, 10]) + </label> + </div> + <div class="col-md-3"> + <label> + $services.localization.render('analytics.rowEvolution.field') + #rowEvolutionFieldSelect($matomoFields) + </label> + </div> + </div> + </div> </div> - <label for="analyticsApplicationRowEvolutionField"> - $services.localization.render('analytics.rowEvolution.field') - </label> - #fieldsSelect($fields) <div class='loading'> - <canvas class="analyticsChartisplay"></canvas> - </div> + <canvas class="chartDisplay"></canvas> + </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal"> @@ -158,7 +171,7 @@ #end #set ($analyticsId = -1) -#macro (getAnalyticsMacroId $macro) +#macro (getAnalyticsMacroId $macro $macroId) #set ($analyticsId = $analyticsId + 1) #set ($macroId = $macro + '_' + $analyticsId) #end From e888f9d8bc9903f67eb024fc258b0e3bbbe3592f Mon Sep 17 00:00:00 2001 From: Farcasut Date: Mon, 28 Aug 2023 11:11:08 +0300 Subject: [PATCH 059/105] Row Evolution feature for the MostViewedPages #15 * [MISC] Updated the js --- .../Analytics/Code/Macros/RowEvolution.xml | 54 +++++++++++-------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml index 2279723a..d6208db7 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml @@ -131,7 +131,17 @@ }); require(['jquery', 'chartjs'], ($) => { - const PERIODS = ["Day", "Week", "Month", "Year"]; + const SELECTORS = { + modal: '.analyticsRowEvolutionModal', + field: '.rowEvolutionField', + periodLength: '.rowEvolutionPeriodLength', + startsWithPeriodLength: '[class^="rowEvolutionPeriodLength"]', + options: '.analyticsRowEvolutionOptions', + period: '.rowEvolutionPeriod', + loading: '.loading', + chart: '.chartDisplay', + button:'.analyticsActions .analyticsRowEvolution' + }; let chart; let currentModal; let currentOptions; @@ -144,8 +154,8 @@ require(['jquery', 'chartjs'], ($) => { */ const renderChart = (data) => { const dates = data.map(item => item.date); - const field = data.map(item => item[currentModal.find('.rowEvolutionField').val()] || 0); - const ctx = currentModal.find('.chartDisplay'); + const field = data.map(item => item[currentModal.find(SELECTORS.field).val()] || 0); + const ctx = currentModal.find(SELECTORS.chart); if (chart) { chart.destroy(); } @@ -201,30 +211,30 @@ require(['jquery', 'chartjs'], ($) => { * @param period period day/week/month/year */ const displayPeriodDurations = (period) => { - currentModal.find('[class^="rowEvolutionPeriodLength"]').not(`.analyticsRowEvolutionModal .rowEvolutionPeriodLength${period}`).hide(); - currentModal.find(`.rowEvolutionPeriodLength${period}`).show(); + currentModal.find(SELECTORS.startsWithPeriodLength).not(`${SELECTORS.periodLength}${period}`).hide(); + currentModal.find(`${SELECTORS.periodLength}${period}`).show(); currentOptions.data('period', period.toLowerCase()); - currentOptions.data('date', $(`.rowEvolutionPeriodLength${period}`).val()); + currentOptions.data('date', $(`${SELECTORS.periodLength}${period}`).val()); }; /** * Resets all the selectors for the current modal when opened. */ - $(document).on('show.bs.modal', '.analyticsRowEvolutionModal', function (e) { + $(document).on('show.bs.modal', SELECTORS.modal, function (e) { let modal = $(e.target); - modal.find('[class^="rowEvolutionPeriodLength"]').each(function () { $(this).prop('selectedIndex', 0) }) - modal.find('.analyticsRowEvolutionOptions').data('date', 30); - modal.find('.analyticsRowEvolutionOptions').data('period', 'day'); - modal.find('.rowEvolutionField').prop('selectedIndex', 0); - modal.find('.rowEvolutionPeriod').prop('selectedIndex', 0); - modal.find('[class^="rowEvolutionPeriodLength"]').not('.rowEvolutionPeriodLengthDay').hide(); - modal.find('.rowEvolutionPeriodLengthDay').show(); + modal.find(SELECTORS.startsWithPeriodLength).each(function () { $(this).prop('selectedIndex', 0) }) + modal.find(SELECTORS.options).data('date', 30); + modal.find(SELECTORS.options).data('period', 'day'); + modal.find(SELECTORS.field).prop('selectedIndex', 0); + modal.find(SELECTORS.period).prop('selectedIndex', 0); + modal.find(SELECTORS.startsWithPeriodLength).not(`${SELECTORS.periodLength}Day`).hide(); + modal.find(`${SELECTORS.periodLength}Day`).show(); }); - $(document).on('keyup change', '.analyticsRowEvolutionModal .rowEvolutionField', function () { + $(document).on('keyup change', `${SELECTORS.modal} ${SELECTORS.field}`, function () { renderChart(JSON.parse(window.sessionStorage.getItem('analyticsLastRequestCache'))); }); - $(document).on('hide.bs.modal', '.analyticsRowEvolutionModal', function (e) { + $(document).on('hide.bs.modal', SELECTORS.modal, function (e) { chart.destroy(); currentLoadingAnimation.addClass('loading'); }); @@ -232,11 +242,11 @@ require(['jquery', 'chartjs'], ($) => { * Handles the click event on the RowEvolution buttons. It will save the current modal and current options div for easy of use and * fetchs the data for the default parameters. */ - $(document).on('click', '.analyticsActions .analyticsRowEvolution', function () { + $(document).on('click', SELECTORS.button, function () { const parameter = $(this).data('parameter'); - currentModal = $(this).closest('.xwiki-livetable-container').next('.analyticsRowEvolutionModal'); - currentOptions = currentModal.find('.analyticsRowEvolutionOptions'); - currentLoadingAnimation = currentModal.find('.loading'); + currentModal = $(this).closest('.xwiki-livetable-container').next(SELECTORS.modal); + currentOptions = currentModal.find(SELECTORS.options); + currentLoadingAnimation = currentModal.find(SELECTORS.loading); currentModal.modal('show'); fetchChartData(parameter); }); @@ -245,7 +255,7 @@ require(['jquery', 'chartjs'], ($) => { * Handles the update of the chart when the time is changed. * */ - $(document).on('keyup change', '.analyticsRowEvolutionModal [class^="rowEvolutionPeriodLength"]', function () { + $(document).on('keyup change', `${SELECTORS.modal} ${SELECTORS.startsWithPeriodLength}`, function () { currentOptions.data('date', $(this).val()); fetchChartData(currentOptions.data('parameter')); }); @@ -253,7 +263,7 @@ require(['jquery', 'chartjs'], ($) => { /** * Handles the update of the chart when the period is changed. */ - $(document).on("keyup change", '.analyticsRowEvolutionModal .rowEvolutionPeriod', function () { + $(document).on("keyup change", `${SELECTORS.modal} ${SELECTORS.period}`, function () { const period = $(this).val(); displayPeriodDurations(period); fetchChartData(currentOptions.data('parameter')); From e11aef78d63181722e3210d26a7cd75371a543e6 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Tue, 29 Aug 2023 16:23:09 +0300 Subject: [PATCH 060/105] Moved the MostViwedMacro to livedata from livetable --- .../MostViewedPages.xml | 50 +++++++++++-------- .../MostViewedPagesJSON.xml | 5 +- 2 files changed, 30 insertions(+), 25 deletions(-) rename application-analytics-ui/src/main/resources/Analytics/Code/Macros/{MostViewedPagesMacro => }/MostViewedPages.xml (92%) rename application-analytics-ui/src/main/resources/Analytics/Code/Macros/{MostViewedPagesMacro => }/MostViewedPagesJSON.xml (95%) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml similarity index 92% rename from application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml rename to application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index 67db4a97..06f0c3e2 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -20,8 +20,8 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - - Analytics.Code.Macros.MostViewedPagesMacro + + Analytics.Code.Macros MostViewedPages @@ -38,7 +38,7 @@ true {{mostViewedPage/}} - Analytics.Code.Macros.MostViewedPagesMacro.MostViewedPages + Analytics.Code.Macros.MostViewedPages 0 XWiki.WikiMacroClass dc943965-6af8-4905-8508-9b8c248c3930 @@ -241,31 +241,37 @@ {{include reference='Analytics.Code.Macros.VelocityMacros'/}} + {{velocity}} #getDate() #set ($period = 'range') #set ($parameters = $escapetool.url({ + 'resultPage': 'Analytics.Code.Macros.MostViewedPagesJSON', 'date': $date, 'period': $period, - 'limitEntries': '-1' + 'limitEntries': '-1', + 'translationPrefix': 'analytics.mostViewedPages.header.' })) -#set ($columns = ['date', 'title', 'visits', 'hits', 'timeSpent', 'entryNbActions', 'bounceRate', 'exitRate']) -#set ($columnsProperties = { - 'date' : { 'type' : 'text', 'sortable' : true }, - 'title' : { 'type' : 'text', 'sortable' : true }, - 'visits' : { 'type' : 'text', 'sortable' : true }, - 'hits' : { 'type' : 'text', 'sortable' : true }, - 'timeSpent' : { 'type' : 'text', 'sortable' : true }, - 'entryNbActions' : { 'type' : 'text', 'sortable' : true }, - 'bounceRate' : { 'type' : 'text', 'sortable' : true }, - 'exitRate' : { 'type' : 'text', 'sortable' : true } -}) -#set($options = { - 'translationPrefix': 'analytics.mostViewedPages.header.', - 'resultPage': 'Analytics.Code.Macros.MostViewedPagesMacro.MostViewedPagesJSON', - 'extraParams': $parameters +#set ($liveDataConfig={ + 'meta':{ + 'propertyDescriptors': [ + {'id': 'title', 'displayer': 'html'}, + {'id': 'visits', 'displayer': 'html'}, + {'id': 'hits', 'displayer': 'html'}, + {'id': 'timeSpent', 'displayer': 'html'}, + {'id': 'entryNbActions', 'displayer': 'html'}, + {'id': 'bounceRate', 'displayer': 'html'}, + {'id': 'exitRate', 'displayer': 'html'} + ]} }) -#livetable('mostViewedPages' $columns $columnsProperties $options) + +{{liveData + id="mostViwedMacro" + properties="title, visits, hits, timeSpent, entryNbActions, bounceRate, exitRate" + source='liveTable' + sourceParameters="$parameters" +}}$jsontool.serialize($liveDataConfig){{/liveData}} + {{/velocity}} @@ -300,7 +306,7 @@ - Analytics.Code.Macros.MostViewedPagesMacro.MostViewedPages + Analytics.Code.Macros.MostViewedPages 0 XWiki.WikiMacroParameterClass 6e3cae3f-3127-4fb2-a590-5dce2f4be0d0 @@ -378,7 +384,7 @@ - Analytics.Code.Macros.MostViewedPagesMacro.MostViewedPages + Analytics.Code.Macros.MostViewedPages 1 XWiki.WikiMacroParameterClass 8a9fb4a7-d7e2-4d7d-84b8-8afb71b4606d diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml similarity index 95% rename from application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPagesJSON.xml rename to application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml index 3a706aa3..3d7d766e 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesMacro/MostViewedPagesJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml @@ -20,8 +20,8 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - - Analytics.Code.Macros.MostViewedPagesMacro + + Analytics.Code.Macros MostViewedPagesJSON @@ -89,7 +89,6 @@ #foreach ($currentEntry in $analyticsResult) #set ($url = $currentEntry.get('url').asText()) #set ($discard = $results.rows.add({ - 'date' : $currentEntry.get('date').asText(), 'title' : $currentEntry.get('label').asText(), 'visits' : $currentEntry.get('nb_visits').asText(), 'hits' : $currentEntry.get('nb_hits').asText(), From b89d5082220e5fd696cd246a7916f254e55665fe Mon Sep 17 00:00:00 2001 From: Farcasut Date: Tue, 29 Aug 2023 16:47:44 +0300 Subject: [PATCH 061/105] Migrate MostViewedMacro from livetable to livedata #21 * [MISC] Missing spaces --- .../resources/Analytics/Code/Macros/MostViewedPages.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index 06f0c3e2..3cbd8caa 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -252,8 +252,8 @@ 'limitEntries': '-1', 'translationPrefix': 'analytics.mostViewedPages.header.' })) -#set ($liveDataConfig={ - 'meta':{ +#set ($liveDataConfig= { + 'meta': { 'propertyDescriptors': [ {'id': 'title', 'displayer': 'html'}, {'id': 'visits', 'displayer': 'html'}, @@ -266,7 +266,7 @@ }) {{liveData - id="mostViwedMacro" + id="mostViewedMacro" properties="title, visits, hits, timeSpent, entryNbActions, bounceRate, exitRate" source='liveTable' sourceParameters="$parameters" From 48960723016b7117cf128d36f7140266a45bb23a Mon Sep 17 00:00:00 2001 From: Farcasut Date: Tue, 29 Aug 2023 19:30:43 +0300 Subject: [PATCH 062/105] Row Evolution feature for the MostViewedPages #15 *[Misc] --- .../Analytics/Code/Macros/MostViewedPages.xml | 10 +++++----- .../resources/Analytics/Code/Macros/RowEvolution.xml | 11 +++++++---- .../Analytics/Code/Macros/RowEvolutionJson.xml | 2 +- .../Analytics/Code/Macros/VelocityMacros.xml | 4 ++-- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index 0ae6e06d..e81ea45c 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -267,14 +267,14 @@ 'resultPage': 'Analytics.Code.Macros.MostViewedPagesJSON', 'extraParams': $parameters }) -#getAnalyticsMacroId('mostViwedMacro', $macroId ) +#getAnalyticsMacroId('mostViwedMacro', $macroId) #livetable($macroId $columns $columnsProperties $options) #set ($parameters = { - 'pageviews':'nb_hits', - 'uniqueViews':'nb_visits', - "avgTime":'avg_time_on_page' + 'pageViews': 'nb_hits', + 'uniqueViews': 'nb_visits', + "avgTime": 'avg_time_on_page' }) -#modalRowEvolution($parameters, 'MostViewedMacro') +#modalRowEvolution($parameters, 'MostViewedPages') {{/velocity}} diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml index d6208db7..454de089 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml @@ -136,7 +136,7 @@ require(['jquery', 'chartjs'], ($) => { field: '.rowEvolutionField', periodLength: '.rowEvolutionPeriodLength', startsWithPeriodLength: '[class^="rowEvolutionPeriodLength"]', - options: '.analyticsRowEvolutionOptions', + options: '.rowEvolutionOptions', period: '.rowEvolutionPeriod', loading: '.loading', chart: '.chartDisplay', @@ -156,6 +156,7 @@ require(['jquery', 'chartjs'], ($) => { const dates = data.map(item => item.date); const field = data.map(item => item[currentModal.find(SELECTORS.field).val()] || 0); const ctx = currentModal.find(SELECTORS.chart); + //You always if (chart) { chart.destroy(); } @@ -198,8 +199,7 @@ require(['jquery', 'chartjs'], ($) => { .done((data) => { window.sessionStorage.setItem('analyticsLastRequestCache', JSON.stringify(data)); renderChart(data); - }) - .fail((jqxhr, textStatus, error) => { + }).fail((jqxhr, textStatus, error) => { const err = `${textStatus}, ${error}`; console.error(`Request Failed: ${err}`); }); @@ -220,7 +220,6 @@ require(['jquery', 'chartjs'], ($) => { /** * Resets all the selectors for the current modal when opened. */ - $(document).on('show.bs.modal', SELECTORS.modal, function (e) { let modal = $(e.target); modal.find(SELECTORS.startsWithPeriodLength).each(function () { $(this).prop('selectedIndex', 0) }) @@ -231,18 +230,22 @@ require(['jquery', 'chartjs'], ($) => { modal.find(SELECTORS.startsWithPeriodLength).not(`${SELECTORS.periodLength}Day`).hide(); modal.find(`${SELECTORS.periodLength}Day`).show(); }); + $(document).on('keyup change', `${SELECTORS.modal} ${SELECTORS.field}`, function () { renderChart(JSON.parse(window.sessionStorage.getItem('analyticsLastRequestCache'))); }); + $(document).on('hide.bs.modal', SELECTORS.modal, function (e) { chart.destroy(); currentLoadingAnimation.addClass('loading'); }); + /** * Handles the click event on the RowEvolution buttons. It will save the current modal and current options div for easy of use and * fetchs the data for the default parameters. */ $(document).on('click', SELECTORS.button, function () { + //This parameter is used to identify a specific row in the matomo tables. const parameter = $(this).data('parameter'); currentModal = $(this).closest('.xwiki-livetable-container').next(SELECTORS.modal); currentOptions = currentModal.find(SELECTORS.options); diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJson.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJson.xml index c97ce1a7..ccd66f5f 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJson.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJson.xml @@ -44,7 +44,7 @@ 'method' : 'Actions.getPageUrl', 'format' : 'json' }) -#if ($request.macroName=='MostViewedMacro') +#if ($request.macroName=='MostViewedPages') $parameters.put('pageUrl', $request.parameter) #end #set ($analyticsResult = $services.analytics.getMatomoRequestResult($parameters, 'RowEvolution')) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index 6a341094..32bb88e5 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -66,7 +66,7 @@ <div class="analyticsActions"> <div class="analyticsRowEvolution" data-parameter=$escapetool.url($parameter)> <a class="action" title="$escapetool.xml($services.localization.render( - 'analytics.mostViwedMacros.action.rowEvolution'))" href="#"> + 'analytics.mostViewedMacros.action.rowEvolution'))" href="#"> <span class="action-icon">$services.icon.renderHTML('eye')</span><span class="action-label"> $escapetool.xml($services.localization.render('analytics.action.label.rowEvolution'))</span> </a> @@ -122,7 +122,7 @@ $services.localization.render('analytics.rowEvolution.label') </h4> </div> - <div class="analyticsRowEvolutionOptions" data-macro-name=$macroName data-request-page-url=$requestPageURL> + <div class="rowEvolutionOptions" data-macro-name=$macroName data-request-page-url=$requestPageURL> <div class="modal-body"> <div class=".container-fluid"> <div class="row"> From 209e8ec0845fd7cf775f95486fc0532c98b111b9 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Wed, 30 Aug 2023 18:11:20 +0300 Subject: [PATCH 063/105] Bug Fixes: -Fixed LiveData pagination. -Improved LiveData filtering. Added a method to filter text based on user input. For now, title filtering searches the URL, not the page title. -Fixed LiveData sorting issues by removing $request.reqNo != '1'. New Features: -Changed title property to show both page title and link to the page. --- .../com/xwiki/analytics/AnalyticsManager.java | 5 +- .../com/xwiki/analytics/JsonNormaliser.java | 5 +- .../internal/AbstractJsonNormaliser.java | 147 ++++++++++++++++++ .../internal/MatomoAnalyticsManager.java | 5 +- .../internal/MostViewedJsonNormaliser.java | 97 ++++-------- .../script/AnalyticsScriptService.java | 25 ++- .../internal/MatomoAnalyticsManagerTest.java | 8 +- .../MostViewedJsonNormaliserTest.java | 4 +- .../src/test/resources/tests.json | 12 +- .../Analytics/Code/Macros/MostViewedPages.xml | 1 + .../Code/Macros/MostViewedPagesJSON.xml | 31 +++- .../Analytics/Code/Macros/VelocityMacros.xml | 4 + 12 files changed, 252 insertions(+), 92 deletions(-) create mode 100644 application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java b/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java index 715cd5e2..a3fa9d7b 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java @@ -39,9 +39,12 @@ public interface AnalyticsManager { /** * Request specific analytics data. + * * @param jsonNormaliserHint hint to select the json normaliser * @param parameters a list of key, value pairs that will represent the parameters for the request + * @param filters holds the criteria for filtering a dataset. * @return a jsonNode with the processed data */ - JsonNode requestData(Map parameters, String jsonNormaliserHint) throws IOException; + JsonNode requestData(Map parameters, Map filters, String jsonNormaliserHint) + throws IOException; } diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java index efc8c4f8..35ce4b3e 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java @@ -19,6 +19,8 @@ */ package com.xwiki.analytics; +import java.util.Map; + import org.xwiki.component.annotation.Role; import org.xwiki.stability.Unstable; @@ -39,9 +41,10 @@ public interface JsonNormaliser /** * Normalise the data returned to have the expected format. * + * @param filters holds the criteria for filtering a dataset. * @param jsonString a string that has the expected format * @return a {@link JsonNode} with the root of the normalized JSON * @throws JsonProcessingException Throws this error when the jsonString param is not a proper json */ - JsonNode normaliseData(String jsonString) throws JsonProcessingException; + JsonNode normaliseData(String jsonString, Map filters) throws JsonProcessingException; } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java new file mode 100644 index 00000000..de23b912 --- /dev/null +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java @@ -0,0 +1,147 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.analytics.internal; + +import java.util.Iterator; +import java.util.Map; + +import javax.inject.Inject; + +import org.slf4j.Logger; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.xwiki.analytics.JsonNormaliser; + +/** + * Abstract class for the JsonNormaliser that will serve as a bases for some of the normalisers. + * + * @version $Id$ + * @since 1.0 + */ +public abstract class AbstractJsonNormaliser implements JsonNormaliser +{ + protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + protected static final String DATE = "date"; + + protected static final String LABEL = "label"; + + @Inject + protected Logger logger; + + /** + * Normalize Matomo response for format consistency and add extra information needed by XWiki. + * + * @param jsonString a string that has the expected format + * @param filters holds the criteria for filtering a dataset. + * @return the normalised json + * @throws JsonProcessingException + */ + @Override + public JsonNode normaliseData(String jsonString, Map filters) throws JsonProcessingException + { + // Convert the string returned by Matomo in a JSON format to easily handle the processing of the nodes. + JsonNode jsonRoot = OBJECT_MAPPER.readTree(jsonString); + // Matomo may return several variants of JSON formats. In one scenario, when the period is set to + // day/week/month/year, it returns a JSON object with keys representing dates. The corresponding value for + // each key is an array of JSON objects, each of which represents a page. However, if the user sets the + // period parameter to "range" Matomo returns an array of JSON objects, with each JSON object representing + // a page. Due to these variations, the result needs to be processed to create a single format. + // This normalized format is an array of JSON objects and each JSON object in this array will have a new + // field called 'date'. This 'date' field will be set to 'N/A' when Matomo returns an array instead of an + // object. For both type of formats, the label field is also altered in order to contain the full page name + if (jsonRoot.isArray()) { + return processArrayNode(jsonRoot, filters); + } else { + return processObjectNode(jsonRoot, filters); + } + } + + /** + * This method processes each entry to append an empty date to it. + * + * @param jsonNode an array of jsons + * @param filters holds the criteria for filtering a dataset. + * @return array of jsons + */ + protected ArrayNode processArrayNode(JsonNode jsonNode, Map filters) + { + ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); + for (JsonNode objNode : jsonNode) { + if (objNode.isObject()) { + // If all the filters match then the entry would be added to the final json. + if (matchesAllContainsFilters(objNode, filters)) { + ((ObjectNode) objNode).put(DATE, ""); + arrayNode.add(objNode); + } + } + } + return arrayNode; + } + + /** + * Handle the scenario where Matomo returns an object with dates as keys and arrays of JSON objects as values. This + * function extracts the date from the key and adds it to each page. Ultimately, it returns an array of JSON + * objects. + * + * @param jsonNode + * @param filters holds the criteria for filtering a dataset. + * @return array of jsons + */ + protected ArrayNode processObjectNode(JsonNode jsonNode, Map filters) throws JsonProcessingException + { + ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); + Iterator fieldNames = jsonNode.fieldNames(); + while (fieldNames.hasNext()) { + String date = fieldNames.next(); + JsonNode childNode = jsonNode.get(date); + for (JsonNode objNode : childNode) { + if (objNode.isObject()) { + // If all the filters match then the entry would be added to the final json. + if (matchesAllContainsFilters(objNode, filters)) { + ((ObjectNode) objNode).put(DATE, date); + arrayNode.add(objNode); + } + } + } + } + return arrayNode; + } + + protected boolean matchesAllContainsFilters(JsonNode objNode, Map filters) + { + if (filters == null) { + return true; + } + for (Map.Entry entry : filters.entrySet()) { + String filterField = entry.getKey(); + String filterValue = entry.getValue(); + // Check if the field exists and its value matches the filter value + if (!(objNode.has(filterField) && objNode.get(filterField).asText().contains(filterValue))) { + return false; + } + } + return true; + } +} diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java index 7afd2082..82747dd6 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java @@ -72,7 +72,8 @@ public class MatomoAnalyticsManager implements AnalyticsManager * @return the altered Matomo request response, as JSON format */ @Override - public JsonNode requestData(Map parameters, String jsonNormaliserHint) throws IOException + public JsonNode requestData(Map parameters, + Map filters, String jsonNormaliserHint) throws IOException { parameters.put("idSite", configuration.getIdSite()); parameters.put("token_auth", configuration.getAuthenticationToken()); @@ -85,7 +86,7 @@ public JsonNode requestData(Map parameters, String jsonNormalise HttpGet request = new HttpGet(buildURI(parameters)); HttpResponse response = client.execute(request); String responseBody = EntityUtils.toString(response.getEntity()); - return jsonNormaliser.normaliseData(responseBody); + return jsonNormaliser.normaliseData(responseBody, filters); } /** diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index f3a31846..dd38d40f 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -25,14 +25,13 @@ import java.net.URL; import java.util.Collections; import java.util.Iterator; +import java.util.Map; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; -import org.slf4j.Logger; import org.xwiki.component.annotation.Component; -import org.xwiki.model.reference.EntityReferenceSerializer; import org.xwiki.resource.CreateResourceReferenceException; import org.xwiki.resource.CreateResourceTypeException; import org.xwiki.resource.ResourceReference; @@ -43,9 +42,7 @@ import org.xwiki.resource.entity.EntityResourceReference; import org.xwiki.url.ExtendedURL; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.xwiki.analytics.JsonNormaliser; @@ -61,86 +58,49 @@ @Component @Named("MostViewedPages") @Singleton -public class MostViewedJsonNormaliser implements JsonNormaliser +public class MostViewedJsonNormaliser extends AbstractJsonNormaliser { /** * Hint for the MostViewedJsonNormaliser. */ public static final String HINT = "MostViewedPages"; - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - private static final String DATE = "date"; - private static final String URL = "url"; - private static final String LABEL = "label"; - @Inject private ResourceReferenceResolver resourceReferenceResolver; - @Inject - private Logger logger; - @Inject private ResourceTypeResolver resourceTypeResolver; - @Inject - @Named("compactwiki") - private EntityReferenceSerializer serializer; - - /** - * Normalize Matomo response for format consistency and add extra information needed by XWiki. - * - * @param jsonString the Matomo JSON response, in {@code String} format - * @return the normalised json - */ - public JsonNode normaliseData(String jsonString) throws JsonProcessingException - { - // Convert the string returned by Matomo in a JSON format to easily handle the processing of the nodes. - JsonNode jsonRoot = OBJECT_MAPPER.readTree(jsonString); - // Matomo may return several variants of JSON formats. In one scenario, when the period is set to - // day/week/month/year, it returns a JSON object with keys representing dates. The corresponding value for - // each key is an array of JSON objects, each of which represents a page. However, if the user sets the - // period parameter to "range" Matomo returns an array of JSON objects, with each JSON object representing - // a page. Due to these variations, the result needs to be processed to create a single format. - // This normalized format is an array of JSON objects and each JSON object in this array will have a new - // field called 'date'. This 'date' field will be set to 'N/A' when Matomo returns an array instead of an - // object. For both type of formats, the label field is also altered in order to contain the full page name - if (jsonRoot.isArray()) { - processArrayNode(jsonRoot); - } else { - jsonRoot = processObjectNode(jsonRoot); - } - return jsonRoot; - } - /** - * This method processes each entry to append an empty date to it. + * This method processes each entry to append an empty date to it and updates the label with the page name. * * @param jsonNode an array of jsons + * @param filters holds the criteria for filtering a dataset. + * @return */ - private void processArrayNode(JsonNode jsonNode) + @Override + protected ArrayNode processArrayNode(JsonNode jsonNode, Map filters) { + ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); for (JsonNode objNode : jsonNode) { - if (objNode.isObject()) { - ((ObjectNode) objNode).put(DATE, ""); - if (objNode.has(URL)) { - this.handleURLNode((ObjectNode) objNode); - } - } + handleNode(arrayNode, objNode, filters); } + return arrayNode; } /** * Handle the scenario where Matomo returns an object with dates as keys and arrays of JSON objects as values. This - * function extracts the date from the key and adds it to each page. Ultimately, it returns an array of JSON objects - * instead of a single JSON object. + * function extracts the date from the key and adds it to each page and updates the label with the name of the page. + * Ultimately, it returns an array of JSON objects instead of a single JSON object. * * @param jsonNode json object - * @return array of jsons + * @param filters holds the criteria for filtering a dataset. + * @return */ - private ArrayNode processObjectNode(JsonNode jsonNode) + @Override + protected ArrayNode processObjectNode(JsonNode jsonNode, Map filters) { ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); Iterator fieldNames = jsonNode.fieldNames(); @@ -148,18 +108,25 @@ private ArrayNode processObjectNode(JsonNode jsonNode) String date = fieldNames.next(); JsonNode childNode = jsonNode.get(date); for (JsonNode objNode : childNode) { - if (objNode.isObject()) { - ((ObjectNode) objNode).put(DATE, date); - if (objNode.has(URL)) { - this.handleURLNode((ObjectNode) objNode); - } - arrayNode.add(objNode); - } + handleNode(arrayNode, objNode, filters); } } return arrayNode; } + private void handleNode(ArrayNode arrayNode, JsonNode objNode, Map filters) + { + if (matchesAllContainsFilters(objNode, filters)) { + + if (objNode.isObject()) { + if (objNode.has(URL)) { + this.handleURLNode((ObjectNode) objNode); + } + arrayNode.add(objNode); + } + } + } + /** * Process the URL of a page to obtain the documentReference. This is necessary to retrieve the document name for * display when rendering the table. @@ -182,7 +149,7 @@ private ResourceReference getResourceReferenceFromStringURL(String resourceRefer return this.resourceReferenceResolver.resolve(extendedURL, resourceType, Collections.emptyMap()); } catch (MalformedURLException | CreateResourceReferenceException | CreateResourceTypeException | UnsupportedResourceReferenceException | URISyntaxException e) { - logger.warn("Failed to get resource reference from URL: [{}].", resourceReferenceURL, + this.logger.warn("Failed to get resource reference from URL: [{}].", resourceReferenceURL, ExceptionUtils.getRootCauseMessage(e)); return null; } @@ -198,7 +165,7 @@ private void handleURLNode(ObjectNode objNode) EntityResourceReference entityResourceReference = (EntityResourceReference) this.getResourceReferenceFromStringURL(objNode.get(URL).asText()); if (entityResourceReference != null) { - objNode.put(LABEL, this.serializer.serialize(entityResourceReference.getEntityReference())); + objNode.put(LABEL, entityResourceReference.getEntityReference().getName()); } } } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java index 9b860ff6..8db718b4 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java @@ -19,6 +19,8 @@ */ package com.xwiki.analytics.script; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import javax.inject.Inject; @@ -53,15 +55,34 @@ public class AnalyticsScriptService implements ScriptService * * @param jsonNormaliserHint hint specific to the component that will normalize the Matomo result response, * since it's given / resulted format depends on the context were is used. + * @param filters holds the criteria for filtering a dataset. * @param parameters a map of the parameters needed for this request * @return response from Matomo API, in a normalized JSON format */ - public JsonNode getMatomoRequestResult(Map parameters, String jsonNormaliserHint) + public JsonNode getMatomoRequestResult(Map parameters, Map filters, + String jsonNormaliserHint) { try { - return analyticsManager.requestData(parameters, jsonNormaliserHint); + return analyticsManager.requestData(parameters, filters, jsonNormaliserHint); } catch (Exception e) { throw new RuntimeException(String.format("Failed to get data for [%s]", jsonNormaliserHint), e); } } + + /** + * Creates a subset from an array of jsons. + * + * @param jsonNode array of jsons + * @param start index of the first element + * @param end index+1 of the last element + * @return a subset of jsons + */ + public List subSetJSON(JsonNode jsonNode, int start, int end) + { + List subSet = new ArrayList<>(); + for (int i = start; i < end; i++) { + subSet.add(jsonNode.get(i)); + } + return subSet; + } } diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java index 8b487da9..b1a781cb 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java @@ -36,6 +36,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -66,8 +68,8 @@ public void requestDataWithCorrectHintForNormaliser() throws IOException, Interr when(this.configuration.getAuthenticationToken()).thenReturn("token"); when(this.configuration.getRequestAddress()).thenReturn("http://130.61.233.19/matomo"); when(this.configuration.getIdSite()).thenReturn("3"); - this.matomoAnalyticsManager.requestData(new HashMap<>(), MostViewedJsonNormaliser.HINT); - verify(this.jsonNormaliser).normaliseData(any(String.class)); + this.matomoAnalyticsManager.requestData(new HashMap<>(), new HashMap<>(), MostViewedJsonNormaliser.HINT); + verify(this.jsonNormaliser).normaliseData(any(String.class), eq(new HashMap<>())); } @Test @@ -78,7 +80,7 @@ public void requestDataWithInvalidNormaliser() throws IOException, InterruptedEx when(this.configuration.getRequestAddress()).thenReturn("http://130.61.233.19/matomo"); when(this.configuration.getIdSite()).thenReturn("3"); try { - when(this.matomoAnalyticsManager.requestData(new HashMap<>(), "RANDOM_NORMALISER")).thenThrow( + when(this.matomoAnalyticsManager.requestData(new HashMap<>(),new HashMap<>(), "RANDOM_NORMALISER")).thenThrow( new RuntimeException()); verify(this.logger).warn("There is no JSON normalizer associated with the [{}] hint you provided.", "RANDOM_NORMALISER"); diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java index 75ec5334..6f95bef1 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java @@ -66,7 +66,7 @@ public void testObjectJSONResponse() throws Exception setupURLs("http://localhost:8080/xwiki/bin/view/Analytics/", "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage"); assertEquals(node.get("ResponseObjectJSON"), - mostViewedJsonNormaliser.normaliseData(node.get("ObjectJSON").toString())); + mostViewedJsonNormaliser.normaliseData(node.get("ObjectJSON").toString(), null)); } @Test @@ -76,7 +76,7 @@ public void testArrayJSONResponse() throws Exception setupURLs("http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages", "http://localhost:8081/xwiki/bin/view/Main/"); assertEquals(node.get("ResponseArrayJSON"), - mostViewedJsonNormaliser.normaliseData(node.get("ArrayJSONS").toString())); + mostViewedJsonNormaliser.normaliseData(node.get("ArrayJSONS").toString(), null)); } private void setupURLs(String url1, String url2) throws Exception diff --git a/application-analytics-default/src/test/resources/tests.json b/application-analytics-default/src/test/resources/tests.json index 4c044f95..7aadfcf6 100644 --- a/application-analytics-default/src/test/resources/tests.json +++ b/application-analytics-default/src/test/resources/tests.json @@ -22,13 +22,11 @@ "ResponseObjectJSON": [ { "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", - "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages", - "date": "2023-07" + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" }, { "label": "/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", - "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", - "date": "2023-08" + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" } ], "ArrayJSONS": [ @@ -44,13 +42,11 @@ "ResponseArrayJSON": [ { "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", - "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages", - "date": "" + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" }, { "label": "/xwiki/bin/view/Main/", - "url": "http://localhost:8081/xwiki/bin/view/Main/", - "date": "" + "url": "http://localhost:8081/xwiki/bin/view/Main/" } ] } \ No newline at end of file diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index 3cbd8caa..8fa791ca 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -270,6 +270,7 @@ properties="title, visits, hits, timeSpent, entryNbActions, bounceRate, exitRate" source='liveTable' sourceParameters="$parameters" + sort='hits:desc' }}$jsontool.serialize($liveDataConfig){{/liveData}} {{/velocity}} diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml index 3d7d766e..670b0669 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml @@ -36,7 +36,8 @@ false xwiki/2.1 true - + {{include reference='Analytics.Code.Macros.VelocityMacros'/}} + {{velocity}} #if ($xcontext.action == 'get') #set ($offset = $numbertool.toNumber($request.offset).intValue()) @@ -49,9 +50,8 @@ #if (!$limit) #set ($limit = 15) #end - ## + #set ($keys = { - 'date' : 'date', 'title' : 'label', 'visits' : 'nb_visits', 'hits' : 'nb_hits', @@ -62,10 +62,19 @@ }) #set ($sort = 'nb_hits') #set ($order = 'desc') - #if ($request.sort && $request.reqNo!='1') + #if ($request.sort) #set($sort = $keys.get($request.sort)) #set($order = $request.dir) #end + + #set ($filters={}) + #foreach ($key in $keys.keySet()) + #set ($value = $request.get($key)) + #if ($value && $value!="") + $filters.put($keys.get($key), $request.get($key)) + #end + #end + #set ($parameters = { 'period' : $request.period, 'date' : $request.date, @@ -78,18 +87,24 @@ 'expanded' : '1', 'flat' : '1' }) - #set ($analyticsResult = $services.analytics.getMatomoRequestResult($parameters, 'MostViewedPages')) + + ## Json from matomo + #set ($analyticsResult = $services.analytics.getMatomoRequestResult($parameters,$filters, 'MostViewedPages')) + #if ($offset < $analyticsResult.size()) + #set ($toIndex = $mathtool.min($mathtool.add($offset, $limit), $analyticsResult.size())) + #set ($resultList = $services.analytics.subSetJSON($analyticsResult, $offset, $toIndex)) + #end #set ($results = { "totalrows": $analyticsResult.size(), - "returnedrows": $analyticsResult.size(), + "returnedrows": $resultList.size(), "offset": $mathtool.add($offset, 1), "reqNo": $numbertool.toNumber($request.reqNo).intValue(), "rows": [] }) - #foreach ($currentEntry in $analyticsResult) + #foreach ($currentEntry in $resultList) #set ($url = $currentEntry.get('url').asText()) #set ($discard = $results.rows.add({ - 'title' : $currentEntry.get('label').asText(), + 'title' : "#createLink($currentEntry.get('label').asText(), $url)", 'visits' : $currentEntry.get('nb_visits').asText(), 'hits' : $currentEntry.get('nb_hits').asText(), 'timeSpent' : $currentEntry.get('sum_time_spent').asText(), diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index acdd076e..0bed82f4 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -61,5 +61,9 @@ #end #set ($date = $startDate + ',' + $endDate) #end + +#macro (createLink $text $link) +<a href=$link>$text</a> +#end {{/velocity}} From 0a8dd3476f3c3b01f1efe722582d639699cb51b4 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Wed, 30 Aug 2023 21:38:31 +0300 Subject: [PATCH 064/105] [MISC] Changed the name of a function --- .../xwiki/analytics/internal/AbstractJsonNormaliser.java | 6 +++--- .../xwiki/analytics/internal/MostViewedJsonNormaliser.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java index de23b912..41943b69 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java @@ -91,7 +91,7 @@ protected ArrayNode processArrayNode(JsonNode jsonNode, Map filt for (JsonNode objNode : jsonNode) { if (objNode.isObject()) { // If all the filters match then the entry would be added to the final json. - if (matchesAllContainsFilters(objNode, filters)) { + if (matchesAllFilters(objNode, filters)) { ((ObjectNode) objNode).put(DATE, ""); arrayNode.add(objNode); } @@ -119,7 +119,7 @@ protected ArrayNode processObjectNode(JsonNode jsonNode, Map fil for (JsonNode objNode : childNode) { if (objNode.isObject()) { // If all the filters match then the entry would be added to the final json. - if (matchesAllContainsFilters(objNode, filters)) { + if (matchesAllFilters(objNode, filters)) { ((ObjectNode) objNode).put(DATE, date); arrayNode.add(objNode); } @@ -129,7 +129,7 @@ protected ArrayNode processObjectNode(JsonNode jsonNode, Map fil return arrayNode; } - protected boolean matchesAllContainsFilters(JsonNode objNode, Map filters) + protected boolean matchesAllFilters(JsonNode objNode, Map filters) { if (filters == null) { return true; diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index dd38d40f..c3ea3368 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -116,7 +116,7 @@ protected ArrayNode processObjectNode(JsonNode jsonNode, Map fil private void handleNode(ArrayNode arrayNode, JsonNode objNode, Map filters) { - if (matchesAllContainsFilters(objNode, filters)) { + if (matchesAllFilters(objNode, filters)) { if (objNode.isObject()) { if (objNode.has(URL)) { From 89880ff13a775952de4877f29d2938955d081200 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 31 Aug 2023 11:47:33 +0300 Subject: [PATCH 065/105] Row Evolution feature for the MostViewedPages #15 * -Added ids to the labels for the rowEvolution modal - Added a new parameter for the line color --- .../Analytics/Code/Macros/MostViewedPages.xml | 2 +- .../Analytics/Code/Macros/RowEvolution.xml | 17 +++++++---- .../Analytics/Code/Macros/VelocityMacros.xml | 30 +++++++++---------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index 52e78096..967e2c43 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -293,7 +293,7 @@ 'uniqueViews': 'nb_visits', "avgTime": 'avg_time_on_page' }) -#modalRowEvolution($parameters, 'MostViewedPages') +#modalRowEvolution($parameters, 'MostViewedPages', $macroId) {{/velocity}} diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml index 14ed76fe..3a9c19e0 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml @@ -156,8 +156,6 @@ require(['jquery', 'chart'], ($) => { const dates = data.map(item => item.date); const field = data.map(item => item[currentModal.find(SELECTORS.field).val()] || 0); const ctx = currentModal.find(SELECTORS.chart); - console.log(dates) - console.log(data) if (chart) { chart.destroy(); } @@ -168,8 +166,7 @@ require(['jquery', 'chart'], ($) => { datasets: [{ label: '', data: field, - borderColor: 'rgba(75, 192, 192, 1)', - backgroundColor: 'rgba(75, 192, 192, 0.2)', + borderColor: currentOptions.data('color'), fill: false }] }, @@ -188,7 +185,7 @@ require(['jquery', 'chart'], ($) => { * Fetches the data from Matomo using the XWiki server as a proxy through the RowEvolutionJSON page. It then saves the data in session * storage for easy access. This allows the user to display a different field for the same time interval without making a new request and updates the chart. * - * @param parameter parameter need for the request + * @param parameter will help identify a single row in the matomo tables */ const fetchChartData = (parameter) => { currentOptions.data('parameter', parameter); @@ -196,7 +193,15 @@ require(['jquery', 'chart'], ($) => { const date = currentOptions.data('date'); const requestPageUrl = currentOptions.data('requestPageUrl'); const macroName = currentOptions.data('macroName'); - $.getJSON(`${requestPageUrl}?parameter=${parameter}&period=${period}&date=last${date}&macroName=${macroName}`) + const params = { + period: period, + date: `last${date}`, + macroName: macroName + }; + const queryString = $.param(params); + // I cannot use jQuery's $.params to add the parameter because it can represent multple things: url, segment pagename, etc. In some cases, + // the parameter is already encoded and using $.params would result in double encodingl. + $.getJSON(`${requestPageUrl}?${queryString}&parameter=${parameter}`) .done((data) => { window.sessionStorage.setItem('analyticsLastRequestCache', JSON.stringify(data)); renderChart(data); diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index ee7f396f..8a15f196 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -73,8 +73,8 @@ </div> #end -#macro(durationSelect $className $options) -<select class="$className form-control"> +#macro(durationSelect $className $options $id) +<select i class="$className form-control" aria-labelledby=$id> #foreach($option in $options) <option value="$option">$option</option> #end @@ -96,8 +96,8 @@ </select> #end -#macro(rowEvolutionFieldSelect $matomoFields) - <select class="form-control rowEvolutionField"> +#macro(rowEvolutionFieldSelect $matomoFields $id) + <select id="rowEvolutionPeriod$id" class="form-control rowEvolutionField"> #foreach($key in $matomoFields.keySet()) <option value="$matomoFields.get($key)"> $services.localization.render('analytics.rowEvolution.field.' + $key) @@ -106,7 +106,7 @@ </select> #end -#macro(modalRowEvolution $matomoFields $macroName) +#macro(modalRowEvolution $matomoFields $macroName $id) #set ($requestPageURL = $xwiki.getURL('Analytics.Code.Macros.RowEvolutionJson')) {{html clean=false}} <div class="modal fade analyticsRowEvolutionModal" tabindex="-1" role="dialog" aria-label="chartModalLabel" @@ -121,13 +121,13 @@ $services.localization.render('analytics.rowEvolution.label') </h4> </div> - <div class="rowEvolutionOptions" data-macro-name=$macroName data-request-page-url=$requestPageURL> + <div class="rowEvolutionOptions" data-macro-name=$macroName data-request-page-url=$requestPageURL data-color=$theme.notificationInfoColor> <div class="modal-body"> <div class="container-fluid"> <div class="row"> <div class="col-md-3"> - <p>$services.localization.render('analytics.rowEvolution.period')</p> - <select class="rowEvolutionPeriod form-control"> + <label for="rowEvolutionPeriod$id" >$services.localization.render('analytics.rowEvolution.period')</label> + <select id="rowEvolutionPeriod$id" class="rowEvolutionPeriod form-control"> #durationOption("Day") #durationOption("Week") #durationOption("Month") @@ -135,15 +135,15 @@ </select> </div> <div class="col-md-3"> - <p>$services.localization.render('analytics.rowEvolution.periodLength')</p> - #durationSelectList("rowEvolutionPeriodLengthDay" [30, 8, 60, 90, 180, 365, 500]) - #durationSelectList("rowEvolutionPeriodLengthWeek" [4, 12, 26, 52, 104, 500]) - #durationSelectList("rowEvolutionPeriodLengthMonth" [3, 6, 12, 24, 36, 120]) - #durationSelectList("rowEvolutionPeriodLengthYear" [3, 5, 10]) + <label id=$id>$services.localization.render('analytics.rowEvolution.periodLength'):</label> + #durationSelectList("rowEvolutionPeriodLengthDay" [30, 8, 60, 90, 180, 365, 500] $id) + #durationSelectList("rowEvolutionPeriodLengthWeek" [4, 12, 26, 52, 104, 500] $id) + #durationSelectList("rowEvolutionPeriodLengthMonth" [3, 6, 12, 24, 36, 120] $id) + #durationSelectList("rowEvolutionPeriodLengthYear" [3, 5, 10] $id) </div> <div class="col-md-3"> - <p>$services.localization.render('analytics.rowEvolution.field')</p> - #rowEvolutionFieldSelect($matomoFields) + <label for="rowEvolutionPeriod$id" >$services.localization.render('analytics.rowEvolution.field')</label> + #rowEvolutionFieldSelect($matomoFields $id) </div> </div> </div> From c15af984967d52bcb195c996f9b014bbda092f0a Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 31 Aug 2023 16:36:41 +0300 Subject: [PATCH 066/105] Migrate MostViewedMacro from livetable to livedata #21 * Added a method to process the nodes and added a new test for the filtering. --- .../com/xwiki/analytics/AnalyticsManager.java | 2 +- .../com/xwiki/analytics/JsonNormaliser.java | 2 +- .../internal/AbstractJsonNormaliser.java | 35 ++++++++------- .../internal/MatomoAnalyticsManager.java | 8 ++-- .../internal/MostViewedJsonNormaliser.java | 44 +++---------------- .../MostViewedJsonNormaliserTest.java | 22 ++++++++-- .../testNormalizationWithFilter.json | 18 ++++++++ .../testNormalizationWithoutFilterArray.json | 22 ++++++++++ ...testNormalizationWithoutFilterObject.json} | 20 --------- .../Code/Macros/MostViewedPagesJSON.xml | 8 ++-- .../Analytics/Code/Macros/VelocityMacros.xml | 2 +- 11 files changed, 94 insertions(+), 89 deletions(-) create mode 100644 application-analytics-default/src/test/resources/testNormalizationWithFilter.json create mode 100644 application-analytics-default/src/test/resources/testNormalizationWithoutFilterArray.json rename application-analytics-default/src/test/resources/{tests.json => testNormalizationWithoutFilterObject.json} (59%) diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java b/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java index a3fa9d7b..88c0dc17 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/AnalyticsManager.java @@ -42,7 +42,7 @@ public interface AnalyticsManager * * @param jsonNormaliserHint hint to select the json normaliser * @param parameters a list of key, value pairs that will represent the parameters for the request - * @param filters holds the criteria for filtering a dataset. + * @param filters holds the criteria for filtering a dataset * @return a jsonNode with the processed data */ JsonNode requestData(Map parameters, Map filters, String jsonNormaliserHint) diff --git a/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java index 35ce4b3e..d518e9af 100644 --- a/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java +++ b/application-analytics-api/src/main/java/com/xwiki/analytics/JsonNormaliser.java @@ -41,7 +41,7 @@ public interface JsonNormaliser /** * Normalise the data returned to have the expected format. * - * @param filters holds the criteria for filtering a dataset. + * @param filters holds the criteria for filtering a dataset * @param jsonString a string that has the expected format * @return a {@link JsonNode} with the root of the normalized JSON * @throws JsonProcessingException Throws this error when the jsonString param is not a proper json diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java index 41943b69..323c5b04 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java @@ -30,7 +30,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.xwiki.analytics.JsonNormaliser; /** @@ -54,7 +53,7 @@ public abstract class AbstractJsonNormaliser implements JsonNormaliser * Normalize Matomo response for format consistency and add extra information needed by XWiki. * * @param jsonString a string that has the expected format - * @param filters holds the criteria for filtering a dataset. + * @param filters holds the criteria for filtering a dataset * @return the normalised json * @throws JsonProcessingException */ @@ -82,7 +81,7 @@ public JsonNode normaliseData(String jsonString, Map filters) th * This method processes each entry to append an empty date to it. * * @param jsonNode an array of jsons - * @param filters holds the criteria for filtering a dataset. + * @param filters holds the criteria for filtering a dataset * @return array of jsons */ protected ArrayNode processArrayNode(JsonNode jsonNode, Map filters) @@ -90,11 +89,7 @@ protected ArrayNode processArrayNode(JsonNode jsonNode, Map filt ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); for (JsonNode objNode : jsonNode) { if (objNode.isObject()) { - // If all the filters match then the entry would be added to the final json. - if (matchesAllFilters(objNode, filters)) { - ((ObjectNode) objNode).put(DATE, ""); - arrayNode.add(objNode); - } + processNode(arrayNode, objNode, filters); } } return arrayNode; @@ -106,7 +101,7 @@ protected ArrayNode processArrayNode(JsonNode jsonNode, Map filt * objects. * * @param jsonNode - * @param filters holds the criteria for filtering a dataset. + * @param filters holds the criteria for filtering a dataset * @return array of jsons */ protected ArrayNode processObjectNode(JsonNode jsonNode, Map filters) throws JsonProcessingException @@ -118,11 +113,7 @@ protected ArrayNode processObjectNode(JsonNode jsonNode, Map fil JsonNode childNode = jsonNode.get(date); for (JsonNode objNode : childNode) { if (objNode.isObject()) { - // If all the filters match then the entry would be added to the final json. - if (matchesAllFilters(objNode, filters)) { - ((ObjectNode) objNode).put(DATE, date); - arrayNode.add(objNode); - } + processNode(arrayNode, objNode, filters); } } } @@ -137,11 +128,25 @@ protected boolean matchesAllFilters(JsonNode objNode, Map filter for (Map.Entry entry : filters.entrySet()) { String filterField = entry.getKey(); String filterValue = entry.getValue(); - // Check if the field exists and its value matches the filter value if (!(objNode.has(filterField) && objNode.get(filterField).asText().contains(filterValue))) { return false; } } return true; } + + /** + * Process the current node and add it to the final array of jsons. + * + * @param arrayNode final array of jsons + * @param currentNode the current json that has to be processed + * @param filters filters holds the criteria for filtering a dataset + */ + protected void processNode(ArrayNode arrayNode, JsonNode currentNode, Map filters) + { + // If all the filters match then the entry would be added to the final json. + if (matchesAllFilters(currentNode, filters)) { + arrayNode.add(currentNode); + } + } } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java index 82747dd6..0166bed3 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java @@ -72,8 +72,8 @@ public class MatomoAnalyticsManager implements AnalyticsManager * @return the altered Matomo request response, as JSON format */ @Override - public JsonNode requestData(Map parameters, - Map filters, String jsonNormaliserHint) throws IOException + public JsonNode requestData(Map parameters, Map filters, String jsonNormaliserHint) + throws IOException { parameters.put("idSite", configuration.getIdSite()); parameters.put("token_auth", configuration.getAuthenticationToken()); @@ -83,9 +83,9 @@ public JsonNode requestData(Map parameters, throw new RuntimeException("Error occurred while retrieving Matomo statistic results."); } HttpClient client = HttpClients.createDefault(); - HttpGet request = new HttpGet(buildURI(parameters)); + HttpGet request = new HttpGet(buildURI(parameters)); HttpResponse response = client.execute(request); - String responseBody = EntityUtils.toString(response.getEntity()); + String responseBody = EntityUtils.toString(response.getEntity()); return jsonNormaliser.normaliseData(responseBody, filters); } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index c3ea3368..1ca5a474 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -24,7 +24,6 @@ import java.net.URISyntaxException; import java.net.URL; import java.util.Collections; -import java.util.Iterator; import java.util.Map; import javax.inject.Inject; @@ -74,47 +73,14 @@ public class MostViewedJsonNormaliser extends AbstractJsonNormaliser private ResourceTypeResolver resourceTypeResolver; /** - * This method processes each entry to append an empty date to it and updates the label with the page name. + * Process the current node and add it to the final array of jsons. * - * @param jsonNode an array of jsons - * @param filters holds the criteria for filtering a dataset. - * @return + * @param arrayNode final array of jsons + * @param objNode the current json that has to be processed + * @param filters filters holds the criteria for filtering a dataset */ @Override - protected ArrayNode processArrayNode(JsonNode jsonNode, Map filters) - { - ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); - for (JsonNode objNode : jsonNode) { - handleNode(arrayNode, objNode, filters); - } - return arrayNode; - } - - /** - * Handle the scenario where Matomo returns an object with dates as keys and arrays of JSON objects as values. This - * function extracts the date from the key and adds it to each page and updates the label with the name of the page. - * Ultimately, it returns an array of JSON objects instead of a single JSON object. - * - * @param jsonNode json object - * @param filters holds the criteria for filtering a dataset. - * @return - */ - @Override - protected ArrayNode processObjectNode(JsonNode jsonNode, Map filters) - { - ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); - Iterator fieldNames = jsonNode.fieldNames(); - while (fieldNames.hasNext()) { - String date = fieldNames.next(); - JsonNode childNode = jsonNode.get(date); - for (JsonNode objNode : childNode) { - handleNode(arrayNode, objNode, filters); - } - } - return arrayNode; - } - - private void handleNode(ArrayNode arrayNode, JsonNode objNode, Map filters) + protected void processNode(ArrayNode arrayNode, JsonNode objNode, Map filters) { if (matchesAllFilters(objNode, filters)) { diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java index 6f95bef1..29517d0d 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java @@ -23,6 +23,7 @@ import java.io.InputStream; import java.net.URL; import java.util.Collections; +import java.util.HashMap; import org.junit.jupiter.api.Test; import org.xwiki.resource.ResourceReferenceResolver; @@ -62,7 +63,7 @@ public class MostViewedJsonNormaliserTest @Test public void testObjectJSONResponse() throws Exception { - readJSONS(); + readJSONS("/testNormalizationWithoutFilterObject.json"); setupURLs("http://localhost:8080/xwiki/bin/view/Analytics/", "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage"); assertEquals(node.get("ResponseObjectJSON"), @@ -72,13 +73,25 @@ public void testObjectJSONResponse() throws Exception @Test public void testArrayJSONResponse() throws Exception { - readJSONS(); + readJSONS("/testNormalizationWithoutFilterArray.json"); setupURLs("http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages", "http://localhost:8081/xwiki/bin/view/Main/"); assertEquals(node.get("ResponseArrayJSON"), mostViewedJsonNormaliser.normaliseData(node.get("ArrayJSONS").toString(), null)); } + @Test + public void testFiltersJson() throws Exception + { + readJSONS("/testNormalizationWithFilter.json"); + HashMap filters= new HashMap<>(); + filters.put("label", "/xwiki/bin/view/Analytics/Code/MostViwedPages"); + setupURLs("http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages", + "http://localhost:8081/xwiki/bin/view/Main/"); + assertEquals(node.get("ResponseArrayJSONFilter"), + mostViewedJsonNormaliser.normaliseData(node.get("ArrayJSONSFilter").toString(), filters)); + } + private void setupURLs(String url1, String url2) throws Exception { ExtendedURL extendedURL1 = new ExtendedURL(new URL(url1), null); @@ -91,10 +104,11 @@ private void setupURLs(String url1, String url2) throws Exception when(resourceReferenceResolver.resolve(extendedURL2, resourceType2, Collections.emptyMap())).thenReturn(null); } - private void readJSONS() throws IOException + private void readJSONS(String file) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); - InputStream is = JsonReader.class.getResourceAsStream("/tests.json"); + InputStream is = JsonReader.class.getResourceAsStream(file); node = objectMapper.readTree(is); } + } diff --git a/application-analytics-default/src/test/resources/testNormalizationWithFilter.json b/application-analytics-default/src/test/resources/testNormalizationWithFilter.json new file mode 100644 index 00000000..8e3796a1 --- /dev/null +++ b/application-analytics-default/src/test/resources/testNormalizationWithFilter.json @@ -0,0 +1,18 @@ +{ + "ArrayJSONSFilter": [ + { + "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" + }, + { + "label": "/xwiki/bin/view/Main/", + "url": "http://localhost:8081/xwiki/bin/view/Main/" + } + ], + "ResponseArrayJSONFilter": [ + { + "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" + } + ] +} \ No newline at end of file diff --git a/application-analytics-default/src/test/resources/testNormalizationWithoutFilterArray.json b/application-analytics-default/src/test/resources/testNormalizationWithoutFilterArray.json new file mode 100644 index 00000000..3913c626 --- /dev/null +++ b/application-analytics-default/src/test/resources/testNormalizationWithoutFilterArray.json @@ -0,0 +1,22 @@ +{ + "ArrayJSONS": [ + { + "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" + }, + { + "label": "/xwiki/bin/view/Main/", + "url": "http://localhost:8081/xwiki/bin/view/Main/" + } + ], + "ResponseArrayJSON": [ + { + "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" + }, + { + "label": "/xwiki/bin/view/Main/", + "url": "http://localhost:8081/xwiki/bin/view/Main/" + } + ] +} \ No newline at end of file diff --git a/application-analytics-default/src/test/resources/tests.json b/application-analytics-default/src/test/resources/testNormalizationWithoutFilterObject.json similarity index 59% rename from application-analytics-default/src/test/resources/tests.json rename to application-analytics-default/src/test/resources/testNormalizationWithoutFilterObject.json index 7aadfcf6..9a64ccfb 100644 --- a/application-analytics-default/src/test/resources/tests.json +++ b/application-analytics-default/src/test/resources/testNormalizationWithoutFilterObject.json @@ -28,25 +28,5 @@ "label": "/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" } - ], - "ArrayJSONS": [ - { - "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", - "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" - }, - { - "label": "/xwiki/bin/view/Main/", - "url": "http://localhost:8081/xwiki/bin/view/Main/" - } - ], - "ResponseArrayJSON": [ - { - "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", - "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" - }, - { - "label": "/xwiki/bin/view/Main/", - "url": "http://localhost:8081/xwiki/bin/view/Main/" - } ] } \ No newline at end of file diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml index 670b0669..52c2bd53 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml @@ -70,7 +70,7 @@ #set ($filters={}) #foreach ($key in $keys.keySet()) #set ($value = $request.get($key)) - #if ($value && $value!="") + #if ($value && $value != '') $filters.put($keys.get($key), $request.get($key)) #end #end @@ -88,8 +88,8 @@ 'flat' : '1' }) - ## Json from matomo - #set ($analyticsResult = $services.analytics.getMatomoRequestResult($parameters,$filters, 'MostViewedPages')) + ## Get the JSON response from Matomo. + #set ($analyticsResult = $services.analytics.getMatomoRequestResult($parameters, $filters, 'MostViewedPages')) #if ($offset < $analyticsResult.size()) #set ($toIndex = $mathtool.min($mathtool.add($offset, $limit), $analyticsResult.size())) #set ($resultList = $services.analytics.subSetJSON($analyticsResult, $offset, $toIndex)) @@ -104,7 +104,7 @@ #foreach ($currentEntry in $resultList) #set ($url = $currentEntry.get('url').asText()) #set ($discard = $results.rows.add({ - 'title' : "#createLink($currentEntry.get('label').asText(), $url)", + 'title' : "#createLinkElement($currentEntry.get('label').asText(), $url)", 'visits' : $currentEntry.get('nb_visits').asText(), 'hits' : $currentEntry.get('nb_hits').asText(), 'timeSpent' : $currentEntry.get('sum_time_spent').asText(), diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index 0bed82f4..288e4956 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -62,7 +62,7 @@ #set ($date = $startDate + ',' + $endDate) #end -#macro (createLink $text $link) +#macro (createLinkElement $text $link) <a href=$link>$text</a> #end {{/velocity}} From eededf25f5d40679eed1db1985d058dac983e4e3 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 31 Aug 2023 16:37:02 +0300 Subject: [PATCH 067/105] Migrate MostViewedMacro from livetable to livedata #21 * Removed an unused variable. --- .../com/xwiki/analytics/internal/AbstractJsonNormaliser.java | 1 - 1 file changed, 1 deletion(-) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java index 323c5b04..1191da8a 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java @@ -42,7 +42,6 @@ public abstract class AbstractJsonNormaliser implements JsonNormaliser { protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - protected static final String DATE = "date"; protected static final String LABEL = "label"; From 9eca5598b647c923985eb7e285b7be4fd0bc571f Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 31 Aug 2023 16:43:40 +0300 Subject: [PATCH 068/105] Row Evolution feature for the MostViewedPages #15 * Added a new file to store the jsons for the tests --- .../RowEvolutionJsonNormaliserTest.java | 2 +- .../testNormalizationRowEvolutionArray.json | 185 ++++++++++++++++++ 2 files changed, 186 insertions(+), 1 deletion(-) diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliserTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliserTest.java index 8d3c3c6e..3fdc43ca 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliserTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliserTest.java @@ -48,7 +48,7 @@ public class RowEvolutionJsonNormaliserTest private void readJSONS() throws IOException { ObjectMapper objectMapper = new ObjectMapper(); - InputStream is = JsonReader.class.getResourceAsStream("/tests.json"); + InputStream is = JsonReader.class.getResourceAsStream("/testNormalizationRowEvolutionArray.json"); node = objectMapper.readTree(is); } diff --git a/application-analytics-default/src/test/resources/testNormalizationRowEvolutionArray.json b/application-analytics-default/src/test/resources/testNormalizationRowEvolutionArray.json index e69de29b..32068788 100644 --- a/application-analytics-default/src/test/resources/testNormalizationRowEvolutionArray.json +++ b/application-analytics-default/src/test/resources/testNormalizationRowEvolutionArray.json @@ -0,0 +1,185 @@ +{ + "RowEvolutionTestObject": { + "2023-08-07": [ + { + "label": "/MostViewedPage", + "nb_visits": 2, + "nb_uniq_visitors": 1, + "nb_hits": 2, + "sum_time_spent": 96, + "avg_time_on_page": 48, + "bounce_rate": "0%", + "exit_rate": "0%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-08": [ + { + "label": "/MostViewedPage", + "nb_visits": 3, + "nb_uniq_visitors": 1, + "nb_hits": 67, + "sum_time_spent": 10263, + "nb_hits_following_search": "1", + "exit_nb_uniq_visitors": 1, + "exit_nb_visits": "1", + "avg_time_on_page": 153, + "bounce_rate": "0%", + "exit_rate": "33%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-09": [ + { + "label": "/MostViewedPage", + "nb_visits": 2, + "nb_uniq_visitors": 1, + "nb_hits": 76, + "sum_time_spent": 5822, + "exit_nb_uniq_visitors": 1, + "exit_nb_visits": "2", + "avg_time_on_page": 77, + "bounce_rate": "0%", + "exit_rate": "100%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-10": [ + { + "label": "/MostViewedPage", + "nb_visits": 1, + "nb_uniq_visitors": 1, + "nb_hits": 16, + "sum_time_spent": 1794, + "avg_time_on_page": 112, + "bounce_rate": "0%", + "exit_rate": "0%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-11": [ + + ], + "2023-08-12": [ + + ], + "2023-08-13": [ + { + "label": "/MostViewedPage", + "nb_visits": 1, + "nb_uniq_visitors": 1, + "nb_hits": 1, + "sum_time_spent": 0, + "entry_nb_uniq_visitors": 1, + "entry_nb_visits": "1", + "entry_nb_actions": "1", + "entry_sum_visit_length": "0", + "entry_bounce_count": "1", + "exit_nb_uniq_visitors": 1, + "exit_nb_visits": "1", + "avg_time_on_page": 0, + "bounce_rate": "100%", + "exit_rate": "100%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-14": [ + + ], + "2023-08-15": [ + + ], + "2023-08-16": [ + + ] + }, + "RowEvolutionTestObjectResponse": [ + { + "label": "/MostViewedPage", + "nb_visits": 2, + "nb_uniq_visitors": 1, + "nb_hits": 2, + "sum_time_spent": 96, + "avg_time_on_page": 48, + "bounce_rate": "0%", + "exit_rate": "0%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "date": "2023-08-07" + }, + { + "label": "/MostViewedPage", + "nb_visits": 3, + "nb_uniq_visitors": 1, + "nb_hits": 67, + "sum_time_spent": 10263, + "nb_hits_following_search": "1", + "exit_nb_uniq_visitors": 1, + "exit_nb_visits": "1", + "avg_time_on_page": 153, + "bounce_rate": "0%", + "exit_rate": "33%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "date": "2023-08-08" + }, + { + "label": "/MostViewedPage", + "nb_visits": 2, + "nb_uniq_visitors": 1, + "nb_hits": 76, + "sum_time_spent": 5822, + "exit_nb_uniq_visitors": 1, + "exit_nb_visits": "2", + "avg_time_on_page": 77, + "bounce_rate": "0%", + "exit_rate": "100%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "date": "2023-08-09" + }, + { + "label": "/MostViewedPage", + "nb_visits": 1, + "nb_uniq_visitors": 1, + "nb_hits": 16, + "sum_time_spent": 1794, + "avg_time_on_page": 112, + "bounce_rate": "0%", + "exit_rate": "0%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "date": "2023-08-10" + }, + { + "date": "2023-08-11" + }, + { + "date": "2023-08-12" + }, + { + "label": "/MostViewedPage", + "nb_visits": 1, + "nb_uniq_visitors": 1, + "nb_hits": 1, + "sum_time_spent": 0, + "entry_nb_uniq_visitors": 1, + "entry_nb_visits": "1", + "entry_nb_actions": "1", + "entry_sum_visit_length": "0", + "entry_bounce_count": "1", + "exit_nb_uniq_visitors": 1, + "exit_nb_visits": "1", + "avg_time_on_page": 0, + "bounce_rate": "100%", + "exit_rate": "100%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "date": "2023-08-13" + }, + { + "date": "2023-08-14" + }, + { + "date": "2023-08-15" + }, + { + "date": "2023-08-16" + } + ] +} \ No newline at end of file From ee29da968c4c64113dc663a49ba4eeaf70adb1f7 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 1 Sep 2023 17:02:08 +0300 Subject: [PATCH 069/105] Migrate MostViewedMacro from livetable to livedata #21 * Added new tests and a new macro to get the subset of a json --- .../internal/AbstractJsonNormaliser.java | 18 +-- .../internal/MatomoAnalyticsManager.java | 63 ++++++++-- .../internal/MostViewedJsonNormaliser.java | 20 +--- .../DefaultAnalyticsConfiguration.java | 8 +- .../script/AnalyticsScriptService.java | 19 --- .../internal/MatomoAnalyticsManagerTest.java | 48 +++++--- .../MostViewedJsonNormaliserTest.java | 108 ++++++++++++++---- .../DefaultAnalyticsConfigurationTest.java | 94 ++++++++++++++- .../mostViewedPages/jsonWithoutURL.json | 18 +++ .../normalizationWithOneFilter.json} | 4 +- ...eDataWithArrayResponseWithoutFilters.json} | 0 .../normalizeDataWithMalformedUrl.json | 14 +++ .../normalizeDataWithMultipleFilters.json | 76 ++++++++++++ ...DataWithObjectResponseWithoutFilters.json} | 0 .../normalizeDataWithPartialMatchFilter.json | 87 ++++++++++++++ .../Code/Macros/MostViewedPagesJSON.xml | 3 +- .../Analytics/Code/Macros/VelocityMacros.xml | 8 ++ 17 files changed, 485 insertions(+), 103 deletions(-) create mode 100644 application-analytics-default/src/test/resources/mostViewedPages/jsonWithoutURL.json rename application-analytics-default/src/test/resources/{testNormalizationWithFilter.json => mostViewedPages/normalizationWithOneFilter.json} (73%) rename application-analytics-default/src/test/resources/{testNormalizationWithoutFilterArray.json => mostViewedPages/normalizeDataWithArrayResponseWithoutFilters.json} (100%) create mode 100644 application-analytics-default/src/test/resources/mostViewedPages/normalizeDataWithMalformedUrl.json create mode 100644 application-analytics-default/src/test/resources/mostViewedPages/normalizeDataWithMultipleFilters.json rename application-analytics-default/src/test/resources/{testNormalizationWithoutFilterObject.json => mostViewedPages/normalizeDataWithObjectResponseWithoutFilters.json} (100%) create mode 100644 application-analytics-default/src/test/resources/mostViewedPages/normalizeDataWithPartialMatchFilter.json diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java index 1191da8a..58df3a4d 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/AbstractJsonNormaliser.java @@ -42,7 +42,6 @@ public abstract class AbstractJsonNormaliser implements JsonNormaliser { protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - protected static final String LABEL = "label"; @Inject @@ -87,8 +86,8 @@ protected ArrayNode processArrayNode(JsonNode jsonNode, Map filt { ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); for (JsonNode objNode : jsonNode) { - if (objNode.isObject()) { - processNode(arrayNode, objNode, filters); + if (objNode.isObject() && matchesAllFilters(objNode, filters)) { + arrayNode.add(processNode(objNode)); } } return arrayNode; @@ -111,8 +110,8 @@ protected ArrayNode processObjectNode(JsonNode jsonNode, Map fil String date = fieldNames.next(); JsonNode childNode = jsonNode.get(date); for (JsonNode objNode : childNode) { - if (objNode.isObject()) { - processNode(arrayNode, objNode, filters); + if (objNode.isObject() && matchesAllFilters(objNode, filters)) { + arrayNode.add(processNode(objNode)); } } } @@ -137,15 +136,10 @@ protected boolean matchesAllFilters(JsonNode objNode, Map filter /** * Process the current node and add it to the final array of jsons. * - * @param arrayNode final array of jsons * @param currentNode the current json that has to be processed - * @param filters filters holds the criteria for filtering a dataset */ - protected void processNode(ArrayNode arrayNode, JsonNode currentNode, Map filters) + protected JsonNode processNode(JsonNode currentNode) { - // If all the filters match then the entry would be added to the final json. - if (matchesAllFilters(currentNode, filters)) { - arrayNode.add(currentNode); - } + return currentNode; } } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java index 0166bed3..0cb19f2e 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MatomoAnalyticsManager.java @@ -54,6 +54,8 @@ @Unstable public class MatomoAnalyticsManager implements AnalyticsManager { + private static final String FAIL_RETRIEVE = "Error occurred while retrieving Matomo statistic results."; + @Inject private Logger logger; @@ -75,18 +77,41 @@ public class MatomoAnalyticsManager implements AnalyticsManager public JsonNode requestData(Map parameters, Map filters, String jsonNormaliserHint) throws IOException { + validateParameters(parameters); parameters.put("idSite", configuration.getIdSite()); parameters.put("token_auth", configuration.getAuthenticationToken()); - JsonNormaliser jsonNormaliser = this.getNormaliser(jsonNormaliserHint); - if (jsonNormaliser == null) { - logger.warn("There is no JSON normalizer associated with the [{}] hint you provided.", jsonNormaliserHint); - throw new RuntimeException("Error occurred while retrieving Matomo statistic results."); + JsonNormaliser jsonNormaliser = this.selectNormaliser(jsonNormaliserHint); + getJsonNormaliser(jsonNormaliserHint); + return jsonNormaliser.normaliseData(executeHttpRequest(parameters), filters); + } + + /** + * Validates that the request parameters are not null. + * + * @param parameters a list of key, value pairs + * @throws RuntimeException if parameters are null + */ + private void validateParameters(Map parameters) + { + if (parameters == null) { + logger.warn("Parameters must not be null."); + throw new RuntimeException(FAIL_RETRIEVE); } + } + + /** + * Execute the HTTP request and returns the response body as a string. + * + * @param parameters the HTTP request parameters + * @return response body as string + * @throws IOException if there's a problem executing the HTTP request + */ + private String executeHttpRequest(Map parameters) throws IOException + { HttpClient client = HttpClients.createDefault(); HttpGet request = new HttpGet(buildURI(parameters)); HttpResponse response = client.execute(request); - String responseBody = EntityUtils.toString(response.getEntity()); - return jsonNormaliser.normaliseData(responseBody, filters); + return EntityUtils.toString(response.getEntity()); } /** @@ -99,16 +124,30 @@ private URI buildURI(Map parameterList) { UriBuilder uriBuilder = UriBuilder.fromUri(configuration.getRequestAddress()).path("index.php"); - if (parameterList != null && !parameterList.isEmpty()) { - for (Map.Entry entry : parameterList.entrySet()) { - uriBuilder.queryParam(entry.getKey(), entry.getValue()); - } + for (Map.Entry entry : parameterList.entrySet()) { + uriBuilder.queryParam(entry.getKey(), entry.getValue()); } - return uriBuilder.build(); } - private JsonNormaliser getNormaliser(String hint) + /** + * Gets the JsonNormaliser based on the hint. + * + * @param jsonNormaliserHint the hint + * @return the JsonNormaliser instance + * @throws RuntimeException if JsonNormaliser is null + */ + private JsonNormaliser getJsonNormaliser(String jsonNormaliserHint) + { + JsonNormaliser jsonNormaliser = this.selectNormaliser(jsonNormaliserHint); + if (jsonNormaliser == null) { + logger.warn("There is no JSON normalizer associated with the [{}] hint you provided.", jsonNormaliserHint); + throw new RuntimeException(FAIL_RETRIEVE); + } + return jsonNormaliser; + } + + private JsonNormaliser selectNormaliser(String hint) { if (hint.equals(MostViewedJsonNormaliser.HINT)) { return this.mostViewedNormaliser; diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index 1ca5a474..5a115856 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -24,7 +24,6 @@ import java.net.URISyntaxException; import java.net.URL; import java.util.Collections; -import java.util.Map; import javax.inject.Inject; import javax.inject.Named; @@ -42,7 +41,6 @@ import org.xwiki.url.ExtendedURL; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.xwiki.analytics.JsonNormaliser; @@ -75,22 +73,16 @@ public class MostViewedJsonNormaliser extends AbstractJsonNormaliser /** * Process the current node and add it to the final array of jsons. * - * @param arrayNode final array of jsons - * @param objNode the current json that has to be processed - * @param filters filters holds the criteria for filtering a dataset + * @param currentNode the current json that has to be processed */ @Override - protected void processNode(ArrayNode arrayNode, JsonNode objNode, Map filters) + protected JsonNode processNode(JsonNode currentNode) { - if (matchesAllFilters(objNode, filters)) { - if (objNode.isObject()) { - if (objNode.has(URL)) { - this.handleURLNode((ObjectNode) objNode); - } - arrayNode.add(objNode); - } + if (currentNode.has(URL)) { + this.handleURLNode((ObjectNode) currentNode); } + return currentNode; } /** @@ -115,7 +107,7 @@ private ResourceReference getResourceReferenceFromStringURL(String resourceRefer return this.resourceReferenceResolver.resolve(extendedURL, resourceType, Collections.emptyMap()); } catch (MalformedURLException | CreateResourceReferenceException | CreateResourceTypeException | UnsupportedResourceReferenceException | URISyntaxException e) { - this.logger.warn("Failed to get resource reference from URL: [{}].", resourceReferenceURL, + this.logger.warn("Failed to get resource reference from URL: [{}]. Caused by [{}]", resourceReferenceURL, ExceptionUtils.getRootCauseMessage(e)); return null; } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java index 5d2f8c48..b4bb0c42 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfiguration.java @@ -25,6 +25,7 @@ import javax.inject.Named; import javax.inject.Singleton; +import org.slf4j.Logger; import org.xwiki.component.annotation.Component; import org.xwiki.configuration.ConfigurationSource; import org.xwiki.stability.Unstable; @@ -46,6 +47,9 @@ public class DefaultAnalyticsConfiguration implements AnalyticsConfiguration @Named("analytics") private ConfigurationSource configDocument; + @Inject + private Logger logger; + @Override public String getRequestAddress() { @@ -67,8 +71,8 @@ public String getAuthenticationToken() private T getProperty(String key, T defaultValue) { T value = this.configDocument.getProperty(key, defaultValue); - if (value.equals(defaultValue)) { - throw new RuntimeException(MessageFormat.format("The {0} is missing", key)); + if (value == null || value.equals(defaultValue)) { + throw new RuntimeException(MessageFormat.format("The {0} is missing.", key)); } return value; } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java index 8db718b4..b7173977 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/script/AnalyticsScriptService.java @@ -19,8 +19,6 @@ */ package com.xwiki.analytics.script; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import javax.inject.Inject; @@ -68,21 +66,4 @@ public JsonNode getMatomoRequestResult(Map parameters, Map subSetJSON(JsonNode jsonNode, int start, int end) - { - List subSet = new ArrayList<>(); - for (int i = start; i < end; i++) { - subSet.add(jsonNode.get(i)); - } - return subSet; - } } diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java index b1a781cb..29bccad0 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MatomoAnalyticsManagerTest.java @@ -34,12 +34,12 @@ import com.xwiki.analytics.JsonNormaliser; import com.xwiki.analytics.configuration.AnalyticsConfiguration; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * Unit test for {@link MatomoAnalyticsManager} @@ -62,8 +62,11 @@ public class MatomoAnalyticsManagerTest @MockComponent private Logger logger; + /** + * Will test the Manager with a valid hint. + */ @Test - public void requestDataWithCorrectHintForNormaliser() throws IOException, InterruptedException + public void requestDataWithCorrectHintForNormaliser() throws IOException { when(this.configuration.getAuthenticationToken()).thenReturn("token"); when(this.configuration.getRequestAddress()).thenReturn("http://130.61.233.19/matomo"); @@ -72,23 +75,38 @@ public void requestDataWithCorrectHintForNormaliser() throws IOException, Interr verify(this.jsonNormaliser).normaliseData(any(String.class), eq(new HashMap<>())); } + /** + * Will test that an error happens if the user sets the parameters to be equal with null. + */ + @Test + public void requestDataWithNullParameters() + { + ReflectionUtils.setFieldValue(this.matomoAnalyticsManager, "logger", this.logger); + when(configuration.getAuthenticationToken()).thenReturn("token"); + when(configuration.getRequestAddress()).thenReturn("http://130.61.233.19/matomo"); + when(configuration.getIdSite()).thenReturn("3"); + RuntimeException exception = assertThrows(RuntimeException.class, () -> { + matomoAnalyticsManager.requestData(null, new HashMap<>(), MostViewedJsonNormaliser.HINT); + }); + assertEquals("Error occurred while retrieving Matomo statistic results.", exception.getMessage()); + verify(logger).warn("Parameters must not be null."); + } + + /** + * Will test the Manager with an invalid hint + */ @Test - public void requestDataWithInvalidNormaliser() throws IOException, InterruptedException + public void requestDataWithInvalidNormaliser() { ReflectionUtils.setFieldValue(this.matomoAnalyticsManager, "logger", this.logger); when(this.configuration.getAuthenticationToken()).thenReturn("token"); when(this.configuration.getRequestAddress()).thenReturn("http://130.61.233.19/matomo"); when(this.configuration.getIdSite()).thenReturn("3"); - try { - when(this.matomoAnalyticsManager.requestData(new HashMap<>(),new HashMap<>(), "RANDOM_NORMALISER")).thenThrow( - new RuntimeException()); - verify(this.logger).warn("There is no JSON normalizer associated with the [{}] hint you provided.", - "RANDOM_NORMALISER"); - } - catch (RuntimeException e) - { - assertEquals(e.getMessage(), "Error occurred while retrieving Matomo statistic results."); - } - + RuntimeException exception = assertThrows(RuntimeException.class, () -> { + matomoAnalyticsManager.requestData(new HashMap<>(), new HashMap<>(), "RANDOM_NORMALISER"); + }); + assertEquals("Error occurred while retrieving Matomo statistic results.", exception.getMessage()); + verify(logger).warn("There is no JSON normalizer associated with the [{}] hint you provided.", + "RANDOM_NORMALISER"); } } diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java index 29517d0d..736bc0a9 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/MostViewedJsonNormaliserTest.java @@ -21,11 +21,17 @@ import java.io.IOException; import java.io.InputStream; -import java.net.URL; import java.util.Collections; import java.util.HashMap; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.eq; + import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.xwiki.component.util.ReflectionUtils; +import org.xwiki.resource.CreateResourceTypeException; +import org.xwiki.resource.ResourceReference; import org.xwiki.resource.ResourceReferenceResolver; import org.xwiki.resource.ResourceType; import org.xwiki.resource.ResourceTypeResolver; @@ -39,6 +45,8 @@ import com.google.gson.stream.JsonReader; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** @@ -58,50 +66,105 @@ public class MostViewedJsonNormaliserTest @MockComponent private ResourceTypeResolver resourceTypeResolver; + @MockComponent + private Logger logger; + private static JsonNode node; + /** + * Will test if the normaliser works properly when the response from Matomo is an object. + */ @Test - public void testObjectJSONResponse() throws Exception + public void normalizeDataWithObjectResponseWithoutFilters() throws Exception { - readJSONS("/testNormalizationWithoutFilterObject.json"); - setupURLs("http://localhost:8080/xwiki/bin/view/Analytics/", - "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage"); + readJSONS("/mostViewedPages/normalizeDataWithObjectResponseWithoutFilters.json"); + setupAnyURL(); assertEquals(node.get("ResponseObjectJSON"), mostViewedJsonNormaliser.normaliseData(node.get("ObjectJSON").toString(), null)); } + /** + * Will test if the normaliser works properly when the response from Matomo is an array of jsons. + */ @Test - public void testArrayJSONResponse() throws Exception + public void normalizeDataWithArrayResponseWithoutFilters() throws Exception { - readJSONS("/testNormalizationWithoutFilterArray.json"); - setupURLs("http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages", - "http://localhost:8081/xwiki/bin/view/Main/"); + readJSONS("/mostViewedPages/normalizeDataWithArrayResponseWithoutFilters.json"); + setupAnyURL(); assertEquals(node.get("ResponseArrayJSON"), mostViewedJsonNormaliser.normaliseData(node.get("ArrayJSONS").toString(), null)); } + /** + * Will test if the filtering works properly with an exact match. + */ @Test - public void testFiltersJson() throws Exception + public void normalizeDataWithExactMatchFilter() throws Exception { - readJSONS("/testNormalizationWithFilter.json"); - HashMap filters= new HashMap<>(); - filters.put("label", "/xwiki/bin/view/Analytics/Code/MostViwedPages"); - setupURLs("http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages", - "http://localhost:8081/xwiki/bin/view/Main/"); + readJSONS("/mostViewedPages/normalizationWithOneFilter.json"); + setupAnyURL(); + HashMap filters = new HashMap<>(); + filters.put("label", "/xwiki/bin/view/Analytics/Code/MostViewedPages"); assertEquals(node.get("ResponseArrayJSONFilter"), mostViewedJsonNormaliser.normaliseData(node.get("ArrayJSONSFilter").toString(), filters)); } - private void setupURLs(String url1, String url2) throws Exception + /** + * Will test if the filtering works properly with partial matching. + */ + @Test + public void normalizeDataWithPartialMatchFilter() throws Exception + { + readJSONS("/mostViewedPages/normalizeDataWithMultipleFilters.json"); + setupAnyURL(); + HashMap filters = new HashMap<>(); + filters.put("nb_hits", "27"); + filters.put("label", "MostViewedPage?editor=wiki"); + assertEquals(node.get("ResponseWithFilters"), + mostViewedJsonNormaliser.normaliseData(node.get("JsonFilters").toString(), filters)); + } + + @Test + public void normalizeDataWithMultipleFilters() throws Exception + { + readJSONS("/mostViewedPages/normalizeDataWithPartialMatchFilter.json"); + setupAnyURL(); + HashMap filters = new HashMap<>(); + filters.put("nb_hits", "27"); + filters.put("nb_hits", "27"); + + assertEquals(node.get("ResponseWithFilters"), + mostViewedJsonNormaliser.normaliseData(node.get("JsonFilters").toString(), filters)); + } + + /** + * Will test the case when the url is invalid. + */ + @Test + public void normalizeDataWithMalformedUrl() throws Exception { - ExtendedURL extendedURL1 = new ExtendedURL(new URL(url1), null); - ExtendedURL extendedURL2 = new ExtendedURL(new URL(url2), null); + ReflectionUtils.setFieldValue(this.mostViewedJsonNormaliser, "logger", this.logger); + readJSONS("/mostViewedPages/normalizeDataWithMalformedUrl.json"); + HashMap filters = new HashMap<>(); + assertEquals(node.get("MalformedJSONResponse"), + mostViewedJsonNormaliser.normaliseData(node.get("MalformedJSON").toString(), filters)); + verify(logger).warn("Failed to get resource reference from URL: [{}]. Caused by [{}]", + "htttp://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages", + "MalformedURLException: unknown protocol: htttp"); + } - ResourceType resourceType1 = resourceTypeResolver.resolve(extendedURL1, Collections.emptyMap()); - ResourceType resourceType2 = resourceTypeResolver.resolve(extendedURL2, Collections.emptyMap()); + @Test + public void jsonWithoutURL() throws IOException + { + readJSONS("/mostViewedPages/jsonWithoutURL.json"); + assertEquals(node.get("ArrayWithoutURLResponse"), + mostViewedJsonNormaliser.normaliseData(node.get("ArrayWithoutURL").toString(), null)); + } - when(resourceReferenceResolver.resolve(extendedURL1, resourceType1, Collections.emptyMap())).thenReturn(null); - when(resourceReferenceResolver.resolve(extendedURL2, resourceType2, Collections.emptyMap())).thenReturn(null); + private void setupAnyURL() throws Exception + { + ResourceType resourceType1 = mock(ResourceType.class); + when(resourceTypeResolver.resolve(any(ExtendedURL.class), eq(Collections.emptyMap()))).thenReturn(null); } private void readJSONS(String file) throws IOException @@ -110,5 +173,4 @@ private void readJSONS(String file) throws IOException InputStream is = JsonReader.class.getResourceAsStream(file); node = objectMapper.readTree(is); } - } diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfigurationTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfigurationTest.java index d0fbd6d3..c58c7d6f 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfigurationTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfigurationTest.java @@ -22,12 +22,16 @@ import javax.inject.Named; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.xwiki.component.util.ReflectionUtils; import org.xwiki.configuration.ConfigurationSource; import org.xwiki.test.junit5.mockito.ComponentTest; import org.xwiki.test.junit5.mockito.InjectMockComponents; import org.xwiki.test.junit5.mockito.MockComponent; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** @@ -45,24 +49,108 @@ public class DefaultAnalyticsConfigurationTest @Named("analytics") private ConfigurationSource analyticsConfigurationSource; + @MockComponent + private Logger logger; + @Test - public void getAuthenticationTokenTest() + public void getAuthenticationToken() { when(this.analyticsConfigurationSource.getProperty("authToken", "")).thenReturn("token"); assertEquals("token", this.defaultAnalyticsConfiguration.getAuthenticationToken()); } @Test - public void getSiteIDTest() + public void getSiteID() { when(this.analyticsConfigurationSource.getProperty("siteId", "")).thenReturn("3"); assertEquals("3", this.defaultAnalyticsConfiguration.getIdSite()); } @Test - public void getAddressTest() + public void getAddress() { when(this.analyticsConfigurationSource.getProperty("requestAddress", "")).thenReturn("13.192.200.19"); assertEquals("13.192.200.19", this.defaultAnalyticsConfiguration.getRequestAddress()); } + + /** + * Will test to see if the function will throw an error when the request address is null + */ + @Test + public void missingRequestAddress() + { + ReflectionUtils.setFieldValue(this.defaultAnalyticsConfiguration, "logger", this.logger); + RuntimeException exception = assertThrows(RuntimeException.class, () -> { + this.defaultAnalyticsConfiguration.getRequestAddress(); + }); + assertEquals("The requestAddress is missing.", exception.getMessage()); + } + + /** + * Will test to see if the function will throw an error when the id site is null + */ + @Test + public void missingIdSite() + { + ReflectionUtils.setFieldValue(this.defaultAnalyticsConfiguration, "logger", this.logger); + RuntimeException exception = assertThrows(RuntimeException.class, () -> { + this.defaultAnalyticsConfiguration.getIdSite(); + }); + assertEquals("The siteId is missing.", exception.getMessage()); + } + + /** + * Will test to see if the function will throw an error when the auth token is null + */ + @Test + public void missingAuthToken() + { + ReflectionUtils.setFieldValue(this.defaultAnalyticsConfiguration, "logger", this.logger); + RuntimeException exception = assertThrows(RuntimeException.class, () -> { + this.defaultAnalyticsConfiguration.getAuthenticationToken(); + }); + assertEquals("The authToken is missing.", exception.getMessage()); + } + + /** + * Will test to see if the function will throw an error when the request address is empty + */ + @Test + public void returnDefaultValueRequestAddress() + { + when(this.analyticsConfigurationSource.getProperty("requestAddress", "")).thenReturn(""); + ReflectionUtils.setFieldValue(this.defaultAnalyticsConfiguration, "logger", this.logger); + RuntimeException exception = assertThrows(RuntimeException.class, () -> { + this.defaultAnalyticsConfiguration.getRequestAddress(); + }); + assertEquals("The requestAddress is missing.", exception.getMessage()); + } + + /** + * Will test to see if the function will throw an error when the id site is empty + */ + @Test + public void returnDefaultIdSite() + { + when(this.analyticsConfigurationSource.getProperty("siteId", "")).thenReturn(""); + ReflectionUtils.setFieldValue(this.defaultAnalyticsConfiguration, "logger", this.logger); + RuntimeException exception = assertThrows(RuntimeException.class, () -> { + this.defaultAnalyticsConfiguration.getIdSite(); + }); + assertEquals("The siteId is missing.", exception.getMessage()); + } + + /** + * Will test to see if the function will throw an error when the auth token is empty + */ + @Test + public void returnDefaultAuthToken() + { + when(this.analyticsConfigurationSource.getProperty("authToken", "")).thenReturn(""); + ReflectionUtils.setFieldValue(this.defaultAnalyticsConfiguration, "logger", this.logger); + RuntimeException exception = assertThrows(RuntimeException.class, () -> { + this.defaultAnalyticsConfiguration.getAuthenticationToken(); + }); + assertEquals("The authToken is missing.", exception.getMessage()); + } } diff --git a/application-analytics-default/src/test/resources/mostViewedPages/jsonWithoutURL.json b/application-analytics-default/src/test/resources/mostViewedPages/jsonWithoutURL.json new file mode 100644 index 00000000..009508fa --- /dev/null +++ b/application-analytics-default/src/test/resources/mostViewedPages/jsonWithoutURL.json @@ -0,0 +1,18 @@ +{ + "ArrayWithoutURL": [ + { + "label": "/xwiki/bin/view/Analytics/Code/MostViewedPages" + }, + { + "label": "/xwiki/bin/view/Main/" + } + ], + "ArrayWithoutURLResponse": [ + { + "label": "/xwiki/bin/view/Analytics/Code/MostViewedPages" + }, + { + "label": "/xwiki/bin/view/Main/" + } + ] +} \ No newline at end of file diff --git a/application-analytics-default/src/test/resources/testNormalizationWithFilter.json b/application-analytics-default/src/test/resources/mostViewedPages/normalizationWithOneFilter.json similarity index 73% rename from application-analytics-default/src/test/resources/testNormalizationWithFilter.json rename to application-analytics-default/src/test/resources/mostViewedPages/normalizationWithOneFilter.json index 8e3796a1..2adf13ab 100644 --- a/application-analytics-default/src/test/resources/testNormalizationWithFilter.json +++ b/application-analytics-default/src/test/resources/mostViewedPages/normalizationWithOneFilter.json @@ -1,7 +1,7 @@ { "ArrayJSONSFilter": [ { - "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", + "label": "/xwiki/bin/view/Analytics/Code/MostViewedPages", "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" }, { @@ -11,7 +11,7 @@ ], "ResponseArrayJSONFilter": [ { - "label": "/xwiki/bin/view/Analytics/Code/MostViwedPages", + "label": "/xwiki/bin/view/Analytics/Code/MostViewedPages", "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" } ] diff --git a/application-analytics-default/src/test/resources/testNormalizationWithoutFilterArray.json b/application-analytics-default/src/test/resources/mostViewedPages/normalizeDataWithArrayResponseWithoutFilters.json similarity index 100% rename from application-analytics-default/src/test/resources/testNormalizationWithoutFilterArray.json rename to application-analytics-default/src/test/resources/mostViewedPages/normalizeDataWithArrayResponseWithoutFilters.json diff --git a/application-analytics-default/src/test/resources/mostViewedPages/normalizeDataWithMalformedUrl.json b/application-analytics-default/src/test/resources/mostViewedPages/normalizeDataWithMalformedUrl.json new file mode 100644 index 00000000..b5f62fe1 --- /dev/null +++ b/application-analytics-default/src/test/resources/mostViewedPages/normalizeDataWithMalformedUrl.json @@ -0,0 +1,14 @@ +{ + "MalformedJSON": [ + { + "label": "/xwiki/bin/view/Analytics/Code/MostViewedPages", + "url": "htttp://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" + } + ], + "MalformedJSONResponse": [ + { + "label": "/xwiki/bin/view/Analytics/Code/MostViewedPages", + "url": "htttp://localhost:8080/xwiki/bin/view/Analytics/Code/MostViwedPages" + } + ] +} \ No newline at end of file diff --git a/application-analytics-default/src/test/resources/mostViewedPages/normalizeDataWithMultipleFilters.json b/application-analytics-default/src/test/resources/mostViewedPages/normalizeDataWithMultipleFilters.json new file mode 100644 index 00000000..c84b4c9e --- /dev/null +++ b/application-analytics-default/src/test/resources/mostViewedPages/normalizeDataWithMultipleFilters.json @@ -0,0 +1,76 @@ +{ + "JsonFilters": [ + { + "label": "/xwiki/bin/view/Main/", + "nb_hits": 142, + "url": "http://localhost:8081/xwiki/bin/view/Main/" + }, + { + "label": "/xwiki/bin/view/Analytics/", + "nb_hits": 79, + "url": "http://localhost:8080/xwiki/bin/view/Analytics/" + }, + { + "label": "/xwiki/bin/admin/XWiki/XWikiPreferences", + "nb_hits": 38, + "url": "http://localhost:8080/xwiki/bin/admin/XWiki/XWikiPreferences" + }, + { + "label": "/xwiki/bin/admin/XWiki/XWikiPreferences?editor=globaladmin§ion=analytics.application", + "nb_hits": 16, + "url": "http://localhost:8080/xwiki/bin/admin/XWiki/XWikiPreferences?editor=globaladmin§ion=analytics.application" + }, + { + "label": "/xwiki/bin/admin/XWiki/XWikiPreferences?editor=globaladmin§ion=XWiki.Extensions", + "nb_hits": 20, + "url": "http://localhost:8081/xwiki/bin/admin/XWiki/XWikiPreferences?editor=globaladmin§ion=XWiki.Extensions" + }, + { + "label": "/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "nb_hits": 278, + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + }, + { + "label": "/xwiki/bin/view/Sandbox/TestPage1", + "nb_hits": 49, + "url": "http://localhost:8081/xwiki/bin/view/Sandbox/TestPage1" + }, + { + "label": "/xwiki/bin/view/Analytics/Code/Macros/MostViewedPages", + "nb_hits": 115, + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPages" + }, + { + "label": "/xwiki/bin/get/Collabora/Code/Main?document=xwiki:Sandbox.TestPage1&filename=test.odt&action=edit&xpage=plain&minify=false", + "nb_hits": 25, + "url": "http://collabora:8080/xwiki/bin/get/Collabora/Code/Main?document=xwiki:Sandbox.TestPage1&filename=test.odt&action=edit&xpage=plain&minify=false" + }, + { + "label": "/xwiki/bin/admin/XWiki/XWikiPreferences?editor=globaladmin§ion=Import", + "nb_hits": 9, + "url": "http://localhost:8080/xwiki/bin/admin/XWiki/XWikiPreferences?editor=globaladmin§ion=Import" + }, + { + "label": "/xwiki/bin/edit/Analytics/Code/Macros/MostViewedPage?editor=wiki", + "nb_hits": 27, + "url": "http://localhost:8080/xwiki/bin/edit/Analytics/Code/Macros/MostViewedPage?editor=wiki" + }, + { + "label": "/xwiki/bin/view/Analytics/Code/Macros/Macros", + "nb_hits": 14, + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/Macros" + }, + { + "label": "/xwiki/bin/view/Analytics/Code/Macros/MostViewedPageJSON", + "nb_hits": 27, + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPageJSON" + } + ], + "ResponseWithFilters": [ + { + "label": "/xwiki/bin/edit/Analytics/Code/Macros/MostViewedPage?editor=wiki", + "nb_hits": 27, + "url": "http://localhost:8080/xwiki/bin/edit/Analytics/Code/Macros/MostViewedPage?editor=wiki" + } + ] +} \ No newline at end of file diff --git a/application-analytics-default/src/test/resources/testNormalizationWithoutFilterObject.json b/application-analytics-default/src/test/resources/mostViewedPages/normalizeDataWithObjectResponseWithoutFilters.json similarity index 100% rename from application-analytics-default/src/test/resources/testNormalizationWithoutFilterObject.json rename to application-analytics-default/src/test/resources/mostViewedPages/normalizeDataWithObjectResponseWithoutFilters.json diff --git a/application-analytics-default/src/test/resources/mostViewedPages/normalizeDataWithPartialMatchFilter.json b/application-analytics-default/src/test/resources/mostViewedPages/normalizeDataWithPartialMatchFilter.json new file mode 100644 index 00000000..b3323381 --- /dev/null +++ b/application-analytics-default/src/test/resources/mostViewedPages/normalizeDataWithPartialMatchFilter.json @@ -0,0 +1,87 @@ +{ + "JsonFilters": [ + { + "label": "/xwiki/bin/view/Main/", + "nb_hits": 142, + "url": "http://localhost:8081/xwiki/bin/view/Main/" + }, + { + "label": "/xwiki/bin/view/Analytics/", + "nb_hits": 79, + "url": "http://localhost:8080/xwiki/bin/view/Analytics/" + }, + { + "label": "/xwiki/bin/admin/XWiki/XWikiPreferences", + "nb_hits": 38, + "url": "http://localhost:8080/xwiki/bin/admin/XWiki/XWikiPreferences" + }, + { + "label": "/xwiki/bin/admin/XWiki/XWikiPreferences?editor=globaladmin§ion=analytics.application", + "nb_hits": 16, + "url": "http://localhost:8080/xwiki/bin/admin/XWiki/XWikiPreferences?editor=globaladmin§ion=analytics.application" + }, + { + "label": "/xwiki/bin/admin/XWiki/XWikiPreferences?editor=globaladmin§ion=XWiki.Extensions", + "nb_hits": 20, + "url": "http://localhost:8081/xwiki/bin/admin/XWiki/XWikiPreferences?editor=globaladmin§ion=XWiki.Extensions" + }, + { + "label": "/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "nb_hits": 278, + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + }, + { + "label": "/xwiki/bin/view/Sandbox/TestPage1", + "nb_hits": 49, + "url": "http://localhost:8081/xwiki/bin/view/Sandbox/TestPage1" + }, + { + "label": "/xwiki/bin/view/Analytics/Code/Macros/MostViewedPages", + "nb_hits": 115, + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPages" + }, + { + "label": "/xwiki/bin/get/Collabora/Code/Main?document=xwiki:Sandbox.TestPage1&filename=test.odt&action=edit&xpage=plain&minify=false", + "nb_hits": 25, + "url": "http://collabora:8080/xwiki/bin/get/Collabora/Code/Main?document=xwiki:Sandbox.TestPage1&filename=test.odt&action=edit&xpage=plain&minify=false" + }, + { + "label": "/xwiki/bin/admin/XWiki/XWikiPreferences?editor=globaladmin§ion=Import", + "nb_hits": 9, + "url": "http://localhost:8080/xwiki/bin/admin/XWiki/XWikiPreferences?editor=globaladmin§ion=Import" + }, + { + "label": "/xwiki/bin/edit/Analytics/Code/Macros/MostViewedPage?editor=wiki", + "nb_hits": 27, + "url": "http://localhost:8080/xwiki/bin/edit/Analytics/Code/Macros/MostViewedPage?editor=wiki" + }, + { + "label": "/xwiki/bin/view/Analytics/Code/Macros/Macros", + "nb_hits": 14, + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/Macros" + }, + { + "label": "/xwiki/bin/view/Analytics/Code/Macros/MostViewedPageJSON", + "nb_hits": 27, + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPageJSON" + } + ], + "ResponseWithFilters": [ + + { + "label": "/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "nb_hits": 278, + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + }, + { + "label": "/xwiki/bin/edit/Analytics/Code/Macros/MostViewedPage?editor=wiki", + "nb_hits": 27, + "url": "http://localhost:8080/xwiki/bin/edit/Analytics/Code/Macros/MostViewedPage?editor=wiki" + }, + { + "label": "/xwiki/bin/view/Analytics/Code/Macros/MostViewedPageJSON", + "nb_hits": 27, + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPageJSON" + } + ] +} \ No newline at end of file diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml index 52c2bd53..a0ef586e 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPagesJSON.xml @@ -92,7 +92,7 @@ #set ($analyticsResult = $services.analytics.getMatomoRequestResult($parameters, $filters, 'MostViewedPages')) #if ($offset < $analyticsResult.size()) #set ($toIndex = $mathtool.min($mathtool.add($offset, $limit), $analyticsResult.size())) - #set ($resultList = $services.analytics.subSetJSON($analyticsResult, $offset, $toIndex)) + #subSet( $analyticsResult, $offset, $toIndex, $resultList) #end #set ($results = { "totalrows": $analyticsResult.size(), @@ -104,6 +104,7 @@ #foreach ($currentEntry in $resultList) #set ($url = $currentEntry.get('url').asText()) #set ($discard = $results.rows.add({ + 'test': $resultList, 'title' : "#createLinkElement($currentEntry.get('label').asText(), $url)", 'visits' : $currentEntry.get('nb_visits').asText(), 'hits' : $currentEntry.get('nb_hits').asText(), diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index 288e4956..3f41b406 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -65,5 +65,13 @@ #macro (createLinkElement $text $link) <a href=$link>$text</a> #end + +#macro (subSet $object $start $end $resultList) + #set($resultList = []) + #foreach($i in [$start..$end]) + #set($element = $object.get($i)) + #set($dummy = $resultList.add($element)) + #end +#end {{/velocity}} From 0f13439d4f2d0dbf2ef117ac018cfe111ff89166 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 1 Sep 2023 20:37:35 +0300 Subject: [PATCH 070/105] Migrate MostViewedMacro from livetable to livedata #21 - Fixed a bug where the subSet macro would return the wrong number of elements --- .../src/main/resources/Analytics/Code/Macros/VelocityMacros.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index 3f41b406..06b1a089 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -68,6 +68,8 @@ #macro (subSet $object $start $end $resultList) #set($resultList = []) + #set ($resultList = []) + #set ($end = $mathtool.sub($end,1)) #foreach($i in [$start..$end]) #set($element = $object.get($i)) #set($dummy = $resultList.add($element)) From c383430ccb4e07704464876e4d1a830fd04f8e8e Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 1 Sep 2023 20:38:49 +0300 Subject: [PATCH 071/105] Row Evolution feature for the MostViewedPages #15 - Removed the ability to sort the actions column --- .../main/resources/Analytics/Code/Macros/MostViewedPages.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml index 34e1c45c..7eb735c3 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/MostViewedPages.xml @@ -263,7 +263,7 @@ {'id': 'entryNbActions', 'displayer': 'html'}, {'id': 'bounceRate', 'displayer': 'html'}, {'id': 'exitRate', 'displayer': 'html'}, - {'id': 'actions', 'displayer': 'html'} + {'id': 'actions',"filterable": false, "sortable": false, 'displayer': 'html'} ], 'entryDescriptor': { 'idProperty': 'title' From 89b5a01ddc3b82d2bad649d6e84beb868bdeb2f9 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 1 Sep 2023 21:06:15 +0300 Subject: [PATCH 072/105] Row Evolution feature for the MostViewedPages #15 - Added a filtering system for the Row Evolution Feature --- .../internal/MostViewedJsonNormaliser.java | 1 - .../internal/RowEvolutionJsonNormaliser.java | 51 ++++++++++++------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index 5a115856..801632a2 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -78,7 +78,6 @@ public class MostViewedJsonNormaliser extends AbstractJsonNormaliser @Override protected JsonNode processNode(JsonNode currentNode) { - if (currentNode.has(URL)) { this.handleURLNode((ObjectNode) currentNode); } diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java index ec8b666f..84bd179a 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java @@ -55,21 +55,14 @@ public class RowEvolutionJsonNormaliser extends AbstractJsonNormaliser private static final String DATE = "date"; - @Override - public JsonNode normaliseData(String jsonString, Map filters) throws JsonProcessingException - { - JsonNode jsonNode = OBJECT_MAPPER.readTree(jsonString); - return processObjectNode(jsonNode); - } - /** * Transforming the json object returned by Matomo into an array of jsons to make it easier to use in javascript. * - * @param jsonNode the JSON node to be processed. - * @return the processed and transformed JSON node. - * @throws JsonProcessingException if any error occurs during JSON processing. + * @param jsonNode + * @param filters holds the criteria for filtering a dataset + * @return filtered array of jsons */ - private ArrayNode processObjectNode(JsonNode jsonNode) throws JsonProcessingException + protected ArrayNode processObjectNode(JsonNode jsonNode, Map filters) throws JsonProcessingException { ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); Iterator fieldNames = jsonNode.fieldNames(); @@ -77,15 +70,39 @@ private ArrayNode processObjectNode(JsonNode jsonNode) throws JsonProcessingExce while (fieldNames.hasNext()) { String date = fieldNames.next(); JsonNode childNode = jsonNode.get(date); - JsonNode node = childNode.get(0); - if (node == null) { - node = OBJECT_MAPPER.readTree(String.format("{\"%s\" : \"%s\"}", DATE, date)); - } else { - ((ObjectNode) node).put(DATE, date); + if (childNode.get(0) == null) { + arrayNode.add(OBJECT_MAPPER.readTree(String.format("{\"%s\" : \"%s\"}", DATE, date))); + } + + for (JsonNode node : childNode) { + if (matchesAllFilters(node, filters)) { + arrayNode.add(processNode(node, date)); + } } - arrayNode.add(node); } return arrayNode; } + + protected JsonNode processNode(JsonNode currentNode, String date) + { + ((ObjectNode) currentNode).put(DATE, date); + return currentNode; + } + + @Override + protected boolean matchesAllFilters(JsonNode objNode, Map filters) + { + if (filters == null) { + return true; + } + for (Map.Entry entry : filters.entrySet()) { + String filterField = entry.getKey(); + String filterValue = entry.getValue(); + if (!(objNode.has(filterField) && objNode.get(filterField).asText().equals(filterValue))) { + return false; + } + } + return true; + } } From 057f16ce637dd88f9789b9cbeb159fd17ad40b3b Mon Sep 17 00:00:00 2001 From: Farcasut Date: Sun, 3 Sep 2023 15:13:40 +0300 Subject: [PATCH 073/105] Row Evolution feature for the MostViewedPages #15 - Adding new tests --- .../internal/RowEvolutionJsonNormaliser.java | 14 +- .../RowEvolutionJsonNormaliserTest.java | 33 +++- .../normalizeDataWithFilters.json | 137 +++++++++++++ .../normalizeDataWithNullFilters.json} | 0 .../normalizeDataWithoutFilters.json | 185 ++++++++++++++++++ 5 files changed, 357 insertions(+), 12 deletions(-) create mode 100644 application-analytics-default/src/test/resources/rowEvolution/normalizeDataWithFilters.json rename application-analytics-default/src/test/resources/{testNormalizationRowEvolutionArray.json => rowEvolution/normalizeDataWithNullFilters.json} (100%) create mode 100644 application-analytics-default/src/test/resources/rowEvolution/normalizeDataWithoutFilters.json diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java index 84bd179a..80766856 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java @@ -70,16 +70,18 @@ protected ArrayNode processObjectNode(JsonNode jsonNode, Map fil while (fieldNames.hasNext()) { String date = fieldNames.next(); JsonNode childNode = jsonNode.get(date); - - if (childNode.get(0) == null) { - arrayNode.add(OBJECT_MAPPER.readTree(String.format("{\"%s\" : \"%s\"}", DATE, date))); - } - + boolean nodeFound = true; for (JsonNode node : childNode) { if (matchesAllFilters(node, filters)) { - arrayNode.add(processNode(node, date)); + arrayNode.add(this.processNode(node, date)); + nodeFound = false; + break; } } + if (childNode.get(0) == null || nodeFound) { + arrayNode.add(OBJECT_MAPPER.readTree(String.format("{\"%s\" : \"%s\"}", DATE, date))); + processNode(OBJECT_MAPPER.createObjectNode(), date); + } } return arrayNode; } diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliserTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliserTest.java index 3fdc43ca..78183ec2 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliserTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliserTest.java @@ -23,6 +23,7 @@ import java.io.InputStream; import java.util.HashMap; +import org.apache.ecs.storage.Hash; import org.junit.jupiter.api.Test; import org.xwiki.test.junit5.mockito.ComponentTest; import org.xwiki.test.junit5.mockito.InjectMockComponents; @@ -43,21 +44,41 @@ public class RowEvolutionJsonNormaliserTest { @InjectMockComponents private RowEvolutionJsonNormaliser rowEvolutionJsonNormaliser; + private static JsonNode node; - private void readJSONS() throws IOException + private void readJSONS(String path) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); - InputStream is = JsonReader.class.getResourceAsStream("/testNormalizationRowEvolutionArray.json"); + InputStream is = JsonReader.class.getResourceAsStream(path); node = objectMapper.readTree(is); } @Test - public void testNormalisation() throws IOException + public void normalizeDataWithoutFilters() throws IOException + { + readJSONS("/rowEvolution/normalizeDataWithoutFilters.json"); + assertEquals(node.get("response"), + rowEvolutionJsonNormaliser.normaliseData(node.get("initialJson").toString(), new HashMap<>())); + } + + @Test + public void normalizeDataWithNullFilters() throws IOException { - readJSONS(); - assertEquals(node.get("RowEvolutionTestObjectResponse"), - rowEvolutionJsonNormaliser.normaliseData(node.get("RowEvolutionTestObject").toString(), new HashMap<>())); + readJSONS("/rowEvolution/normalizeDataWithoutFilters.json"); + assertEquals(node.get("response"), + rowEvolutionJsonNormaliser.normaliseData(node.get("initialJson").toString(), new HashMap<>())); + } + @Test + public void normalizeDataWithFilters() throws IOException + { + readJSONS("/rowEvolution/normalizeDataWithFilters.json"); + HashMap filters = new HashMap<>(); + filters.put("label", "/MostViewedPage"); + filters.put("sum_time_spent", "96"); + filters.put("avg_time_on_page", "48"); + assertEquals(node.get("response"), + rowEvolutionJsonNormaliser.normaliseData(node.get("initialJson").toString(), filters)); } } diff --git a/application-analytics-default/src/test/resources/rowEvolution/normalizeDataWithFilters.json b/application-analytics-default/src/test/resources/rowEvolution/normalizeDataWithFilters.json new file mode 100644 index 00000000..235b5304 --- /dev/null +++ b/application-analytics-default/src/test/resources/rowEvolution/normalizeDataWithFilters.json @@ -0,0 +1,137 @@ +{ + "initialJson": { + "2023-08-07": [ + { + "label": "/MostViewedPage", + "nb_visits": 2, + "nb_uniq_visitors": 1, + "nb_hits": 2, + "sum_time_spent": 96, + "avg_time_on_page": 48, + "bounce_rate": "0%", + "exit_rate": "0%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-08": [ + { + "label": "/MostViewedPage", + "nb_visits": 3, + "nb_uniq_visitors": 1, + "nb_hits": 67, + "sum_time_spent": 10263, + "nb_hits_following_search": "1", + "exit_nb_uniq_visitors": 1, + "exit_nb_visits": "1", + "avg_time_on_page": 153, + "bounce_rate": "0%", + "exit_rate": "33%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-09": [ + { + "label": "/MostViewedPage", + "nb_visits": 2, + "nb_uniq_visitors": 1, + "nb_hits": 76, + "sum_time_spent": 5822, + "exit_nb_uniq_visitors": 1, + "exit_nb_visits": "2", + "avg_time_on_page": 77, + "bounce_rate": "0%", + "exit_rate": "100%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-10": [ + { + "label": "/MostViewedPage", + "nb_visits": 1, + "nb_uniq_visitors": 1, + "nb_hits": 16, + "sum_time_spent": 1794, + "avg_time_on_page": 112, + "bounce_rate": "0%", + "exit_rate": "0%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-11": [ + + ], + "2023-08-12": [ + + ], + "2023-08-13": [ + { + "label": "/MostViewedPage", + "nb_visits": 1, + "nb_uniq_visitors": 1, + "nb_hits": 1, + "sum_time_spent": 0, + "entry_nb_uniq_visitors": 1, + "entry_nb_visits": "1", + "entry_nb_actions": "1", + "entry_sum_visit_length": "0", + "entry_bounce_count": "1", + "exit_nb_uniq_visitors": 1, + "exit_nb_visits": "1", + "avg_time_on_page": 0, + "bounce_rate": "100%", + "exit_rate": "100%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-14": [ + + ], + "2023-08-15": [ + + ], + "2023-08-16": [ + + ] + }, + "response": [ + { + "label": "/MostViewedPage", + "nb_visits": 2, + "nb_uniq_visitors": 1, + "nb_hits": 2, + "sum_time_spent": 96, + "avg_time_on_page": 48, + "bounce_rate": "0%", + "exit_rate": "0%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "date": "2023-08-07" + }, + { + "date": "2023-08-08" + }, + { + "date": "2023-08-09" + }, + { + "date": "2023-08-10" + }, + { + "date": "2023-08-11" + }, + { + "date": "2023-08-12" + }, + { + "date": "2023-08-13" + }, + { + "date": "2023-08-14" + }, + { + "date": "2023-08-15" + }, + { + "date": "2023-08-16" + } + ] +} \ No newline at end of file diff --git a/application-analytics-default/src/test/resources/testNormalizationRowEvolutionArray.json b/application-analytics-default/src/test/resources/rowEvolution/normalizeDataWithNullFilters.json similarity index 100% rename from application-analytics-default/src/test/resources/testNormalizationRowEvolutionArray.json rename to application-analytics-default/src/test/resources/rowEvolution/normalizeDataWithNullFilters.json diff --git a/application-analytics-default/src/test/resources/rowEvolution/normalizeDataWithoutFilters.json b/application-analytics-default/src/test/resources/rowEvolution/normalizeDataWithoutFilters.json new file mode 100644 index 00000000..ca1cd9c8 --- /dev/null +++ b/application-analytics-default/src/test/resources/rowEvolution/normalizeDataWithoutFilters.json @@ -0,0 +1,185 @@ +{ + "initialJson": { + "2023-08-07": [ + { + "label": "/MostViewedPage", + "nb_visits": 2, + "nb_uniq_visitors": 1, + "nb_hits": 2, + "sum_time_spent": 96, + "avg_time_on_page": 48, + "bounce_rate": "0%", + "exit_rate": "0%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-08": [ + { + "label": "/MostViewedPage", + "nb_visits": 3, + "nb_uniq_visitors": 1, + "nb_hits": 67, + "sum_time_spent": 10263, + "nb_hits_following_search": "1", + "exit_nb_uniq_visitors": 1, + "exit_nb_visits": "1", + "avg_time_on_page": 153, + "bounce_rate": "0%", + "exit_rate": "33%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-09": [ + { + "label": "/MostViewedPage", + "nb_visits": 2, + "nb_uniq_visitors": 1, + "nb_hits": 76, + "sum_time_spent": 5822, + "exit_nb_uniq_visitors": 1, + "exit_nb_visits": "2", + "avg_time_on_page": 77, + "bounce_rate": "0%", + "exit_rate": "100%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-10": [ + { + "label": "/MostViewedPage", + "nb_visits": 1, + "nb_uniq_visitors": 1, + "nb_hits": 16, + "sum_time_spent": 1794, + "avg_time_on_page": 112, + "bounce_rate": "0%", + "exit_rate": "0%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-11": [ + + ], + "2023-08-12": [ + + ], + "2023-08-13": [ + { + "label": "/MostViewedPage", + "nb_visits": 1, + "nb_uniq_visitors": 1, + "nb_hits": 1, + "sum_time_spent": 0, + "entry_nb_uniq_visitors": 1, + "entry_nb_visits": "1", + "entry_nb_actions": "1", + "entry_sum_visit_length": "0", + "entry_bounce_count": "1", + "exit_nb_uniq_visitors": 1, + "exit_nb_visits": "1", + "avg_time_on_page": 0, + "bounce_rate": "100%", + "exit_rate": "100%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage" + } + ], + "2023-08-14": [ + + ], + "2023-08-15": [ + + ], + "2023-08-16": [ + + ] + }, + "response": [ + { + "label": "/MostViewedPage", + "nb_visits": 2, + "nb_uniq_visitors": 1, + "nb_hits": 2, + "sum_time_spent": 96, + "avg_time_on_page": 48, + "bounce_rate": "0%", + "exit_rate": "0%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "date": "2023-08-07" + }, + { + "label": "/MostViewedPage", + "nb_visits": 3, + "nb_uniq_visitors": 1, + "nb_hits": 67, + "sum_time_spent": 10263, + "nb_hits_following_search": "1", + "exit_nb_uniq_visitors": 1, + "exit_nb_visits": "1", + "avg_time_on_page": 153, + "bounce_rate": "0%", + "exit_rate": "33%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "date": "2023-08-08" + }, + { + "label": "/MostViewedPage", + "nb_visits": 2, + "nb_uniq_visitors": 1, + "nb_hits": 76, + "sum_time_spent": 5822, + "exit_nb_uniq_visitors": 1, + "exit_nb_visits": "2", + "avg_time_on_page": 77, + "bounce_rate": "0%", + "exit_rate": "100%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "date": "2023-08-09" + }, + { + "label": "/MostViewedPage", + "nb_visits": 1, + "nb_uniq_visitors": 1, + "nb_hits": 16, + "sum_time_spent": 1794, + "avg_time_on_page": 112, + "bounce_rate": "0%", + "exit_rate": "0%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "date": "2023-08-10" + }, + { + "date": "2023-08-11" + }, + { + "date": "2023-08-12" + }, + { + "label": "/MostViewedPage", + "nb_visits": 1, + "nb_uniq_visitors": 1, + "nb_hits": 1, + "sum_time_spent": 0, + "entry_nb_uniq_visitors": 1, + "entry_nb_visits": "1", + "entry_nb_actions": "1", + "entry_sum_visit_length": "0", + "entry_bounce_count": "1", + "exit_nb_uniq_visitors": 1, + "exit_nb_visits": "1", + "avg_time_on_page": 0, + "bounce_rate": "100%", + "exit_rate": "100%", + "url": "http://localhost:8080/xwiki/bin/view/Analytics/Code/Macros/MostViewedPage", + "date": "2023-08-13" + }, + { + "date": "2023-08-14" + }, + { + "date": "2023-08-15" + }, + { + "date": "2023-08-16" + } + ] +} \ No newline at end of file From 0c44c9c1d6c4796aa38fb695d11f9bf68e4b40cd Mon Sep 17 00:00:00 2001 From: Farcasut Date: Sun, 3 Sep 2023 15:17:11 +0300 Subject: [PATCH 074/105] Row Evolution feature for the MostViewedPages #15 - Removed a variable --- .../com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java | 1 - 1 file changed, 1 deletion(-) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java index 80766856..312b653d 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliser.java @@ -51,7 +51,6 @@ public class RowEvolutionJsonNormaliser extends AbstractJsonNormaliser */ public static final String HINT = "RowEvolution"; - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final String DATE = "date"; From 32b69b87eafa9c4518d20eee260f61122062762e Mon Sep 17 00:00:00 2001 From: Farcasut Date: Mon, 11 Sep 2023 15:12:25 +0300 Subject: [PATCH 075/105] Row Evolution feature for the MostViewedPages #15 - Merged the main branch --- application-analytics-test/docker/pom.xml | 88 +++++++++++++++++++ .../docker/src/test/it/AllIT.java | 40 +++++++++ application-analytics-test/pom.xml | 52 +++++++++++ pom.xml | 1 + 4 files changed, 181 insertions(+) create mode 100644 application-analytics-test/docker/pom.xml create mode 100644 application-analytics-test/docker/src/test/it/AllIT.java create mode 100644 application-analytics-test/pom.xml diff --git a/application-analytics-test/docker/pom.xml b/application-analytics-test/docker/pom.xml new file mode 100644 index 00000000..b0435fc0 --- /dev/null +++ b/application-analytics-test/docker/pom.xml @@ -0,0 +1,88 @@ + + + + + + 4.0.0 + + com.xwiki.analytics + application-analytics-test + 1.0-SNAPSHOT + + application-analytics-test-docker + Analytics Application (Pro) - Tests - Functional Docker Tests + + jar + + + true + + + + + org.xwiki.platform + xwiki-platform-test-docker + ${platform.version} + test + + + org.xwiki.platform + xwiki-platform-menu-test-pageobjects + ${platform.version} + test + + + org.xwiki.platform + xwiki-platform-panels-test-pageobjects + ${platform.version} + test + + + org.xwiki.platform + xwiki-platform-application-test-pageobjects + ${platform.version} + test + + + org.xwiki.platform + xwiki-platform-administration-test-pageobjects + ${platform.version} + test + + + +src/test/it + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + \ No newline at end of file diff --git a/application-analytics-test/docker/src/test/it/AllIT.java b/application-analytics-test/docker/src/test/it/AllIT.java new file mode 100644 index 00000000..2321235d --- /dev/null +++ b/application-analytics-test/docker/src/test/it/AllIT.java @@ -0,0 +1,40 @@ + +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.xwiki.test.docker.junit5.UITest; +import org.xwiki.test.ui.PageObjectSuite; +import org.xwiki.test.ui.XWikiWebDriver; +import org.xwiki.test.ui.TestUtils; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.xwiki.application.test.po.ApplicationIndexHomePage; +import org.xwiki.test.ui.po.ViewPage; +import org.xwiki.menu.test.po.MenuHomePage; +@UITest +public class AllIT +{ + @Test + @Order(1) + void verifyMenuInApplicationsIndex(TestUtils setup) + { + // Log in as superadmin + setup.loginAsSuperAdmin(); + + ApplicationIndexHomePage applicationIndexHomePage = ApplicationIndexHomePage.gotoPage(); + + assertTrue(applicationIndexHomePage.containsApplication("Menu")); + ViewPage vp = applicationIndexHomePage.clickApplication("Menu"); + + // Verify we're on the right page! + assertEquals(MenuHomePage.getSpace(), vp.getMetaDataValue("space")); + assertEquals(MenuHomePage.getPage(), vp.getMetaDataValue("page")); + + // Now log out to verify that the Menu entry is not displayed for guest users + setup.forceGuestUser(); + // Navigate again to the Application Index page to perform the verification + applicationIndexHomePage = ApplicationIndexHomePage.gotoPage(); + assertFalse(applicationIndexHomePage.containsApplication("Menu")); + } +} \ No newline at end of file diff --git a/application-analytics-test/pom.xml b/application-analytics-test/pom.xml new file mode 100644 index 00000000..b2222a45 --- /dev/null +++ b/application-analytics-test/pom.xml @@ -0,0 +1,52 @@ + + + + + + 4.0.0 + + com.xwiki.analytics + application-analytics + 1.0-SNAPSHOT + + application-analytics-test + Analytics Application (Pro) - Tests - Parent POM + pom + Discover unique insights to help you better understand the usage of your instance. Streamline and improve + internal processes by having full visibility on the most viewed pages. Understand how you can help your teams boost + productivity by using real-time statistics. + + + + + true + + true + + + + docker + + docker + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 4b19451b..57de3ec7 100644 --- a/pom.xml +++ b/pom.xml @@ -52,5 +52,6 @@ application-analytics-api application-analytics-default application-analytics-ui + application-analytics-test \ No newline at end of file From 13dfbb37fa28b387f4491809e3db24435d733ea9 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 14 Sep 2023 10:45:40 +0300 Subject: [PATCH 076/105] Test functiona --- .../application-analytics-test-docker/pom.xml | 110 ++++++++++++++++++ .../it/xwiki/analytics/test/ui/AllITs.java | 35 ++++++ .../xwiki/analytics/test/ui/AnalyticsIT.java | 35 ++++++ application-analytics-test/docker/pom.xml | 88 -------------- .../docker/src/test/it/AllIT.java | 40 ------- application-analytics-test/pom.xml | 24 ++-- 6 files changed, 193 insertions(+), 139 deletions(-) create mode 100644 application-analytics-test/application-analytics-test-docker/pom.xml create mode 100644 application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AllITs.java create mode 100644 application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java delete mode 100644 application-analytics-test/docker/pom.xml delete mode 100644 application-analytics-test/docker/src/test/it/AllIT.java diff --git a/application-analytics-test/application-analytics-test-docker/pom.xml b/application-analytics-test/application-analytics-test-docker/pom.xml new file mode 100644 index 00000000..c9ad3d94 --- /dev/null +++ b/application-analytics-test/application-analytics-test-docker/pom.xml @@ -0,0 +1,110 @@ + + + + + + 4.0.0 + + com.xwiki.analytics + application-analytics-test + 1.0-SNAPSHOT + + application-analytics-test-docker + jar + Functional tests for the Analytics Application + + + true + + + + + com.xwiki.licensing + application-licensing-test-api + ${licensing.version} + runtime + + + org.xwiki.platform + xwiki-platform-panels-ui + ${platform.version} + xar + + + + com.xwiki.licensing + application-licensing-test-dependencies + ${licensing.version} + pom + test + + + com.xwiki.analytics + application-analytics-ui + ${project.version} + xar + runtime + + + + src/test/it + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + + + clover + + + + org.openclover + clover + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + + xwiki.test.ui.profiles + clover + + + + + + + + + \ No newline at end of file diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AllITs.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AllITs.java new file mode 100644 index 00000000..7a43896c --- /dev/null +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AllITs.java @@ -0,0 +1,35 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package xwiki.analytics.test.ui; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.xwiki.test.docker.junit5.UITest; + + +@UITest +public class AllITs +{ + @Nested + @DisplayName("Analytics UI Test") + class NestedAnalyticsIT extends AnalyticsIT + { + + } +} diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java new file mode 100644 index 00000000..04056826 --- /dev/null +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -0,0 +1,35 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package xwiki.analytics.test.ui; + +import org.junit.jupiter.api.Test; +import org.xwiki.test.docker.junit5.UITest; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +@UITest +public class AnalyticsIT +{ + @Test + void appEntryRedirectsToHomePage() + { + assertFalse(true); + } +} diff --git a/application-analytics-test/docker/pom.xml b/application-analytics-test/docker/pom.xml deleted file mode 100644 index b0435fc0..00000000 --- a/application-analytics-test/docker/pom.xml +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - 4.0.0 - - com.xwiki.analytics - application-analytics-test - 1.0-SNAPSHOT - - application-analytics-test-docker - Analytics Application (Pro) - Tests - Functional Docker Tests - - jar - - - true - - - - - org.xwiki.platform - xwiki-platform-test-docker - ${platform.version} - test - - - org.xwiki.platform - xwiki-platform-menu-test-pageobjects - ${platform.version} - test - - - org.xwiki.platform - xwiki-platform-panels-test-pageobjects - ${platform.version} - test - - - org.xwiki.platform - xwiki-platform-application-test-pageobjects - ${platform.version} - test - - - org.xwiki.platform - xwiki-platform-administration-test-pageobjects - ${platform.version} - test - - - -src/test/it - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - - \ No newline at end of file diff --git a/application-analytics-test/docker/src/test/it/AllIT.java b/application-analytics-test/docker/src/test/it/AllIT.java deleted file mode 100644 index 2321235d..00000000 --- a/application-analytics-test/docker/src/test/it/AllIT.java +++ /dev/null @@ -1,40 +0,0 @@ - -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.runner.RunWith; -import org.xwiki.test.docker.junit5.UITest; -import org.xwiki.test.ui.PageObjectSuite; -import org.xwiki.test.ui.XWikiWebDriver; -import org.xwiki.test.ui.TestUtils; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.xwiki.application.test.po.ApplicationIndexHomePage; -import org.xwiki.test.ui.po.ViewPage; -import org.xwiki.menu.test.po.MenuHomePage; -@UITest -public class AllIT -{ - @Test - @Order(1) - void verifyMenuInApplicationsIndex(TestUtils setup) - { - // Log in as superadmin - setup.loginAsSuperAdmin(); - - ApplicationIndexHomePage applicationIndexHomePage = ApplicationIndexHomePage.gotoPage(); - - assertTrue(applicationIndexHomePage.containsApplication("Menu")); - ViewPage vp = applicationIndexHomePage.clickApplication("Menu"); - - // Verify we're on the right page! - assertEquals(MenuHomePage.getSpace(), vp.getMetaDataValue("space")); - assertEquals(MenuHomePage.getPage(), vp.getMetaDataValue("page")); - - // Now log out to verify that the Menu entry is not displayed for guest users - setup.forceGuestUser(); - // Navigate again to the Application Index page to perform the verification - applicationIndexHomePage = ApplicationIndexHomePage.gotoPage(); - assertFalse(applicationIndexHomePage.containsApplication("Menu")); - } -} \ No newline at end of file diff --git a/application-analytics-test/pom.xml b/application-analytics-test/pom.xml index b2222a45..e4412343 100644 --- a/application-analytics-test/pom.xml +++ b/application-analytics-test/pom.xml @@ -20,7 +20,9 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - + 4.0.0 com.xwiki.analytics @@ -28,24 +30,24 @@ 1.0-SNAPSHOT application-analytics-test - Analytics Application (Pro) - Tests - Parent POM + Application Analytics (PRO)- Tests - Parent POM pom - Discover unique insights to help you better understand the usage of your instance. Streamline and improve - internal processes by having full visibility on the most viewed pages. Understand how you can help your teams boost - productivity by using real-time statistics. - - - - + Application Analytics (PRO) - Tests - Parent POM + + + true true true - + + application-analytics-test-docker + + docker - docker + application-analytics-test-docker From 4841735a480d8d4461bb49c06d7a695899bd5c30 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 14 Sep 2023 15:14:36 +0300 Subject: [PATCH 077/105] test --- .../RowEvolutionJsonNormaliserTest.java | 84 ------------------- .../application-analytics-test-docker/pom.xml | 36 ++++++++ .../it/xwiki/analytics/test/ui/AllITs.java | 2 +- .../xwiki/analytics/test/ui/AnalyticsIT.java | 15 +++- 4 files changed, 51 insertions(+), 86 deletions(-) delete mode 100644 application-analytics-default/src/test/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliserTest.java diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliserTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliserTest.java deleted file mode 100644 index 78183ec2..00000000 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/RowEvolutionJsonNormaliserTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * See the NOTICE file distributed with this work for additional - * information regarding copyright ownership. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package com.xwiki.analytics.internal; - -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; - -import org.apache.ecs.storage.Hash; -import org.junit.jupiter.api.Test; -import org.xwiki.test.junit5.mockito.ComponentTest; -import org.xwiki.test.junit5.mockito.InjectMockComponents; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.gson.stream.JsonReader; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * Unit test for {@link RowEvolutionJsonNormaliserTest} - * - * @version $Id$ - */ -@ComponentTest -public class RowEvolutionJsonNormaliserTest -{ - @InjectMockComponents - private RowEvolutionJsonNormaliser rowEvolutionJsonNormaliser; - - private static JsonNode node; - - private void readJSONS(String path) throws IOException - { - ObjectMapper objectMapper = new ObjectMapper(); - InputStream is = JsonReader.class.getResourceAsStream(path); - node = objectMapper.readTree(is); - } - - @Test - public void normalizeDataWithoutFilters() throws IOException - { - readJSONS("/rowEvolution/normalizeDataWithoutFilters.json"); - assertEquals(node.get("response"), - rowEvolutionJsonNormaliser.normaliseData(node.get("initialJson").toString(), new HashMap<>())); - } - - @Test - public void normalizeDataWithNullFilters() throws IOException - { - readJSONS("/rowEvolution/normalizeDataWithoutFilters.json"); - assertEquals(node.get("response"), - rowEvolutionJsonNormaliser.normaliseData(node.get("initialJson").toString(), new HashMap<>())); - } - - @Test - public void normalizeDataWithFilters() throws IOException - { - readJSONS("/rowEvolution/normalizeDataWithFilters.json"); - HashMap filters = new HashMap<>(); - filters.put("label", "/MostViewedPage"); - filters.put("sum_time_spent", "96"); - filters.put("avg_time_on_page", "48"); - assertEquals(node.get("response"), - rowEvolutionJsonNormaliser.normaliseData(node.get("initialJson").toString(), filters)); - } -} diff --git a/application-analytics-test/application-analytics-test-docker/pom.xml b/application-analytics-test/application-analytics-test-docker/pom.xml index c9ad3d94..832748c1 100644 --- a/application-analytics-test/application-analytics-test-docker/pom.xml +++ b/application-analytics-test/application-analytics-test-docker/pom.xml @@ -44,6 +44,12 @@ ${licensing.version} runtime + + org.xwiki.platform + xwiki-platform-menu-ui + ${platform.version} + xar + org.xwiki.platform xwiki-platform-panels-ui @@ -51,6 +57,36 @@ xar + + org.xwiki.platform + xwiki-platform-test-docker + ${platform.version} + test + + + org.xwiki.platform + xwiki-platform-menu-test-pageobjects + ${platform.version} + test + + + org.xwiki.platform + xwiki-platform-panels-test-pageobjects + ${platform.version} + test + + + org.xwiki.platform + xwiki-platform-application-test-pageobjects + ${platform.version} + test + + + org.xwiki.platform + xwiki-platform-administration-test-pageobjects + ${platform.version} + test + com.xwiki.licensing application-licensing-test-dependencies diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AllITs.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AllITs.java index 7a43896c..eefd9d5b 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AllITs.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AllITs.java @@ -21,7 +21,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.xwiki.test.docker.junit5.UITest; - +import org.xwiki.test.docker.junit5.servletengine.ServletEngine; @UITest public class AllITs diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index 04056826..3c729b60 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -20,16 +20,29 @@ package xwiki.analytics.test.ui; import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.xwiki.application.test.po.ApplicationIndexHomePage; import org.xwiki.test.docker.junit5.UITest; +import org.xwiki.test.docker.junit5.servletengine.ServletEngine; +import org.xwiki.test.ui.TestUtils; +import org.xwiki.test.ui.XWikiWebDriver; +import org.xwiki.administration.test.po.AdministrationPage; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import org.xwiki.test.ui.po.ViewPage; @UITest public class AnalyticsIT { @Test - void appEntryRedirectsToHomePage() + void appEntryRedirectsToHomePage(XWikiWebDriver driver, TestUtils setup) { + + ApplicationIndexHomePage applicationIndexHomePage = ApplicationIndexHomePage.gotoPage(); + System.out.println(applicationIndexHomePage.getPageURL()); assertFalse(true); + + } } From ac3e15b5e21df8307edc0a6b023c2edacc5a0ad9 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 15 Sep 2023 12:39:20 +0300 Subject: [PATCH 078/105] page object --- .../pom.xml | 41 +++++++++++++++++++ application-analytics-test/pom.xml | 1 + 2 files changed, 42 insertions(+) create mode 100644 application-analytics-test/application-analytics-test-pageobjects/pom.xml diff --git a/application-analytics-test/application-analytics-test-pageobjects/pom.xml b/application-analytics-test/application-analytics-test-pageobjects/pom.xml new file mode 100644 index 00000000..d44c5f9b --- /dev/null +++ b/application-analytics-test/application-analytics-test-pageobjects/pom.xml @@ -0,0 +1,41 @@ + + + + + + 4.0.0 + + com.xwiki.analytics + application-analytics-test + 1.0-SNAPSHOT + + application-analytics-test-pageobjects + Application Analytics - Tests - Page Objects + jar + Application Analytics - Tests - Page Objects + + + org.xwiki.platform + xwiki-platform-test-ui + ${platform.version} + + + \ No newline at end of file diff --git a/application-analytics-test/pom.xml b/application-analytics-test/pom.xml index e4412343..9bf62cbc 100644 --- a/application-analytics-test/pom.xml +++ b/application-analytics-test/pom.xml @@ -42,6 +42,7 @@ application-analytics-test-docker + application-analytics-test-pageobjects From 3eb23a57b583b5fa6eb0d85ce4892c00bbeb753f Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 15 Sep 2023 14:29:23 +0300 Subject: [PATCH 079/105] test --- .../analytics/test/po/HomePageViewPage.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java new file mode 100644 index 00000000..ab134512 --- /dev/null +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java @@ -0,0 +1,19 @@ +package com.xwiki.analytics.test.po; + + +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.xwiki.test.ui.po.ViewPage; + +public class HomePageViewPage extends ViewPage +{ + public HomePageViewPage() + { + + } + public static ApplicationIndexHomePage gotoPage() + { + + } +} From 65722951f3da03477c2d733c438649af6cd1bbbc Mon Sep 17 00:00:00 2001 From: Farcasut Date: Sun, 17 Sep 2023 23:46:57 +0300 Subject: [PATCH 080/105] functional tests --- .../application-analytics-test-docker/pom.xml | 6 +++ .../xwiki/analytics/test/ui/AnalyticsIT.java | 54 +++++++++++++++++-- .../analytics/test/po/HomePageViewPage.java | 27 +++++++++- 3 files changed, 80 insertions(+), 7 deletions(-) diff --git a/application-analytics-test/application-analytics-test-docker/pom.xml b/application-analytics-test/application-analytics-test-docker/pom.xml index 832748c1..c3438ee0 100644 --- a/application-analytics-test/application-analytics-test-docker/pom.xml +++ b/application-analytics-test/application-analytics-test-docker/pom.xml @@ -101,6 +101,12 @@ xar runtime + + com.xwiki.analytics + application-analytics-test-pageobjects + 1.0-SNAPSHOT + test + src/test/it diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index 3c729b60..b5864cfb 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -19,9 +19,19 @@ */ package xwiki.analytics.test.ui; +import javax.inject.Inject; + +import org.hibernate.validator.constraints.ru.INN; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.openqa.selenium.By; +import org.slf4j.Logger; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.DockerImageName; import org.xwiki.application.test.po.ApplicationIndexHomePage; +import org.xwiki.test.docker.internal.junit5.DockerTestUtils; +import org.xwiki.test.docker.junit5.TestConfiguration; import org.xwiki.test.docker.junit5.UITest; import org.xwiki.test.docker.junit5.servletengine.ServletEngine; import org.xwiki.test.ui.TestUtils; @@ -30,19 +40,53 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; + import org.xwiki.test.ui.po.ViewPage; +import com.xwiki.analytics.test.po.HomePageViewPage; + +import java.util.Collections; +import java.util.List; + @UITest public class AnalyticsIT { - @Test - void appEntryRedirectsToHomePage(XWikiWebDriver driver, TestUtils setup) + @BeforeAll + void congig(TestConfiguration testConfiguration) { - ApplicationIndexHomePage applicationIndexHomePage = ApplicationIndexHomePage.gotoPage(); - System.out.println(applicationIndexHomePage.getPageURL()); - assertFalse(true); + GenericContainer mariadb = + new GenericContainer<>(DockerImageName.parse("mariadb")).withEnv("MYSQL_ROOT_PASSWORD", + "exampleRootPassword").withEnv("MYSQL_DATABASE", "matomo").withEnv("MYSQL_USER", "matomo") + .withEnv("MYSQL_PASSWORD", "examplePassword").withExposedPorts(3306) + .waitingFor(Wait.forListeningPort()); + // Define the Matomo container + GenericContainer matomo = + new GenericContainer<>(DockerImageName.parse("matomo")).withEnv("MATOMO_DATABASE_HOST", mariadb.getHost()) + .withEnv("MATOMO_DATABASE_USERNAME", "matomo").withEnv("MATOMO_DATABASE_PASSWORD", "examplePassword") + .withEnv("MATOMO_DATABASE_DBNAME", "matomo").withExposedPorts(80).dependsOn(mariadb) + .waitingFor(Wait.forHttp("/")); + +/* GenericContainer genericContainer = new GenericContainer("matomo"); + genericContainer.setPortBindings(Collections.singletonList("9999:80"));*/ + mariadb.setPortBindings(Collections.singletonList("9999:3306")); // Map host port 9999 to container port 3306 + matomo.setPortBindings(Collections.singletonList("9998:80")); // Map host port 9998 to container port 80 + try { + DockerTestUtils.startContainer(mariadb, testConfiguration); + DockerTestUtils.startContainer(matomo, testConfiguration); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + void appEntryRedirectsToHomePage(XWikiWebDriver driver, TestUtils setup) + { + HomePageViewPage.gotoPage(); + while (true) { + System.out.print("1"); + } } } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java index ab134512..7a08625b 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java @@ -1,6 +1,27 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ package com.xwiki.analytics.test.po; +import javax.swing.text.View; + import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; @@ -12,8 +33,10 @@ public HomePageViewPage() { } - public static ApplicationIndexHomePage gotoPage() + public static HomePageViewPage gotoPage() { - + getUtil().gotoPage("Analytics", "WebHome"); + return new HomePageViewPage(); } + } From fba25f868087a5c320e0877aeccac89280d35798 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Mon, 18 Sep 2023 14:47:06 +0300 Subject: [PATCH 081/105] Check the matomo credentials for the api --- .../src/main/resources/config.ini.php | 79 +++++++++++++++++++ .../xwiki/analytics/test/ui/AnalyticsIT.java | 65 +++++++-------- 2 files changed, 107 insertions(+), 37 deletions(-) create mode 100644 application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php diff --git a/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php b/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php new file mode 100644 index 00000000..a2f53e3c --- /dev/null +++ b/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php @@ -0,0 +1,79 @@ +; DO NOT REMOVE THIS LINE +; file automatically generated or modified by Matomo; you can manually override the default values in global.ini.php by redefining them in this file. +[database] +host = "172.17.0.1" +username = "matomo" +password = "secret" +dbname = "matomo" +tables_prefix = "matomo_" +port = 9034 +charset = "utf8mb4" + +[General] +salt = "a10241fb4ea24ba3a00a16ac9fbcac0e" +trusted_hosts[] = "localhost" + +[PluginsInstalled] +PluginsInstalled[] = "Diagnostics" +PluginsInstalled[] = "Login" +PluginsInstalled[] = "CoreAdminHome" +PluginsInstalled[] = "UsersManager" +PluginsInstalled[] = "SitesManager" +PluginsInstalled[] = "Installation" +PluginsInstalled[] = "Monolog" +PluginsInstalled[] = "Intl" +PluginsInstalled[] = "CoreVue" +PluginsInstalled[] = "CorePluginsAdmin" +PluginsInstalled[] = "CoreHome" +PluginsInstalled[] = "WebsiteMeasurable" +PluginsInstalled[] = "IntranetMeasurable" +PluginsInstalled[] = "CoreVisualizations" +PluginsInstalled[] = "Proxy" +PluginsInstalled[] = "API" +PluginsInstalled[] = "Widgetize" +PluginsInstalled[] = "Transitions" +PluginsInstalled[] = "LanguagesManager" +PluginsInstalled[] = "Actions" +PluginsInstalled[] = "Dashboard" +PluginsInstalled[] = "MultiSites" +PluginsInstalled[] = "Referrers" +PluginsInstalled[] = "UserLanguage" +PluginsInstalled[] = "DevicesDetection" +PluginsInstalled[] = "Goals" +PluginsInstalled[] = "Ecommerce" +PluginsInstalled[] = "SEO" +PluginsInstalled[] = "Events" +PluginsInstalled[] = "UserCountry" +PluginsInstalled[] = "GeoIp2" +PluginsInstalled[] = "VisitsSummary" +PluginsInstalled[] = "VisitFrequency" +PluginsInstalled[] = "VisitTime" +PluginsInstalled[] = "VisitorInterest" +PluginsInstalled[] = "RssWidget" +PluginsInstalled[] = "Feedback" +PluginsInstalled[] = "TwoFactorAuth" +PluginsInstalled[] = "CoreUpdater" +PluginsInstalled[] = "CoreConsole" +PluginsInstalled[] = "ScheduledReports" +PluginsInstalled[] = "UserCountryMap" +PluginsInstalled[] = "Live" +PluginsInstalled[] = "PrivacyManager" +PluginsInstalled[] = "ImageGraph" +PluginsInstalled[] = "Annotations" +PluginsInstalled[] = "MobileMessaging" +PluginsInstalled[] = "Overlay" +PluginsInstalled[] = "SegmentEditor" +PluginsInstalled[] = "Insights" +PluginsInstalled[] = "Morpheus" +PluginsInstalled[] = "Contents" +PluginsInstalled[] = "BulkTracking" +PluginsInstalled[] = "Resolution" +PluginsInstalled[] = "DevicePlugins" +PluginsInstalled[] = "Heartbeat" +PluginsInstalled[] = "Marketplace" +PluginsInstalled[] = "ProfessionalServices" +PluginsInstalled[] = "UserId" +PluginsInstalled[] = "CustomJsTracker" +PluginsInstalled[] = "Tour" +PluginsInstalled[] = "PagePerformance" +PluginsInstalled[] = "CustomDimensions" diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index b5864cfb..be3a8baa 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -19,35 +19,23 @@ */ package xwiki.analytics.test.ui; -import javax.inject.Inject; +import java.util.Collections; -import org.hibernate.validator.constraints.ru.INN; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.openqa.selenium.By; -import org.slf4j.Logger; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.firefox.FirefoxDriver; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.containers.MySQLContainer; import org.testcontainers.utility.DockerImageName; -import org.xwiki.application.test.po.ApplicationIndexHomePage; import org.xwiki.test.docker.internal.junit5.DockerTestUtils; import org.xwiki.test.docker.junit5.TestConfiguration; import org.xwiki.test.docker.junit5.UITest; -import org.xwiki.test.docker.junit5.servletengine.ServletEngine; import org.xwiki.test.ui.TestUtils; import org.xwiki.test.ui.XWikiWebDriver; -import org.xwiki.administration.test.po.AdministrationPage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; - -import org.xwiki.test.ui.po.ViewPage; import com.xwiki.analytics.test.po.HomePageViewPage; -import java.util.Collections; -import java.util.List; - @UITest public class AnalyticsIT { @@ -55,38 +43,41 @@ public class AnalyticsIT void congig(TestConfiguration testConfiguration) { - GenericContainer mariadb = - new GenericContainer<>(DockerImageName.parse("mariadb")).withEnv("MYSQL_ROOT_PASSWORD", - "exampleRootPassword").withEnv("MYSQL_DATABASE", "matomo").withEnv("MYSQL_USER", "matomo") - .withEnv("MYSQL_PASSWORD", "examplePassword").withExposedPorts(3306) - .waitingFor(Wait.forListeningPort()); - - // Define the Matomo container - GenericContainer matomo = - new GenericContainer<>(DockerImageName.parse("matomo")).withEnv("MATOMO_DATABASE_HOST", mariadb.getHost()) - .withEnv("MATOMO_DATABASE_USERNAME", "matomo").withEnv("MATOMO_DATABASE_PASSWORD", "examplePassword") - .withEnv("MATOMO_DATABASE_DBNAME", "matomo").withExposedPorts(80).dependsOn(mariadb) - .waitingFor(Wait.forHttp("/")); - -/* GenericContainer genericContainer = new GenericContainer("matomo"); - genericContainer.setPortBindings(Collections.singletonList("9999:80"));*/ - mariadb.setPortBindings(Collections.singletonList("9999:3306")); // Map host port 9999 to container port 3306 - matomo.setPortBindings(Collections.singletonList("9998:80")); // Map host port 9998 to container port 80 + MySQLContainer mysqlContainer = new MySQLContainer<>(DockerImageName.parse("mysql:5.7")) + .withDatabaseName("matomo") + .withUsername("matomo") + .withPassword("secret") + .withExposedPorts(3306); + mysqlContainer.setPortBindings(Collections.singletonList("9034:3306")); try { - DockerTestUtils.startContainer(mariadb, testConfiguration); - DockerTestUtils.startContainer(matomo, testConfiguration); + //172.17.0.1 + DockerTestUtils.startContainer(mysqlContainer, testConfiguration); + GenericContainer matomoContainer = new GenericContainer<>("matomo:latest") + .withExposedPorts(80) + .withEnv("MATOMO_DATABASE_HOST", + "172.17.0.1" + ":" + mysqlContainer.getMappedPort(3306)) + .withEnv("MATOMO_DATABASE_USERNAME", "matomo") + .withEnv("MATOMO_DATABASE_PASSWORD", "secret") + .withEnv("MATOMO_DATABASE_DBNAME", "matomo"); + //.withFileSystemBind("src/main/resources/config.ini.php","/var/www/html/config/config.ini.php"); + matomoContainer.setPortBindings( + // !After the container has been opened and the selenium register a user I have to overwrite the + // config.ini.php to make Matomo work + Collections.singletonList("9999:80")); // Map host port 9999 to container port 80 + DockerTestUtils.startContainer(matomoContainer, testConfiguration); } catch (Exception e) { throw new RuntimeException(e); } } @Test - void appEntryRedirectsToHomePage(XWikiWebDriver driver, TestUtils setup) + void appEntryRedirectsToHomePage(XWikiWebDriver driver, TestUtils setup) throws InterruptedException { HomePageViewPage.gotoPage(); while (true) { - System.out.print("1"); + Thread.sleep(10 * 1000); + System.out.println("Test"); } } } From 4e839e904db014a05309404fd669f193fc5c2635 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Wed, 20 Sep 2023 10:43:10 +0300 Subject: [PATCH 082/105] env for ExtraTools --- .../src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index be3a8baa..bd7acbcf 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -58,13 +58,18 @@ void congig(TestConfiguration testConfiguration) "172.17.0.1" + ":" + mysqlContainer.getMappedPort(3306)) .withEnv("MATOMO_DATABASE_USERNAME", "matomo") .withEnv("MATOMO_DATABASE_PASSWORD", "secret") - .withEnv("MATOMO_DATABASE_DBNAME", "matomo"); + .withEnv("MATOMO_DATABASE_DBNAME", "matomo") + .withEnv("MATOMO_FIRST_USER_NAME", "ADMIN1") + .withEnv("MATOMO_FIRST_USER_EMAIL", "ADMIN1@xwiki.com") + .withEnv("MATOMO_FIRST_USER_PASSWORD", "ADMIN1"); //.withFileSystemBind("src/main/resources/config.ini.php","/var/www/html/config/config.ini.php"); matomoContainer.setPortBindings( // !After the container has been opened and the selenium register a user I have to overwrite the // config.ini.php to make Matomo work Collections.singletonList("9999:80")); // Map host port 9999 to container port 80 DockerTestUtils.startContainer(matomoContainer, testConfiguration); + matomoContainer.execInContainer("apt-get -y update"); + matomoContainer.execInContainer("apt install git"); } catch (Exception e) { throw new RuntimeException(e); } From c2327226458cb8043cfc0d2c119618a24656b7eb Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 21 Sep 2023 16:55:36 +0300 Subject: [PATCH 083/105] test * - Added escapetool.xml to the translations --- .../src/main/resources/config.ini.php | 74 ++++++++++++++++++- .../xwiki/analytics/test/ui/AnalyticsIT.java | 7 +- 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php b/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php index a2f53e3c..92e419de 100644 --- a/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php +++ b/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php @@ -9,9 +9,72 @@ port = 9034 charset = "utf8mb4" -[General] -salt = "a10241fb4ea24ba3a00a16ac9fbcac0e" -trusted_hosts[] = "localhost" +[Plugins] +Plugins[] = "CoreVue" +Plugins[] = "CorePluginsAdmin" +Plugins[] = "CoreAdminHome" +Plugins[] = "CoreHome" +Plugins[] = "WebsiteMeasurable" +Plugins[] = "IntranetMeasurable" +Plugins[] = "Diagnostics" +Plugins[] = "CoreVisualizations" +Plugins[] = "Proxy" +Plugins[] = "API" +Plugins[] = "Widgetize" +Plugins[] = "Transitions" +Plugins[] = "LanguagesManager" +Plugins[] = "Actions" +Plugins[] = "Dashboard" +Plugins[] = "MultiSites" +Plugins[] = "Referrers" +Plugins[] = "UserLanguage" +Plugins[] = "DevicesDetection" +Plugins[] = "Goals" +Plugins[] = "Ecommerce" +Plugins[] = "SEO" +Plugins[] = "Events" +Plugins[] = "UserCountry" +Plugins[] = "GeoIp2" +Plugins[] = "VisitsSummary" +Plugins[] = "VisitFrequency" +Plugins[] = "VisitTime" +Plugins[] = "VisitorInterest" +Plugins[] = "RssWidget" +Plugins[] = "Feedback" +Plugins[] = "Monolog" +Plugins[] = "Login" +Plugins[] = "TwoFactorAuth" +Plugins[] = "UsersManager" +Plugins[] = "SitesManager" +Plugins[] = "Installation" +Plugins[] = "CoreUpdater" +Plugins[] = "CoreConsole" +Plugins[] = "ScheduledReports" +Plugins[] = "UserCountryMap" +Plugins[] = "Live" +Plugins[] = "PrivacyManager" +Plugins[] = "ImageGraph" +Plugins[] = "Annotations" +Plugins[] = "MobileMessaging" +Plugins[] = "Overlay" +Plugins[] = "SegmentEditor" +Plugins[] = "Insights" +Plugins[] = "Morpheus" +Plugins[] = "Contents" +Plugins[] = "BulkTracking" +Plugins[] = "Resolution" +Plugins[] = "DevicePlugins" +Plugins[] = "Heartbeat" +Plugins[] = "Intl" +Plugins[] = "Marketplace" +Plugins[] = "ProfessionalServices" +Plugins[] = "UserId" +Plugins[] = "CustomJsTracker" +Plugins[] = "Tour" +Plugins[] = "PagePerformance" +Plugins[] = "CustomDimensions" +Plugins[] = "ExtraTools" + [PluginsInstalled] PluginsInstalled[] = "Diagnostics" @@ -77,3 +140,8 @@ PluginsInstalled[] = "Tour" PluginsInstalled[] = "PagePerformance" PluginsInstalled[] = "CustomDimensions" +PluginsInstalled[] = "ExtraTools" + +[ExtraTools] +db_backup_path = "/var/www/html/tmp" +always_load_commands_from_plugin=ExtraTools diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index bd7acbcf..4c3e34cc 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -61,15 +61,14 @@ void congig(TestConfiguration testConfiguration) .withEnv("MATOMO_DATABASE_DBNAME", "matomo") .withEnv("MATOMO_FIRST_USER_NAME", "ADMIN1") .withEnv("MATOMO_FIRST_USER_EMAIL", "ADMIN1@xwiki.com") - .withEnv("MATOMO_FIRST_USER_PASSWORD", "ADMIN1"); - //.withFileSystemBind("src/main/resources/config.ini.php","/var/www/html/config/config.ini.php"); + .withEnv("MATOMO_FIRST_USER_PASSWORD", "ADMIN1") + .withFileSystemBind("src/main/resources/config.ini.php","/var/www/html/config/config.ini.php"); matomoContainer.setPortBindings( // !After the container has been opened and the selenium register a user I have to overwrite the // config.ini.php to make Matomo work Collections.singletonList("9999:80")); // Map host port 9999 to container port 80 DockerTestUtils.startContainer(matomoContainer, testConfiguration); - matomoContainer.execInContainer("apt-get -y update"); - matomoContainer.execInContainer("apt install git"); + matomoContainer.execInContainer("apt-get -y update", "apt-get -y install git"); } catch (Exception e) { throw new RuntimeException(e); } From ed9aeb7bf151849ddd58e30a8aad7ae6cafaba83 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Sat, 23 Sep 2023 23:30:12 +0300 Subject: [PATCH 084/105] Test container nou --- .../src/main/resources/config.ini.php | 75 +------------------ .../src/main/resources/config.json | 13 ++++ .../xwiki/analytics/test/ui/AnalyticsIT.java | 29 +++---- 3 files changed, 33 insertions(+), 84 deletions(-) create mode 100644 application-analytics-test/application-analytics-test-docker/src/main/resources/config.json diff --git a/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php b/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php index 92e419de..878c4fe3 100644 --- a/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php +++ b/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php @@ -9,72 +9,10 @@ port = 9034 charset = "utf8mb4" -[Plugins] -Plugins[] = "CoreVue" -Plugins[] = "CorePluginsAdmin" -Plugins[] = "CoreAdminHome" -Plugins[] = "CoreHome" -Plugins[] = "WebsiteMeasurable" -Plugins[] = "IntranetMeasurable" -Plugins[] = "Diagnostics" -Plugins[] = "CoreVisualizations" -Plugins[] = "Proxy" -Plugins[] = "API" -Plugins[] = "Widgetize" -Plugins[] = "Transitions" -Plugins[] = "LanguagesManager" -Plugins[] = "Actions" -Plugins[] = "Dashboard" -Plugins[] = "MultiSites" -Plugins[] = "Referrers" -Plugins[] = "UserLanguage" -Plugins[] = "DevicesDetection" -Plugins[] = "Goals" -Plugins[] = "Ecommerce" -Plugins[] = "SEO" -Plugins[] = "Events" -Plugins[] = "UserCountry" -Plugins[] = "GeoIp2" -Plugins[] = "VisitsSummary" -Plugins[] = "VisitFrequency" -Plugins[] = "VisitTime" -Plugins[] = "VisitorInterest" -Plugins[] = "RssWidget" -Plugins[] = "Feedback" -Plugins[] = "Monolog" -Plugins[] = "Login" -Plugins[] = "TwoFactorAuth" -Plugins[] = "UsersManager" -Plugins[] = "SitesManager" -Plugins[] = "Installation" -Plugins[] = "CoreUpdater" -Plugins[] = "CoreConsole" -Plugins[] = "ScheduledReports" -Plugins[] = "UserCountryMap" -Plugins[] = "Live" -Plugins[] = "PrivacyManager" -Plugins[] = "ImageGraph" -Plugins[] = "Annotations" -Plugins[] = "MobileMessaging" -Plugins[] = "Overlay" -Plugins[] = "SegmentEditor" -Plugins[] = "Insights" -Plugins[] = "Morpheus" -Plugins[] = "Contents" -Plugins[] = "BulkTracking" -Plugins[] = "Resolution" -Plugins[] = "DevicePlugins" -Plugins[] = "Heartbeat" -Plugins[] = "Intl" -Plugins[] = "Marketplace" -Plugins[] = "ProfessionalServices" -Plugins[] = "UserId" -Plugins[] = "CustomJsTracker" -Plugins[] = "Tour" -Plugins[] = "PagePerformance" -Plugins[] = "CustomDimensions" -Plugins[] = "ExtraTools" - +[General] +salt = "221e020a358ad259431e9ec76ba8a941" +trusted_hosts[] = "localhost" +trusted_hosts[] = "localhost:9999" [PluginsInstalled] PluginsInstalled[] = "Diagnostics" @@ -140,8 +78,3 @@ PluginsInstalled[] = "Tour" PluginsInstalled[] = "PagePerformance" PluginsInstalled[] = "CustomDimensions" -PluginsInstalled[] = "ExtraTools" - -[ExtraTools] -db_backup_path = "/var/www/html/tmp" -always_load_commands_from_plugin=ExtraTools diff --git a/application-analytics-test/application-analytics-test-docker/src/main/resources/config.json b/application-analytics-test/application-analytics-test-docker/src/main/resources/config.json new file mode 100644 index 00000000..1a4e78b5 --- /dev/null +++ b/application-analytics-test/application-analytics-test-docker/src/main/resources/config.json @@ -0,0 +1,13 @@ +{ + "db_container_name": "farcasut/custom-mysql:latest", + "db_container_exposed_port": 3306, + "db_bridge_port":9034, + "db_name": "matomo", + "db_username": "matomo", + "db_password": "secret", + "address": "172.17.0.1", + "matomo_config_file_path":"src/main/resources/config.ini.php", + "matomo_bridge_port": 9999, + "matomo_credential": "ADMIN1", + "matomo_auth_token": "91be1bca1315c35abd605ad8a544eece" +} \ No newline at end of file diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index 4c3e34cc..ccc88d37 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -19,6 +19,8 @@ */ package xwiki.analytics.test.ui; +import java.io.IOException; +import java.io.InputStream; import java.util.Collections; import org.junit.jupiter.api.BeforeAll; @@ -27,6 +29,7 @@ import org.openqa.selenium.firefox.FirefoxDriver; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.shaded.com.fasterxml.jackson.databind.JsonNode; import org.testcontainers.utility.DockerImageName; import org.xwiki.test.docker.internal.junit5.DockerTestUtils; import org.xwiki.test.docker.junit5.TestConfiguration; @@ -34,41 +37,41 @@ import org.xwiki.test.ui.TestUtils; import org.xwiki.test.ui.XWikiWebDriver; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.stream.JsonReader; import com.xwiki.analytics.test.po.HomePageViewPage; @UITest public class AnalyticsIT { + + @BeforeAll - void congig(TestConfiguration testConfiguration) + void config(TestConfiguration testConfiguration) { - MySQLContainer mysqlContainer = new MySQLContainer<>(DockerImageName.parse("mysql:5.7")) + // Since the MySQL container is derived from the official MySQL image I have to mark the image as compatible + // with MySQLContainers. + DockerImageName sqlContainer = DockerImageName.parse("farcasut/custom-mysql:latest").asCompatibleSubstituteFor("mysql"); + // I create a new db and a new user. + MySQLContainer mysqlContainer = new MySQLContainer<>(sqlContainer) .withDatabaseName("matomo") .withUsername("matomo") .withPassword("secret") .withExposedPorts(3306); mysqlContainer.setPortBindings(Collections.singletonList("9034:3306")); try { - //172.17.0.1 + // These are some credentials all of them will be moved to a separate file to make it easier to handle. + //172.17.0.1 ADMIN1 91be1bca1315c35abd605ad8a544eece DockerTestUtils.startContainer(mysqlContainer, testConfiguration); GenericContainer matomoContainer = new GenericContainer<>("matomo:latest") .withExposedPorts(80) .withEnv("MATOMO_DATABASE_HOST", "172.17.0.1" + ":" + mysqlContainer.getMappedPort(3306)) - .withEnv("MATOMO_DATABASE_USERNAME", "matomo") - .withEnv("MATOMO_DATABASE_PASSWORD", "secret") - .withEnv("MATOMO_DATABASE_DBNAME", "matomo") - .withEnv("MATOMO_FIRST_USER_NAME", "ADMIN1") - .withEnv("MATOMO_FIRST_USER_EMAIL", "ADMIN1@xwiki.com") - .withEnv("MATOMO_FIRST_USER_PASSWORD", "ADMIN1") .withFileSystemBind("src/main/resources/config.ini.php","/var/www/html/config/config.ini.php"); matomoContainer.setPortBindings( - // !After the container has been opened and the selenium register a user I have to overwrite the - // config.ini.php to make Matomo work - Collections.singletonList("9999:80")); // Map host port 9999 to container port 80 + Collections.singletonList("9999:80")); // Map host port 9999 to be able to access the matomo instance DockerTestUtils.startContainer(matomoContainer, testConfiguration); - matomoContainer.execInContainer("apt-get -y update", "apt-get -y install git"); } catch (Exception e) { throw new RuntimeException(e); } From 252738b65f1d6d30fc49c0c78d37e1d5d9f0baa1 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Sun, 24 Sep 2023 02:29:49 +0300 Subject: [PATCH 085/105] dashboard dep --- .../src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java | 3 +++ application-analytics-ui/pom.xml | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index ccc88d37..0d318881 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -41,6 +41,8 @@ import com.google.gson.stream.JsonReader; import com.xwiki.analytics.test.po.HomePageViewPage; +import static org.junit.jupiter.api.Assertions.fail; + @UITest public class AnalyticsIT { @@ -82,6 +84,7 @@ void appEntryRedirectsToHomePage(XWikiWebDriver driver, TestUtils setup) throws { HomePageViewPage.gotoPage(); + fail(); while (true) { Thread.sleep(10 * 1000); System.out.println("Test"); diff --git a/application-analytics-ui/pom.xml b/application-analytics-ui/pom.xml index b2612bd3..d31a0b5a 100644 --- a/application-analytics-ui/pom.xml +++ b/application-analytics-ui/pom.xml @@ -69,6 +69,13 @@ application-analytics-default ${project.version} + + org.xwiki.platform + xwiki-platform-dashboard-ui + ${platform.version} + xar + + org.xwiki.contrib application-chartjs-webjar From b42d4c7d76bc658c6ec3bb8bda8450d3cbee5c95 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Mon, 25 Sep 2023 23:25:04 +0300 Subject: [PATCH 086/105] test --- .../xwiki/analytics/test/ui/AnalyticsIT.java | 13 +++-- .../analytics/test/po/HomePageViewPage.java | 50 ++++++++++++++++++- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index 0d318881..aa77614a 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -24,6 +24,7 @@ import java.util.Collections; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; @@ -51,7 +52,6 @@ public class AnalyticsIT @BeforeAll void config(TestConfiguration testConfiguration) { - // Since the MySQL container is derived from the official MySQL image I have to mark the image as compatible // with MySQLContainers. DockerImageName sqlContainer = DockerImageName.parse("farcasut/custom-mysql:latest").asCompatibleSubstituteFor("mysql"); @@ -78,16 +78,21 @@ void config(TestConfiguration testConfiguration) throw new RuntimeException(e); } } - + @BeforeEach + void login(TestUtils setup) + { + setup.getURLToLoginAsAdmin(); + } @Test void appEntryRedirectsToHomePage(XWikiWebDriver driver, TestUtils setup) throws InterruptedException { - HomePageViewPage.gotoPage(); - fail(); + HomePageViewPage.gotoAndEdit().addNewMacro(driver, "searchCategories"); while (true) { Thread.sleep(10 * 1000); System.out.println("Test"); + fail(); + } } } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java index 7a08625b..09b20535 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java @@ -20,11 +20,21 @@ package com.xwiki.analytics.test.po; +import java.sql.SQLOutput; +import java.util.HashMap; +import java.util.Map; + import javax.swing.text.View; import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.test.ui.XWikiWebDriver; import org.xwiki.test.ui.po.ViewPage; public class HomePageViewPage extends ViewPage @@ -33,10 +43,48 @@ public HomePageViewPage() { } - public static HomePageViewPage gotoPage() + public static HomePageViewPage gotoPageHomePage() { getUtil().gotoPage("Analytics", "WebHome"); return new HomePageViewPage(); } + public static HomePageViewPage gotoAndEdit() + { + DocumentReference documentReference = new DocumentReference("xwiki", "Analytics", "WebHome"); + Map params = new HashMap<>(); + params.put("force","1"); + getUtil().gotoPage(documentReference, "edit", params); + return new HomePageViewPage(); + } + private static HomePageViewPage clickAddGadget(XWikiWebDriver driver) + { + driver.findElement(By.cssSelector(".addgadget")).click(); + + return new HomePageViewPage(); + } + + private static void selectMacro(XWikiWebDriver driver, String macroID) { + + WebElement element = driver.findElement(By.cssSelector(String.format("li[data-macroid=\"%s\"]", macroID))); + + // Double-click using JavaScript + JavascriptExecutor executor = (JavascriptExecutor) driver; + executor.executeScript("var event = new MouseEvent('dblclick', { 'bubbles': true }); arguments[0].dispatchEvent(event);", element); + + // Wait for the button to become present in the DOM + WebDriverWait wait = new WebDriverWait(driver, 10); // waits up to 10 seconds + WebElement button = wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(".modal.macro-editor-modal.in.gadget-editor-modal .modal-footer .btn-primary'"))); + + button.click(); + } + + + + + public static HomePageViewPage addNewMacro(XWikiWebDriver driver, String macroID) + { + clickAddGadget(driver).selectMacro(driver, macroID); + return new HomePageViewPage(); + } } From 0f6f3d50bb5145be12ca27f22f12fdbfbea97bf4 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Tue, 26 Sep 2023 12:41:56 +0300 Subject: [PATCH 087/105] Functional tests --- .../xwiki/analytics/test/ui/AnalyticsIT.java | 2 +- .../analytics/test/po/HomePageViewPage.java | 23 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index aa77614a..037ceeaa 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -87,7 +87,7 @@ void login(TestUtils setup) void appEntryRedirectsToHomePage(XWikiWebDriver driver, TestUtils setup) throws InterruptedException { - HomePageViewPage.gotoAndEdit().addNewMacro(driver, "searchCategories"); + HomePageViewPage.gotoAndEdit().addNewMacro(driver, "searchCategories", "Search Categories"); while (true) { Thread.sleep(10 * 1000); System.out.println("Test"); diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java index 09b20535..0fc12852 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.Map; +import javax.swing.*; import javax.swing.text.View; import org.openqa.selenium.By; @@ -63,27 +64,27 @@ private static HomePageViewPage clickAddGadget(XWikiWebDriver driver) return new HomePageViewPage(); } - private static void selectMacro(XWikiWebDriver driver, String macroID) { + private static void selectMacro(XWikiWebDriver driver, String macroID, String macroName) { + // Will bring into view the macro that I want to use + driver.findElement(By.cssSelector(".macro-textFilter")).sendKeys(macroName); WebElement element = driver.findElement(By.cssSelector(String.format("li[data-macroid=\"%s\"]", macroID))); - - // Double-click using JavaScript - JavascriptExecutor executor = (JavascriptExecutor) driver; - executor.executeScript("var event = new MouseEvent('dblclick', { 'bubbles': true }); arguments[0].dispatchEvent(event);", element); - + System.out.println(element); + Actions actions = new Actions(driver); + actions.doubleClick(element).perform(); // Wait for the button to become present in the DOM - WebDriverWait wait = new WebDriverWait(driver, 10); // waits up to 10 seconds - WebElement button = wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(".modal.macro-editor-modal.in.gadget-editor-modal .modal-footer .btn-primary'"))); - + WebElement button = driver.findElement(By.cssSelector(".modal.macro-editor-modal.in.gadget-editor-modal " + + ".modal-footer .btn-primary'")); + driver.waitUntilElementIsEnabled(button); button.click(); } - public static HomePageViewPage addNewMacro(XWikiWebDriver driver, String macroID) + public static HomePageViewPage addNewMacro(XWikiWebDriver driver, String macroID, String macroName) { - clickAddGadget(driver).selectMacro(driver, macroID); + clickAddGadget(driver).selectMacro(driver, macroID, macroName); return new HomePageViewPage(); } From 2e6319317224a4f2d5e8b1b73eddf8a8e1098976 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Sun, 1 Oct 2023 20:03:07 +0300 Subject: [PATCH 088/105] Draft Functional Tests #38 * - First functional test(Checks the ability of an admin user to add and remove a macro from the main page) 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 --- .../src/main/resources/config.json | 13 -- .../xwiki/analytics/test/ui/AnalyticsIT.java | 111 ++++++++++-------- .../analytics/test/ui/config/Config.java | 46 ++++++++ .../analytics/test/po/HomePageViewPage.java | 72 +++++++----- 4 files changed, 153 insertions(+), 89 deletions(-) delete mode 100644 application-analytics-test/application-analytics-test-docker/src/main/resources/config.json create mode 100644 application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java diff --git a/application-analytics-test/application-analytics-test-docker/src/main/resources/config.json b/application-analytics-test/application-analytics-test-docker/src/main/resources/config.json deleted file mode 100644 index 1a4e78b5..00000000 --- a/application-analytics-test/application-analytics-test-docker/src/main/resources/config.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "db_container_name": "farcasut/custom-mysql:latest", - "db_container_exposed_port": 3306, - "db_bridge_port":9034, - "db_name": "matomo", - "db_username": "matomo", - "db_password": "secret", - "address": "172.17.0.1", - "matomo_config_file_path":"src/main/resources/config.ini.php", - "matomo_bridge_port": 9999, - "matomo_credential": "ADMIN1", - "matomo_auth_token": "91be1bca1315c35abd605ad8a544eece" -} \ No newline at end of file diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index 037ceeaa..f600f76e 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -19,18 +19,12 @@ */ package xwiki.analytics.test.ui; -import java.io.IOException; -import java.io.InputStream; import java.util.Collections; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.firefox.FirefoxDriver; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.MySQLContainer; -import org.testcontainers.shaded.com.fasterxml.jackson.databind.JsonNode; import org.testcontainers.utility.DockerImageName; import org.xwiki.test.docker.internal.junit5.DockerTestUtils; import org.xwiki.test.docker.junit5.TestConfiguration; @@ -38,61 +32,84 @@ import org.xwiki.test.ui.TestUtils; import org.xwiki.test.ui.XWikiWebDriver; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.gson.stream.JsonReader; import com.xwiki.analytics.test.po.HomePageViewPage; -import static org.junit.jupiter.api.Assertions.fail; +import xwiki.analytics.test.ui.config.Config; + +import static org.junit.jupiter.api.Assertions.assertEquals; @UITest public class AnalyticsIT { + @BeforeAll + void setup(TestConfiguration testConfiguration, TestUtils testUtils) throws Exception + { + GenericContainer sqlContainer = startDb(testConfiguration); + GenericContainer matomoContainer = startMatomo(testConfiguration, sqlContainer); + testUtils.getURLToLoginAsAdmin(); + } + /** + * Checks if an admin can add/remove macros to the main dashboard. + * + * @param driver + */ + @Test + void appEntryRedirectsToHomePage(XWikiWebDriver driver) throws InterruptedException + { + // Add a gadget to the dashboard. + HomePageViewPage.gotoAndEdit().addNewMacro(driver, "searchCategories", "Search Categories") + .saveDashboard(driver); + assertEquals(HomePageViewPage.noOfGadgets(driver), 3); + // Remove a gadget from the dashboard. + HomePageViewPage.gotoAndEdit().removeLastMacro(driver).saveDashboard(driver); + assertEquals(HomePageViewPage.noOfGadgets(driver), 2); + while (true) { + Thread.sleep(10000); + System.out.println("test"); + } + } - @BeforeAll - void config(TestConfiguration testConfiguration) + /** + * Create and start a container with the database. + * + * @param testConfiguration configuration for the test container + * @return reference to the container + */ + private MySQLContainer startDb(TestConfiguration testConfiguration) throws Exception { // Since the MySQL container is derived from the official MySQL image I have to mark the image as compatible // with MySQLContainers. - DockerImageName sqlContainer = DockerImageName.parse("farcasut/custom-mysql:latest").asCompatibleSubstituteFor("mysql"); - // I create a new db and a new user. + DockerImageName sqlContainer = + DockerImageName.parse(Config.DB_CONTAINER_NAME).asCompatibleSubstituteFor("mysql"); MySQLContainer mysqlContainer = new MySQLContainer<>(sqlContainer) - .withDatabaseName("matomo") - .withUsername("matomo") - .withPassword("secret") + .withDatabaseName(Config.DB_NAME) + .withUsername(Config.DB_USERNAME) + .withPassword(Config.DB_PASSWORD) .withExposedPorts(3306); - mysqlContainer.setPortBindings(Collections.singletonList("9034:3306")); - try { - // These are some credentials all of them will be moved to a separate file to make it easier to handle. - //172.17.0.1 ADMIN1 91be1bca1315c35abd605ad8a544eece - DockerTestUtils.startContainer(mysqlContainer, testConfiguration); - GenericContainer matomoContainer = new GenericContainer<>("matomo:latest") - .withExposedPorts(80) - .withEnv("MATOMO_DATABASE_HOST", - "172.17.0.1" + ":" + mysqlContainer.getMappedPort(3306)) - .withFileSystemBind("src/main/resources/config.ini.php","/var/www/html/config/config.ini.php"); - matomoContainer.setPortBindings( - Collections.singletonList("9999:80")); // Map host port 9999 to be able to access the matomo instance - DockerTestUtils.startContainer(matomoContainer, testConfiguration); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - @BeforeEach - void login(TestUtils setup) - { - setup.getURLToLoginAsAdmin(); + mysqlContainer.setPortBindings(Collections.singletonList(String.format("%d:%d", Config.DB_BRIDGE_PORT, + Config.DB_CONTAINER_EXPOSED_PORT))); + DockerTestUtils.startContainer(mysqlContainer, testConfiguration); + return mysqlContainer; } - @Test - void appEntryRedirectsToHomePage(XWikiWebDriver driver, TestUtils setup) throws InterruptedException - { - HomePageViewPage.gotoAndEdit().addNewMacro(driver, "searchCategories", "Search Categories"); - while (true) { - Thread.sleep(10 * 1000); - System.out.println("Test"); - fail(); - - } + /** + * Creates&starts the Matomo container. + * + * @param testConfiguration test configuration + * @param dbContainer reference to the db container + * @return reference to the container + */ + private GenericContainer startMatomo(TestConfiguration testConfiguration, GenericContainer dbContainer) + throws Exception + { + GenericContainer matomoContainer = new GenericContainer<>(Config.MATOMO_CONTAINER_NAME) + .withExposedPorts(80) + .withEnv("MATOMO_DATABASE_HOST", + Config.ADDRESS + ":" + dbContainer.getMappedPort(Config.DB_CONTAINER_EXPOSED_PORT)) + .withFileSystemBind("src/main/resources/config.ini.php", Config.MATOMO_CONFIG_FILE_PATH); + matomoContainer.setPortBindings(Collections.singletonList(String.format("%d:80", Config.MATOMO_BRIDGE_PORT))); + DockerTestUtils.startContainer(matomoContainer, testConfiguration); + return matomoContainer; } } diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java new file mode 100644 index 00000000..00c86315 --- /dev/null +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java @@ -0,0 +1,46 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package xwiki.analytics.test.ui.config; + +public class Config +{ + public static final String DB_CONTAINER_NAME = "farcasut/custom-mysql:latest"; + + public static final int DB_CONTAINER_EXPOSED_PORT = 3306; + + public static final int DB_BRIDGE_PORT = 9034; + + public static final String DB_NAME = "matomo"; + + public static final String DB_USERNAME = "matomo"; + + public static final String DB_PASSWORD = "secret"; + + public static final String ADDRESS = "172.17.0.1"; + public static final String MATOMO_CONTAINER_NAME = "matomo:latest"; + + public static final String MATOMO_CONFIG_FILE_PATH = "/var/www/html/config/config.ini.php"; + + public static final int MATOMO_BRIDGE_PORT = 9999; + + public static final String MATOMO_CREDENTIALS = "ADMIN1"; + + public static final String MATOMO_AUTH_TOKEN = "91be1bca1315c35abd605ad8a544eece"; +} diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java index 0fc12852..aac29fd7 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java @@ -19,23 +19,15 @@ */ package com.xwiki.analytics.test.po; - -import java.sql.SQLOutput; import java.util.HashMap; import java.util.Map; -import javax.swing.*; -import javax.swing.text.View; - import org.openqa.selenium.By; -import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebElement; -import org.openqa.selenium.interactions.Actions; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.ui.ExpectedConditions; -import org.openqa.selenium.support.ui.WebDriverWait; import org.xwiki.model.reference.DocumentReference; import org.xwiki.test.ui.XWikiWebDriver; +import org.openqa.selenium.interactions.Actions; + import org.xwiki.test.ui.po.ViewPage; public class HomePageViewPage extends ViewPage @@ -44,19 +36,28 @@ public HomePageViewPage() { } + public static HomePageViewPage gotoPageHomePage() { getUtil().gotoPage("Analytics", "WebHome"); return new HomePageViewPage(); } + public static HomePageViewPage gotoAndEdit() { DocumentReference documentReference = new DocumentReference("xwiki", "Analytics", "WebHome"); - Map params = new HashMap<>(); - params.put("force","1"); + Map params = new HashMap<>(); + params.put("force", "1"); getUtil().gotoPage(documentReference, "edit", params); return new HomePageViewPage(); } + + public static HomePageViewPage addNewMacro(XWikiWebDriver driver, String macroID, String macroName) + { + clickAddGadget(driver).selectMacro(driver, macroID, macroName); + return new HomePageViewPage(); + } + private static HomePageViewPage clickAddGadget(XWikiWebDriver driver) { driver.findElement(By.cssSelector(".addgadget")).click(); @@ -64,28 +65,41 @@ private static HomePageViewPage clickAddGadget(XWikiWebDriver driver) return new HomePageViewPage(); } - private static void selectMacro(XWikiWebDriver driver, String macroID, String macroName) { - - // Will bring into view the macro that I want to use + private static void waitAndClick(XWikiWebDriver driver, String css) + { + driver.waitUntilElementIsEnabled(driver.findElement(By.cssSelector(css))); + driver.findElement(By.cssSelector(css)).click(); + } + private static void selectMacro(XWikiWebDriver driver, String macroID, String macroName) + { + // Will bring into view the macro that I want to use. driver.findElement(By.cssSelector(".macro-textFilter")).sendKeys(macroName); - WebElement element = driver.findElement(By.cssSelector(String.format("li[data-macroid=\"%s\"]", macroID))); - System.out.println(element); - Actions actions = new Actions(driver); - actions.doubleClick(element).perform(); - // Wait for the button to become present in the DOM - WebElement button = driver.findElement(By.cssSelector(".modal.macro-editor-modal.in.gadget-editor-modal " - + ".modal-footer .btn-primary'")); - driver.waitUntilElementIsEnabled(button); - button.click(); + waitAndClick(driver, String.format("li[data-macroid=\"%s\"]", macroID)); + // Will wait until the select button becomes enabled and will click on it. + waitAndClick(driver, ".modal.macro-selector-modal.gadget-selector-modal.in .modal-footer .btn-primary"); + // Will wait until the submit button becomes enabled and will click on it. + waitAndClick(driver,".modal.macro-editor-modal.in .modal-footer .btn-primary"); } - - - - public static HomePageViewPage addNewMacro(XWikiWebDriver driver, String macroID, String macroName) + public static int noOfGadgets(XWikiWebDriver driver) { - clickAddGadget(driver).selectMacro(driver, macroID, macroName); + return driver.findElements(By.className("gadget")).size(); + } + public static HomePageViewPage saveDashboard(XWikiWebDriver driver) + { + waitAndClick(driver, ".bottombuttons.sticky-buttons .btn-primary"); return new HomePageViewPage(); } + public static HomePageViewPage removeLastMacro(XWikiWebDriver driver) + { + WebElement lastGadget = driver.findElement(By.cssSelector(".gadget:last-of-type")); + // Move the cursor to be on top of the macro to reveal the remove button. + new Actions(driver.getWrappedDriver()).moveToElement(lastGadget).perform(); + // Click on the remove button when it becomes available. + waitAndClick(driver ,".gadget:last-of-type .remove"); + // Click on the confirm button to remove the macro. + waitAndClick(driver, ".xdialog-box.xdialog-box-confirmation .xdialog-content .buttonwrapper"); + return new HomePageViewPage(); + } } From 932f687f5f2f02cb57721b420d7c6ee7649e8f52 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Mon, 2 Oct 2023 11:19:42 +0300 Subject: [PATCH 089/105] Admin PageView --- .../application-analytics-test-docker/pom.xml | 11 +++++-- .../pom.xml | 8 ++++- .../analytics/test/po/AdminViewPage.java | 32 +++++++++++++++++++ .../analytics/test/po/HomePageViewPage.java | 1 - 4 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java diff --git a/application-analytics-test/application-analytics-test-docker/pom.xml b/application-analytics-test/application-analytics-test-docker/pom.xml index c3438ee0..d44ba1e6 100644 --- a/application-analytics-test/application-analytics-test-docker/pom.xml +++ b/application-analytics-test/application-analytics-test-docker/pom.xml @@ -44,12 +44,12 @@ ${licensing.version} runtime - + org.xwiki.platform xwiki-platform-menu-ui ${platform.version} xar - + org.xwiki.platform xwiki-platform-panels-ui @@ -57,7 +57,7 @@ xar - + org.xwiki.platform xwiki-platform-test-docker ${platform.version} @@ -101,6 +101,11 @@ xar runtime + + org.xwiki.platform + xwiki-platform-localization-script + ${platform.version} + com.xwiki.analytics application-analytics-test-pageobjects diff --git a/application-analytics-test/application-analytics-test-pageobjects/pom.xml b/application-analytics-test/application-analytics-test-pageobjects/pom.xml index d44c5f9b..782102a1 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/pom.xml +++ b/application-analytics-test/application-analytics-test-pageobjects/pom.xml @@ -20,7 +20,8 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - + 4.0.0 com.xwiki.analytics @@ -37,5 +38,10 @@ xwiki-platform-test-ui ${platform.version} + + org.xwiki.platform + xwiki-platform-administration-test-pageobjects + ${platform.version} + \ No newline at end of file diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java new file mode 100644 index 00000000..c2342f6a --- /dev/null +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java @@ -0,0 +1,32 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.analytics.test.po; + +import org.xwiki.administration.test.po.AdministrablePage; +import org.xwiki.administration.test.po.AdministrationPage; + +public class AdminViewPage +{ + public void gotoAdminPage() + { + AdministrationPage administrationPage = AdministrationPage.gotoPage(); + administrationPage.clickSection("Other", "analytics.application"); + } +} diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java index aac29fd7..9f72d5f3 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java @@ -61,7 +61,6 @@ public static HomePageViewPage addNewMacro(XWikiWebDriver driver, String macroID private static HomePageViewPage clickAddGadget(XWikiWebDriver driver) { driver.findElement(By.cssSelector(".addgadget")).click(); - return new HomePageViewPage(); } From 83b4a48ba5fa71a5cd50bfbc53d06455e8eed1ed Mon Sep 17 00:00:00 2001 From: Farcasut Date: Mon, 2 Oct 2023 18:37:08 +0300 Subject: [PATCH 090/105] * Added 2 new tests one that checks wrong configs and one checks valid configs. --- .../application-analytics-test-docker/pom.xml | 18 ++++ .../src/main/resources/config.ini.php | 1 + .../xwiki/analytics/test/ui/AnalyticsIT.java | 84 +++++++++++++++---- .../analytics/test/po/AdminViewPage.java | 70 +++++++++++++++- 4 files changed, 156 insertions(+), 17 deletions(-) diff --git a/application-analytics-test/application-analytics-test-docker/pom.xml b/application-analytics-test/application-analytics-test-docker/pom.xml index d44ba1e6..e429fdb3 100644 --- a/application-analytics-test/application-analytics-test-docker/pom.xml +++ b/application-analytics-test/application-analytics-test-docker/pom.xml @@ -94,6 +94,18 @@ pom test + + org.xwiki.platform + xwiki-platform-localization-webjar + ${platform.version} + runtime + + + org.xwiki.platform + xwiki-platform-localization-rest-default + ${platform.version} + runtime + com.xwiki.analytics application-analytics-ui @@ -101,6 +113,12 @@ xar runtime + + org.xwiki.platform + xwiki-platform-uiextension-ui + ${platform.version} + xar + org.xwiki.platform xwiki-platform-localization-script diff --git a/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php b/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php index 878c4fe3..6980a791 100644 --- a/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php +++ b/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php @@ -13,6 +13,7 @@ salt = "221e020a358ad259431e9ec76ba8a941" trusted_hosts[] = "localhost" trusted_hosts[] = "localhost:9999" +debug = 1 [PluginsInstalled] PluginsInstalled[] = "Diagnostics" diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index f600f76e..d58076e1 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -22,7 +22,9 @@ import java.util.Collections; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.utility.DockerImageName; @@ -32,11 +34,13 @@ import org.xwiki.test.ui.TestUtils; import org.xwiki.test.ui.XWikiWebDriver; +import com.xwiki.analytics.test.po.AdminViewPage; import com.xwiki.analytics.test.po.HomePageViewPage; import xwiki.analytics.test.ui.config.Config; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; @UITest public class AnalyticsIT @@ -46,7 +50,65 @@ void setup(TestConfiguration testConfiguration, TestUtils testUtils) throws Exce { GenericContainer sqlContainer = startDb(testConfiguration); GenericContainer matomoContainer = startMatomo(testConfiguration, sqlContainer); - testUtils.getURLToLoginAsAdmin(); + testUtils.loginAsSuperAdmin(); + testUtils.setGlobalRights("XWiki.XWikiAdminGroup", "", "admin", true); + testUtils.createAdminUser(); + testUtils.loginAsAdmin(); + } + + /** + * The function checks that the "Save" button displays the correct messages when the configurations provided by the + * users are incorrect. + * + * @param driver + */ + //@Test + // @Order(1) + void checkWrongConfigs(XWikiWebDriver driver) throws InterruptedException + { + AdminViewPage.gotoAdminPage().setTrackingCode(driver, "").setAuthTokenId(driver, Config.MATOMO_AUTH_TOKEN) + .setIdSiteId(driver, "1").setRequestAddressId(driver, Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT) + .bringSaveButtonIntoView(driver); + assertEquals(AdminViewPage.inProgressNotification(driver), "Saving..."); + assertEquals(AdminViewPage.successNotification(driver), "Saved"); + assertEquals(AdminViewPage.inProgressNotification(driver), "Checking connection to Matomo."); + assertEquals(AdminViewPage.errorNotification(driver), + "Failed to connect to Matomo. Please check your configuration values."); + } + + @Test + @Order(1) + void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException + { + AdminViewPage.gotoAdminPage().setTrackingCode(driver, "\n" + + "\n" + + "\n").setAuthTokenId(driver, Config.MATOMO_AUTH_TOKEN) + .setIdSiteId(driver, "1").setRequestAddressId(driver, + "http://"+Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT + "/matomo/") + .bringSaveButtonIntoView(driver); + assertEquals(AdminViewPage.inProgressNotification(driver), "Saving..."); + assertEquals(AdminViewPage.successNotification(driver), "Saved"); + assertEquals(AdminViewPage.inProgressNotification(driver), "Checking connection to Matomo."); + driver.waitUntilElementDisappears(By.cssSelector(".xnotification.xnotification-inprogress")); + // assertEquals(AdminViewPage.successNotification(driver), + // "Test connection succeeded!"); + while (true) + { + Thread.sleep(10000); + System.out.printf("test"); + } } /** @@ -55,6 +117,7 @@ void setup(TestConfiguration testConfiguration, TestUtils testUtils) throws Exce * @param driver */ @Test + @Order(2) void appEntryRedirectsToHomePage(XWikiWebDriver driver) throws InterruptedException { // Add a gadget to the dashboard. @@ -64,10 +127,6 @@ void appEntryRedirectsToHomePage(XWikiWebDriver driver) throws InterruptedExcept // Remove a gadget from the dashboard. HomePageViewPage.gotoAndEdit().removeLastMacro(driver).saveDashboard(driver); assertEquals(HomePageViewPage.noOfGadgets(driver), 2); - while (true) { - Thread.sleep(10000); - System.out.println("test"); - } } /** @@ -82,13 +141,11 @@ private MySQLContainer startDb(TestConfiguration testConfiguration) throws Excep // with MySQLContainers. DockerImageName sqlContainer = DockerImageName.parse(Config.DB_CONTAINER_NAME).asCompatibleSubstituteFor("mysql"); - MySQLContainer mysqlContainer = new MySQLContainer<>(sqlContainer) - .withDatabaseName(Config.DB_NAME) - .withUsername(Config.DB_USERNAME) - .withPassword(Config.DB_PASSWORD) - .withExposedPorts(3306); - mysqlContainer.setPortBindings(Collections.singletonList(String.format("%d:%d", Config.DB_BRIDGE_PORT, - Config.DB_CONTAINER_EXPOSED_PORT))); + MySQLContainer mysqlContainer = + new MySQLContainer<>(sqlContainer).withDatabaseName(Config.DB_NAME).withUsername(Config.DB_USERNAME) + .withPassword(Config.DB_PASSWORD).withExposedPorts(3306); + mysqlContainer.setPortBindings( + Collections.singletonList(String.format("%d:%d", Config.DB_BRIDGE_PORT, Config.DB_CONTAINER_EXPOSED_PORT))); DockerTestUtils.startContainer(mysqlContainer, testConfiguration); return mysqlContainer; } @@ -103,8 +160,7 @@ private MySQLContainer startDb(TestConfiguration testConfiguration) throws Excep private GenericContainer startMatomo(TestConfiguration testConfiguration, GenericContainer dbContainer) throws Exception { - GenericContainer matomoContainer = new GenericContainer<>(Config.MATOMO_CONTAINER_NAME) - .withExposedPorts(80) + GenericContainer matomoContainer = new GenericContainer<>(Config.MATOMO_CONTAINER_NAME).withExposedPorts(80) .withEnv("MATOMO_DATABASE_HOST", Config.ADDRESS + ":" + dbContainer.getMappedPort(Config.DB_CONTAINER_EXPOSED_PORT)) .withFileSystemBind("src/main/resources/config.ini.php", Config.MATOMO_CONFIG_FILE_PATH); diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java index c2342f6a..b92d927e 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java @@ -19,14 +19,78 @@ */ package com.xwiki.analytics.test.po; -import org.xwiki.administration.test.po.AdministrablePage; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; import org.xwiki.administration.test.po.AdministrationPage; +import org.xwiki.test.ui.XWikiWebDriver; public class AdminViewPage { - public void gotoAdminPage() + public AdminViewPage() + { + + } + + private static final String trackingCodeId = "Analytics.Code.ConfigurationClass_0_trackingCode"; + + private static final String authTokenId = "Analytics.Code.ConfigurationClass_0_authToken"; + + private static final String idSiteId = "Analytics.Code.ConfigurationClass_0_siteId"; + + private static final String requestAddressId = "Analytics.Code.ConfigurationClass_0_requestAddress"; + + public static AdminViewPage gotoAdminPage() { AdministrationPage administrationPage = AdministrationPage.gotoPage(); - administrationPage.clickSection("Other", "analytics.application"); + administrationPage.clickSection("Other", "Analytics"); + return new AdminViewPage(); + } + + public static AdminViewPage setTrackingCode(XWikiWebDriver driver, String value) + { + driver.findElement(By.id(trackingCodeId)).sendKeys(value); + return new AdminViewPage(); + } + + public static AdminViewPage setAuthTokenId(XWikiWebDriver driver, String value) + { + driver.findElement(By.id(authTokenId)).sendKeys(value); + return new AdminViewPage(); + } + + public static AdminViewPage setIdSiteId(XWikiWebDriver driver, String value) + { + driver.findElement(By.id(idSiteId)).sendKeys(value); + return new AdminViewPage(); + } + + public static AdminViewPage setRequestAddressId(XWikiWebDriver driver, String value) + { + driver.findElement(By.id(requestAddressId)).sendKeys(value); + return new AdminViewPage(); + } + + public static AdminViewPage bringSaveButtonIntoView(XWikiWebDriver driver) + { + WebElement saveButton = driver.findElement(By.cssSelector(".btn.btn-primary")); + Actions actions = driver.createActions(); + actions.moveToElement(saveButton).click().perform(); + return new AdminViewPage(); + } + + public static String inProgressNotification(XWikiWebDriver driver) + { + return driver.findElement(By.cssSelector(".xnotification.xnotification-inprogress")).getText(); + } + + public static String errorNotification(XWikiWebDriver driver) + { + return driver.findElement(By.cssSelector(".xnotification.xnotification-error")).getText(); + } + + public static String successNotification(XWikiWebDriver driver) + { + return driver.findElement(By.cssSelector(".xnotification.xnotification-done")).getText(); } } From 778ebe1c2a4fb8969b7d00dc674fe55bfd4434a1 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Mon, 9 Oct 2023 09:43:44 +0300 Subject: [PATCH 091/105] Matomo page object * Dosen't work for the time being I can't connect to the matomo application from the testing container. --- .../src/main/resources/config.ini.php | 1 + .../xwiki/analytics/test/ui/AnalyticsIT.java | 13 +++-- .../analytics/test/ui/config/Config.java | 2 +- .../analytics/test/po/AdminViewPage.java | 3 +- .../analytics/test/po/MatomoTokenCreator.java | 49 +++++++++++++++++++ 5 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTokenCreator.java diff --git a/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php b/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php index 6980a791..e95e676b 100644 --- a/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php +++ b/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php @@ -13,6 +13,7 @@ salt = "221e020a358ad259431e9ec76ba8a941" trusted_hosts[] = "localhost" trusted_hosts[] = "localhost:9999" +enable_trusted_host_check = 0 debug = 1 [PluginsInstalled] diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index d58076e1..c706e943 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -36,6 +36,7 @@ import com.xwiki.analytics.test.po.AdminViewPage; import com.xwiki.analytics.test.po.HomePageViewPage; +import com.xwiki.analytics.test.po.MatomoTokenCreator; import xwiki.analytics.test.ui.config.Config; @@ -54,6 +55,7 @@ void setup(TestConfiguration testConfiguration, TestUtils testUtils) throws Exce testUtils.setGlobalRights("XWiki.XWikiAdminGroup", "", "admin", true); testUtils.createAdminUser(); testUtils.loginAsAdmin(); + } /** @@ -87,7 +89,7 @@ void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException + " _paq.push(['trackPageView']);\n" + " _paq.push(['enableLinkTracking']);\n" + " (function() {\n" - + " var u=\"http://"+ Config.ADDRESS+":"+Config.MATOMO_BRIDGE_PORT+"/matomo/"+"\";\n" + + " var u=\"http://"+ Config.ADDRESS+":"+Config.MATOMO_BRIDGE_PORT+"/"+"\";\n" + " _paq.push(['setTrackerUrl', u+'matomo.php']);\n" + " _paq.push(['setSiteId', '1']);\n" + " var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];\n" @@ -96,11 +98,11 @@ void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException + "\n" + "\n").setAuthTokenId(driver, Config.MATOMO_AUTH_TOKEN) .setIdSiteId(driver, "1").setRequestAddressId(driver, - "http://"+Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT + "/matomo/") + "http://"+Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT + "/") .bringSaveButtonIntoView(driver); - assertEquals(AdminViewPage.inProgressNotification(driver), "Saving..."); - assertEquals(AdminViewPage.successNotification(driver), "Saved"); - assertEquals(AdminViewPage.inProgressNotification(driver), "Checking connection to Matomo."); + //assertEquals(AdminViewPage.inProgressNotification(driver), "Saving..."); + //assertEquals(AdminViewPage.successNotification(driver), "Saved"); + //assertEquals(AdminViewPage.inProgressNotification(driver), "Checking connection to Matomo."); driver.waitUntilElementDisappears(By.cssSelector(".xnotification.xnotification-inprogress")); // assertEquals(AdminViewPage.successNotification(driver), // "Test connection succeeded!"); @@ -167,5 +169,6 @@ private GenericContainer startMatomo(TestConfiguration testConfiguration, Generi matomoContainer.setPortBindings(Collections.singletonList(String.format("%d:80", Config.MATOMO_BRIDGE_PORT))); DockerTestUtils.startContainer(matomoContainer, testConfiguration); return matomoContainer; + } } diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java index 00c86315..e9dc4037 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java @@ -42,5 +42,5 @@ public class Config public static final String MATOMO_CREDENTIALS = "ADMIN1"; - public static final String MATOMO_AUTH_TOKEN = "91be1bca1315c35abd605ad8a544eece"; + public static String MATOMO_AUTH_TOKEN = "91be1bca1315c35abd605ad8a544eece"; } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java index b92d927e..1b90ef02 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java @@ -24,8 +24,9 @@ import org.openqa.selenium.interactions.Actions; import org.xwiki.administration.test.po.AdministrationPage; import org.xwiki.test.ui.XWikiWebDriver; +import org.xwiki.test.ui.po.ViewPage; -public class AdminViewPage +public class AdminViewPage extends ViewPage { public AdminViewPage() { diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTokenCreator.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTokenCreator.java new file mode 100644 index 00000000..beb233f5 --- /dev/null +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTokenCreator.java @@ -0,0 +1,49 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.analytics.test.po; + +import org.mockito.internal.verification.DefaultRegisteredInvocations; +import org.openqa.selenium.By; +import org.xwiki.test.ui.XWikiWebDriver; +import org.xwiki.test.ui.po.ViewPage; + +public class MatomoTokenCreator extends ViewPage +{ + private static final String credentials = "ADMIN1" ; + public MatomoTokenCreator() + { + + } + static public String createToken() + { + XWikiWebDriver driver = getUtil().getDriver(); + getUtil().gotoPage( + "http://matomo:9999/index.php?module=UsersManager&action=addNewToken&idSite=1&period=day&date=2023" + + "-09-03"); + driver.findElement(By.id("login_form_login")).sendKeys(credentials); + driver.findElement(By.id("login_form_password")).sendKeys(credentials); + driver.findElement(By.id("login_form_submit")).click(); + driver.findElement(By.id("login_form_password")).sendKeys(credentials); + driver.findElement(By.id("login_form_submit")).click(); + driver.findElement(By.id("description")).sendKeys("TEST TOKEN"); + driver.findElement(By.cssSelector(".btn")).click(); + return driver.findElement(By.tagName("code")).getText(); + } +} From 9c37c1704046876c38941788ff2d9eb464ac78e4 Mon Sep 17 00:00:00 2001 From: farcasut Date: Fri, 10 Nov 2023 16:56:11 +0200 Subject: [PATCH 092/105] Backup --- .../xwiki/analytics/test/ui/AnalyticsIT.java | 121 +++++++++++------- .../analytics/test/ui/config/Config.java | 1 + .../analytics/test/po/AdminViewPage.java | 59 ++++++--- .../analytics/test/po/MatomoTokenCreator.java | 49 ------- 4 files changed, 114 insertions(+), 116 deletions(-) delete mode 100644 application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTokenCreator.java diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index c706e943..0bbe37cc 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -19,15 +19,18 @@ */ package xwiki.analytics.test.ui; +import java.sql.SQLOutput; import java.util.Collections; +import org.jmock.lib.action.ThrowAction; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.By; +import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.utility.DockerImageName; +import org.xwiki.model.reference.DocumentReference; import org.xwiki.test.docker.internal.junit5.DockerTestUtils; import org.xwiki.test.docker.junit5.TestConfiguration; import org.xwiki.test.docker.junit5.UITest; @@ -36,11 +39,12 @@ import com.xwiki.analytics.test.po.AdminViewPage; import com.xwiki.analytics.test.po.HomePageViewPage; -import com.xwiki.analytics.test.po.MatomoTokenCreator; +import com.xwiki.analytics.test.po.MatomoViewPage; import xwiki.analytics.test.ui.config.Config; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @UITest @@ -49,79 +53,86 @@ public class AnalyticsIT @BeforeAll void setup(TestConfiguration testConfiguration, TestUtils testUtils) throws Exception { + GenericContainer sqlContainer = startDb(testConfiguration); GenericContainer matomoContainer = startMatomo(testConfiguration, sqlContainer); + Container.ExecResult result = matomoContainer.execInContainer( + "sh", "-c", + "grep -rl 'matomo.js' /var/www/html/ | xargs -d '\\n' -I {} sed -i 's/matomo.js/xwiki.js/g' \"{}\"" + ); + matomoContainer.execInContainer( + "sh", "-c", + "mv /var/www/html/matomo.js /var/www/html/xwiki.js" + ); + + Config.MATOMO_AUTH_TOKEN = + MatomoViewPage.createToken("http://" + Config.ADDRESS + ":" + matomoContainer.getMappedPort(80)); + HomePageViewPage.gotoPageHomePage(); testUtils.loginAsSuperAdmin(); testUtils.setGlobalRights("XWiki.XWikiAdminGroup", "", "admin", true); testUtils.createAdminUser(); testUtils.loginAsAdmin(); - + HomePageViewPage.gotoPageHomePage(); + testUtils.setWikiPreference("meta", + "#foreach($uix in $services.uix.getExtensions(\"org.xwiki.platform.html.head\"," + + " {'sortByParameter' : 'order'}))\n" + " $services.rendering.render($uix.execute(), 'xhtml/1.0')\n" + + "#end"); } /** * The function checks that the "Save" button displays the correct messages when the configurations provided by the * users are incorrect. - * - * @param driver */ - //@Test - // @Order(1) + @Test + @Order(1) void checkWrongConfigs(XWikiWebDriver driver) throws InterruptedException { - AdminViewPage.gotoAdminPage().setTrackingCode(driver, "").setAuthTokenId(driver, Config.MATOMO_AUTH_TOKEN) + AdminViewPage adminViewPage = new AdminViewPage(); + AdminViewPage.gotoAdminPage(); + adminViewPage.setTrackingCode(driver, "").setAuthTokenId(driver, Config.MATOMO_AUTH_TOKEN) .setIdSiteId(driver, "1").setRequestAddressId(driver, Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT) .bringSaveButtonIntoView(driver); - assertEquals(AdminViewPage.inProgressNotification(driver), "Saving..."); - assertEquals(AdminViewPage.successNotification(driver), "Saved"); - assertEquals(AdminViewPage.inProgressNotification(driver), "Checking connection to Matomo."); - assertEquals(AdminViewPage.errorNotification(driver), - "Failed to connect to Matomo. Please check your configuration values."); + + assertTrue(adminViewPage.inProgressNotification("Saving...")); + assertTrue(adminViewPage.successNotification("Saved")); + assertTrue(adminViewPage.inProgressNotification("Checking connection to Matomo.")); + assertTrue(adminViewPage.errorNotification("Failed to connect to Matomo. Please check your configuration " + + "values.")); + HomePageViewPage.gotoPageHomePage(); + } + /** + * The function checks that the "Save" button displays the correct messages when the configurations provided by the + * users are correct. + */ @Test - @Order(1) + @Order(2) void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException { - AdminViewPage.gotoAdminPage().setTrackingCode(driver, "\n" - + "\n" - + "\n").setAuthTokenId(driver, Config.MATOMO_AUTH_TOKEN) - .setIdSiteId(driver, "1").setRequestAddressId(driver, - "http://"+Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT + "/") - .bringSaveButtonIntoView(driver); - //assertEquals(AdminViewPage.inProgressNotification(driver), "Saving..."); - //assertEquals(AdminViewPage.successNotification(driver), "Saved"); - //assertEquals(AdminViewPage.inProgressNotification(driver), "Checking connection to Matomo."); - driver.waitUntilElementDisappears(By.cssSelector(".xnotification.xnotification-inprogress")); - // assertEquals(AdminViewPage.successNotification(driver), - // "Test connection succeeded!"); - while (true) - { - Thread.sleep(10000); - System.out.printf("test"); - } + + AdminViewPage adminViewPage = new AdminViewPage(); + + AdminViewPage.gotoAdminPage(); + adminViewPage.setTrackingCode(driver, getTrackingCode()).setAuthTokenId(driver, + Config.MATOMO_AUTH_TOKEN).setIdSiteId(driver, "1").setRequestAddressId(driver, + "http://" + Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT + "/").bringSaveButtonIntoView(driver); + assertTrue(adminViewPage.inProgressNotification("Saving...")); + assertTrue(adminViewPage.successNotification("Saved")); + assertTrue(adminViewPage.inProgressNotification("Checking connection to Matomo.")); + assertTrue(adminViewPage.successNotification("Test connection succeeded!")); + HomePageViewPage.gotoPageHomePage(); + } /** * Checks if an admin can add/remove macros to the main dashboard. - * - * @param driver */ @Test - @Order(2) + @Order(3) void appEntryRedirectsToHomePage(XWikiWebDriver driver) throws InterruptedException { + // Add a gadget to the dashboard. HomePageViewPage.gotoAndEdit().addNewMacro(driver, "searchCategories", "Search Categories") .saveDashboard(driver); @@ -169,6 +180,24 @@ private GenericContainer startMatomo(TestConfiguration testConfiguration, Generi matomoContainer.setPortBindings(Collections.singletonList(String.format("%d:80", Config.MATOMO_BRIDGE_PORT))); DockerTestUtils.startContainer(matomoContainer, testConfiguration); return matomoContainer; + } + private String getTrackingCode() + { + return "\n" + + "\n" + + "\n"; } } diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java index e9dc4037..a7dba4f4 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java @@ -42,5 +42,6 @@ public class Config public static final String MATOMO_CREDENTIALS = "ADMIN1"; + //139fe0f667fe8eb309cad6179e486d1e public static String MATOMO_AUTH_TOKEN = "91be1bca1315c35abd605ad8a544eece"; } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java index 1b90ef02..8ac4bae7 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java @@ -28,11 +28,6 @@ public class AdminViewPage extends ViewPage { - public AdminViewPage() - { - - } - private static final String trackingCodeId = "Analytics.Code.ConfigurationClass_0_trackingCode"; private static final String authTokenId = "Analytics.Code.ConfigurationClass_0_authToken"; @@ -41,6 +36,11 @@ public AdminViewPage() private static final String requestAddressId = "Analytics.Code.ConfigurationClass_0_requestAddress"; + public AdminViewPage() + { + + } + public static AdminViewPage gotoAdminPage() { AdministrationPage administrationPage = AdministrationPage.gotoPage(); @@ -48,50 +48,67 @@ public static AdminViewPage gotoAdminPage() return new AdminViewPage(); } - public static AdminViewPage setTrackingCode(XWikiWebDriver driver, String value) + public AdminViewPage setTrackingCode(XWikiWebDriver driver, String value) { driver.findElement(By.id(trackingCodeId)).sendKeys(value); - return new AdminViewPage(); + return this; } - public static AdminViewPage setAuthTokenId(XWikiWebDriver driver, String value) + public AdminViewPage setAuthTokenId(XWikiWebDriver driver, String value) { driver.findElement(By.id(authTokenId)).sendKeys(value); - return new AdminViewPage(); + return this; } - public static AdminViewPage setIdSiteId(XWikiWebDriver driver, String value) + public AdminViewPage setIdSiteId(XWikiWebDriver driver, String value) { driver.findElement(By.id(idSiteId)).sendKeys(value); - return new AdminViewPage(); + return this; } - public static AdminViewPage setRequestAddressId(XWikiWebDriver driver, String value) + public AdminViewPage setRequestAddressId(XWikiWebDriver driver, String value) { driver.findElement(By.id(requestAddressId)).sendKeys(value); - return new AdminViewPage(); + return this; } - public static AdminViewPage bringSaveButtonIntoView(XWikiWebDriver driver) + public AdminViewPage bringSaveButtonIntoView(XWikiWebDriver driver) { WebElement saveButton = driver.findElement(By.cssSelector(".btn.btn-primary")); Actions actions = driver.createActions(); actions.moveToElement(saveButton).click().perform(); - return new AdminViewPage(); + return this; } - public static String inProgressNotification(XWikiWebDriver driver) + public boolean inProgressNotification(String message) { - return driver.findElement(By.cssSelector(".xnotification.xnotification-inprogress")).getText(); + + try { + this.waitForNotificationInProgressMessage(message); + return true; + } catch (Exception e) { + return false; + } } - public static String errorNotification(XWikiWebDriver driver) + public boolean errorNotification(String message) { - return driver.findElement(By.cssSelector(".xnotification.xnotification-error")).getText(); + + try { + this.waitForNotificationErrorMessage(message); + return true; + } catch (Exception e) { + return false; + } } - public static String successNotification(XWikiWebDriver driver) + public boolean successNotification(String message) { - return driver.findElement(By.cssSelector(".xnotification.xnotification-done")).getText(); + try { + this.waitForNotificationSuccessMessage(message); + return true; + } catch (Exception e) { + return false; + } } } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTokenCreator.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTokenCreator.java deleted file mode 100644 index beb233f5..00000000 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTokenCreator.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * See the NOTICE file distributed with this work for additional - * information regarding copyright ownership. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package com.xwiki.analytics.test.po; - -import org.mockito.internal.verification.DefaultRegisteredInvocations; -import org.openqa.selenium.By; -import org.xwiki.test.ui.XWikiWebDriver; -import org.xwiki.test.ui.po.ViewPage; - -public class MatomoTokenCreator extends ViewPage -{ - private static final String credentials = "ADMIN1" ; - public MatomoTokenCreator() - { - - } - static public String createToken() - { - XWikiWebDriver driver = getUtil().getDriver(); - getUtil().gotoPage( - "http://matomo:9999/index.php?module=UsersManager&action=addNewToken&idSite=1&period=day&date=2023" - + "-09-03"); - driver.findElement(By.id("login_form_login")).sendKeys(credentials); - driver.findElement(By.id("login_form_password")).sendKeys(credentials); - driver.findElement(By.id("login_form_submit")).click(); - driver.findElement(By.id("login_form_password")).sendKeys(credentials); - driver.findElement(By.id("login_form_submit")).click(); - driver.findElement(By.id("description")).sendKeys("TEST TOKEN"); - driver.findElement(By.cssSelector(".btn")).click(); - return driver.findElement(By.tagName("code")).getText(); - } -} From 94298538faaa423adcfefe185559f3f9e44dae9f Mon Sep 17 00:00:00 2001 From: farcasut Date: Sun, 12 Nov 2023 09:59:28 +0200 Subject: [PATCH 093/105] BACKUP --- .../src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index 0bbe37cc..101773cb 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -98,8 +98,11 @@ void checkWrongConfigs(XWikiWebDriver driver) throws InterruptedException assertTrue(adminViewPage.inProgressNotification("Checking connection to Matomo.")); assertTrue(adminViewPage.errorNotification("Failed to connect to Matomo. Please check your configuration " + "values.")); +<<<<<<< Updated upstream HomePageViewPage.gotoPageHomePage(); +======= +>>>>>>> Stashed changes } /** @@ -123,7 +126,7 @@ void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException assertTrue(adminViewPage.successNotification("Test connection succeeded!")); HomePageViewPage.gotoPageHomePage(); - } + } /** * Checks if an admin can add/remove macros to the main dashboard. From 10e6d8a5466af602579339dbaf2e2e07460602fb Mon Sep 17 00:00:00 2001 From: Farcasut Date: Wed, 22 Nov 2023 10:13:17 +0200 Subject: [PATCH 094/105] Backup --- .../it/xwiki/analytics/test/ui/AnalyticsIT.java | 9 +++------ .../xwiki/analytics/test/po/AdminViewPage.java | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index 101773cb..755a50f0 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -89,6 +89,7 @@ void checkWrongConfigs(XWikiWebDriver driver) throws InterruptedException { AdminViewPage adminViewPage = new AdminViewPage(); AdminViewPage.gotoAdminPage(); + System.out.println("/q/q " + Config.MATOMO_AUTH_TOKEN +" /q/q"); adminViewPage.setTrackingCode(driver, "").setAuthTokenId(driver, Config.MATOMO_AUTH_TOKEN) .setIdSiteId(driver, "1").setRequestAddressId(driver, Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT) .bringSaveButtonIntoView(driver); @@ -98,11 +99,7 @@ void checkWrongConfigs(XWikiWebDriver driver) throws InterruptedException assertTrue(adminViewPage.inProgressNotification("Checking connection to Matomo.")); assertTrue(adminViewPage.errorNotification("Failed to connect to Matomo. Please check your configuration " + "values.")); -<<<<<<< Updated upstream HomePageViewPage.gotoPageHomePage(); - -======= ->>>>>>> Stashed changes } /** @@ -131,8 +128,8 @@ void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException /** * Checks if an admin can add/remove macros to the main dashboard. */ - @Test - @Order(3) + //@Test + //@Order(3) void appEntryRedirectsToHomePage(XWikiWebDriver driver) throws InterruptedException { diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java index 8ac4bae7..100739d9 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java @@ -50,25 +50,33 @@ public static AdminViewPage gotoAdminPage() public AdminViewPage setTrackingCode(XWikiWebDriver driver, String value) { - driver.findElement(By.id(trackingCodeId)).sendKeys(value); + WebElement element = driver.findElement(By.id(trackingCodeId)); + element.clear(); + element.sendKeys(value); return this; } public AdminViewPage setAuthTokenId(XWikiWebDriver driver, String value) { - driver.findElement(By.id(authTokenId)).sendKeys(value); + WebElement element = driver.findElement(By.id(authTokenId)); + element.clear(); + element.sendKeys(value); return this; } public AdminViewPage setIdSiteId(XWikiWebDriver driver, String value) { - driver.findElement(By.id(idSiteId)).sendKeys(value); + WebElement element = driver.findElement(By.id(idSiteId)); + element.clear(); + element.sendKeys(value); return this; } public AdminViewPage setRequestAddressId(XWikiWebDriver driver, String value) { - driver.findElement(By.id(requestAddressId)).sendKeys(value); + WebElement element = driver.findElement(By.id(requestAddressId)); + element.clear(); + element.sendKeys(value); return this; } From 753ea0f770c84b8ff35087033301a4c52d0edcf5 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 23 Nov 2023 09:42:39 +0200 Subject: [PATCH 095/105] Create functional tests #45 * Test for the description button --- .../xwiki/analytics/test/ui/AnalyticsIT.java | 23 +++++++++++++++--- .../analytics/test/po/HomePageViewPage.java | 8 +++++++ .../test/po/MostViewedMacroViewPage.java | 24 +++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPage.java diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index 755a50f0..97eb72d4 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -128,20 +128,37 @@ void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException /** * Checks if an admin can add/remove macros to the main dashboard. */ - //@Test - //@Order(3) + @Test + @Order(3) void appEntryRedirectsToHomePage(XWikiWebDriver driver) throws InterruptedException { - + HomePageViewPage.gotoPageHomePage(); // Add a gadget to the dashboard. HomePageViewPage.gotoAndEdit().addNewMacro(driver, "searchCategories", "Search Categories") .saveDashboard(driver); + // Wait 2 seconds for the macros to load + Thread.sleep(2000); assertEquals(HomePageViewPage.noOfGadgets(driver), 3); // Remove a gadget from the dashboard. HomePageViewPage.gotoAndEdit().removeLastMacro(driver).saveDashboard(driver); + // Wait 2 seconds for the macros to load + Thread.sleep(2000); assertEquals(HomePageViewPage.noOfGadgets(driver), 2); } + + @Test + @Order(4) + void checkMacroDescription(XWikiWebDriver driver) + { + HomePageViewPage.gotoPageHomePage(); + String description = HomePageViewPage.getMacroDescription(driver, 0); + System.out.println("/n/n" + description + "/n/n"); + assertEquals("When visitors search on your website, they are looking for a particular page, content, product," + + " or service. This report lists the pages that were clicked the most after an internal search.", description); + } + + /** * Create and start a container with the database. * diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java index 9f72d5f3..e3baa27b 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java @@ -20,6 +20,7 @@ package com.xwiki.analytics.test.po; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.openqa.selenium.By; @@ -101,4 +102,11 @@ public static HomePageViewPage removeLastMacro(XWikiWebDriver driver) waitAndClick(driver, ".xdialog-box.xdialog-box-confirmation .xdialog-content .buttonwrapper"); return new HomePageViewPage(); } + + public static String getMacroDescription(XWikiWebDriver driver, int id) + { + List elements = driver.findElements(By.cssSelector(".analytics-description")); + return elements.get(id).getAttribute("title"); + } + } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPage.java new file mode 100644 index 00000000..83a81ffc --- /dev/null +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPage.java @@ -0,0 +1,24 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.analytics.test.po; + +public class MostViewedMacroViewPage +{ +} From f954595c0dd7d3699f2d7d0cd8ec25ac78f5422e Mon Sep 17 00:00:00 2001 From: Farcasut Date: Thu, 23 Nov 2023 13:40:33 +0200 Subject: [PATCH 096/105] Create functional tests #45 * BACKUP --- .../application-analytics-test-docker/pom.xml | 1 + .../xwiki/analytics/test/ui/AnalyticsIT.java | 53 +++++++++------ .../analytics/test/po/AdminViewPage.java | 13 ++-- .../analytics/test/po/HomePageViewPage.java | 35 +++++----- .../test/po/MostViewedMacroViewPage.java | 24 ------- .../test/po/MostViewedMacroViewPages.java | 64 +++++++++++++++++++ application-analytics-ui/pom.xml | 16 ++++- 7 files changed, 139 insertions(+), 67 deletions(-) delete mode 100644 application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPage.java create mode 100644 application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPages.java diff --git a/application-analytics-test/application-analytics-test-docker/pom.xml b/application-analytics-test/application-analytics-test-docker/pom.xml index e429fdb3..3164a14a 100644 --- a/application-analytics-test/application-analytics-test-docker/pom.xml +++ b/application-analytics-test/application-analytics-test-docker/pom.xml @@ -37,6 +37,7 @@ true + com.xwiki.licensing diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index 97eb72d4..5f8f8461 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -19,10 +19,8 @@ */ package xwiki.analytics.test.ui; -import java.sql.SQLOutput; import java.util.Collections; -import org.jmock.lib.action.ThrowAction; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -30,7 +28,6 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.utility.DockerImageName; -import org.xwiki.model.reference.DocumentReference; import org.xwiki.test.docker.internal.junit5.DockerTestUtils; import org.xwiki.test.docker.junit5.TestConfiguration; import org.xwiki.test.docker.junit5.UITest; @@ -40,12 +37,13 @@ import com.xwiki.analytics.test.po.AdminViewPage; import com.xwiki.analytics.test.po.HomePageViewPage; import com.xwiki.analytics.test.po.MatomoViewPage; +import com.xwiki.analytics.test.po.MostViewedMacroViewPages; import xwiki.analytics.test.ui.config.Config; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; + @UITest public class AnalyticsIT @@ -90,9 +88,9 @@ void checkWrongConfigs(XWikiWebDriver driver) throws InterruptedException AdminViewPage adminViewPage = new AdminViewPage(); AdminViewPage.gotoAdminPage(); System.out.println("/q/q " + Config.MATOMO_AUTH_TOKEN +" /q/q"); - adminViewPage.setTrackingCode(driver, "").setAuthTokenId(driver, Config.MATOMO_AUTH_TOKEN) - .setIdSiteId(driver, "1").setRequestAddressId(driver, Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT) - .bringSaveButtonIntoView(driver); + adminViewPage.setTrackingCode("").setAuthTokenId(Config.MATOMO_AUTH_TOKEN) + .setIdSiteId("1").setRequestAddressId(Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT) + .bringSaveButtonIntoView(); assertTrue(adminViewPage.inProgressNotification("Saving...")); assertTrue(adminViewPage.successNotification("Saved")); @@ -114,9 +112,9 @@ void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException AdminViewPage adminViewPage = new AdminViewPage(); AdminViewPage.gotoAdminPage(); - adminViewPage.setTrackingCode(driver, getTrackingCode()).setAuthTokenId(driver, - Config.MATOMO_AUTH_TOKEN).setIdSiteId(driver, "1").setRequestAddressId(driver, - "http://" + Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT + "/").bringSaveButtonIntoView(driver); + adminViewPage.setTrackingCode(getTrackingCode()).setAuthTokenId( + Config.MATOMO_AUTH_TOKEN).setIdSiteId("1").setRequestAddressId( + "http://" + Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT + "/").bringSaveButtonIntoView(); assertTrue(adminViewPage.inProgressNotification("Saving...")); assertTrue(adminViewPage.successNotification("Saved")); assertTrue(adminViewPage.inProgressNotification("Checking connection to Matomo.")); @@ -134,28 +132,43 @@ void appEntryRedirectsToHomePage(XWikiWebDriver driver) throws InterruptedExcept { HomePageViewPage.gotoPageHomePage(); // Add a gadget to the dashboard. - HomePageViewPage.gotoAndEdit().addNewMacro(driver, "searchCategories", "Search Categories") - .saveDashboard(driver); + HomePageViewPage.gotoAndEdit().addNewMacro("searchCategories", "Search Categories") + .saveDashboard(); // Wait 2 seconds for the macros to load Thread.sleep(2000); - assertEquals(HomePageViewPage.noOfGadgets(driver), 3); + assertEquals(HomePageViewPage.noOfGadgets(), 3); // Remove a gadget from the dashboard. - HomePageViewPage.gotoAndEdit().removeLastMacro(driver).saveDashboard(driver); + HomePageViewPage.gotoAndEdit().removeLastMacro().saveDashboard(); // Wait 2 seconds for the macros to load Thread.sleep(2000); - assertEquals(HomePageViewPage.noOfGadgets(driver), 2); + assertEquals(HomePageViewPage.noOfGadgets(), 2); + } + + @Test + @Order(5) + void checkRowEvolutionModal() + { + MostViewedMacroViewPages.gotoPage(); + MostViewedMacroViewPages.openRowEvolutionModal(); + assertTrue(MostViewedMacroViewPages.isModalDisplayed()); + MostViewedMacroViewPages.closeModal(); } @Test @Order(4) - void checkMacroDescription(XWikiWebDriver driver) + void checkMacroDescription(XWikiWebDriver driver) throws InterruptedException { HomePageViewPage.gotoPageHomePage(); - String description = HomePageViewPage.getMacroDescription(driver, 0); - System.out.println("/n/n" + description + "/n/n"); - assertEquals("When visitors search on your website, they are looking for a particular page, content, product," - + " or service. This report lists the pages that were clicked the most after an internal search.", description); + while (true) + { + System.out.println("test"); + Thread.sleep(10000); + + } + //assertEquals("When visitors search on your website, they are looking for a particular page, content, product," + /// + " or service. This report lists the pages that were clicked the most after an internal search.", + //description); } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java index 100739d9..22269cc6 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java @@ -36,9 +36,10 @@ public class AdminViewPage extends ViewPage private static final String requestAddressId = "Analytics.Code.ConfigurationClass_0_requestAddress"; + private static XWikiWebDriver driver; public AdminViewPage() { - + driver = getUtil().getDriver(); } public static AdminViewPage gotoAdminPage() @@ -48,7 +49,7 @@ public static AdminViewPage gotoAdminPage() return new AdminViewPage(); } - public AdminViewPage setTrackingCode(XWikiWebDriver driver, String value) + public AdminViewPage setTrackingCode(String value) { WebElement element = driver.findElement(By.id(trackingCodeId)); element.clear(); @@ -56,7 +57,7 @@ public AdminViewPage setTrackingCode(XWikiWebDriver driver, String value) return this; } - public AdminViewPage setAuthTokenId(XWikiWebDriver driver, String value) + public AdminViewPage setAuthTokenId( String value) { WebElement element = driver.findElement(By.id(authTokenId)); element.clear(); @@ -64,7 +65,7 @@ public AdminViewPage setAuthTokenId(XWikiWebDriver driver, String value) return this; } - public AdminViewPage setIdSiteId(XWikiWebDriver driver, String value) + public AdminViewPage setIdSiteId( String value) { WebElement element = driver.findElement(By.id(idSiteId)); element.clear(); @@ -72,7 +73,7 @@ public AdminViewPage setIdSiteId(XWikiWebDriver driver, String value) return this; } - public AdminViewPage setRequestAddressId(XWikiWebDriver driver, String value) + public AdminViewPage setRequestAddressId(String value) { WebElement element = driver.findElement(By.id(requestAddressId)); element.clear(); @@ -80,7 +81,7 @@ public AdminViewPage setRequestAddressId(XWikiWebDriver driver, String value) return this; } - public AdminViewPage bringSaveButtonIntoView(XWikiWebDriver driver) + public AdminViewPage bringSaveButtonIntoView() { WebElement saveButton = driver.findElement(By.cssSelector(".btn.btn-primary")); Actions actions = driver.createActions(); diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java index e3baa27b..22406f3f 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java @@ -33,9 +33,11 @@ public class HomePageViewPage extends ViewPage { + + private static XWikiWebDriver driver; public HomePageViewPage() { - + this.driver = getUtil().getDriver(); } public static HomePageViewPage gotoPageHomePage() @@ -53,57 +55,58 @@ public static HomePageViewPage gotoAndEdit() return new HomePageViewPage(); } - public static HomePageViewPage addNewMacro(XWikiWebDriver driver, String macroID, String macroName) + public static HomePageViewPage addNewMacro(String macroID, String macroName) { - clickAddGadget(driver).selectMacro(driver, macroID, macroName); + + clickAddGadget().selectMacro(macroID, macroName); return new HomePageViewPage(); } - private static HomePageViewPage clickAddGadget(XWikiWebDriver driver) + private static HomePageViewPage clickAddGadget() { driver.findElement(By.cssSelector(".addgadget")).click(); return new HomePageViewPage(); } - private static void waitAndClick(XWikiWebDriver driver, String css) + private static void waitAndClick(String css) { driver.waitUntilElementIsEnabled(driver.findElement(By.cssSelector(css))); driver.findElement(By.cssSelector(css)).click(); } - private static void selectMacro(XWikiWebDriver driver, String macroID, String macroName) + private static void selectMacro(String macroID, String macroName) { // Will bring into view the macro that I want to use. driver.findElement(By.cssSelector(".macro-textFilter")).sendKeys(macroName); - waitAndClick(driver, String.format("li[data-macroid=\"%s\"]", macroID)); + waitAndClick(String.format("li[data-macroid=\"%s\"]", macroID)); // Will wait until the select button becomes enabled and will click on it. - waitAndClick(driver, ".modal.macro-selector-modal.gadget-selector-modal.in .modal-footer .btn-primary"); + waitAndClick(".modal.macro-selector-modal.gadget-selector-modal.in .modal-footer .btn-primary"); // Will wait until the submit button becomes enabled and will click on it. - waitAndClick(driver,".modal.macro-editor-modal.in .modal-footer .btn-primary"); + waitAndClick(".modal.macro-editor-modal.in .modal-footer .btn-primary"); } - public static int noOfGadgets(XWikiWebDriver driver) + public static int noOfGadgets() { return driver.findElements(By.className("gadget")).size(); } - public static HomePageViewPage saveDashboard(XWikiWebDriver driver) + public static HomePageViewPage saveDashboard() { - waitAndClick(driver, ".bottombuttons.sticky-buttons .btn-primary"); + waitAndClick(".bottombuttons.sticky-buttons .btn-primary"); return new HomePageViewPage(); } - public static HomePageViewPage removeLastMacro(XWikiWebDriver driver) + public static HomePageViewPage removeLastMacro() { WebElement lastGadget = driver.findElement(By.cssSelector(".gadget:last-of-type")); // Move the cursor to be on top of the macro to reveal the remove button. new Actions(driver.getWrappedDriver()).moveToElement(lastGadget).perform(); // Click on the remove button when it becomes available. - waitAndClick(driver ,".gadget:last-of-type .remove"); + waitAndClick(".gadget:last-of-type .remove"); // Click on the confirm button to remove the macro. - waitAndClick(driver, ".xdialog-box.xdialog-box-confirmation .xdialog-content .buttonwrapper"); + waitAndClick(".xdialog-box.xdialog-box-confirmation .xdialog-content .buttonwrapper"); return new HomePageViewPage(); } - public static String getMacroDescription(XWikiWebDriver driver, int id) + public static String getMacroDescription(int id) { List elements = driver.findElements(By.cssSelector(".analytics-description")); return elements.get(id).getAttribute("title"); diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPage.java deleted file mode 100644 index 83a81ffc..00000000 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPage.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * See the NOTICE file distributed with this work for additional - * information regarding copyright ownership. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package com.xwiki.analytics.test.po; - -public class MostViewedMacroViewPage -{ -} diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPages.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPages.java new file mode 100644 index 00000000..d0a5778e --- /dev/null +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPages.java @@ -0,0 +1,64 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.analytics.test.po; + +import java.util.List; +import java.util.Arrays; + +import org.openqa.selenium.By; +import org.xwiki.test.ui.XWikiWebDriver; +import org.xwiki.test.ui.po.ViewPage; + +public class MostViewedMacroViewPages extends ViewPage +{ + + private static XWikiWebDriver driver; + private static final String ROW_EVOLUTION_BUTTON_SELECTOR = ".analyticsRowEvolution"; + private static final String ROW_EVOLUTION_MODAL_SELECTOR = ".modal.fade.analyticsRowEvolutionModal"; + public MostViewedMacroViewPages() + { + driver = getUtil().getDriver(); + } + + public static MostViewedMacroViewPages gotoPage() + { + List spaces = Arrays.asList("Analytics", "Code", "Macros"); + getUtil().gotoPage(spaces, "MostViewedPages", "view", ""); + return new MostViewedMacroViewPages(); + } + + public static void openRowEvolutionModal() + { + driver.waitUntilElementIsVisible(By.cssSelector(ROW_EVOLUTION_BUTTON_SELECTOR)); + driver.findElement(By.cssSelector(ROW_EVOLUTION_BUTTON_SELECTOR)).click(); + } + + public static boolean isModalDisplayed() + { + driver.waitUntilElementIsVisible(By.cssSelector(ROW_EVOLUTION_MODAL_SELECTOR)); + if(driver.findElement(By.cssSelector(ROW_EVOLUTION_MODAL_SELECTOR)).isDisplayed()) + return true; + return false; + } + public static void closeModal() + { + driver.findElement(By.cssSelector(".btn.btn-default")).click(); + } +} diff --git a/application-analytics-ui/pom.xml b/application-analytics-ui/pom.xml index 457ac9e0..6b2ee6a4 100644 --- a/application-analytics-ui/pom.xml +++ b/application-analytics-ui/pom.xml @@ -66,10 +66,24 @@ org.xwiki.platform xwiki-platform-dashboard-ui + runtime ${platform.version} xar - + + org.xwiki.platform + xwiki-platform-icon-ui + ${platform.version} + runtime + xar + + + org.xwiki.platform + xwiki-platform-icon-rest-default + ${platform.version} + runtime + jar + org.xwiki.contrib application-chartjs-webjar From 93fb3590da9e628f72b8c5d2f2e615e7613a7c4d Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 24 Nov 2023 12:01:42 +0200 Subject: [PATCH 097/105] Create functional tests #45 * Added a few functional tests for the basic operations of the extension. --- .../xwiki/analytics/test/ui/AnalyticsIT.java | 137 ++++++++---------- .../analytics/test/ui/config/Config.java | 12 ++ .../analytics/test/po/HomePageViewPage.java | 3 +- .../test/po/MostViewedMacroViewPages.java | 7 + 4 files changed, 85 insertions(+), 74 deletions(-) diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index 5f8f8461..e7a8c694 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -44,39 +44,56 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; - @UITest public class AnalyticsIT { - @BeforeAll - void setup(TestConfiguration testConfiguration, TestUtils testUtils) throws Exception + /** + * Creates the Admin user + */ + public void setupUsers(TestUtils testUtils) { + testUtils.loginAsSuperAdmin(); + testUtils.setGlobalRights("XWiki.XWikiAdminGroup", "", "admin", true); + testUtils.createAdminUser(); + testUtils.loginAsAdmin(); + } + /** + * Start the matomo and sql containers, rename the matomo.js to a random int to force the browser to load the + * tracking script without explicit settings and generate a new auth token for matomo to be used in the tests. + */ + public void setupContainers(TestConfiguration testConfiguration) throws Exception + { GenericContainer sqlContainer = startDb(testConfiguration); GenericContainer matomoContainer = startMatomo(testConfiguration, sqlContainer); - Container.ExecResult result = matomoContainer.execInContainer( - "sh", "-c", - "grep -rl 'matomo.js' /var/www/html/ | xargs -d '\\n' -I {} sed -i 's/matomo.js/xwiki.js/g' \"{}\"" - ); - matomoContainer.execInContainer( - "sh", "-c", - "mv /var/www/html/matomo.js /var/www/html/xwiki.js" - ); + Container.ExecResult result = matomoContainer.execInContainer("sh", "-c", + "grep -rl 'matomo.js' /var/www/html/ | xargs -d '\\n' -I {} sed -i 's/matomo.js/36011373.js/g' \"{}\""); + matomoContainer.execInContainer("sh", "-c", "mv /var/www/html/matomo.js /var/www/html/36011373.js"); Config.MATOMO_AUTH_TOKEN = MatomoViewPage.createToken("http://" + Config.ADDRESS + ":" + matomoContainer.getMappedPort(80)); - HomePageViewPage.gotoPageHomePage(); - testUtils.loginAsSuperAdmin(); - testUtils.setGlobalRights("XWiki.XWikiAdminGroup", "", "admin", true); - testUtils.createAdminUser(); - testUtils.loginAsAdmin(); - HomePageViewPage.gotoPageHomePage(); + } + + /** + * Import the platform.html.head UIExtension point to make the tracking code work in the test environmnet. + */ + private void setupUIExtension(TestUtils testUtils) throws Exception + { testUtils.setWikiPreference("meta", "#foreach($uix in $services.uix.getExtensions(\"org.xwiki.platform.html.head\"," + " {'sortByParameter' : 'order'}))\n" + " $services.rendering.render($uix.execute(), 'xhtml/1.0')\n" + "#end"); } + @BeforeAll + void setup(TestConfiguration testConfiguration, TestUtils testUtils) throws Exception + { + setupContainers(testConfiguration); + setupUIExtension(testUtils); + setupUsers(testUtils); + HomePageViewPage.gotoPageHomePage(); + } + /** * The function checks that the "Save" button displays the correct messages when the configurations provided by the * users are incorrect. @@ -87,16 +104,15 @@ void checkWrongConfigs(XWikiWebDriver driver) throws InterruptedException { AdminViewPage adminViewPage = new AdminViewPage(); AdminViewPage.gotoAdminPage(); - System.out.println("/q/q " + Config.MATOMO_AUTH_TOKEN +" /q/q"); - adminViewPage.setTrackingCode("").setAuthTokenId(Config.MATOMO_AUTH_TOKEN) - .setIdSiteId("1").setRequestAddressId(Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT) - .bringSaveButtonIntoView(); + System.out.println("/q/q " + Config.MATOMO_AUTH_TOKEN + " /q/q"); + adminViewPage.setTrackingCode("").setAuthTokenId(Config.MATOMO_AUTH_TOKEN).setIdSiteId("1") + .setRequestAddressId(Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT).bringSaveButtonIntoView(); assertTrue(adminViewPage.inProgressNotification("Saving...")); assertTrue(adminViewPage.successNotification("Saved")); assertTrue(adminViewPage.inProgressNotification("Checking connection to Matomo.")); - assertTrue(adminViewPage.errorNotification("Failed to connect to Matomo. Please check your configuration " - + "values.")); + assertTrue(adminViewPage.errorNotification( + "Failed to connect to Matomo. Please check your configuration " + "values.")); HomePageViewPage.gotoPageHomePage(); } @@ -112,28 +128,27 @@ void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException AdminViewPage adminViewPage = new AdminViewPage(); AdminViewPage.gotoAdminPage(); - adminViewPage.setTrackingCode(getTrackingCode()).setAuthTokenId( - Config.MATOMO_AUTH_TOKEN).setIdSiteId("1").setRequestAddressId( - "http://" + Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT + "/").bringSaveButtonIntoView(); + adminViewPage.setTrackingCode(Config.getTrackingCode()).setAuthTokenId(Config.MATOMO_AUTH_TOKEN).setIdSiteId( + "1") + .setRequestAddressId("http://" + Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT + "/") + .bringSaveButtonIntoView(); assertTrue(adminViewPage.inProgressNotification("Saving...")); assertTrue(adminViewPage.successNotification("Saved")); assertTrue(adminViewPage.inProgressNotification("Checking connection to Matomo.")); assertTrue(adminViewPage.successNotification("Test connection succeeded!")); HomePageViewPage.gotoPageHomePage(); - - } + } /** - * Checks if an admin can add/remove macros to the main dashboard. + * Checks if the admin has edit permissions in the home page of the application. */ @Test @Order(3) - void appEntryRedirectsToHomePage(XWikiWebDriver driver) throws InterruptedException + void checkEditPermissions(XWikiWebDriver driver) throws InterruptedException { HomePageViewPage.gotoPageHomePage(); // Add a gadget to the dashboard. - HomePageViewPage.gotoAndEdit().addNewMacro("searchCategories", "Search Categories") - .saveDashboard(); + HomePageViewPage.gotoAndEdit().addNewMacro("searchCategories", "Search Categories").saveDashboard(); // Wait 2 seconds for the macros to load Thread.sleep(2000); assertEquals(HomePageViewPage.noOfGadgets(), 3); @@ -144,39 +159,35 @@ void appEntryRedirectsToHomePage(XWikiWebDriver driver) throws InterruptedExcept assertEquals(HomePageViewPage.noOfGadgets(), 2); } + /** + * Checks that the description is loaded properly for a macro. + */ @Test - @Order(5) - void checkRowEvolutionModal() + @Order(4) + void checkMacroDescription(XWikiWebDriver driver) throws InterruptedException { MostViewedMacroViewPages.gotoPage(); - MostViewedMacroViewPages.openRowEvolutionModal(); - assertTrue(MostViewedMacroViewPages.isModalDisplayed()); - MostViewedMacroViewPages.closeModal(); - } + assertEquals("When visitors search on your website, they are looking for a particular page, content, product," + + " or service. This report lists the pages that were clicked the most after an internal search.", + MostViewedMacroViewPages.getMacroDescription()); + } + /** + * Checks that the Row Evolution modal is loaded properly. + */ @Test - @Order(4) - void checkMacroDescription(XWikiWebDriver driver) throws InterruptedException + @Order(5) + void checkRowEvolutionModal() { - HomePageViewPage.gotoPageHomePage(); - while (true) - { - System.out.println("test"); - Thread.sleep(10000); - - } - //assertEquals("When visitors search on your website, they are looking for a particular page, content, product," - /// + " or service. This report lists the pages that were clicked the most after an internal search.", - //description); + MostViewedMacroViewPages.gotoPage(); + MostViewedMacroViewPages.openRowEvolutionModal(); + assertTrue(MostViewedMacroViewPages.isModalDisplayed()); } - /** * Create and start a container with the database. * - * @param testConfiguration configuration for the test container - * @return reference to the container */ private MySQLContainer startDb(TestConfiguration testConfiguration) throws Exception { @@ -198,7 +209,6 @@ private MySQLContainer startDb(TestConfiguration testConfiguration) throws Excep * * @param testConfiguration test configuration * @param dbContainer reference to the db container - * @return reference to the container */ private GenericContainer startMatomo(TestConfiguration testConfiguration, GenericContainer dbContainer) throws Exception @@ -211,23 +221,4 @@ private GenericContainer startMatomo(TestConfiguration testConfiguration, Generi DockerTestUtils.startContainer(matomoContainer, testConfiguration); return matomoContainer; } - - private String getTrackingCode() - { - return "\n" - + "\n" - + "\n"; - } } diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java index a7dba4f4..9ec9c172 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java @@ -44,4 +44,16 @@ public class Config //139fe0f667fe8eb309cad6179e486d1e public static String MATOMO_AUTH_TOKEN = "91be1bca1315c35abd605ad8a544eece"; + + public static String getTrackingCode() + { + return "\n" + "\n" + + "\n"; + } } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java index 22406f3f..b2e8bb8d 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java @@ -88,8 +88,9 @@ public static int noOfGadgets() { return driver.findElements(By.className("gadget")).size(); } - public static HomePageViewPage saveDashboard() + public static HomePageViewPage saveDashboard() throws InterruptedException { + waitAndClick(".bottombuttons.sticky-buttons .btn-primary"); return new HomePageViewPage(); } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPages.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPages.java index d0a5778e..499c9e7d 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPages.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPages.java @@ -23,6 +23,7 @@ import java.util.Arrays; import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; import org.xwiki.test.ui.XWikiWebDriver; import org.xwiki.test.ui.po.ViewPage; @@ -56,6 +57,12 @@ public static boolean isModalDisplayed() if(driver.findElement(By.cssSelector(ROW_EVOLUTION_MODAL_SELECTOR)).isDisplayed()) return true; return false; + } + public static String getMacroDescription() + { + driver.waitUntilElementIsVisible(By.cssSelector(".xcontent h2 div a")); + return driver.findElement(By.cssSelector(".xcontent h2 div a")).getAttribute("title"); + } public static void closeModal() { From bd616cba87c9c3fc1da3544cd1a8a0e567b1c06b Mon Sep 17 00:00:00 2001 From: Farcasut Date: Fri, 24 Nov 2023 12:04:07 +0200 Subject: [PATCH 098/105] Create functional tests #45 * Removed a file --- .../mostViewedPages/jsonWithoutURL.json | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 application-analytics-default/src/test/resources/mostViewedPages/jsonWithoutURL.json diff --git a/application-analytics-default/src/test/resources/mostViewedPages/jsonWithoutURL.json b/application-analytics-default/src/test/resources/mostViewedPages/jsonWithoutURL.json deleted file mode 100644 index 009508fa..00000000 --- a/application-analytics-default/src/test/resources/mostViewedPages/jsonWithoutURL.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "ArrayWithoutURL": [ - { - "label": "/xwiki/bin/view/Analytics/Code/MostViewedPages" - }, - { - "label": "/xwiki/bin/view/Main/" - } - ], - "ArrayWithoutURLResponse": [ - { - "label": "/xwiki/bin/view/Analytics/Code/MostViewedPages" - }, - { - "label": "/xwiki/bin/view/Main/" - } - ] -} \ No newline at end of file From 9d3d437be9159b066352a51dd1dc2349f60ee581 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Mon, 18 Dec 2023 23:10:08 +0200 Subject: [PATCH 099/105] Create functional tests #45 * -Update the poms.xml -Moved from a class with static methods to a normal one --- .../application-analytics-test-docker/pom.xml | 77 +++------------ .../xwiki/analytics/test/ui/AnalyticsIT.java | 46 ++++----- .../analytics/test/ui/config/Config.java | 21 ++-- .../pom.xml | 4 +- .../analytics/test/po/AdminViewPage.java | 47 +++------ .../analytics/test/po/HomePageEditPage.java | 87 +++++++++++++++++ .../test/po/MostViewedMacroViewPages.java | 38 ++++---- application-analytics-test/pom.xml | 7 +- application-analytics-ui/pom.xml | 28 +++++- .../Analytics/Code/Macros/RowEvolution.xml | 3 +- .../Code/Macros/RowEvolutionJSON.xml | 96 ++++++++++--------- .../Code/Macros/RowEvolutionJson.xml | 54 ----------- 12 files changed, 251 insertions(+), 257 deletions(-) create mode 100644 application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageEditPage.java delete mode 100644 application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJson.xml diff --git a/application-analytics-test/application-analytics-test-docker/pom.xml b/application-analytics-test/application-analytics-test-docker/pom.xml index 3164a14a..cf1459dd 100644 --- a/application-analytics-test/application-analytics-test-docker/pom.xml +++ b/application-analytics-test/application-analytics-test-docker/pom.xml @@ -31,7 +31,7 @@ application-analytics-test-docker jar - Functional tests for the Analytics Application + Functional tests for the Analytics Application (PRO) true @@ -45,6 +45,7 @@ ${licensing.version} runtime + org.xwiki.platform xwiki-platform-menu-ui @@ -52,61 +53,11 @@ xar - org.xwiki.platform - xwiki-platform-panels-ui - ${platform.version} - xar - - - - org.xwiki.platform - xwiki-platform-test-docker - ${platform.version} - test - - - org.xwiki.platform - xwiki-platform-menu-test-pageobjects - ${platform.version} - test - - - org.xwiki.platform - xwiki-platform-panels-test-pageobjects - ${platform.version} - test - - - org.xwiki.platform - xwiki-platform-application-test-pageobjects - ${platform.version} - test - - - org.xwiki.platform - xwiki-platform-administration-test-pageobjects - ${platform.version} - test - - - com.xwiki.licensing - application-licensing-test-dependencies - ${licensing.version} - pom + com.xwiki.analytics + application-analytics-test-pageobjects + 1.0-SNAPSHOT test - - org.xwiki.platform - xwiki-platform-localization-webjar - ${platform.version} - runtime - - - org.xwiki.platform - xwiki-platform-localization-rest-default - ${platform.version} - runtime - com.xwiki.analytics application-analytics-ui @@ -114,21 +65,19 @@ xar runtime + + org.xwiki.platform - xwiki-platform-uiextension-ui - ${platform.version} - xar - - - org.xwiki.platform - xwiki-platform-localization-script + xwiki-platform-test-docker ${platform.version} + test - com.xwiki.analytics - application-analytics-test-pageobjects - 1.0-SNAPSHOT + com.xwiki.licensing + application-licensing-test-dependencies + ${licensing.version} + pom test diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index e7a8c694..c9e7e003 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -33,6 +33,7 @@ import org.xwiki.test.docker.junit5.UITest; import org.xwiki.test.ui.TestUtils; import org.xwiki.test.ui.XWikiWebDriver; +import org.xwiki.test.ui.po.BaseModal; import com.xwiki.analytics.test.po.AdminViewPage; import com.xwiki.analytics.test.po.HomePageViewPage; @@ -69,7 +70,6 @@ public void setupContainers(TestConfiguration testConfiguration) throws Exceptio Container.ExecResult result = matomoContainer.execInContainer("sh", "-c", "grep -rl 'matomo.js' /var/www/html/ | xargs -d '\\n' -I {} sed -i 's/matomo.js/36011373.js/g' \"{}\""); matomoContainer.execInContainer("sh", "-c", "mv /var/www/html/matomo.js /var/www/html/36011373.js"); - Config.MATOMO_AUTH_TOKEN = MatomoViewPage.createToken("http://" + Config.ADDRESS + ":" + matomoContainer.getMappedPort(80)); } @@ -103,16 +103,15 @@ void setup(TestConfiguration testConfiguration, TestUtils testUtils) throws Exce void checkWrongConfigs(XWikiWebDriver driver) throws InterruptedException { AdminViewPage adminViewPage = new AdminViewPage(); - AdminViewPage.gotoAdminPage(); + adminViewPage.gotoAdminPage(); System.out.println("/q/q " + Config.MATOMO_AUTH_TOKEN + " /q/q"); adminViewPage.setTrackingCode("").setAuthTokenId(Config.MATOMO_AUTH_TOKEN).setIdSiteId("1") .setRequestAddressId(Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT).bringSaveButtonIntoView(); - assertTrue(adminViewPage.inProgressNotification("Saving...")); - assertTrue(adminViewPage.successNotification("Saved")); - assertTrue(adminViewPage.inProgressNotification("Checking connection to Matomo.")); - assertTrue(adminViewPage.errorNotification( - "Failed to connect to Matomo. Please check your configuration " + "values.")); + adminViewPage.inProgressNotification("Saving..."); + adminViewPage.successNotification("Saved"); + adminViewPage.inProgressNotification("Checking connection to Matomo."); + adminViewPage.errorNotification("Failed to connect to Matomo. Please check your configuration " + "values."); HomePageViewPage.gotoPageHomePage(); } @@ -127,15 +126,14 @@ void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException AdminViewPage adminViewPage = new AdminViewPage(); - AdminViewPage.gotoAdminPage(); - adminViewPage.setTrackingCode(Config.getTrackingCode()).setAuthTokenId(Config.MATOMO_AUTH_TOKEN).setIdSiteId( - "1") - .setRequestAddressId("http://" + Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT + "/") + adminViewPage.gotoAdminPage(); + adminViewPage.setTrackingCode(Config.getTrackingCode()).setAuthTokenId(Config.MATOMO_AUTH_TOKEN) + .setIdSiteId("1").setRequestAddressId("http://" + Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT + "/") .bringSaveButtonIntoView(); - assertTrue(adminViewPage.inProgressNotification("Saving...")); - assertTrue(adminViewPage.successNotification("Saved")); - assertTrue(adminViewPage.inProgressNotification("Checking connection to Matomo.")); - assertTrue(adminViewPage.successNotification("Test connection succeeded!")); + adminViewPage.inProgressNotification("Saving..."); + adminViewPage.successNotification("Saved"); + adminViewPage.inProgressNotification("Checking connection to Matomo."); + adminViewPage.successNotification("Test connection succeeded!"); HomePageViewPage.gotoPageHomePage(); } @@ -146,17 +144,18 @@ void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException @Order(3) void checkEditPermissions(XWikiWebDriver driver) throws InterruptedException { + HomePageViewPage.gotoPageHomePage(); // Add a gadget to the dashboard. HomePageViewPage.gotoAndEdit().addNewMacro("searchCategories", "Search Categories").saveDashboard(); // Wait 2 seconds for the macros to load Thread.sleep(2000); - assertEquals(HomePageViewPage.noOfGadgets(), 3); + assertEquals(3,HomePageViewPage.noOfGadgets() ); // Remove a gadget from the dashboard. HomePageViewPage.gotoAndEdit().removeLastMacro().saveDashboard(); // Wait 2 seconds for the macros to load Thread.sleep(2000); - assertEquals(HomePageViewPage.noOfGadgets(), 2); + assertEquals(2,HomePageViewPage.noOfGadgets()); } /** @@ -166,11 +165,12 @@ void checkEditPermissions(XWikiWebDriver driver) throws InterruptedException @Order(4) void checkMacroDescription(XWikiWebDriver driver) throws InterruptedException { - MostViewedMacroViewPages.gotoPage(); + MostViewedMacroViewPages mostViewedMacroViewPages = new MostViewedMacroViewPages(); + mostViewedMacroViewPages.gotoPage(); assertEquals("When visitors search on your website, they are looking for a particular page, content, product," + " or service. This report lists the pages that were clicked the most after an internal search.", - MostViewedMacroViewPages.getMacroDescription()); + mostViewedMacroViewPages.getMacroDescription()); } /** @@ -180,14 +180,14 @@ void checkMacroDescription(XWikiWebDriver driver) throws InterruptedException @Order(5) void checkRowEvolutionModal() { - MostViewedMacroViewPages.gotoPage(); - MostViewedMacroViewPages.openRowEvolutionModal(); - assertTrue(MostViewedMacroViewPages.isModalDisplayed()); + MostViewedMacroViewPages mostViewedMacroViewPages = new MostViewedMacroViewPages(); + BaseModal baseModal = mostViewedMacroViewPages.gotoPage().openRowEvolutionModal(); + System.out.println("/MODAL/ " + baseModal.isDisplayed() + "/A/A"); + assertTrue(baseModal.isDisplayed()); } /** * Create and start a container with the database. - * */ private MySQLContainer startDb(TestConfiguration testConfiguration) throws Exception { diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java index 9ec9c172..7d8752c9 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java @@ -34,6 +34,7 @@ public class Config public static final String DB_PASSWORD = "secret"; public static final String ADDRESS = "172.17.0.1"; + public static final String MATOMO_CONTAINER_NAME = "matomo:latest"; public static final String MATOMO_CONFIG_FILE_PATH = "/var/www/html/config/config.ini.php"; @@ -42,18 +43,26 @@ public class Config public static final String MATOMO_CREDENTIALS = "ADMIN1"; - //139fe0f667fe8eb309cad6179e486d1e public static String MATOMO_AUTH_TOKEN = "91be1bca1315c35abd605ad8a544eece"; public static String getTrackingCode() { - return "\n" + "\n" + + " g.async=true; g.src=u+'36011373.js'; s.parentNode.insertBefore(g,s);\n" + + " })();\n" + + "\n" + "\n"; + return sb; } } diff --git a/application-analytics-test/application-analytics-test-pageobjects/pom.xml b/application-analytics-test/application-analytics-test-pageobjects/pom.xml index 782102a1..b7ba5463 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/pom.xml +++ b/application-analytics-test/application-analytics-test-pageobjects/pom.xml @@ -29,9 +29,9 @@ 1.0-SNAPSHOT application-analytics-test-pageobjects - Application Analytics - Tests - Page Objects + Application Analytics (PRO) - Test - Page Objects jar - Application Analytics - Tests - Page Objects + Application Analytics (PRO) - Test - Page Objects org.xwiki.platform diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java index 22269cc6..64fd4705 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java @@ -28,21 +28,21 @@ public class AdminViewPage extends ViewPage { - private static final String trackingCodeId = "Analytics.Code.ConfigurationClass_0_trackingCode"; + private static final String TRACKING_CODE_ID = "Analytics.Code.ConfigurationClass_0_trackingCode"; - private static final String authTokenId = "Analytics.Code.ConfigurationClass_0_authToken"; + private static final String AUTH_TOKEN_ID = "Analytics.Code.ConfigurationClass_0_authToken"; - private static final String idSiteId = "Analytics.Code.ConfigurationClass_0_siteId"; + private static final String IS_SITE = "Analytics.Code.ConfigurationClass_0_siteId"; - private static final String requestAddressId = "Analytics.Code.ConfigurationClass_0_requestAddress"; + private static final String REQUEST_ADDRESS_ID = "Analytics.Code.ConfigurationClass_0_requestAddress"; - private static XWikiWebDriver driver; + private XWikiWebDriver driver; public AdminViewPage() { driver = getUtil().getDriver(); } - public static AdminViewPage gotoAdminPage() + public AdminViewPage gotoAdminPage() { AdministrationPage administrationPage = AdministrationPage.gotoPage(); administrationPage.clickSection("Other", "Analytics"); @@ -51,7 +51,7 @@ public static AdminViewPage gotoAdminPage() public AdminViewPage setTrackingCode(String value) { - WebElement element = driver.findElement(By.id(trackingCodeId)); + WebElement element = driver.findElement(By.id(TRACKING_CODE_ID)); element.clear(); element.sendKeys(value); return this; @@ -59,7 +59,7 @@ public AdminViewPage setTrackingCode(String value) public AdminViewPage setAuthTokenId( String value) { - WebElement element = driver.findElement(By.id(authTokenId)); + WebElement element = driver.findElement(By.id(AUTH_TOKEN_ID)); element.clear(); element.sendKeys(value); return this; @@ -67,7 +67,7 @@ public AdminViewPage setAuthTokenId( String value) public AdminViewPage setIdSiteId( String value) { - WebElement element = driver.findElement(By.id(idSiteId)); + WebElement element = driver.findElement(By.id(IS_SITE)); element.clear(); element.sendKeys(value); return this; @@ -75,7 +75,7 @@ public AdminViewPage setIdSiteId( String value) public AdminViewPage setRequestAddressId(String value) { - WebElement element = driver.findElement(By.id(requestAddressId)); + WebElement element = driver.findElement(By.id(REQUEST_ADDRESS_ID)); element.clear(); element.sendKeys(value); return this; @@ -89,35 +89,18 @@ public AdminViewPage bringSaveButtonIntoView() return this; } - public boolean inProgressNotification(String message) + public void inProgressNotification(String message) { - - try { - this.waitForNotificationInProgressMessage(message); - return true; - } catch (Exception e) { - return false; - } + this.waitForNotificationInProgressMessage(message); } - public boolean errorNotification(String message) + public void errorNotification(String message) { - - try { this.waitForNotificationErrorMessage(message); - return true; - } catch (Exception e) { - return false; - } } - public boolean successNotification(String message) + public void successNotification(String message) { - try { - this.waitForNotificationSuccessMessage(message); - return true; - } catch (Exception e) { - return false; - } + this.waitForNotificationSuccessMessage(message); } } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageEditPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageEditPage.java new file mode 100644 index 00000000..115bf65b --- /dev/null +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageEditPage.java @@ -0,0 +1,87 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.analytics.test.po; + +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; +import org.xwiki.test.ui.XWikiWebDriver; +import org.xwiki.test.ui.po.editor.EditPage; + +public class HomePageEditPage extends EditPage +{ + private XWikiWebDriver driver; + + public HomePageEditPage() + { + driver = getUtil().getDriver(); + } + + private HomePageEditPage clickAddGadget() + { + driver.findElement(By.cssSelector(".addgadget")).click(); + return this; + } + + private void waitAndClick(String css) + { + driver.waitUntilElementIsEnabled(driver.findElement(By.cssSelector(css))); + driver.findElement(By.cssSelector(css)).click(); + } + + private void selectMacro(String macroID, String macroName) + { + // Will bring into view the macro that I want to use. + driver.findElement(By.cssSelector(".macro-textFilter")).sendKeys(macroName); + waitAndClick(String.format("li[data-macroid=\"%s\"]", macroID)); + // Will wait until the select button becomes enabled and will click on it. + waitAndClick(".modal.macro-selector-modal.gadget-selector-modal.in .modal-footer .btn-primary"); + // Will wait until the submit button becomes enabled and will click on it. + waitAndClick(".modal.macro-editor-modal.in .modal-footer .btn-primary"); + } + + public HomePageEditPage addNewMacro(String macroID, String macroName) + { + + this.clickAddGadget().selectMacro(macroID, macroName); + return this; + } + + public HomePageEditPage removeLastMacro() + { + + WebElement lastGadget = driver.findElement(By.cssSelector(".gadget:last-of-type")); + // Move the cursor to be on top of the macro to reveal the remove button. + new Actions(driver.getWrappedDriver()).moveToElement(lastGadget).perform(); + // Click on the remove button when it becomes available. + waitAndClick(".gadget:last-of-type .remove"); + // Click on the confirm button to remove the macro. + waitAndClick(".xdialog-box.xdialog-box-confirmation .xdialog-content .buttonwrapper"); + return this; + } + + public HomePageViewPage saveDashboard() + { + + EditPage editPage = new EditPage(); + editPage.clickSaveAndView(); + return new HomePageViewPage(); + } +} diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPages.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPages.java index 499c9e7d..3a75ed36 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPages.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPages.java @@ -17,55 +17,51 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ + + package com.xwiki.analytics.test.po; -import java.util.List; import java.util.Arrays; +import java.util.List; import org.openqa.selenium.By; -import org.openqa.selenium.WebElement; import org.xwiki.test.ui.XWikiWebDriver; +import org.xwiki.test.ui.po.BaseModal; import org.xwiki.test.ui.po.ViewPage; +/** + * View for the MostViewedPages macro that let's us interact with the page. + */ public class MostViewedMacroViewPages extends ViewPage { + private final XWikiWebDriver driver; - private static XWikiWebDriver driver; private static final String ROW_EVOLUTION_BUTTON_SELECTOR = ".analyticsRowEvolution"; - private static final String ROW_EVOLUTION_MODAL_SELECTOR = ".modal.fade.analyticsRowEvolutionModal"; + public MostViewedMacroViewPages() { - driver = getUtil().getDriver(); + driver = getUtil().getDriver(); } - public static MostViewedMacroViewPages gotoPage() + public MostViewedMacroViewPages gotoPage() { List spaces = Arrays.asList("Analytics", "Code", "Macros"); getUtil().gotoPage(spaces, "MostViewedPages", "view", ""); - return new MostViewedMacroViewPages(); + return this; } - public static void openRowEvolutionModal() + public BaseModal openRowEvolutionModal() { + BaseModal baseModal = new BaseModal(By.cssSelector(".modal-dialog.modal-lg")); driver.waitUntilElementIsVisible(By.cssSelector(ROW_EVOLUTION_BUTTON_SELECTOR)); driver.findElement(By.cssSelector(ROW_EVOLUTION_BUTTON_SELECTOR)).click(); + return baseModal; } - public static boolean isModalDisplayed() - { - driver.waitUntilElementIsVisible(By.cssSelector(ROW_EVOLUTION_MODAL_SELECTOR)); - if(driver.findElement(By.cssSelector(ROW_EVOLUTION_MODAL_SELECTOR)).isDisplayed()) - return true; - return false; - } - public static String getMacroDescription() + public String getMacroDescription() { driver.waitUntilElementIsVisible(By.cssSelector(".xcontent h2 div a")); return driver.findElement(By.cssSelector(".xcontent h2 div a")).getAttribute("title"); - - } - public static void closeModal() - { - driver.findElement(By.cssSelector(".btn.btn-default")).click(); } + } diff --git a/application-analytics-test/pom.xml b/application-analytics-test/pom.xml index 9bf62cbc..e5c69030 100644 --- a/application-analytics-test/pom.xml +++ b/application-analytics-test/pom.xml @@ -40,15 +40,12 @@ true - - application-analytics-test-docker - application-analytics-test-pageobjects - docker - + application-analytics-test-docker + application-analytics-test-pageobjects diff --git a/application-analytics-ui/pom.xml b/application-analytics-ui/pom.xml index 6b2ee6a4..4eb68a5c 100644 --- a/application-analytics-ui/pom.xml +++ b/application-analytics-ui/pom.xml @@ -20,7 +20,8 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - + 4.0.0 com.xwiki.analytics @@ -63,6 +64,23 @@ application-analytics-default ${project.version} + + org.xwiki.platform + xwiki-platform-localization-webjar + ${platform.version} + runtime + + + org.xwiki.platform + xwiki-platform-localization-script + ${platform.version} + + + org.xwiki.platform + xwiki-platform-localization-rest-default + ${platform.version} + runtime + org.xwiki.platform xwiki-platform-dashboard-ui @@ -79,7 +97,13 @@ org.xwiki.platform - xwiki-platform-icon-rest-default + xwiki-platform-uiextension-ui + ${platform.version} + xar + + + org.xwiki.platform + xwiki-platform-icon-rest-api ${platform.version} runtime jar diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml index 5b6cae74..60680c8a 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml @@ -212,7 +212,8 @@ require(['jquery', 'chart'], ($) => { period: currentOptions.data('period'), date: `last${currentOptions.data('date')}`, macroName: currentOptions.data('macroName'), - rowIdentifier: rowIdentifier + rowIdentifier: rowIdentifier, + action: "GET" }; const queryString = $.param(params); $.getJSON(`${requestPageUrl}?${queryString}`) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJSON.xml index c822fc86..a6d65668 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJSON.xml @@ -37,55 +37,57 @@ xwiki/2.1 true {{velocity}} -#set ($parameters = { - 'period' : $request.period, - 'date' : $request.date, - 'module' : 'API', - 'format' : 'json' -}) -#set ($filters = {}) -#if ($request.macroName == 'MostViewedPages') - #set ($discard = $parameters.put('pageUrl', $request.rowIdentifier)) - #set ($discard = $parameters.put('method', 'Actions.getPageUrl')) -#elseif ($request.macroName == 'PagesFollowingASiteSearch') - #set ($discard = $parameters.put('method', 'Actions.getPageUrlsFollowingSiteSearch')) - #set ($discard = $parameters.put('expanded', '1')) - #set ($discard = $parameters.put('flat', '1')) - #set ($discard = $filters.put('url',$request.rowIdentifier)) -#elseif ($request.macroName == 'SearchCategories') - #set ($discard = $parameters.put('segment', $request.rowIdentifier)) - #set ($discard = $parameters.put('method', 'Actions.getSiteSearchCategories')) -#elseif ($request.macroName == 'SiteSearchKeyword') - #set ($discard = $parameters.put('segment', $request.rowIdentifier)) - #set ($discard = $parameters.put('method', 'Actions.getSiteSearchKeywords')) -#elseif ($request.macroName == 'BrowserEngines') - #set ($discard = $parameters.put('method', 'DevicesDetection.getBrowserEngines')) - #set ($discard = $parameters.put('segment', $request.rowIdentifier)) -#elseif ($request.macroName == 'Browsers') - #set ($discard = $parameters.put('method', 'DevicesDetection.getBrowsers')) - #set ($discard = $parameters.put('segment', $request.rowIdentifier)) -#elseif($request.macroName == 'Configurations') - #set ($discard = $parameters.put('method', 'Resolution.getConfiguration')) - #set ($discard = $parameters.put('segment', $request.rowIdentifier)) -#elseif($request.macroName == 'DeviceBrand') - #set ($discard = $parameters.put('method', 'DevicesDetection.getBrand')) - #set ($discard = $parameters.put('segment', $request.rowIdentifier)) -#elseif($request.macroName == 'DeviceModel') - #set ($discard = $parameters.put('method', 'DevicesDetection.getModel')) - #set ($discard = $parameters.put('segment', $request.rowIdentifier)) -#elseif($request.macroName == 'DeviceType') - #set ($discard = $parameters.put('method', 'DevicesDetection.getType')) - #set ($discard = $parameters.put('segment', $request.rowIdentifier)) -#elseif($request.macroName == 'ScreenResolution') - #set ($discard = $parameters.put('method', 'Resolution.getResolution')) - #set ($discard = $parameters.put('segment', $request.rowIdentifier)) -#elseif($request.macroName == 'OperatingSystemVersions') - #set ($discard = $parameters.put('method', 'DevicesDetection.getOsVersions')) - #set ($discard = $parameters.put('segment', $request.rowIdentifier)) +#if ($request.action == 'GET') + #set ($parameters = { + 'period' : $request.period, + 'date' : $request.date, + 'module' : 'API', + 'format' : 'json' + }) + #set ($filters = {}) + #if ($request.macroName == 'MostViewedPages') + #set ($discard = $parameters.put('pageUrl', $request.rowIdentifier)) + #set ($discard = $parameters.put('method', 'Actions.getPageUrl')) + #elseif ($request.macroName == 'PagesFollowingASiteSearch') + #set ($discard = $parameters.put('method', 'Actions.getPageUrlsFollowingSiteSearch')) + #set ($discard = $parameters.put('expanded', '1')) + #set ($discard = $parameters.put('flat', '1')) + #set ($discard = $filters.put('url',$request.rowIdentifier)) + #elseif ($request.macroName == 'SearchCategories') + #set ($discard = $parameters.put('segment', $request.rowIdentifier)) + #set ($discard = $parameters.put('method', 'Actions.getSiteSearchCategories')) + #elseif ($request.macroName == 'SiteSearchKeyword') + #set ($discard = $parameters.put('segment', $request.rowIdentifier)) + #set ($discard = $parameters.put('method', 'Actions.getSiteSearchKeywords')) + #elseif ($request.macroName == 'BrowserEngines') + #set ($discard = $parameters.put('method', 'DevicesDetection.getBrowserEngines')) + #set ($discard = $parameters.put('segment', $request.rowIdentifier)) + #elseif ($request.macroName == 'Browsers') + #set ($discard = $parameters.put('method', 'DevicesDetection.getBrowsers')) + #set ($discard = $parameters.put('segment', $request.rowIdentifier)) + #elseif($request.macroName == 'Configurations') + #set ($discard = $parameters.put('method', 'Resolution.getConfiguration')) + #set ($discard = $parameters.put('segment', $request.rowIdentifier)) + #elseif($request.macroName == 'DeviceBrand') + #set ($discard = $parameters.put('method', 'DevicesDetection.getBrand')) + #set ($discard = $parameters.put('segment', $request.rowIdentifier)) + #elseif($request.macroName == 'DeviceModel') + #set ($discard = $parameters.put('method', 'DevicesDetection.getModel')) + #set ($discard = $parameters.put('segment', $request.rowIdentifier)) + #elseif($request.macroName == 'DeviceType') + #set ($discard = $parameters.put('method', 'DevicesDetection.getType')) + #set ($discard = $parameters.put('segment', $request.rowIdentifier)) + #elseif($request.macroName == 'ScreenResolution') + #set ($discard = $parameters.put('method', 'Resolution.getResolution')) + #set ($discard = $parameters.put('segment', $request.rowIdentifier)) + #elseif($request.macroName == 'OperatingSystemVersions') + #set ($discard = $parameters.put('method', 'DevicesDetection.getOsVersions')) + #set ($discard = $parameters.put('segment', $request.rowIdentifier)) + #end + #set ($analyticsResult = $services.analytics.makeRequest($parameters, $filters, 'RowEvolution')) + #jsonResponse($analyticsResult) #end -#set ($analyticsResult = $services.analytics.makeRequest($parameters, $filters, 'RowEvolution')) -#jsonResponse($analyticsResult) {{/velocity}} diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJson.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJson.xml deleted file mode 100644 index e84f1d4e..00000000 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJson.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - Analytics.Code.Macros - RowEvolutionJson - - - 0 - xwiki:XWiki.Admin - WebHome - xwiki:XWiki.Admin - xwiki:XWiki.Admin - 1.1 - RowEvolutionJson - - false - xwiki/2.1 - true - {{velocity}} -#set ($parameters ={ - 'period' : $request.period, - 'date' : $request.date, - 'module' : 'API', - 'method' : 'Actions.getPageUrl', - 'format' : 'json' -}) -#if ($request.macroName=='MostViewedPages') - $parameters.put('pageUrl', $request.parameter) -#end -#set ($analyticsResult = $services.analytics.getMatomoRequestResult($parameters, {}, 'RowEvolution')) -#jsonResponse($analyticsResult) -{{/velocity}} - - From 4fe5e973cbd82233489eed1dd7dff02482188ba6 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Tue, 19 Dec 2023 08:26:44 +0200 Subject: [PATCH 100/105] Create functional tests #45 * - More comments --- .../it/xwiki/analytics/test/ui/AllITs.java | 3 +- .../xwiki/analytics/test/ui/AnalyticsIT.java | 16 ++--- .../analytics/test/po/AdminViewPage.java | 11 +-- .../analytics/test/po/HomePageEditPage.java | 58 +++++++++------ .../analytics/test/po/HomePageViewPage.java | 72 +++---------------- .../analytics/test/po/MatomoViewPage.java | 27 +++---- ...java => MostViewedPagesMacroViewPage.java} | 22 ++++-- 7 files changed, 89 insertions(+), 120 deletions(-) rename application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/{MostViewedMacroViewPages.java => MostViewedPagesMacroViewPage.java} (76%) diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AllITs.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AllITs.java index eefd9d5b..e3198e55 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AllITs.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AllITs.java @@ -18,10 +18,10 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package xwiki.analytics.test.ui; + import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.xwiki.test.docker.junit5.UITest; -import org.xwiki.test.docker.junit5.servletengine.ServletEngine; @UITest public class AllITs @@ -30,6 +30,5 @@ public class AllITs @DisplayName("Analytics UI Test") class NestedAnalyticsIT extends AnalyticsIT { - } } diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java index c9e7e003..56fa2dde 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java @@ -38,7 +38,7 @@ import com.xwiki.analytics.test.po.AdminViewPage; import com.xwiki.analytics.test.po.HomePageViewPage; import com.xwiki.analytics.test.po.MatomoViewPage; -import com.xwiki.analytics.test.po.MostViewedMacroViewPages; +import com.xwiki.analytics.test.po.MostViewedPagesMacroViewPage; import xwiki.analytics.test.ui.config.Config; @@ -150,12 +150,12 @@ void checkEditPermissions(XWikiWebDriver driver) throws InterruptedException HomePageViewPage.gotoAndEdit().addNewMacro("searchCategories", "Search Categories").saveDashboard(); // Wait 2 seconds for the macros to load Thread.sleep(2000); - assertEquals(3,HomePageViewPage.noOfGadgets() ); + assertEquals(3, HomePageViewPage.noOfGadgets()); // Remove a gadget from the dashboard. HomePageViewPage.gotoAndEdit().removeLastMacro().saveDashboard(); // Wait 2 seconds for the macros to load Thread.sleep(2000); - assertEquals(2,HomePageViewPage.noOfGadgets()); + assertEquals(2, HomePageViewPage.noOfGadgets()); } /** @@ -165,12 +165,12 @@ void checkEditPermissions(XWikiWebDriver driver) throws InterruptedException @Order(4) void checkMacroDescription(XWikiWebDriver driver) throws InterruptedException { - MostViewedMacroViewPages mostViewedMacroViewPages = new MostViewedMacroViewPages(); - mostViewedMacroViewPages.gotoPage(); + MostViewedPagesMacroViewPage mostViewedPagesMacroViewPage = new MostViewedPagesMacroViewPage(); + mostViewedPagesMacroViewPage.gotoPage(); assertEquals("When visitors search on your website, they are looking for a particular page, content, product," + " or service. This report lists the pages that were clicked the most after an internal search.", - mostViewedMacroViewPages.getMacroDescription()); + mostViewedPagesMacroViewPage.getMacroDescription()); } /** @@ -180,8 +180,8 @@ void checkMacroDescription(XWikiWebDriver driver) throws InterruptedException @Order(5) void checkRowEvolutionModal() { - MostViewedMacroViewPages mostViewedMacroViewPages = new MostViewedMacroViewPages(); - BaseModal baseModal = mostViewedMacroViewPages.gotoPage().openRowEvolutionModal(); + MostViewedPagesMacroViewPage mostViewedPagesMacroViewPage = new MostViewedPagesMacroViewPage(); + BaseModal baseModal = mostViewedPagesMacroViewPage.gotoPage().openRowEvolutionModal(); System.out.println("/MODAL/ " + baseModal.isDisplayed() + "/A/A"); assertTrue(baseModal.isDisplayed()); } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java index 64fd4705..d5c6abfc 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java @@ -36,10 +36,11 @@ public class AdminViewPage extends ViewPage private static final String REQUEST_ADDRESS_ID = "Analytics.Code.ConfigurationClass_0_requestAddress"; - private XWikiWebDriver driver; + private final XWikiWebDriver driver; + public AdminViewPage() { - driver = getUtil().getDriver(); + driver = getUtil().getDriver(); } public AdminViewPage gotoAdminPage() @@ -57,7 +58,7 @@ public AdminViewPage setTrackingCode(String value) return this; } - public AdminViewPage setAuthTokenId( String value) + public AdminViewPage setAuthTokenId(String value) { WebElement element = driver.findElement(By.id(AUTH_TOKEN_ID)); element.clear(); @@ -65,7 +66,7 @@ public AdminViewPage setAuthTokenId( String value) return this; } - public AdminViewPage setIdSiteId( String value) + public AdminViewPage setIdSiteId(String value) { WebElement element = driver.findElement(By.id(IS_SITE)); element.clear(); @@ -96,7 +97,7 @@ public void inProgressNotification(String message) public void errorNotification(String message) { - this.waitForNotificationErrorMessage(message); + this.waitForNotificationErrorMessage(message); } public void successNotification(String message) diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageEditPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageEditPage.java index 115bf65b..616a8eef 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageEditPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageEditPage.java @@ -27,35 +27,19 @@ public class HomePageEditPage extends EditPage { - private XWikiWebDriver driver; + private final XWikiWebDriver driver; public HomePageEditPage() { driver = getUtil().getDriver(); } - private HomePageEditPage clickAddGadget() - { - driver.findElement(By.cssSelector(".addgadget")).click(); - return this; - } - - private void waitAndClick(String css) - { - driver.waitUntilElementIsEnabled(driver.findElement(By.cssSelector(css))); - driver.findElement(By.cssSelector(css)).click(); - } - - private void selectMacro(String macroID, String macroName) - { - // Will bring into view the macro that I want to use. - driver.findElement(By.cssSelector(".macro-textFilter")).sendKeys(macroName); - waitAndClick(String.format("li[data-macroid=\"%s\"]", macroID)); - // Will wait until the select button becomes enabled and will click on it. - waitAndClick(".modal.macro-selector-modal.gadget-selector-modal.in .modal-footer .btn-primary"); - // Will wait until the submit button becomes enabled and will click on it. - waitAndClick(".modal.macro-editor-modal.in .modal-footer .btn-primary"); - } + /** + * Adds a new macro to the homepage of the application. + * @param macroID id of the macro + * @param macroName name of the macro + * @return + */ public HomePageEditPage addNewMacro(String macroID, String macroName) { @@ -64,6 +48,11 @@ public HomePageEditPage addNewMacro(String macroID, String macroName) return this; } + /** + * Removes the last macro that is on the page. + * @return + */ + public HomePageEditPage removeLastMacro() { @@ -84,4 +73,27 @@ public HomePageViewPage saveDashboard() editPage.clickSaveAndView(); return new HomePageViewPage(); } + + private HomePageEditPage clickAddGadget() + { + driver.findElement(By.cssSelector(".addgadget")).click(); + return this; + } + + private void waitAndClick(String css) + { + driver.waitUntilElementIsEnabled(driver.findElement(By.cssSelector(css))); + driver.findElement(By.cssSelector(css)).click(); + } + + private void selectMacro(String macroID, String macroName) + { + // Will bring into view the macro that I want to use. + driver.findElement(By.cssSelector(".macro-textFilter")).sendKeys(macroName); + waitAndClick(String.format("li[data-macroid=\"%s\"]", macroID)); + // Will wait until the select button becomes enabled and will click on it. + waitAndClick(".modal.macro-selector-modal.gadget-selector-modal.in .modal-footer .btn-primary"); + // Will wait until the submit button becomes enabled and will click on it. + waitAndClick(".modal.macro-editor-modal.in .modal-footer .btn-primary"); + } } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java index b2e8bb8d..b3e34b52 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java @@ -20,24 +20,20 @@ package com.xwiki.analytics.test.po; import java.util.HashMap; -import java.util.List; import java.util.Map; import org.openqa.selenium.By; -import org.openqa.selenium.WebElement; import org.xwiki.model.reference.DocumentReference; import org.xwiki.test.ui.XWikiWebDriver; -import org.openqa.selenium.interactions.Actions; - import org.xwiki.test.ui.po.ViewPage; public class HomePageViewPage extends ViewPage { - private static XWikiWebDriver driver; + public HomePageViewPage() { - this.driver = getUtil().getDriver(); + driver = getUtil().getDriver(); } public static HomePageViewPage gotoPageHomePage() @@ -46,71 +42,21 @@ public static HomePageViewPage gotoPageHomePage() return new HomePageViewPage(); } - public static HomePageViewPage gotoAndEdit() + public static HomePageEditPage gotoAndEdit() { DocumentReference documentReference = new DocumentReference("xwiki", "Analytics", "WebHome"); Map params = new HashMap<>(); params.put("force", "1"); getUtil().gotoPage(documentReference, "edit", params); - return new HomePageViewPage(); - } - - public static HomePageViewPage addNewMacro(String macroID, String macroName) - { - - clickAddGadget().selectMacro(macroID, macroName); - return new HomePageViewPage(); - } - - private static HomePageViewPage clickAddGadget() - { - driver.findElement(By.cssSelector(".addgadget")).click(); - return new HomePageViewPage(); - } - - private static void waitAndClick(String css) - { - driver.waitUntilElementIsEnabled(driver.findElement(By.cssSelector(css))); - driver.findElement(By.cssSelector(css)).click(); - } - private static void selectMacro(String macroID, String macroName) - { - // Will bring into view the macro that I want to use. - driver.findElement(By.cssSelector(".macro-textFilter")).sendKeys(macroName); - waitAndClick(String.format("li[data-macroid=\"%s\"]", macroID)); - // Will wait until the select button becomes enabled and will click on it. - waitAndClick(".modal.macro-selector-modal.gadget-selector-modal.in .modal-footer .btn-primary"); - // Will wait until the submit button becomes enabled and will click on it. - waitAndClick(".modal.macro-editor-modal.in .modal-footer .btn-primary"); + return new HomePageEditPage(); } + /** + * Calculates the number of gadgets that are present on the homepage of the application. + * @return number of gadgets + */ public static int noOfGadgets() { - return driver.findElements(By.className("gadget")).size(); - } - public static HomePageViewPage saveDashboard() throws InterruptedException - { - - waitAndClick(".bottombuttons.sticky-buttons .btn-primary"); - return new HomePageViewPage(); - } - public static HomePageViewPage removeLastMacro() - { - - WebElement lastGadget = driver.findElement(By.cssSelector(".gadget:last-of-type")); - // Move the cursor to be on top of the macro to reveal the remove button. - new Actions(driver.getWrappedDriver()).moveToElement(lastGadget).perform(); - // Click on the remove button when it becomes available. - waitAndClick(".gadget:last-of-type .remove"); - // Click on the confirm button to remove the macro. - waitAndClick(".xdialog-box.xdialog-box-confirmation .xdialog-content .buttonwrapper"); - return new HomePageViewPage(); + return driver.findElements(By.className("gadget")).size(); } - - public static String getMacroDescription(int id) - { - List elements = driver.findElements(By.cssSelector(".analytics-description")); - return elements.get(id).getAttribute("title"); - } - } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoViewPage.java index 8208de48..65fc8896 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoViewPage.java @@ -20,25 +20,33 @@ package com.xwiki.analytics.test.po; import org.openqa.selenium.By; +import org.xwiki.model.reference.DocumentReference; import org.xwiki.test.ui.XWikiWebDriver; import org.xwiki.test.ui.po.ViewPage; -import static org.junit.Assert.fail; - +/** + * Encompass the process of creating a Matomo token using the browser interface provided by Matomo. + */ public class MatomoViewPage extends ViewPage { - private static final String credentials = "ADMIN1" ; + private static final String credentials = "ADMIN1"; + public MatomoViewPage() { } + + /** + * Creates a new access token by accesing the Matomo GUI. + * @param address The address of the Matomo container. + * @return the newly created token as a string + */ static public String createToken(String address) { XWikiWebDriver driver = getUtil().getDriver(); - getUtil().gotoPage(address); - getUtil().gotoPage( - address+"/index.php?module=UsersManager&action=addNewToken&idSite=1&period=day&date=2023" - + "-09-03"); + getUtil().gotoPage(address); + getUtil().gotoPage( + address + "/index.php?module=UsersManager&action=addNewToken&idSite=1&period=day&date=2023" + "-09-03"); driver.findElement(By.id("login_form_login")).sendKeys(credentials); driver.findElement(By.id("login_form_password")).sendKeys(credentials); @@ -49,9 +57,4 @@ static public String createToken(String address) driver.findElement(By.cssSelector(".btn")).click(); return driver.findElement(By.tagName("code")).getText(); } - - static public void returnToXwiki() - { - getUtil().gotoPage("http://localhost:8080/xwiki/bin/view/Main/"); - } } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPages.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedPagesMacroViewPage.java similarity index 76% rename from application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPages.java rename to application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedPagesMacroViewPage.java index 3a75ed36..9a142c43 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedMacroViewPages.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedPagesMacroViewPage.java @@ -30,26 +30,30 @@ import org.xwiki.test.ui.po.ViewPage; /** - * View for the MostViewedPages macro that let's us interact with the page. + * View for the MostViewedPages macro that lets us interact with the page of the macro. */ -public class MostViewedMacroViewPages extends ViewPage +public class MostViewedPagesMacroViewPage extends ViewPage { - private final XWikiWebDriver driver; - private static final String ROW_EVOLUTION_BUTTON_SELECTOR = ".analyticsRowEvolution"; - public MostViewedMacroViewPages() + private final XWikiWebDriver driver; + + public MostViewedPagesMacroViewPage() { driver = getUtil().getDriver(); } - public MostViewedMacroViewPages gotoPage() + public MostViewedPagesMacroViewPage gotoPage() { List spaces = Arrays.asList("Analytics", "Code", "Macros"); getUtil().gotoPage(spaces, "MostViewedPages", "view", ""); return this; } + /** + * Opens the Row Evolution modal and waits until it is fully visible before returning it. + * @return the modal + */ public BaseModal openRowEvolutionModal() { BaseModal baseModal = new BaseModal(By.cssSelector(".modal-dialog.modal-lg")); @@ -58,10 +62,14 @@ public BaseModal openRowEvolutionModal() return baseModal; } + /*** + * This method first waits for the visibility of an information button, identified by a specific CSS selector. + * Once the information button is visible it selects the tile attribute and returns it. + * @return THe description of a macro. + */ public String getMacroDescription() { driver.waitUntilElementIsVisible(By.cssSelector(".xcontent h2 div a")); return driver.findElement(By.cssSelector(".xcontent h2 div a")).getAttribute("title"); } - } From 59080fff35a4cbd2da3db0d11771fad0afde4931 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Sun, 11 Feb 2024 17:46:46 +0200 Subject: [PATCH 101/105] Updated the parent to 14.10 and added new dependencies to make sure that everything works properly --- .../internal/MostViewedJsonNormaliser.java | 2 +- .../DefaultAnalyticsConfigurationTest.java | 2 +- .../application-analytics-test-docker/pom.xml | 25 ++++++++++++++++++- .../xwiki/analytics/test/ui/AllITs.java | 2 +- .../xwiki/analytics/test/ui/AnalyticsIT.java | 7 +++--- .../analytics/test/ui/config/Config.java | 4 +-- .../it}/resources/config.ini.php | 4 ++- .../analytics/test/po/MatomoViewPage.java | 3 ++- pom.xml | 13 +++++++--- 9 files changed, 47 insertions(+), 15 deletions(-) rename application-analytics-test/application-analytics-test-docker/src/test/it/{ => com}/xwiki/analytics/test/ui/AllITs.java (96%) rename application-analytics-test/application-analytics-test-docker/src/test/it/{ => com}/xwiki/analytics/test/ui/AnalyticsIT.java (98%) rename application-analytics-test/application-analytics-test-docker/src/test/it/{ => com}/xwiki/analytics/test/ui/config/Config.java (95%) rename application-analytics-test/application-analytics-test-docker/src/{main => test/it}/resources/config.ini.php (98%) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index 31f2bd91..aba46c32 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -30,6 +30,7 @@ import javax.inject.Named; import javax.inject.Singleton; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.xwiki.component.annotation.Component; import org.xwiki.resource.CreateResourceReferenceException; import org.xwiki.resource.CreateResourceTypeException; @@ -45,7 +46,6 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.xwiki.analytics.JsonNormaliser; -import liquibase.repackaged.org.apache.commons.lang3.exception.ExceptionUtils; /** * Implementation for {@link JsonNormaliser}. diff --git a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfigurationTest.java b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfigurationTest.java index 7ae13fdd..0d60e710 100644 --- a/application-analytics-default/src/test/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfigurationTest.java +++ b/application-analytics-default/src/test/java/com/xwiki/analytics/internal/configuration/DefaultAnalyticsConfigurationTest.java @@ -80,7 +80,7 @@ void getTrackingCode() assertEquals("TEST", this.defaultAnalyticsConfiguration.getTrackingCode()); } @BeforeEach - private void setLogger() + void setLogger() { ReflectionUtils.setFieldValue(this.defaultAnalyticsConfiguration, "logger", this.logger); } diff --git a/application-analytics-test/application-analytics-test-docker/pom.xml b/application-analytics-test/application-analytics-test-docker/pom.xml index cf1459dd..9007708c 100644 --- a/application-analytics-test/application-analytics-test-docker/pom.xml +++ b/application-analytics-test/application-analytics-test-docker/pom.xml @@ -63,10 +63,33 @@ application-analytics-ui ${project.version} xar - runtime + + + + com.xwiki.licensing + application-licensing-licensor-api + + + + + org.xwiki.platform + xwiki-platform-model-api + ${platform.version} + + + org.xwiki.commons + xwiki-commons-extension-api + ${commons.version} + + + com.google.code.findbugs + jsr305 + 3.0.2 + + org.xwiki.platform xwiki-platform-test-docker diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AllITs.java b/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AllITs.java similarity index 96% rename from application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AllITs.java rename to application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AllITs.java index e3198e55..62c25ea7 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AllITs.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AllITs.java @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package xwiki.analytics.test.ui; +package com.xwiki.analytics.test.ui; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AnalyticsIT.java similarity index 98% rename from application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java rename to application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AnalyticsIT.java index 56fa2dde..3f245514 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AnalyticsIT.java @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package xwiki.analytics.test.ui; +package com.xwiki.analytics.test.ui; import java.util.Collections; @@ -39,8 +39,7 @@ import com.xwiki.analytics.test.po.HomePageViewPage; import com.xwiki.analytics.test.po.MatomoViewPage; import com.xwiki.analytics.test.po.MostViewedPagesMacroViewPage; - -import xwiki.analytics.test.ui.config.Config; +import com.xwiki.analytics.test.ui.config.Config; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -216,7 +215,7 @@ private GenericContainer startMatomo(TestConfiguration testConfiguration, Generi GenericContainer matomoContainer = new GenericContainer<>(Config.MATOMO_CONTAINER_NAME).withExposedPorts(80) .withEnv("MATOMO_DATABASE_HOST", Config.ADDRESS + ":" + dbContainer.getMappedPort(Config.DB_CONTAINER_EXPOSED_PORT)) - .withFileSystemBind("src/main/resources/config.ini.php", Config.MATOMO_CONFIG_FILE_PATH); + .withFileSystemBind("src/test/it/resources/config.ini.php", Config.MATOMO_CONFIG_FILE_PATH); matomoContainer.setPortBindings(Collections.singletonList(String.format("%d:80", Config.MATOMO_BRIDGE_PORT))); DockerTestUtils.startContainer(matomoContainer, testConfiguration); return matomoContainer; diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java b/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/config/Config.java similarity index 95% rename from application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java rename to application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/config/Config.java index 7d8752c9..4c22c49a 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/xwiki/analytics/test/ui/config/Config.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/config/Config.java @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package xwiki.analytics.test.ui.config; +package com.xwiki.analytics.test.ui.config; public class Config { @@ -35,7 +35,7 @@ public class Config public static final String ADDRESS = "172.17.0.1"; - public static final String MATOMO_CONTAINER_NAME = "matomo:latest"; + public static final String MATOMO_CONTAINER_NAME = "matomo:4.15.1"; public static final String MATOMO_CONFIG_FILE_PATH = "/var/www/html/config/config.ini.php"; diff --git a/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php b/application-analytics-test/application-analytics-test-docker/src/test/it/resources/config.ini.php similarity index 98% rename from application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php rename to application-analytics-test/application-analytics-test-docker/src/test/it/resources/config.ini.php index e95e676b..bec34296 100644 --- a/application-analytics-test/application-analytics-test-docker/src/main/resources/config.ini.php +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/resources/config.ini.php @@ -10,10 +10,10 @@ charset = "utf8mb4" [General] +enable_trusted_host_check = 0 salt = "221e020a358ad259431e9ec76ba8a941" trusted_hosts[] = "localhost" trusted_hosts[] = "localhost:9999" -enable_trusted_host_check = 0 debug = 1 [PluginsInstalled] @@ -80,3 +80,5 @@ PluginsInstalled[] = "Tour" PluginsInstalled[] = "PagePerformance" PluginsInstalled[] = "CustomDimensions" +PluginsInstalled[] = "JsTrackerInstallCheck" + diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoViewPage.java index 65fc8896..cdd52f3d 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoViewPage.java @@ -24,6 +24,8 @@ import org.xwiki.test.ui.XWikiWebDriver; import org.xwiki.test.ui.po.ViewPage; +import static org.junit.Assert.fail; + /** * Encompass the process of creating a Matomo token using the browser interface provided by Matomo. */ @@ -47,7 +49,6 @@ static public String createToken(String address) getUtil().gotoPage(address); getUtil().gotoPage( address + "/index.php?module=UsersManager&action=addNewToken&idSite=1&period=day&date=2023" + "-09-03"); - driver.findElement(By.id("login_form_login")).sendKeys(credentials); driver.findElement(By.id("login_form_password")).sendKeys(credentials); driver.findElement(By.id("login_form_submit")).click(); diff --git a/pom.xml b/pom.xml index 57de3ec7..90cf1518 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ com.xwiki.parent xwikisas-parent-platform - 13.10-3 + 14.10-1 com.xwiki.analytics application-analytics @@ -52,6 +52,13 @@ application-analytics-api application-analytics-default application-analytics-ui - application-analytics-test - \ No newline at end of file + + + integration-tests + + application-analytics-test + + + + From c0bc012e6dab028e5dcd4e13108a660bd33053b4 Mon Sep 17 00:00:00 2001 From: Farcasut Date: Mon, 12 Feb 2024 13:48:28 +0200 Subject: [PATCH 102/105] Renamed classes and methods, added a class to a html element to make it easier to select it. --- application-analytics-default/pom.xml | 4 + .../application-analytics-test-docker/pom.xml | 14 +-- .../xwiki/analytics/test/ui/AnalyticsIT.java | 79 +++++++-------- .../pom.xml | 9 +- ...> AnalyticsAdministrationSectionPage.java} | 33 ++----- .../analytics/test/po/AnalyticsEditPage.java | 85 ++++++++++++++++ ...geViewPage.java => AnalyticsViewPage.java} | 31 ++---- .../analytics/test/po/HomePageEditPage.java | 99 ------------------- .../test/po/MostViewedPagesMacroViewPage.java | 11 +-- application-analytics-test/pom.xml | 10 +- .../resources/Analytics/Code/AnalyticsJS.xml | 2 +- .../Analytics/Code/Macros/VelocityMacros.xml | 2 +- 12 files changed, 170 insertions(+), 209 deletions(-) rename application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/{AdminViewPage.java => AnalyticsAdministrationSectionPage.java} (77%) create mode 100644 application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsEditPage.java rename application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/{HomePageViewPage.java => AnalyticsViewPage.java} (59%) delete mode 100644 application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageEditPage.java diff --git a/application-analytics-default/pom.xml b/application-analytics-default/pom.xml index 1a705fde..5871fe9e 100644 --- a/application-analytics-default/pom.xml +++ b/application-analytics-default/pom.xml @@ -66,6 +66,10 @@ jersey-client 2.30.1 + + org.apache.commons + commons-lang3 + diff --git a/application-analytics-test/application-analytics-test-docker/pom.xml b/application-analytics-test/application-analytics-test-docker/pom.xml index 9007708c..1128d49d 100644 --- a/application-analytics-test/application-analytics-test-docker/pom.xml +++ b/application-analytics-test/application-analytics-test-docker/pom.xml @@ -45,19 +45,13 @@ ${licensing.version} runtime - + org.xwiki.platform xwiki-platform-menu-ui ${platform.version} xar - - com.xwiki.analytics - application-analytics-test-pageobjects - 1.0-SNAPSHOT - test - com.xwiki.analytics application-analytics-ui @@ -74,6 +68,12 @@ + + com.xwiki.analytics + application-analytics-test-pageobjects + ${project.version} + test + org.xwiki.platform xwiki-platform-model-api diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AnalyticsIT.java index 3f245514..0294b1d2 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AnalyticsIT.java @@ -24,7 +24,8 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.testcontainers.containers.Container; +import org.openqa.selenium.By; +import org.openqa.selenium.support.ui.ExpectedConditions; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.utility.DockerImageName; @@ -35,17 +36,17 @@ import org.xwiki.test.ui.XWikiWebDriver; import org.xwiki.test.ui.po.BaseModal; -import com.xwiki.analytics.test.po.AdminViewPage; -import com.xwiki.analytics.test.po.HomePageViewPage; +import com.xwiki.analytics.test.po.AnalyticsAdministrationSectionPage; +import com.xwiki.analytics.test.po.AnalyticsEditPage; +import com.xwiki.analytics.test.po.AnalyticsViewPage; import com.xwiki.analytics.test.po.MatomoViewPage; import com.xwiki.analytics.test.po.MostViewedPagesMacroViewPage; import com.xwiki.analytics.test.ui.config.Config; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; - @UITest -public class AnalyticsIT +class AnalyticsIT { /** * Creates the Admin user @@ -53,9 +54,11 @@ public class AnalyticsIT public void setupUsers(TestUtils testUtils) { testUtils.loginAsSuperAdmin(); + //TODO remove this line when upgrading the parent to >=15.10 because it was added to createAdminUser function testUtils.setGlobalRights("XWiki.XWikiAdminGroup", "", "admin", true); testUtils.createAdminUser(); testUtils.loginAsAdmin(); + } /** @@ -66,7 +69,8 @@ public void setupContainers(TestConfiguration testConfiguration) throws Exceptio { GenericContainer sqlContainer = startDb(testConfiguration); GenericContainer matomoContainer = startMatomo(testConfiguration, sqlContainer); - Container.ExecResult result = matomoContainer.execInContainer("sh", "-c", + // Modify the name of the matomo.js to make sure that the browser doesn't block it. + matomoContainer.execInContainer("sh", "-c", "grep -rl 'matomo.js' /var/www/html/ | xargs -d '\\n' -I {} sed -i 's/matomo.js/36011373.js/g' \"{}\""); matomoContainer.execInContainer("sh", "-c", "mv /var/www/html/matomo.js /var/www/html/36011373.js"); Config.MATOMO_AUTH_TOKEN = @@ -74,7 +78,7 @@ public void setupContainers(TestConfiguration testConfiguration) throws Exceptio } /** - * Import the platform.html.head UIExtension point to make the tracking code work in the test environmnet. + * Import the platform.html.head UIExtension point to make the tracking code work in the test environment. */ private void setupUIExtension(TestUtils testUtils) throws Exception { @@ -90,32 +94,31 @@ void setup(TestConfiguration testConfiguration, TestUtils testUtils) throws Exce setupContainers(testConfiguration); setupUIExtension(testUtils); setupUsers(testUtils); - HomePageViewPage.gotoPageHomePage(); + AnalyticsViewPage.gotoPage(); } /** - * The function checks that the "Save" button displays the correct messages when the configurations provided by the + * Check that the Save button displays the correct messages when the configurations provided by the * users are incorrect. */ @Test @Order(1) void checkWrongConfigs(XWikiWebDriver driver) throws InterruptedException { - AdminViewPage adminViewPage = new AdminViewPage(); - adminViewPage.gotoAdminPage(); - System.out.println("/q/q " + Config.MATOMO_AUTH_TOKEN + " /q/q"); - adminViewPage.setTrackingCode("").setAuthTokenId(Config.MATOMO_AUTH_TOKEN).setIdSiteId("1") - .setRequestAddressId(Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT).bringSaveButtonIntoView(); - - adminViewPage.inProgressNotification("Saving..."); - adminViewPage.successNotification("Saved"); - adminViewPage.inProgressNotification("Checking connection to Matomo."); - adminViewPage.errorNotification("Failed to connect to Matomo. Please check your configuration " + "values."); - HomePageViewPage.gotoPageHomePage(); + AnalyticsAdministrationSectionPage analyticsConfigViewPage = new AnalyticsAdministrationSectionPage(); + analyticsConfigViewPage.gotoPage(); + analyticsConfigViewPage.setTrackingCode("").setAuthTokenId(Config.MATOMO_AUTH_TOKEN).setIdSiteId("1") + .setRequestAddressId(Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT).saveConfigs(); + + analyticsConfigViewPage.waitForNotificationInProgressMessage("Saving..."); + analyticsConfigViewPage.waitForNotificationSuccessMessage("Saved"); + analyticsConfigViewPage.waitForNotificationInProgressMessage("Checking connection to Matomo."); + analyticsConfigViewPage.waitForNotificationErrorMessage("Failed to connect to Matomo. Please check your configuration " + "values."); + AnalyticsViewPage.gotoPage(); } /** - * The function checks that the "Save" button displays the correct messages when the configurations provided by the + * Check that the Save button displays the correct messages when the configurations provided by the * users are correct. */ @Test @@ -123,17 +126,17 @@ void checkWrongConfigs(XWikiWebDriver driver) throws InterruptedException void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException { - AdminViewPage adminViewPage = new AdminViewPage(); + AnalyticsAdministrationSectionPage analyticsConfigViewPage = new AnalyticsAdministrationSectionPage(); - adminViewPage.gotoAdminPage(); - adminViewPage.setTrackingCode(Config.getTrackingCode()).setAuthTokenId(Config.MATOMO_AUTH_TOKEN) + analyticsConfigViewPage.gotoPage(); + analyticsConfigViewPage.setTrackingCode(Config.getTrackingCode()).setAuthTokenId(Config.MATOMO_AUTH_TOKEN) .setIdSiteId("1").setRequestAddressId("http://" + Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT + "/") - .bringSaveButtonIntoView(); - adminViewPage.inProgressNotification("Saving..."); - adminViewPage.successNotification("Saved"); - adminViewPage.inProgressNotification("Checking connection to Matomo."); - adminViewPage.successNotification("Test connection succeeded!"); - HomePageViewPage.gotoPageHomePage(); + .saveConfigs(); + analyticsConfigViewPage.waitForNotificationInProgressMessage("Saving..."); + analyticsConfigViewPage.waitForNotificationSuccessMessage("Saved"); + analyticsConfigViewPage.waitForNotificationInProgressMessage("Checking connection to Matomo."); + analyticsConfigViewPage.waitForNotificationSuccessMessage("Test connection succeeded!"); + AnalyticsViewPage.gotoPage(); } /** @@ -144,17 +147,12 @@ void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException void checkEditPermissions(XWikiWebDriver driver) throws InterruptedException { - HomePageViewPage.gotoPageHomePage(); + AnalyticsViewPage analyticsViewPage = AnalyticsViewPage.gotoPage(); // Add a gadget to the dashboard. - HomePageViewPage.gotoAndEdit().addNewMacro("searchCategories", "Search Categories").saveDashboard(); - // Wait 2 seconds for the macros to load - Thread.sleep(2000); - assertEquals(3, HomePageViewPage.noOfGadgets()); - // Remove a gadget from the dashboard. - HomePageViewPage.gotoAndEdit().removeLastMacro().saveDashboard(); - // Wait 2 seconds for the macros to load - Thread.sleep(2000); - assertEquals(2, HomePageViewPage.noOfGadgets()); + AnalyticsEditPage.gotoPage().addNewMacro("Search Categories").saveDashboard(); + driver.waitUntilCondition(ExpectedConditions.numberOfElementsToBeMoreThan(By.cssSelector(".gadget-content"), + 2)); + assertEquals(3, analyticsViewPage.getGadgetCount()); } /** @@ -181,7 +179,6 @@ void checkRowEvolutionModal() { MostViewedPagesMacroViewPage mostViewedPagesMacroViewPage = new MostViewedPagesMacroViewPage(); BaseModal baseModal = mostViewedPagesMacroViewPage.gotoPage().openRowEvolutionModal(); - System.out.println("/MODAL/ " + baseModal.isDisplayed() + "/A/A"); assertTrue(baseModal.isDisplayed()); } diff --git a/application-analytics-test/application-analytics-test-pageobjects/pom.xml b/application-analytics-test/application-analytics-test-pageobjects/pom.xml index b7ba5463..73d42e0a 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/pom.xml +++ b/application-analytics-test/application-analytics-test-pageobjects/pom.xml @@ -20,8 +20,7 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - + 4.0.0 com.xwiki.analytics @@ -43,5 +42,11 @@ xwiki-platform-administration-test-pageobjects ${platform.version} + + org.xwiki.platform + xwiki-platform-ckeditor-test-pageobjects + ${platform.version} + jar + \ No newline at end of file diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsAdministrationSectionPage.java similarity index 77% rename from application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java rename to application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsAdministrationSectionPage.java index d5c6abfc..455cb7db 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AdminViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsAdministrationSectionPage.java @@ -26,7 +26,7 @@ import org.xwiki.test.ui.XWikiWebDriver; import org.xwiki.test.ui.po.ViewPage; -public class AdminViewPage extends ViewPage +public class AnalyticsAdministrationSectionPage extends ViewPage { private static final String TRACKING_CODE_ID = "Analytics.Code.ConfigurationClass_0_trackingCode"; @@ -38,19 +38,19 @@ public class AdminViewPage extends ViewPage private final XWikiWebDriver driver; - public AdminViewPage() + public AnalyticsAdministrationSectionPage() { driver = getUtil().getDriver(); } - public AdminViewPage gotoAdminPage() + public AnalyticsAdministrationSectionPage gotoPage() { AdministrationPage administrationPage = AdministrationPage.gotoPage(); administrationPage.clickSection("Other", "Analytics"); - return new AdminViewPage(); + return new AnalyticsAdministrationSectionPage(); } - public AdminViewPage setTrackingCode(String value) + public AnalyticsAdministrationSectionPage setTrackingCode(String value) { WebElement element = driver.findElement(By.id(TRACKING_CODE_ID)); element.clear(); @@ -58,7 +58,7 @@ public AdminViewPage setTrackingCode(String value) return this; } - public AdminViewPage setAuthTokenId(String value) + public AnalyticsAdministrationSectionPage setAuthTokenId(String value) { WebElement element = driver.findElement(By.id(AUTH_TOKEN_ID)); element.clear(); @@ -66,7 +66,7 @@ public AdminViewPage setAuthTokenId(String value) return this; } - public AdminViewPage setIdSiteId(String value) + public AnalyticsAdministrationSectionPage setIdSiteId(String value) { WebElement element = driver.findElement(By.id(IS_SITE)); element.clear(); @@ -74,7 +74,7 @@ public AdminViewPage setIdSiteId(String value) return this; } - public AdminViewPage setRequestAddressId(String value) + public AnalyticsAdministrationSectionPage setRequestAddressId(String value) { WebElement element = driver.findElement(By.id(REQUEST_ADDRESS_ID)); element.clear(); @@ -82,26 +82,11 @@ public AdminViewPage setRequestAddressId(String value) return this; } - public AdminViewPage bringSaveButtonIntoView() + public AnalyticsAdministrationSectionPage saveConfigs() { WebElement saveButton = driver.findElement(By.cssSelector(".btn.btn-primary")); Actions actions = driver.createActions(); actions.moveToElement(saveButton).click().perform(); return this; } - - public void inProgressNotification(String message) - { - this.waitForNotificationInProgressMessage(message); - } - - public void errorNotification(String message) - { - this.waitForNotificationErrorMessage(message); - } - - public void successNotification(String message) - { - this.waitForNotificationSuccessMessage(message); - } } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsEditPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsEditPage.java new file mode 100644 index 00000000..7ec567f7 --- /dev/null +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsEditPage.java @@ -0,0 +1,85 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.analytics.test.po; + +import java.util.HashMap; +import java.util.Map; + +import org.openqa.selenium.By; +import org.xwiki.ckeditor.test.po.MacroDialogSelectModal; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.test.ui.po.editor.EditPage; +public class AnalyticsEditPage extends EditPage +{ + + public AnalyticsEditPage(){} + + /** + * Adds a new macro to the homepage of the application. + * + * @param macroName name of the macro + * @return + */ + + public AnalyticsEditPage addNewMacro(String macroName) + { + + this.clickAddGadget().selectMacro(macroName); + return this; + } + public static AnalyticsEditPage gotoPage() + { + DocumentReference documentReference = new DocumentReference("xwiki", "Analytics", "WebHome"); + Map params = new HashMap<>(); + params.put("force", "1"); + getUtil().gotoPage(documentReference, "edit", params); + return new AnalyticsEditPage(); + } + public AnalyticsViewPage saveDashboard() + { + this.clickSaveAndView(); + return new AnalyticsViewPage(); + } + + private AnalyticsEditPage clickAddGadget() + { + getUtil().getDriver().findElement(By.cssSelector(".addgadget")).click(); + return this; + } + + private void waitAndClick(String css) + { + getUtil().getDriver().waitUntilElementIsEnabled( getUtil().getDriver().findElement(By.cssSelector(css))); + getUtil().getDriver().findElement(By.cssSelector(css)).click(); + } + + private void selectMacro(String macroName) + { + MacroDialogSelectModal macroDialogSelectModal = new MacroDialogSelectModal(); + // Will bring into view the macro that I want to use. + getUtil().getDriver().findElement(By.cssSelector(".macro-textFilter")).sendKeys(macroName); + macroDialogSelectModal.filterByText(macroName, 1); + macroDialogSelectModal.getFirstMacro(); + macroDialogSelectModal.clickSelect(); + // Right now the MacroDialogSelectModal doesn't have a method to press submit. + waitAndClick(".modal.macro-editor-modal.in .modal-footer .btn-primary"); + + } +} diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsViewPage.java similarity index 59% rename from application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java rename to application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsViewPage.java index b3e34b52..2a825d2e 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsViewPage.java @@ -19,44 +19,29 @@ */ package com.xwiki.analytics.test.po; -import java.util.HashMap; -import java.util.Map; - import org.openqa.selenium.By; -import org.xwiki.model.reference.DocumentReference; -import org.xwiki.test.ui.XWikiWebDriver; import org.xwiki.test.ui.po.ViewPage; -public class HomePageViewPage extends ViewPage +public class AnalyticsViewPage extends ViewPage { - private static XWikiWebDriver driver; - public HomePageViewPage() - { - driver = getUtil().getDriver(); - } - public static HomePageViewPage gotoPageHomePage() + public AnalyticsViewPage() { - getUtil().gotoPage("Analytics", "WebHome"); - return new HomePageViewPage(); + } - public static HomePageEditPage gotoAndEdit() + public static AnalyticsViewPage gotoPage() { - DocumentReference documentReference = new DocumentReference("xwiki", "Analytics", "WebHome"); - Map params = new HashMap<>(); - params.put("force", "1"); - getUtil().gotoPage(documentReference, "edit", params); - return new HomePageEditPage(); + getUtil().gotoPage("Analytics", "WebHome"); + return new AnalyticsViewPage(); } - /** * Calculates the number of gadgets that are present on the homepage of the application. * @return number of gadgets */ - public static int noOfGadgets() + public int getGadgetCount() { - return driver.findElements(By.className("gadget")).size(); + return getUtil().getDriver().findElements(By.className("gadget")).size(); } } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageEditPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageEditPage.java deleted file mode 100644 index 616a8eef..00000000 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/HomePageEditPage.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * See the NOTICE file distributed with this work for additional - * information regarding copyright ownership. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package com.xwiki.analytics.test.po; - -import org.openqa.selenium.By; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.interactions.Actions; -import org.xwiki.test.ui.XWikiWebDriver; -import org.xwiki.test.ui.po.editor.EditPage; - -public class HomePageEditPage extends EditPage -{ - private final XWikiWebDriver driver; - - public HomePageEditPage() - { - driver = getUtil().getDriver(); - } - - /** - * Adds a new macro to the homepage of the application. - * @param macroID id of the macro - * @param macroName name of the macro - * @return - */ - - public HomePageEditPage addNewMacro(String macroID, String macroName) - { - - this.clickAddGadget().selectMacro(macroID, macroName); - return this; - } - - /** - * Removes the last macro that is on the page. - * @return - */ - - public HomePageEditPage removeLastMacro() - { - - WebElement lastGadget = driver.findElement(By.cssSelector(".gadget:last-of-type")); - // Move the cursor to be on top of the macro to reveal the remove button. - new Actions(driver.getWrappedDriver()).moveToElement(lastGadget).perform(); - // Click on the remove button when it becomes available. - waitAndClick(".gadget:last-of-type .remove"); - // Click on the confirm button to remove the macro. - waitAndClick(".xdialog-box.xdialog-box-confirmation .xdialog-content .buttonwrapper"); - return this; - } - - public HomePageViewPage saveDashboard() - { - - EditPage editPage = new EditPage(); - editPage.clickSaveAndView(); - return new HomePageViewPage(); - } - - private HomePageEditPage clickAddGadget() - { - driver.findElement(By.cssSelector(".addgadget")).click(); - return this; - } - - private void waitAndClick(String css) - { - driver.waitUntilElementIsEnabled(driver.findElement(By.cssSelector(css))); - driver.findElement(By.cssSelector(css)).click(); - } - - private void selectMacro(String macroID, String macroName) - { - // Will bring into view the macro that I want to use. - driver.findElement(By.cssSelector(".macro-textFilter")).sendKeys(macroName); - waitAndClick(String.format("li[data-macroid=\"%s\"]", macroID)); - // Will wait until the select button becomes enabled and will click on it. - waitAndClick(".modal.macro-selector-modal.gadget-selector-modal.in .modal-footer .btn-primary"); - // Will wait until the submit button becomes enabled and will click on it. - waitAndClick(".modal.macro-editor-modal.in .modal-footer .btn-primary"); - } -} diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedPagesMacroViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedPagesMacroViewPage.java index 9a142c43..58f52797 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedPagesMacroViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedPagesMacroViewPage.java @@ -25,7 +25,6 @@ import java.util.List; import org.openqa.selenium.By; -import org.xwiki.test.ui.XWikiWebDriver; import org.xwiki.test.ui.po.BaseModal; import org.xwiki.test.ui.po.ViewPage; @@ -36,11 +35,9 @@ public class MostViewedPagesMacroViewPage extends ViewPage { private static final String ROW_EVOLUTION_BUTTON_SELECTOR = ".analyticsRowEvolution"; - private final XWikiWebDriver driver; public MostViewedPagesMacroViewPage() { - driver = getUtil().getDriver(); } public MostViewedPagesMacroViewPage gotoPage() @@ -57,8 +54,8 @@ public MostViewedPagesMacroViewPage gotoPage() public BaseModal openRowEvolutionModal() { BaseModal baseModal = new BaseModal(By.cssSelector(".modal-dialog.modal-lg")); - driver.waitUntilElementIsVisible(By.cssSelector(ROW_EVOLUTION_BUTTON_SELECTOR)); - driver.findElement(By.cssSelector(ROW_EVOLUTION_BUTTON_SELECTOR)).click(); + getUtil().getDriver().waitUntilElementIsVisible(By.cssSelector(ROW_EVOLUTION_BUTTON_SELECTOR)); + getUtil().getDriver().findElement(By.cssSelector(ROW_EVOLUTION_BUTTON_SELECTOR)).click(); return baseModal; } @@ -69,7 +66,7 @@ public BaseModal openRowEvolutionModal() */ public String getMacroDescription() { - driver.waitUntilElementIsVisible(By.cssSelector(".xcontent h2 div a")); - return driver.findElement(By.cssSelector(".xcontent h2 div a")).getAttribute("title"); + getUtil().getDriver().waitUntilElementIsVisible(By.cssSelector(".analyticsDescription")); + return getUtil().getDriver().findElement(By.cssSelector(".analyticsDescription")).getAttribute("title"); } } diff --git a/application-analytics-test/pom.xml b/application-analytics-test/pom.xml index e5c69030..fb14978e 100644 --- a/application-analytics-test/pom.xml +++ b/application-analytics-test/pom.xml @@ -33,19 +33,21 @@ Application Analytics (PRO)- Tests - Parent POM pom Application Analytics (PRO) - Tests - Parent POM - + true true true - + + application-analytics-test-pageobjects + + docker - + application-analytics-test-docker - application-analytics-test-pageobjects diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsJS.xml b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsJS.xml index 1e8de365..071ba1ce 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsJS.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsJS.xml @@ -132,7 +132,7 @@ $('[id^="gadget_"]').each(function () { let hiddenElement = $(this).find('.gadget-content .analytics-description'); if (hiddenElement.length > 0) { - let dataElement = $(`<a class="${icon.cssClass} analyticsTitleMargin"></a>`); + let dataElement = $(`<a class="${icon.cssClass} analyticsTitleMargin analyticsDescription"></a>`); dataElement.attr('title', hiddenElement.data('description')); dataElement.attr('href', hiddenElement.data('url')); $(this).find('.gadget-title').append(dataElement); diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index e63ea13d..966bb966 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -203,7 +203,7 @@ #set ($discard = $xwiki.jsx.use('Analytics.Code.AnalyticsJS')) #else {{html clean="false"}} - <h2><div><span>$escapetool.xml($services.localization.render($nameTranslationKey))</span> <a title="$description" href=$url>$services.icon.renderHTML('info')</a><div></h2> + <h2><div><span>$escapetool.xml($services.localization.render($nameTranslationKey))</span> <a class="analyticsDescription" title="$description" href=$url>$services.icon.renderHTML('info')</a><div></h2> {{/html}} #end #end From f5add7f70d896866bcfdba750b28d467fa57116b Mon Sep 17 00:00:00 2001 From: farcasut Date: Thu, 15 Feb 2024 11:26:04 +0200 Subject: [PATCH 103/105] Create functional tests #45 Added a FindBy to select the elements in the config tab Removed unnecessary imports Renamed classes Modified the URL of the rowevolution request to have the get action insted of the view --- .../xwiki/analytics/test/ui/AnalyticsIT.java | 114 ++++++++---------- .../analytics/test/ui/config/Config.java | 2 +- .../AnalyticsAdministrationSectionPage.java | 54 ++++++--- .../analytics/test/po/AnalyticsEditPage.java | 4 + .../analytics/test/po/AnalyticsViewPage.java | 4 +- .../analytics/test/po/MatomoTestUtils.java | 66 ++++++++++ .../analytics/test/po/MatomoViewPage.java | 61 ---------- ...wPage.java => MostViewedPagesElement.java} | 35 ++---- .../analytics/test/po/RowEvolutionModal.java | 39 ++++++ .../resources/Analytics/Code/AnalyticsJS.xml | 2 +- .../Analytics/Code/Macros/RowEvolution.xml | 3 +- .../Code/Macros/RowEvolutionJSON.xml | 2 +- .../Analytics/Code/Macros/VelocityMacros.xml | 2 +- 13 files changed, 211 insertions(+), 177 deletions(-) create mode 100644 application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTestUtils.java delete mode 100644 application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoViewPage.java rename application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/{MostViewedPagesMacroViewPage.java => MostViewedPagesElement.java} (53%) create mode 100644 application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/RowEvolutionModal.java diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AnalyticsIT.java index 0294b1d2..a3fd82cd 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AnalyticsIT.java @@ -21,6 +21,7 @@ import java.util.Collections; +import com.xwiki.analytics.test.po.*; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -34,25 +35,18 @@ import org.xwiki.test.docker.junit5.UITest; import org.xwiki.test.ui.TestUtils; import org.xwiki.test.ui.XWikiWebDriver; -import org.xwiki.test.ui.po.BaseModal; -import com.xwiki.analytics.test.po.AnalyticsAdministrationSectionPage; -import com.xwiki.analytics.test.po.AnalyticsEditPage; -import com.xwiki.analytics.test.po.AnalyticsViewPage; -import com.xwiki.analytics.test.po.MatomoViewPage; -import com.xwiki.analytics.test.po.MostViewedPagesMacroViewPage; import com.xwiki.analytics.test.ui.config.Config; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; + @UITest -class AnalyticsIT -{ +class AnalyticsIT { /** * Creates the Admin user */ - public void setupUsers(TestUtils testUtils) - { + public void setupUsers(TestUtils testUtils) { testUtils.loginAsSuperAdmin(); //TODO remove this line when upgrading the parent to >=15.10 because it was added to createAdminUser function testUtils.setGlobalRights("XWiki.XWikiAdminGroup", "", "admin", true); @@ -65,33 +59,30 @@ public void setupUsers(TestUtils testUtils) * Start the matomo and sql containers, rename the matomo.js to a random int to force the browser to load the * tracking script without explicit settings and generate a new auth token for matomo to be used in the tests. */ - public void setupContainers(TestConfiguration testConfiguration) throws Exception - { + public void setupContainers(XWikiWebDriver driver, TestConfiguration testConfiguration) throws Exception { GenericContainer sqlContainer = startDb(testConfiguration); GenericContainer matomoContainer = startMatomo(testConfiguration, sqlContainer); // Modify the name of the matomo.js to make sure that the browser doesn't block it. matomoContainer.execInContainer("sh", "-c", - "grep -rl 'matomo.js' /var/www/html/ | xargs -d '\\n' -I {} sed -i 's/matomo.js/36011373.js/g' \"{}\""); + "grep -rl 'matomo.js' /var/www/html/ | xargs -d '\\n' -I {} sed -i 's/matomo.js/36011373.js/g' \"{}\""); matomoContainer.execInContainer("sh", "-c", "mv /var/www/html/matomo.js /var/www/html/36011373.js"); Config.MATOMO_AUTH_TOKEN = - MatomoViewPage.createToken("http://" + Config.ADDRESS + ":" + matomoContainer.getMappedPort(80)); + MatomoTestUtils.createToken("http://" + Config.ADDRESS + ":" + matomoContainer.getMappedPort(80), driver); } /** * Import the platform.html.head UIExtension point to make the tracking code work in the test environment. */ - private void setupUIExtension(TestUtils testUtils) throws Exception - { + private void setupUIExtension(TestUtils testUtils) throws Exception { testUtils.setWikiPreference("meta", - "#foreach($uix in $services.uix.getExtensions(\"org.xwiki.platform.html.head\"," - + " {'sortByParameter' : 'order'}))\n" + " $services.rendering.render($uix.execute(), 'xhtml/1.0')\n" - + "#end"); + "#foreach($uix in $services.uix.getExtensions(\"org.xwiki.platform.html.head\"," + + " {'sortByParameter' : 'order'}))\n" + " $services.rendering.render($uix.execute(), 'xhtml/1.0')\n" + + "#end"); } @BeforeAll - void setup(TestConfiguration testConfiguration, TestUtils testUtils) throws Exception - { - setupContainers(testConfiguration); + void setup(XWikiWebDriver driver, TestConfiguration testConfiguration, TestUtils testUtils) throws Exception { + setupContainers(driver, testConfiguration); setupUIExtension(testUtils); setupUsers(testUtils); AnalyticsViewPage.gotoPage(); @@ -103,12 +94,11 @@ void setup(TestConfiguration testConfiguration, TestUtils testUtils) throws Exce */ @Test @Order(1) - void checkWrongConfigs(XWikiWebDriver driver) throws InterruptedException - { + void checkWrongConfigs(XWikiWebDriver driver) throws InterruptedException { AnalyticsAdministrationSectionPage analyticsConfigViewPage = new AnalyticsAdministrationSectionPage(); analyticsConfigViewPage.gotoPage(); analyticsConfigViewPage.setTrackingCode("").setAuthTokenId(Config.MATOMO_AUTH_TOKEN).setIdSiteId("1") - .setRequestAddressId(Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT).saveConfigs(); + .setRequestAddressId(Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT).saveConfigs(); analyticsConfigViewPage.waitForNotificationInProgressMessage("Saving..."); analyticsConfigViewPage.waitForNotificationSuccessMessage("Saved"); @@ -123,15 +113,14 @@ void checkWrongConfigs(XWikiWebDriver driver) throws InterruptedException */ @Test @Order(2) - void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException - { + void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException { AnalyticsAdministrationSectionPage analyticsConfigViewPage = new AnalyticsAdministrationSectionPage(); analyticsConfigViewPage.gotoPage(); analyticsConfigViewPage.setTrackingCode(Config.getTrackingCode()).setAuthTokenId(Config.MATOMO_AUTH_TOKEN) - .setIdSiteId("1").setRequestAddressId("http://" + Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT + "/") - .saveConfigs(); + .setIdSiteId("1").setRequestAddressId("http://" + Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT + "/") + .saveConfigs(); analyticsConfigViewPage.waitForNotificationInProgressMessage("Saving..."); analyticsConfigViewPage.waitForNotificationSuccessMessage("Saved"); analyticsConfigViewPage.waitForNotificationInProgressMessage("Checking connection to Matomo."); @@ -139,63 +128,61 @@ void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException AnalyticsViewPage.gotoPage(); } + + /** + * Checks that the description is loaded properly for a macro. + */ + @Test + @Order(3) + void checkMacroDescription() throws InterruptedException { + AnalyticsViewPage.gotoPage(); + MostViewedPagesElement mostViewedPagesELement = new MostViewedPagesElement("gadget_1"); + assertEquals("When visitors search on your website, they are looking for a particular page, content, product," + + " or service. This report lists the pages that were clicked the most after an internal search.", + mostViewedPagesELement.getMacroDescription()); + } + + /** * Checks if the admin has edit permissions in the home page of the application. */ @Test @Order(3) - void checkEditPermissions(XWikiWebDriver driver) throws InterruptedException - { + void checkEditPermissions(XWikiWebDriver driver) throws InterruptedException { AnalyticsViewPage analyticsViewPage = AnalyticsViewPage.gotoPage(); // Add a gadget to the dashboard. AnalyticsEditPage.gotoPage().addNewMacro("Search Categories").saveDashboard(); driver.waitUntilCondition(ExpectedConditions.numberOfElementsToBeMoreThan(By.cssSelector(".gadget-content"), - 2)); + 2)); assertEquals(3, analyticsViewPage.getGadgetCount()); } - /** - * Checks that the description is loaded properly for a macro. - */ - @Test - @Order(4) - void checkMacroDescription(XWikiWebDriver driver) throws InterruptedException - { - MostViewedPagesMacroViewPage mostViewedPagesMacroViewPage = new MostViewedPagesMacroViewPage(); - mostViewedPagesMacroViewPage.gotoPage(); - - assertEquals("When visitors search on your website, they are looking for a particular page, content, product," - + " or service. This report lists the pages that were clicked the most after an internal search.", - mostViewedPagesMacroViewPage.getMacroDescription()); - } - /** * Checks that the Row Evolution modal is loaded properly. */ @Test @Order(5) - void checkRowEvolutionModal() - { - MostViewedPagesMacroViewPage mostViewedPagesMacroViewPage = new MostViewedPagesMacroViewPage(); - BaseModal baseModal = mostViewedPagesMacroViewPage.gotoPage().openRowEvolutionModal(); - assertTrue(baseModal.isDisplayed()); + void checkRowEvolutionModal() { + AnalyticsViewPage.gotoPage(); + RowEvolutionModal rowEvolutionModal = new RowEvolutionModal(By.cssSelector(".modal-dialog.modal-lg")); + rowEvolutionModal.openModal(); + assertTrue(rowEvolutionModal.isDisplayed()); } /** * Create and start a container with the database. */ - private MySQLContainer startDb(TestConfiguration testConfiguration) throws Exception - { + private MySQLContainer startDb(TestConfiguration testConfiguration) throws Exception { // Since the MySQL container is derived from the official MySQL image I have to mark the image as compatible // with MySQLContainers. DockerImageName sqlContainer = - DockerImageName.parse(Config.DB_CONTAINER_NAME).asCompatibleSubstituteFor("mysql"); + DockerImageName.parse(Config.DB_CONTAINER_NAME).asCompatibleSubstituteFor("mysql"); MySQLContainer mysqlContainer = - new MySQLContainer<>(sqlContainer).withDatabaseName(Config.DB_NAME).withUsername(Config.DB_USERNAME) - .withPassword(Config.DB_PASSWORD).withExposedPorts(3306); + new MySQLContainer<>(sqlContainer).withDatabaseName(Config.DB_NAME).withUsername(Config.DB_USERNAME) + .withPassword(Config.DB_PASSWORD).withExposedPorts(3306); mysqlContainer.setPortBindings( - Collections.singletonList(String.format("%d:%d", Config.DB_BRIDGE_PORT, Config.DB_CONTAINER_EXPOSED_PORT))); + Collections.singletonList(String.format("%d:%d", Config.DB_BRIDGE_PORT, Config.DB_CONTAINER_EXPOSED_PORT))); DockerTestUtils.startContainer(mysqlContainer, testConfiguration); return mysqlContainer; } @@ -204,15 +191,14 @@ private MySQLContainer startDb(TestConfiguration testConfiguration) throws Excep * Creates&starts the Matomo container. * * @param testConfiguration test configuration - * @param dbContainer reference to the db container + * @param dbContainer reference to the db container */ private GenericContainer startMatomo(TestConfiguration testConfiguration, GenericContainer dbContainer) - throws Exception - { + throws Exception { GenericContainer matomoContainer = new GenericContainer<>(Config.MATOMO_CONTAINER_NAME).withExposedPorts(80) - .withEnv("MATOMO_DATABASE_HOST", - Config.ADDRESS + ":" + dbContainer.getMappedPort(Config.DB_CONTAINER_EXPOSED_PORT)) - .withFileSystemBind("src/test/it/resources/config.ini.php", Config.MATOMO_CONFIG_FILE_PATH); + .withEnv("MATOMO_DATABASE_HOST", + Config.ADDRESS + ":" + dbContainer.getMappedPort(Config.DB_CONTAINER_EXPOSED_PORT)) + .withFileSystemBind("src/test/it/resources/config.ini.php", Config.MATOMO_CONFIG_FILE_PATH); matomoContainer.setPortBindings(Collections.singletonList(String.format("%d:80", Config.MATOMO_BRIDGE_PORT))); DockerTestUtils.startContainer(matomoContainer, testConfiguration); return matomoContainer; diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/config/Config.java b/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/config/Config.java index 4c22c49a..71fa8367 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/config/Config.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/config/Config.java @@ -43,7 +43,7 @@ public class Config public static final String MATOMO_CREDENTIALS = "ADMIN1"; - public static String MATOMO_AUTH_TOKEN = "91be1bca1315c35abd605ad8a544eece"; + public static String MATOMO_AUTH_TOKEN = ""; public static String getTrackingCode() { diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsAdministrationSectionPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsAdministrationSectionPage.java index 455cb7db..301f67fa 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsAdministrationSectionPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsAdministrationSectionPage.java @@ -22,25 +22,43 @@ import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; +import org.openqa.selenium.support.FindBy; import org.xwiki.administration.test.po.AdministrationPage; import org.xwiki.test.ui.XWikiWebDriver; import org.xwiki.test.ui.po.ViewPage; +/** + * Responsible for interacting with the configuration tab of the application. + */ public class AnalyticsAdministrationSectionPage extends ViewPage { - private static final String TRACKING_CODE_ID = "Analytics.Code.ConfigurationClass_0_trackingCode"; + @FindBy( + id = "Analytics.Code.ConfigurationClass_0_trackingCode" + ) + private WebElement tracking_code; + + + @FindBy( + id = "Analytics.Code.ConfigurationClass_0_authToken" + ) + private WebElement auth_token; - private static final String AUTH_TOKEN_ID = "Analytics.Code.ConfigurationClass_0_authToken"; + @FindBy( + id = "Analytics.Code.ConfigurationClass_0_siteId" + ) + private WebElement site; - private static final String IS_SITE = "Analytics.Code.ConfigurationClass_0_siteId"; - private static final String REQUEST_ADDRESS_ID = "Analytics.Code.ConfigurationClass_0_requestAddress"; + @FindBy( + id = "Analytics.Code.ConfigurationClass_0_requestAddress" + ) + private WebElement request_address; + - private final XWikiWebDriver driver; public AnalyticsAdministrationSectionPage() { - driver = getUtil().getDriver(); + } public AnalyticsAdministrationSectionPage gotoPage() @@ -52,40 +70,36 @@ public AnalyticsAdministrationSectionPage gotoPage() public AnalyticsAdministrationSectionPage setTrackingCode(String value) { - WebElement element = driver.findElement(By.id(TRACKING_CODE_ID)); - element.clear(); - element.sendKeys(value); + tracking_code.clear(); + tracking_code.sendKeys(value); return this; } public AnalyticsAdministrationSectionPage setAuthTokenId(String value) { - WebElement element = driver.findElement(By.id(AUTH_TOKEN_ID)); - element.clear(); - element.sendKeys(value); + auth_token.clear(); + auth_token.sendKeys(value); return this; } public AnalyticsAdministrationSectionPage setIdSiteId(String value) { - WebElement element = driver.findElement(By.id(IS_SITE)); - element.clear(); - element.sendKeys(value); + site.clear(); + site.sendKeys(value); return this; } public AnalyticsAdministrationSectionPage setRequestAddressId(String value) { - WebElement element = driver.findElement(By.id(REQUEST_ADDRESS_ID)); - element.clear(); - element.sendKeys(value); + request_address.clear(); + request_address.sendKeys(value); return this; } public AnalyticsAdministrationSectionPage saveConfigs() { - WebElement saveButton = driver.findElement(By.cssSelector(".btn.btn-primary")); - Actions actions = driver.createActions(); + WebElement saveButton = getUtil().getDriver().findElement(By.cssSelector(".btn.btn-primary")); + Actions actions = getUtil().getDriver().createActions(); actions.moveToElement(saveButton).click().perform(); return this; } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsEditPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsEditPage.java index 7ec567f7..c7cee97b 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsEditPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsEditPage.java @@ -26,6 +26,10 @@ import org.xwiki.ckeditor.test.po.MacroDialogSelectModal; import org.xwiki.model.reference.DocumentReference; import org.xwiki.test.ui.po.editor.EditPage; + +/** + * Responsible for interacting with the main page of the application when the page is in edit mode. + */ public class AnalyticsEditPage extends EditPage { diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsViewPage.java index 2a825d2e..cdf3d917 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsViewPage.java @@ -22,13 +22,15 @@ import org.openqa.selenium.By; import org.xwiki.test.ui.po.ViewPage; +/** + * Responsible for interacting with the main page of the application when the page is in view mode. + */ public class AnalyticsViewPage extends ViewPage { public AnalyticsViewPage() { - } public static AnalyticsViewPage gotoPage() diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTestUtils.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTestUtils.java new file mode 100644 index 00000000..dd52097f --- /dev/null +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTestUtils.java @@ -0,0 +1,66 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.analytics.test.po; + +import org.openqa.selenium.By; +import org.xwiki.test.ui.XWikiWebDriver; + +import static org.junit.Assert.fail; + +/** + * Encompass the process of creating a Matomo token using the browser interface provided by Matomo. + */ +public class MatomoTestUtils { + private static final String CREDENTIALS = "ADMIN1"; + private static final String LOGIN_FORM_LOGIN_ID = "login_form_login"; + private static final String LOGIN_FORM_PASSWORD_ID = "login_form_password"; + private static final String LOGIN_FORM_SUBMIT_ID = "login_form_submit"; + private static final String DESCRIPTION_ID = "description"; + private static final String BTN_CSS_SELECTOR = ".btn"; + private static final String TAG_NAME_CODE = "code"; + + /** + * Creates a new access token by accessing the Matomo GUI. + * + * @param address The address of the Matomo container. + * @return the newly created token as a string + */ + public static String createToken(String address, XWikiWebDriver driver) { + + driver.get(address + "/index.php?module=UsersManager&action=addNewToken&idSite=1&period=day&date=2023-09-03"); + driver.waitUntilElementIsVisible(By.id(LOGIN_FORM_LOGIN_ID)); + driver.findElement(By.id(LOGIN_FORM_LOGIN_ID)).sendKeys(CREDENTIALS); + driver.waitUntilElementIsVisible(By.id(LOGIN_FORM_PASSWORD_ID)); + driver.findElement(By.id(LOGIN_FORM_PASSWORD_ID)).sendKeys(CREDENTIALS); + driver.waitUntilElementIsVisible(By.id(LOGIN_FORM_SUBMIT_ID)); + driver.findElement(By.id(LOGIN_FORM_SUBMIT_ID)).click(); + driver.waitUntilElementIsVisible(By.id(LOGIN_FORM_PASSWORD_ID)); + driver.findElement(By.id(LOGIN_FORM_PASSWORD_ID)).sendKeys(CREDENTIALS); + driver.waitUntilElementIsVisible(By.id(LOGIN_FORM_SUBMIT_ID)); + driver.findElement(By.id(LOGIN_FORM_SUBMIT_ID)).click(); + driver.waitUntilElementIsVisible(By.id(DESCRIPTION_ID)); + driver.findElement(By.id(DESCRIPTION_ID)).sendKeys("TEST TOKEN"); + driver.waitUntilElementIsVisible(By.cssSelector(BTN_CSS_SELECTOR)); + driver.findElement(By.cssSelector(BTN_CSS_SELECTOR)).click(); + driver.waitUntilElementIsVisible(By.tagName(TAG_NAME_CODE)); + return driver.findElement(By.tagName(TAG_NAME_CODE)).getText(); + } +} + diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoViewPage.java deleted file mode 100644 index cdd52f3d..00000000 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoViewPage.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * See the NOTICE file distributed with this work for additional - * information regarding copyright ownership. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package com.xwiki.analytics.test.po; - -import org.openqa.selenium.By; -import org.xwiki.model.reference.DocumentReference; -import org.xwiki.test.ui.XWikiWebDriver; -import org.xwiki.test.ui.po.ViewPage; - -import static org.junit.Assert.fail; - -/** - * Encompass the process of creating a Matomo token using the browser interface provided by Matomo. - */ -public class MatomoViewPage extends ViewPage -{ - private static final String credentials = "ADMIN1"; - - public MatomoViewPage() - { - - } - - /** - * Creates a new access token by accesing the Matomo GUI. - * @param address The address of the Matomo container. - * @return the newly created token as a string - */ - static public String createToken(String address) - { - XWikiWebDriver driver = getUtil().getDriver(); - getUtil().gotoPage(address); - getUtil().gotoPage( - address + "/index.php?module=UsersManager&action=addNewToken&idSite=1&period=day&date=2023" + "-09-03"); - driver.findElement(By.id("login_form_login")).sendKeys(credentials); - driver.findElement(By.id("login_form_password")).sendKeys(credentials); - driver.findElement(By.id("login_form_submit")).click(); - driver.findElement(By.id("login_form_password")).sendKeys(credentials); - driver.findElement(By.id("login_form_submit")).click(); - driver.findElement(By.id("description")).sendKeys("TEST TOKEN"); - driver.findElement(By.cssSelector(".btn")).click(); - return driver.findElement(By.tagName("code")).getText(); - } -} diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedPagesMacroViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedPagesElement.java similarity index 53% rename from application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedPagesMacroViewPage.java rename to application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedPagesElement.java index 58f52797..0d38b132 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedPagesMacroViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedPagesElement.java @@ -25,48 +25,33 @@ import java.util.List; import org.openqa.selenium.By; -import org.xwiki.test.ui.po.BaseModal; +import org.openqa.selenium.WebElement; +import org.xwiki.test.ui.po.BaseElement; import org.xwiki.test.ui.po.ViewPage; /** - * View for the MostViewedPages macro that lets us interact with the page of the macro. + * View for the MostViewedPages macro that lets us interact with the macro. */ -public class MostViewedPagesMacroViewPage extends ViewPage +public class MostViewedPagesElement extends BaseElement { - private static final String ROW_EVOLUTION_BUTTON_SELECTOR = ".analyticsRowEvolution"; - public MostViewedPagesMacroViewPage() + WebElement macroOutput; + public MostViewedPagesElement(String id) { + getUtil().getDriver().waitUntilElementIsVisible(By.id(id)); + macroOutput = getUtil().getDriver().findElement(By.id(id)); } - public MostViewedPagesMacroViewPage gotoPage() - { - List spaces = Arrays.asList("Analytics", "Code", "Macros"); - getUtil().gotoPage(spaces, "MostViewedPages", "view", ""); - return this; - } - /** - * Opens the Row Evolution modal and waits until it is fully visible before returning it. - * @return the modal - */ - public BaseModal openRowEvolutionModal() - { - BaseModal baseModal = new BaseModal(By.cssSelector(".modal-dialog.modal-lg")); - getUtil().getDriver().waitUntilElementIsVisible(By.cssSelector(ROW_EVOLUTION_BUTTON_SELECTOR)); - getUtil().getDriver().findElement(By.cssSelector(ROW_EVOLUTION_BUTTON_SELECTOR)).click(); - return baseModal; - } /*** * This method first waits for the visibility of an information button, identified by a specific CSS selector. * Once the information button is visible it selects the tile attribute and returns it. - * @return THe description of a macro. + * @return the description of a macro. */ public String getMacroDescription() { - getUtil().getDriver().waitUntilElementIsVisible(By.cssSelector(".analyticsDescription")); - return getUtil().getDriver().findElement(By.cssSelector(".analyticsDescription")).getAttribute("title"); + return macroOutput.findElement(By.cssSelector(".analyticsDescription")).getAttribute("title"); } } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/RowEvolutionModal.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/RowEvolutionModal.java new file mode 100644 index 00000000..977ab526 --- /dev/null +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/RowEvolutionModal.java @@ -0,0 +1,39 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.analytics.test.po; + +import org.openqa.selenium.By; +import org.xwiki.test.ui.po.BaseModal; + +public class RowEvolutionModal extends BaseModal { + private static final String ROW_EVOLUTION_BUTTON_SELECTOR = ".analyticsRowEvolution"; + + + public RowEvolutionModal(By selector) { + super(selector); + } + public RowEvolutionModal openModal() + { + getUtil().getDriver().waitUntilElementIsVisible(By.cssSelector(ROW_EVOLUTION_BUTTON_SELECTOR)); + getUtil().getDriver().findElement(By.cssSelector(ROW_EVOLUTION_BUTTON_SELECTOR)).click(); + return this; + } + +} diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsJS.xml b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsJS.xml index 071ba1ce..9a265f58 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsJS.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/AnalyticsJS.xml @@ -132,7 +132,7 @@ $('[id^="gadget_"]').each(function () { let hiddenElement = $(this).find('.gadget-content .analytics-description'); if (hiddenElement.length > 0) { - let dataElement = $(`<a class="${icon.cssClass} analyticsTitleMargin analyticsDescription"></a>`); + let dataElement = $(`<a class="${icon.cssClass} analyticsDescription analyticsTitleMargin"></a>`); dataElement.attr('title', hiddenElement.data('description')); dataElement.attr('href', hiddenElement.data('url')); $(this).find('.gadget-title').append(dataElement); diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml index 60680c8a..5b6cae74 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolution.xml @@ -212,8 +212,7 @@ require(['jquery', 'chart'], ($) => { period: currentOptions.data('period'), date: `last${currentOptions.data('date')}`, macroName: currentOptions.data('macroName'), - rowIdentifier: rowIdentifier, - action: "GET" + rowIdentifier: rowIdentifier }; const queryString = $.param(params); $.getJSON(`${requestPageUrl}?${queryString}`) diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJSON.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJSON.xml index a6d65668..2a776c6f 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJSON.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/RowEvolutionJSON.xml @@ -37,7 +37,7 @@ xwiki/2.1 true {{velocity}} -#if ($request.action == 'GET') +#if ($xcontext.action == 'get') #set ($parameters = { 'period' : $request.period, 'date' : $request.date, diff --git a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml index 966bb966..b72d777f 100644 --- a/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml +++ b/application-analytics-ui/src/main/resources/Analytics/Code/Macros/VelocityMacros.xml @@ -119,7 +119,7 @@ #end #macro(modalRowEvolution $matomoFields $macroName $id) - #set ($requestPageURL = $xwiki.getURL('Analytics.Code.Macros.RowEvolutionJSON')) + #set ($requestPageURL = $xwiki.getURL('Analytics.Code.Macros.RowEvolutionJSON', 'get')) {{html clean=false}} <div class="modal fade analyticsRowEvolutionModal" tabindex="-1" role="dialog" aria-label="chartModalLabel" aria-hidden="true"> From 05114fec66e406aad7cb620c9e4ff37cb7756a93 Mon Sep 17 00:00:00 2001 From: farcasut Date: Thu, 15 Feb 2024 14:49:59 +0200 Subject: [PATCH 104/105] Create functional tests #45 Added a wait to avoid flickering --- .../java/com/xwiki/analytics/test/po/MatomoTestUtils.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTestUtils.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTestUtils.java index dd52097f..5b9dcf01 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTestUtils.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTestUtils.java @@ -20,10 +20,9 @@ package com.xwiki.analytics.test.po; import org.openqa.selenium.By; +import org.openqa.selenium.support.ui.ExpectedConditions; import org.xwiki.test.ui.XWikiWebDriver; -import static org.junit.Assert.fail; - /** * Encompass the process of creating a Matomo token using the browser interface provided by Matomo. */ @@ -43,7 +42,6 @@ public class MatomoTestUtils { * @return the newly created token as a string */ public static String createToken(String address, XWikiWebDriver driver) { - driver.get(address + "/index.php?module=UsersManager&action=addNewToken&idSite=1&period=day&date=2023-09-03"); driver.waitUntilElementIsVisible(By.id(LOGIN_FORM_LOGIN_ID)); driver.findElement(By.id(LOGIN_FORM_LOGIN_ID)).sendKeys(CREDENTIALS); @@ -55,7 +53,8 @@ public static String createToken(String address, XWikiWebDriver driver) { driver.findElement(By.id(LOGIN_FORM_PASSWORD_ID)).sendKeys(CREDENTIALS); driver.waitUntilElementIsVisible(By.id(LOGIN_FORM_SUBMIT_ID)); driver.findElement(By.id(LOGIN_FORM_SUBMIT_ID)).click(); - driver.waitUntilElementIsVisible(By.id(DESCRIPTION_ID)); + // Sometimes the Matomo page loads slower and to avoid a flicker the timeout needs to be increased. + driver.waitUntilCondition(ExpectedConditions.visibilityOfElementLocated(By.id(DESCRIPTION_ID)), 15); driver.findElement(By.id(DESCRIPTION_ID)).sendKeys("TEST TOKEN"); driver.waitUntilElementIsVisible(By.cssSelector(BTN_CSS_SELECTOR)); driver.findElement(By.cssSelector(BTN_CSS_SELECTOR)).click(); From 19fb31d2fba042492c76dc2d6978e55058f2cfa2 Mon Sep 17 00:00:00 2001 From: farcasut Date: Thu, 29 Feb 2024 13:14:24 +0200 Subject: [PATCH 105/105] Create functional tests #45 Refactored code, added a new test and improved formatted the code. --- .../internal/MostViewedJsonNormaliser.java | 1 - .../application-analytics-test-docker/pom.xml | 2 +- .../xwiki/analytics/test/ui/AnalyticsIT.java | 188 ++++++++++-------- .../analytics/test/ui/config/Config.java | 1 + .../pom.xml | 4 +- .../AnalyticsAdministrationSectionPage.java | 21 +- .../analytics/test/po/AnalyticsEditPage.java | 23 +-- .../analytics/test/po/AnalyticsViewPage.java | 9 +- .../analytics/test/po/MatomoTestUtils.java | 12 +- .../test/po/MostViewedPagesElement.java | 28 ++- .../analytics/test/po/RowEvolutionModal.java | 13 +- application-analytics-test/pom.xml | 4 +- 12 files changed, 162 insertions(+), 144 deletions(-) diff --git a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java index cf7f9f4a..aba46c32 100644 --- a/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java +++ b/application-analytics-default/src/main/java/com/xwiki/analytics/internal/MostViewedJsonNormaliser.java @@ -46,7 +46,6 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.xwiki.analytics.JsonNormaliser; -import org.apache.commons.lang3.exception.ExceptionUtils; /** * Implementation for {@link JsonNormaliser}. diff --git a/application-analytics-test/application-analytics-test-docker/pom.xml b/application-analytics-test/application-analytics-test-docker/pom.xml index 1128d49d..60bdbfd4 100644 --- a/application-analytics-test/application-analytics-test-docker/pom.xml +++ b/application-analytics-test/application-analytics-test-docker/pom.xml @@ -31,7 +31,7 @@ application-analytics-test-docker jar - Functional tests for the Analytics Application (PRO) + Functional tests for the Analytics Application (Pro) true diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AnalyticsIT.java b/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AnalyticsIT.java index a3fd82cd..ed44dc70 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AnalyticsIT.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/AnalyticsIT.java @@ -21,12 +21,10 @@ import java.util.Collections; -import com.xwiki.analytics.test.po.*; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.openqa.selenium.By; -import org.openqa.selenium.support.ui.ExpectedConditions; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.utility.DockerImageName; @@ -36,126 +34,97 @@ import org.xwiki.test.ui.TestUtils; import org.xwiki.test.ui.XWikiWebDriver; +import com.xwiki.analytics.test.po.AnalyticsAdministrationSectionPage; +import com.xwiki.analytics.test.po.AnalyticsViewPage; +import com.xwiki.analytics.test.po.MatomoTestUtils; +import com.xwiki.analytics.test.po.MostViewedPagesElement; +import com.xwiki.analytics.test.po.RowEvolutionModal; import com.xwiki.analytics.test.ui.config.Config; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @UITest -class AnalyticsIT { - /** - * Creates the Admin user - */ - public void setupUsers(TestUtils testUtils) { - testUtils.loginAsSuperAdmin(); - //TODO remove this line when upgrading the parent to >=15.10 because it was added to createAdminUser function - testUtils.setGlobalRights("XWiki.XWikiAdminGroup", "", "admin", true); - testUtils.createAdminUser(); - testUtils.loginAsAdmin(); - - } - - /** - * Start the matomo and sql containers, rename the matomo.js to a random int to force the browser to load the - * tracking script without explicit settings and generate a new auth token for matomo to be used in the tests. - */ - public void setupContainers(XWikiWebDriver driver, TestConfiguration testConfiguration) throws Exception { - GenericContainer sqlContainer = startDb(testConfiguration); - GenericContainer matomoContainer = startMatomo(testConfiguration, sqlContainer); - // Modify the name of the matomo.js to make sure that the browser doesn't block it. - matomoContainer.execInContainer("sh", "-c", - "grep -rl 'matomo.js' /var/www/html/ | xargs -d '\\n' -I {} sed -i 's/matomo.js/36011373.js/g' \"{}\""); - matomoContainer.execInContainer("sh", "-c", "mv /var/www/html/matomo.js /var/www/html/36011373.js"); - Config.MATOMO_AUTH_TOKEN = - MatomoTestUtils.createToken("http://" + Config.ADDRESS + ":" + matomoContainer.getMappedPort(80), driver); - } - - /** - * Import the platform.html.head UIExtension point to make the tracking code work in the test environment. - */ - private void setupUIExtension(TestUtils testUtils) throws Exception { - testUtils.setWikiPreference("meta", - "#foreach($uix in $services.uix.getExtensions(\"org.xwiki.platform.html.head\"," - + " {'sortByParameter' : 'order'}))\n" + " $services.rendering.render($uix.execute(), 'xhtml/1.0')\n" - + "#end"); - } - +class AnalyticsIT +{ @BeforeAll - void setup(XWikiWebDriver driver, TestConfiguration testConfiguration, TestUtils testUtils) throws Exception { + void setup(XWikiWebDriver driver, TestConfiguration testConfiguration, TestUtils testUtils) throws Exception + { setupContainers(driver, testConfiguration); setupUIExtension(testUtils); setupUsers(testUtils); - AnalyticsViewPage.gotoPage(); } /** - * Check that the Save button displays the correct messages when the configurations provided by the - * users are incorrect. + * Check that the Save button displays the correct messages when the configurations provided by the users are + * incorrect. */ @Test @Order(1) - void checkWrongConfigs(XWikiWebDriver driver) throws InterruptedException { + void checkWrongConfigs(XWikiWebDriver driver) throws InterruptedException + { + AnalyticsViewPage.gotoPage(); AnalyticsAdministrationSectionPage analyticsConfigViewPage = new AnalyticsAdministrationSectionPage(); analyticsConfigViewPage.gotoPage(); analyticsConfigViewPage.setTrackingCode("").setAuthTokenId(Config.MATOMO_AUTH_TOKEN).setIdSiteId("1") - .setRequestAddressId(Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT).saveConfigs(); + .setRequestAddressId(Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT).saveConfigs(); analyticsConfigViewPage.waitForNotificationInProgressMessage("Saving..."); analyticsConfigViewPage.waitForNotificationSuccessMessage("Saved"); analyticsConfigViewPage.waitForNotificationInProgressMessage("Checking connection to Matomo."); - analyticsConfigViewPage.waitForNotificationErrorMessage("Failed to connect to Matomo. Please check your configuration " + "values."); - AnalyticsViewPage.gotoPage(); + analyticsConfigViewPage.waitForNotificationErrorMessage( + "Failed to connect to Matomo. Please check your configuration values."); } /** - * Check that the Save button displays the correct messages when the configurations provided by the - * users are correct. + * Check that the Save button displays the correct messages when the configurations provided by the users are + * correct. */ @Test @Order(2) - void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException { - + void checkValidConfigs(XWikiWebDriver driver) throws InterruptedException + { + AnalyticsViewPage.gotoPage(); AnalyticsAdministrationSectionPage analyticsConfigViewPage = new AnalyticsAdministrationSectionPage(); - analyticsConfigViewPage.gotoPage(); analyticsConfigViewPage.setTrackingCode(Config.getTrackingCode()).setAuthTokenId(Config.MATOMO_AUTH_TOKEN) - .setIdSiteId("1").setRequestAddressId("http://" + Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT + "/") - .saveConfigs(); + .setIdSiteId("1").setRequestAddressId("http://" + Config.ADDRESS + ":" + Config.MATOMO_BRIDGE_PORT + "/") + .saveConfigs(); analyticsConfigViewPage.waitForNotificationInProgressMessage("Saving..."); analyticsConfigViewPage.waitForNotificationSuccessMessage("Saved"); analyticsConfigViewPage.waitForNotificationInProgressMessage("Checking connection to Matomo."); analyticsConfigViewPage.waitForNotificationSuccessMessage("Test connection succeeded!"); - AnalyticsViewPage.gotoPage(); } - /** - * Checks that the description is loaded properly for a macro. + * Checks if the admin has edit permissions in the home page of the application. */ @Test @Order(3) - void checkMacroDescription() throws InterruptedException { - AnalyticsViewPage.gotoPage(); - MostViewedPagesElement mostViewedPagesELement = new MostViewedPagesElement("gadget_1"); - assertEquals("When visitors search on your website, they are looking for a particular page, content, product," - + " or service. This report lists the pages that were clicked the most after an internal search.", - mostViewedPagesELement.getMacroDescription()); - } + void checkEditPermissionsForAdmin(XWikiWebDriver driver) throws InterruptedException + { + AnalyticsViewPage analyticsViewPage = AnalyticsViewPage.gotoPage(); + assertTrue(analyticsViewPage.hasEditButton()); + } /** - * Checks if the admin has edit permissions in the home page of the application. + * Checks if a user has view permissions in the home page of the application. */ @Test - @Order(3) - void checkEditPermissions(XWikiWebDriver driver) throws InterruptedException { - + @Order(4) + void checkEditPermisionForUser(XWikiWebDriver driver, TestUtils testUtils) throws InterruptedException + { + testUtils.createUser("test", "test", null); + // Logout from the admin account + testUtils.setSession(null); + testUtils.login("test", "test"); AnalyticsViewPage analyticsViewPage = AnalyticsViewPage.gotoPage(); - // Add a gadget to the dashboard. - AnalyticsEditPage.gotoPage().addNewMacro("Search Categories").saveDashboard(); - driver.waitUntilCondition(ExpectedConditions.numberOfElementsToBeMoreThan(By.cssSelector(".gadget-content"), - 2)); - assertEquals(3, analyticsViewPage.getGadgetCount()); + assertEquals("You are not allowed to view this page or perform this action.", + driver.findElement(By.cssSelector("p.xwikimessage")).getText()); + // Login as the admin to run the next test + testUtils.setSession(null); + testUtils.loginAsAdmin(); } /** @@ -163,26 +132,70 @@ void checkEditPermissions(XWikiWebDriver driver) throws InterruptedException { */ @Test @Order(5) - void checkRowEvolutionModal() { + void checkRowEvolutionModal() + { AnalyticsViewPage.gotoPage(); - RowEvolutionModal rowEvolutionModal = new RowEvolutionModal(By.cssSelector(".modal-dialog.modal-lg")); - rowEvolutionModal.openModal(); + MostViewedPagesElement mostViewedPagesElement = new MostViewedPagesElement(); + RowEvolutionModal rowEvolutionModal = mostViewedPagesElement.openModal(); assertTrue(rowEvolutionModal.isDisplayed()); } + /** + * Creates the Admin user + */ + private void setupUsers(TestUtils testUtils) + { + testUtils.loginAsSuperAdmin(); + // TODO: remove this line after upgrading the XWiki parent to a version >= 15.10, because it was added as part + // of the createAdminUser method + testUtils.setGlobalRights("XWiki.XWikiAdminGroup", "", "admin", true); + testUtils.createAdminUser(); + testUtils.loginAsAdmin(); + } + + /** + * Start the matomo and sql containers, rename the matomo.js to a random int to force the browser to load the + * tracking script without explicit settings and generate a new auth token for matomo to be used in the tests. + */ + private void setupContainers(XWikiWebDriver driver, TestConfiguration testConfiguration) throws Exception + { + GenericContainer sqlContainer = startDb(testConfiguration); + GenericContainer matomoContainer = startMatomo(testConfiguration, sqlContainer); + // Modify the name of the matomo.js to make sure that the browser doesn't block it. + matomoContainer.execInContainer("sh", "-c", + "grep -rl 'matomo.js' /var/www/html/ | xargs -d '\\n' -I {} sed -i 's/matomo.js/36011373.js/g' \"{}\""); + matomoContainer.execInContainer("sh", "-c", "mv /var/www/html/matomo.js /var/www/html/36011373.js"); + Config.MATOMO_AUTH_TOKEN = + MatomoTestUtils.createToken("http://" + Config.ADDRESS + ":" + matomoContainer.getMappedPort(80), driver); + } + + /** + * Import the platform.html.head UIExtension point to make the tracking code work in the test environment. + * 'org.xwiki.platform:xwiki-platform-distribution-ui-base' but we didn't add it as a test dependency because it + * brings too many transitive dependencies that we don't need. + */ + private void setupUIExtension(TestUtils testUtils) throws Exception + { + testUtils.setWikiPreference("meta", + "#foreach($uix in $services.uix.getExtensions(\"org.xwiki.platform.html.head\"," + + " {'sortByParameter' : 'order'}))\n" + " $services.rendering.render($uix.execute(), 'xhtml/1.0')\n" + + "#end"); + } + /** * Create and start a container with the database. */ - private MySQLContainer startDb(TestConfiguration testConfiguration) throws Exception { + private MySQLContainer startDb(TestConfiguration testConfiguration) throws Exception + { // Since the MySQL container is derived from the official MySQL image I have to mark the image as compatible // with MySQLContainers. DockerImageName sqlContainer = - DockerImageName.parse(Config.DB_CONTAINER_NAME).asCompatibleSubstituteFor("mysql"); + DockerImageName.parse(Config.DB_CONTAINER_NAME).asCompatibleSubstituteFor("mysql"); MySQLContainer mysqlContainer = - new MySQLContainer<>(sqlContainer).withDatabaseName(Config.DB_NAME).withUsername(Config.DB_USERNAME) - .withPassword(Config.DB_PASSWORD).withExposedPorts(3306); + new MySQLContainer<>(sqlContainer).withDatabaseName(Config.DB_NAME).withUsername(Config.DB_USERNAME) + .withPassword(Config.DB_PASSWORD).withExposedPorts(3306); mysqlContainer.setPortBindings( - Collections.singletonList(String.format("%d:%d", Config.DB_BRIDGE_PORT, Config.DB_CONTAINER_EXPOSED_PORT))); + Collections.singletonList(String.format("%d:%d", Config.DB_BRIDGE_PORT, Config.DB_CONTAINER_EXPOSED_PORT))); DockerTestUtils.startContainer(mysqlContainer, testConfiguration); return mysqlContainer; } @@ -191,14 +204,15 @@ private MySQLContainer startDb(TestConfiguration testConfiguration) throws Excep * Creates&starts the Matomo container. * * @param testConfiguration test configuration - * @param dbContainer reference to the db container + * @param dbContainer reference to the db container */ private GenericContainer startMatomo(TestConfiguration testConfiguration, GenericContainer dbContainer) - throws Exception { + throws Exception + { GenericContainer matomoContainer = new GenericContainer<>(Config.MATOMO_CONTAINER_NAME).withExposedPorts(80) - .withEnv("MATOMO_DATABASE_HOST", - Config.ADDRESS + ":" + dbContainer.getMappedPort(Config.DB_CONTAINER_EXPOSED_PORT)) - .withFileSystemBind("src/test/it/resources/config.ini.php", Config.MATOMO_CONFIG_FILE_PATH); + .withEnv("MATOMO_DATABASE_HOST", + Config.ADDRESS + ":" + dbContainer.getMappedPort(Config.DB_CONTAINER_EXPOSED_PORT)) + .withFileSystemBind("src/test/it/resources/config.ini.php", Config.MATOMO_CONFIG_FILE_PATH); matomoContainer.setPortBindings(Collections.singletonList(String.format("%d:80", Config.MATOMO_BRIDGE_PORT))); DockerTestUtils.startContainer(matomoContainer, testConfiguration); return matomoContainer; diff --git a/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/config/Config.java b/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/config/Config.java index 71fa8367..c2698455 100644 --- a/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/config/Config.java +++ b/application-analytics-test/application-analytics-test-docker/src/test/it/com/xwiki/analytics/test/ui/config/Config.java @@ -21,6 +21,7 @@ public class Config { + // The custom sql container is needed to skip the Matomo installation wizard. public static final String DB_CONTAINER_NAME = "farcasut/custom-mysql:latest"; public static final int DB_CONTAINER_EXPOSED_PORT = 3306; diff --git a/application-analytics-test/application-analytics-test-pageobjects/pom.xml b/application-analytics-test/application-analytics-test-pageobjects/pom.xml index 73d42e0a..0cd40b17 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/pom.xml +++ b/application-analytics-test/application-analytics-test-pageobjects/pom.xml @@ -28,9 +28,9 @@ 1.0-SNAPSHOT application-analytics-test-pageobjects - Application Analytics (PRO) - Test - Page Objects + Application Analytics (Pro) - Test - Page Objects jar - Application Analytics (PRO) - Test - Page Objects + Application Analytics (Pro) - Test - Page Objects org.xwiki.platform diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsAdministrationSectionPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsAdministrationSectionPage.java index 301f67fa..30e67520 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsAdministrationSectionPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsAdministrationSectionPage.java @@ -24,7 +24,6 @@ import org.openqa.selenium.interactions.Actions; import org.openqa.selenium.support.FindBy; import org.xwiki.administration.test.po.AdministrationPage; -import org.xwiki.test.ui.XWikiWebDriver; import org.xwiki.test.ui.po.ViewPage; /** @@ -32,30 +31,18 @@ */ public class AnalyticsAdministrationSectionPage extends ViewPage { - @FindBy( - id = "Analytics.Code.ConfigurationClass_0_trackingCode" - ) + @FindBy(id = "Analytics.Code.ConfigurationClass_0_trackingCode") private WebElement tracking_code; - - @FindBy( - id = "Analytics.Code.ConfigurationClass_0_authToken" - ) + @FindBy(id = "Analytics.Code.ConfigurationClass_0_authToken") private WebElement auth_token; - @FindBy( - id = "Analytics.Code.ConfigurationClass_0_siteId" - ) + @FindBy(id = "Analytics.Code.ConfigurationClass_0_siteId") private WebElement site; - - @FindBy( - id = "Analytics.Code.ConfigurationClass_0_requestAddress" - ) + @FindBy(id = "Analytics.Code.ConfigurationClass_0_requestAddress") private WebElement request_address; - - public AnalyticsAdministrationSectionPage() { diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsEditPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsEditPage.java index c7cee97b..160412bd 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsEditPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsEditPage.java @@ -32,8 +32,9 @@ */ public class AnalyticsEditPage extends EditPage { - - public AnalyticsEditPage(){} + public AnalyticsEditPage() + { + } /** * Adds a new macro to the homepage of the application. @@ -41,13 +42,13 @@ public AnalyticsEditPage(){} * @param macroName name of the macro * @return */ - public AnalyticsEditPage addNewMacro(String macroName) { this.clickAddGadget().selectMacro(macroName); return this; } + public static AnalyticsEditPage gotoPage() { DocumentReference documentReference = new DocumentReference("xwiki", "Analytics", "WebHome"); @@ -56,6 +57,7 @@ public static AnalyticsEditPage gotoPage() getUtil().gotoPage(documentReference, "edit", params); return new AnalyticsEditPage(); } + public AnalyticsViewPage saveDashboard() { this.clickSaveAndView(); @@ -64,26 +66,21 @@ public AnalyticsViewPage saveDashboard() private AnalyticsEditPage clickAddGadget() { - getUtil().getDriver().findElement(By.cssSelector(".addgadget")).click(); + getDriver().findElement(By.cssSelector(".addgadget")).click(); return this; } - private void waitAndClick(String css) - { - getUtil().getDriver().waitUntilElementIsEnabled( getUtil().getDriver().findElement(By.cssSelector(css))); - getUtil().getDriver().findElement(By.cssSelector(css)).click(); - } - private void selectMacro(String macroName) { MacroDialogSelectModal macroDialogSelectModal = new MacroDialogSelectModal(); // Will bring into view the macro that I want to use. - getUtil().getDriver().findElement(By.cssSelector(".macro-textFilter")).sendKeys(macroName); + getDriver().findElement(By.cssSelector(".macro-textFilter")).sendKeys(macroName); macroDialogSelectModal.filterByText(macroName, 1); macroDialogSelectModal.getFirstMacro(); macroDialogSelectModal.clickSelect(); // Right now the MacroDialogSelectModal doesn't have a method to press submit. - waitAndClick(".modal.macro-editor-modal.in .modal-footer .btn-primary"); - + String css = ".modal.macro-editor-modal.in .modal-footer .btn-primary"; + getDriver().waitUntilElementIsEnabled(getUtil().getDriver().findElement(By.cssSelector(css))); + getDriver().findElement(By.cssSelector(css)).click(); } } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsViewPage.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsViewPage.java index cdf3d917..8cb4421c 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsViewPage.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/AnalyticsViewPage.java @@ -27,8 +27,6 @@ */ public class AnalyticsViewPage extends ViewPage { - - public AnalyticsViewPage() { } @@ -38,12 +36,19 @@ public static AnalyticsViewPage gotoPage() getUtil().gotoPage("Analytics", "WebHome"); return new AnalyticsViewPage(); } + /** * Calculates the number of gadgets that are present on the homepage of the application. + * * @return number of gadgets */ public int getGadgetCount() { return getUtil().getDriver().findElements(By.className("gadget")).size(); } + + public boolean hasEditButton() + { + return !getDriver().findElementsWithoutWaiting(By.id("tmEdit")).isEmpty(); + } } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTestUtils.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTestUtils.java index 5b9dcf01..922c7955 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTestUtils.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MatomoTestUtils.java @@ -26,13 +26,20 @@ /** * Encompass the process of creating a Matomo token using the browser interface provided by Matomo. */ -public class MatomoTestUtils { +public class MatomoTestUtils +{ private static final String CREDENTIALS = "ADMIN1"; + private static final String LOGIN_FORM_LOGIN_ID = "login_form_login"; + private static final String LOGIN_FORM_PASSWORD_ID = "login_form_password"; + private static final String LOGIN_FORM_SUBMIT_ID = "login_form_submit"; + private static final String DESCRIPTION_ID = "description"; + private static final String BTN_CSS_SELECTOR = ".btn"; + private static final String TAG_NAME_CODE = "code"; /** @@ -41,7 +48,8 @@ public class MatomoTestUtils { * @param address The address of the Matomo container. * @return the newly created token as a string */ - public static String createToken(String address, XWikiWebDriver driver) { + public static String createToken(String address, XWikiWebDriver driver) + { driver.get(address + "/index.php?module=UsersManager&action=addNewToken&idSite=1&period=day&date=2023-09-03"); driver.waitUntilElementIsVisible(By.id(LOGIN_FORM_LOGIN_ID)); driver.findElement(By.id(LOGIN_FORM_LOGIN_ID)).sendKeys(CREDENTIALS); diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedPagesElement.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedPagesElement.java index 0d38b132..29730e8d 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedPagesElement.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/MostViewedPagesElement.java @@ -21,30 +21,23 @@ package com.xwiki.analytics.test.po; -import java.util.Arrays; -import java.util.List; - import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.xwiki.test.ui.po.BaseElement; -import org.xwiki.test.ui.po.ViewPage; /** * View for the MostViewedPages macro that lets us interact with the macro. */ public class MostViewedPagesElement extends BaseElement { + static final private String MODAL_CSS = ".modal-dialog.modal-lg"; + private WebElement container; - WebElement macroOutput; - public MostViewedPagesElement(String id) + public MostViewedPagesElement() { - getUtil().getDriver().waitUntilElementIsVisible(By.id(id)); - macroOutput = getUtil().getDriver().findElement(By.id(id)); } - - /*** * This method first waits for the visibility of an information button, identified by a specific CSS selector. * Once the information button is visible it selects the tile attribute and returns it. @@ -52,6 +45,19 @@ public MostViewedPagesElement(String id) */ public String getMacroDescription() { - return macroOutput.findElement(By.cssSelector(".analyticsDescription")).getAttribute("title"); + return container.findElement(By.cssSelector(".analyticsDescription")).getAttribute("title"); + } + + public RowEvolutionModal openModal() + { + RowEvolutionModal modal = new RowEvolutionModal(By.cssSelector(MODAL_CSS)); + modal.openModal(); + return modal; + } + + public void waitUntilReady(String id) + { + getDriver().waitUntilElementIsVisible(By.id(id)); + container = getDriver().findElement(By.id(id)); } } diff --git a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/RowEvolutionModal.java b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/RowEvolutionModal.java index 977ab526..897b39f1 100644 --- a/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/RowEvolutionModal.java +++ b/application-analytics-test/application-analytics-test-pageobjects/src/main/java/com/xwiki/analytics/test/po/RowEvolutionModal.java @@ -22,18 +22,19 @@ import org.openqa.selenium.By; import org.xwiki.test.ui.po.BaseModal; -public class RowEvolutionModal extends BaseModal { +public class RowEvolutionModal extends BaseModal +{ private static final String ROW_EVOLUTION_BUTTON_SELECTOR = ".analyticsRowEvolution"; - - public RowEvolutionModal(By selector) { + public RowEvolutionModal(By selector) + { super(selector); } + public RowEvolutionModal openModal() { - getUtil().getDriver().waitUntilElementIsVisible(By.cssSelector(ROW_EVOLUTION_BUTTON_SELECTOR)); - getUtil().getDriver().findElement(By.cssSelector(ROW_EVOLUTION_BUTTON_SELECTOR)).click(); + getDriver().waitUntilElementIsVisible(By.cssSelector(ROW_EVOLUTION_BUTTON_SELECTOR)); + getDriver().findElement(By.cssSelector(ROW_EVOLUTION_BUTTON_SELECTOR)).click(); return this; } - } diff --git a/application-analytics-test/pom.xml b/application-analytics-test/pom.xml index fb14978e..4f5b4788 100644 --- a/application-analytics-test/pom.xml +++ b/application-analytics-test/pom.xml @@ -30,9 +30,9 @@ 1.0-SNAPSHOT application-analytics-test - Application Analytics (PRO)- Tests - Parent POM + Application Analytics (Pro)- Tests - Parent POM pom - Application Analytics (PRO) - Tests - Parent POM + Application Analytics (Pro) - Tests - Parent POM true