Skip to content
Snippets Groups Projects
Commit 0d39adfd authored by Thomas Mortagne's avatar Thomas Mortagne
Browse files

XWIKI-13327: Uninstalling a XAR extension should not propose to delete pages...

XWIKI-13327: Uninstalling a XAR extension should not propose to delete pages that is also owned by another installed extension
parent 5d7c2419
No related merge requests found
Showing
with 702 additions and 76 deletions
......@@ -20,11 +20,12 @@
package org.xwiki.extension.xar.internal.handler;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
......@@ -38,8 +39,10 @@
import org.xwiki.extension.LocalExtension;
import org.xwiki.extension.job.internal.InstallJob;
import org.xwiki.extension.job.internal.UninstallJob;
import org.xwiki.extension.repository.InstalledExtensionRepository;
import org.xwiki.extension.xar.internal.handler.packager.PackageConfiguration;
import org.xwiki.extension.xar.internal.handler.packager.Packager;
import org.xwiki.extension.xar.internal.repository.XarInstalledExtensionRepository;
import org.xwiki.extension.xar.question.CleanPagesQuestion;
import org.xwiki.job.Job;
import org.xwiki.job.Request;
......@@ -66,8 +69,8 @@ public class XarExtensionJobFinishedListener implements EventListener
/**
* The list of events observed.
*/
private static final List<Event> EVENTS = Arrays.<Event> asList(new JobFinishedEvent(InstallJob.JOBTYPE),
new JobFinishedEvent(UninstallJob.JOBTYPE));
private static final List<Event> EVENTS =
Arrays.<Event>asList(new JobFinishedEvent(InstallJob.JOBTYPE), new JobFinishedEvent(UninstallJob.JOBTYPE));
@Inject
private Execution execution;
......@@ -81,6 +84,10 @@ public class XarExtensionJobFinishedListener implements EventListener
@Inject
private Logger logger;
@Inject
@Named(XarExtensionHandler.TYPE)
private InstalledExtensionRepository xarRepository;
@Override
public String getName()
{
......@@ -122,22 +129,29 @@ public void onEvent(Event event, Object source, Object data)
// Get pages to delete
List<DocumentReference> pagesToDelete = new ArrayList<DocumentReference>();
Set<DocumentReference> pagesToDelete = new HashSet<DocumentReference>();
for (Map.Entry<String, Map<XarEntry, XarExtensionPlanEntry>> previousWikiEntry : previousXAREntries
.entrySet()) {
if (!previousWikiEntry.getValue().isEmpty()) {
try {
pagesToDelete.addAll(packager.getDocumentReferences(
previousWikiEntry.getValue().keySet(),
createPackageConfiguration(jobFinishedEvent.getRequest(),
previousWikiEntry.getKey())));
List<DocumentReference> references =
packager.getDocumentReferences(previousWikiEntry.getValue().keySet(),
createPackageConfiguration(jobFinishedEvent.getRequest(),
previousWikiEntry.getKey()));
for (DocumentReference reference : references) {
// Ignore document that are part of other installed extensions (don't even
// propose to enable them)
if (((XarInstalledExtensionRepository) this.xarRepository)
.getXarInstalledExtensions(reference).isEmpty()) {
pagesToDelete.add(reference);
}
}
} catch (Exception e) {
this.logger
.warn(
"Exception when cleaning pages removed since previous xar extension version",
e);
this.logger.warn(
"Exception when cleaning pages removed since previous xar extension version",
e);
}
}
}
......@@ -247,8 +261,8 @@ private PackageConfiguration createPackageConfiguration(Request request, String
PackageConfiguration configuration = new PackageConfiguration();
configuration.setInteractive(false);
configuration.setUser(XarExtensionHandler.getRequestUserReference(XarExtensionHandler.PROPERTY_USERREFERENCE,
request));
configuration
.setUser(XarExtensionHandler.getRequestUserReference(XarExtensionHandler.PROPERTY_USERREFERENCE, request));
configuration.setWiki(wiki);
configuration.setVerbose(request.isVerbose());
configuration.setSkipMandatorytDocuments(true);
......
/*
* 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.repository;
import java.util.Collection;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.slf4j.Logger;
import org.xwiki.component.annotation.Component;
import org.xwiki.extension.InstalledExtension;
import org.xwiki.extension.event.ExtensionEvent;
import org.xwiki.extension.event.ExtensionInstalledEvent;
import org.xwiki.extension.event.ExtensionUninstalledEvent;
import org.xwiki.extension.event.ExtensionUpgradedEvent;
import org.xwiki.extension.repository.InstalledExtensionRepository;
import org.xwiki.extension.xar.internal.handler.UnsupportedNamespaceException;
import org.xwiki.extension.xar.internal.handler.XarExtensionHandler;
import org.xwiki.observation.AbstractEventListener;
import org.xwiki.observation.event.Event;
/**
* Maintain extension stored in {@link XarInstalledExtensionRepository} in sync with the standard
* {@link InstalledExtensionRepository} component.
*
* @version $Id$
* @since 8.1M2
*/
@Component
@Named("org.xwiki.extension.xar.internal.repository.InstalledExtensionSynchronizer")
@Singleton
public class InstalledExtensionSynchronizer extends AbstractEventListener
{
@Inject
@Named(XarExtensionHandler.TYPE)
private InstalledExtensionRepository xarRepository;
@Inject
private Logger logger;
/**
* Default constructor.
*/
public InstalledExtensionSynchronizer()
{
super(InstalledExtensionSynchronizer.class.getName(), new ExtensionInstalledEvent(),
new ExtensionUninstalledEvent(), new ExtensionUpgradedEvent());
}
private XarInstalledExtensionRepository getXarRepository()
{
return (XarInstalledExtensionRepository) this.xarRepository;
}
@Override
public void onEvent(Event event, Object source, Object data)
{
ExtensionEvent extensionEvent = (ExtensionEvent) event;
try {
if (extensionEvent instanceof ExtensionUninstalledEvent) {
// Update documents index
getXarRepository().pagesRemoved(extensionEvent.getExtensionId(), extensionEvent.getNamespace());
// Update extension cache
getXarRepository().updateCachedXarExtension(extensionEvent.getExtensionId());
} else {
// Previous extensions
if (data != null) {
for (InstalledExtension installedExtension : (Collection<InstalledExtension>) data) {
// Update documents index
getXarRepository().pagesRemoved(installedExtension.getId(), extensionEvent.getNamespace());
// Update extension cache
getXarRepository().updateCachedXarExtension(installedExtension.getId());
}
}
// New extension
// Update extension cache
getXarRepository().updateCachedXarExtension(extensionEvent.getExtensionId());
// Update documents index
getXarRepository().pagesAdded(extensionEvent.getExtensionId(), extensionEvent.getNamespace());
}
} catch (UnsupportedNamespaceException e) {
logger.error("Failed to extract wiki from namespace [{}]", extensionEvent.getNamespace());
}
}
}
......@@ -20,10 +20,13 @@
package org.xwiki.extension.xar.internal.repository;
import java.io.IOException;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.inject.Inject;
import javax.inject.Named;
......@@ -39,16 +42,16 @@
import org.xwiki.extension.LocalExtension;
import org.xwiki.extension.ResolveException;
import org.xwiki.extension.UninstallException;
import org.xwiki.extension.event.ExtensionInstalledEvent;
import org.xwiki.extension.event.ExtensionUninstalledEvent;
import org.xwiki.extension.event.ExtensionUpgradedEvent;
import org.xwiki.extension.repository.DefaultExtensionRepositoryDescriptor;
import org.xwiki.extension.repository.InstalledExtensionRepository;
import org.xwiki.extension.repository.internal.installed.AbstractInstalledExtensionRepository;
import org.xwiki.extension.xar.internal.handler.UnsupportedNamespaceException;
import org.xwiki.extension.xar.internal.handler.XarExtensionHandler;
import org.xwiki.observation.EventListener;
import org.xwiki.observation.ObservationManager;
import org.xwiki.observation.event.Event;
import org.xwiki.extension.xar.internal.handler.XarHandlerUtils;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.LocalDocumentReference;
import org.xwiki.model.reference.WikiReference;
import org.xwiki.xar.XarEntry;
import org.xwiki.xar.XarException;
/**
......@@ -63,20 +66,21 @@
public class XarInstalledExtensionRepository extends AbstractInstalledExtensionRepository<XarInstalledExtension>
implements InstalledExtensionRepository, Initializable
{
private static final List<Event> EVENTS = Arrays.<Event>asList(new ExtensionInstalledEvent(),
new ExtensionUninstalledEvent(), new ExtensionUpgradedEvent());
@Inject
private transient InstalledExtensionRepository installedRepository;
@Inject
private transient ObservationManager observation;
private Logger logger;
/**
* The logger to log.
* Index used to find extensions owners of a document installed on a specific wiki.
*/
@Inject
private Logger logger;
private Map<DocumentReference, Collection<XarInstalledExtension>> documents = new ConcurrentHashMap<>();
/**
* Index used to find extensions owners of a document installed on root namespace.
*/
private Map<LocalDocumentReference, Collection<XarInstalledExtension>> rootDocuments = new ConcurrentHashMap<>();
@Override
public void initialize() throws InitializationException
......@@ -85,65 +89,92 @@ public void initialize() throws InitializationException
this.installedRepository.getDescriptor().getURI()));
loadExtensions();
}
void pagesRemoved(ExtensionId extensionId, String namespace) throws UnsupportedNamespaceException
{
pagesUpdated(extensionId, namespace, false);
}
this.observation.addListener(new EventListener()
{
@Override
public void onEvent(Event event, Object source, Object data)
{
LocalExtension extension = (LocalExtension) source;
if (extension.getType().equals(XarExtensionHandler.TYPE)) {
updateXarExtension(extension);
if (data != null) {
for (InstalledExtension installedExtension : (Collection<InstalledExtension>) data) {
updateXarExtension(installedExtension);
void pagesAdded(ExtensionId extensionId, String namespace) throws UnsupportedNamespaceException
{
pagesUpdated(extensionId, namespace, true);
}
private void pagesUpdated(ExtensionId extensionId, String namespace, boolean add)
throws UnsupportedNamespaceException
{
XarInstalledExtension installedExtension = (XarInstalledExtension) getInstalledExtension(extensionId);
if (installedExtension != null) {
for (XarEntry xarEntry : installedExtension.getXarPackage().getEntries()) {
if (namespace != null) {
DocumentReference reference = new DocumentReference(xarEntry,
new WikiReference(XarHandlerUtils.getWikiFromNamespace(namespace)));
synchronized (this.documents) {
Collection<XarInstalledExtension> referenceExtensions = this.documents.get(reference);
if (referenceExtensions != null || add) {
Set<XarInstalledExtension> newSet = referenceExtensions != null
? new LinkedHashSet<>(referenceExtensions) : new LinkedHashSet<>();
if (add) {
newSet.add(installedExtension);
} else {
newSet.remove(installedExtension);
}
this.documents.put(reference, newSet);
}
}
}
}
} else {
synchronized (this.rootDocuments) {
Collection<XarInstalledExtension> referenceExtensions = this.rootDocuments.get(xarEntry);
if (referenceExtensions != null || add) {
Set<XarInstalledExtension> newSet = referenceExtensions != null
? new LinkedHashSet<>(referenceExtensions) : new LinkedHashSet<>();
@Override
public String getName()
{
return XarInstalledExtensionRepository.class.getName();
}
if (add) {
newSet.add(installedExtension);
} else {
newSet.remove(installedExtension);
}
@Override
public List<Event> getEvents()
{
return EVENTS;
this.rootDocuments.put(xarEntry, newSet);
}
}
}
}
});
}
}
private void updateXarExtension(LocalExtension extension)
void updateCachedXarExtension(ExtensionId extensionId)
{
if (this.extensions.containsKey(extension.getId())) {
if (!(extension instanceof InstalledExtension)) {
removeXarExtension(extension.getId());
}
} else {
if (extension instanceof InstalledExtension) {
InstalledExtension installedExtension = this.installedRepository.getInstalledExtension(extensionId);
if (installedExtension != null && installedExtension.getType().equals(XarExtensionHandler.TYPE)) {
if (getInstalledExtension(installedExtension.getId()) == null) {
try {
addXarExtension((InstalledExtension) extension);
addCacheXarExtension(installedExtension);
} catch (Exception e) {
this.logger.error("Failed to parse extension [" + extension + "]", e);
this.logger.error("Failed to parse extension [" + installedExtension + "]", e);
}
}
} else {
removeCachedXarExtension(extensionId);
}
}
private void addXarExtension(InstalledExtension extension) throws IOException, XarException
private void addCacheXarExtension(InstalledExtension installedExtension) throws IOException, XarException
{
XarInstalledExtension xarExtension = new XarInstalledExtension(extension, this);
XarInstalledExtension xarExtension = new XarInstalledExtension(installedExtension, this);
addCachedExtension(xarExtension);
}
private void removeXarExtension(ExtensionId extensionId)
protected void removeCachedXarExtension(ExtensionId extensionId)
{
removeCachedExtension(this.extensions.get(extensionId));
super.removeCachedExtension((XarInstalledExtension) getInstalledExtension(extensionId));
}
private void loadExtensions()
......@@ -151,7 +182,7 @@ private void loadExtensions()
for (InstalledExtension localExtension : this.installedRepository.getInstalledExtensions()) {
if (localExtension.getType().equalsIgnoreCase(XarExtensionHandler.TYPE)) {
try {
addXarExtension(localExtension);
addCacheXarExtension(localExtension);
} catch (Exception e) {
this.logger.error("Failed to parse extension [" + localExtension + "]", e);
}
......@@ -159,6 +190,29 @@ private void loadExtensions()
}
}
/**
* @param reference the reference of the document
* @return the extension owners of the passed document
* @since 8.1M2
*/
public Collection<XarInstalledExtension> getXarInstalledExtensions(DocumentReference reference)
{
Collection<XarInstalledExtension> wikiExtensions = this.documents.get(reference);
Collection<XarInstalledExtension> rootExtensions = this.rootDocuments.get(reference);
List<XarInstalledExtension> allExtensions = new ArrayList<>();
if (wikiExtensions != null) {
allExtensions.addAll(wikiExtensions);
}
if (rootExtensions != null) {
allExtensions.addAll(rootExtensions);
}
return allExtensions;
}
// InstalledExtensionRepository
@Override
......@@ -195,8 +249,8 @@ public Collection<InstalledExtension> getBackwardDependencies(String id, String
{
InstalledExtension extension = this.installedRepository.getInstalledExtension(id, namespace);
return extension.getType().equals(XarExtensionHandler.TYPE) ? this.installedRepository.getBackwardDependencies(
id, namespace) : null;
return extension.getType().equals(XarExtensionHandler.TYPE)
? this.installedRepository.getBackwardDependencies(id, namespace) : null;
}
@Override
......@@ -205,7 +259,7 @@ public Map<String, Collection<InstalledExtension>> getBackwardDependencies(Exten
{
InstalledExtension extension = this.installedRepository.resolve(extensionId);
return extension.getType().equals(XarExtensionHandler.TYPE) ? this.installedRepository
.getBackwardDependencies(extensionId) : null;
return extension.getType().equals(XarExtensionHandler.TYPE)
? this.installedRepository.getBackwardDependencies(extensionId) : null;
}
}
......@@ -9,6 +9,7 @@ org.xwiki.extension.xar.internal.job.DiffXarJob
org.xwiki.extension.xar.internal.job.RepairXarJob
org.xwiki.extension.xar.internal.question.CleanPagesQuestionRecorder
org.xwiki.extension.xar.internal.question.ConflictQuestionRecorder
org.xwiki.extension.xar.internal.repository.InstalledExtensionSynchronizer
org.xwiki.extension.xar.internal.repository.XarInstalledExtensionRepository
org.xwiki.extension.xar.internal.script.ConflictQuestionScriptSafeProvider
org.xwiki.extension.xar.internal.script.DiffXarJobStatusScriptSafeProvider
......
......@@ -67,6 +67,7 @@
import com.xpn.xwiki.test.MockitoOldcoreRule;
import com.xpn.xwiki.util.XWikiStubContextProvider;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
......@@ -89,6 +90,10 @@ public class XarExtensionHandlerTest
private ExtensionId localXarExtensiontId2;
private ExtensionId collisionextension1;
private ExtensionId collisionextension2;
private JobExecutor jobExecutor;
private InstalledExtensionRepository xarExtensionRepository;
......@@ -108,6 +113,8 @@ public void setUp() throws Exception
this.localXarExtensiontId1 = new ExtensionId("test", "1.0");
this.localXarExtensiontId2 = new ExtensionId("test", "2.0");
this.collisionextension1 = new ExtensionId("collisionextension1", "version");
this.collisionextension2 = new ExtensionId("collisionextension2", "version");
// classes
......@@ -829,6 +836,47 @@ public void testUninstallMandatory() throws Throwable
Assert.assertFalse("Document wiki.space.page has been removed from the database", page.isNew());
}
@Test
public void testUninstallExtensionWithCommonDocumentOnWiki() throws Throwable
{
mockHasAdminRight(true);
install(this.collisionextension1, "wiki", this.contextUser);
install(this.collisionextension2, "wiki", this.contextUser);
// uninstall
uninstall(this.collisionextension1, "wiki");
XWikiDocument page = this.oldcore.getSpyXWiki()
.getDocument(new DocumentReference("wiki", "samespace", "samepage"), getXWikiContext());
assertFalse(page.isNew());
}
@Test
public void testUninstallExtensionWithCommonDocumentOnRoot() throws Throwable
{
mockHasAdminRight(true);
install(this.collisionextension1, null, this.contextUser);
install(this.collisionextension2, null, this.contextUser);
}
@Test
public void testUninstallExtensionWithCommonDocumentOnRootAndWiki() throws Throwable
{
mockHasAdminRight(true);
install(this.collisionextension1, "wiki", this.contextUser);
install(this.collisionextension2, null, this.contextUser);
}
@Test
public void testUninstallExtensionWithCommonDocumentOnWikiAndRoot() throws Throwable
{
mockHasAdminRight(true);
install(this.collisionextension1, null, this.contextUser);
install(this.collisionextension2, "wiki", this.contextUser);
}
@Test
public void testInstallOnRoot() throws Throwable
{
......
......@@ -19,9 +19,6 @@
*/
package org.xwiki.extension.xar.internal.job;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
import org.xwiki.extension.ExtensionId;
......@@ -29,9 +26,12 @@
import org.xwiki.extension.repository.InstalledExtensionRepository;
import org.xwiki.extension.test.AbstractExtensionHandlerTest;
import org.xwiki.extension.xar.internal.handler.XarExtensionHandler;
import org.xwiki.extension.xar.internal.job.RepairXarJob;
import org.xwiki.job.Job;
import org.xwiki.logging.LogLevel;
import org.xwiki.security.authorization.ContextualAuthorizationManager;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class RepairXarJobTest extends AbstractExtensionHandlerTest
{
......@@ -43,6 +43,8 @@ public void setUp() throws Exception
{
super.setUp();
this.mocker.registerMockComponent(ContextualAuthorizationManager.class);
this.xarExtensionRepository =
this.mocker.getInstance(InstalledExtensionRepository.class, XarExtensionHandler.TYPE);
}
......@@ -73,7 +75,7 @@ public void testRepairOnWiki() throws Throwable
{
ExtensionId extensionId = new ExtensionId("test", "1.0");
repair(extensionId, new String[] {"wiki1"}, LogLevel.WARN);
repair(extensionId, new String[] {"wiki:wiki1"}, LogLevel.WARN);
InstalledExtension installedExtension = this.xarExtensionRepository.resolve(extensionId);
......
<?xml version="1.0" encoding="UTF-8"?>
<package>
<infos/>
<name>space.page</name>
<description></description>
<licence>LGPL</licence>
<author>XWiki</author>
<version>1.0.0</version>
<backupPack>false</backupPack>
<preserveVersion>false</preserveVersion><files>
<file defaultAction="0" language="">samespace.samespage</file></files></package>
<?xml version="1.0" encoding="UTF-8"?>
<xwikidoc>
<web>samespace</web>
<name>samepage</name>
<language></language>
<defaultLanguage>en</defaultLanguage>
<translation>0</translation>
<parent></parent>
<creator>XWiki.creator</creator>
<author>XWiki.author</author>
<customClass></customClass>
<contentAuthor>XWiki.contentAuthor</contentAuthor>
<creationDate>1297952360000</creationDate>
<date>1297952371000</date>
<contentUpdateDate>1297952371000</contentUpdateDate>
<version>1.1</version>
<title></title>
<template></template>
<defaultTemplate></defaultTemplate>
<validationScript></validationScript>
<comment></comment>
<minorEdit>false</minorEdit>
<syntaxId>xwiki/2.0</syntaxId>
<hidden>false</hidden>
<attachment>
<filename>attachment.txt</filename>
<filesize>19</filesize>
<author>XWiki.attachmentauthor</author>
<date>1342102850000</date>
<version>1.1</version>
<comment></comment>
<content>YXR0YWNobWVudCBjb250ZW50C</content>
</attachment>
<class>
<name>samespace.samepage</name>
<customClass></customClass>
<customMapping></customMapping>
<defaultViewSheet></defaultViewSheet>
<defaultEditSheet></defaultEditSheet>
<defaultWeb></defaultWeb>
<nameField></nameField>
<validationScript></validationScript>
<property>
<disabled>0</disabled>
<name>property</name>
<number>1</number>
<numberType>long</numberType>
<prettyName>property</prettyName>
<size>30</size>
<unmodifiable>0</unmodifiable>
<classType>com.xpn.xwiki.objects.classes.NumberClass</classType>
</property>
</class>
<object>
<class>
<name>XWiki.StyleSheetExtension</name>
<customClass></customClass>
<customMapping></customMapping>
<defaultViewSheet></defaultViewSheet>
<defaultEditSheet></defaultEditSheet>
<defaultWeb></defaultWeb>
<nameField></nameField>
<validationScript></validationScript>
<cache>
<cache>0</cache>
<disabled>0</disabled>
<displayType>select</displayType>
<multiSelect>0</multiSelect>
<name>cache</name>
<number>5</number>
<prettyName>Caching policy</prettyName>
<relationalStorage>0</relationalStorage>
<separator>
</separator>
<separators> ,|</separators>
<size>1</size>
<unmodifiable>0</unmodifiable>
<values>long|short|default|forbid</values>
<classType>com.xpn.xwiki.objects.classes.StaticListClass</classType>
</cache>
<code>
<disabled>0</disabled>
<name>code</name>
<number>2</number>
<prettyName>Code</prettyName>
<rows>20</rows>
<size>50</size>
<unmodifiable>0</unmodifiable>
<classType>com.xpn.xwiki.objects.classes.TextAreaClass</classType>
</code>
<name>
<disabled>0</disabled>
<name>name</name>
<number>1</number>
<prettyName>Name</prettyName>
<size>30</size>
<unmodifiable>0</unmodifiable>
<classType>com.xpn.xwiki.objects.classes.StringClass</classType>
</name>
<parse>
<disabled>0</disabled>
<displayFormType>select</displayFormType>
<displayType>yesno</displayType>
<name>parse</name>
<number>4</number>
<prettyName>Parse content</prettyName>
<unmodifiable>0</unmodifiable>
<classType>com.xpn.xwiki.objects.classes.BooleanClass</classType>
</parse>
<use>
<cache>0</cache>
<disabled>0</disabled>
<displayType>select</displayType>
<multiSelect>0</multiSelect>
<name>use</name>
<number>3</number>
<prettyName>Use this extension</prettyName>
<relationalStorage>0</relationalStorage>
<separator>
</separator>
<separators> ,|</separators>
<size>1</size>
<unmodifiable>0</unmodifiable>
<values>onDemand=On demand|always=Always</values>
<classType>com.xpn.xwiki.objects.classes.StaticListClass</classType>
</use>
</class>
<name>samespace.samepage</name>
<number>0</number>
<className>XWiki.StyleSheetExtension</className>
<guid>8eaeac52-e2f2-47b2-87e1-bc6909597b39</guid>
<property>
<cache>long</cache>
</property>
<property>
<code>some code</code>
</property>
<property>
<name>name</name>
</property>
<property>
<parse></parse>
</property>
<property>
<use>onDemand</use>
</property>
</object>
<object>
<class>
<name>samespace.samepage</name>
<customClass></customClass>
<customMapping></customMapping>
<defaultViewSheet></defaultViewSheet>
<defaultEditSheet></defaultEditSheet>
<defaultWeb></defaultWeb>
<nameField></nameField>
<validationScript></validationScript>
<property>
<disabled>0</disabled>
<name>property</name>
<number>1</number>
<numberType>long</numberType>
<prettyName>property</prettyName>
<size>30</size>
<unmodifiable>0</unmodifiable>
<classType>com.xpn.xwiki.objects.classes.NumberClass</classType>
</property>
</class>
<name>samespace.samepage</name>
<number>0</number>
<className>samespace.samepage</className>
<guid>e2167721-2a64-430c-9520-bac1c0ee68cb</guid>
<property>
<property>12</property>
</property>
</object>
<content>content</content>
</xwikidoc>
<?xml version="1.0" encoding="UTF-8"?>
<package>
<infos/>
<name>space.page</name>
<description></description>
<licence>LGPL</licence>
<author>XWiki</author>
<version>1.0.0</version>
<backupPack>false</backupPack>
<preserveVersion>false</preserveVersion><files>
<file defaultAction="0" language="">samespace.samespage</file></files></package>
<?xml version="1.0" encoding="UTF-8"?>
<xwikidoc>
<web>samespace</web>
<name>samepage</name>
<language></language>
<defaultLanguage>en</defaultLanguage>
<translation>0</translation>
<parent></parent>
<creator>XWiki.creator</creator>
<author>XWiki.author</author>
<customClass></customClass>
<contentAuthor>XWiki.contentAuthor</contentAuthor>
<creationDate>1297952360000</creationDate>
<date>1297952371000</date>
<contentUpdateDate>1297952371000</contentUpdateDate>
<version>1.1</version>
<title></title>
<template></template>
<defaultTemplate></defaultTemplate>
<validationScript></validationScript>
<comment></comment>
<minorEdit>false</minorEdit>
<syntaxId>xwiki/2.0</syntaxId>
<hidden>false</hidden>
<attachment>
<filename>attachment.txt</filename>
<filesize>19</filesize>
<author>XWiki.attachmentauthor</author>
<date>1342102850000</date>
<version>1.1</version>
<comment></comment>
<content>YXR0YWNobWVudCBjb250ZW50C</content>
</attachment>
<class>
<name>samespace.samepage</name>
<customClass></customClass>
<customMapping></customMapping>
<defaultViewSheet></defaultViewSheet>
<defaultEditSheet></defaultEditSheet>
<defaultWeb></defaultWeb>
<nameField></nameField>
<validationScript></validationScript>
<property>
<disabled>0</disabled>
<name>property</name>
<number>1</number>
<numberType>long</numberType>
<prettyName>property</prettyName>
<size>30</size>
<unmodifiable>0</unmodifiable>
<classType>com.xpn.xwiki.objects.classes.NumberClass</classType>
</property>
</class>
<object>
<class>
<name>XWiki.StyleSheetExtension</name>
<customClass></customClass>
<customMapping></customMapping>
<defaultViewSheet></defaultViewSheet>
<defaultEditSheet></defaultEditSheet>
<defaultWeb></defaultWeb>
<nameField></nameField>
<validationScript></validationScript>
<cache>
<cache>0</cache>
<disabled>0</disabled>
<displayType>select</displayType>
<multiSelect>0</multiSelect>
<name>cache</name>
<number>5</number>
<prettyName>Caching policy</prettyName>
<relationalStorage>0</relationalStorage>
<separator>
</separator>
<separators> ,|</separators>
<size>1</size>
<unmodifiable>0</unmodifiable>
<values>long|short|default|forbid</values>
<classType>com.xpn.xwiki.objects.classes.StaticListClass</classType>
</cache>
<code>
<disabled>0</disabled>
<name>code</name>
<number>2</number>
<prettyName>Code</prettyName>
<rows>20</rows>
<size>50</size>
<unmodifiable>0</unmodifiable>
<classType>com.xpn.xwiki.objects.classes.TextAreaClass</classType>
</code>
<name>
<disabled>0</disabled>
<name>name</name>
<number>1</number>
<prettyName>Name</prettyName>
<size>30</size>
<unmodifiable>0</unmodifiable>
<classType>com.xpn.xwiki.objects.classes.StringClass</classType>
</name>
<parse>
<disabled>0</disabled>
<displayFormType>select</displayFormType>
<displayType>yesno</displayType>
<name>parse</name>
<number>4</number>
<prettyName>Parse content</prettyName>
<unmodifiable>0</unmodifiable>
<classType>com.xpn.xwiki.objects.classes.BooleanClass</classType>
</parse>
<use>
<cache>0</cache>
<disabled>0</disabled>
<displayType>select</displayType>
<multiSelect>0</multiSelect>
<name>use</name>
<number>3</number>
<prettyName>Use this extension</prettyName>
<relationalStorage>0</relationalStorage>
<separator>
</separator>
<separators> ,|</separators>
<size>1</size>
<unmodifiable>0</unmodifiable>
<values>onDemand=On demand|always=Always</values>
<classType>com.xpn.xwiki.objects.classes.StaticListClass</classType>
</use>
</class>
<name>samespace.samepage</name>
<number>0</number>
<className>XWiki.StyleSheetExtension</className>
<guid>8eaeac52-e2f2-47b2-87e1-bc6909597b39</guid>
<property>
<cache>long</cache>
</property>
<property>
<code>some code</code>
</property>
<property>
<name>name</name>
</property>
<property>
<parse></parse>
</property>
<property>
<use>onDemand</use>
</property>
</object>
<object>
<class>
<name>samespace.samepage</name>
<customClass></customClass>
<customMapping></customMapping>
<defaultViewSheet></defaultViewSheet>
<defaultEditSheet></defaultEditSheet>
<defaultWeb></defaultWeb>
<nameField></nameField>
<validationScript></validationScript>
<property>
<disabled>0</disabled>
<name>property</name>
<number>1</number>
<numberType>long</numberType>
<prettyName>property</prettyName>
<size>30</size>
<unmodifiable>0</unmodifiable>
<classType>com.xpn.xwiki.objects.classes.NumberClass</classType>
</property>
</class>
<name>samespace.samepage</name>
<number>0</number>
<className>samespace.samepage</className>
<guid>e2167721-2a64-430c-9520-bac1c0ee68cb</guid>
<property>
<property>12</property>
</property>
</object>
<content>content</content>
</xwikidoc>
<?xml version="1.0" encoding="UTF-8"?>
<extension>
<id>collisionextension1</id>
<version>version</version>
<type>xar</type>
</extension>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<extension>
<id>collisionextension2</id>
<version>version</version>
<type>xar</type>
</extension>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment