Code source wiki de MacroService

Modifié par Xavier Richard le 15/11/2022 à 22:52

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