Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,19 @@

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;

Expand All @@ -46,27 +44,17 @@
* @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()
{
Expand All @@ -86,43 +74,47 @@ public XWikiDBVersion getVersion()
}

@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());
}
}
}
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -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
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,37 @@

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;
Expand All @@ -58,14 +68,20 @@ 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;
Expand All @@ -79,26 +95,48 @@ class R140300000XWIKI19614DataMigrationTest
@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
Expand All @@ -111,8 +149,11 @@ void migrateQueryException() throws Exception
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);
Expand All @@ -125,5 +166,7 @@ void migrateNotHasBacklinks() throws Exception
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());
}
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@
{{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
##
#*
Expand Down Expand Up @@ -186,8 +188,10 @@
$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))
Expand Down Expand Up @@ -275,8 +279,10 @@
#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())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* 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/>
Expand All @@ -34,7 +34,7 @@
<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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* 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/>
Expand All @@ -34,9 +34,9 @@
<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}}
#*
Expand Down Expand Up @@ -93,7 +93,9 @@
$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
##
##---------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* 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/>
Expand All @@ -34,9 +34,13 @@
<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/>
Expand Down Expand Up @@ -121,10 +125,10 @@
<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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* 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/>
Expand All @@ -34,11 +34,11 @@
<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}}
#*
Expand Down Expand Up @@ -120,7 +120,9 @@ $xwiki.get('ssx').use($config.get('commonPage'))
#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
##
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* 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/>
Expand All @@ -34,7 +34,7 @@
<title/>
<comment/>
<minorEdit>false</minorEdit>
<syntaxId>xwiki/2.0</syntaxId>
<syntaxId>xwiki/2.1</syntaxId>
<hidden>true</hidden>
<content>{{velocity}}
#*
Expand All @@ -53,7 +53,9 @@
* 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
##
#*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ void setUp() throws Exception
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());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
}
Original file line number Diff line number Diff line change
@@ -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());
}
}
Original file line number Diff line number Diff line change
@@ -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());
}
}
Original file line number Diff line number Diff line change
@@ -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());
}
}
Original file line number Diff line number Diff line change
@@ -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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,13 @@
<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) -->
Expand Down
5 changes: 5 additions & 0 deletions xwiki-platform-core/xwiki-platform-oldcore/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,11 @@
<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>
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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());
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,6 @@ 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
Original file line number Diff line number Diff line change
@@ -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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@
</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>
Expand Down