Code source wiki de MacroService

Version 3.1 par Florent Charton le 2024/08/08 18:44

Masquer les derniers auteurs
superadmin 1.1 1 {{include reference="CKEditor.VelocityMacros" /}}
2
3 {{velocity output="false"}}
4 ## ================================================================
5 ## Returned JSON format:
6 ##
7 ## {
8 ## 'options': {
9 ## 'allMacrosExcludedCategories': [
10 ## (translated category name), ...
11 ## ]
12 ## },
13 ## 'list': [
14 ## {
15 ## 'id': (macro id),
16 ## 'name': (translated macro name),
17 ## 'description': (translated macro description),
18 ## 'defaultCategory': (translated macro category)
19 ## },
20 ## ...
21 ## ],
22 ## 'notinstalled': [
23 ## {
24 ## 'id': (macro id),
25 ## 'name': (translated macro name),
26 ## 'description': (translated macro description),
27 ## 'defaultCategory': '_notinstalled',
28 ## 'extensionId': (extension id)
29 ## 'extensionVersion': (extension version)
30 ## 'extensionType': (extension type)
31 ## 'extensionRecommended': (is extension recommended)
32 ## 'extensionName': (extension name)
33 ## 'extensionSummary': (extension summary)
34 ## ]
35 ## },
36 ## ...
37 ## ]
38 ## }
39 ## ================================================================
40 #macro (getMacroList $syntaxId)
Florent Charton 3.1 41
42 ## Loads the css resources to display the macros list
43 ## TODO: Refactor once we add support for loading css files from javascript without velocity.
44 #if ($xcontext.action == 'get')
45 #template('display_macros.vm')
46 #initRequiredSkinExtensions()
47 #end
48
49 #set ($discard = $xwiki.linkx.use($services.webjars.url('selectize.js', 'css/selectize.bootstrap3.css'),
50 {'type': 'text/css', 'rel': 'stylesheet'}))
51 #set ($discard = $xwiki.ssfx.use('uicomponents/suggest/xwiki.selectize.css', true))
52
53 #if ($xcontext.action == 'get')
54 #getRequiredSkinExtensions($requiredSkinExtensions)
55 #set ($discard = $response.setHeader('X-XWIKI-HTML-HEAD', $requiredSkinExtensions))
56 #end
57
superadmin 1.1 58 #set ($syntax = $services.rendering.resolveSyntax($syntaxId))
59 #set ($macroDescriptors = $services.rendering.getMacroDescriptors($syntax))
60 #if (!$macroDescriptors)
61 ## Before XWiki 9.7RC1 we had to use APIs that require programming rights.
62 #set ($macroDescriptors = [])
63 #try()
64 #set ($macroManager = $services.component.getInstance('org.xwiki.rendering.macro.MacroManager'))
65 #foreach ($macroId in $macroManager.getMacroIds($syntax))
66 #set ($macroDescriptor = $macroManager.getMacro($macroId).descriptor)
67 #set ($discard = $macroDescriptors.add($macroDescriptor))
68 #end
69 #end
70 #end
71 #set ($data = {})
72 #set ($allMacrosExcludedCategories = [])
73 #set ($discard = $allMacrosExcludedCategories.add("#maybeTranslate('rendering.macroCategory.Internal' 'Internal')"))
74 #set ($discard = $allMacrosExcludedCategories.add("#maybeTranslate('rendering.macroCategory.Deprecated' 'Deprecated')"))
75 #set ($discard = $data.put('options', { 'allMacrosExcludedCategories' : $allMacrosExcludedCategories }))
Florent Charton 3.1 76 ## If the current user do not want to display hidden documents, we initialize the set of hidden default
77 ## categories.
78 ## TODO: Make the list of hidden by default categories configurable from the administration (XWIKI-19993).
79 #if(!$services.user.getProperties().displayHiddenDocuments())
80 #set ($hiddenCategories = $services.rendering.getHiddenMacroCategories())
81 #else
82 #set ($hiddenCategories = [])
83 #end
superadmin 1.1 84 #set ($macroList = [])
Florent Charton 3.1 85 #set ($installedMacros = [])
superadmin 1.1 86 #foreach ($macroDescriptor in $macroDescriptors)
Florent Charton 3.1 87 #set ($discard = $installedMacros.add($macroDescriptor.id.id))
88
superadmin 1.1 89 #set ($macroTranslationKey = "rendering.macro.$macroDescriptor.id")
Florent Charton 3.1 90 #set ($categories = [])
91 #set ($hidden = false)
92 #foreach ($category in $services.rendering.getMacroCategories($macroDescriptor.id))
93 #set ($macroCategoryTranslationKey = "rendering.macroCategory.$category")
94 #set ($hidden = $hidden || $hiddenCategories.contains($category))
95 #set ($discard = $categories.add({
96 'id': $category,
97 'label': "#maybeTranslate($macroCategoryTranslationKey $category)"
98 }))
99 #end
100
101 #if (!$hidden)
102 #set ($defaultCategoryTranslation = "rendering.macroCategory.${macroDescriptor.defaultCategory}")
103 #set ($discard = $macroList.add({
104 'id': $macroDescriptor.id,
105 'name': "#maybeTranslate(""${macroTranslationKey}.name"" $macroDescriptor.name)",
106 'description': "#maybeTranslate(""${macroTranslationKey}.description"" $macroDescriptor.description)",
107 'defaultCategory': "#maybeTranslate($defaultCategoryTranslation $macroDescriptor.defaultCategory)",
108 'categories': $categories
109 }))
110 #end
superadmin 1.1 111 #end
112 #set ($macroList = $resolvedSortTool.sort($macroList, 'name'))
113 #set ($discard = $data.put('list', $macroList))
114 ## Get macros provided by compatible available extensions
115 #set ($macroExtensionsList = [])
116 #set($extensionQuery = $services.extension.index.newQuery("$!request.search"))
117 #set ($discard = $extensionQuery.addFilter('components__org.xwiki.rendering.macro.Macro', '', 'MATCH'))
118 #if ($xcontext.isMainWiki())
119 #set ($discard = $extensionQuery.setCompatible(true, '', "wiki:$xcontext.database"))
120 #else
121 #set ($discard = $extensionQuery.setCompatible(true, "wiki:$xcontext.database"))
122 #end
123 #set ($discard = $extensionQuery.setInstalled(false, '', "wiki:$xcontext.database"))
124 #set ($extensions = $services.extension.index.repository.search($extensionQuery))
125 #if ($extensions.size > 0)
126 #set ($macroExtensionsMap = {})
127 #foreach ($extension in $extensions)
128 ## TODO: move to a proper generic API to check if an extension can be installed by a given user
Florent Charton 2.1 129 #set ($extensionInstallAllowed = $services.security.authorization.hasAccess('programming', $xcontext.userReference, $NULL)
superadmin 1.1 130 || (($extension.type == 'xar' || $extension.type == 'webjar')
Florent Charton 2.1 131 && $services.security.authorization.hasAccess('admin', $xcontext.userReference, "wiki:$xcontext.database") && $services.extension.isAllowed($extension, "wiki:$xcontext.database")))
132 #foreach ($extensionComponent in $extension.getComponents())
133 #if ($extensionComponent.roleType == 'org.xwiki.rendering.macro.Macro')
Florent Charton 3.1 134 ## Skip macros identifiers for which a macro is already installed
135 #if (!$installedMacros.contains($extensionComponent.roleHint))
136 #set ($discard = $macroExtensionsList.add({
137 'id' : {
138 'id' : $extensionComponent.roleHint
139 },
140 'name': $extensionComponent.roleHint,
141 'description': $extension.summary,
142 'defaultCategory': '_notinstalled',
143 'categories': [{
144 'id' :'_notinstalled',
145 'label': $services.localization.render('macroSelector.filter.category.notinstalled')
146 }],
147 'extensionId' : $extension.id.id,
148 'extensionVersion' : $extension.id.version.value,
149 'extensionType' : $extension.type,
150 'extensionRecommended': $extension.recommended,
151 'extensionName': $extension.name,
152 'extensionSummary': $extension.summary,
153 'extensionInstallAllowed': $extensionInstallAllowed
154 }))
155 #end
superadmin 1.1 156 #end
157 #end
158 #end
159 #set ($discard = $data.put('notinstalled', $macroExtensionsList))
160 #end
161 #end
162
163 #macro (maybeGetMacroDescriptor $macroIdAsString)
164 #set ($xmacro = $NULL)
165 #set ($macroDescriptor = $NULL)
166 #set ($macroId = $services.rendering.resolveMacroId($macroIdAsString))
167 #if ($macroId)
168 #set ($macroDescriptor = $services.rendering.getMacroDescriptor($macroId))
169 #if (!$macroDescriptor && $macroId.syntax)
170 ## Try the macro id without the syntax.
171 #set ($macroId = $services.rendering.resolveMacroId($macroId.id))
172 #set ($macroDescriptor = $services.rendering.getMacroDescriptor($macroId))
173 #end
174 #else
175 ## Either the macro id could not be resolved (unlikely) or we are on an older XWiki instance (before 10.10RC1) where
176 ## we had to use APIs that require programming rights.
177 #getMacroWithPR($macroIdAsString)
178 #if ($xmacro)
179 #set ($macroDescriptor = $xmacro.descriptor)
180 #end
181 #end
182 #if ($macroDescriptor)
183 #getMacroDescriptor($macroDescriptor)
184 #if ($xmacro)
185 ## supportsInlineMode was not exposed on the macro descriptor before XWiki 10.10RC1.
186 #set ($data.supportsInlineMode = $xmacro.supportsInlineMode())
187 #end
188 #end
189 #end
190
191 #macro (getMacroWithPR $macroIdAsString)
192 #set ($xmacro = $NULL)
193 #try()
194 #set ($macroIdFactory = $services.component.getInstance('org.xwiki.rendering.macro.MacroIdFactory'))
195 #set ($macroId = $macroIdFactory.createMacroId($macroIdAsString))
196 #set ($macroManager = $services.component.getInstance('org.xwiki.rendering.macro.MacroManager'))
197 #if ($macroManager.exists($macroId))
198 #set ($xmacro = $macroManager.getMacro($macroId))
199 #elseif ($macroId.syntax)
200 ## Try the macro id without the syntax.
201 #set ($macroId = $macroIdFactory.createMacroId($macroId.id))
202 #if ($macroManager.exists($macroId))
203 #set ($xmacro = $macroManager.getMacro($macroId))
204 #end
205 #end
206 #end
207 #end
208
209 #macro (getMacroDescriptor $macroDescriptor)
210 ## Translate the macro name and description.
211 #set ($macroTranslationKey = "rendering.macro.$macroDescriptor.id")
212 #ckeditor_initRequiredSkinExtensions()
213 #set ($data = {
214 'id': $macroDescriptor.id,
215 'name': "#maybeTranslate(""${macroTranslationKey}.name"" $macroDescriptor.name)",
216 'description': "#maybeTranslate(""${macroTranslationKey}.description"" $macroDescriptor.description)",
217 'defaultCategory': $macroDescriptor.defaultCategory,
218 'supportsInlineMode': $macroDescriptor.supportsInlineMode(),
219 'parameterDescriptorMap': {}
220 })
221 #if ($macroDescriptor.contentDescriptor)
222 ## Translate the content label and description.
223 ## Treat the macro content as if it is the last macro parameter.
224 #set ($data.contentDescriptor = {
225 'name': "#maybeTranslate('rendering.macroContent' 'Content')",
226 'description': "#maybeTranslate(""${macroTranslationKey}.content.description""
227 $macroDescriptor.contentDescriptor.description)",
228 'mandatory': $macroDescriptor.contentDescriptor.mandatory,
229 'deprecated': $macroDescriptor.contentDescriptor.deprecated,
230 'advanced': $macroDescriptor.contentDescriptor.advanced,
231 'defaultValue': $macroDescriptor.contentDescriptor.defaultValue,
232 'type': $macroDescriptor.contentDescriptor.type,
233 'editTemplate': '<textarea name="$content" rows="7"></textarea>',
234 'index': $macroDescriptor.parameterDescriptorMap.size()
235 })
236 #fixDescriptorType($data.contentDescriptor)
237 #end
238 #set ($groupDescriptorTree = {})
239 #foreach ($entry in $macroDescriptor.parameterDescriptorMap.entrySet())
240 #set ($parameterDescriptor = $entry.value)
241 ## Translate the parameter name and description.
242 #set ($parameterTranslationKey = "${macroTranslationKey}.parameter.$parameterDescriptor.id")
243 ## Note: The displayHidden parameter is new in XWiki 12.4RC1 so make sure we set 'hidden' to false if it doesn't
244 ## exist
245 #if ("$!parameterDescriptor.displayHidden" != '')
246 #set ($parameterHidden = $parameterDescriptor.displayHidden)
247 #else
248 #set ($parameterHidden = false)
249 #end
250 #set ($translatedParameterDescriptor = {
251 'id': $parameterDescriptor.id,
252 'name': "#maybeTranslate(""${parameterTranslationKey}.name"" $parameterDescriptor.name)",
253 'description': "#maybeTranslate(""${parameterTranslationKey}.description"" $parameterDescriptor.description)",
254 'mandatory': $parameterDescriptor.mandatory,
255 'deprecated': $parameterDescriptor.deprecated,
256 'advanced': $parameterDescriptor.advanced,
257 'defaultValue': $parameterDescriptor.defaultValue,
258 'type': $parameterDescriptor.displayType,
259 'hidden' : $parameterHidden,
260 'index': $foreach.index
261 })
262 #if ("$!translatedParameterDescriptor.type" == '')
263 ## displayType is not available before XWiki 11.0 so we need to fall back on parameterType.
264 #set ($translatedParameterDescriptor.type = $parameterDescriptor.parameterType)
265 #end
266 #set ($translatedParameterDescriptor.caseInsensitive = $translatedParameterDescriptor.type.isEnum())
267 #set ($groupDescriptor = $parameterDescriptor.groupDescriptor)
268 #if ($groupDescriptor)
269 #handleMacroParameterGroup($groupDescriptor $groupDescriptorTree $translatedParameterDescriptor)
270 #end
271 #if ($translatedParameterDescriptor.type.getName() == 'java.lang.String'
272 && ($parameterDescriptor.defaultValue == 'false' || $parameterDescriptor.defaultValue == 'true')
273 && $macroDescriptor.parametersBeanClass.getSimpleName() == 'WikiMacroParameters')
274 #set ($translatedParameterDescriptor.defaultValue = $parameterDescriptor.defaultValue == 'true')
275 #set ($translatedParameterDescriptor.type = $translatedParameterDescriptor.defaultValue.getClass())
276 #end
277 #set ($htmlDisplayerParameters = {'name': $parameterDescriptor.id})
278 #if ($translatedParameterDescriptor.group)
279 #set ($discard = $htmlDisplayerParameters.put('data-property-group',
280 $stringtool.join($translatedParameterDescriptor.group, '/')))
281 #end
282 #set ($translatedParameterDescriptor.editTemplate = $services.display.html.display(
283 $translatedParameterDescriptor.type, $translatedParameterDescriptor.defaultValue, $htmlDisplayerParameters, 'edit'
284 ))
285 #if ("$!translatedParameterDescriptor.editTemplate" == '')
286 #set ($translatedParameterDescriptor.editTemplate = "#getMacroParameterEditTemplate(
287 $translatedParameterDescriptor)")
288 #end
289 #set ($translatedParameterDescriptor.editTemplate = $translatedParameterDescriptor.editTemplate.trim())
290 #fixDescriptorType($translatedParameterDescriptor)
291 ## Make sure the key is lowercase (for XWiki <9.0).
292 ## See XWIKI-13990: Inconsistency between Java-based and Wiki-based rendering macros regarding the parameter
293 ## descriptor map keys
294 #set ($discard = $data.parameterDescriptorMap.put($entry.key.toLowerCase(), $translatedParameterDescriptor))
295 #end
296 #if ($groupDescriptorTree.groups)
297 #set ($data.groupDescriptorTree = $groupDescriptorTree.groups)
298 #end
299 #set ($data.requiredSkinExtensions = "#ckeditor_getRequiredSkinExtensions()")
300 #end
301
302 #macro (fixDescriptorType $descriptor)
303 ## The goal of this code is to obtain a normalized string representation of the type specified in the descriptor.
304 ## See XCOMMONS-1583: Define a stable way to serialize types
305 ##
306 ## The type specified in the descriptor can be any implementation of java.lang.reflect.Type, not necessarily a
307 ## java.lang.Class. We can't use toString() because the return of Class#toString() is different than Class#getName().
308 ## We can't use Type#getTypeName() either because the access to this method is restricted from Velocity. The only
309 ## option for now is to try #getName() first and fall back on #toString() for types that are not instances of
310 ## java.lang.Class.
311 #set ($typeName = $descriptor.type.getName())
312 #if ("$!typeName" == '')
313 ## Probably not a java.lang.Class. Fall back on #toString().
314 #set ($typeName = "$!descriptor.type")
315 #end
316 ## Remove whitespace from the type name in order to have a single string representation.
317 #set ($descriptor.type = $typeName.replaceAll('\s+', ''))
318 #end
319
320 ## Builds the group tree with the following structure:
321 ##
322 ## {
323 ## 'parentGroupId': {
324 ## 'id': 'parentGroupId',
325 ## 'name': 'Parent Group',
326 ## 'feature': 'someFeature',
327 ## 'groups': {
328 ## 'childGroupId': {...},
329 ## ...
330 ## }
331 ## },
332 ## ...
333 ## }
334 #macro (handleMacroParameterGroup $groupDescriptor $groupDescriptorTree $translatedParameterDescriptor)
335 #if ($groupDescriptor.group && $groupDescriptor.group.size() > 0)
336 #set ($translatedParameterDescriptor.group = $groupDescriptor.group)
337 #set ($parentGroup = $groupDescriptorTree)
338 #foreach ($groupId in $groupDescriptor.group)
339 #if (!$parentGroup.groups)
340 #set ($parentGroup.groups = {})
341 #end
342 #set ($childGroup = $parentGroup.groups.get($groupId))
343 #if (!$childGroup)
344 #if ($groupId == $translatedParameterDescriptor.id)
345 #set ($groupName = $translatedParameterDescriptor.name)
346 #else
347 #set ($groupTranslationKey = "${macroTranslationKey}.group.$groupId")
348 #set ($groupName = "#maybeTranslate(""${groupTranslationKey}.name"" $groupId)")
349 #end
350 #set ($childGroup = {
351 'id': $groupId,
352 'name': $groupName
353 })
354 #set ($discard = $parentGroup.groups.put($groupId, $childGroup))
355 #end
356 #set ($parentGroup = $childGroup)
357 #end
358 #if ("$!groupDescriptor.feature" != '')
359 #set ($parentGroup.feature = $groupDescriptor.feature)
360 #end
361 #elseif ($groupDescriptor.feature)
362 ## This group is made of a single parameter. The feature then refers to this parameter.
363 #set ($translatedParameterDescriptor.feature = $groupDescriptor.feature)
364 #end
365 #end
366
367 #macro (getMacroParameterEditTemplate $translatedParameterDescriptor)
368 #if ($translatedParameterDescriptor.type.getName() == 'boolean'
369 || $translatedParameterDescriptor.type.getName() == 'java.lang.Boolean')
370 <input type="checkbox" name="$escapetool.xml($translatedParameterDescriptor.id)" value="true"/>##
371 ## We need to submit something in case the checkbox is not checked.
372 <input type="hidden" name="$escapetool.xml($translatedParameterDescriptor.id)" value="false"/>
373 #elseif ($translatedParameterDescriptor.type.isEnum())
374 #if ($translatedParameterDescriptor.defaultValue)
375 #set ($enumValues = $translatedParameterDescriptor.defaultValue.values())
376 #else
377 ## A parameter of type enum that doesn't have a default value is very unlikely. We attempt to read the list of
378 ## possible values from the enum type in this case, which is currently forbidden, but at least it will generate
379 ## a warning in the logs that will help us investigate the problem.
380 #set ($enumValues = $translatedParameterDescriptor.type.getEnumConstants())
381 #end
382 <select name="$escapetool.xml($translatedParameterDescriptor.id)">##
383 #foreach ($enumValue in $enumValues)
384 #set ($value = $enumValue.name())
385 #set ($label = "#maybeTranslate(""${parameterTranslationKey}.value.$value"" $enumValue)")
386 <option value="$escapetool.xml($value)">$escapetool.xml($label)</option>##
387 #end
388 </select>
389 #else
390 <input type="text" name="$escapetool.xml($translatedParameterDescriptor.id)"/>
391 #end
392 #end
393
394 #macro (maybeTranslate $key $defaultValue)
395 #if ($services.localization.get($key))
396 $services.localization.render($key)##
397 #else
398 $!defaultValue##
399 #end
400 #end
401
402 #macro (installMacroExtension $extensionId, $extensionVersion)
403 #set ($extension = $services.extension.index.repository.resolve("$extensionId/$extensionVersion"))
404 #if ($extension)
405 ## Find where to install it
406 ## 1) Check if a diffferent version is already installed
407 ## 2) Check if it's allowed to install it at current wiki level
408 #set ($rootNamespace = $NULL)
409 #set ($currentWikiNamespace = "wiki:$xcontext.database")
410 #if ($services.extension.installed.getInstalledExtension($extensionId, $rootNamespace))
411 #set ($extensionNamespace = $rootNamespace)
412 #elseif ($services.extension.installed.getInstalledExtension($extensionId, $currentWikiNamespace))
413 #set ($extensionNamespace = $currentWikiNamespace)
414 #else
415 #if ($services.extension.isAllowed($extension, "wiki:$xcontext.database"))
416 #set ($extensionNamespace = $currentWikiNamespace)
417 #else
418 #set ($extensionNamespace = $NULL)
419 #end
420 #end
421 ## Make the install non interractive
422 #set ($installRequest = $services.extension.createInstallRequest($extensionId, $extensionVersion, $extensionNamespace))
423 #set ($discard = $installRequest.setInteractive(false))
424 ## Start the install
425 #set ($job = $services.extension.install($installRequest))
426 ## Wait for the job to finish
427 #set ($discard = $job.join())
428 #if ($job.status.error)
429 ## The install failed
430 $response.sendError(500, $exceptiontool.getRootCauseMessage($job.status.error))
431 #else
432 ## The install succeeded
433 #set ($data = {
434 'extensionId': $extensionId,
435 'extensionVersion': $extensionVersion,
436 'extensionNamespace': $extensionNamespace
437 })
438 #end
439 #else
440 $response.sendError(404, $exceptiontool.getRootCauseMessage($job.status.error))
441 #end
442 #end
443 {{/velocity}}
444
445 {{velocity wiki="false"}}
446 #if ("$!request.action" == 'install')
447 #if ($services.csrf.isTokenValid($request.form_token))
448 #installMacroExtension($request.extensionId, $request.extensionVersion)
449 #else
450 $response.sendError(403)
451 #end
452 #elseif ("$!request.data" != '')
453 #set ($data = $NULL)
454 #if ($request.data == 'list')
455 #getMacroList($request.syntaxId)
456 #elseif ($request.data == 'descriptor')
457 #maybeGetMacroDescriptor($request.macroId)
458 #end
459 #if ($data)
460 #set ($discard = $response.setContentType('application/json'))
461 $jsontool.serialize($data)
462 #else
463 $response.sendError(404)
464 #end
465 #end
466 {{/velocity}}