XAR Extensions

Last modified by Thomas Mortagne on 2021/03/02 18:22

Handling of XAR packages as defined in specifications.

Script service

The XAR extension handler expose a ScriptService component for XAR extension specific features.

XAR Repair

Sometimes you have pages coming from an extension in the database but lost the actual extension index. The most common use case is when upgrading from a pre extension manager version of XWiki.

To recreate index for those extension the script service provide Job repairInstalledExtension(String id, String version, String wiki) API.

    /**
     * Make sure the provided XAR extension properly is registered in the installed extensions index.
     * <p>
     * Start an asynchronous Job.
     *
     * @param id the extension identifier
     * @param version the extension version
     * @param wiki the wiki where the extension is installed
     * @return the {@link Job} object which can be used to monitor the progress of the installation process, or
     *         {@code null} in case of failure
     */

   public Job repairInstalledExtension(String id, String version, String wiki)

Extension Diff

[since 7.0]

It's possible to get a full diff between an extension and all its dependencies and what is actually in the database. For this you can use Job diff(String feature, String wiki) method.

    /**
     * Computes the differences, in unified format, between the documents of an installed XAR extension and the document
     * from the wiki.
     *
     * @param feature the identifier of a XAR extension (or one of its features)
     * @param wiki the wiki where the XAR extension is installed
     * @return the {@link Job} object which can be used to monitor the progress while the differences are being
     *         computed, or {@code null} in case of failure
     * @since 7.0RC1
     */

   public Job diff(String feature, String wiki)

   /**
     * Get the id of the previously (or currently) computed differences, in unified format, between the documents of an
     * installed XAR extension and the document from the wiki..
     *
     * @param feature the identifier of a XAR extension (or one of its features)
     * @param namespace the namespace where the XAR extension is installed
     * @return the id of the {@link Job}
     * @since 9.3RC1
     */

   public List<String> getDiffJobId(String feature, String namespace)

Conflict handling

[since 9.2]

The script service allow getting all the possible types of conflict to can get when installing/upgrading an extension.

    /**
     * @return the possible conflicts
     * @since 9.2RC1
     */

   public ConflictQuestion.ConflictType[] getConflictTypes()

It's also possible to set the default answer (as one of the org.xwiki.extension.xar.question.GlobalAction enum entries) for each kind of conflict in the InstalRequest trough setProperty(String key, Object value) method:

  • extension.xar.packager.conflict.always.MERGE_SUCCESS: 3 ways merge succeed
  • extension.xar.packager.conflict.always.MERGE_FAILURE: 3 ways merge failed
  • extension.xar.packager.conflict.always.CURRENT_EXIST: Already existing different document in database but not in previous version of upgraded/uninstalled extension
  • extension.xar.packager.conflict.always.CURRENT_DELETED: Document already deleted in the database

Document to extension

[since 9.3]

The script service provide a set of API to link document to the installed extension where they originated from.

It's possible to:

  • get extensions associated to a specific document reference
  • it's possible to get a com.xpn.xwiki.api.Document instance of a document coming from a XAR package
  • reset a document to it's standard (as in what it looks like in the XAR package) version
    /**
     * @param reference the reference of the document to reset to its standard state (what it looks like in the
     *            extension XAR)
     * @param extensionId the installed extension from which to get the standard version of the document
     * @param jobId the id of the job which computed the diff if any
     * @return true if the reset actually did something, false otherwise (any produced error can be accessed using
     *         {@link #getLastError()})
     * @since 9.3RC1
     */

   public boolean reset(DocumentReference reference, ExtensionId extensionId, List<String> jobId)

   /**
     * @param reference the reference of the document to reset to its standard state (what it looks like in the
     *            extension XAR)
     * @param jobId the id of the job which computed the diff if any
     * @return true if the reset actually did something, false otherwise (any produced error can be accessed using
     *         {@link #getLastError()})
     * @since 9.3RC1
     */

   public boolean reset(DocumentReference reference, List<String> jobId)

   /**
     * @param reference the reference of the document
     * @return the installed XAR extensions in which this document can be found
     * @since 9.3RC1
     */

   public Collection<InstalledExtension> getInstalledExtensions(DocumentReference reference)

   /**
     * @param reference the reference of the document
     * @return a Document instance of passed document when extracted from the standard extension matching this
     *         reference. Null if none could be found.
     * @throws XarExtensionExtension when failing to get the document
     * @since 9.3RC1
     */

   public Document getInstalledExtensionDocument(DocumentReference reference) throws XarExtensionExtension

Extension pages protection

Since 10.4 extension pages are protected against edition and deletion. A warning is shown and you need to force edit or force delete for those protected pages. However it's possible for extension author to indicate pages which don't need protection (pages meant to be customized, configuration, etc.), see http://dev.xwiki.org/xwiki/bin/view/Community/XARPlugin#HDocumentstypes and http://extensions.xwiki.org/xwiki/bin/view/Extension/XAR+Module+Specifications#Hpackage.xml for details.

Since 10.5 several protection systems are provided and can be selected in xwiki.properties file using the property extension.xar.protection:

  • none: no protection at all
  • warning default: everyone get a warning when trying to edit a protected document
  • deny = EDIT/DELETE right is denied for everyone except for admins who just get a warning
  • forcedDeny = EDIT/DELETE right is denied for everyone, admins can't force edit/delete
  • denySimple = EDIT/DELETE right is denied for simple users except for simple admins who just get a warning
  • forcedDenySimple = EDIT/DELETE right is denied for all simple users, simple admins can't force edit/delete

Standard types

  • default: used to force the default. Edit and delete are not allowed and a 3-way merge is applied to the document during upgrades.
  • configuration: a document containing configuration. Edit is allowed and the document is never upgraded.
  • demo: a document which purpose is to provide demo data. Edit and delete are allowed and the document is upgraded only if it's not been customized.
  • customizable: a document meant to be modified but not deleted. Edit is allowed and the document is upgraded only if it's not been customized.

Also the following document references have a different default type:

  • XWiki.XWikiPreference: configuration
  • Main.WebHome: demo

Provide custom entry types

XWiki comes with standard XAR entry types but it's possible to extend this list with your own by implementing the component role org.xwiki.xar.XarEntryType.

Each XarEntryType component can indicate:

  • if edit is allowed for this type of document
  • if delete is allwoed for this type of document
  • the generic list of possible behavior to apply during an upgrade

Appart from an arbitrary entry type name (like "configuration" or "demo") a XarEntryType can be associated to a document reference in which case documents with this reference will be assigned this type unless a type was explicitly associated to it in the XAR extension:

@Component(hints = { MyXarEntryType.HINT, XarEntryTypeResolver.DOCUMENT_PREFIX + "MySpace.MyPage" })
@Singleton
public class MyXarEntryType extends AbstractXarEntryType
{
   /**
     * The name of the type.
     */

   public static final String HINT = "mytype";

   /**
     * Default constructor.
     */

   public MyXarEntryType()
   {
       super(HINT);

       // This kind of document should not be modified
       setEditAllowed(false);
       // Deleting this kind of document is fine
       setDeleteAllowed(true);

       // Don't upgrade the configuration even if the default configuration changed
       setUpgradeType(UpgradeType.SKIP_ALLWAYS);
   }
}

Customize upgrade behavior

10.3 introduced XAR entry types to customize install/upgrade behavior but XAR Extension also provide an extension point for more complex use cases: org.xwiki.extension.xar.XWikiDocumentMerger.

You can implement this role with one of the following hint:

  • the XAR entry type name ("home", "configuration", etc) to customize the behavior of this type of XAR entry
  • or the reference with document: prefix (for example document:XWiki.XWikiPreferences) to customize the behavior of this specific document

The method merge will will called every time a document need to be installed/upgraded the the 3 versions of the document (previous standard version, current version in the database, new standard version) and the XWikiDocumentMergerConfiguration which provide information about the type, conflict resolution, etc.

Get Connected