CVE-2023-35150: Improper Control of Generation of Code ('Code Injection') (CWE-94)
value: XWiki Platform is a generic wiki platform offering runtime services for applications built on top of it. Starting in version 2.40m-2 and prior to versions 14.4.8, 14.10.4, and 15.0, any user with view rights on any document can execute code with programming rights, leading to remote code execution by crafting an url with a dangerous payload. The problem has been patched in XWiki 15.0, 14.10.4 and 14.4.8.
CWE description: The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
CVSS v3 base score: 8
Published: 2023-06-23T17:15Z
Repository: https://github.com/xwiki/xwiki-platform
Language: Java
Files changed: xwiki-platform-core/pom.xml, xwiki-platform-core/xwiki-platform-extension/xwiki-platform-extension-handlers/xwiki-platform-extension-handler-xar/src/main/java/org/xwiki/extension/xar/internal/DefaultXARExtensionIndex.java, xwiki-platform-core/xwiki-platform-extension/xwiki-platform-extension-handlers/xwiki-platform-extension-handler-xar/src/main/resources/META-INF/components.txt, xwiki-platform-core/xwiki-platform-index/pom.xml, xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/pom.xml, xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/TaskConsumerScriptService.java, xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/resources/META-INF/components.txt, xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/TaskConsumerScriptServiceTest.java, xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/pom.xml, xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/TaskApplicationReadyListener.java, xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/migration/R140300000XWIKI19614DataMigration.java, xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/resources/META-INF/components.txt, xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/test/java/org/xwiki/index/migration/R140300000XWIKI19614DataMigrationTest.java, xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationCommon.xml, xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationConfig.xml, xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationGuestActions.xml, xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationMailClass.xml, xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationMemberActions.xml, xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationMembersCommon.xml, xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationCommonPageTest.java, xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationConfigPageTest.java, xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationGuestActionsPageTest.java, xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationMailClassPageTest.java, xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationMemberActionsPageTest.java, xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationMembersCommonPageTest.java, xwiki-platform-core/xwiki-platform-minimaldependencies/pom.xml, xwiki-platform-core/xwiki-platform-oldcore/pom.xml, xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/extension/XARExtensionIndex.java, xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/migration/AbstractDocumentsMigration.java, xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/migration/InvitationInternalDocumentParameterEscapingFixer.java, xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/migration/InvitationInternalDocumentParameterEscapingTaskConsumer.java, xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/migration/R150000000XWIKI20285DataMigration.java, xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/META-INF/components.txt, xwiki-platform-core/xwiki-platform-oldcore/src/test/java/org/xwiki/internal/migration/InvitationInternalDocumentParameterEscapingFixerTest.java, xwiki-platform-tools/xwiki-platform-tool-packager-plugin/pom.xml
Commit date: 2023-01-24 08:38:28 +0100
Commit message: XWIKI-20285: Improve escaping of the Invitation Application

- Introduce an internal role (DefaultXARExtensionIndex) to know if a document is provided by an extension
- Split index-api to index-api/index-default to be able to use the api in oldcore
- Fix the escaping in invitation-ui
- Provide an automatic fix during upgrade for potential escaping issues in generated code

Diff: diff --git a/xwiki-platform-core/pom.xml b/xwiki-platform-core/pom.xml
index 3204f4bb40124..758964a4c7bfa 100644
--- a/xwiki-platform-core/pom.xml
+++ b/xwiki-platform-core/pom.xml
@@ -177,10 +177,26 @@
                   <code>java.class.removed</code>
                   <old>class org.xwiki.office.viewer.script.DefaultOfficeViewerScriptService</old>
                 </item>
               </differences>
             </revapi.differences>
+            <revapi.differences>
+              <justification>Moved to xwiki-platform-index-default</justification>
+              <criticality>allowed</criticality>
+              <differences>
+                <item>
+                  <ignore>true</ignore>
+                  <code>java.class.removed</code>
+                  <old>class org.xwiki.index.migration.R140300000XWIKI19614DataMigration</old>
+                </item>
+                <item>
+                  <ignore>true</ignore>
+                  <code>java.class.removed</code>
+                  <old>class org.xwiki.index.migration.R140300001XWIKI19571DataMigration</old>
+                </item>
+              </differences>
+            </revapi.differences>
           </analysisConfiguration>
         </configuration>
       </plugin>
     </plugins>
   </build>
diff --git a/xwiki-platform-core/xwiki-platform-extension/xwiki-platform-extension-handlers/xwiki-platform-extension-handler-xar/src/main/java/org/xwiki/extension/xar/internal/DefaultXARExtensionIndex.java b/xwiki-platform-core/xwiki-platform-extension/xwiki-platform-extension-handlers/xwiki-platform-extension-handler-xar/src/main/java/org/xwiki/extension/xar/internal/DefaultXARExtensionIndex.java
new file mode 100644
index 0000000000000..c57cc9047e8bc
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-extension/xwiki-platform-extension-handlers/xwiki-platform-extension-handler-xar/src/main/java/org/xwiki/extension/xar/internal/DefaultXARExtensionIndex.java
@@ -0,0 +1,56 @@
+/*
+ * 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 org.xwiki.extension.xar.internal;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.xwiki.component.annotation.Component;
+import org.xwiki.extension.repository.InstalledExtensionRepository;
+import org.xwiki.extension.xar.internal.handler.XarExtensionHandler;
+import org.xwiki.extension.xar.internal.repository.XarInstalledExtensionRepository;
+import org.xwiki.internal.extension.XARExtensionIndex;
+import org.xwiki.model.reference.DocumentReference;
+
+/**
+ * Default implementation of {@link XARExtensionIndex}. Uses {@link XarInstalledExtensionRepository} to implement the
+ * operations.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@Component
+@Singleton
+public class DefaultXARExtensionIndex implements XARExtensionIndex
+{
+    @Inject
+    @Named(XarExtensionHandler.TYPE)
+    private InstalledExtensionRepository installedXARs;
+
+    @Override
+    public boolean isExtensionDocument(DocumentReference documentReference)
+    {
+        return !((XarInstalledExtensionRepository) this.installedXARs).getXarInstalledExtensions(documentReference)
+            .isEmpty();
+    }
+}
diff --git a/xwiki-platform-core/xwiki-platform-extension/xwiki-platform-extension-handlers/xwiki-platform-extension-handler-xar/src/main/resources/META-INF/components.txt b/xwiki-platform-core/xwiki-platform-extension/xwiki-platform-extension-handlers/xwiki-platform-extension-handler-xar/src/main/resources/META-INF/components.txt
index e08ce23721aed..0bb067fb11732 100644
--- a/xwiki-platform-core/xwiki-platform-extension/xwiki-platform-extension-handlers/xwiki-platform-extension-handler-xar/src/main/resources/META-INF/components.txt
+++ b/xwiki-platform-core/xwiki-platform-extension/xwiki-platform-extension-handlers/xwiki-platform-extension-handler-xar/src/main/resources/META-INF/components.txt
@@ -1,6 +1,7 @@
 org.xwiki.extension.xar.internal.DefaultXarExtensionConfiguration
+org.xwiki.extension.xar.internal.DefaultXARExtensionIndex
 org.xwiki.extension.xar.internal.delete.DocumentsDeletingListener
 org.xwiki.extension.xar.internal.doc.XarDocumentRevisionProvider
 org.xwiki.extension.xar.internal.doc.InstalledExtensionDocumentCustomizationDetector
 org.xwiki.extension.xar.internal.doc.InstalledExtensionDocumentListener
 org.xwiki.extension.xar.internal.doc.InstalledExtensionDocumentTree
diff --git a/xwiki-platform-core/xwiki-platform-index/pom.xml b/xwiki-platform-core/xwiki-platform-index/pom.xml
index 6ef99d44788b7..e7180ed52dc31 100644
--- a/xwiki-platform-core/xwiki-platform-index/pom.xml
+++ b/xwiki-platform-core/xwiki-platform-index/pom.xml
@@ -31,10 +31,11 @@
   <name>XWiki Platform - Index - Parent POM</name>
   <packaging>pom</packaging>
   <description>XWiki Platform - Index - Parent POM</description>
   <modules>
     <module>xwiki-platform-index-api</module>
+    <module>xwiki-platform-index-default</module>
     <module>xwiki-platform-index-ui</module>
     <module>xwiki-platform-index-tree</module>
   </modules>
   <profiles>
     <profile>
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/pom.xml b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/pom.xml
index 2fe906650a300..0498df0722085 100644
--- a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/pom.xml
+++ b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/pom.xml
@@ -30,11 +30,11 @@
   <artifactId>xwiki-platform-index-api</artifactId>
   <name>XWiki Platform - Index - API</name>
   <description>API to queue asynchronous tasks dedicated to the analysis of wiki pages.</description>
   <packaging>jar</packaging>
   <properties>
-    <xwiki.jacoco.instructionRatio>0.85</xwiki.jacoco.instructionRatio>
+    <xwiki.jacoco.instructionRatio>0.04</xwiki.jacoco.instructionRatio>
     <!-- Name to display by the Extension Manager -->
     <xwiki.extension.name>Index API</xwiki.extension.name>
     <xwiki.extension.category>api</xwiki.extension.category>
   </properties>
   <dependencies>
@@ -48,48 +48,18 @@
       <groupId>org.xwiki.platform</groupId>
       <artifactId>xwiki-platform-wiki-api</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
-      <groupId>org.xwiki.platform</groupId>
-      <artifactId>xwiki-platform-query-manager</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <!-- Required to have access to XWikiDocumentIndexingTask. -->
-    <dependency>
-      <groupId>org.xwiki.platform</groupId>
-      <artifactId>xwiki-platform-oldcore</artifactId>
-      <version>${project.version}</version>
+      <groupId>org.xwiki.commons</groupId>
+      <artifactId>xwiki-commons-stability</artifactId>
+      <version>${commons.version}</version>
     </dependency>
     <!-- Test dependencies. -->
     <dependency>
       <groupId>org.xwiki.commons</groupId>
       <artifactId>xwiki-commons-tool-test-component</artifactId>
       <version>${commons.version}</version>
       <scope>test</scope>
     </dependency>
-    <dependency>
-      <groupId>javax.servlet</groupId>
-      <artifactId>javax.servlet-api</artifactId>
-      <scope>test</scope>
-    </dependency>
   </dependencies>
-  <build>
-    <plugins>
-      <plugin>
-        <!-- Apply the Checkstyle configurations defined in the top level pom.xml file -->
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-checkstyle-plugin</artifactId>
-        <executions>
-          <execution>
-            <!-- Specify the "default" execution id so that the "blocker" one is always executed -->
-            <id>default</id>
-            <configuration>
-              <failsOnError>true</failsOnError>
-              <suppressionsLocation>${basedir}/src/checkstyle/checkstyle-suppressions.xml</suppressionsLocation>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
 </project>
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/TaskConsumerScriptService.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/TaskConsumerScriptService.java
index 174456460f0cd..4b935b3b6ea36 100644
--- a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/TaskConsumerScriptService.java
+++ b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/TaskConsumerScriptService.java
@@ -21,18 +21,16 @@
 
 import java.util.Map;
 
 import javax.inject.Inject;
 import javax.inject.Named;
-import javax.inject.Provider;
 import javax.inject.Singleton;
 
 import org.xwiki.component.annotation.Component;
 import org.xwiki.script.service.ScriptService;
 import org.xwiki.stability.Unstable;
-
-import com.xpn.xwiki.XWikiContext;
+import org.xwiki.wiki.descriptor.WikiDescriptorManager;
 
 /**
  * Provides the operations to interact with the task consumer from the scripts.
  *
  * @version $Id$
@@ -46,15 +44,15 @@ public class TaskConsumerScriptService implements ScriptService
 {
     @Inject
     private TaskManager taskManager;
 
     @Inject
-    private Provider<XWikiContext> xcontextProvider;
+    private WikiDescriptorManager wikiDescriptorManager;
 
     /**
      * @return the count of queued tasks, grouped by task type
      */
     public Map<String, Long> getQueueSizePerType()
     {
-        return this.taskManager.getQueueSizePerType(this.xcontextProvider.get().getWikiId());
+        return this.taskManager.getQueueSizePerType(this.wikiDescriptorManager.getCurrentWikiId());
     }
 }
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/resources/META-INF/components.txt b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/resources/META-INF/components.txt
index 29bbcf5e0a676..f32b85edfb607 100644
--- a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/resources/META-INF/components.txt
+++ b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/resources/META-INF/components.txt
@@ -1,9 +1 @@
-org.xwiki.index.internal.DefaultTasksManager
-org.xwiki.index.internal.TasksStore
-org.xwiki.index.internal.TaskExecutor
-org.xwiki.index.internal.TaskApplicationReadyListener
-org.xwiki.index.internal.DefaultLinksTaskConsumer
-org.xwiki.index.internal.listener.LinksUpdateListener
-org.xwiki.index.migration.R140300000XWIKI19614DataMigration
-org.xwiki.index.migration.R140300001XWIKI19571DataMigration
 org.xwiki.index.TaskConsumerScriptService
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/TaskConsumerScriptServiceTest.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/TaskConsumerScriptServiceTest.java
index f8bac58e2f622..340e8bc177862 100644
--- a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/TaskConsumerScriptServiceTest.java
+++ b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/TaskConsumerScriptServiceTest.java
@@ -19,20 +19,16 @@
  */
 package org.xwiki.index;
 
 import java.util.Map;
 
-import javax.inject.Provider;
-
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.mockito.Mock;
 import org.xwiki.test.junit5.mockito.ComponentTest;
 import org.xwiki.test.junit5.mockito.InjectMockComponents;
 import org.xwiki.test.junit5.mockito.MockComponent;
-
-import com.xpn.xwiki.XWikiContext;
+import org.xwiki.wiki.descriptor.WikiDescriptorManager;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.Mockito.when;
 
 /**
@@ -49,20 +45,17 @@
 
     @MockComponent
     private TaskManager taskManager;
 
     @MockComponent
-    private Provider<XWikiContext> xcontextProvider;
+    private WikiDescriptorManager wikiDescriptorManager;
 
-    @Mock
-    private XWikiContext xcontext;
 
     @BeforeEach
     void setUp()
     {
-        when(this.xcontextProvider.get()).thenReturn(this.xcontext);
-        when(this.xcontext.getWikiId()).thenReturn("xwiki");
+        when(this.wikiDescriptorManager.getCurrentWikiId()).thenReturn("xwiki");
     }
 
     @Test
     void getQueueSizePerType()
     {
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/pom.xml b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/pom.xml
new file mode 100644
index 0000000000000..0f0323b0ea8d6
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/pom.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.xwiki.platform</groupId>
+    <artifactId>xwiki-platform-index</artifactId>
+    <version>15.1-SNAPSHOT</version>
+  </parent>
+  <artifactId>xwiki-platform-index-default</artifactId>
+  <name>XWiki Platform - Index - Default</name>
+  <description>Default implementation of the API to queue asynchronous tasks dedicated to the analysis of wiki pages.</description>
+  <packaging>jar</packaging>
+  <properties>
+    <xwiki.jacoco.instructionRatio>0.84</xwiki.jacoco.instructionRatio>
+    <!-- Name to display by the Extension Manager -->
+    <xwiki.extension.name>Default implementation of the Index API</xwiki.extension.name>
+  </properties>
+  <dependencies>
+    <dependency>
+      <groupId>org.xwiki.platform</groupId>
+      <artifactId>xwiki-platform-index-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.xwiki.commons</groupId>
+      <artifactId>xwiki-commons-component-api</artifactId>
+      <version>${commons.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.xwiki.platform</groupId>
+      <artifactId>xwiki-platform-oldcore</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <!-- Test dependencies. -->
+    <dependency>
+      <groupId>org.xwiki.commons</groupId>
+      <artifactId>xwiki-commons-tool-test-component</artifactId>
+      <version>${commons.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <!-- Apply the Checkstyle configurations defined in the top level pom.xml file -->
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <executions>
+          <execution>
+            <!-- Specify the "default" execution id so that the "blocker" one is always executed -->
+            <id>default</id>
+            <configuration>
+              <failsOnError>true</failsOnError>
+              <suppressionsLocation>${basedir}/src/checkstyle/checkstyle-suppressions.xml</suppressionsLocation>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
\ No newline at end of file
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/checkstyle/checkstyle-suppressions.xml b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/checkstyle/checkstyle-suppressions.xml
similarity index 100%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/checkstyle/checkstyle-suppressions.xml
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/checkstyle/checkstyle-suppressions.xml
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/internal/DefaultLinksTaskConsumer.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/DefaultLinksTaskConsumer.java
similarity index 100%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/internal/DefaultLinksTaskConsumer.java
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/DefaultLinksTaskConsumer.java
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/internal/DefaultTasksManager.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/DefaultTasksManager.java
similarity index 100%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/internal/DefaultTasksManager.java
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/DefaultTasksManager.java
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/internal/TaskApplicationReadyListener.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/TaskApplicationReadyListener.java
similarity index 96%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/internal/TaskApplicationReadyListener.java
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/TaskApplicationReadyListener.java
index 2c15821aa014c..c0550883cc83a 100644
--- a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/internal/TaskApplicationReadyListener.java
+++ b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/TaskApplicationReadyListener.java
@@ -25,11 +25,10 @@
 import javax.inject.Singleton;
 
 import org.xwiki.bridge.event.ApplicationReadyEvent;
 import org.xwiki.component.annotation.Component;
 import org.xwiki.component.phase.Initializable;
-import org.xwiki.component.phase.InitializationException;
 import org.xwiki.index.TaskManager;
 import org.xwiki.observation.AbstractEventListener;
 import org.xwiki.observation.event.Event;
 
 import com.xpn.xwiki.XWikiContext;
@@ -71,11 +70,11 @@ public void onEvent(Event event, Object source, Object data)
             ((DefaultTasksManager) this.taskManager).startThread();
         }
     }
 
     @Override
-    public void initialize() throws InitializationException
+    public void initialize()
     {
         // If the application is already initialized we start the threads immediately (e.g. in case of extension
         // install) and the implementation of type DefaultTasksManager.
         if (this.contextProvider.get() != null && this.taskManager instanceof DefaultTasksManager) {
             ((DefaultTasksManager) this.taskManager).startThread();
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/internal/TaskExecutor.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/TaskExecutor.java
similarity index 100%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/internal/TaskExecutor.java
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/TaskExecutor.java
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/internal/TasksStore.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/TasksStore.java
similarity index 100%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/internal/TasksStore.java
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/TasksStore.java
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/internal/jmx/JMXTasks.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/jmx/JMXTasks.java
similarity index 100%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/internal/jmx/JMXTasks.java
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/jmx/JMXTasks.java
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/internal/jmx/JMXTasksMBean.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/jmx/JMXTasksMBean.java
similarity index 100%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/internal/jmx/JMXTasksMBean.java
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/jmx/JMXTasksMBean.java
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/internal/listener/LinksUpdateListener.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/listener/LinksUpdateListener.java
similarity index 100%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/internal/listener/LinksUpdateListener.java
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/internal/listener/LinksUpdateListener.java
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/migration/R140300000XWIKI19614DataMigration.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/migration/R140300000XWIKI19614DataMigration.java
similarity index 63%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/migration/R140300000XWIKI19614DataMigration.java
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/migration/R140300000XWIKI19614DataMigration.java
index aa534f65e5224..94727cb8b4235 100644
--- a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/migration/R140300000XWIKI19614DataMigration.java
+++ b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/migration/R140300000XWIKI19614DataMigration.java
@@ -19,56 +19,44 @@
  */
 package org.xwiki.index.migration;
 
 import java.util.List;
 
-import javax.inject.Inject;
 import javax.inject.Named;
-import javax.inject.Provider;
 import javax.inject.Singleton;
 
 import org.xwiki.component.annotation.Component;
-import org.xwiki.index.TaskManager;
+import org.xwiki.internal.migration.AbstractDocumentsMigration;
 import org.xwiki.query.Query;
 import org.xwiki.query.QueryException;
-import org.xwiki.query.QueryManager;
 
+import com.xpn.xwiki.XWiki;
 import com.xpn.xwiki.XWikiContext;
+import com.xpn.xwiki.doc.XWikiDocument;
 import com.xpn.xwiki.store.migration.DataMigrationException;
 import com.xpn.xwiki.store.migration.XWikiDBVersion;
-import com.xpn.xwiki.store.migration.hibernate.HibernateDataMigration;
 
 import static org.xwiki.index.internal.DefaultLinksTaskConsumer.LINKS_TASK_TYPE;
 
 /**
  * Migrates the links by reindexing all the documents of the farm.
  *
  * @version $Id$
  * @since 14.2RC1
  * @deprecated link storage and indexing moved to Solr (implemented in xwiki-platform-search-solr-api)
  */
-// TODO: Implement DataMigration once XWIKI-19399 is fixed.
 @Component
 @Singleton
 @Named(R140300000XWIKI19614DataMigration.HINT)
 @Deprecated(since = "14.8RC1")
-public class R140300000XWIKI19614DataMigration implements HibernateDataMigration
+public class R140300000XWIKI19614DataMigration extends AbstractDocumentsMigration
 {
     /**
      * The hint for this component.
      */
     public static final String HINT = "R140300000XWIKI19614";
 
-    @Inject
-    private QueryManager queryManager;
-
-    @Inject
-    private TaskManager taskManager;
-
-    @Inject
-    private Provider<XWikiContext> contextProvider;
-
     @Override
     public String getName()
     {
         return HINT;
     }
@@ -84,45 +72,49 @@ public XWikiDBVersion getVersion()
     {
         return new XWikiDBVersion(140900000);
     }
 
     @Override
-    public void migrate() throws DataMigrationException
+    protected String getTaskType()
+    {
+        return LINKS_TASK_TYPE;
+    }
+
+    @Override
+    protected List<String> selectDocuments() throws DataMigrationException
     {
-        XWikiContext context = this.contextProvider.get();
-        // No need to migrate if the wiki does not support backlinks.
+        List<String> documents;
+        XWikiContext context = getXWikiContext();
+        XWiki wiki = getXWikiContext().getWiki();
         if (context.getWiki().hasBacklinks(context)) {
-            String wikiId = context.getWikiId();
             try {
-                List<Long> ids =
-                    this.queryManager.createQuery("SELECT doc.id FROM XWikiDocument doc", Query.HQL).setWiki(wikiId)
-                        .execute();
-                for (Long id : ids) {
-                    this.taskManager.addTask(wikiId, id, LINKS_TASK_TYPE);
-                }
+                documents = wiki.getStore().getQueryManager()
+                    .createQuery("SELECT doc.fullName FROM XWikiDocument doc", Query.HQL)
+                    .setWiki(context.getWikiId())
+                    .execute();
             } catch (QueryException e) {
                 throw new DataMigrationException(
-                    String.format("Failed retrieve the list of all the documents for wiki [%s].", wikiId), e);
+                    String.format("Failed retrieve the list of all the documents for wiki [%s].", wiki.getName()), e);
             }
+        } else {
+            documents = List.of();
         }
+        return documents;
     }
 
     @Override
-    public boolean shouldExecute(XWikiDBVersion startupVersion)
+    protected void logBeforeQueuingTask(XWikiDocument document)
     {
-        return true;
+        // No logs here as it would be too verbose (all documents of the wiki are queued).
     }
 
     @Override
-    public String getPreHibernateLiquibaseChangeLog()
+    protected void logBeforeQueuingTasks(List<XWikiDocument> documents)
     {
-        // TODO: Remove once XWIKI-19399 is fixed.
-        return null;
-    }
-
-    @Override
-    public String getLiquibaseChangeLog()
-    {
-        // TODO: Remove once XWIKI-19399 is fixed.
-        return null;
+        XWikiContext context = getXWikiContext();
+        if (context.getWiki().hasBacklinks(context)) {
+            super.logBeforeQueuingTasks(documents);
+        } else {
+            this.logger.info("Skipped because backlinks are not supported on [{}]", context.getWikiId());
+        }
     }
 }
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/migration/R140300001XWIKI19571DataMigration.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/migration/R140300001XWIKI19571DataMigration.java
similarity index 100%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/main/java/org/xwiki/index/migration/R140300001XWIKI19571DataMigration.java
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/java/org/xwiki/index/migration/R140300001XWIKI19571DataMigration.java
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/resources/META-INF/components.txt b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/resources/META-INF/components.txt
new file mode 100644
index 0000000000000..5a46995d9b3a0
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/main/resources/META-INF/components.txt
@@ -0,0 +1,8 @@
+org.xwiki.index.internal.DefaultTasksManager
+org.xwiki.index.internal.TasksStore
+org.xwiki.index.internal.TaskExecutor
+org.xwiki.index.internal.TaskApplicationReadyListener
+org.xwiki.index.internal.DefaultLinksTaskConsumer
+org.xwiki.index.internal.listener.LinksUpdateListener
+org.xwiki.index.migration.R140300000XWIKI19614DataMigration
+org.xwiki.index.migration.R140300001XWIKI19571DataMigration
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/internal/DefaultLinksTaskConsumerTest.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/test/java/org/xwiki/index/internal/DefaultLinksTaskConsumerTest.java
similarity index 100%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/internal/DefaultLinksTaskConsumerTest.java
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/test/java/org/xwiki/index/internal/DefaultLinksTaskConsumerTest.java
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/internal/DefaultTasksManagerTest.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/test/java/org/xwiki/index/internal/DefaultTasksManagerTest.java
similarity index 100%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/internal/DefaultTasksManagerTest.java
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/test/java/org/xwiki/index/internal/DefaultTasksManagerTest.java
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/internal/TaskExecutorTest.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/test/java/org/xwiki/index/internal/TaskExecutorTest.java
similarity index 100%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/internal/TaskExecutorTest.java
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/test/java/org/xwiki/index/internal/TaskExecutorTest.java
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/internal/TasksStoreTest.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/test/java/org/xwiki/index/internal/TasksStoreTest.java
similarity index 100%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/internal/TasksStoreTest.java
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/test/java/org/xwiki/index/internal/TasksStoreTest.java
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/internal/listener/LinksUpdateListenerTest.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/test/java/org/xwiki/index/internal/listener/LinksUpdateListenerTest.java
similarity index 100%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/internal/listener/LinksUpdateListenerTest.java
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/test/java/org/xwiki/index/internal/listener/LinksUpdateListenerTest.java
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/migration/R140300000XWIKI19614DataMigrationTest.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/test/java/org/xwiki/index/migration/R140300000XWIKI19614DataMigrationTest.java
similarity index 57%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/migration/R140300000XWIKI19614DataMigrationTest.java
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/test/java/org/xwiki/index/migration/R140300000XWIKI19614DataMigrationTest.java
index f99ee490efce4..e7ac2714382f1 100644
--- a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/migration/R140300000XWIKI19614DataMigrationTest.java
+++ b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/test/java/org/xwiki/index/migration/R140300000XWIKI19614DataMigrationTest.java
@@ -19,31 +19,41 @@
  */
 package org.xwiki.index.migration;
 
 import java.util.List;
 
-import javax.inject.Provider;
-
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
 import org.mockito.Mock;
+import org.xwiki.context.Execution;
+import org.xwiki.context.ExecutionContext;
 import org.xwiki.index.TaskManager;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.model.reference.DocumentReferenceResolver;
 import org.xwiki.query.Query;
 import org.xwiki.query.QueryException;
 import org.xwiki.query.QueryManager;
+import org.xwiki.test.LogLevel;
+import org.xwiki.test.junit5.LogCaptureExtension;
 import org.xwiki.test.junit5.mockito.ComponentTest;
 import org.xwiki.test.junit5.mockito.InjectMockComponents;
 import org.xwiki.test.junit5.mockito.MockComponent;
 
 import com.xpn.xwiki.XWiki;
 import com.xpn.xwiki.XWikiContext;
+import com.xpn.xwiki.doc.XWikiDocument;
+import com.xpn.xwiki.store.XWikiStoreInterface;
 import com.xpn.xwiki.store.migration.DataMigrationException;
 import com.xpn.xwiki.store.migration.hibernate.HibernateDataMigration;
 
+import ch.qos.logback.classic.Level;
+
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
 
 /**
@@ -56,18 +66,24 @@
 class R140300000XWIKI19614DataMigrationTest
 {
     @InjectMockComponents(role = HibernateDataMigration.class)
     private R140300000XWIKI19614DataMigration migration;
 
-    @MockComponent
+    @Mock
     private QueryManager queryManager;
 
     @MockComponent
     private TaskManager taskManager;
 
     @MockComponent
-    private Provider<XWikiContext> contextProvider;
+    private Execution execution;
+
+    @MockComponent
+    private DocumentReferenceResolver<String> resolver;
+
+    @RegisterExtension
+    LogCaptureExtension logCapture = new LogCaptureExtension(LogLevel.INFO);
 
     @Mock
     private XWikiContext context;
 
     @Mock
@@ -77,30 +93,52 @@
     private Query query;
 
     @BeforeEach
     void setUp() throws Exception
     {
-        when(this.contextProvider.get()).thenReturn(this.context);
+        ExecutionContext executionContext = mock(ExecutionContext.class);
+        XWikiStoreInterface xWikiStoreInterface = mock(XWikiStoreInterface.class);
+
+        when(this.execution.getContext()).thenReturn(executionContext);
+        when(executionContext.getProperty("xwikicontext")).thenReturn(this.context);
         when(this.context.getWiki()).thenReturn(this.wiki);
+        when(this.wiki.getName()).thenReturn("wiki1");
         when(this.context.getWikiId()).thenReturn("wiki1");
-        when(this.queryManager.createQuery("SELECT doc.id FROM XWikiDocument doc",
+        when(this.wiki.getStore()).thenReturn(xWikiStoreInterface);
+        when(xWikiStoreInterface.getQueryManager()).thenReturn(this.queryManager);
+
+        when(this.queryManager.createQuery("SELECT doc.fullName FROM XWikiDocument doc",
             Query.HQL)).thenReturn(this.query);
         when(this.query.setWiki(any())).thenReturn(this.query);
     }
 
     @Test
     void migrate() throws Exception
     {
+        DocumentReference doc42 = new DocumentReference("xwiki", "XWiki", "Doc42");
+        DocumentReference doc43 = new DocumentReference("xwiki", "XWiki", "Doc43");
+        XWikiDocument xWikiDocument42 = mock(XWikiDocument.class);
+        XWikiDocument xWikiDocument43 = mock(XWikiDocument.class);
+
         when(this.wiki.hasBacklinks(this.context)).thenReturn(true);
 
-        when(this.query.execute()).thenReturn(List.of(42L, 43L));
+        when(this.query.execute()).thenReturn(List.of("xwiki.XWiki.Doc42", "xwiki.XWiki.Doc43"));
+        when(this.resolver.resolve("xwiki.XWiki.Doc42")).thenReturn(doc42);
+        when(this.resolver.resolve("xwiki.XWiki.Doc43")).thenReturn(doc43);
+        when(this.wiki.getDocument(doc42, this.context)).thenReturn(xWikiDocument42);
+        when(this.wiki.getDocument(doc43, this.context)).thenReturn(xWikiDocument43);
+        when(xWikiDocument42.getId()).thenReturn(42L);
+        when(xWikiDocument43.getId()).thenReturn(43L);
 
         this.migration.migrate();
 
         verify(this.query).setWiki("wiki1");
-        verify(this.taskManager).addTask("wiki1", 42L,  "links");
+        verify(this.taskManager).addTask("wiki1", 42L, "links");
         verify(this.taskManager).addTask("wiki1", 43L, "links");
+
+        assertEquals("[2] documents queued to task [links]", this.logCapture.getMessage(0));
+        assertEquals(Level.INFO, this.logCapture.getLogEvent(0).getLevel());
     }
 
     @Test
     void migrateQueryException() throws Exception
     {
@@ -109,12 +147,15 @@ void migrateQueryException() throws Exception
         when(this.query.execute()).thenThrow(QueryException.class);
 
         DataMigrationException queryException =
             assertThrows(DataMigrationException.class, () -> this.migration.migrate());
 
-        assertEquals("Failed retrieve the list of all the documents for wiki [wiki1].", queryException.getMessage());
-        assertEquals(QueryException.class, queryException.getCause().getClass());
+        assertEquals("Data migration R140300000XWIKI19614 failed", queryException.getMessage());
+        assertEquals("Failed retrieve the list of all the documents for wiki [wiki1].",
+            queryException.getCause().getMessage());
+        assertEquals(DataMigrationException.class, queryException.getCause().getClass());
+        assertEquals(QueryException.class, queryException.getCause().getCause().getClass());
 
         verify(this.query).setWiki("wiki1");
         verifyNoInteractions(this.taskManager);
     }
 
@@ -123,7 +164,9 @@ void migrateNotHasBacklinks() throws Exception
     {
         when(this.wiki.hasBacklinks(this.context)).thenReturn(false);
         this.migration.migrate();
         verifyNoInteractions(this.queryManager);
         verifyNoInteractions(this.taskManager);
+        assertEquals("Skipped because backlinks are not supported on [wiki1]", this.logCapture.getMessage(0));
+        assertEquals(Level.INFO, this.logCapture.getLogEvent(0).getLevel());
     }
 }
diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/migration/R140300001XWIKI19571DataMigrationTest.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/test/java/org/xwiki/index/migration/R140300001XWIKI19571DataMigrationTest.java
similarity index 100%
rename from xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-api/src/test/java/org/xwiki/index/migration/R140300001XWIKI19571DataMigrationTest.java
rename to xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-default/src/test/java/org/xwiki/index/migration/R140300001XWIKI19571DataMigrationTest.java
diff --git a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationCommon.xml b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationCommon.xml
index 1807432252467..a8215260b6527 100644
--- a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationCommon.xml
+++ b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationCommon.xml
@@ -55,11 +55,13 @@
 ##
 #if($request.getParameter('test') == '1')
   {{info}}testLoadInvitationConfig{{/info}}
   #testLoadInvitationConfig()
 #elseif($doc.documentReference.name == 'InvitationCommon')
-  {{info}}$services.localization.render('xe.invitation.internalDocument', ["${doc.getSpace()}.WebHome"]){{/info}}
+  #set ($linkTarget = "${doc.getSpace()}.WebHome")
+  #set ($linkTarget = $services.rendering.escape($linkTarget, 'xwiki/2.1'))
+  {{info}}$services.localization.render('xe.invitation.internalDocument', [$linkTarget]){{/info}}
 #end
 ##
 #*
  * Display a message for the sender (preview) or for the admin (review sent messages)
  *
@@ -184,12 +186,14 @@
       #set($defaultConfigObj = $thisDocument.getObject($configClassNameInternal))
       #foreach($element in $defaultConfigObj.getProperties())
         $config.put($element.getName(), $defaultConfigObj.getProperty($element.getName()).getValue())
       #end
       ##
-      #set($configDocContent = '{{velo' + 'city}}{{info}}$services.localization.render(''xe.invitation.internalDocument'', ["'
-                               + "$!config.get('mainPage')" + '"]){{/info}}{{/velo' + 'city}}')
+      #set($configDocContent = 
+        '{{velocity}}{{info}}$services.localization.render(''xe.invitation.internalDocument'','
+          + '[$services.rendering.escape("' + "$!config.get('mainPage')" 
+          + '", ''xwiki/2.1'')]){{/info}}{{/velocity}}')
       $configDoc.setContent($configDocContent)
       $configDoc.setParent($configClassNameInternal)
       #set($configObj = $configDoc.newObject($configClassNameInternal))
       #foreach($key in $config.keySet())
         $configObj.set($key, $config.get($key))
@@ -273,12 +277,14 @@
  * $mail (Map&lt;String, XObject&gt;) will be populated with email message XObjects by their messageID.
  *###
 #macro(loadInvitationMail, $config, $emailContainer, $mail)
   ## If this doesn't already exist, it's created.
   #if($emailContainer.isNew())
-    #set($emailContainerContent = '{{velo' + 'city}}{{info}}$services.localization.render(''xe.invitation.internalDocument'', ["'
-                                  + "$config.get('emailContainer')" + '"]){{/info}}{{/velo' + 'city}}')
+    #set($emailContainerContent = 
+      '{{velocity}}{{info}}$services.localization.render(''xe.invitation.internalDocument'','
+        + '[$services.rendering.escape("' + "$config.get('emailContainer')" 
+        + '", ''xwiki/2.1'')]){{/info}}{{/velocity}}')
     #set($discard = $emailContainer.setContent($emailContainerContent))
     #set($discard = $emailContainer.setHidden(true))
     #set($discard = $emailContainer.saveAsAuthor())
   #end
   ##
diff --git a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationConfig.xml b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationConfig.xml
index 5320cb6dfa49e..ab60cebcf8a68 100644
--- a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationConfig.xml
+++ b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationConfig.xml
@@ -18,11 +18,11 @@
  * 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="Invitation.InvitationConfig" locale="">
+<xwikidoc version="1.5" reference="Invitation.InvitationConfig" locale="">
   <web>Invitation</web>
   <name>InvitationConfig</name>
   <language/>
   <defaultLanguage/>
   <translation>0</translation>
@@ -32,11 +32,11 @@
   <contentAuthor>xwiki:XWiki.Admin</contentAuthor>
   <version>1.1</version>
   <title/>
   <comment/>
   <minorEdit>false</minorEdit>
-  <syntaxId>xwiki/2.0</syntaxId>
+  <syntaxId>xwiki/2.1</syntaxId>
   <hidden>true</hidden>
   <content>{{velocity}}{{info}}$services.localization.render('xe.invitation.internalDocument', ['Invitation.WebHome']){{/info}}{{/velocity}}</content>
   <attachment>
     <filename>InvitationSectionIcon.png</filename>
     <mimetype>image/png</mimetype>
diff --git a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationGuestActions.xml b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationGuestActions.xml
index d5bfc36b569d7..04e3ce7b7993f 100644
--- a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationGuestActions.xml
+++ b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationGuestActions.xml
@@ -18,11 +18,11 @@
  * 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.3" reference="Invitation.InvitationGuestActions" locale="">
+<xwikidoc version="1.5" reference="Invitation.InvitationGuestActions" locale="">
   <web>Invitation</web>
   <name>InvitationGuestActions</name>
   <language/>
   <defaultLanguage/>
   <translation>0</translation>
@@ -32,13 +32,13 @@
   <contentAuthor>xwiki:XWiki.Admin</contentAuthor>
   <version>1.1</version>
   <title/>
   <comment/>
   <minorEdit>false</minorEdit>
-  <syntaxId>xwiki/2.0</syntaxId>
+  <syntaxId>xwiki/2.1</syntaxId>
   <hidden>true</hidden>
-  <content>{{include reference="Invitation.InvitationCommon" /}}
+  <content>{{include reference="Invitation.InvitationCommon"/}}
 
 {{velocity}}
 #*
  * Invitation Application
  * Options for guests who are invited.
@@ -91,11 +91,13 @@
             $request.getParameter('memo'),
             $request.getParameter('confirm'),
             $mail,
             $emailContainer)
 #else
-  {{info}}$services.localization.render('xe.invitation.internalDocument', ["${doc.getSpace()}.WebHome"]){{/info}}
+  #set ($linkTarget = "${doc.getSpace()}.WebHome")
+  #set ($linkTarget = $services.rendering.escape($linkTarget, 'xwiki/2.1'))
+  {{info}}$services.localization.render('xe.invitation.internalDocument', [$linkTarget]){{/info}}
 #end
 ##
 ##---------------------------------------------------------------------
 ## The macros (Nothing below this point is run directly)
 ##---------------------------------------------------------------------
diff --git a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationMailClass.xml b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationMailClass.xml
index b8fbdd8a3c9b5..52e4f3a94b00c 100644
--- a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationMailClass.xml
+++ b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationMailClass.xml
@@ -18,11 +18,11 @@
  * 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.3" reference="Invitation.InvitationMailClass" locale="">
+<xwikidoc version="1.5" reference="Invitation.InvitationMailClass" locale="">
   <web>Invitation</web>
   <name>InvitationMailClass</name>
   <language/>
   <defaultLanguage/>
   <translation>0</translation>
@@ -32,13 +32,17 @@
   <contentAuthor>xwiki:XWiki.Admin</contentAuthor>
   <version>1.1</version>
   <title>InvitationMailClass</title>
   <comment/>
   <minorEdit>false</minorEdit>
-  <syntaxId>xwiki/2.0</syntaxId>
+  <syntaxId>xwiki/2.1</syntaxId>
   <hidden>true</hidden>
-  <content>{{velocity}}{{info}}$services.localization.render('xe.invitation.internalDocument', ["${doc.getSpace()}.WebHome"]){{/info}}{{/velocity}}</content>
+  <content>{{velocity}}
+  #set ($linkTarget = "${doc.getSpace()}.WebHome")
+  #set ($linkTarget = $services.rendering.escape($linkTarget, 'xwiki/2.1'))
+  {{info}}$services.localization.render('xe.invitation.internalDocument', [$linkTarget]){{/info}}
+{{/velocity}}</content>
   <class>
     <name>Invitation.InvitationMailClass</name>
     <customClass/>
     <customMapping/>
     <defaultViewSheet/>
@@ -119,14 +123,14 @@
       <multiSelect>0</multiSelect>
       <name>sendingUser</name>
       <number>3</number>
       <picker>1</picker>
       <prettyName>sendingUser</prettyName>
-      <size>30</size>
       <relationalStorage>0</relationalStorage>
       <separator> </separator>
       <separators/>
+      <size>30</size>
       <sort>none</sort>
       <unmodifiable>0</unmodifiable>
       <usesList>0</usesList>
       <validationMessage/>
       <validationRegExp/>
diff --git a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationMemberActions.xml b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationMemberActions.xml
index 4fffcf9168e28..0a050ed930948 100644
--- a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationMemberActions.xml
+++ b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationMemberActions.xml
@@ -18,11 +18,11 @@
  * 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.3" reference="Invitation.InvitationMemberActions" locale="">
+<xwikidoc version="1.5" reference="Invitation.InvitationMemberActions" locale="">
   <web>Invitation</web>
   <name>InvitationMemberActions</name>
   <language/>
   <defaultLanguage/>
   <translation>0</translation>
@@ -32,15 +32,15 @@
   <contentAuthor>xwiki:XWiki.Admin</contentAuthor>
   <version>1.1</version>
   <title>$services.localization.render('xe.invitation.tools.heading')</title>
   <comment/>
   <minorEdit>false</minorEdit>
-  <syntaxId>xwiki/2.0</syntaxId>
+  <syntaxId>xwiki/2.1</syntaxId>
   <hidden>true</hidden>
-  <content>{{include reference="Invitation.InvitationCommon" /}}
+  <content>{{include reference="Invitation.InvitationCommon"/}}
 
-{{include reference="Invitation.InvitationMembersCommon" /}}
+{{include reference="Invitation.InvitationMembersCommon"/}}
 
 {{velocity}}
 #*
  * Invitation Application
  * This script is only for use by registered members to inspect messages and do actions on them (cancel invitations)
@@ -118,11 +118,13 @@
     #inspectMessages($mail, $inspect, $isAdmin, $request.getParameter('messageID'))
     #invitationFooter($mail, $request.getParameterMap(), $isAdmin, $config)
   #else
     ##
     ## No orders, Lets just explain what this page is for.
-    {{info}}$services.localization.render('xe.invitation.internalDocument', ["${doc.getSpace()}.WebHome"]){{/info}}
+    #set ($linkTarget = "${doc.getSpace()}.WebHome")
+    #set ($linkTarget = $services.rendering.escape($linkTarget, 'xwiki/2.1'))
+    {{info}}$services.localization.render('xe.invitation.internalDocument', [$linkTarget]){{/info}}
   #end
 #end
 ##
 ## Prevent $mail and config from leaking if this page is included in another page.
 #set($mail = 0)
diff --git a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationMembersCommon.xml b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationMembersCommon.xml
index ba61c6058563d..e48e5c8aa623a 100644
--- a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationMembersCommon.xml
+++ b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationMembersCommon.xml
@@ -18,11 +18,11 @@
  * 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.3" reference="Invitation.InvitationMembersCommon" locale="">
+<xwikidoc version="1.5" reference="Invitation.InvitationMembersCommon" locale="">
   <web>Invitation</web>
   <name>InvitationMembersCommon</name>
   <language/>
   <defaultLanguage/>
   <translation>0</translation>
@@ -32,11 +32,11 @@
   <contentAuthor>xwiki:XWiki.Admin</contentAuthor>
   <version>1.1</version>
   <title/>
   <comment/>
   <minorEdit>false</minorEdit>
-  <syntaxId>xwiki/2.0</syntaxId>
+  <syntaxId>xwiki/2.1</syntaxId>
   <hidden>true</hidden>
   <content>{{velocity}}
 #*
  * Invitation Application
  * This document contains common macros used by all documents in the Invitation Application which are 
@@ -51,11 +51,13 @@
  * $escapetool an object of class org.apache.velocity.tools.generic.EscapeTool
  *
  * Macros also depend on other macros but only other macros which are contained in this script.
  *###
 #if($doc.documentReference.name == 'InvitationMembersCommon')
-  {{info}}$services.localization.render('xe.invitation.internalDocument', ["${doc.getSpace()}.WebHome"]){{/info}}
+  #set ($linkTarget = "${doc.getSpace()}.WebHome")
+  #set ($linkTarget = $services.rendering.escape($linkTarget, 'xwiki/2.1'))
+  {{info}}$services.localization.render('xe.invitation.internalDocument', [$linkTarget]){{/info}}
 #end
 ##
 #*
  * Display a table of messages.
  *
diff --git a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationCommonPageTest.java b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationCommonPageTest.java
index 915724187db70..b2afed39a2c8a 100644
--- a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationCommonPageTest.java
+++ b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationCommonPageTest.java
@@ -87,15 +87,15 @@ void setUp() throws Exception
 
     @Test
     void testEq0() throws Exception
     {
         this.context.setDoc(this.xwiki.getDocument(
-            new DocumentReference("xwiki", "<script>console.log</script>", "InvitationCommon"), this.context));
+            new DocumentReference("xwiki", "]]  {{noscript/}}", "InvitationCommon"), this.context));
 
         DocumentReference invitationCommonReference = new DocumentReference("xwiki", "Invitation", "InvitationCommon");
         Document document = Jsoup.parse(loadPage(invitationCommonReference).getRenderedContent(this.context));
-        assertEquals("xe.invitation.internalDocument [<script>console\\.log</script>.WebHome]",
+        assertEquals("xe.invitation.internalDocument []] {{noscript/}}.WebHome]",
             document.selectFirst(".infomessage").text());
     }
 
     @Test
     void testEq1ConfigClassIsNew() throws Exception
diff --git a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationConfigPageTest.java b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationConfigPageTest.java
new file mode 100644
index 0000000000000..ab1e4a036962c
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationConfigPageTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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 org.xwiki.invitation;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.junit.jupiter.api.Test;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.rendering.RenderingScriptServiceComponentList;
+import org.xwiki.rendering.internal.configuration.DefaultExtendedRenderingConfiguration;
+import org.xwiki.rendering.internal.configuration.RenderingConfigClassDocumentConfigurationSource;
+import org.xwiki.rendering.internal.macro.message.InfoMessageMacro;
+import org.xwiki.test.annotation.ComponentList;
+import org.xwiki.test.page.HTML50ComponentList;
+import org.xwiki.test.page.PageTest;
+import org.xwiki.test.page.TestNoScriptMacro;
+import org.xwiki.test.page.XWikiSyntax21ComponentList;
+
+import com.xpn.xwiki.doc.XWikiDocument;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test of {@code Invitation.InvitationConfig}.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@HTML50ComponentList
+@XWikiSyntax21ComponentList
+@RenderingScriptServiceComponentList
+@ComponentList({
+    InfoMessageMacro.class,
+    TestNoScriptMacro.class,
+    // Start - Required in addition of RenderingScriptServiceComponentList
+    DefaultExtendedRenderingConfiguration.class,
+    RenderingConfigClassDocumentConfigurationSource.class,
+    // End - Required in additional of RenderingScriptServiceComponentList
+})
+class InvitationConfigPageTest extends PageTest
+{
+    private static final DocumentReference INVITATION_CONFIG_DOCUMENT_REFERENCE =
+        new DocumentReference("xwiki", "Invitation", "InvitationConfig");
+
+    @Test
+    void escapeInfoMessageInternalDocumentParameter() throws Exception
+    {
+        XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_CONFIG_DOCUMENT_REFERENCE);
+
+        // Set up the current doc in the context so that $doc is bound in scripts
+        this.context.setDoc(
+            this.xwiki.getDocument(new DocumentReference("xwiki", "]] {{noscript/}}", "Page"), this.context));
+
+        Document document = Jsoup.parse(invitationGuestActionsDocument.getRenderedContent(this.context));
+        Element infomessage = document.selectFirst(".infomessage");
+        assertEquals("xe.invitation.internalDocument [Invitation.WebHome]", infomessage.text());
+    }
+}
diff --git a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationGuestActionsPageTest.java b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationGuestActionsPageTest.java
new file mode 100644
index 0000000000000..c88215464fbe6
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationGuestActionsPageTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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 org.xwiki.invitation;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.rendering.RenderingScriptServiceComponentList;
+import org.xwiki.rendering.internal.configuration.DefaultExtendedRenderingConfiguration;
+import org.xwiki.rendering.internal.configuration.RenderingConfigClassDocumentConfigurationSource;
+import org.xwiki.rendering.internal.macro.message.InfoMessageMacro;
+import org.xwiki.test.annotation.ComponentList;
+import org.xwiki.test.page.HTML50ComponentList;
+import org.xwiki.test.page.PageTest;
+import org.xwiki.test.page.TestNoScriptMacro;
+import org.xwiki.test.page.XWikiSyntax21ComponentList;
+
+import com.xpn.xwiki.doc.XWikiDocument;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test of {@code Invitation.InvitationGuestActions}.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@HTML50ComponentList
+@XWikiSyntax21ComponentList
+@RenderingScriptServiceComponentList
+@ComponentList({
+    InfoMessageMacro.class,
+    TestNoScriptMacro.class,
+    // Start - Required in addition of RenderingScriptServiceComponentList
+    DefaultExtendedRenderingConfiguration.class,
+    RenderingConfigClassDocumentConfigurationSource.class,
+    // End - Required in additional of RenderingScriptServiceComponentList
+})
+class InvitationGuestActionsPageTest extends PageTest
+{
+    private static final DocumentReference INVITATION_COMMON_DOCUMENT_REFERENCE =
+        new DocumentReference("xwiki", "Invitation", "InvitationCommon");
+
+    private static final DocumentReference INVITATION_GUEST_ACTIONS_DOCUMENT_REFERENCE =
+        new DocumentReference("xwiki", "Invitation", "InvitationGuestActions");
+
+    @BeforeEach
+    void setUp() throws Exception
+    {
+        loadPage(INVITATION_COMMON_DOCUMENT_REFERENCE);
+    }
+
+    @Test
+    void escapeInfoMessageInternalDocumentParameter() throws Exception
+    {
+        XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_GUEST_ACTIONS_DOCUMENT_REFERENCE);
+
+        // Set up the current doc in the context so that $doc is bound in scripts
+        this.context.setDoc(
+            this.xwiki.getDocument(new DocumentReference("xwiki", "]] {{noscript/}}", "Page"), this.context));
+
+        Document document = Jsoup.parse(invitationGuestActionsDocument.getRenderedContent(this.context));
+        Element infomessage = document.selectFirst(".infomessage");
+        assertEquals("xe.invitation.internalDocument []] {{noscript/}}.WebHome]", infomessage.text());
+    }
+}
diff --git a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationMailClassPageTest.java b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationMailClassPageTest.java
new file mode 100644
index 0000000000000..bb50f88d9d405
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationMailClassPageTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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 org.xwiki.invitation;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.junit.jupiter.api.Test;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.rendering.RenderingScriptServiceComponentList;
+import org.xwiki.rendering.internal.configuration.DefaultExtendedRenderingConfiguration;
+import org.xwiki.rendering.internal.configuration.RenderingConfigClassDocumentConfigurationSource;
+import org.xwiki.rendering.internal.macro.message.InfoMessageMacro;
+import org.xwiki.test.annotation.ComponentList;
+import org.xwiki.test.page.HTML50ComponentList;
+import org.xwiki.test.page.PageTest;
+import org.xwiki.test.page.TestNoScriptMacro;
+import org.xwiki.test.page.XWikiSyntax21ComponentList;
+
+import com.xpn.xwiki.doc.XWikiDocument;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test of {@code Invitation.InvitationMailClass}.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@HTML50ComponentList
+@XWikiSyntax21ComponentList
+@RenderingScriptServiceComponentList
+@ComponentList({
+    InfoMessageMacro.class,
+    TestNoScriptMacro.class,
+    // Start - Required in addition of RenderingScriptServiceComponentList
+    DefaultExtendedRenderingConfiguration.class,
+    RenderingConfigClassDocumentConfigurationSource.class,
+    // End - Required in additional of RenderingScriptServiceComponentList
+})
+class InvitationMailClassPageTest extends PageTest
+{
+    private static final DocumentReference INVITATION_MAIL_CLASS_DOCUMENT_REFERENCE =
+        new DocumentReference("xwiki", "Invitation", "InvitationMailClass");
+
+    @Test
+    void escapeInfoMessageInternalDocumentParameter() throws Exception
+    {
+        XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_MAIL_CLASS_DOCUMENT_REFERENCE);
+
+        // Set up the current doc in the context so that $doc is bound in scripts
+        this.context.setDoc(
+            this.xwiki.getDocument(new DocumentReference("xwiki", "]] {{noscript/}}", "Page"), this.context));
+
+        Document document = Jsoup.parse(invitationGuestActionsDocument.getRenderedContent(this.context));
+        Element infomessage = document.selectFirst(".infomessage");
+        assertEquals("xe.invitation.internalDocument []] {{noscript/}}.WebHome]", infomessage.text());
+    }
+}
diff --git a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationMemberActionsPageTest.java b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationMemberActionsPageTest.java
new file mode 100644
index 0000000000000..f6975f1880cc8
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationMemberActionsPageTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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 org.xwiki.invitation;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.rendering.RenderingScriptServiceComponentList;
+import org.xwiki.rendering.internal.configuration.DefaultExtendedRenderingConfiguration;
+import org.xwiki.rendering.internal.configuration.RenderingConfigClassDocumentConfigurationSource;
+import org.xwiki.rendering.internal.macro.message.InfoMessageMacro;
+import org.xwiki.test.annotation.ComponentList;
+import org.xwiki.test.page.HTML50ComponentList;
+import org.xwiki.test.page.PageTest;
+import org.xwiki.test.page.TestNoScriptMacro;
+import org.xwiki.test.page.XWikiSyntax21ComponentList;
+
+import com.xpn.xwiki.doc.XWikiDocument;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test of {@code Invitation.InvitationMemberActions}.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@HTML50ComponentList
+@XWikiSyntax21ComponentList
+@RenderingScriptServiceComponentList
+@ComponentList({
+    InfoMessageMacro.class,
+    TestNoScriptMacro.class,
+    // Start - Required in addition of RenderingScriptServiceComponentList
+    DefaultExtendedRenderingConfiguration.class,
+    RenderingConfigClassDocumentConfigurationSource.class,
+    // End - Required in additional of RenderingScriptServiceComponentList
+})
+class InvitationMemberActionsPageTest extends PageTest
+{
+    private static final DocumentReference INVITATION_COMMON_DOCUMENT_REFERENCE =
+        new DocumentReference("xwiki", "Invitation", "InvitationCommon");
+
+    private static final DocumentReference INVITATION_MEMBER_ACTIONS_DOCUMENT_REFERENCE =
+        new DocumentReference("xwiki", "Invitation", "InvitationMemberActions");
+
+    @BeforeEach
+    void setUp() throws Exception
+    {
+        loadPage(INVITATION_COMMON_DOCUMENT_REFERENCE);
+    }
+
+    @Test
+    void escapeInfoMessageInternalDocumentParameter() throws Exception
+    {
+        XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_MEMBER_ACTIONS_DOCUMENT_REFERENCE);
+
+        // Set a non-guest user as otherwise the rendering stops early.
+        this.context.setUserReference(new DocumentReference("xwiki", "XWiki", "U1"));
+
+        // Set up the current doc in the context so that $doc is bound in scripts
+        this.context.setDoc(
+            this.xwiki.getDocument(new DocumentReference("xwiki", "]] {{noscript/}}", "Page"), this.context));
+
+        Document document = Jsoup.parse(invitationGuestActionsDocument.getRenderedContent(this.context));
+        Element infomessage = document.selectFirst(".infomessage");
+        assertEquals("xe.invitation.internalDocument []] {{noscript/}}.WebHome]", infomessage.text());
+    }
+}
diff --git a/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationMembersCommonPageTest.java b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationMembersCommonPageTest.java
new file mode 100644
index 0000000000000..aa0ca6bab0b0d
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationMembersCommonPageTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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 org.xwiki.invitation;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.junit.jupiter.api.Test;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.rendering.RenderingScriptServiceComponentList;
+import org.xwiki.rendering.internal.configuration.DefaultExtendedRenderingConfiguration;
+import org.xwiki.rendering.internal.configuration.RenderingConfigClassDocumentConfigurationSource;
+import org.xwiki.rendering.internal.macro.message.InfoMessageMacro;
+import org.xwiki.test.annotation.ComponentList;
+import org.xwiki.test.page.HTML50ComponentList;
+import org.xwiki.test.page.PageTest;
+import org.xwiki.test.page.TestNoScriptMacro;
+import org.xwiki.test.page.XWikiSyntax21ComponentList;
+
+import com.xpn.xwiki.doc.XWikiDocument;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test of {@code Invitation.InvitationMembersCommon}.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@HTML50ComponentList
+@XWikiSyntax21ComponentList
+@RenderingScriptServiceComponentList
+@ComponentList({
+    InfoMessageMacro.class,
+    TestNoScriptMacro.class,
+    // Start - Required in addition of RenderingScriptServiceComponentList
+    DefaultExtendedRenderingConfiguration.class,
+    RenderingConfigClassDocumentConfigurationSource.class,
+    // End - Required in additional of RenderingScriptServiceComponentList
+})
+class InvitationMembersCommonPageTest extends PageTest
+{
+    private static final DocumentReference INVITATION_MEMBER_COMMON_DOCUMENT_REFERENCE =
+        new DocumentReference("xwiki", "Invitation", "InvitationMembersCommon");
+
+    @Test
+    void escapeInfoMessageInternalDocumentParameter() throws Exception
+    {
+        XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_MEMBER_COMMON_DOCUMENT_REFERENCE);
+
+        // Set a non-guest user as otherwise the rendering stops early.
+        this.context.setUserReference(new DocumentReference("xwiki", "XWiki", "U1"));
+
+        // Set up the current doc in the context so that $doc is bound in scripts
+        this.context.setDoc(
+            this.xwiki.getDocument(new DocumentReference("xwiki", "]] {{noscript/}}", "InvitationMembersCommon"),
+                this.context));
+
+        Document document = Jsoup.parse(invitationGuestActionsDocument.getRenderedContent(this.context));
+        assertEquals("xe.invitation.internalDocument []] {{noscript/}}.WebHome]",
+            document.selectFirst(".infomessage").text());
+    }
+}
diff --git a/xwiki-platform-core/xwiki-platform-minimaldependencies/pom.xml b/xwiki-platform-core/xwiki-platform-minimaldependencies/pom.xml
index 2d7fb56692468..b4f32faaf3c7a 100644
--- a/xwiki-platform-core/xwiki-platform-minimaldependencies/pom.xml
+++ b/xwiki-platform-core/xwiki-platform-minimaldependencies/pom.xml
@@ -347,10 +347,17 @@
       <groupId>org.xwiki.platform</groupId>
       <artifactId>xwiki-platform-model-validation-default</artifactId>
       <version>${project.version}</version>
       <scope>runtime</scope>
     </dependency>
+    <!-- The default document task analysis implementation -->
+    <dependency>
+      <groupId>org.xwiki.platform</groupId>
+      <artifactId>xwiki-platform-index-default</artifactId>
+      <version>${project.version}</version>
+      <scope>runtime</scope>
+    </dependency>
 
     <!-- ********************************************************************** -->
     <!-- Plugins, hard to install as extensions (we need to get rid of all that) -->
 
     <!-- Skin Extension -->
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/pom.xml b/xwiki-platform-core/xwiki-platform-oldcore/pom.xml
index 726107929739d..f400e4d59f439 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/pom.xml
+++ b/xwiki-platform-core/xwiki-platform-oldcore/pom.xml
@@ -638,10 +638,15 @@
     <dependency>
       <groupId>org.mortbay.jasper</groupId>
       <artifactId>apache-el</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.xwiki.platform</groupId>
+      <artifactId>xwiki-platform-index-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
   <build>
     <plugins>
       <!-- Publish a Core Test JAR -->
       <plugin>
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/extension/XARExtensionIndex.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/extension/XARExtensionIndex.java
new file mode 100644
index 0000000000000..185c05412e4fb
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/extension/XARExtensionIndex.java
@@ -0,0 +1,41 @@
+/*
+ * 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 org.xwiki.internal.extension;
+
+import org.xwiki.component.annotation.Role;
+import org.xwiki.model.reference.DocumentReference;
+
+/**
+ * Provides operation to access to information relative to XARs provided by extensions.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@Role
+public interface XARExtensionIndex
+{
+    /**
+     * @param documentReference a document reference
+     * @return {@code true} if a given document reference is provided by an extension
+     */
+    boolean isExtensionDocument(DocumentReference documentReference);
+}
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/migration/AbstractDocumentsMigration.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/migration/AbstractDocumentsMigration.java
new file mode 100644
index 0000000000000..2765b8975370b
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/migration/AbstractDocumentsMigration.java
@@ -0,0 +1,120 @@
+/*
+ * 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 org.xwiki.internal.migration;
+
+import java.util.List;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import org.slf4j.Logger;
+import org.xwiki.index.TaskManager;
+import org.xwiki.model.reference.DocumentReferenceResolver;
+
+import com.xpn.xwiki.XWikiContext;
+import com.xpn.xwiki.XWikiException;
+import com.xpn.xwiki.doc.XWikiDocument;
+import com.xpn.xwiki.store.migration.DataMigrationException;
+import com.xpn.xwiki.store.migration.hibernate.AbstractHibernateDataMigration;
+
+import static java.util.stream.Collectors.toList;
+
+/**
+ * Allow to easily queue a document analysis task on a set documents to migrate. Sub-classes need to implement two
+ * methods:
+ * <ul>
+ *     <li>{@link #selectDocuments()}: return the list of document ids to queue for migration</li>
+ *     <li>{@link #getTaskType()}: the type of the task to queue documents to</li>
+ * </ul>
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+public abstract class AbstractDocumentsMigration extends AbstractHibernateDataMigration
+{
+    @Inject
+    protected Logger logger;
+
+    @Inject
+    protected DocumentReferenceResolver<String> documentReferenceResolver;
+
+    @Inject
+    private TaskManager taskManager;
+
+    @Override
+    protected void hibernateMigrate() throws DataMigrationException
+    {
+        List<XWikiDocument> documents = selectDocuments()
+            .stream()
+            .map(this::convert)
+            .filter(Optional::isPresent)
+            .map(Optional::get)
+            .collect(toList());
+        logBeforeQueuingTasks(documents);
+        for (XWikiDocument document : documents) {
+            logBeforeQueuingTask(document);
+            this.taskManager.addTask(this.getXWikiContext().getWikiId(), document.getId(), getTaskType());
+        }
+    }
+
+    private Optional<XWikiDocument> convert(String documentReference)
+    {
+        XWikiContext context = getXWikiContext();
+        try {
+            return Optional.of(
+                context.getWiki().getDocument(this.documentReferenceResolver.resolve(documentReference), context));
+        } catch (XWikiException e) {
+            this.logger.error("Failed to resolve [{}]", documentReference, e);
+            return Optional.empty();
+        }
+    }
+
+    /**
+     * @return the id of the task type to queue documents to
+     */
+    protected abstract String getTaskType();
+
+    /**
+     * @return the list of document ids to migrate
+     */
+    protected abstract List<String> selectDocuments() throws DataMigrationException;
+
+    /**
+     * Prints an info log with the number of queued documents and the type of the task.
+     *
+     * @param documents the full list of documents that will be queued
+     */
+    protected void logBeforeQueuingTasks(List<XWikiDocument> documents)
+    {
+        this.logger.info("[{}] documents queued to task [{}]", documents.size(), getTaskType());
+    }
+
+    /**
+     * Prints an info logs with an individual document and well as its queued task.
+     *
+     * @param document a individual document that will be queued
+     */
+    protected void logBeforeQueuingTask(XWikiDocument document)
+    {
+        this.logger.info("document [{}] queued to task [{}]", document, getTaskType());
+    }
+}
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/migration/InvitationInternalDocumentParameterEscapingFixer.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/migration/InvitationInternalDocumentParameterEscapingFixer.java
new file mode 100644
index 0000000000000..007b177ae30ba
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/migration/InvitationInternalDocumentParameterEscapingFixer.java
@@ -0,0 +1,76 @@
+/*
+ * 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 org.xwiki.internal.migration;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.regex.Pattern;
+
+import javax.inject.Singleton;
+
+import org.xwiki.component.annotation.Component;
+import org.xwiki.rendering.syntax.Syntax;
+
+import static java.util.regex.Matcher.quoteReplacement;
+
+/**
+ * Fix a content by looking for localization of {@code xe.invitation.internalDocument} and escaping its parameter. The
+ * translation fixed by this method is initially introduced by the invitation application but the fix is localed in
+ * oldcore so that the fix is applied on pages even if the invitation application has been uninstalled.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@Component(roles = InvitationInternalDocumentParameterEscapingFixer.class)
+@Singleton
+public class InvitationInternalDocumentParameterEscapingFixer
+{
+    private static final Pattern PATTERN = Pattern.compile("(\\{\\{info}}"
+        + "\\$services\\.localization\\.render\\('xe\\.invitation\\.internalDocument', \\[)(\"[^\"]+\")(]\\)\\"
+        + "{\\{/info}})");
+
+    /**
+     * @param content the context to fix
+     * @param syntax the syntax of the context to fix (xwiki/2.0 or xwiki/2.1)
+     * @return the context with the fix applied, or {@link Optional#empty()} if nothing needs to be fixed
+     */
+    public Optional<String> fix(String content, Syntax syntax)
+    {
+        String escapedContent = PATTERN.matcher(content).replaceAll(matchResult -> {
+            if (matchResult.group(2).contains("services.rendering.escape")) {
+                return matchResult.group();
+            } else {
+                // Concatenate the various groups of the regex while wrapping the localization argument with a
+                // call to the escaping script service.
+                String format = String.format("%s$services.rendering.escape(%s, '%s')%s", matchResult.group(1),
+                    matchResult.group(2), syntax.toIdString(), matchResult.group(3));
+                return quoteReplacement(format);
+            }
+        });
+
+        if (Objects.equals(escapedContent, content)) {
+            return Optional.empty();
+        } else {
+            return Optional.of(escapedContent);
+        }
+    }
+}
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/migration/InvitationInternalDocumentParameterEscapingTaskConsumer.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/migration/InvitationInternalDocumentParameterEscapingTaskConsumer.java
new file mode 100644
index 0000000000000..57731bdacdd70
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/migration/InvitationInternalDocumentParameterEscapingTaskConsumer.java
@@ -0,0 +1,106 @@
+/*
+ * 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 org.xwiki.internal.migration;
+
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.slf4j.Logger;
+import org.xwiki.component.annotation.Component;
+import org.xwiki.index.IndexException;
+import org.xwiki.index.TaskConsumer;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.rendering.syntax.Syntax;
+
+import com.xpn.xwiki.XWikiContext;
+import com.xpn.xwiki.XWikiException;
+import com.xpn.xwiki.doc.XWikiDocument;
+
+/**
+ * Apply {@link InvitationInternalDocumentParameterEscapingFixer} on the documents queued by
+ * {@link R150000000XWIKI20285DataMigration} if applicable, and log the skipped ones. This translation key is initially
+ * introduced by the invitation application but the fix is localed in oldcore so that the fix is applied on pages even
+ * if the invitation application has been uninstalled.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@Component
+@Singleton
+@Named(InvitationInternalDocumentParameterEscapingTaskConsumer.HINT)
+public class InvitationInternalDocumentParameterEscapingTaskConsumer implements TaskConsumer
+{
+    /**
+     * This task consumer hint.
+     */
+    public static final String HINT = "internal-document-parameter-escaping";
+
+    @Inject
+    private Logger logger;
+
+    @Inject
+    private Provider<XWikiContext> contextProvider;
+
+    @Inject
+    private InvitationInternalDocumentParameterEscapingFixer invitationInternalDocumentParameterEscapingFixer;
+
+    @Override
+    public void consume(DocumentReference documentReference, String version) throws IndexException
+    {
+        try {
+            XWikiContext context = this.contextProvider.get();
+            task(context.getWiki().getDocument(documentReference, context));
+        } catch (XWikiException e) {
+            throw new IndexException(String.format("Failed to resolve document [%s]", documentReference), e);
+        }
+    }
+
+    private void task(XWikiDocument document)
+    {
+        Syntax syntax = document.getSyntax();
+        if (List.of(Syntax.XWIKI_2_1, Syntax.XWIKI_2_0).contains(syntax)) {
+            try {
+                this.invitationInternalDocumentParameterEscapingFixer.fix(document.getContent(), document.getSyntax())
+                    .ifPresent(content -> {
+                        document.setContent(content);
+                        try {
+                            XWikiContext context = this.contextProvider.get();
+                            context.getWiki().saveDocument(document, "Automatic bad escaping fix.", true, context);
+                            this.logger.info("[{}] successfully fixed.", document);
+                        } catch (XWikiException e) {
+                            this.logger.error("Failed to save document [{}]", document, e);
+                        }
+                    });
+            } catch (Exception e) {
+                this.logger.error("Unexpected error while fixing [{}]", document, e);
+            }
+        } else {
+            this.logger.warn(
+                "[{}] skipped because escaping for syntax [{}] is not supported. It is advised to review this file.",
+                document, syntax);
+        }
+    }
+}
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/migration/R150000000XWIKI20285DataMigration.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/migration/R150000000XWIKI20285DataMigration.java
new file mode 100644
index 0000000000000..32efe8051f1f7
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/migration/R150000000XWIKI20285DataMigration.java
@@ -0,0 +1,97 @@
+/*
+ * 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 org.xwiki.internal.migration;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.xwiki.component.annotation.Component;
+import org.xwiki.internal.extension.XARExtensionIndex;
+import org.xwiki.query.QueryException;
+
+import com.xpn.xwiki.XWiki;
+import com.xpn.xwiki.store.migration.DataMigrationException;
+import com.xpn.xwiki.store.migration.XWikiDBVersion;
+
+import static org.xwiki.query.Query.XWQL;
+
+/**
+ * Search for documents with a known invalid content produced by code generation (i.e., that cannot be fixed by editing
+ * a xar file), and apply an automatic escaping fix.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@Component
+@Named("R150000000XWIKI20285")
+@Singleton
+public class R150000000XWIKI20285DataMigration extends AbstractDocumentsMigration
+{
+    @Inject
+    private XARExtensionIndex installedXARs;
+
+    @Override
+    public String getDescription()
+    {
+        return "Patch the InvitationConfig documents with improper escaping.";
+    }
+
+    @Override
+    public XWikiDBVersion getVersion()
+    {
+        return new XWikiDBVersion(150000000);
+    }
+
+    @Override
+    protected String getTaskType()
+    {
+        return InvitationInternalDocumentParameterEscapingTaskConsumer.HINT;
+    }
+
+    @Override
+    protected List<String> selectDocuments() throws DataMigrationException
+    {
+        XWiki wiki = getXWikiContext().getWiki();
+        try {
+            // We select potentially impacted documents using like wildcards. This selection might lead to false 
+            // positive that wild be filtered out by the more accurate regex in
+            // InvitationInternalDocumentParameterEscapingFixer.
+            return wiki.getStore().getQueryManager()
+                .createQuery("where doc.content "
+                    + "like '%{{info}}%services.localization.render%xe.invitation.internalDocument%{{/info}}%'", XWQL)
+                .<String>execute()
+                .stream()
+                // Exclude document that are provided by extensions, because they are fixed using the usual xar upgrade
+                // mechanism.
+                .filter(documentReference -> !this.installedXARs.isExtensionDocument(
+                    this.documentReferenceResolver.resolve(documentReference)))
+                .collect(Collectors.toList());
+        } catch (QueryException e) {
+            throw new DataMigrationException(
+                String.format("Failed retrieve the list of all the documents for wiki [%s].", wiki.getName()), e);
+        }
+    }
+}
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/META-INF/components.txt b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/META-INF/components.txt
index 352b59f2dcce1..0860da2bea9dc 100644
--- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/META-INF/components.txt
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/META-INF/components.txt
@@ -265,5 +265,8 @@ com.xpn.xwiki.web.UnknownAction
 com.xpn.xwiki.web.UploadAction
 com.xpn.xwiki.web.ViewAction
 com.xpn.xwiki.web.ViewAttachRevAction
 2000:org.xwiki.internal.attachment.validation.VoidAttachmentValidator
 org.xwiki.internal.template.ActionTemplateRequirement
+org.xwiki.internal.migration.R150000000XWIKI20285DataMigration
+org.xwiki.internal.migration.InvitationInternalDocumentParameterEscapingFixer
+org.xwiki.internal.migration.InvitationInternalDocumentParameterEscapingTaskConsumer
diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/test/java/org/xwiki/internal/migration/InvitationInternalDocumentParameterEscapingFixerTest.java b/xwiki-platform-core/xwiki-platform-oldcore/src/test/java/org/xwiki/internal/migration/InvitationInternalDocumentParameterEscapingFixerTest.java
new file mode 100644
index 0000000000000..04ab6b73504a2
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-oldcore/src/test/java/org/xwiki/internal/migration/InvitationInternalDocumentParameterEscapingFixerTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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 org.xwiki.internal.migration;
+
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.xwiki.rendering.syntax.Syntax;
+import org.xwiki.test.junit5.mockito.ComponentTest;
+import org.xwiki.test.junit5.mockito.InjectMockComponents;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test of {@link InvitationInternalDocumentParameterEscapingFixer}.
+ *
+ * @version $Id$
+ */
+@ComponentTest
+class InvitationInternalDocumentParameterEscapingFixerTest
+{
+    @InjectMockComponents
+    private InvitationInternalDocumentParameterEscapingFixer fixer;
+
+    public static Stream<Arguments> fixSource()
+    {
+        return Stream.of(
+            Arguments.of(
+                "nothing to change 1\n"
+                    + "{{info}}$services.localization.render('xe.invitation.internalDocument', [$noChange]){{/info}}"
+                    + "nothing to change 2\n"
+                    + "{{info}}$services.localization.render('xe.invitation.internalDocument', [\"$change\"]){{/info}}"
+                    + "nothing to change 3",
+                Optional.of(
+                    "nothing to change 1\n"
+                        + "{{info}}$services.localization.render('xe.invitation.internalDocument', [$noChange]){{/info}}nothing to change 2\n"
+                        + "{{info}}$services.localization.render('xe.invitation.internalDocument', [$services.rendering.escape(\"$change\", 'xwiki/2.1')]){{/info}}nothing to change 3"
+                )
+            ),
+            Arguments.of(
+                "nothing to change 1\n"
+                    + "{{info}}$services.localization.render('xe.invitation.internalDocument', [$noChange]){{/info}}"
+                    + "nothing to change 2\n"
+                    + "nothing to change 3",
+                Optional.empty()
+            )
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("fixSource")
+    void fix(String value, Optional<String> expected)
+    {
+        assertEquals(expected, this.fixer.fix(value, Syntax.XWIKI_2_1));
+    }
+}
diff --git a/xwiki-platform-tools/xwiki-platform-tool-packager-plugin/pom.xml b/xwiki-platform-tools/xwiki-platform-tool-packager-plugin/pom.xml
index 092017e6f6df6..f3ef71f40a892 100644
--- a/xwiki-platform-tools/xwiki-platform-tool-packager-plugin/pom.xml
+++ b/xwiki-platform-tools/xwiki-platform-tool-packager-plugin/pom.xml
@@ -115,11 +115,11 @@
       <artifactId>xwiki-commons-component-default</artifactId>
       <version>${commons.version}</version>
     </dependency>
     <dependency>
       <groupId>org.xwiki.platform</groupId>
-      <artifactId>xwiki-platform-index-api</artifactId>
+      <artifactId>xwiki-platform-index-default</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
       <groupId>org.xwiki.platform</groupId>
       <artifactId>xwiki-platform-configuration-default</artifactId>

Vulnerable code:     <xwiki.jacoco.instructionRatio>0.85</xwiki.jacoco.instructionRatio>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-query-manager</artifactId>
      <version>${project.version}</version>
    </dependency>
    <!-- Required to have access to XWikiDocumentIndexingTask. -->
    <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-oldcore</artifactId>
      <version>${project.version}</version>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <scope>test</scope>
    </dependency>
  <build>
    <plugins>
      <plugin>
        <!-- Apply the Checkstyle configurations defined in the top level pom.xml file -->
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-checkstyle-plugin</artifactId>
        <executions>
          <execution>
            <!-- Specify the "default" execution id so that the "blocker" one is always executed -->
            <id>default</id>
            <configuration>
              <failsOnError>true</failsOnError>
              <suppressionsLocation>${basedir}/src/checkstyle/checkstyle-suppressions.xml</suppressionsLocation>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
import javax.inject.Provider;

import com.xpn.xwiki.XWikiContext;
    private Provider<XWikiContext> xcontextProvider;
        return this.taskManager.getQueueSizePerType(this.xcontextProvider.get().getWikiId());
org.xwiki.index.internal.DefaultTasksManager
org.xwiki.index.internal.TasksStore
org.xwiki.index.internal.TaskExecutor
org.xwiki.index.internal.TaskApplicationReadyListener
org.xwiki.index.internal.DefaultLinksTaskConsumer
org.xwiki.index.internal.listener.LinksUpdateListener
org.xwiki.index.migration.R140300000XWIKI19614DataMigration
org.xwiki.index.migration.R140300001XWIKI19571DataMigration
import javax.inject.Provider;

import org.mockito.Mock;

import com.xpn.xwiki.XWikiContext;
    private Provider<XWikiContext> xcontextProvider;
    @Mock
    private XWikiContext xcontext;
        when(this.xcontextProvider.get()).thenReturn(this.xcontext);
        when(this.xcontext.getWikiId()).thenReturn("xwiki");
import org.xwiki.component.phase.InitializationException;
    public void initialize() throws InitializationException
import javax.inject.Inject;
import javax.inject.Provider;
import org.xwiki.index.TaskManager;
import org.xwiki.query.QueryManager;
import com.xpn.xwiki.store.migration.hibernate.HibernateDataMigration;
// TODO: Implement DataMigration once XWIKI-19399 is fixed.
public class R140300000XWIKI19614DataMigration implements HibernateDataMigration
    @Inject
    private QueryManager queryManager;

    @Inject
    private TaskManager taskManager;

    @Inject
    private Provider<XWikiContext> contextProvider;

    public void migrate() throws DataMigrationException
        XWikiContext context = this.contextProvider.get();
        // No need to migrate if the wiki does not support backlinks.
            String wikiId = context.getWikiId();
                List<Long> ids =
                    this.queryManager.createQuery("SELECT doc.id FROM XWikiDocument doc", Query.HQL).setWiki(wikiId)
                        .execute();
                for (Long id : ids) {
                    this.taskManager.addTask(wikiId, id, LINKS_TASK_TYPE);
                }
                    String.format("Failed retrieve the list of all the documents for wiki [%s].", wikiId), e);
    public boolean shouldExecute(XWikiDBVersion startupVersion)
        return true;
    public String getPreHibernateLiquibaseChangeLog()
        // TODO: Remove once XWIKI-19399 is fixed.
        return null;
    }

    @Override
    public String getLiquibaseChangeLog()
    {
        // TODO: Remove once XWIKI-19399 is fixed.
        return null;
import javax.inject.Provider;

    @MockComponent
    private Provider<XWikiContext> contextProvider;
        when(this.contextProvider.get()).thenReturn(this.context);
        when(this.queryManager.createQuery("SELECT doc.id FROM XWikiDocument doc",
        when(this.query.execute()).thenReturn(List.of(42L, 43L));
        verify(this.taskManager).addTask("wiki1", 42L,  "links");
        assertEquals("Failed retrieve the list of all the documents for wiki [wiki1].", queryException.getMessage());
        assertEquals(QueryException.class, queryException.getCause().getClass());
  {{info}}$services.localization.render('xe.invitation.internalDocument', ["${doc.getSpace()}.WebHome"]){{/info}}
      #set($configDocContent = '{{velo' + 'city}}{{info}}$services.localization.render(''xe.invitation.internalDocument'', ["'
                               + "$!config.get('mainPage')" + '"]){{/info}}{{/velo' + 'city}}')
    #set($emailContainerContent = '{{velo' + 'city}}{{info}}$services.localization.render(''xe.invitation.internalDocument'', ["'
                                  + "$config.get('emailContainer')" + '"]){{/info}}{{/velo' + 'city}}')
<xwikidoc version="1.4" reference="Invitation.InvitationConfig" locale="">
  <syntaxId>xwiki/2.0</syntaxId>
<xwikidoc version="1.3" reference="Invitation.InvitationGuestActions" locale="">
  <syntaxId>xwiki/2.0</syntaxId>
  <content>{{include reference="Invitation.InvitationCommon" /}}
  {{info}}$services.localization.render('xe.invitation.internalDocument', ["${doc.getSpace()}.WebHome"]){{/info}}
<xwikidoc version="1.3" reference="Invitation.InvitationMailClass" locale="">
  <syntaxId>xwiki/2.0</syntaxId>
  <content>{{velocity}}{{info}}$services.localization.render('xe.invitation.internalDocument', ["${doc.getSpace()}.WebHome"]){{/info}}{{/velocity}}</content>
      <size>30</size>
<xwikidoc version="1.3" reference="Invitation.InvitationMemberActions" locale="">
  <syntaxId>xwiki/2.0</syntaxId>
  <content>{{include reference="Invitation.InvitationCommon" /}}
{{include reference="Invitation.InvitationMembersCommon" /}}
    {{info}}$services.localization.render('xe.invitation.internalDocument', ["${doc.getSpace()}.WebHome"]){{/info}}
<xwikidoc version="1.3" reference="Invitation.InvitationMembersCommon" locale="">
  <syntaxId>xwiki/2.0</syntaxId>
  {{info}}$services.localization.render('xe.invitation.internalDocument', ["${doc.getSpace()}.WebHome"]){{/info}}
            new DocumentReference("xwiki", "<script>console.log</script>", "InvitationCommon"), this.context));
        assertEquals("xe.invitation.internalDocument [<script>console\\.log</script>.WebHome]",
      <artifactId>xwiki-platform-index-api</artifactId>
Fixed code:             <revapi.differences>
              <justification>Moved to xwiki-platform-index-default</justification>
              <criticality>allowed</criticality>
              <differences>
                <item>
                  <ignore>true</ignore>
                  <code>java.class.removed</code>
                  <old>class org.xwiki.index.migration.R140300000XWIKI19614DataMigration</old>
                </item>
                <item>
                  <ignore>true</ignore>
                  <code>java.class.removed</code>
                  <old>class org.xwiki.index.migration.R140300001XWIKI19571DataMigration</old>
                </item>
              </differences>
            </revapi.differences>
/*
 * 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 org.xwiki.extension.xar.internal;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.xwiki.component.annotation.Component;
import org.xwiki.extension.repository.InstalledExtensionRepository;
import org.xwiki.extension.xar.internal.handler.XarExtensionHandler;
import org.xwiki.extension.xar.internal.repository.XarInstalledExtensionRepository;
import org.xwiki.internal.extension.XARExtensionIndex;
import org.xwiki.model.reference.DocumentReference;

/**
 * Default implementation of {@link XARExtensionIndex}. Uses {@link XarInstalledExtensionRepository} to implement the
 * operations.
 *
 * @version $Id$
 * @since 15.0RC1
 * @since 14.4.8
 * @since 14.10.4
 */
@Component
@Singleton
public class DefaultXARExtensionIndex implements XARExtensionIndex
{
    @Inject
    @Named(XarExtensionHandler.TYPE)
    private InstalledExtensionRepository installedXARs;

    @Override
    public boolean isExtensionDocument(DocumentReference documentReference)
    {
        return !((XarInstalledExtensionRepository) this.installedXARs).getXarInstalledExtensions(documentReference)
            .isEmpty();
    }
}
org.xwiki.extension.xar.internal.DefaultXARExtensionIndex
    <module>xwiki-platform-index-default</module>
    <xwiki.jacoco.instructionRatio>0.04</xwiki.jacoco.instructionRatio>
      <groupId>org.xwiki.commons</groupId>
      <artifactId>xwiki-commons-stability</artifactId>
      <version>${commons.version}</version>
import org.xwiki.wiki.descriptor.WikiDescriptorManager;
    private WikiDescriptorManager wikiDescriptorManager;
        return this.taskManager.getQueueSizePerType(this.wikiDescriptorManager.getCurrentWikiId());
import org.xwiki.wiki.descriptor.WikiDescriptorManager;
    private WikiDescriptorManager wikiDescriptorManager;
        when(this.wikiDescriptorManager.getCurrentWikiId()).thenReturn("xwiki");
<?xml version="1.0" 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.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.xwiki.platform</groupId>
    <artifactId>xwiki-platform-index</artifactId>
    <version>15.1-SNAPSHOT</version>
  </parent>
  <artifactId>xwiki-platform-index-default</artifactId>
  <name>XWiki Platform - Index - Default</name>
  <description>Default implementation of the API to queue asynchronous tasks dedicated to the analysis of wiki pages.</description>
  <packaging>jar</packaging>
  <properties>
    <xwiki.jacoco.instructionRatio>0.84</xwiki.jacoco.instructionRatio>
    <!-- Name to display by the Extension Manager -->
    <xwiki.extension.name>Default implementation of the Index API</xwiki.extension.name>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-index-api</artifactId>
      <version>${project.version}</version>
    </dependency>
    <dependency>
      <groupId>org.xwiki.commons</groupId>
      <artifactId>xwiki-commons-component-api</artifactId>
      <version>${commons.version}</version>
    </dependency>
    <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-oldcore</artifactId>
      <version>${project.version}</version>
    </dependency>
    <!-- Test dependencies. -->
    <dependency>
      <groupId>org.xwiki.commons</groupId>
      <artifactId>xwiki-commons-tool-test-component</artifactId>
      <version>${commons.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <!-- Apply the Checkstyle configurations defined in the top level pom.xml file -->
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-checkstyle-plugin</artifactId>
        <executions>
          <execution>
            <!-- Specify the "default" execution id so that the "blocker" one is always executed -->
            <id>default</id>
            <configuration>
              <failsOnError>true</failsOnError>
              <suppressionsLocation>${basedir}/src/checkstyle/checkstyle-suppressions.xml</suppressionsLocation>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>
    public void initialize()
import org.xwiki.internal.migration.AbstractDocumentsMigration;
import com.xpn.xwiki.XWiki;
import com.xpn.xwiki.doc.XWikiDocument;
public class R140300000XWIKI19614DataMigration extends AbstractDocumentsMigration
    protected String getTaskType()
    {
        return LINKS_TASK_TYPE;
    }

    @Override
    protected List<String> selectDocuments() throws DataMigrationException
        List<String> documents;
        XWikiContext context = getXWikiContext();
        XWiki wiki = getXWikiContext().getWiki();
                documents = wiki.getStore().getQueryManager()
                    .createQuery("SELECT doc.fullName FROM XWikiDocument doc", Query.HQL)
                    .setWiki(context.getWikiId())
                    .execute();
                    String.format("Failed retrieve the list of all the documents for wiki [%s].", wiki.getName()), e);
        } else {
            documents = List.of();
        return documents;
    protected void logBeforeQueuingTask(XWikiDocument document)
        // No logs here as it would be too verbose (all documents of the wiki are queued).
    protected void logBeforeQueuingTasks(List<XWikiDocument> documents)
        XWikiContext context = getXWikiContext();
        if (context.getWiki().hasBacklinks(context)) {
            super.logBeforeQueuingTasks(documents);
        } else {
            this.logger.info("Skipped because backlinks are not supported on [{}]", context.getWikiId());
        }
org.xwiki.index.internal.DefaultTasksManager
org.xwiki.index.internal.TasksStore
org.xwiki.index.internal.TaskExecutor
org.xwiki.index.internal.TaskApplicationReadyListener
org.xwiki.index.internal.DefaultLinksTaskConsumer
org.xwiki.index.internal.listener.LinksUpdateListener
org.xwiki.index.migration.R140300000XWIKI19614DataMigration
org.xwiki.index.migration.R140300001XWIKI19571DataMigration
import org.junit.jupiter.api.extension.RegisterExtension;
import org.xwiki.context.Execution;
import org.xwiki.context.ExecutionContext;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.DocumentReferenceResolver;
import org.xwiki.test.LogLevel;
import org.xwiki.test.junit5.LogCaptureExtension;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.store.XWikiStoreInterface;
import ch.qos.logback.classic.Level;

import static org.mockito.Mockito.mock;
    @Mock
    private Execution execution;

    @MockComponent
    private DocumentReferenceResolver<String> resolver;

    @RegisterExtension
    LogCaptureExtension logCapture = new LogCaptureExtension(LogLevel.INFO);
        ExecutionContext executionContext = mock(ExecutionContext.class);
        XWikiStoreInterface xWikiStoreInterface = mock(XWikiStoreInterface.class);

        when(this.execution.getContext()).thenReturn(executionContext);
        when(executionContext.getProperty("xwikicontext")).thenReturn(this.context);
        when(this.wiki.getName()).thenReturn("wiki1");
        when(this.wiki.getStore()).thenReturn(xWikiStoreInterface);
        when(xWikiStoreInterface.getQueryManager()).thenReturn(this.queryManager);

        when(this.queryManager.createQuery("SELECT doc.fullName FROM XWikiDocument doc",
        DocumentReference doc42 = new DocumentReference("xwiki", "XWiki", "Doc42");
        DocumentReference doc43 = new DocumentReference("xwiki", "XWiki", "Doc43");
        XWikiDocument xWikiDocument42 = mock(XWikiDocument.class);
        XWikiDocument xWikiDocument43 = mock(XWikiDocument.class);

        when(this.query.execute()).thenReturn(List.of("xwiki.XWiki.Doc42", "xwiki.XWiki.Doc43"));
        when(this.resolver.resolve("xwiki.XWiki.Doc42")).thenReturn(doc42);
        when(this.resolver.resolve("xwiki.XWiki.Doc43")).thenReturn(doc43);
        when(this.wiki.getDocument(doc42, this.context)).thenReturn(xWikiDocument42);
        when(this.wiki.getDocument(doc43, this.context)).thenReturn(xWikiDocument43);
        when(xWikiDocument42.getId()).thenReturn(42L);
        when(xWikiDocument43.getId()).thenReturn(43L);
        verify(this.taskManager).addTask("wiki1", 42L, "links");

        assertEquals("[2] documents queued to task [links]", this.logCapture.getMessage(0));
        assertEquals(Level.INFO, this.logCapture.getLogEvent(0).getLevel());
        assertEquals("Data migration R140300000XWIKI19614 failed", queryException.getMessage());
        assertEquals("Failed retrieve the list of all the documents for wiki [wiki1].",
            queryException.getCause().getMessage());
        assertEquals(DataMigrationException.class, queryException.getCause().getClass());
        assertEquals(QueryException.class, queryException.getCause().getCause().getClass());
        assertEquals("Skipped because backlinks are not supported on [wiki1]", this.logCapture.getMessage(0));
        assertEquals(Level.INFO, this.logCapture.getLogEvent(0).getLevel());
  #set ($linkTarget = "${doc.getSpace()}.WebHome")
  #set ($linkTarget = $services.rendering.escape($linkTarget, 'xwiki/2.1'))
  {{info}}$services.localization.render('xe.invitation.internalDocument', [$linkTarget]){{/info}}
      #set($configDocContent = 
        '{{velocity}}{{info}}$services.localization.render(''xe.invitation.internalDocument'','
          + '[$services.rendering.escape("' + "$!config.get('mainPage')" 
          + '", ''xwiki/2.1'')]){{/info}}{{/velocity}}')
    #set($emailContainerContent = 
      '{{velocity}}{{info}}$services.localization.render(''xe.invitation.internalDocument'','
        + '[$services.rendering.escape("' + "$config.get('emailContainer')" 
        + '", ''xwiki/2.1'')]){{/info}}{{/velocity}}')
<xwikidoc version="1.5" reference="Invitation.InvitationConfig" locale="">
  <syntaxId>xwiki/2.1</syntaxId>
<xwikidoc version="1.5" reference="Invitation.InvitationGuestActions" locale="">
  <syntaxId>xwiki/2.1</syntaxId>
  <content>{{include reference="Invitation.InvitationCommon"/}}
  #set ($linkTarget = "${doc.getSpace()}.WebHome")
  #set ($linkTarget = $services.rendering.escape($linkTarget, 'xwiki/2.1'))
  {{info}}$services.localization.render('xe.invitation.internalDocument', [$linkTarget]){{/info}}
<xwikidoc version="1.5" reference="Invitation.InvitationMailClass" locale="">
  <syntaxId>xwiki/2.1</syntaxId>
  <content>{{velocity}}
  #set ($linkTarget = "${doc.getSpace()}.WebHome")
  #set ($linkTarget = $services.rendering.escape($linkTarget, 'xwiki/2.1'))
  {{info}}$services.localization.render('xe.invitation.internalDocument', [$linkTarget]){{/info}}
{{/velocity}}</content>
      <size>30</size>
<xwikidoc version="1.5" reference="Invitation.InvitationMemberActions" locale="">
  <syntaxId>xwiki/2.1</syntaxId>
  <content>{{include reference="Invitation.InvitationCommon"/}}
{{include reference="Invitation.InvitationMembersCommon"/}}
    #set ($linkTarget = "${doc.getSpace()}.WebHome")
    #set ($linkTarget = $services.rendering.escape($linkTarget, 'xwiki/2.1'))
    {{info}}$services.localization.render('xe.invitation.internalDocument', [$linkTarget]){{/info}}
<xwikidoc version="1.5" reference="Invitation.InvitationMembersCommon" locale="">
  <syntaxId>xwiki/2.1</syntaxId>
  #set ($linkTarget = "${doc.getSpace()}.WebHome")
  #set ($linkTarget = $services.rendering.escape($linkTarget, 'xwiki/2.1'))
  {{info}}$services.localization.render('xe.invitation.internalDocument', [$linkTarget]){{/info}}
            new DocumentReference("xwiki", "]]  {{noscript/}}", "InvitationCommon"), this.context));
        assertEquals("xe.invitation.internalDocument []] {{noscript/}}.WebHome]",
/*
 * 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 org.xwiki.invitation;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.junit.jupiter.api.Test;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.rendering.RenderingScriptServiceComponentList;
import org.xwiki.rendering.internal.configuration.DefaultExtendedRenderingConfiguration;
import org.xwiki.rendering.internal.configuration.RenderingConfigClassDocumentConfigurationSource;
import org.xwiki.rendering.internal.macro.message.InfoMessageMacro;
import org.xwiki.test.annotation.ComponentList;
import org.xwiki.test.page.HTML50ComponentList;
import org.xwiki.test.page.PageTest;
import org.xwiki.test.page.TestNoScriptMacro;
import org.xwiki.test.page.XWikiSyntax21ComponentList;

import com.xpn.xwiki.doc.XWikiDocument;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * Test of {@code Invitation.InvitationConfig}.
 *
 * @version $Id$
 * @since 15.0RC1
 * @since 14.4.8
 * @since 14.10.4
 */
@HTML50ComponentList
@XWikiSyntax21ComponentList
@RenderingScriptServiceComponentList
@ComponentList({
    InfoMessageMacro.class,
    TestNoScriptMacro.class,
    // Start - Required in addition of RenderingScriptServiceComponentList
    DefaultExtendedRenderingConfiguration.class,
    RenderingConfigClassDocumentConfigurationSource.class,
    // End - Required in additional of RenderingScriptServiceComponentList
})
class InvitationConfigPageTest extends PageTest
{
    private static final DocumentReference INVITATION_CONFIG_DOCUMENT_REFERENCE =
        new DocumentReference("xwiki", "Invitation", "InvitationConfig");

    @Test
    void escapeInfoMessageInternalDocumentParameter() throws Exception
    {
        XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_CONFIG_DOCUMENT_REFERENCE);

        // Set up the current doc in the context so that $doc is bound in scripts
        this.context.setDoc(
            this.xwiki.getDocument(new DocumentReference("xwiki", "]] {{noscript/}}", "Page"), this.context));

        Document document = Jsoup.parse(invitationGuestActionsDocument.getRenderedContent(this.context));
        Element infomessage = document.selectFirst(".infomessage");
        assertEquals("xe.invitation.internalDocument [Invitation.WebHome]", infomessage.text());
    }
}
/*
 * 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 org.xwiki.invitation;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.rendering.RenderingScriptServiceComponentList;
import org.xwiki.rendering.internal.configuration.DefaultExtendedRenderingConfiguration;
import org.xwiki.rendering.internal.configuration.RenderingConfigClassDocumentConfigurationSource;
import org.xwiki.rendering.internal.macro.message.InfoMessageMacro;
import org.xwiki.test.annotation.ComponentList;
import org.xwiki.test.page.HTML50ComponentList;
import org.xwiki.test.page.PageTest;
import org.xwiki.test.page.TestNoScriptMacro;
import org.xwiki.test.page.XWikiSyntax21ComponentList;

import com.xpn.xwiki.doc.XWikiDocument;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * Test of {@code Invitation.InvitationGuestActions}.
 *
 * @version $Id$
 * @since 15.0RC1
 * @since 14.4.8
 * @since 14.10.4
 */
@HTML50ComponentList
@XWikiSyntax21ComponentList
@RenderingScriptServiceComponentList
@ComponentList({
    InfoMessageMacro.class,
    TestNoScriptMacro.class,
    // Start - Required in addition of RenderingScriptServiceComponentList
    DefaultExtendedRenderingConfiguration.class,
    RenderingConfigClassDocumentConfigurationSource.class,
    // End - Required in additional of RenderingScriptServiceComponentList
})
class InvitationGuestActionsPageTest extends PageTest
{
    private static final DocumentReference INVITATION_COMMON_DOCUMENT_REFERENCE =
        new DocumentReference("xwiki", "Invitation", "InvitationCommon");

    private static final DocumentReference INVITATION_GUEST_ACTIONS_DOCUMENT_REFERENCE =
        new DocumentReference("xwiki", "Invitation", "InvitationGuestActions");

    @BeforeEach
    void setUp() throws Exception
    {
        loadPage(INVITATION_COMMON_DOCUMENT_REFERENCE);
    }

    @Test
    void escapeInfoMessageInternalDocumentParameter() throws Exception
    {
        XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_GUEST_ACTIONS_DOCUMENT_REFERENCE);

        // Set up the current doc in the context so that $doc is bound in scripts
        this.context.setDoc(
            this.xwiki.getDocument(new DocumentReference("xwiki", "]] {{noscript/}}", "Page"), this.context));

        Document document = Jsoup.parse(invitationGuestActionsDocument.getRenderedContent(this.context));
        Element infomessage = document.selectFirst(".infomessage");
        assertEquals("xe.invitation.internalDocument []] {{noscript/}}.WebHome]", infomessage.text());
    }
}
/*
 * 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 org.xwiki.invitation;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.junit.jupiter.api.Test;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.rendering.RenderingScriptServiceComponentList;
import org.xwiki.rendering.internal.configuration.DefaultExtendedRenderingConfiguration;
import org.xwiki.rendering.internal.configuration.RenderingConfigClassDocumentConfigurationSource;
import org.xwiki.rendering.internal.macro.message.InfoMessageMacro;
import org.xwiki.test.annotation.ComponentList;
import org.xwiki.test.page.HTML50ComponentList;
import org.xwiki.test.page.PageTest;
import org.xwiki.test.page.TestNoScriptMacro;
import org.xwiki.test.page.XWikiSyntax21ComponentList;

import com.xpn.xwiki.doc.XWikiDocument;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * Test of {@code Invitation.InvitationMailClass}.
 *
 * @version $Id$
 * @since 15.0RC1
 * @since 14.4.8
 * @since 14.10.4
 */
@HTML50ComponentList
@XWikiSyntax21ComponentList
@RenderingScriptServiceComponentList
@ComponentList({
    InfoMessageMacro.class,
    TestNoScriptMacro.class,
    // Start - Required in addition of RenderingScriptServiceComponentList
    DefaultExtendedRenderingConfiguration.class,
    RenderingConfigClassDocumentConfigurationSource.class,
    // End - Required in additional of RenderingScriptServiceComponentList
})
class InvitationMailClassPageTest extends PageTest
{
    private static final DocumentReference INVITATION_MAIL_CLASS_DOCUMENT_REFERENCE =
        new DocumentReference("xwiki", "Invitation", "InvitationMailClass");

    @Test
    void escapeInfoMessageInternalDocumentParameter() throws Exception
    {
        XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_MAIL_CLASS_DOCUMENT_REFERENCE);

        // Set up the current doc in the context so that $doc is bound in scripts
        this.context.setDoc(
            this.xwiki.getDocument(new DocumentReference("xwiki", "]] {{noscript/}}", "Page"), this.context));

        Document document = Jsoup.parse(invitationGuestActionsDocument.getRenderedContent(this.context));
        Element infomessage = document.selectFirst(".infomessage");
        assertEquals("xe.invitation.internalDocument []] {{noscript/}}.WebHome]", infomessage.text());
    }
}
/*
 * 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 org.xwiki.invitation;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.rendering.RenderingScriptServiceComponentList;
import org.xwiki.rendering.internal.configuration.DefaultExtendedRenderingConfiguration;
import org.xwiki.rendering.internal.configuration.RenderingConfigClassDocumentConfigurationSource;
import org.xwiki.rendering.internal.macro.message.InfoMessageMacro;
import org.xwiki.test.annotation.ComponentList;
import org.xwiki.test.page.HTML50ComponentList;
import org.xwiki.test.page.PageTest;
import org.xwiki.test.page.TestNoScriptMacro;
import org.xwiki.test.page.XWikiSyntax21ComponentList;

import com.xpn.xwiki.doc.XWikiDocument;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * Test of {@code Invitation.InvitationMemberActions}.
 *
 * @version $Id$
 * @since 15.0RC1
 * @since 14.4.8
 * @since 14.10.4
 */
@HTML50ComponentList
@XWikiSyntax21ComponentList
@RenderingScriptServiceComponentList
@ComponentList({
    InfoMessageMacro.class,
    TestNoScriptMacro.class,
    // Start - Required in addition of RenderingScriptServiceComponentList
    DefaultExtendedRenderingConfiguration.class,
    RenderingConfigClassDocumentConfigurationSource.class,
    // End - Required in additional of RenderingScriptServiceComponentList
})
class InvitationMemberActionsPageTest extends PageTest
{
    private static final DocumentReference INVITATION_COMMON_DOCUMENT_REFERENCE =
        new DocumentReference("xwiki", "Invitation", "InvitationCommon");

    private static final DocumentReference INVITATION_MEMBER_ACTIONS_DOCUMENT_REFERENCE =
        new DocumentReference("xwiki", "Invitation", "InvitationMemberActions");

    @BeforeEach
    void setUp() throws Exception
    {
        loadPage(INVITATION_COMMON_DOCUMENT_REFERENCE);
    }

    @Test
    void escapeInfoMessageInternalDocumentParameter() throws Exception
    {
        XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_MEMBER_ACTIONS_DOCUMENT_REFERENCE);

        // Set a non-guest user as otherwise the rendering stops early.
        this.context.setUserReference(new DocumentReference("xwiki", "XWiki", "U1"));

        // Set up the current doc in the context so that $doc is bound in scripts
        this.context.setDoc(
            this.xwiki.getDocument(new DocumentReference("xwiki", "]] {{noscript/}}", "Page"), this.context));

        Document document = Jsoup.parse(invitationGuestActionsDocument.getRenderedContent(this.context));
        Element infomessage = document.selectFirst(".infomessage");
        assertEquals("xe.invitation.internalDocument []] {{noscript/}}.WebHome]", infomessage.text());
    }
}
/*
 * 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 org.xwiki.invitation;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.junit.jupiter.api.Test;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.rendering.RenderingScriptServiceComponentList;
import org.xwiki.rendering.internal.configuration.DefaultExtendedRenderingConfiguration;
import org.xwiki.rendering.internal.configuration.RenderingConfigClassDocumentConfigurationSource;
import org.xwiki.rendering.internal.macro.message.InfoMessageMacro;
import org.xwiki.test.annotation.ComponentList;
import org.xwiki.test.page.HTML50ComponentList;
import org.xwiki.test.page.PageTest;
import org.xwiki.test.page.TestNoScriptMacro;
import org.xwiki.test.page.XWikiSyntax21ComponentList;

import com.xpn.xwiki.doc.XWikiDocument;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * Test of {@code Invitation.InvitationMembersCommon}.
 *
 * @version $Id$
 * @since 15.0RC1
 * @since 14.4.8
 * @since 14.10.4
 */
@HTML50ComponentList
@XWikiSyntax21ComponentList
@RenderingScriptServiceComponentList
@ComponentList({
    InfoMessageMacro.class,
    TestNoScriptMacro.class,
    // Start - Required in addition of RenderingScriptServiceComponentList
    DefaultExtendedRenderingConfiguration.class,
    RenderingConfigClassDocumentConfigurationSource.class,
    // End - Required in additional of RenderingScriptServiceComponentList
})
class InvitationMembersCommonPageTest extends PageTest
{
    private static final DocumentReference INVITATION_MEMBER_COMMON_DOCUMENT_REFERENCE =
        new DocumentReference("xwiki", "Invitation", "InvitationMembersCommon");

    @Test
    void escapeInfoMessageInternalDocumentParameter() throws Exception
    {
        XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_MEMBER_COMMON_DOCUMENT_REFERENCE);

        // Set a non-guest user as otherwise the rendering stops early.
        this.context.setUserReference(new DocumentReference("xwiki", "XWiki", "U1"));

        // Set up the current doc in the context so that $doc is bound in scripts
        this.context.setDoc(
            this.xwiki.getDocument(new DocumentReference("xwiki", "]] {{noscript/}}", "InvitationMembersCommon"),
                this.context));

        Document document = Jsoup.parse(invitationGuestActionsDocument.getRenderedContent(this.context));
        assertEquals("xe.invitation.internalDocument []] {{noscript/}}.WebHome]",
            document.selectFirst(".infomessage").text());
    }
}
    <!-- The default document task analysis implementation -->
    <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-index-default</artifactId>
      <version>${project.version}</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-index-api</artifactId>
      <version>${project.version}</version>
    </dependency>
/*
 * 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 org.xwiki.internal.extension;

import org.xwiki.component.annotation.Role;
import org.xwiki.model.reference.DocumentReference;

/**
 * Provides operation to access to information relative to XARs provided by extensions.
 *
 * @version $Id$
 * @since 15.0RC1
 * @since 14.4.8
 * @since 14.10.4
 */
@Role
public interface XARExtensionIndex
{
    /**
     * @param documentReference a document reference
     * @return {@code true} if a given document reference is provided by an extension
     */
    boolean isExtensionDocument(DocumentReference documentReference);
}
/*
 * 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 org.xwiki.internal.migration;

import java.util.List;
import java.util.Optional;

import javax.inject.Inject;

import org.slf4j.Logger;
import org.xwiki.index.TaskManager;
import org.xwiki.model.reference.DocumentReferenceResolver;

import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.store.migration.DataMigrationException;
import com.xpn.xwiki.store.migration.hibernate.AbstractHibernateDataMigration;

import static java.util.stream.Collectors.toList;

/**
 * Allow to easily queue a document analysis task on a set documents to migrate. Sub-classes need to implement two
 * methods:
 * <ul>
 *     <li>{@link #selectDocuments()}: return the list of document ids to queue for migration</li>
 *     <li>{@link #getTaskType()}: the type of the task to queue documents to</li>
 * </ul>
 *
 * @version $Id$
 * @since 15.0RC1
 * @since 14.4.8
 * @since 14.10.4
 */
public abstract class AbstractDocumentsMigration extends AbstractHibernateDataMigration
{
    @Inject
    protected Logger logger;

    @Inject
    protected DocumentReferenceResolver<String> documentReferenceResolver;

    @Inject
    private TaskManager taskManager;

    @Override
    protected void hibernateMigrate() throws DataMigrationException
    {
        List<XWikiDocument> documents = selectDocuments()
            .stream()
            .map(this::convert)
            .filter(Optional::isPresent)
            .map(Optional::get)
            .collect(toList());
        logBeforeQueuingTasks(documents);
        for (XWikiDocument document : documents) {
            logBeforeQueuingTask(document);
            this.taskManager.addTask(this.getXWikiContext().getWikiId(), document.getId(), getTaskType());
        }
    }

    private Optional<XWikiDocument> convert(String documentReference)
    {
        XWikiContext context = getXWikiContext();
        try {
            return Optional.of(
                context.getWiki().getDocument(this.documentReferenceResolver.resolve(documentReference), context));
        } catch (XWikiException e) {
            this.logger.error("Failed to resolve [{}]", documentReference, e);
            return Optional.empty();
        }
    }

    /**
     * @return the id of the task type to queue documents to
     */
    protected abstract String getTaskType();

    /**
     * @return the list of document ids to migrate
     */
    protected abstract List<String> selectDocuments() throws DataMigrationException;

    /**
     * Prints an info log with the number of queued documents and the type of the task.
     *
     * @param documents the full list of documents that will be queued
     */
    protected void logBeforeQueuingTasks(List<XWikiDocument> documents)
    {
        this.logger.info("[{}] documents queued to task [{}]", documents.size(), getTaskType());
    }

    /**
     * Prints an info logs with an individual document and well as its queued task.
     *
     * @param document a individual document that will be queued
     */
    protected void logBeforeQueuingTask(XWikiDocument document)
    {
        this.logger.info("document [{}] queued to task [{}]", document, getTaskType());
    }
}
/*
 * 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 org.xwiki.internal.migration;

import java.util.Objects;
import java.util.Optional;
import java.util.regex.Pattern;

import javax.inject.Singleton;

import org.xwiki.component.annotation.Component;
import org.xwiki.rendering.syntax.Syntax;

import static java.util.regex.Matcher.quoteReplacement;

/**
 * Fix a content by looking for localization of {@code xe.invitation.internalDocument} and escaping its parameter. The
 * translation fixed by this method is initially introduced by the invitation application but the fix is localed in
 * oldcore so that the fix is applied on pages even if the invitation application has been uninstalled.
 *
 * @version $Id$
 * @since 15.0RC1
 * @since 14.4.8
 * @since 14.10.4
 */
@Component(roles = InvitationInternalDocumentParameterEscapingFixer.class)
@Singleton
public class InvitationInternalDocumentParameterEscapingFixer
{
    private static final Pattern PATTERN = Pattern.compile("(\\{\\{info}}"
        + "\\$services\\.localization\\.render\\('xe\\.invitation\\.internalDocument', \\[)(\"[^\"]+\")(]\\)\\"
        + "{\\{/info}})");

    /**
     * @param content the context to fix
     * @param syntax the syntax of the context to fix (xwiki/2.0 or xwiki/2.1)
     * @return the context with the fix applied, or {@link Optional#empty()} if nothing needs to be fixed
     */
    public Optional<String> fix(String content, Syntax syntax)
    {
        String escapedContent = PATTERN.matcher(content).replaceAll(matchResult -> {
            if (matchResult.group(2).contains("services.rendering.escape")) {
                return matchResult.group();
            } else {
                // Concatenate the various groups of the regex while wrapping the localization argument with a
                // call to the escaping script service.
                String format = String.format("%s$services.rendering.escape(%s, '%s')%s", matchResult.group(1),
                    matchResult.group(2), syntax.toIdString(), matchResult.group(3));
                return quoteReplacement(format);
            }
        });

        if (Objects.equals(escapedContent, content)) {
            return Optional.empty();
        } else {
            return Optional.of(escapedContent);
        }
    }
}
/*
 * 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 org.xwiki.internal.migration;

import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;

import org.slf4j.Logger;
import org.xwiki.component.annotation.Component;
import org.xwiki.index.IndexException;
import org.xwiki.index.TaskConsumer;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.rendering.syntax.Syntax;

import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.doc.XWikiDocument;

/**
 * Apply {@link InvitationInternalDocumentParameterEscapingFixer} on the documents queued by
 * {@link R150000000XWIKI20285DataMigration} if applicable, and log the skipped ones. This translation key is initially
 * introduced by the invitation application but the fix is localed in oldcore so that the fix is applied on pages even
 * if the invitation application has been uninstalled.
 *
 * @version $Id$
 * @since 15.0RC1
 * @since 14.4.8
 * @since 14.10.4
 */
@Component
@Singleton
@Named(InvitationInternalDocumentParameterEscapingTaskConsumer.HINT)
public class InvitationInternalDocumentParameterEscapingTaskConsumer implements TaskConsumer
{
    /**
     * This task consumer hint.
     */
    public static final String HINT = "internal-document-parameter-escaping";

    @Inject
    private Logger logger;

    @Inject
    private Provider<XWikiContext> contextProvider;

    @Inject
    private InvitationInternalDocumentParameterEscapingFixer invitationInternalDocumentParameterEscapingFixer;

    @Override
    public void consume(DocumentReference documentReference, String version) throws IndexException
    {
        try {
            XWikiContext context = this.contextProvider.get();
            task(context.getWiki().getDocument(documentReference, context));
        } catch (XWikiException e) {
            throw new IndexException(String.format("Failed to resolve document [%s]", documentReference), e);
        }
    }

    private void task(XWikiDocument document)
    {
        Syntax syntax = document.getSyntax();
        if (List.of(Syntax.XWIKI_2_1, Syntax.XWIKI_2_0).contains(syntax)) {
            try {
                this.invitationInternalDocumentParameterEscapingFixer.fix(document.getContent(), document.getSyntax())
                    .ifPresent(content -> {
                        document.setContent(content);
                        try {
                            XWikiContext context = this.contextProvider.get();
                            context.getWiki().saveDocument(document, "Automatic bad escaping fix.", true, context);
                            this.logger.info("[{}] successfully fixed.", document);
                        } catch (XWikiException e) {
                            this.logger.error("Failed to save document [{}]", document, e);
                        }
                    });
            } catch (Exception e) {
                this.logger.error("Unexpected error while fixing [{}]", document, e);
            }
        } else {
            this.logger.warn(
                "[{}] skipped because escaping for syntax [{}] is not supported. It is advised to review this file.",
                document, syntax);
        }
    }
}
/*
 * 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 org.xwiki.internal.migration;

import java.util.List;
import java.util.stream.Collectors;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.xwiki.component.annotation.Component;
import org.xwiki.internal.extension.XARExtensionIndex;
import org.xwiki.query.QueryException;

import com.xpn.xwiki.XWiki;
import com.xpn.xwiki.store.migration.DataMigrationException;
import com.xpn.xwiki.store.migration.XWikiDBVersion;

import static org.xwiki.query.Query.XWQL;

/**
 * Search for documents with a known invalid content produced by code generation (i.e., that cannot be fixed by editing
 * a xar file), and apply an automatic escaping fix.
 *
 * @version $Id$
 * @since 15.0RC1
 * @since 14.4.8
 * @since 14.10.4
 */
@Component
@Named("R150000000XWIKI20285")
@Singleton
public class R150000000XWIKI20285DataMigration extends AbstractDocumentsMigration
{
    @Inject
    private XARExtensionIndex installedXARs;

    @Override
    public String getDescription()
    {
        return "Patch the InvitationConfig documents with improper escaping.";
    }

    @Override
    public XWikiDBVersion getVersion()
    {
        return new XWikiDBVersion(150000000);
    }

    @Override
    protected String getTaskType()
    {
        return InvitationInternalDocumentParameterEscapingTaskConsumer.HINT;
    }

    @Override
    protected List<String> selectDocuments() throws DataMigrationException
    {
        XWiki wiki = getXWikiContext().getWiki();
        try {
            // We select potentially impacted documents using like wildcards. This selection might lead to false 
            // positive that wild be filtered out by the more accurate regex in
            // InvitationInternalDocumentParameterEscapingFixer.
            return wiki.getStore().getQueryManager()
                .createQuery("where doc.content "
                    + "like '%{{info}}%services.localization.render%xe.invitation.internalDocument%{{/info}}%'", XWQL)
                .<String>execute()
                .stream()
                // Exclude document that are provided by extensions, because they are fixed using the usual xar upgrade
                // mechanism.
                .filter(documentReference -> !this.installedXARs.isExtensionDocument(
                    this.documentReferenceResolver.resolve(documentReference)))
                .collect(Collectors.toList());
        } catch (QueryException e) {
            throw new DataMigrationException(
                String.format("Failed retrieve the list of all the documents for wiki [%s].", wiki.getName()), e);
        }
    }
}
org.xwiki.internal.migration.R150000000XWIKI20285DataMigration
org.xwiki.internal.migration.InvitationInternalDocumentParameterEscapingFixer
org.xwiki.internal.migration.InvitationInternalDocumentParameterEscapingTaskConsumer
/*
 * 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 org.xwiki.internal.migration;

import java.util.Optional;
import java.util.stream.Stream;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.xwiki.rendering.syntax.Syntax;
import org.xwiki.test.junit5.mockito.ComponentTest;
import org.xwiki.test.junit5.mockito.InjectMockComponents;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * Test of {@link InvitationInternalDocumentParameterEscapingFixer}.
 *
 * @version $Id$
 */
@ComponentTest
class InvitationInternalDocumentParameterEscapingFixerTest
{
    @InjectMockComponents
    private InvitationInternalDocumentParameterEscapingFixer fixer;

    public static Stream<Arguments> fixSource()
    {
        return Stream.of(
            Arguments.of(
                "nothing to change 1\n"
                    + "{{info}}$services.localization.render('xe.invitation.internalDocument', [$noChange]){{/info}}"
                    + "nothing to change 2\n"
                    + "{{info}}$services.localization.render('xe.invitation.internalDocument', [\"$change\"]){{/info}}"
                    + "nothing to change 3",
                Optional.of(
                    "nothing to change 1\n"
                        + "{{info}}$services.localization.render('xe.invitation.internalDocument', [$noChange]){{/info}}nothing to change 2\n"
                        + "{{info}}$services.localization.render('xe.invitation.internalDocument', [$services.rendering.escape(\"$change\", 'xwiki/2.1')]){{/info}}nothing to change 3"
                )
            ),
            Arguments.of(
                "nothing to change 1\n"
                    + "{{info}}$services.localization.render('xe.invitation.internalDocument', [$noChange]){{/info}}"
                    + "nothing to change 2\n"
                    + "nothing to change 3",
                Optional.empty()
            )
        );
    }

    @ParameterizedTest
    @MethodSource("fixSource")
    void fix(String value, Optional<String> expected)
    {
        assertEquals(expected, this.fixer.fix(value, Syntax.XWIKI_2_1));
    }
}
      <artifactId>xwiki-platform-index-default</artifactId>