diff --git a/pom.xml b/pom.xml index 1b0abe5c24ef..a49e3621496c 100644 --- a/pom.xml +++ b/pom.xml @@ -128,6 +128,7 @@ 10.17.1 + https://github.com/${xwiki.github.owner}/${xwiki.github.repository}/tree/master/ HEAD diff --git a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/ExtensionStore.java b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/ExtensionStore.java index 3c08accd77cb..353ade71e4e9 100644 --- a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/ExtensionStore.java +++ b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/ExtensionStore.java @@ -22,28 +22,56 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Provider; import javax.inject.Singleton; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.slf4j.Logger; +import org.xwiki.cache.Cache; +import org.xwiki.cache.CacheException; +import org.xwiki.cache.CacheManager; +import org.xwiki.cache.config.CacheConfiguration; +import org.xwiki.cache.eviction.LRUEvictionConfiguration; import org.xwiki.component.annotation.Component; +import org.xwiki.component.manager.ComponentLifecycleException; +import org.xwiki.component.phase.Disposable; +import org.xwiki.component.phase.Initializable; +import org.xwiki.component.phase.InitializationException; import org.xwiki.extension.DefaultExtensionSupportPlan; import org.xwiki.extension.DefaultExtensionSupportPlans; import org.xwiki.extension.DefaultExtensionSupporter; +import org.xwiki.extension.Extension; import org.xwiki.extension.ExtensionSupportPlan; import org.xwiki.extension.ExtensionSupportPlans; import org.xwiki.extension.ExtensionSupporter; +import org.xwiki.extension.version.Version; +import org.xwiki.extension.version.internal.DefaultVersion; +import org.xwiki.model.EntityType; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.model.reference.DocumentReferenceResolver; +import org.xwiki.model.reference.EntityReference; +import org.xwiki.model.reference.PageReference; +import org.xwiki.observation.EventListener; +import org.xwiki.observation.ObservationManager; +import org.xwiki.observation.event.Event; +import org.xwiki.query.Query; +import org.xwiki.query.QueryException; +import org.xwiki.query.QueryManager; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.internal.event.XObjectPropertyAddedEvent; +import com.xpn.xwiki.internal.event.XObjectPropertyDeletedEvent; +import com.xpn.xwiki.internal.event.XObjectPropertyUpdatedEvent; import com.xpn.xwiki.objects.BaseObject; import com.xpn.xwiki.objects.BaseProperty; import com.xpn.xwiki.objects.NumberProperty; @@ -56,14 +84,95 @@ */ @Component(roles = ExtensionStore.class) @Singleton -public class ExtensionStore +@SuppressWarnings("checkstyle:ClassFanOutComplexity") +public class ExtensionStore implements Initializable, Disposable { + /** + * The reference to match property {@link XWikiRepositoryModel#PROP_EXTENSION_ID} of class + * {@link XWikiRepositoryModel#EXTENSION_CLASSNAME} on whatever wiki. + */ + private static final EntityReference EXTENSIONID_PROPERTY_REFERENCE = + new EntityReference(XWikiRepositoryModel.PROP_EXTENSION_ID, EntityType.OBJECT_PROPERTY, + XWikiRepositoryModel.EXTENSION_OBJECTREFERENCE); + + private static final List EVENTS = + Arrays.asList(new XObjectPropertyAddedEvent(EXTENSIONID_PROPERTY_REFERENCE), + new XObjectPropertyDeletedEvent(EXTENSIONID_PROPERTY_REFERENCE), + new XObjectPropertyUpdatedEvent(EXTENSIONID_PROPERTY_REFERENCE)); + @Inject private Provider xcontextProvider; + @Inject + private CacheManager cacheManager; + + @Inject + private QueryManager queryManager; + + @Inject + private ObservationManager observation; + + @Inject + @Named("current") + private DocumentReferenceResolver currentStringResolver; + + /** + * Link extension id to document reference. The tabe contains null if the id link to no extension. + */ + private Cache documentReferenceCache; + @Inject private Logger logger; + private EventListener listener = new EventListener() + { + @Override + public void onEvent(Event event, Object source, Object data) + { + // TODO: Improve a bit by removing only what's changed + documentReferenceCache.removeAll(); + } + + @Override + public String getName() + { + return "repository.DefaultRepositoryManager"; + } + + @Override + public List getEvents() + { + return EVENTS; + } + }; + + @Override + public void initialize() throws InitializationException + { + // Init cache + CacheConfiguration cacheConfiguration = new CacheConfiguration(); + cacheConfiguration.setConfigurationId("repository.extensionid.documentreference"); + LRUEvictionConfiguration lru = new LRUEvictionConfiguration(); + lru.setMaxEntries(10000); + cacheConfiguration.put(LRUEvictionConfiguration.CONFIGURATIONID, lru); + + try { + this.documentReferenceCache = this.cacheManager.createNewCache(cacheConfiguration); + } catch (CacheException e) { + throw new InitializationException("Failed to initialize cache", e); + } + + // Listen to modifications + this.observation.addListener(listener, EventListener.CACHE_INVALIDATION_DEFAULT_PRIORITY); + } + + @Override + public void dispose() throws ComponentLifecycleException + { + this.observation.removeListener(listener.getName()); + this.documentReferenceCache.dispose(); + } + /** * @param the expected type of the value to return * @param xobject the xobject @@ -75,6 +184,18 @@ public T getValue(BaseObject xobject, String propertyName) return getValue(xobject, propertyName, (T) null); } + /** + * @param the expected type of the value to return + * @param xobjects the xobject to try + * @param propertyName the property of the xobject + * @return the value + * @since 17.9.0RC1 + */ + public T getValue(List xobjects, String propertyName) + { + return getValue(xobjects, propertyName, (T) null); + } + /** * @param the expected type of the value to return * @param xobject the xobject @@ -89,7 +210,11 @@ public T getValue(BaseObject xobject, String propertyName, T def) T value = def; if (property != null) { value = (T) property.getValue(); - if (value == null) { + if (value instanceof String stringValue) { + value = (T) StringUtils.defaultIfEmpty(stringValue, (String) def); + } else if (value instanceof Collection collectionValue) { + value = collectionValue.isEmpty() ? def : (T) collectionValue; + } else if (value == null) { value = def; } } @@ -97,6 +222,27 @@ public T getValue(BaseObject xobject, String propertyName, T def) return value; } + /** + * @param the expected type of the value to return + * @param xobjects the xobjects to try + * @param propertyName the property of the xobject + * @param def the value to return if the property is not set + * @return the value + * @since 17.9.0RC1 + */ + public T getValue(List xobjects, String propertyName, T def) + { + for (BaseObject xobject : xobjects) { + T result = getValue(xobject, propertyName, null); + + if (result != null) { + return result; + } + } + + return def; + } + private URL getURLValue(BaseProperty property, boolean fallbackOnDocumentURL, XWikiContext xcontext) { URL url = null; @@ -272,4 +418,283 @@ public BaseObject getExtensionSupporterObject(XWikiDocument extensionSupporterDo return extensionSupporterDocument.getXObject(XWikiRepositoryModel.EXTENSIONSUPPORTER_CLASSREFERENCE); } + /** + * Retrieve the extension version document for the given extension document and the given version. + * + * @param extensionDocument the document of the extension + * @param extensionVersion the version for which to retrieve the document + * @param xcontext the current context + * @return the version document or the given extension document + * @throws XWikiException in case of problem for loading the document + * @since 17.9.0RC1 + */ + public XWikiDocument getExtensionVersionDocument(XWikiDocument extensionDocument, Version extensionVersion, + XWikiContext xcontext) throws XWikiException + { + return getExtensionVersionDocument(extensionDocument, extensionVersion.getValue(), xcontext); + } + + /** + * Retrieve the extension version document for the given extension document and the given version. + * + * @param extensionDocument the document of the extension + * @param extensionVersion the version for which to retrieve the document + * @param xcontext the current context + * @return the version document or the given extension document + * @throws XWikiException in case of problem for loading the document + * @since 17.9.0RC1 + */ + public XWikiDocument getExtensionVersionDocument(XWikiDocument extensionDocument, String extensionVersion, + XWikiContext xcontext) throws XWikiException + { + if (isVersionPageEnabled(extensionDocument)) { + return xcontext.getWiki() + .getDocument( + new PageReference(extensionVersion, new PageReference( + XWikiRepositoryModel.EXTENSIONVERSIONS_SPACENAME, extensionDocument.getPageReference())), + xcontext); + } + + return extensionDocument; + } + + /** + * Retrieve the xobject of the version for given extension. + * + * @param extensionVersionDocument the document that might contain the version object + * @param extensionVersion the version for which to retrieve the object + * @param create {@code true} to create the object if it doesn't exist + * @param xcontext the current context + * @return the version object or {@code null} if it doesn't exist and shouldn't be created + * @throws XWikiException in case of problem for loading the document + * @since 17.9.0RC1 + */ + public BaseObject getExtensionVersionObject(XWikiDocument extensionVersionDocument, Extension extensionVersion, + boolean create, XWikiContext xcontext) throws XWikiException + { + // Update version object + BaseObject versionObject = + getExtensionVersionObject(extensionVersionDocument, extensionVersion.getId().getVersion()); + if (versionObject == null && create) { + versionObject = + extensionVersionDocument.newXObject(XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE, xcontext); + + versionObject.set(XWikiRepositoryModel.PROP_VERSION_VERSION, + extensionVersion.getId().getVersion().getValue(), xcontext); + } + + return versionObject; + } + + /** + * Retrieve the xobject of the version from the given document. + * + * @param document the document where to retrieve the version object. + * @param version the version for which to retrieve the object + * @return the version object or {@code null} if it doesn't exist and shouldn't be created + * @since 17.9.0RC1 + */ + public BaseObject getExtensionVersionObject(XWikiDocument document, String version) + { + return getExtensionVersionObject(document, new DefaultVersion(version)); + } + + /** + * Retrieve the xobject of the version from the given document. + * + * @param document the document where to retrieve the version object. + * @param version the version for which to retrieve the object + * @return the version object or {@code null} if it doesn't exist and shouldn't be created + * @since 17.9.0RC1 + */ + public BaseObject getExtensionVersionObject(XWikiDocument document, Version version) + { + List objects = document.getXObjects(XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE); + if (objects != null) { + for (BaseObject versionObject : objects) { + if (versionObject != null) { + String versionString = + getValue(versionObject, XWikiRepositoryModel.PROP_VERSION_VERSION, (String) null); + + if (StringUtils.isNotEmpty(versionString) && version.equals(new DefaultVersion(versionString))) { + return versionObject; + } + } + } + } + + return null; + } + + /** + * @param extensionDocument the document containing the extension metadata + * @return true if the passed extension document indicate that version should be proxied + * @since 17.9.0RC1 + */ + public boolean isVersionProxyingEnabled(XWikiDocument extensionDocument) + { + BaseObject extensionProxyObject = + extensionDocument.getXObject(XWikiRepositoryModel.EXTENSIONPROXY_CLASSREFERENCE); + if (extensionProxyObject == null) { + return false; + } + + return XWikiRepositoryModel.PROP_PROXY_PROXYLEVEL_VALUE_VERSION + .equals(getValue(extensionProxyObject, XWikiRepositoryModel.PROP_PROXY_PROXYLEVEL, (String) null)); + } + + /** + * @param extensionId the identifier of the extension + * @return the main document holder the extension metadata, or null if none count be found + * @throws QueryException when failing to search for the extension document + * @throws XWikiException when failing to get the extension document + * @since 17.9.0RC1 + */ + public XWikiDocument getExistingExtensionDocumentById(String extensionId) throws QueryException, XWikiException + { + XWikiContext xcontext = this.xcontextProvider.get(); + + DocumentReference[] cachedDocumentReference = this.documentReferenceCache.get(extensionId); + + if (cachedDocumentReference == null) { + Query query = this.queryManager.createQuery( + "select doc.fullName from Document doc, doc.object(" + XWikiRepositoryModel.EXTENSION_CLASSNAME + + ") as extension where extension." + XWikiRepositoryModel.PROP_EXTENSION_ID + " = :extensionId", + Query.XWQL); + + query.bindValue("extensionId", extensionId); + + List documentNames = query.execute(); + + if (!documentNames.isEmpty()) { + cachedDocumentReference = + new DocumentReference[] {this.currentStringResolver.resolve(documentNames.get(0))}; + } else { + cachedDocumentReference = new DocumentReference[1]; + } + + this.documentReferenceCache.set(extensionId, cachedDocumentReference); + } + + return cachedDocumentReference[0] != null ? xcontext.getWiki().getDocument(cachedDocumentReference[0], xcontext) + : null; + } + + /** + * @param extensionId the identifier of the extension + * @return the latest version of the extension + * @throws QueryException when failing to search for the extension latest version + * @since 17.9.0RC1 + */ + public String getLastVersion(String extensionId) throws QueryException + { + Query query = + this.queryManager.createQuery("select version.version, version.index from Document doc, doc.object(" + + XWikiRepositoryModel.EXTENSIONVERSION_CLASSNAME + + ") as version where version.id = :versionId and version.index is not null " + + "order by version.index desc", Query.XWQL); + + query.bindValue("versionId", extensionId); + query.setLimit(1); + + List results = query.execute(); + + if (results.isEmpty()) { + return null; + } + + return (String) results.get(0)[0]; + } + + /** + * @param extensionDocument the document holder the extension metadata + * @return the object holding the main extension metadata + * @since 17.9.0RC1 + */ + public BaseObject getExtensionObject(XWikiDocument extensionDocument) + { + return extensionDocument.getXObject(XWikiRepositoryModel.EXTENSION_CLASSREFERENCE); + } + + /** + * @param extensionId the identifier of the extension + * @return true if extension version should be stored in dedicated pages + * @throws QueryException when failing to search for the extension page + * @throws XWikiException when failing to get the extension page + * @since 17.9.0RC1 + */ + public boolean isVersionPageEnabled(String extensionId) throws QueryException, XWikiException + { + XWikiDocument document = getExistingExtensionDocumentById(extensionId); + + return isVersionPageEnabled(document); + } + + /** + * @param extensionDocument the main page holding extension metadata + * @return true if extension version should be stored in dedicated pages + * @since 17.9.0RC1 + */ + public boolean isVersionPageEnabled(XWikiDocument extensionDocument) + { + BaseObject extensionObject = getExtensionObject(extensionDocument); + + return isVersionPageEnabled(extensionObject); + } + + /** + * @param extensionOject the main object holding extension metadata + * @return true if extension version should be stored in dedicated pages + * @since 17.9.0RC1 + */ + public boolean isVersionPageEnabled(BaseObject extensionOject) + { + return getBooleanValue(extensionOject, XWikiRepositoryModel.PROP_EXTENSION_VERSIONPAGE, false); + } + + /** + * @param extensionOject true if extension version should be stored in dedicated pages + * @return true if the object has been modified, false otherwise + * @since 17.9.0RC1 + */ + public boolean setVersionPageEnabled(BaseObject extensionOject) + { + if (!isVersionPageEnabled(extensionOject)) { + extensionOject.setIntValue(XWikiRepositoryModel.PROP_EXTENSION_VERSIONPAGE, 1); + + return true; + } + + return false; + } + + /** + * @param document the document holding the extension metadata + * @return the identifier of the extension + * @since 17.9.0RC1 + */ + public String getExtensionId(XWikiDocument document) + { + return document.getStringValue(XWikiRepositoryModel.PROP_EXTENSION_ID); + } + + /** + * @param document the document holding the extension metadata + * @return the name of the extension + * @since 17.9.0RC1 + */ + public String getExtensionName(XWikiDocument document) + { + return document.getStringValue(XWikiRepositoryModel.PROP_EXTENSION_NAME); + } + + /** + * @param document the document holding the extension metadata + * @return the type of the extension + * @since 17.9.0RC1 + */ + public String getExtensionType(XWikiDocument document) + { + return document.getStringValue(XWikiRepositoryModel.PROP_EXTENSION_TYPE); + } } diff --git a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/ExtensionUpdaterListener.java b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/ExtensionUpdaterListener.java index b99d4228c028..7a997a467f36 100644 --- a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/ExtensionUpdaterListener.java +++ b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/ExtensionUpdaterListener.java @@ -34,7 +34,6 @@ import org.xwiki.observation.EventListener; import org.xwiki.observation.event.Event; -import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.objects.BaseObject; @@ -46,8 +45,8 @@ public class ExtensionUpdaterListener implements EventListener /** * Listened events. */ - private static final List EVENTS = Arrays. asList(new DocumentCreatingEvent(), - new DocumentUpdatingEvent()); + private static final List EVENTS = + Arrays.asList(new DocumentCreatingEvent(), new DocumentUpdatingEvent()); /** * The logger to log. @@ -80,7 +79,7 @@ public void onEvent(Event event, Object source, Object data) if (extensionObject != null) { try { this.repositoryManagerProvider.get().validateExtension(document, false); - } catch (XWikiException e) { + } catch (Exception e) { this.logger.error("Failed to validate extension in document [{}]", document.getDocumentReference(), e); } } diff --git a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/RepositoryManager.java b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/RepositoryManager.java index 5c110720f1a6..432fa947245f 100644 --- a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/RepositoryManager.java +++ b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/RepositoryManager.java @@ -45,16 +45,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Strings; import org.slf4j.Logger; -import org.xwiki.cache.Cache; -import org.xwiki.cache.CacheException; -import org.xwiki.cache.CacheManager; -import org.xwiki.cache.config.CacheConfiguration; -import org.xwiki.cache.eviction.LRUEvictionConfiguration; import org.xwiki.component.annotation.Component; -import org.xwiki.component.manager.ComponentLifecycleException; -import org.xwiki.component.phase.Disposable; -import org.xwiki.component.phase.Initializable; -import org.xwiki.component.phase.InitializationException; import org.xwiki.extension.DefaultExtensionDependency; import org.xwiki.extension.Extension; import org.xwiki.extension.ExtensionAuthor; @@ -84,10 +75,8 @@ import org.xwiki.model.reference.DocumentReferenceResolver; import org.xwiki.model.reference.EntityReference; import org.xwiki.model.reference.EntityReferenceSerializer; +import org.xwiki.model.reference.PageReference; import org.xwiki.model.reference.WikiReference; -import org.xwiki.observation.EventListener; -import org.xwiki.observation.ObservationManager; -import org.xwiki.observation.event.Event; import org.xwiki.query.Query; import org.xwiki.query.QueryException; import org.xwiki.query.QueryManager; @@ -96,35 +85,27 @@ import org.xwiki.rendering.listener.reference.ResourceType; import org.xwiki.rendering.parser.ResourceReferenceParser; import org.xwiki.rendering.renderer.reference.ResourceReferenceTypeSerializer; +import org.xwiki.rendering.syntax.Syntax; import org.xwiki.repository.internal.reference.ExtensionResourceReference; +import com.xpn.xwiki.XWiki; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiAttachment; import com.xpn.xwiki.doc.XWikiDocument; -import com.xpn.xwiki.internal.event.XObjectPropertyAddedEvent; -import com.xpn.xwiki.internal.event.XObjectPropertyDeletedEvent; -import com.xpn.xwiki.internal.event.XObjectPropertyUpdatedEvent; import com.xpn.xwiki.objects.BaseObject; import com.xpn.xwiki.objects.StringProperty; +/** + * Expose various tools to manipulate extensions in the wiki. + * + * @version $Id$ + */ @Component(roles = RepositoryManager.class) @Singleton -public class RepositoryManager implements Initializable, Disposable +@SuppressWarnings("checkstyle:ClassFanOutComplexity") +public class RepositoryManager { - /** - * The reference to match property {@link XWikiRepositoryModel#PROP_EXTENSION_ID} of class - * {@link XWikiRepositoryModel#EXTENSION_CLASSNAME} on whatever wiki. - */ - private static final EntityReference EXTENSIONID_PROPERTY_REFERENCE = - new EntityReference(XWikiRepositoryModel.PROP_EXTENSION_ID, EntityType.OBJECT_PROPERTY, - XWikiRepositoryModel.EXTENSION_OBJECTREFERENCE); - - private static final List EVENTS = - Arrays.asList(new XObjectPropertyAddedEvent(EXTENSIONID_PROPERTY_REFERENCE), - new XObjectPropertyDeletedEvent(EXTENSIONID_PROPERTY_REFERENCE), - new XObjectPropertyUpdatedEvent(EXTENSIONID_PROPERTY_REFERENCE)); - private static final Pattern PATTERN_NEWLINE = Pattern.compile("[\n\r]"); /** @@ -173,17 +154,11 @@ public class RepositoryManager implements Initializable, Disposable @Inject private RepositoryConfiguration configuration; - @Inject - private CacheManager cacheManager; - - @Inject - private ObservationManager observation; - @Inject private ExtensionFactory extensionFactory; @Inject - protected ExtensionStore extensionStore; + private ExtensionStore extensionStore; @Inject private Logger logger; @@ -192,117 +167,7 @@ public class RepositoryManager implements Initializable, Disposable private int maxStringPropertySize = -1; - /** - * Link extension id to document reference. The tabe contains null if the id link to no extension. - */ - private Cache documentReferenceCache; - - private EventListener listener = new EventListener() - { - @Override - public void onEvent(Event event, Object source, Object data) - { - // TODO: Improve a bit by removing only what's changed - documentReferenceCache.removeAll(); - } - - @Override - public String getName() - { - return "repository.DefaultRepositoryManager"; - } - - @Override - public List getEvents() - { - return EVENTS; - } - }; - - @Override - public void initialize() throws InitializationException - { - // Init cache - CacheConfiguration cacheConfiguration = new CacheConfiguration(); - cacheConfiguration.setConfigurationId("repository.extensionid.documentreference"); - LRUEvictionConfiguration lru = new LRUEvictionConfiguration(); - lru.setMaxEntries(10000); - cacheConfiguration.put(LRUEvictionConfiguration.CONFIGURATIONID, lru); - - try { - this.documentReferenceCache = this.cacheManager.createNewCache(cacheConfiguration); - } catch (CacheException e) { - throw new InitializationException("Failed to initialize cache", e); - } - - // Listen to modifications - this.observation.addListener(listener, EventListener.CACHE_INVALIDATION_DEFAULT_PRIORITY); - } - - @Override - public void dispose() throws ComponentLifecycleException - { - this.observation.removeListener(listener.getName()); - this.documentReferenceCache.dispose(); - } - - public XWikiDocument getDocument(String fullName) throws XWikiException - { - XWikiContext xcontext = this.xcontextProvider.get(); - - return xcontext.getWiki().getDocument(this.currentStringResolver.resolve(fullName), xcontext); - } - - public XWikiDocument getExistingExtensionDocumentById(String extensionId) throws QueryException, XWikiException - { - XWikiContext xcontext = this.xcontextProvider.get(); - - DocumentReference[] cachedDocumentReference = this.documentReferenceCache.get(extensionId); - - if (cachedDocumentReference == null) { - Query query = this.queryManager.createQuery( - "select doc.fullName from Document doc, doc.object(" + XWikiRepositoryModel.EXTENSION_CLASSNAME - + ") as extension where extension." + XWikiRepositoryModel.PROP_EXTENSION_ID + " = :extensionId", - Query.XWQL); - - query.bindValue("extensionId", extensionId); - - List documentNames = query.execute(); - - if (!documentNames.isEmpty()) { - cachedDocumentReference = - new DocumentReference[] {this.currentStringResolver.resolve(documentNames.get(0))}; - } else { - cachedDocumentReference = new DocumentReference[1]; - } - - this.documentReferenceCache.set(extensionId, cachedDocumentReference); - } - - return cachedDocumentReference[0] != null ? xcontext.getWiki().getDocument(cachedDocumentReference[0], xcontext) - : null; - } - - public BaseObject getExtensionVersion(XWikiDocument document, Version version) - { - List objects = document.getXObjects(XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE); - if (objects != null) { - for (BaseObject versionObject : objects) { - if (versionObject != null) { - String versionString = this.extensionStore.getValue(versionObject, - XWikiRepositoryModel.PROP_VERSION_VERSION, (String) null); - - if (StringUtils.isNotEmpty(versionString) && version.equals(new DefaultVersion(versionString))) { - return versionObject; - } - } - } - } - - return null; - } - - public void validateExtension(XWikiDocument document, boolean save) throws XWikiException + public void validateExtension(XWikiDocument document, boolean save) throws XWikiException, QueryException { BaseObject extensionObject = document.getXObject(XWikiRepositoryModel.EXTENSION_CLASSREFERENCE); @@ -360,7 +225,6 @@ public void validateExtension(XWikiDocument document, boolean save) throws XWiki } // Save document - if (save && needSave) { xcontext.getWiki().saveDocument(document, "Validated extension", true, xcontext); } @@ -371,43 +235,43 @@ public void validateExtension(XWikiDocument document, boolean save) throws XWiki * * @param document the extension document * @return the last version + * @throws QueryException when failing to resolve the last version */ - private String findLastVersion(XWikiDocument document) + private String findLastVersion(XWikiDocument document) throws QueryException { - String extensionId = getExtensionId(document); - - DocumentReference versionClassReference = - getClassReference(document, XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE); - - List versionObjects = document.getXObjects(versionClassReference); + String extensionId = this.extensionStore.getExtensionId(document); - DefaultVersion lastVersion = null; - if (versionObjects != null) { - for (BaseObject versionObject : versionObjects) { - if (versionObject != null) { - String versionId = - this.extensionStore.getValue(versionObject, XWikiRepositoryModel.PROP_VERSION_ID); - - if (StringUtils.isEmpty(versionId) || Objects.equals(extensionId, versionId)) { - String versionString = - this.extensionStore.getValue(versionObject, XWikiRepositoryModel.PROP_VERSION_VERSION); - if (versionString != null) { - DefaultVersion version = new DefaultVersion(versionString); - if (lastVersion == null || version.compareTo(lastVersion) > 0) { - lastVersion = version; + if (this.extensionStore.isVersionPageEnabled(document)) { + return this.extensionStore.getLastVersion(extensionId); + } else { + DocumentReference versionClassReference = + getClassReference(document, XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE); + + List versionObjects = document.getXObjects(versionClassReference); + + DefaultVersion lastVersion = null; + if (versionObjects != null) { + for (BaseObject versionObject : versionObjects) { + if (versionObject != null) { + String versionId = + this.extensionStore.getValue(versionObject, XWikiRepositoryModel.PROP_VERSION_ID); + + if (StringUtils.isEmpty(versionId) || Objects.equals(extensionId, versionId)) { + String versionString = + this.extensionStore.getValue(versionObject, XWikiRepositoryModel.PROP_VERSION_VERSION); + if (versionString != null) { + DefaultVersion version = new DefaultVersion(versionString); + if (lastVersion == null || version.compareTo(lastVersion) > 0) { + lastVersion = version; + } } } } } } - } - return lastVersion != null ? lastVersion.getValue() : null; - } - - private String getExtensionId(XWikiDocument document) - { - return document.getStringValue(XWikiRepositoryModel.PROP_EXTENSION_ID); + return lastVersion != null ? lastVersion.getValue() : null; + } } private DocumentReference getClassReference(XWikiDocument document, EntityReference localReference) @@ -416,13 +280,13 @@ private DocumentReference getClassReference(XWikiDocument document, EntityRefere } /** - * @param document the extension document + * @param extensionDocument the extension document * @param extensionObject the extension object - * @param context the XWiki context + * @param xcontext the XWiki context * @return true if the extension is valid from Extension Manager point of view * @throws XWikiException unknown issue when manipulating the model */ - private boolean isValid(XWikiDocument document, BaseObject extensionObject, XWikiContext context) + private boolean isValid(XWikiDocument extensionDocument, BaseObject extensionObject, XWikiContext xcontext) throws XWikiException { String extensionId = this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_ID); @@ -434,14 +298,15 @@ private boolean isValid(XWikiDocument document, BaseObject extensionObject, XWik valid = this.configuration.isValidType(type); if (valid) { - // Versions valid = false; + + // Legacy Versions List extensionVersions = - document.getXObjects(XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE); + extensionDocument.getXObjects(XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE); if (extensionVersions != null) { for (BaseObject extensionVersionObject : extensionVersions) { if (extensionVersionObject != null) { - valid = isVersionValid(document, type, extensionVersionObject, context); + valid = isVersionValid(extensionDocument, type, extensionVersionObject, xcontext); if (!valid) { return false; @@ -449,6 +314,20 @@ private boolean isValid(XWikiDocument document, BaseObject extensionObject, XWik } } } + + // New version storage + if (this.extensionStore.isVersionPageEnabled(extensionObject)) { + // Current version + String lastVersion = + extensionDocument.getStringValue(XWikiRepositoryModel.PROP_EXTENSION_LASTVERSION); + if (StringUtils.isNotBlank(lastVersion)) { + valid = isVersionValid(extensionDocument, type, lastVersion, xcontext); + + if (!valid) { + return false; + } + } + } } } @@ -456,11 +335,13 @@ private boolean isValid(XWikiDocument document, BaseObject extensionObject, XWik } private boolean isVersionValid(XWikiDocument document, String type, BaseObject extensionVersionObject, - XWikiContext context) + XWikiContext xcontext) { // Has a version String extensionVersion = this.extensionStore.getValue(extensionVersionObject, XWikiRepositoryModel.PROP_VERSION_VERSION); + + // Has a version if (StringUtils.isBlank(extensionVersion)) { this.logger.debug("No actual version provided for object [{}({})]", XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE, extensionVersionObject.getNumber()); @@ -468,6 +349,19 @@ private boolean isVersionValid(XWikiDocument document, String type, BaseObject e return false; } + return isVersionValid(document, type, extensionVersion, xcontext); + } + + private boolean isVersionValid(XWikiDocument document, String type, String extensionVersion, XWikiContext xcontext) + { + // Has a version + if (StringUtils.isBlank(extensionVersion)) { + this.logger.debug("No actual version provided for document [{}({})]", + XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE, document.getDocumentReference()); + + return false; + } + boolean valid; if (StringUtils.isEmpty(type)) { @@ -477,9 +371,9 @@ private boolean isVersionValid(XWikiDocument document, String type, BaseObject e ResourceReference resourceReference = null; try { resourceReference = getDownloadReference(document, extensionVersion); - } catch (ResolveException e) { - logger.debug("Cannot obtain download source reference for object [{}({})]", - XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE, extensionVersionObject.getNumber()); + } catch (Exception e) { + logger.debug("Cannot obtain download source reference for version [({})]", extensionVersion); + return false; } @@ -494,7 +388,7 @@ private boolean isVersionValid(XWikiDocument document, String type, BaseObject e attachmentDocument = document; } else { attachmentDocument = - context.getWiki().getDocument(attachmentReference.getDocumentReference(), context); + xcontext.getWiki().getDocument(attachmentReference.getDocumentReference(), xcontext); } valid = attachmentDocument.getAttachment(attachmentReference.getName()) != null; @@ -518,8 +412,7 @@ private boolean isVersionValid(XWikiDocument document, String type, BaseObject e } else { valid = false; - this.logger.debug("No actual download provided for object [{}({})]", - XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE, extensionVersionObject.getNumber()); + this.logger.debug("No actual download provided for version [({})]", extensionVersion); } } @@ -536,22 +429,31 @@ public void validateExtensions() throws QueryException, XWikiException } } + private XWikiDocument getDocument(String fullName) throws XWikiException + { + XWikiContext xcontext = this.xcontextProvider.get(); + + return xcontext.getWiki().getDocument(this.currentStringResolver.resolve(fullName), xcontext); + } + /** - * @since 9.5RC1 + * @param document the document holding the extension metadata + * @param extensionVersion the version for which to return the download reference + * @return the download reference + * @throws ResolveException when failing to resolve the download reference + * @throws XWikiException when failing to resolve the download reference */ public ResourceReference getDownloadReference(XWikiDocument document, String extensionVersion) - throws ResolveException + throws ResolveException, XWikiException { - String downloadURL = null; - BaseObject extensionVersionObject = getExtensionVersionObject(document, extensionVersion, false); // this - // 'false' is - // important + BaseObject extensionVersionObject = + getExtensionVersionObject(document, extensionVersion, false, this.xcontextProvider.get()); if (extensionVersionObject != null) { downloadURL = this.extensionStore.getValue(extensionVersionObject, XWikiRepositoryModel.PROP_VERSION_DOWNLOAD); - } else if (isVersionProxyingEnabled(document)) { + } else if (this.extensionStore.isVersionProxyingEnabled(document)) { downloadURL = resolveExtensionDownloadURL(document, extensionVersion); } @@ -642,30 +544,33 @@ public DocumentReference importExtension(String extensionId, ExtensionRepository boolean needSave = false; - XWikiDocument document = getExistingExtensionDocumentById(extensionId); + XWikiDocument extensionDocument = this.extensionStore.getExistingExtensionDocumentById(extensionId); - if (document == null) { + if (extensionDocument == null) { // Create document - document = xcontext.getWiki().getDocument( + extensionDocument = xcontext.getWiki().getDocument( new DocumentReference(xcontext.getWikiId(), Arrays.asList("Extension", extension.getName()), "WebHome"), xcontext); - for (int i = 1; !document.isNew(); ++i) { - document = xcontext.getWiki().getDocument(new DocumentReference(xcontext.getWikiId(), + for (int i = 1; !extensionDocument.isNew(); ++i) { + extensionDocument = xcontext.getWiki().getDocument(new DocumentReference(xcontext.getWikiId(), Arrays.asList("Extension", extension.getName() + ' ' + i), "WebHome"), xcontext); } - document.readFromTemplate(this.currentResolver.resolve(XWikiRepositoryModel.EXTENSION_TEMPLATEREFERENCE), - xcontext); + extensionDocument.readFromTemplate( + this.currentResolver.resolve(XWikiRepositoryModel.EXTENSION_TEMPLATEREFERENCE), xcontext); needSave = true; + } else { + // Avoid modifying the cached document + extensionDocument = extensionDocument.clone(); } // Update document - BaseObject extensionObject = document.getXObject(XWikiRepositoryModel.EXTENSION_CLASSREFERENCE); + BaseObject extensionObject = this.extensionStore.getExtensionObject(extensionDocument); if (extensionObject == null) { - extensionObject = document.newXObject(XWikiRepositoryModel.EXTENSION_CLASSREFERENCE, xcontext); + extensionObject = extensionDocument.newXObject(XWikiRepositoryModel.EXTENSION_CLASSREFERENCE, xcontext); needSave = true; } @@ -675,9 +580,14 @@ public DocumentReference importExtension(String extensionId, ExtensionRepository needSave = true; } - // Update extension informations + // Make sure we use the dedicated version page mode + needSave |= this.extensionStore.setVersionPageEnabled(extensionObject); + + // Update main extension page needSave |= updateExtension(extension, extensionObject, xcontext); + needSave |= updateExtensionMain(extension, extensionObject, xcontext); + needSave |= updateExtensionVersionDependencies(extension, extensionDocument); // Get former ids versions TreeMap featureVersions = new TreeMap<>(); @@ -708,9 +618,11 @@ public DocumentReference importExtension(String extensionId, ExtensionRepository // Proxy marker - BaseObject extensionProxyObject = document.getXObject(XWikiRepositoryModel.EXTENSIONPROXY_CLASSREFERENCE); + BaseObject extensionProxyObject = + extensionDocument.getXObject(XWikiRepositoryModel.EXTENSIONPROXY_CLASSREFERENCE); if (extensionProxyObject == null) { - extensionProxyObject = document.newXObject(XWikiRepositoryModel.EXTENSIONPROXY_CLASSREFERENCE, xcontext); + extensionProxyObject = + extensionDocument.newXObject(XWikiRepositoryModel.EXTENSIONPROXY_CLASSREFERENCE, xcontext); extensionProxyObject.setIntValue(XWikiRepositoryModel.PROP_PROXY_AUTOUPDATE, 1); needSave = true; } @@ -722,29 +634,44 @@ public DocumentReference importExtension(String extensionId, ExtensionRepository needSave |= update(extensionProxyObject, XWikiRepositoryModel.PROP_PROXY_REPOSITORYURI, repository.getDescriptor().getURI().toString()); - // Remove unexisting versions - Set validVersions = new HashSet<>(); - List versionObjects = document.getXObjects(XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE); + // Check the versions storage and proxy mode + boolean versionPageEnabled = this.extensionStore.isVersionPageEnabled(extensionObject); + boolean versionProxyEnabled = this.extensionStore.isVersionProxyingEnabled(extensionDocument); + + // Remove unexisting versions + List versionObjects = + extensionDocument.getXObjects(XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE); if (versionObjects != null) { for (BaseObject versionObject : versionObjects) { if (versionObject != null) { String version = this.extensionStore.getValue(versionObject, XWikiRepositoryModel.PROP_VERSION_VERSION); - if (StringUtils.isBlank(version) || (isVersionProxyingEnabled(document) - && !new DefaultVersion(version).equals(extension.getId().getVersion()))) { - // Empty version OR old versions should be proxied - document.removeXObject(versionObject); + // Remove the object if: + // * versions should be stored in dedicated pages + // * the version is blank + // * versions should be proxied + if (versionPageEnabled || StringUtils.isBlank(version) + || (versionProxyEnabled + && !new DefaultVersion(version).equals(extension.getId().getVersion()))) { + extensionDocument.removeXObject(versionObject); needSave = true; + + // When moving from legacy storage to dedicated version page storage, we need to migrate the + // object and not just deleted it + if (versionPageEnabled) { + moveLegacyVersion(extensionDocument, versionObject); + } } else { if (!extensionVersions.containsKey(new DefaultVersion(version)) && featureVersions.containsKey(new DefaultVersion(version))) { + // Version are stored on dedicated pages // The version does not exist on remote repository - if (!isVersionValid(document, extension.getType(), versionObject, xcontext)) { + if (!isVersionValid(extensionDocument, extension.getType(), versionObject, xcontext)) { // The version is invalid, removing it to not make the whole extension invalid - document.removeXObject(versionObject); + extensionDocument.removeXObject(versionObject); needSave = true; } else { // The version is valid, lets keep it @@ -758,8 +685,13 @@ public DocumentReference importExtension(String extensionId, ExtensionRepository } } } + + // Current version is valid + validVersions.add(extension.getId().getVersion().getValue()); + + // Cleanup dependencies not associated to valid versions List dependencyObjects = - document.getXObjects(XWikiRepositoryModel.EXTENSIONDEPENDENCY_CLASSREFERENCE); + extensionDocument.getXObjects(XWikiRepositoryModel.EXTENSIONDEPENDENCY_CLASSREFERENCE); if (dependencyObjects != null) { for (BaseObject dependencyObject : dependencyObjects) { if (dependencyObject != null) { @@ -768,66 +700,103 @@ public DocumentReference importExtension(String extensionId, ExtensionRepository if (!validVersions.contains(version)) { // The version is invalid, removing it to not make the whole extension invalid - document.removeXObject(dependencyObject); + extensionDocument.removeXObject(dependencyObject); needSave = true; } } } } - // Update features versions + // Remove version pages corresponding to not existing versions + cleanNotExistingVersionPages(extensionDocument, extension.getId().getVersion(), extensionVersions, + featureVersions, versionProxyEnabled, xcontext); + // Update features versions + long index = 0; for (Map.Entry entry : featureVersions.entrySet()) { Version version = entry.getKey(); String id = entry.getValue(); // Give priority to extension version in case of conflict if (!extensionVersions.containsKey(version)) { - updateVersion(id, version, extension, repository, document); + updateVersion(id, version, extension, repository, index++, extensionDocument); } } // Update extension versions - for (Map.Entry entry : extensionVersions.entrySet()) { Version version = entry.getKey(); String id = entry.getValue(); - updateVersion(id, version, extension, repository, document); + updateVersion(id, version, extension, repository, index++, extensionDocument); } // Save if (needSave) { - document.setAuthorReference(xcontext.getUserReference()); - if (document.isNew()) { - document.setContentAuthorReference(xcontext.getUserReference()); - document.setCreatorReference(xcontext.getUserReference()); + saveDocument(extensionDocument, + "Imported extension [" + extensionId + "] from repository [" + repository.getDescriptor() + "]", + xcontext); + } + + return extensionDocument.getDocumentReference(); + } + + private void cleanNotExistingVersionPages(XWikiDocument extensionDocument, Version currentVersion, + TreeMap extensionVersions, TreeMap featureVersions, + boolean versionProxyEnabled, XWikiContext xcontext) throws QueryException, XWikiException + { + EntityReference versionsSpaceReference = new EntityReference(XWikiRepositoryModel.EXTENSIONVERSIONS_SPACENAME, + EntityType.SPACE, extensionDocument.getDocumentReference().getLocalDocumentReference().getParent()); + Query query = this.queryManager.createQuery( + "select doc.fullName, version.version from Document doc, doc.object(" + + XWikiRepositoryModel.EXTENSIONVERSION_CLASSNAME + ") version where doc.space like :space", + Query.XWQL); + query.bindValue("space", this.entityReferenceSerializer.serialize(versionsSpaceReference) + ".%"); + List results = query.execute(); + + XWiki xwiki = xcontext.getWiki(); + for (Object[] result : results) { + Version version = new DefaultVersion((String) result[1]); + + // Remove the document if: + // * the versions does not exist + // * versions should be proxied but it's not the current one + if (versionProxyEnabled || (!currentVersion.equals(version) && !extensionVersions.containsKey(version) + && !featureVersions.containsKey(version))) { + DocumentReference documentReference = this.currentStringResolver.resolve((String) result[0]); + XWikiDocument document = xwiki.getDocument(documentReference, xcontext); + xwiki.deleteDocument(document, xcontext); } + } + } - xcontext.getWiki().saveDocument(document, - "Imported extension [" + extensionId + "] from repository [" + repository.getDescriptor() + "]", true, - xcontext); + private void saveDocument(XWikiDocument document, String comment, XWikiContext xcontext) throws XWikiException + { + document.setAuthorReference(xcontext.getUserReference()); + if (document.isNew()) { + document.setContentAuthorReference(xcontext.getUserReference()); + document.setCreatorReference(xcontext.getUserReference()); } - return document.getDocumentReference(); + xcontext.getWiki().saveDocument(document, comment, xcontext); } private boolean updateVersion(String id, Version version, Extension extension, ExtensionRepository repository, - XWikiDocument document) + long index, XWikiDocument extensionDocument) { try { Extension versionExtension; if (version.equals(extension.getId().getVersion())) { versionExtension = extension; - } else if (isVersionProxyingEnabled(document)) { + } else if (this.extensionStore.isVersionProxyingEnabled(extensionDocument)) { return false; } else { versionExtension = repository.resolve(new ExtensionId(id, version)); } // Update version related informations - return updateExtensionVersion(document, versionExtension); + return updateExtensionVersion(versionExtension, extensionDocument, index, true); } catch (Exception e) { this.logger.error("Failed to resolve extension with id [" + id + "] and version [" + version + "] on repository [" + repository + "]", e); @@ -837,17 +806,107 @@ private boolean updateVersion(String id, Version version, Extension extension, E } /** - * @since 9.5RC1 + * @param extensionDocument the document holder the main extension metadata + * @param version the version for which to return the object + * @param xcontext the XWiki Context + * @return the object holding the extension version metadata, or null if none could be found + * @throws XWikiException when failing to get the extension version object + * @since 17.9.0RC1 */ - public boolean isVersionProxyingEnabled(XWikiDocument extensionDocument) + public BaseObject getExtensionVersionObject(XWikiDocument extensionDocument, String version, XWikiContext xcontext) + throws XWikiException { - BaseObject extensionProxyObject = - extensionDocument.getXObject(XWikiRepositoryModel.EXTENSIONPROXY_CLASSREFERENCE); - if (extensionProxyObject == null) { - return false; + return getExtensionVersionObject(extensionDocument, version, true, xcontext); + } + + /** + * @param extensionDocument the document holder the main extension metadata + * @param version the version for which to return the object + * @param allowProxying true if the method to follow the proxy when the version cannot be found locally + * @param xcontext the XWiki Context + * @return the object holding the extension version metadata, or null if none could be found + * @throws XWikiException when failing to get the extension version object + * @since 17.9.0RC1 + */ + public BaseObject getExtensionVersionObject(XWikiDocument extensionDocument, String version, boolean allowProxying, + XWikiContext xcontext) throws XWikiException + { + XWikiDocument extensionVersionDocument = + this.extensionStore.getExtensionVersionDocument(extensionDocument, version, xcontext); + + if (version == null) { + List objects = + extensionVersionDocument.getXObjects(XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE); + + if (objects == null || objects.isEmpty()) { + return null; + } else { + return objects.get(objects.size() - 1); + } } - return XWikiRepositoryModel.PROP_PROXY_PROXYLEVEL_VALUE_VERSION.equals(this.extensionStore - .getValue(extensionProxyObject, XWikiRepositoryModel.PROP_PROXY_PROXYLEVEL, (String) null)); + + BaseObject extensionVersionObject = extensionVersionDocument + .getXObject(XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE, "version", version, false); + + if (extensionVersionObject == null && allowProxying + && this.extensionStore.isVersionProxyingEnabled(extensionDocument)) { + // No ExtensionVersionClass object for the version, but proxy is enabled, so try to find remotely + Extension extension = null; + try { + extension = resolveExtensionVersion(extensionDocument, version); + } catch (ExtensionNotFoundException e) { + this.logger.debug("No extension could be found remotely with version [{}] for extension page [{}]", + version, extensionDocument.getDocumentReference()); + } catch (ResolveException e) { + throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR); + } + + // No extension could be found for the provided version + if (extension == null) { + return null; + } + + // Create a "detached" xobject for that extension version + // FIXME: find a more elegant solution + try { + XWikiDocument extensionDocumentClone = extensionDocument.clone(); + updateExtensionVersion(extension, extensionDocumentClone, 0, xcontext); + extensionVersionObject = extensionDocumentClone + .getXObject(XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE, "version", version, false); + } catch (XWikiException e) { + throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR); + } + } + + return extensionVersionObject; + } + + private boolean updateExtensionMain(Extension extension, BaseObject extensionObject, XWikiContext xcontext) + throws XWikiException + { + boolean needSave = false; + + // Description + if (StringUtils.isEmpty(this.extensionStore.getValue(extensionObject, + XWikiRepositoryModel.PROP_EXTENSION_DESCRIPTION, (String) null))) { + extensionObject.set(XWikiRepositoryModel.PROP_EXTENSION_DESCRIPTION, getDescription(extension), xcontext); + needSave = true; + } + + // Issue Management + ExtensionIssueManagement issueManagement = extension.getIssueManagement(); + if (issueManagement != null) { + if (issueManagement.getSystem() != null) { + needSave |= update(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_ISSUEMANAGEMENT_SYSTEM, + issueManagement.getSystem()); + } + if (issueManagement.getURL() != null) { + needSave |= update(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_ISSUEMANAGEMENT_URL, + issueManagement.getURL()); + } + } + + return needSave; } private boolean updateExtension(Extension extension, BaseObject extensionObject, XWikiContext xcontext) @@ -878,13 +937,6 @@ private boolean updateExtension(Extension extension, BaseObject extensionObject, * update(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_WEBSITE, extension.getWebSite()); */ - // Description - if (StringUtils.isEmpty(this.extensionStore.getValue(extensionObject, - XWikiRepositoryModel.PROP_EXTENSION_DESCRIPTION, (String) null))) { - extensionObject.set(XWikiRepositoryModel.PROP_EXTENSION_DESCRIPTION, getDescription(extension), xcontext); - needSave = true; - } - // License if (!extension.getLicenses().isEmpty() && !Strings.CS.equals(extension.getLicenses().iterator().next().getName(), this.extensionStore @@ -910,19 +962,6 @@ private boolean updateExtension(Extension extension, BaseObject extensionObject, } } - // Issue Management - ExtensionIssueManagement issueManagement = extension.getIssueManagement(); - if (issueManagement != null) { - if (issueManagement.getSystem() != null) { - needSave |= update(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_ISSUEMANAGEMENT_SYSTEM, - issueManagement.getSystem()); - } - if (issueManagement.getURL() != null) { - needSave |= update(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_ISSUEMANAGEMENT_URL, - issueManagement.getURL()); - } - } - // Authors needSave |= updateAuthors(extensionObject, extension.getAuthors()); @@ -1091,7 +1130,7 @@ private String resolveAuthorIdOnWiki(String wiki, String authorName, String[] au return null; } - private boolean updateExtensionVersionDependencies(XWikiDocument document, Extension extension) + private boolean updateExtensionVersionDependencies(Extension extension, XWikiDocument document) throws XWikiException { boolean needSave = false; @@ -1189,6 +1228,27 @@ private boolean updateExtensionVersionDependencies(XWikiDocument document, Exten */ public Extension resolveExtensionVersion(XWikiDocument extensionDocument, String extensionVersion) throws ResolveException + { + BaseObject extensionObject = extensionDocument.getXObject(XWikiRepositoryModel.EXTENSION_CLASSREFERENCE); + if (extensionObject == null) { + return null; + } + String extensionId = + this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_ID, (String) null); + + if (extensionId == null) { + return null; + } + + ExtensionRepository repository = getExtensionRepository(extensionDocument); + if (isGivenVersionOneOfExtensionVersions(repository, extensionId, extensionVersion)) { + return repository.resolve(new ExtensionId(extensionId, extensionVersion)); + } else { + return tryToResolveExtensionFromExtensionFeatures(repository, extensionObject, extensionVersion); + } + } + + public ExtensionRepository getExtensionRepository(XWikiDocument extensionDocument) { BaseObject extensionObject = extensionDocument.getXObject(XWikiRepositoryModel.EXTENSION_CLASSREFERENCE); if (extensionObject == null) { @@ -1209,12 +1269,7 @@ public Extension resolveExtensionVersion(XWikiDocument extensionDocument, String return null; } - ExtensionRepository repository = this.extensionRepositoryManager.getRepository(repositoryId); - if (isGivenVersionOneOfExtensionVersions(repository, extensionId, extensionVersion)) { - return repository.resolve(new ExtensionId(extensionId, extensionVersion)); - } else { - return tryToResolveExtensionFromExtensionFeatures(repository, extensionObject, extensionVersion); - } + return this.extensionRepositoryManager.getRepository(repositoryId); } /** @@ -1225,7 +1280,8 @@ private Extension tryToResolveExtensionFromExtensionFeatures(ExtensionRepository { List features = (List) this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_FEATURES); - return features.stream().map(feature -> { + + return features == null ? null : features.stream().map(feature -> { try { String featureId = feature.split("/")[0]; return repository.resolve(new ExtensionId(featureId, extensionVersion)); @@ -1243,46 +1299,125 @@ private boolean isGivenVersionOneOfExtensionVersions(ExtensionRepository reposit .anyMatch(version -> version.getValue().equals(extensionVersion)); } - private boolean updateExtensionVersion(XWikiDocument document, Extension extension) throws XWikiException + private void moveLegacyVersion(XWikiDocument extensionDocument, BaseObject versionObject) throws XWikiException { - boolean needSave = false; - XWikiContext xcontext = this.xcontextProvider.get(); + // Resolve the version + String version = versionObject.getStringValue(XWikiRepositoryModel.PROP_VERSION_VERSION); + + // Resolve the version document + XWikiDocument extensionVersionDocument = this.extensionStore + .getExtensionVersionDocument(extensionDocument, new DefaultVersion(version), xcontext).clone(); + + extensionVersionDocument.addXObject(versionObject.clone()); + + // Save if dedicated version page + saveDocument(extensionVersionDocument, "Migrate the extension version", xcontext); + } + + private boolean updateExtensionVersion(Extension extensionVersion, XWikiDocument extensionVersionDocument, + long index, XWikiContext xcontext) throws XWikiException + { + boolean needSave = false; + // Update version object - BaseObject versionObject = getExtensionVersion(document, extension.getId().getVersion()); + BaseObject versionObject = this.extensionStore.getExtensionVersionObject(extensionVersionDocument, + extensionVersion.getId().getVersion()); if (versionObject == null) { - versionObject = document.newXObject(XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE, xcontext); + versionObject = + extensionVersionDocument.newXObject(XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE, xcontext); - versionObject.set(XWikiRepositoryModel.PROP_VERSION_VERSION, extension.getId().getVersion().getValue(), - xcontext); + versionObject.set(XWikiRepositoryModel.PROP_VERSION_VERSION, + extensionVersion.getId().getVersion().getValue(), xcontext); needSave = true; } // Id - needSave |= update(versionObject, XWikiRepositoryModel.PROP_VERSION_ID, extension.getId().getId()); + needSave |= update(versionObject, XWikiRepositoryModel.PROP_VERSION_ID, extensionVersion.getId().getId()); // Features - needSave |= - updateFeatures(XWikiRepositoryModel.PROP_VERSION_FEATURES, versionObject, extension.getExtensionFeatures()); + needSave |= updateFeatures(XWikiRepositoryModel.PROP_VERSION_FEATURES, versionObject, + extensionVersion.getExtensionFeatures()); // Repositories - List repositories = XWikiRepositoryModel.toStringList(extension.getRepositories()); + List repositories = XWikiRepositoryModel.toStringList(extensionVersion.getRepositories()); needSave |= update(versionObject, XWikiRepositoryModel.PROP_VERSION_REPOSITORIES, repositories); // Update dependencies - needSave |= updateExtensionVersionDependencies(document, extension); + needSave |= updateExtensionVersionDependencies(extensionVersion, extensionVersionDocument); // Download - if (!StringUtils.isEmpty(extension.getType())) { - String download = getDownloadURL(extension); + if (!StringUtils.isEmpty(extensionVersion.getType())) { + String download = getDownloadURL(extensionVersion); needSave |= update(versionObject, XWikiRepositoryModel.PROP_VERSION_DOWNLOAD, download); } + // Common properties + needSave |= updateExtension(extensionVersion, versionObject, xcontext); + + // index + needSave |= update(versionObject, XWikiRepositoryModel.PROP_VERSION_INDEX, index); + return needSave; } + private boolean updateExtensionVersion(Extension extensionVersion, XWikiDocument extensiondocument, long index, + boolean save) throws XWikiException + { + boolean needSave = false; + + XWikiContext xcontext = this.xcontextProvider.get(); + + // Resolve the version document + XWikiDocument extensionVersionDocument = this.extensionStore.getExtensionVersionDocument(extensiondocument, + extensionVersion.getId().getVersion(), xcontext); + if (extensionVersionDocument.isNew()) { + // New document version + needSave = true; + } + + // Avoid modifying the cached document + if (save) { + extensionVersionDocument = extensionVersionDocument.clone(); + } + + needSave |= updateExtensionVersion(extensionVersion, extensionVersionDocument, index, xcontext); + + // Save if dedicated version page + if (save) { + // Make sure the Versions home page exist + updateVersionHome(extensiondocument, xcontext); + + if (needSave) { + boolean versionPageEnabled = this.extensionStore.isVersionPageEnabled(extensiondocument); + if (versionPageEnabled) { + // Save the version + saveDocument(extensionVersionDocument, "Update", xcontext); + + // Since the data are saved, there is no point saving the extension document + return false; + } + } + } + + return needSave; + } + + private void updateVersionHome(XWikiDocument extensiondocument, XWikiContext xcontext) throws XWikiException + { + PageReference versionsReference = new PageReference("Versions", extensiondocument.getPageReference()); + + if (!xcontext.getWiki().exists(versionsReference, xcontext)) { + XWikiDocument versionsDocument = xcontext.getWiki().getDocument(versionsReference, xcontext); + + versionsDocument.setContent("{{include reference=\"ExtensionCode.VersionsHome\"/}}", Syntax.XWIKI_2_1); + + saveDocument(versionsDocument, "", xcontext); + } + } + private String getDownloadURL(Extension extension) { ExtensionResourceReference resource = new ExtensionResourceReference(extension.getId().getId(), @@ -1353,63 +1488,4 @@ private boolean updateCollection(BaseObject extensionObject, String fieldName, C return needSave; } - - /** - * @since 9.5RC1 - */ - public BaseObject getExtensionVersionObject(XWikiDocument extensionDocument, String version) - { - return getExtensionVersionObject(extensionDocument, version, true); - } - - /** - * @since 9.5RC1 - */ - public BaseObject getExtensionVersionObject(XWikiDocument extensionDocument, String version, boolean allowProxying) - { - if (version == null) { - List objects = - extensionDocument.getXObjects(XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE); - - if (objects == null || objects.isEmpty()) { - return null; - } else { - return objects.get(objects.size() - 1); - } - } - - BaseObject extensionVersionObject = extensionDocument - .getXObject(XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE, "version", version, false); - - if (extensionVersionObject == null && allowProxying && isVersionProxyingEnabled(extensionDocument)) { - // No ExtensionVersionClass object for the version, but proxy is enabled, so try to find remotely - Extension extension = null; - try { - extension = resolveExtensionVersion(extensionDocument, version); - } catch (ExtensionNotFoundException e) { - this.logger.debug("No extension could be found remotely with version [{}] for extension page [{}]", - version, extensionDocument.getDocumentReference()); - } catch (ResolveException e) { - throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR); - } - - // No extension could be found for the provided version - if (extension == null) { - return null; - } - - // Create a temporary xobject for that extension version - // FIXME: find a more elegant solution - try { - XWikiDocument extensionDocumentClone = extensionDocument.clone(); - updateExtensionVersion(extensionDocumentClone, extension); - extensionVersionObject = extensionDocumentClone - .getXObject(XWikiRepositoryModel.EXTENSIONVERSION_CLASSREFERENCE, "version", version, false); - } catch (XWikiException e) { - throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR); - } - } - - return extensionVersionObject; - } } diff --git a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/XWikiRepositoryModel.java b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/XWikiRepositoryModel.java index 4cb012795261..c681940412cd 100644 --- a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/XWikiRepositoryModel.java +++ b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/XWikiRepositoryModel.java @@ -51,6 +51,11 @@ public class XWikiRepositoryModel */ public static final String EXTENSION_SPACENAME = "ExtensionCode"; + /** + * @since 17.9.0RC1 + */ + public static final String EXTENSIONVERSIONS_SPACENAME = "Versions"; + public static final String EXTENSION_CLASSNAME = EXTENSION_SPACENAME + ".ExtensionClass"; public static final String AVERAGERATING_CLASSNAME = "XWiki.AverageRatingsClass"; @@ -187,6 +192,11 @@ public class XWikiRepositoryModel public static final String PROP_EXTENSION_PROPERTIES = "properties"; + /** + * @since 17.9.0RC1 + */ + public static final String PROP_EXTENSION_VERSIONPAGE = "versionPage"; + public static final String PROP_VERSION_ID = "id"; public static final String PROP_VERSION_VERSION = "version"; @@ -198,6 +208,11 @@ public class XWikiRepositoryModel */ public static final String PROP_VERSION_FEATURES = "features"; + /** + * @since 17.9.0RC1 + */ + public static final String PROP_VERSION_INDEX = "index"; + /** * @since 7.3M1 */ diff --git a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/resources/AbstractExtensionRESTResource.java b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/resources/AbstractExtensionRESTResource.java index 802313f1e459..803ef2499e62 100644 --- a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/resources/AbstractExtensionRESTResource.java +++ b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/resources/AbstractExtensionRESTResource.java @@ -63,6 +63,7 @@ import org.xwiki.extension.repository.xwiki.model.jaxb.Namespaces; import org.xwiki.extension.repository.xwiki.model.jaxb.ObjectFactory; import org.xwiki.extension.repository.xwiki.model.jaxb.Property; +import org.xwiki.extension.version.Version; import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.DocumentReferenceResolver; import org.xwiki.query.Query; @@ -93,38 +94,28 @@ */ public abstract class AbstractExtensionRESTResource extends XWikiResource implements Initializable { - public static final String[] EPROPERTIES_SUMMARY = new String[] {XWikiRepositoryModel.PROP_EXTENSION_ID, - XWikiRepositoryModel.PROP_EXTENSION_TYPE, XWikiRepositoryModel.PROP_EXTENSION_NAME}; - protected static final String DEFAULT_BOOST; protected static final String DEFAULT_FL; - protected static Map EPROPERTIES_INDEX = new HashMap(); - - protected static String SELECT_EXTENSIONSUMMARY; + protected static final Map EPROPERTIES_INDEX = + Map.of(XWikiRepositoryModel.PROP_EXTENSION_ID, 0, XWikiRepositoryModel.PROP_EXTENSION_NAME, 1, + XWikiRepositoryModel.PROP_EXTENSION_TYPE, 2, XWikiRepositoryModel.PROP_VERSION_VERSION, 3); - static { - StringBuilder pattern = new StringBuilder(); + // Note that the order of the retrieved information is stable and documented by EPROPERTIES_INDEX, + // therefore it shouldn't be changed or it would break some of the APIs relying on it. + protected static final String SELECT_EXTENSIONSUMMARY = String.format( + "doc.name, doc.space, " + "extension.%s, extension.%s, extension.%s", XWikiRepositoryModel.PROP_EXTENSION_ID, + XWikiRepositoryModel.PROP_EXTENSION_NAME, XWikiRepositoryModel.PROP_EXTENSION_TYPE); - int j = 0; - - pattern.append("doc.name"); - EPROPERTIES_INDEX.put("doc.name", j++); - pattern.append(", "); - pattern.append("doc.space"); - EPROPERTIES_INDEX.put("doc.space", j++); - - // Extension summary - for (int i = 0; i < EPROPERTIES_SUMMARY.length; ++i, ++j) { - String value = EPROPERTIES_SUMMARY[i]; - pattern.append(", extension."); - pattern.append(value); - EPROPERTIES_INDEX.put(value, j); - } - - SELECT_EXTENSIONSUMMARY = pattern.toString(); + protected static final String SELECT_EXTENSION_VERSION = String.format( + "doc.name, doc.space, " + "extensionVersion.%s, extensionVersion.%s, extensionVersion.%s, " + + "extensionVersion.%s, extensionVersion.%s", + XWikiRepositoryModel.PROP_EXTENSION_ID, XWikiRepositoryModel.PROP_EXTENSION_NAME, + XWikiRepositoryModel.PROP_EXTENSION_TYPE, XWikiRepositoryModel.PROP_VERSION_VERSION, + XWikiRepositoryModel.PROP_VERSION_INDEX); + static { // Solr StringBuilder boostBuilder = new StringBuilder(); @@ -132,7 +123,7 @@ public abstract class AbstractExtensionRESTResource extends XWikiResource implem for (SolrField field : XWikiRepositoryModel.SOLR_FIELDS.values()) { // Boost if (field.boostValue != null) { - if (boostBuilder.length() > 0) { + if (!boostBuilder.isEmpty()) { boostBuilder.append(' '); } boostBuilder.append(field.boostName); @@ -142,7 +133,7 @@ public abstract class AbstractExtensionRESTResource extends XWikiResource implem // Fields list if (field.name != null) { - if (flBuilder.length() > 0) { + if (!flBuilder.isEmpty()) { flBuilder.append(','); } flBuilder.append(field.name); @@ -188,7 +179,7 @@ public void initialize() throws InitializationException public XWikiDocument getExistingExtensionDocumentById(String extensionId) throws QueryException, XWikiException { - XWikiDocument document = this.repositoryManager.getExistingExtensionDocumentById(extensionId); + XWikiDocument document = this.extensionStore.getExistingExtensionDocumentById(extensionId); if (document == null) { throw new WebApplicationException(Status.NOT_FOUND); @@ -203,7 +194,7 @@ protected Query createExtensionsCountQuery(String from, String where) throws Que String select = "count(extension." + XWikiRepositoryModel.PROP_EXTENSION_ID + ")"; - return createExtensionsQuery(select, from, where, 0, -1, false); + return createExtensionsQuery(select, from, where, 0, -1, false, false); } protected long getExtensionsCountResult(Query query) throws QueryException @@ -211,31 +202,36 @@ protected long getExtensionsCountResult(Query query) throws QueryException return ((Number) query.execute().get(0)).intValue(); } - protected Query createExtensionsSummariesQuery(String from, String where, int offset, int number, boolean versions) - throws QueryException + protected Query createExtensionsSummariesQuery(String from, String where, int offset, int number, boolean versions, + boolean pageVersion) throws QueryException { String select = SELECT_EXTENSIONSUMMARY; - return createExtensionsQuery(select, from, where, offset, number, versions); + if (versions && pageVersion) { + select = SELECT_EXTENSION_VERSION; + } + + return createExtensionsQuery(select, from, where, offset, number, versions, pageVersion); } private Query createExtensionsQuery(String select, String from, String where, int offset, int number, - boolean versions) throws QueryException + boolean versions, boolean pageVersion) throws QueryException { // select StringBuilder queryStr = new StringBuilder("select "); queryStr.append(select); - if (versions) { + if (versions && !pageVersion) { queryStr.append(", extensionVersion." + XWikiRepositoryModel.PROP_VERSION_VERSION + ""); } - // from - - queryStr - .append(" from Document doc, doc.object(" + XWikiRepositoryModel.EXTENSION_CLASSNAME + ") as extension"); + queryStr.append(" from Document doc"); + // from + if (!pageVersion) { + queryStr.append(", doc.object(" + XWikiRepositoryModel.EXTENSION_CLASSNAME + ") as extension"); + } if (versions) { queryStr .append(", doc.object(" + XWikiRepositoryModel.EXTENSIONVERSION_CLASSNAME + ") as extensionVersion"); @@ -253,9 +249,15 @@ private Query createExtensionsQuery(String select, String from, String where, in queryStr.append('('); queryStr.append(where); queryStr.append(')'); - queryStr.append(" and "); } - queryStr.append("extension." + XWikiRepositoryModel.PROP_EXTENSION_VALIDEXTENSION + " = 1"); + if (versions && pageVersion) { + queryStr.append(" order by extensionVersion." + XWikiRepositoryModel.PROP_VERSION_INDEX); + } else { + if (where != null) { + queryStr.append(" and "); + } + queryStr.append("extension." + XWikiRepositoryModel.PROP_EXTENSION_VALIDEXTENSION + " = 1"); + } Query query = this.queryManager.createQuery(queryStr.toString(), Query.XWQL); @@ -315,8 +317,11 @@ protected void addLicense(AbstractExtension extension, String licenseName) } protected E createExtension(XWikiDocument extensionDocument, String version) + throws XWikiException { BaseObject extensionObject = getExtensionObject(extensionDocument); + XWikiDocument versionDocument = + this.extensionStore.getExtensionVersionDocument(extensionDocument, version, getXWikiContext()); DocumentReference extensionDocumentReference = extensionDocument.getDocumentReference(); if (extensionObject == null) { @@ -326,12 +331,15 @@ protected E createExtension(XWikiDocument extensio AbstractExtension extension; ExtensionVersion extensionVersion; BaseObject extensionVersionObject; + List extensionVersionObjects; if (version == null) { extension = this.extensionObjectFactory.createExtension(); extensionVersion = null; - extensionVersionObject = null; + extensionVersionObject = extensionObject; + extensionVersionObjects = List.of(extensionVersionObject); } else { - extensionVersionObject = repositoryManager.getExtensionVersionObject(extensionDocument, version); + extensionVersionObject = + this.repositoryManager.getExtensionVersionObject(extensionDocument, version, getXWikiContext()); if (extensionVersionObject == null) { throw new WebApplicationException(Status.NOT_FOUND); @@ -339,29 +347,31 @@ protected E createExtension(XWikiDocument extensio extensionVersion = this.extensionObjectFactory.createExtensionVersion(); extension = extensionVersion; - extensionVersion.setVersion((String) this.extensionStore.getValue(extensionVersionObject, - XWikiRepositoryModel.PROP_VERSION_VERSION)); + extensionVersion.setVersion( + this.extensionStore.getValue(extensionVersionObject, XWikiRepositoryModel.PROP_VERSION_VERSION)); + + extensionVersionObjects = List.of(extensionVersionObject, extensionObject); } - extension.setId((String) this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_ID)); + extension.setId(this.extensionStore.getValue(extensionVersionObjects, XWikiRepositoryModel.PROP_EXTENSION_ID)); extension.setType(StringUtils.stripToNull( - (String) this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_TYPE))); + this.extensionStore.getValue(extensionVersionObjects, XWikiRepositoryModel.PROP_EXTENSION_TYPE))); extension.setRating(getExtensionRating(extensionDocumentReference)); extension.setSummary( - (String) this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_SUMMARY)); + this.extensionStore.getValue(extensionVersionObjects, XWikiRepositoryModel.PROP_EXTENSION_SUMMARY)); extension.setDescription( - (String) this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_DESCRIPTION)); + this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_DESCRIPTION)); extension - .setName((String) this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_NAME)); + .setName(this.extensionStore.getValue(extensionVersionObjects, XWikiRepositoryModel.PROP_EXTENSION_NAME)); extension.setCategory( - (String) this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_CATEGORY)); + this.extensionStore.getValue(extensionVersionObjects, XWikiRepositoryModel.PROP_EXTENSION_CATEGORY)); extension.setWebsite(StringUtils.defaultIfEmpty( - (String) this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_WEBSITE), + this.extensionStore.getValue(extensionVersionObjects, XWikiRepositoryModel.PROP_EXTENSION_WEBSITE), extensionDocument.getExternalURL("view", getXWikiContext()))); // Recommended - extension.setRecommended(this.extensionStore.getBooleanValue(extensionVersionObject, + extension.setRecommended(this.extensionStore.getBooleanValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_RECOMMENDED, false)); // Support plans @@ -370,18 +380,18 @@ protected E createExtension(XWikiDocument extensio // SCM ExtensionScm scm = new ExtensionScm(); - scm.setUrl((String) this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_SCMURL)); + scm.setUrl(this.extensionStore.getValue(extensionVersionObjects, XWikiRepositoryModel.PROP_EXTENSION_SCMURL)); scm.setConnection(toScmConnection( - (String) this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_SCMCONNECTION))); - scm.setDeveloperConnection(toScmConnection((String) this.extensionStore.getValue(extensionObject, + this.extensionStore.getValue(extensionVersionObjects, XWikiRepositoryModel.PROP_EXTENSION_SCMCONNECTION))); + scm.setDeveloperConnection(toScmConnection(this.extensionStore.getValue(extensionVersionObjects, XWikiRepositoryModel.PROP_EXTENSION_SCMDEVCONNECTION))); extension.setScm(scm); // Issue Management ExtensionIssueManagement issueManagement = new ExtensionIssueManagement(); - issueManagement.setSystem((String) this.extensionStore.getValue(extensionObject, + issueManagement.setSystem(this.extensionStore.getValue(extensionVersionObjects, XWikiRepositoryModel.PROP_EXTENSION_ISSUEMANAGEMENT_SYSTEM)); - issueManagement.setUrl((String) this.extensionStore.getValue(extensionObject, + issueManagement.setUrl(this.extensionStore.getValue(extensionVersionObjects, XWikiRepositoryModel.PROP_EXTENSION_ISSUEMANAGEMENT_URL)); if (StringUtils.isNotEmpty(issueManagement.getSystem()) || StringUtils.isNotEmpty(issueManagement.getUrl())) { extension.setIssueManagement(issueManagement); @@ -389,37 +399,21 @@ protected E createExtension(XWikiDocument extensio // Authors addExtensionAuthors(extension, - this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_AUTHORS)); + this.extensionStore.getValue(extensionVersionObjects, XWikiRepositoryModel.PROP_EXTENSION_AUTHORS)); // Features - List features; - if (extensionVersionObject != null) { - features = (List) this.extensionStore.getValue(extensionVersionObject, - XWikiRepositoryModel.PROP_VERSION_FEATURES); - } else { - features = (List) this.extensionStore.getValue(extensionObject, - XWikiRepositoryModel.PROP_EXTENSION_FEATURES); - } - if (features != null && !features.isEmpty()) { - for (String feature : features) { - org.xwiki.extension.ExtensionId extensionId = ExtensionIdConverter.toExtensionId(feature, null); - ExtensionId extensionFeature = this.extensionObjectFactory.createExtensionId(); - extensionFeature.setId(extensionId.getId()); - if (extensionId.getVersion() != null) { - extensionFeature.setVersion(extensionId.getVersion().getValue()); - } - extension.getExtensionFeatures().add(extensionFeature); - extension.getFeatures().add(extensionFeature.getId()); - } - } + List features = + this.extensionStore.getValue(extensionVersionObject, XWikiRepositoryModel.PROP_VERSION_FEATURES); + + extractFeatureInformation(features, extension); // License addLicense(extension, this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_LICENSENAME)); // Allowed namespaces - List namespaces = (List) this.extensionStore.getValue(extensionObject, - XWikiRepositoryModel.PROP_EXTENSION_ALLOWEDNAMESPACES); + List namespaces = + this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_ALLOWEDNAMESPACES); Integer namespacesEmpty = this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_ALLOWEDNAMESPACES_EMPTY, 0); if (namespaces != null && (!namespaces.isEmpty() || namespacesEmpty == 1)) { @@ -429,35 +423,32 @@ protected E createExtension(XWikiDocument extensio } // Properties - addProperties(extension, (List) this.extensionStore.getValue(extensionObject, - XWikiRepositoryModel.PROP_EXTENSION_PROPERTIES)); + addProperties(extension, + this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_PROPERTIES)); if (extensionVersion != null) { - // Repositories - if (extensionVersionObject != null) { - List repositories = (List) this.extensionStore.getValue(extensionVersionObject, - XWikiRepositoryModel.PROP_VERSION_REPOSITORIES); - extensionVersion.withRepositories(toExtensionRepositories(repositories)); - } + List repositories = + this.extensionStore.getValue(extensionVersionObjects, XWikiRepositoryModel.PROP_VERSION_REPOSITORIES); + extensionVersion.withRepositories(toExtensionRepositories(repositories)); // Dependencies List dependencies = - extensionDocument.getXObjects(XWikiRepositoryModel.EXTENSIONDEPENDENCY_CLASSREFERENCE); + versionDocument.getXObjects(XWikiRepositoryModel.EXTENSIONDEPENDENCY_CLASSREFERENCE); if (dependencies != null) { for (BaseObject dependencyObject : dependencies) { if (dependencyObject != null) { if (Strings.CS.equals(this.extensionStore.getValue(dependencyObject, - XWikiRepositoryModel.PROP_DEPENDENCY_EXTENSIONVERSION, (String) null), version)) { + XWikiRepositoryModel.PROP_DEPENDENCY_EXTENSIONVERSION, null), version)) { ExtensionDependency dependency = extensionObjectFactory.createExtensionDependency(); - dependency.setId((String) this.extensionStore.getValue(dependencyObject, + dependency.setId(this.extensionStore.getValue(dependencyObject, XWikiRepositoryModel.PROP_DEPENDENCY_ID)); - dependency.setConstraint((String) this.extensionStore.getValue(dependencyObject, + dependency.setConstraint(this.extensionStore.getValue(dependencyObject, XWikiRepositoryModel.PROP_DEPENDENCY_CONSTRAINT)); dependency.setOptional(this.extensionStore.getBooleanValue(dependencyObject, XWikiRepositoryModel.PROP_DEPENDENCY_OPTIONAL, false)); - List repositories = (List) this.extensionStore.getValue(dependencyObject, + List dependencyRepositories = this.extensionStore.getValue(dependencyObject, XWikiRepositoryModel.PROP_DEPENDENCY_REPOSITORIES); - dependency.withRepositories(toExtensionRepositories(repositories)); + dependency.withRepositories(toExtensionRepositories(dependencyRepositories)); extensionVersion.getDependencies().add(dependency); } @@ -469,6 +460,22 @@ protected E createExtension(XWikiDocument extensio return (E) extension; } + private void extractFeatureInformation(Collection features, AbstractExtension extension) + { + if (features != null && !features.isEmpty()) { + for (String feature : features) { + org.xwiki.extension.ExtensionId extensionId = ExtensionIdConverter.toExtensionId(feature, null); + ExtensionId extensionFeature = this.extensionObjectFactory.createExtensionId(); + extensionFeature.setId(extensionId.getId()); + if (extensionId.getVersion() != null) { + extensionFeature.setVersion(extensionId.getVersion().getValue()); + } + extension.getExtensionFeatures().add(extensionFeature); + extension.getFeatures().add(extensionFeature.getId()); + } + } + } + protected ExtensionScmConnection toScmConnection(String connectionString) { if (connectionString != null) { @@ -675,18 +682,7 @@ protected ExtensionVersion createExtensionVersionFromSolrDocument(SolrDocument d // Features Collection features = getSolrValues(document, Extension.FIELD_FEATURES); - if (features != null && !features.isEmpty()) { - for (String feature : features) { - org.xwiki.extension.ExtensionId extensionId = ExtensionIdConverter.toExtensionId(feature, null); - ExtensionId extensionFeature = this.extensionObjectFactory.createExtensionId(); - extensionFeature.setId(extensionId.getId()); - if (extensionId.getVersion() != null) { - extensionFeature.setVersion(extensionId.getVersion().getValue()); - } - extension.getExtensionFeatures().add(extensionFeature); - extension.getFeatures().add(extensionFeature.getId()); - } - } + extractFeatureInformation(features, extension); // License addLicense(extension, getSolrValue(document, Extension.FIELD_LICENSE, true)); @@ -751,6 +747,18 @@ protected ExtensionRating getExtensionRating(DocumentReference extensionDocument return extensionRating; } + protected ExtensionVersionSummary createExtensionVersionSummary(String extensionId, String type, String name, + Version version) + { + ExtensionVersionSummary extensionVersion = this.extensionObjectFactory.createExtensionVersionSummary(); + extensionVersion.setVersion(version.getValue()); + extensionVersion.setId(extensionId); + extensionVersion.setType(type); + extensionVersion.setName(name); + + return extensionVersion; + } + protected void getExtensionSummaries(List extensions, Query query) throws QueryException { @@ -764,7 +772,7 @@ protected void getExtensionSummaries(List extens protected ExtensionSummary createExtensionSummaryFromQueryResult(Object[] entry) { ExtensionSummary extension; - int versionIndex = EPROPERTIES_INDEX.get(EPROPERTIES_SUMMARY[EPROPERTIES_SUMMARY.length - 1]) + 1; + int versionIndex = EPROPERTIES_INDEX.get(XWikiRepositoryModel.PROP_VERSION_VERSION); if (entry.length == versionIndex) { // It's a extension summary without version extension = this.extensionObjectFactory.createExtensionSummary(); diff --git a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/resources/ExtensionVersionFileRESTResource.java b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/resources/ExtensionVersionFileRESTResource.java index c40cd6f6d1d7..f2d21554031f 100644 --- a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/resources/ExtensionVersionFileRESTResource.java +++ b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/resources/ExtensionVersionFileRESTResource.java @@ -115,7 +115,7 @@ private ResponseBuilder downloadLocalExtension(String extensionId, String extens checkRights(extensionDocument); ResourceReference resourceReference = - repositoryManager.getDownloadReference(extensionDocument, extensionVersion); + this.repositoryManager.getDownloadReference(extensionDocument, extensionVersion); ResponseBuilder response = null; @@ -166,7 +166,8 @@ private ResponseBuilder downloadLocalExtension(String extensionId, String extens response.type(type); BaseObject extensionObject = getExtensionObject(extensionDocument); - String extensionType = this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_TYPE); + String extensionType = + this.extensionStore.getValue(extensionObject, XWikiRepositoryModel.PROP_EXTENSION_TYPE); response.entity(entity.getContent()); response.header("Content-Disposition", "attachment; filename=\"" + extensionId + '-' + extensionVersion + '.' + extensionType + "\""); diff --git a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/resources/ExtensionVersionsRESTResource.java b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/resources/ExtensionVersionsRESTResource.java index ede02ee92eb5..d2ecbaf60b5c 100644 --- a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/resources/ExtensionVersionsRESTResource.java +++ b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/resources/ExtensionVersionsRESTResource.java @@ -21,12 +21,8 @@ package org.xwiki.repository.internal.resources; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; import java.util.Iterator; import java.util.List; -import java.util.Map; import javax.inject.Named; import javax.ws.rs.DefaultValue; @@ -37,6 +33,9 @@ import org.apache.commons.lang3.StringUtils; import org.xwiki.component.annotation.Component; +import org.xwiki.extension.ResolveException; +import org.xwiki.extension.repository.ExtensionRepository; +import org.xwiki.extension.repository.result.IterableResult; import org.xwiki.extension.repository.xwiki.model.jaxb.ExtensionVersionSummary; import org.xwiki.extension.repository.xwiki.model.jaxb.ExtensionVersions; import org.xwiki.extension.version.InvalidVersionRangeException; @@ -47,6 +46,9 @@ import org.xwiki.query.QueryException; import org.xwiki.repository.Resources; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.doc.XWikiDocument; + /** * @version $Id$ * @since 3.2M3 @@ -60,15 +62,37 @@ public class ExtensionVersionsRESTResource extends AbstractExtensionRESTResource public ExtensionVersions getExtensionVersions(@PathParam("extensionId") String extensionId, @QueryParam(Resources.QPARAM_LIST_START) @DefaultValue("0") int offset, @QueryParam(Resources.QPARAM_LIST_NUMBER) @DefaultValue("-1") int number, - @QueryParam(Resources.QPARAM_VERSIONS_RANGES) String ranges) throws QueryException, InvalidVersionRangeException + @QueryParam(Resources.QPARAM_VERSIONS_RANGES) String ranges) + throws QueryException, InvalidVersionRangeException, XWikiException, ResolveException { - Query query = createExtensionsSummariesQuery(null, "extension.id = :extensionId", 0, -1, true); + XWikiDocument extensionDocument = getExistingExtensionDocumentById(extensionId); - query.bindValue("extensionId", extensionId); + checkRights(extensionDocument); ExtensionVersions extensions = this.extensionObjectFactory.createExtensionVersions(); - getExtensionSummaries(extensions.getExtensionVersionSummaries(), query); + if (this.extensionStore.isVersionProxyingEnabled(extensionDocument)) { + ExtensionRepository repository = this.repositoryManager.getExtensionRepository(extensionDocument); + if (repository != null) { + IterableResult versions = repository.resolveVersions(extensionId, 0, -1); + + String extensionName = this.extensionStore.getExtensionName(extensionDocument); + String extensionType = this.extensionStore.getExtensionType(extensionDocument);; + + for (Version version : versions) { + extensions.getExtensionVersionSummaries() + .add(createExtensionVersionSummary(extensionId, extensionType, extensionName, version)); + } + } + } else { + boolean versionPageEnabled = this.extensionStore.isVersionPageEnabled(extensionDocument); + Query query = createExtensionsSummariesQuery(null, "extensionVersion.id = :extensionId", 0, -1, true, + versionPageEnabled); + + query.bindValue("extensionId", extensionId); + + getExtensionSummaries(extensions.getExtensionVersionSummaries(), query); + } // Filter by ranges if (StringUtils.isNotBlank(ranges)) { @@ -86,29 +110,6 @@ public ExtensionVersions getExtensionVersions(@PathParam("extensionId") String e } } - // Order by version - final Map versionCache = new HashMap<>(); - Collections.sort(extensions.getExtensionVersionSummaries(), new Comparator() - { - @Override - public int compare(ExtensionVersionSummary o1, ExtensionVersionSummary o2) - { - return toVersion(o1.getVersion()).compareTo(toVersion(o2.getVersion())); - } - - private Version toVersion(String versionString) - { - Version version = versionCache.get(versionString); - - if (version == null) { - version = extensionFactory.getVersion(versionString); - versionCache.put(versionString, version); - } - - return version; - } - }); - extensions.setTotalHits(extensions.getExtensionVersionSummaries().size()); extensions.setOffset(offset); diff --git a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/resources/ExtensionsRESTResource.java b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/resources/ExtensionsRESTResource.java index 9649bce608fa..85faf98cadfb 100644 --- a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/resources/ExtensionsRESTResource.java +++ b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/internal/resources/ExtensionsRESTResource.java @@ -59,7 +59,7 @@ public Extensions getExtensions(@QueryParam(Resources.QPARAM_LIST_START) @Defaul extensions.setOffset(offset); - Query query = createExtensionsSummariesQuery(null, null, offset, number, false); + Query query = createExtensionsSummariesQuery(null, null, offset, number, false, false); getExtensionSummaries(extensions.getExtensionSummaries(), query); diff --git a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/script/RepositoryScriptService.java b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/script/RepositoryScriptService.java index 55e78e4304da..e327285f4395 100644 --- a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/script/RepositoryScriptService.java +++ b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-api/src/main/java/org/xwiki/repository/script/RepositoryScriptService.java @@ -23,6 +23,7 @@ import javax.inject.Inject; import javax.inject.Named; +import javax.inject.Provider; import javax.inject.Singleton; import org.xwiki.component.annotation.Component; @@ -33,10 +34,16 @@ import org.xwiki.extension.repository.ExtensionRepositoryManager; import org.xwiki.extension.version.Version; import org.xwiki.model.reference.DocumentReference; +import org.xwiki.query.QueryException; import org.xwiki.repository.internal.ExtensionStore; import org.xwiki.repository.internal.RepositoryManager; import org.xwiki.script.service.ScriptService; +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.api.Object; +import com.xpn.xwiki.doc.XWikiDocument; + @Component @Named("repository") @Singleton @@ -62,6 +69,9 @@ public class RepositoryScriptService implements ScriptService @Inject private Execution execution; + @Inject + private Provider contextProvider; + /** * Store a caught exception in the context, so that it can be later retrieved using {@link #getLastError()}. * @@ -122,4 +132,23 @@ public ExtensionSupportPlans resolveExtensionSupportPlans(Collection sup { return this.extensionStore.resolveExtensionSupportPlans(supportPlanIds); } + + /** + * @param extensionId the identifier of the extension + * @param version the version for which to find the object + * @return the object holding the extension version metadata + * @throws QueryException when failing to get the version object + * @throws XWikiException when failing to get the version object + * @since 17.9.0RC1 + */ + public Object getVersionObject(String extensionId, String version) throws QueryException, XWikiException + { + XWikiDocument extensionDoc = this.extensionStore.getExistingExtensionDocumentById(extensionId); + XWikiContext context = contextProvider.get(); + // FIXME: add some checks + XWikiDocument extensionVersionDocument = + this.extensionStore.getExtensionVersionDocument(extensionDoc, version, context); + return new Object(this.extensionStore.getExtensionVersionObject(extensionVersionDocument, version), + context); + } } diff --git a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/ExtensionSheet.xml b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/ExtensionSheet.xml index b5f3effc5807..3d82840dea68 100644 --- a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/ExtensionSheet.xml +++ b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/ExtensionSheet.xml @@ -20,7 +20,7 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - + ExtensionCode ExtensionSheet @@ -176,7 +176,7 @@ ##------- Download button ------ #set($lastVersion = $doc.getValue("lastVersion")) #if ("$!lastVersion" != '') - #set ($lastVersionObject = $doc.getObject("ExtensionCode.ExtensionVersionClass", 'version', $lastVersion)) + #set ($lastVersionObject = $services.repository.getVersionObject($extension.getProperty('id').value, $lastVersion)) #set ($version = $lastVersionObject.getProperty('version').value) #set ($download = $lastVersionObject.getProperty("download").value) #if ("$!download" == '') @@ -303,7 +303,12 @@ #end ## Only display release notes if there are downloads and release notes - #if ($lastVersionObject) + #if ($doc.getValue('versionPage') == 1) + #set ($versionPageRef = $services.model.createEntityReference('Versions', 'page', $doc.pageReference)) + = Versions = + + {{include page="$services.rendering.escape($services.model.serialize($versionPageRef), 'xwiki/2.1')"/}} + #elseif ($lastVersionObject) #set ($releaseNotes = []) #set ($versions = $doc.getObjects("ExtensionCode.ExtensionVersionClass")) #foreach ($versionObject in $versions) @@ -322,23 +327,23 @@ $entry.get(1) #end #end + #end - #set($extensionDependencies = $doc.getObjects('ExtensionCode.ExtensionDependencyClass', 'extensionVersion', $lastVersionObject.getValue('version'))) - #if ($extensionDependencies.size() > 0) - = Dependencies = + #set($extensionDependencies = $doc.getObjects('ExtensionCode.ExtensionDependencyClass', 'extensionVersion', $lastVersionObject.getValue('version'))) + #if ($extensionDependencies.size() > 0) + = Dependencies = - Dependencies for this extension ($services.rendering.escape("${extension.getValue('id')} ${doc.getValue('lastVersion')}", 'xwiki/2.1')): - #foreach($extensionDependency in $extensionDependencies) - #set($dependencyDocumentName = $null) - #set($dependencyDocumentNames = $services.query.xwql('from doc.object(ExtensionCode.ExtensionClass) as extension where extension.id = :id').bindValue("id", $extensionDependency.id).execute()) - #if (!$dependencyDocumentNames.isEmpty()) - #set($dependencyDocumentName = $dependencyDocumentNames.get(0)) - #end - #if ($dependencyDocumentName) - * [[$services.rendering.escape($services.rendering.escape($extensionDependency.getValue('id'), 'xwiki/2.1'), 'xwiki/2.1')>>$services.rendering.escape($dependencyDocumentName, 'xwiki/2.1')]] $services.rendering.escape($extensionDependency.getValue('constraint'), 'xwiki/2.1') - #else - * $services.rendering.escape("${extensionDependency.getValue('id')} ${extensionDependency.getValue('constraint')}", 'xwiki/2.1') - #end + Dependencies for this extension ($services.rendering.escape("${extension.getValue('id')} ${doc.getValue('lastVersion')}", 'xwiki/2.1')): + #foreach($extensionDependency in $extensionDependencies) + #set($dependencyDocumentName = $null) + #set($dependencyDocumentNames = $services.query.xwql('from doc.object(ExtensionCode.ExtensionClass) as extension where extension.id = :id').bindValue("id", $extensionDependency.id).execute()) + #if (!$dependencyDocumentNames.isEmpty()) + #set($dependencyDocumentName = $dependencyDocumentNames.get(0)) + #end + #if ($dependencyDocumentName) + * [[$services.rendering.escape($services.rendering.escape($extensionDependency.getValue('id'), 'xwiki/2.1'), 'xwiki/2.1')>>$services.rendering.escape($dependencyDocumentName, 'xwiki/2.1')]] $services.rendering.escape($extensionDependency.getValue('constraint'), 'xwiki/2.1') + #else + * $services.rendering.escape("${extensionDependency.getValue('id')} ${extensionDependency.getValue('constraint')}", 'xwiki/2.1') #end #end #end @@ -405,8 +410,11 @@ 0 + long 0 select + forbidden + 0 0 cache 5 @@ -426,6 +434,7 @@ code 2 Code + 0 20 50 0 @@ -435,6 +444,8 @@ 0 0 select + forbidden + 0 0 contentType 6 @@ -470,6 +481,8 @@ 0 0 select + forbidden + 0 0 use 3 @@ -545,8 +558,11 @@ 0 + long 0 select + forbidden + 0 0 cache 5 @@ -566,6 +582,7 @@ code 2 Code + 0 20 50 0 @@ -575,6 +592,8 @@ 0 0 select + forbidden + 0 0 contentType 6 @@ -610,6 +629,8 @@ 0 0 select + forbidden + 0 0 use 3 diff --git a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/ExtensionVersionClass.xml b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/ExtensionVersionClass.xml index b1dec9d138f1..7868b0d86b9f 100644 --- a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/ExtensionVersionClass.xml +++ b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/ExtensionVersionClass.xml @@ -20,7 +20,7 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - + ExtensionCode ExtensionVersionClass @@ -46,6 +46,99 @@ + + 0 + + + 0 + input + + + 0 + 1 + allowednamespaces + 26 + 0 + Allowed namespaces + 1 + + | + 1 + none + 0 + + + + com.xpn.xwiki.objects.classes.StaticListClass + + + + 0 + 0 + checkbox + + + allowednamespaces_empty + 27 + Is allowed namespaces empty + 0 + + + com.xpn.xwiki.objects.classes.BooleanClass + + + 0 + {{include reference="ExtensionCode.ExtensionAuthorsDisplayer"/}} + + 0 + input + + + 0 + 1 + authors + 7 + 0 + Authors + 0 + + ,| + 30 + none + 0 + + + + com.xpn.xwiki.objects.classes.StaticListClass + + + 0 + ExtensionCode.ExtensionCategoryClass + + + 0 + select + + + id + 0 + 0 + category + 16 + 0 + Category + 0 + + + 1 + value + + 0 + + + name + com.xpn.xwiki.objects.classes.DBListClass + PureText @@ -56,6 +149,7 @@ 2 0 Download URL + 0 1 100 0 @@ -66,9 +160,12 @@ 0 + 0 input + + 0 1 features 5 @@ -99,6 +196,59 @@ com.xpn.xwiki.objects.classes.StringClass + + + 0 + + index + 20 + long + index + 30 + 0 + + + com.xpn.xwiki.objects.classes.NumberClass + + + 0 + + + 0 + select + + + 0 + 0 + licenseName + 10 + 0 + License Name + 0 + + ,| + 1 + none + 0 + + + GNU General Public License 1|GNU General Public License 2|GNU General Public License 3|GNU Lesser General Public License 2|GNU Lesser General Public License 2.1|GNU Lesser General Public License 3|Apache License 2.0|BSD license|Modified BSD License|Simplified BSD License|GNU Affero General Public License 3|GNU Free Documentation License 1.1|GNU Free Documentation License 1.2|GNU Free Documentation License 1.3|Educational Community License 1.0|Educational Community License 2.0|Do What The Fuck You Want To Public License 2 + com.xpn.xwiki.objects.classes.StaticListClass + + + + 0 + + name + 3 + 0 + Name + 30 + 0 + + + com.xpn.xwiki.objects.classes.StringClass + --- @@ -109,6 +259,7 @@ 1 0 Release Notes + 0 15 75 0 @@ -116,12 +267,40 @@ com.xpn.xwiki.objects.classes.TextAreaClass + + 0 + + + 0 + input + + + 0 + 1 + properties + 17 + 0 + Properties + 1 + + | + 1 + none + 0 + + + + com.xpn.xwiki.objects.classes.StaticListClass + 0 + 0 input + + 0 1 repositories 6 @@ -138,6 +317,102 @@ com.xpn.xwiki.objects.classes.StaticListClass + + PureText + + 0 + --- + + scmconnection + 25 + 0 + Sources connection + 0 + 1 + 100 + 0 + + + com.xpn.xwiki.objects.classes.TextAreaClass + + + PureText + + 0 + --- + + scmdevconnection + 26 + 0 + Sources dev connection + 0 + 1 + 100 + 0 + + + com.xpn.xwiki.objects.classes.TextAreaClass + + + PureText + + 0 + --- + + source + 24 + 0 + Source + 0 + 1 + 100 + 0 + + + com.xpn.xwiki.objects.classes.TextAreaClass + + + + 0 + + summary + 4 + 0 + Summary + 75 + 0 + + + com.xpn.xwiki.objects.classes.StringClass + + + 0 + ExtensionCode.ExtensionTypeClass + + + 0 + select + + + id + 0 + 0 + type + 2 + 0 + Type + 0 + + + 1 + value + + 0 + + + name + com.xpn.xwiki.objects.classes.DBListClass + 0 @@ -152,7 +427,69 @@ com.xpn.xwiki.objects.classes.StringClass + + PureText + + 0 + --- + + website + 6 + 0 + Website + 0 + 1 + 100 + 0 + + + com.xpn.xwiki.objects.classes.TextAreaClass + + + ExtensionCode.ExtensionVersionClass + 0 + XWiki.ClassSheetBinding + 75bd5971-88a0-48b0-aa01-a817cbe3b4f9 + + XWiki.ClassSheetBinding + + + + + + + + + 0 + + + 0 + input + + + 0 + sheet + 1 + 1 + Sheet + 0 + + + 30 + none + + 0 + + + + com.xpn.xwiki.objects.classes.PageClass + + + + ExtensionCode.ExtensionVersionSheet + + ExtensionCode.ExtensionVersionClass 0 @@ -180,10 +517,10 @@ 1 1 Sheet - 30 0 + 30 none 0 diff --git a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/ExtensionVersionSheet.xml b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/ExtensionVersionSheet.xml index a7e82ceffa6e..58004ad2c04e 100644 --- a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/ExtensionVersionSheet.xml +++ b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/ExtensionVersionSheet.xml @@ -20,7 +20,7 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - + ExtensionCode ExtensionVersionSheet @@ -31,19 +31,151 @@ xwiki:XWiki.Admin xwiki:XWiki.Admin 1.1 - ExtensionVersion Sheet + #if($doc.getObject('ExtensionCode.ExtensionVersionClass'))$doc.getObject('ExtensionCode.ExtensionVersionClass').getProperty('name').value $doc.getObject('ExtensionCode.ExtensionVersionClass').getProperty('version').value#{else}Extension version sheet#end false - xwiki/2.0 + xwiki/2.1 true - {{velocity}} -## You can modify this page to customize the presentation of your object. -## At first you should keep the default presentation and just save the document. - -#set($class = $doc.getObject('ExtensionCode.ExtensionVersionClass').xWikiClass) -#foreach($prop in $class.properties) - ; $prop.prettyName - : $doc.display($prop.getName()) + {{include reference="ExtensionCode.RepositoryCode"/}} + +{{template name="extension.vm"/}} + +{{velocity output="false"}} +#if ($xcontext.action == 'edit') + #set($isEditMode = true) +#else + #set($isViewMode = true) +#end + +#set($extension = $doc.getObject("ExtensionCode.ExtensionClass")) +#set($extensionVersion = $doc.getObject("ExtensionCode.ExtensionVersionClass")) +{{/velocity}} + +{{velocity}} +#if ($extensionVersion && !$extension) + #set($type = $extensionVersion.getProperty('type').value) + #set ($extensionTypeDocumentNames = $services.query.xwql('from doc.object(ExtensionCode.ExtensionTypeClass) as type where type.id = :id').bindValue("id", $type).execute()) + #if ($extensionTypeDocumentNames.size() > 0) + #set ($extensionTypeDocumentName = $extensionTypeDocumentNames.get(0)) + #set($extensionTypeObject = $xwiki.getDocument($extensionTypeDocumentName).getObject("ExtensionCode.ExtensionTypeClass")) + #end + + $doc.use("ExtensionCode.ExtensionVersionClass") + ##------- Back button ---------------- + [[$services.icon.render('left') Extension page>>$services.model.serialize($doc.getDocumentReference().getParent().getParent().getParent())||class="btn btn-primary"]]## + + #if ($isViewMode) + ##------- Icon & Summary ----------------- + (% class="extensionSummary" %) + **${services.rendering.escape($doc.getValue('summary'), 'xwiki/2.1')}** + + ## Viewing + ## + {{box cssClass="floatinginfobox col-xs-12 col-sm-6 pull-right"}} + (% class="extensionInfo" %) + ##------- Type -------------- + #set($typeDisplay = $extensionTypeObject.getProperty('name').value) + #if ("$!typeDisplay" == '') + #set($typeDisplay = $type) + #end + |(% class="label" width='30%' %)Type(%%)|$services.rendering.escape($typeDisplay, 'xwiki/2.1') + ##------- Category -------------- + #set($categoryDisplay = $extensionVersion.getProperty('category').value) + #if ("$!categoryDisplay" == '') + #set($categoryDisplay = $category) + #end + #if ($categoryDisplay) + |(% class="label" width='30%' %)Category(%%)|$services.rendering.escape($categoryDisplay, 'xwiki/2.1') + #end + ##------- Developed By -------- + #set($authors = $doc.getValue("authors")) + |(% class="label" %)Developed by(%%)|#if ($authors.isEmpty()) + Unknown + #else + $doc.display('authors') + #end + ##------- Website -------------- + #set($website = $extensionVersion.getProperty("website").value) + #if ("$!website" != '') + |(% class="label" %)Website(%%)|#if ($website.length() > 40) + [[$services.rendering.escape($services.rendering.escape($website.substring(0, 40), 'xwiki/2.1'), 'xwiki/2.1')...>>$services.rendering.escape($website, 'xwiki/2.1')]] + #else + [[$services.rendering.escape($website, 'xwiki/2.1')]] + #end + #end + ##------- License -------- + #set($licenseName = $doc.getValue("licenseName")) + #if ("$!licenseName" != "") + |(% class="label" %)License(%%)|$services.rendering.escape($licenseName, 'xwiki/2.1') + #else + |(% class="label" %)License(%%)|Unknown + #end + ##--------------------------------- + ##------- Sheet extensions -------- + #if (!$sheetExtensions.isEmpty()) + + #foreach($sheetExtension in $sheetExtensions) + $doc.display('view_info', 'view', $sheetExtension) + #end + #end + ##------------------------------------------- + ##------- Extension Manager ----------------- + #if ($doc.getValue('validExtension') == 1) + + {{success}}**Installable with the Extension Manager**{{/success}} + #end + ##------------------------ + (%class="btn-group pull-right"%)((( + ##------- Download button ------ + #set ($version = $extensionVersion.getProperty('version').value) + #set ($download = $extensionVersion.getProperty('download').value) + #if ("$!download" == '') + #if ($doc.getAttachment("${id}-${version}.${type}")) + [[$services.icon.render('download') Download v$services.rendering.escape($services.rendering.escape($version, 'xwiki/2.1'), 'xwiki/2.1')>>$services.rendering.escape("attach:${id}-${version}.${type}")||class="btn btn-primary"]]## + #end + #else + [[$services.icon.render('download') Download v$services.rendering.escape($services.rendering.escape($version, 'xwiki/2.1'), 'xwiki/2.1')>>$services.rendering.escape($download, 'xwiki/2.1')||class="btn btn-primary"]]## + #end + ##------- Source -------- + #set($source = $extensionVersion.getProperty("source").value) + #if ("$!source" != "") + [[Sources>>$services.rendering.escape($source, 'xwiki/2.1')||class="btn btn-default"]]## + #end + ))) + {{/box}} + + {{box cssClass="toc col-xs-12 col-sm-6"}}(% class="label" %)Table of contents(%%)((({{toc/}}))){{/box}} + (%class="clearfix"%)((())) + #end + + ## Release notes + = Release Notes = + #set($releaseNotes = $doc.getValue("notes")) + $doc.display('notes') + + #if ($isViewMode) + #set($extensionDependencies = $doc.getObjects('ExtensionCode.ExtensionDependencyClass', 'extensionVersion', $version)) + #if ($extensionDependencies.size() > 0) + = Dependencies = + + Dependencies for this extension ($services.rendering.escape("${extensionVersion.getValue('id')} ${version}", 'xwiki/2.1')): + #foreach($extensionDependency in $extensionDependencies) + #set($dependencyDocumentName = $null) + #set($dependencyDocumentNames = $services.query.xwql('from doc.object(ExtensionCode.ExtensionClass) as extension where extension.id = :id').bindValue("id", $extensionDependency.id).execute()) + #if (!$dependencyDocumentNames.isEmpty()) + #set($dependencyDocumentName = $dependencyDocumentNames.get(0)) + #end + #if ($dependencyDocumentName) + * [[$services.rendering.escape($services.rendering.escape($extensionDependency.getValue('id'), 'xwiki/2.1'), 'xwiki/2.1')>>$services.rendering.escape($dependencyDocumentName, 'xwiki/2.1')]] $services.rendering.escape($extensionDependency.getValue('constraint'), 'xwiki/2.1') + #else + * $services.rendering.escape("${extensionDependency.getValue('id')} ${extensionDependency.getValue('constraint')}", 'xwiki/2.1') + #end + #end + #end + #end + +#else + This class sheet must be applied on a document containing an ExtensionCode.ExtensionVersionClass object #end {{/velocity}} @@ -65,4 +197,309 @@ + + ExtensionCode.ExtensionVersionSheet + 0 + XWiki.StyleSheetExtension + 6a915691-5ff6-4850-98ab-917595c2fc10 + + XWiki.StyleSheetExtension + + + + + + + + + 0 + long + 0 + select + forbidden + 0 + 0 + cache + 5 + Caching policy + 0 + + |, + 1 + 0 + long|short|default|forbid + com.xpn.xwiki.objects.classes.StaticListClass + + + PureText + 0 + PureText + code + 2 + Code + 0 + 20 + 50 + 0 + com.xpn.xwiki.objects.classes.TextAreaClass + + + 0 + 0 + select + forbidden + 0 + 0 + contentType + 6 + Content Type + 0 + + |, + 1 + 0 + CSS|LESS + com.xpn.xwiki.objects.classes.StaticListClass + + + 0 + name + 1 + Name + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + select + yesno + parse + 4 + Parse content + 0 + com.xpn.xwiki.objects.classes.BooleanClass + + + 0 + 0 + select + forbidden + 0 + 0 + use + 3 + Use this extension + 0 + + |, + 1 + 0 + currentPage|onDemand|always + com.xpn.xwiki.objects.classes.StaticListClass + + + + long + + + #template('colorThemeInit.vm') + +.extensionSummary { + background-color: $theme.backgroundSecondaryColor; + border: 1px dotted $theme.borderColor; + padding: 5px; + display: block; +} + +.main .extensionInfo { + margin: 0; +} + +.extensionInfo .label { + font-size: 0.85em; + font-weight: bold; + text-transform: uppercase; +} + +/*Rating*/ +.extensionInfo .rating-wrapper { + float: left; +} + +.extensionInfo .rating-container > div { + float: left; + margin-right: 10px +} + + + CSS + + + Extension CSS + + + 1 + + + always + + + + ExtensionCode.ExtensionVersionSheet + 1 + XWiki.StyleSheetExtension + ac3e481a-5ec9-41eb-9a4d-9b109bcae4be + + XWiki.StyleSheetExtension + + + + + + + + + 0 + long + 0 + select + forbidden + 0 + 0 + cache + 5 + Caching policy + 0 + + |, + 1 + 0 + long|short|default|forbid + com.xpn.xwiki.objects.classes.StaticListClass + + + PureText + 0 + PureText + code + 2 + Code + 0 + 20 + 50 + 0 + com.xpn.xwiki.objects.classes.TextAreaClass + + + 0 + 0 + select + forbidden + 0 + 0 + contentType + 6 + Content Type + 0 + + |, + 1 + 0 + CSS|LESS + com.xpn.xwiki.objects.classes.StaticListClass + + + 0 + name + 1 + Name + 30 + 0 + com.xpn.xwiki.objects.classes.StringClass + + + 0 + select + yesno + parse + 4 + Parse content + 0 + com.xpn.xwiki.objects.classes.BooleanClass + + + 0 + 0 + select + forbidden + 0 + 0 + use + 3 + Use this extension + 0 + + |, + 1 + 0 + currentPage|onDemand|always + com.xpn.xwiki.objects.classes.StaticListClass + + + + long + + + .extensionSummary td, /* PROBLEM: General styling reset */ +.extensionInfo td { + border: 0; + padding-top: 0; + padding-bottom: 0; +} + +@media (min-width: 768px) { + .box.floatinginfobox, .box.toc { + max-width: 49%; + } +} + +.box .label { /* PROBLEM: Usage of a class with meaning inside Bootstrap */ + color: inherit; + text-align: inherit; + display: table-cell; +} + +.toc .label { + margin: 0; +} + +/* Download and Source button alignment */ +.box > p { + margin-bottom: 0; +} + +/* Spacing for repository info message */ +.box .extensionInfo { + margin-top: 10px; +} + +/* XINFRA-134: Aligning table cell text */ +.floatinginfobox td { + vertical-align: baseline; +} + + + CSS + + + Junco + + + + + + currentPage + + diff --git a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/ExtensionVersionTemplate.xml b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/ExtensionVersionTemplate.xml index 1117e93abdff..f611b6a0a5ac 100644 --- a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/ExtensionVersionTemplate.xml +++ b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/ExtensionVersionTemplate.xml @@ -51,6 +51,99 @@ + + 0 + + + 0 + input + + + 0 + 1 + allowednamespaces + 26 + 0 + Allowed namespaces + 1 + + | + 1 + none + 0 + + + + com.xpn.xwiki.objects.classes.StaticListClass + + + + 0 + 0 + checkbox + + + allowednamespaces_empty + 27 + Is allowed namespaces empty + 0 + + + com.xpn.xwiki.objects.classes.BooleanClass + + + 0 + {{include reference="ExtensionCode.ExtensionAuthorsDisplayer"/}} + + 0 + input + + + 0 + 1 + authors + 7 + 0 + Authors + 0 + + ,| + 30 + none + 0 + + + + com.xpn.xwiki.objects.classes.StaticListClass + + + 0 + ExtensionCode.ExtensionCategoryClass + + + 0 + select + + + id + 0 + 0 + category + 16 + 0 + Category + 0 + + + 1 + value + + 0 + + + name + com.xpn.xwiki.objects.classes.DBListClass + PureText @@ -104,11 +197,51 @@ com.xpn.xwiki.objects.classes.StringClass + + 0 + + + 0 + select + + + 0 + 0 + licenseName + 10 + 0 + License Name + 0 + + ,| + 1 + none + 0 + + + GNU General Public License 1|GNU General Public License 2|GNU General Public License 3|GNU Lesser General Public License 2|GNU Lesser General Public License 2.1|GNU Lesser General Public License 3|Apache License 2.0|BSD license|Modified BSD License|Simplified BSD License|GNU Affero General Public License 3|GNU Free Documentation License 1.1|GNU Free Documentation License 1.2|GNU Free Documentation License 1.3|Educational Community License 1.0|Educational Community License 2.0|Do What The Fuck You Want To Public License 2 + com.xpn.xwiki.objects.classes.StaticListClass + + + + 0 + + name + 3 + 0 + Name + 30 + 0 + + + com.xpn.xwiki.objects.classes.StringClass + --- 0 --- + notes 1 0 @@ -120,11 +253,37 @@ com.xpn.xwiki.objects.classes.TextAreaClass + + 0 + + + 0 + input + + + 0 + 1 + properties + 17 + 0 + Properties + 1 + + | + 1 + none + 0 + + + + com.xpn.xwiki.objects.classes.StaticListClass + 0 0 input + 1 repositories 6 @@ -141,6 +300,102 @@ com.xpn.xwiki.objects.classes.StaticListClass + + PureText + + 0 + --- + + scmconnection + 25 + 0 + Sources connection + 0 + 1 + 100 + 0 + + + com.xpn.xwiki.objects.classes.TextAreaClass + + + PureText + + 0 + --- + + scmdevconnection + 26 + 0 + Sources dev connection + 0 + 1 + 100 + 0 + + + com.xpn.xwiki.objects.classes.TextAreaClass + + + PureText + + 0 + --- + + source + 24 + 0 + Source + 0 + 1 + 100 + 0 + + + com.xpn.xwiki.objects.classes.TextAreaClass + + + + 0 + + summary + 4 + 0 + Summary + 75 + 0 + + + com.xpn.xwiki.objects.classes.StringClass + + + 0 + ExtensionCode.ExtensionTypeClass + + + 0 + select + + + id + 0 + 0 + type + 2 + 0 + Type + 0 + + + 1 + value + + 0 + + + name + com.xpn.xwiki.objects.classes.DBListClass + 0 @@ -155,7 +410,37 @@ com.xpn.xwiki.objects.classes.StringClass + + PureText + + 0 + --- + + website + 6 + 0 + Website + 0 + 1 + 100 + 0 + + + com.xpn.xwiki.objects.classes.TextAreaClass + + + + + + + + + + + + + @@ -165,14 +450,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/VersionsHome.xml b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/VersionsHome.xml new file mode 100644 index 000000000000..a42d633cf294 --- /dev/null +++ b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/main/resources/ExtensionCode/VersionsHome.xml @@ -0,0 +1,72 @@ + + + + + + ExtensionCode + VersionsHome + + + 0 + xwiki:XWiki.Admin + xwiki:XWiki.Admin + xwiki:XWiki.Admin + 1.1 + + <comment/> + <parent>WebHome</parent> + <minorEdit>false</minorEdit> + <syntaxId>xwiki/2.1</syntaxId> + <hidden>true</hidden> + <content>{{velocity}} +#set($locationCriteria = $doc.space) +#if(!$locationCriteria.endsWith('Versions')) + #set ($locationCriteria = "${locationCriteria}.Versions") +#end + +#set ($optionsLD = { + 'className': 'ExtensionCode.ExtensionVersionClass', + 'location': "${locationCriteria}", + 'translationPrefix': 'extension.repository.' +}) + +{{liveData + id="versions" + properties="version,notes" + source="liveTable" + sort="version:desc" + limit="5" + sourceParameters="$services.rendering.escape($escapetool.url($optionsLD), 'xwiki/2.1')" +}}{ +"meta": { + "propertyDescriptors": [ + { + "id": "version", + "displayer": {"id": "link", "propertyHref": "doc.url"}, + "editable": false + } + ] + } +} +{{/liveData}} + +{{/velocity}}</content> +</xwikidoc> diff --git a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/test/java/org/xwiki/repository/server/ui/ExtensionSheetPageTest.java b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/test/java/org/xwiki/repository/server/ui/ExtensionSheetPageTest.java index b1dc67cbb8f3..112fa302f851 100644 --- a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/test/java/org/xwiki/repository/server/ui/ExtensionSheetPageTest.java +++ b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-server-ui/src/test/java/org/xwiki/repository/server/ui/ExtensionSheetPageTest.java @@ -50,6 +50,7 @@ import org.xwiki.rendering.internal.macro.toc.TocMacro; import org.xwiki.rendering.macro.script.MacroPermissionPolicy; import org.xwiki.rendering.transformation.MacroTransformationContext; +import org.xwiki.repository.script.RepositoryScriptService; import org.xwiki.script.service.ScriptService; import org.xwiki.test.annotation.ComponentList; import org.xwiki.test.junit5.mockito.MockComponent; @@ -58,6 +59,7 @@ import org.xwiki.test.page.TestNoScriptMacro; import org.xwiki.test.page.XWikiSyntax21ComponentList; +import com.xpn.xwiki.api.Object; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.objects.BaseObject; @@ -94,7 +96,7 @@ LoggingScriptService.class, PermissionCheckerListener.class, TestNoScriptMacro.class, - TocMacro.class, + TocMacro.class }) class ExtensionSheetPageTest extends PageTest { @@ -189,6 +191,13 @@ void setUp() throws Exception // Mock restricted contexts. when(this.groovyMacroPermissionPolicy.hasPermission(any(), any())).thenAnswer(i -> !((MacroTransformationContext) i.getArgument(1)).getTransformationContext().isRestricted()); + + // Mock repository script service + RepositoryScriptService repositoryScriptService = + this.componentManager.registerMockComponent(ScriptService.class, "repository", + RepositoryScriptService.class, false); + when(repositoryScriptService.getVersionObject(any(), any())).thenReturn(new Object(extensionVersionObject, + null)); } @Test diff --git a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-test/xwiki-platform-repository-test-tests/src/test/it/org/xwiki/repository/test/ui/repository/RepositoryIT.java b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-test/xwiki-platform-repository-test-tests/src/test/it/org/xwiki/repository/test/ui/repository/RepositoryIT.java index b14177e265a5..5a4880412d6e 100644 --- a/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-test/xwiki-platform-repository-test-tests/src/test/it/org/xwiki/repository/test/ui/repository/RepositoryIT.java +++ b/xwiki-platform-core/xwiki-platform-repository/xwiki-platform-repository-test/xwiki-platform-repository-test-tests/src/test/it/org/xwiki/repository/test/ui/repository/RepositoryIT.java @@ -39,7 +39,10 @@ import org.xwiki.extension.repository.xwiki.model.jaxb.ExtensionsSearchResult; import org.xwiki.extension.repository.xwiki.model.jaxb.Property; import org.xwiki.extension.version.internal.DefaultVersionConstraint; +import org.xwiki.model.EntityType; +import org.xwiki.model.reference.EntityReference; import org.xwiki.model.reference.LocalDocumentReference; +import org.xwiki.rendering.syntax.Syntax; import org.xwiki.repository.Resources; import org.xwiki.repository.internal.XWikiRepositoryModel; import org.xwiki.repository.test.TestExtension; @@ -56,11 +59,10 @@ import org.xwiki.repository.test.po.edit.ExtensionSupporterInlinePage; import org.xwiki.repository.test.ui.AbstractExtensionAdminAuthenticatedIT; import org.xwiki.rest.model.jaxb.Page; -import org.xwiki.test.ui.po.editor.ObjectEditPage; -import org.xwiki.test.ui.po.editor.ObjectEditPane; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -473,10 +475,10 @@ private void testRestAccessToImportedExtension() throws Exception assertEquals("jar", extension.getType()); assertEquals("1.0", extension.getVersion()); assertEquals("name", extension.getName()); - assertEquals("summary2", extension.getSummary()); + assertEquals("summary", extension.getSummary()); assertEquals("summary2\n some more details", extension.getDescription()); - assertEquals(this.baseAuthor.getName(), extension.getAuthors().get(0).getName()); - assertEquals(this.baseAuthor.getURL().toString(), extension.getAuthors().get(0).getUrl()); + assertEquals("Previous Name", extension.getAuthors().get(0).getName()); + assertNull(extension.getAuthors().get(0).getUrl()); assertEquals(Arrays.asList("maven:oldextension", "maven:oldversionnedextension"), extension.getFeatures()); assertEquals("maven:oldextension", extension.getExtensionFeatures().get(0).getId()); @@ -492,14 +494,14 @@ private void testRestAccessToImportedExtension() throws Exception extension = getUtil().rest().getResource(Resources.EXTENSION_VERSION, null, "maven:extension", "0.9"); - assertEquals("maven:extension", extension.getId()); + assertEquals("maven:oldextension", extension.getId()); assertEquals("jar", extension.getType()); assertEquals("0.9", extension.getVersion()); assertEquals("name", extension.getName()); assertEquals("summary2", extension.getSummary()); assertEquals("summary2\n some more details", extension.getDescription()); - assertEquals(this.baseAuthor.getName(), extension.getAuthors().get(0).getName()); - assertEquals(this.baseAuthor.getURL().toString(), extension.getAuthors().get(0).getUrl()); + assertEquals("Old Name", extension.getAuthors().get(0).getName()); + assertNull(extension.getAuthors().get(0).getUrl()); assertEquals(Arrays.asList(), extension.getFeatures()); assertEquals(Arrays.asList(), extension.getExtensionFeatures()); assertEquals("GNU Lesser General Public License 2.1", extension.getLicenses().get(0).getName()); @@ -517,8 +519,7 @@ private void enableProxying(ExtensionPage extensionPage) throws Exception new LocalDocumentReference(List.of("Extension", importedExtensionName), "WebHome"); // assert that this test is going to make sense at all - assertTrue(getNumberOfExtensionVersionsObjects(importedExtensionName) > 1); - assertTrue(getNumberOfExtensionVersionsDependenciesObjects(importedExtensionName) > 1); + assertTrue(getNumberOfExtensionVersionsPages(extensionPageReference) > 1); // indicate that the history of the extension should be proxied getUtil().updateObject(extensionPageReference, XWikiRepositoryModel.EXTENSIONPROXY_CLASSNAME, 0, "proxyLevel", @@ -527,9 +528,8 @@ private void enableProxying(ExtensionPage extensionPage) throws Exception // refresh extension extensionPage.updateExtension(); - // assert that the object to be proxied are now absent - assertEquals(1, getNumberOfExtensionVersionsObjects(importedExtensionName)); - assertEquals(1, getNumberOfExtensionVersionsDependenciesObjects(importedExtensionName)); + // assert that the version to be proxied are now absent + assertEquals(1, getNumberOfExtensionVersionsPages(extensionPageReference)); // Remember the page version Page restPage = getUtil().rest().get(extensionPageReference); @@ -543,25 +543,16 @@ private void enableProxying(ExtensionPage extensionPage) throws Exception assertEquals(extensionPageVersion, restPage.getVersion()); } - private int getNumberOfExtensionVersionsObjects(String extensionName) - { - ObjectEditPage objectEditPage = goToObjectEditPage(extensionName); - List<ObjectEditPane> versionObjects = - objectEditPage.getObjectsOfClass(XWikiRepositoryModel.EXTENSIONVERSION_CLASSNAME); - return versionObjects.size(); - } - - private int getNumberOfExtensionVersionsDependenciesObjects(String extensionName) - { - ObjectEditPage objectEditPage = goToObjectEditPage(extensionName); - List<ObjectEditPane> dependenciesObjects = - objectEditPage.getObjectsOfClass(XWikiRepositoryModel.EXTENSIONDEPENDENCY_CLASSNAME); - return dependenciesObjects.size(); - } - - private ObjectEditPage goToObjectEditPage(String extensionName) + private int getNumberOfExtensionVersionsPages(LocalDocumentReference extensionPageReference) throws Exception { - return getRepositoryTestUtils().gotoExtensionObjectsEditPage(extensionName); + EntityReference versionReference = new EntityReference(XWikiRepositoryModel.EXTENSIONVERSIONS_SPACENAME, + EntityType.SPACE, extensionPageReference.getParent()); + String result = getUtil().executeWikiPlain( + "{{velocity}}$services.query.xwql(\"select space.reference from Space space where space.parent='" + + getUtil().serializeLocalReference(versionReference) + "'\").execute().size(){{/velocity}}", + Syntax.XWIKI_2_1); + + return Integer.parseInt(result); } private void validateSupport() throws Exception diff --git a/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/TestUtils.java b/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/TestUtils.java index 74898686019e..bdf3ad51847f 100644 --- a/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/TestUtils.java +++ b/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/TestUtils.java @@ -104,6 +104,7 @@ import org.xwiki.rest.resources.objects.ObjectsResource; import org.xwiki.rest.resources.pages.PageResource; import org.xwiki.rest.resources.pages.PageTranslationResource; +import org.xwiki.rest.resources.pages.PagesResource; import org.xwiki.test.integration.XWikiExecutor; import org.xwiki.test.ui.po.BasePage; import org.xwiki.test.ui.po.ViewPage; @@ -2539,6 +2540,7 @@ public ResourceAPI(Class<?> api, Class<?> localeAPI) throw new RuntimeException(e); } + RESOURCES_MAP.put(EntityType.SPACE, new ResourceAPI(PagesResource.class, null)); RESOURCES_MAP.put(EntityType.DOCUMENT, new ResourceAPI(PageResource.class, PageTranslationResource.class)); RESOURCES_MAP.put(EntityType.OBJECT, new ResourceAPI(ObjectResource.class, null)); RESOURCES_MAP.put(EntityType.OBJECT_PROPERTY, new ResourceAPI(ObjectPropertyResource.class, null)); diff --git a/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/po/InlinePage.java b/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/po/InlinePage.java index cd3e39a8b5be..b20cc9f3fea7 100644 --- a/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/po/InlinePage.java +++ b/xwiki-platform-core/xwiki-platform-test/xwiki-platform-test-ui/src/main/java/org/xwiki/test/ui/po/InlinePage.java @@ -102,6 +102,8 @@ public <T extends ViewPage> T clickSaveAndView() public void clickSaveAndView(boolean wait) { if (wait) { + // Increase the timeout as a page save can be slow + getDriver().setTimeout(30); getDriver().addPageNotYetReloadedMarker(); }