Wiki source code of XAR Extensions
Last modified by Thomas Mortagne on 2021/03/02 18:22
Show last authors
author | version | line-number | content |
---|---|---|---|
1 | Handling of XAR packages as defined in [[specifications>>Extension.XAR Module Specifications]]. | ||
2 | |||
3 | {{toc/}} | ||
4 | |||
5 | = Script service = | ||
6 | |||
7 | The XAR extension handler expose a ScriptService component for XAR extension specific features. | ||
8 | |||
9 | == XAR Repair == | ||
10 | |||
11 | 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. | ||
12 | |||
13 | To recreate index for those extension the script service provide ##Job repairInstalledExtension(String id, String version, String wiki)## API. | ||
14 | |||
15 | {{code language="java"}} | ||
16 | /** | ||
17 | * Make sure the provided XAR extension properly is registered in the installed extensions index. | ||
18 | * <p> | ||
19 | * Start an asynchronous Job. | ||
20 | * | ||
21 | * @param id the extension identifier | ||
22 | * @param version the extension version | ||
23 | * @param wiki the wiki where the extension is installed | ||
24 | * @return the {@link Job} object which can be used to monitor the progress of the installation process, or | ||
25 | * {@code null} in case of failure | ||
26 | */ | ||
27 | public Job repairInstalledExtension(String id, String version, String wiki) | ||
28 | {{/code}} | ||
29 | |||
30 | == Extension Diff == | ||
31 | |||
32 | [since 7.0] | ||
33 | |||
34 | 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. | ||
35 | |||
36 | {{code language="java"}} | ||
37 | /** | ||
38 | * Computes the differences, in unified format, between the documents of an installed XAR extension and the document | ||
39 | * from the wiki. | ||
40 | * | ||
41 | * @param feature the identifier of a XAR extension (or one of its features) | ||
42 | * @param wiki the wiki where the XAR extension is installed | ||
43 | * @return the {@link Job} object which can be used to monitor the progress while the differences are being | ||
44 | * computed, or {@code null} in case of failure | ||
45 | * @since 7.0RC1 | ||
46 | */ | ||
47 | public Job diff(String feature, String wiki) | ||
48 | |||
49 | /** | ||
50 | * Get the id of the previously (or currently) computed differences, in unified format, between the documents of an | ||
51 | * installed XAR extension and the document from the wiki.. | ||
52 | * | ||
53 | * @param feature the identifier of a XAR extension (or one of its features) | ||
54 | * @param namespace the namespace where the XAR extension is installed | ||
55 | * @return the id of the {@link Job} | ||
56 | * @since 9.3RC1 | ||
57 | */ | ||
58 | public List<String> getDiffJobId(String feature, String namespace) | ||
59 | {{/code}} | ||
60 | |||
61 | == Conflict handling == | ||
62 | |||
63 | [since 9.2] | ||
64 | |||
65 | The script service allow getting all the possible types of conflict to can get when installing/upgrading an extension. | ||
66 | |||
67 | {{code language="java"}} | ||
68 | /** | ||
69 | * @return the possible conflicts | ||
70 | * @since 9.2RC1 | ||
71 | */ | ||
72 | public ConflictQuestion.ConflictType[] getConflictTypes() | ||
73 | {{/code}} | ||
74 | |||
75 | 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: | ||
76 | |||
77 | * ##extension.xar.packager.conflict.always.MERGE_SUCCESS##: 3 ways merge succeed | ||
78 | * ##extension.xar.packager.conflict.always.MERGE_FAILURE##: 3 ways merge failed | ||
79 | * ##extension.xar.packager.conflict.always.CURRENT_EXIST##: Already existing different document in database but not in previous version of upgraded/uninstalled extension | ||
80 | * ##extension.xar.packager.conflict.always.CURRENT_DELETED##: Document already deleted in the database | ||
81 | |||
82 | == Document to extension == | ||
83 | |||
84 | [since 9.3] | ||
85 | |||
86 | The script service provide a set of API to link document to the installed extension where they originated from. | ||
87 | |||
88 | It's possible to: | ||
89 | |||
90 | * get extensions associated to a specific document reference | ||
91 | * it's possible to get a ##com.xpn.xwiki.api.Document## instance of a document coming from a XAR package | ||
92 | * reset a document to it's standard (as in what it looks like in the XAR package) version | ||
93 | |||
94 | {{code language="java"}} | ||
95 | /** | ||
96 | * @param reference the reference of the document to reset to its standard state (what it looks like in the | ||
97 | * extension XAR) | ||
98 | * @param extensionId the installed extension from which to get the standard version of the document | ||
99 | * @param jobId the id of the job which computed the diff if any | ||
100 | * @return true if the reset actually did something, false otherwise (any produced error can be accessed using | ||
101 | * {@link #getLastError()}) | ||
102 | * @since 9.3RC1 | ||
103 | */ | ||
104 | public boolean reset(DocumentReference reference, ExtensionId extensionId, List<String> jobId) | ||
105 | |||
106 | /** | ||
107 | * @param reference the reference of the document to reset to its standard state (what it looks like in the | ||
108 | * extension XAR) | ||
109 | * @param jobId the id of the job which computed the diff if any | ||
110 | * @return true if the reset actually did something, false otherwise (any produced error can be accessed using | ||
111 | * {@link #getLastError()}) | ||
112 | * @since 9.3RC1 | ||
113 | */ | ||
114 | public boolean reset(DocumentReference reference, List<String> jobId) | ||
115 | |||
116 | /** | ||
117 | * @param reference the reference of the document | ||
118 | * @return the installed XAR extensions in which this document can be found | ||
119 | * @since 9.3RC1 | ||
120 | */ | ||
121 | public Collection<InstalledExtension> getInstalledExtensions(DocumentReference reference) | ||
122 | |||
123 | /** | ||
124 | * @param reference the reference of the document | ||
125 | * @return a Document instance of passed document when extracted from the standard extension matching this | ||
126 | * reference. Null if none could be found. | ||
127 | * @throws XarExtensionExtension when failing to get the document | ||
128 | * @since 9.3RC1 | ||
129 | */ | ||
130 | public Document getInstalledExtensionDocument(DocumentReference reference) throws XarExtensionExtension | ||
131 | {{/code}} | ||
132 | |||
133 | = Extension pages protection = | ||
134 | |||
135 | 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. | ||
136 | |||
137 | Since 10.5 several protection systems are provided and can be selected in ##xwiki.properties## file using the property ##extension.xar.protection##: | ||
138 | |||
139 | * ##none##: no protection at all | ||
140 | * ##warning## {{info}}default{{/info}}: everyone get a warning when trying to edit a protected document | ||
141 | * ##deny## = EDIT/DELETE right is denied for everyone except for admins who just get a warning | ||
142 | * ##forcedDeny## = EDIT/DELETE right is denied for everyone, admins can't force edit/delete | ||
143 | * ##denySimple## = EDIT/DELETE right is denied for simple users except for simple admins who just get a warning | ||
144 | * ##forcedDenySimple## = EDIT/DELETE right is denied for all simple users, simple admins can't force edit/delete | ||
145 | |||
146 | = Standard types | ||
147 | |||
148 | * ##default##: used to force the default. Edit and delete are not allowed and a 3-way merge is applied to the document during upgrades. | ||
149 | * ##configuration##: a document containing configuration. Edit is allowed and the document is never upgraded. | ||
150 | * ##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. | ||
151 | * ##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. | ||
152 | |||
153 | Also the following document references have a different default type: | ||
154 | * ##XWiki.XWikiPreference##: ##configuration## | ||
155 | * ##Main.WebHome##: ##demo## | ||
156 | |||
157 | = Provide custom entry types = | ||
158 | |||
159 | 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##. | ||
160 | |||
161 | Each XarEntryType component can indicate: | ||
162 | * if edit is allowed for this type of document | ||
163 | * if delete is allwoed for this type of document | ||
164 | * the generic list of possible behavior to apply during an upgrade | ||
165 | |||
166 | 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: | ||
167 | |||
168 | {{code language="java"}} | ||
169 | @Component(hints = { MyXarEntryType.HINT, XarEntryTypeResolver.DOCUMENT_PREFIX + "MySpace.MyPage" }) | ||
170 | @Singleton | ||
171 | public class MyXarEntryType extends AbstractXarEntryType | ||
172 | { | ||
173 | /** | ||
174 | * The name of the type. | ||
175 | */ | ||
176 | public static final String HINT = "mytype"; | ||
177 | |||
178 | /** | ||
179 | * Default constructor. | ||
180 | */ | ||
181 | public MyXarEntryType() | ||
182 | { | ||
183 | super(HINT); | ||
184 | |||
185 | // This kind of document should not be modified | ||
186 | setEditAllowed(false); | ||
187 | // Deleting this kind of document is fine | ||
188 | setDeleteAllowed(true); | ||
189 | |||
190 | // Don't upgrade the configuration even if the default configuration changed | ||
191 | setUpgradeType(UpgradeType.SKIP_ALLWAYS); | ||
192 | } | ||
193 | } | ||
194 | {{/code}} | ||
195 | |||
196 | = Customize upgrade behavior = | ||
197 | |||
198 | 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##. | ||
199 | |||
200 | You can implement this role with one of the following hint: | ||
201 | |||
202 | * the XAR entry type name ("home", "configuration", etc) to customize the behavior of this type of XAR entry | ||
203 | * or the reference with ##document:## prefix (for example ##document:XWiki.XWikiPreferences##) to customize the behavior of this specific document | ||
204 | |||
205 | 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. |