Исходный код вики Document Tree Macros

Редактировал(а) Андрей Ганьков 2025/07/06 04:36

Последние авторы
1 {{include reference="XWiki.SuggestSolrMacros" /}}
2
3 {{template name="documentTree_macros.vm" /}}
4
5 {{velocity output="false"}}
6 #macro (updateDocTreeConfigFromRequest)
7 #foreach ($entry in $docTreeConfig.entrySet())
8 #set ($valueFromRequest = $request.getParameter($entry.key))
9 #if ("$!valueFromRequest" != '')
10 #if ($entry.value.getClass().getName() == 'java.lang.Boolean')
11 #set ($entry.value = $valueFromRequest == 'true')
12 #elseif ($entry.value.iterator())
13 #set ($valuesFromRequest = $request.getParameterValues($entry.key))
14 #set ($discard = $entry.value.clear())
15 ## We need to convert the String[] to List<String> before calling addAll (which expects a collection).
16 #set ($discard = $entry.value.addAll($valuesFromRequest.subList(0, $valuesFromRequest.size())))
17 #else
18 #set ($entry.value = $valueFromRequest)
19 #end
20 #end
21 #end
22 ## Show the wikis only for global users.
23 #set ($docTreeConfig.showWikis = $docTreeConfig.showWikis &&
24 $xcontext.userReference.wikiReference.name == $xcontext.mainWikiName)
25 #if ("$!docTreeConfig.root" == '')
26 #if ($docTreeConfig.showWikis)
27 #set ($docTreeConfig.root = 'farm:*')
28 #else
29 #set ($docTreeConfig.root = "wiki:$xcontext.database")
30 #end
31 #end
32 ## Handle relative references
33 #makeNodeReferencesAbsolute($docTreeConfig ['root', 'openTo'])
34 ## FIXME: The 'orderBy' property of the tree API is shared by all tree node types, which means we can't indicate a
35 ## different sort field per tree node type (e.g. sort wiki nodes by name and document nodes by last modification
36 ## date). At the same time, this property is currently taken into account only for sorting document tree nodes, so for
37 ## now we set its value to the specified document sort. In the future we may want to convert this into a map, where
38 ## the key is the node type.
39 #set ($docTreeConfig.orderBy = $docTreeConfig.sortDocumentsBy)
40 ## Sort the child documents by (raw) title when the node label is the document title and there's no sort specified.
41 #if ($docTreeConfig.showDocumentTitle && "$!docTreeConfig.orderBy" == '')
42 #set ($docTreeConfig.orderBy = 'title:asc')
43 #end
44 ## Determine which hierarchy needs to be used.
45 #if ($docTreeConfig.showSpaces)
46 #if ($docTreeConfig.hierarchyMode == 'parentchild')
47 #set ($tree = $services.tree.parentChildOnNestedSpaces)
48 #else
49 #set ($tree = $services.tree.nestedSpaces)
50 #end
51 #elseif ($docTreeConfig.hierarchyMode == 'parentchild')
52 #set ($tree = $services.tree.parentChild)
53 #else
54 #set ($tree = $services.tree.nestedPages)
55 #end
56 #set ($discard = $tree.properties.putAll($docTreeConfig))
57 #end
58
59 #set ($documentPseudoNodeTypes = ['translations', 'attachments', 'classProperties', 'objects', 'addDocument',
60 'addAttachment'])
61 #macro (makeNodeReferencesAbsolute $map $keys)
62 #foreach ($key in $keys)
63 #set ($nodeId = $map.get($key))
64 #set ($parts = $nodeId.split(':', 2))
65 #if ($parts && $parts.size() == 2)
66 #set ($nodeType = $parts[0].toLowerCase())
67 #set ($nodeReference = $parts[1])
68 #set ($entityType = $nodeType)
69 #if ($documentPseudoNodeTypes.contains($nodeType))
70 #set ($entityType = 'document')
71 #end
72 #set ($discard = "#evaluate(""${escapetool.h}set (${escapetool.d}entityReference =
73 ${escapetool.d}services.model.resolve$stringtool.capitalize($entityType)(${escapetool.d}nodeReference))"")")
74 #if ($entityReference)
75 #set ($nodeReference = $services.model.serialize($entityReference, 'default'))
76 #end
77 #set ($discard = $map.put($key, "$nodeType:$nodeReference"))
78 #end
79 #end
80 #end
81
82 #macro (handleDocumentTreeRequest)
83 #if ($request.action)
84 #if ($services.csrf.isTokenValid($request.form_token))
85 #if ($request.action == 'create' && $request.type == 'addDocument')
86 #handleNewNodeCreationRequest()
87 #else
88 $response.sendError(400, 'The specified action is not supported.')
89 #end
90 #elseif ($isAjaxRequest)
91 $response.sendError(403, 'The CSRF token is missing.')
92 #else
93 $response.sendRedirect($services.csrf.getResubmissionURL())
94 #end
95 #else
96 #set ($data = $NULL)
97 #if ($request.data == 'children')
98 #getChildren($request.id $data)
99 #elseif ($request.data == 'path')
100 #getPath($request.id $data)
101 #elseif ($request.data == 'contextMenu')
102 #getContextMenu($data)
103 #elseif ($request.data == 'suggestions')
104 #getSuggestions($data)
105 #end
106 #if ($data)
107 #postProcessDocumentTreeData($data)
108 #jsonResponse($data)
109 #else
110 $response.sendError(404)
111 #end
112 #end
113 #end
114
115 #macro (handleNewNodeCreationRequest)
116 #set ($cleanId = $stringtool.substring($request.id, $stringtool.length('document:')))
117 #set ($parentReference = $services.model.resolveDocument($cleanId))
118 #set ($requestedName = $request.name)
119 #set ($transformedName = $services.modelvalidation.transformName($requestedName))
120 #set ($spaceReference = $services.model.createSpaceReference($transformedName, $parentReference.lastSpaceReference))
121 #set ($documentReference = $services.model.createDocumentReference('WebHome', $spaceReference))
122 #set ($data = [])
123 #addDocumentNode($documentReference, $data)
124 ## We want to allow opening the node to add another hierarchy.
125 #set ($data[0].children = true)
126 ## We want to display the actual requested name as node name.
127 #set ($data[0].text = $requestedName)
128 #jsonResponse($data)
129 #end
130
131 #macro (postProcessDocumentTreeData $data)
132 ## This is just a hook to allow post processing the document tree data.
133 #end
134
135 ##------------------------------------------------------------
136 ## Children
137 ##------------------------------------------------------------
138
139 #macro (getChildren $nodeId $return)
140 #set ($children = [])
141 #if ($nodeId == '#')
142 ## Return the top level nodes.
143 #set ($actualNodeId = $docTreeConfig.root)
144 #else
145 ## Return the children of the specified node.
146 #set ($actualNodeId = $nodeId)
147 #end
148 #set ($offset = $mathtool.max($numbertool.toNumber($request.offset).intValue(), 0))
149 #if ("$!offset" == '')
150 #set ($offset = 0)
151 #end
152 #set ($limit = $mathtool.max($numbertool.toNumber($request.limit).intValue(), 1))
153 #if ("$!limit" == '')
154 #set ($limit = 15)
155 #end
156 #if ($nodeId == '#' && $docTreeConfig.showRoot)
157 #maybeAddNode($actualNodeId $children)
158 #else
159 #addChildNodes($actualNodeId $offset $limit $children)
160 #end
161 #if ($children.isEmpty() && $nodeId == '#')
162 ## Inform the user that the tree is empty.
163 #addEmptyTreeNode($children)
164 #end
165 #set ($return = $NULL)
166 #setVariable("$return" $children)
167 #end
168
169 #macro (maybeAddNode $nodeId $siblings $placeholder)
170 #set ($parts = $nodeId.split(':', 2))
171 #if ($parts && $parts.size() == 2)
172 #set ($nodeType = $parts[0])
173 #set ($nodeReference = $parts[1])
174 #set ($discard = "#evaluate(""${escapetool.h}maybeAdd$stringtool.capitalize($nodeType)Node(
175 ${escapetool.d}nodeReference ${escapetool.d}siblings ${escapetool.d}placeholder)"")")
176 #end
177 #end
178
179 #macro (addChildNodes $nodeId $offset $limit $children)
180 ## Avoid pages with only one node when paginating the child nodes.
181 #set ($actualLimit = $limit + 1)
182 #set ($childNodeIds = $tree.getChildren($nodeId, $offset, $actualLimit))
183 #set ($hasMoreChildNodes = false)
184 #if ($childNodeIds.size() >= $actualLimit)
185 #set ($totalCount = $tree.getChildCount($nodeId))
186 #set ($newOffset = $offset + $actualLimit)
187 #if ($newOffset < $totalCount)
188 ## There are at least 2 more child nodes.
189 #set ($hasMoreChildNodes = true)
190 #set ($newOffset = $newOffset - 1)
191 #set ($childNodeIds = $childNodeIds.subList(0, $limit))
192 #end
193 #end
194 #foreach ($childNodeId in $childNodeIds)
195 #maybeAddNode($childNodeId $children)
196 #end
197 #if ($hasMoreChildNodes)
198 #addPaginationNode($nodeId $newOffset $totalCount $children)
199 #end
200 #end
201
202 ##
203 ## Farm Node
204 ##
205
206 #macro (maybeAddFarmNode $nodeReference $siblings)
207 #set ($farmHomeReference = $services.model.resolveDocument('', 'default'))
208 #set ($isOpened = $docTreeConfig.expandToLevel > 0)
209 #set ($discard = $siblings.add({
210 'id': 'farm:*',
211 'text': 'Farm',
212 'icon': 'fa fa-home',
213 'children': true,
214 'data': {
215 'type': 'farm',
216 'validChildren': ['wiki', 'pagination']
217 },
218 'state': {
219 'opened': $isOpened
220 },
221 'a_attr': {
222 'href': $xwiki.getURL($farmHomeReference)
223 }
224 }))
225 #end
226
227 ##
228 ## Wiki Nodes
229 ##
230
231 #macro (maybeAddWikiNode $wikiId $siblings $placeholder)
232 #set ($wiki = $services.wiki.getById($wikiId))
233 #if ($wiki && (!$docTreeConfig.showOnlyViewable || $services.security.authorization.hasAccess('view', $wiki.reference)))
234 #addWikiNode($wiki $siblings)
235 #elseif ($placeholder)
236 #set ($discard = $siblings.add($placeholder))
237 #end
238 #end
239
240 #macro (addWikiNode $wiki $siblings)
241 ## The main wiki cannot be deleted. For the rest we need special rights.
242 #set ($canDeleteWiki = $wiki.id != $services.wiki.mainWikiId
243 && $services.wiki.canDeleteWiki($xcontext.user, $wiki.id))
244 #if ($docTreeConfig.showWikiPrettyName)
245 #set ($label = $wiki.prettyName)
246 #else
247 #set ($label = $wiki.id)
248 #end
249 #set ($isOpened = $docTreeConfig.expandToLevel > 0)
250 #set ($discard = $siblings.add({
251 'id': "wiki:$wiki.id",
252 'text': $label,
253 'icon': 'fa fa-hdd-o',
254 'children': true,
255 'data': {
256 'id': $wiki.id,
257 'type': 'wiki',
258 'validChildren': ['space', 'document', 'pagination'],
259 'canDelete': $canDeleteWiki
260 },
261 'state': {
262 'opened': $isOpened
263 },
264 'a_attr': {
265 'href': $xwiki.getURL($wiki.mainPageReference)
266 }
267 }))
268 #end
269
270 ##
271 ## Space Nodes
272 ##
273
274 #macro (maybeAddSpaceNode $spaceIdOrReference $siblings $placeholder)
275 #if ($spaceIdOrReference.type)
276 #set ($spaceReference = $spaceIdOrReference)
277 #else
278 #set ($spaceReference = $services.model.resolveSpace($spaceIdOrReference))
279 #end
280 #if (!$docTreeConfig.showOnlyViewable || $services.security.authorization.hasAccess('view', $spaceReference))
281 #addSpaceNode($spaceReference $siblings)
282 #elseif ($placeholder)
283 #set ($discard = $siblings.add($placeholder))
284 #end
285 #end
286
287 #macro (addSpaceNode $spaceReference $siblings)
288 #set ($spaceId = $services.model.serialize($spaceReference, 'default'))
289 #set ($nodeId = "space:$spaceId")
290 #set ($hasSpaceAdmin = $services.security.authorization.hasAccess('admin', $spaceReference))
291 #set ($canViewSpace = $services.security.authorization.hasAccess('view', $spaceReference))
292 #if ($docTreeConfig.showTerminalDocuments)
293 ## Each space has at least one document or one sub-space. There's no such thing as "empty space" in the model.
294 #set ($hasChildren = true)
295 #else
296 ## We display only the nested spaces. This space might contain only documents.
297 #set ($hasChildren = $tree.getChildCount($nodeId) > 0)
298 #end
299 #set ($isOpened = false)
300 #if ("$!docTreeConfig.expandToLevel" != '')
301 #set ($rootNode = "wiki:$services.wiki.currentWikiId")
302 #if ("$!docTreeConfig.root" != '')
303 #set ($rootNode = "wiki:$services.wiki.currentWikiId")
304 #else
305 #set ($rootNode = $docTreeConfig.root)
306 #end
307 #set ($rootDistance = $tree.getPath($nodeId).size())
308 #set ($isOpened = ($rootDistance != -1 && $docTreeConfig.expandToLevel >= $rootDistance))
309 #end
310 #set ($discard = $siblings.add({
311 'id': $nodeId,
312 'text': $spaceReference.name,
313 'icon': 'fa fa-folder-o',
314 'iconOpened': 'fa fa-folder-open-o',
315 'children': $hasChildren,
316 'data': {
317 'id': $spaceId,
318 'type': 'space',
319 'validChildren': ['addDocument', 'space', 'document', 'pagination'],
320 'hasContextMenu': true,
321 'draggable': $canViewSpace,
322 'canMove': $hasSpaceAdmin,
323 'canCopy': $canViewSpace,
324 'canRename': $hasSpaceAdmin,
325 'canDelete': $hasSpaceAdmin,
326 'createDocumentURL': $xwiki.getURL($spaceReference, 'create', $NULL),
327 'deleteURL': $xwiki.getURL($spaceReference, 'deletespace', $NULL)
328 },
329 'state': {
330 'opened': $isOpened
331 },
332 'a_attr': {
333 'href': $xwiki.getURL($spaceReference)
334 }
335 }))
336 #end
337
338 ##
339 ## Document Nodes
340 ##
341
342 #macro (maybeAddDocumentNode $documentIdOrReference $siblings $placeholder)
343 #if ($documentIdOrReference.type)
344 #set ($documentReference = $documentIdOrReference)
345 #else
346 #set ($documentReference = $services.model.resolveDocument($documentIdOrReference))
347 #end
348 #if (!$docTreeConfig.showOnlyViewable || $services.security.authorization.hasAccess('view', $documentReference))
349 #addDocumentNode($documentReference $siblings)
350 #elseif ($placeholder)
351 #set ($discard = $siblings.add($placeholder))
352 #end
353 #end
354
355 #macro (addDocumentNode $documentReference $siblings)
356 #set ($documentId = $services.model.serialize($documentReference, 'default'))
357 #set ($nodeId = "document:$documentId")
358 #set ($label = $documentReference.name)
359 #if (!$docTreeConfig.showSpaces &&
360 $documentReference.name == $services.model.getEntityReference('DOCUMENT', 'default').name)
361 ## Use the space name as default value for the node label (in case the document is not viewable).
362 #set ($label = $documentReference.parent.name)
363 #end
364 #set ($canViewDoc = $services.security.authorization.hasAccess('view', $documentReference))
365 #set ($canDeleteDoc = $services.security.authorization.hasAccess('delete', $documentReference))
366 #if ($canViewDoc && $docTreeConfig.showDocumentTitle)
367 ## Display the translated title.
368 #set ($translatedDocument = $xwiki.getDocument($documentReference).translatedDocument)
369 #set ($plainTitle = $translatedDocument.plainTitle)
370 #if (!$stringtool.isBlank($plainTitle))
371 #set ($label = $plainTitle)
372 #end
373 #end
374 #set ($hasChildren = $tree.getChildCount($nodeId) > 0)
375 #set ($isOpened = false)
376 #if ("$!docTreeConfig.expandToLevel" != '')
377 #set ($rootNode = "wiki:$services.wiki.currentWikiId")
378 #if ("$!docTreeConfig.root" != '')
379 #set ($rootNode = "wiki:$services.wiki.currentWikiId")
380 #else
381 #set ($rootNode = $docTreeConfig.root)
382 #end
383 #set ($rootDistance = $tree.getPath($nodeId).size())
384 #set ($isOpened = ($rootDistance != -1 && $docTreeConfig.expandToLevel >= $rootDistance))
385 #end
386 #set ($discard = $siblings.add({
387 'id': $nodeId,
388 'text': $label,
389 'icon': 'fa fa-file-o',
390 'children': $hasChildren,
391 'data': {
392 'id': $services.model.serialize($documentReference, 'default'),
393 'type': 'document',
394 'validChildren': ['translations', 'attachments', 'attachment', 'classProperties', 'objects', 'document', 'pagination'],
395 'hasContextMenu': true,
396 'draggable': $canViewDoc,
397 'canDelete': $canDeleteDoc,
398 'canMove': $canDeleteDoc,
399 'canCopy': $canViewDoc,
400 'createDocumentURL': $xwiki.getURL($documentReference, 'create', $NULL)
401 },
402 'state': {
403 'opened': $isOpened
404 },
405 'a_attr': {
406 'href': $xwiki.getURL($documentReference)
407 }
408 }))
409 #end
410
411 #macro (maybeAddAddDocumentNode $documentId $siblings)
412 #set ($documentReference = $services.model.resolveDocument($documentId))
413 #if ($services.security.authorization.hasAccess('edit', $documentReference.parent))
414 #addAddDocumentNode($documentReference $siblings)
415 #end
416 #end
417
418 #macro (addAddDocumentNode $documentReference $siblings)
419 ## FIXME: This URL is wrong, it should use the $documentReference as the parent for creation of the node:
420 ## the reference is already an existing doc, so it shouldn't be the one used for creation.
421 #set ($discard = $siblings.add({
422 'id': "addDocument:$services.model.serialize($documentReference, 'default')",
423 'text': $services.localization.render('index.documentTree.addDocument'),
424 'icon': 'fa fa-plus-circle',
425 'children': false,
426 'data': {
427 'type': 'addDocument',
428 'validChildren': [],
429 'hasContextMenu': true,
430 'canRename': true
431 },
432 'a_attr': {
433 'href': $xwiki.getURL($documentReference, 'create')
434 }
435 }))
436 #end
437
438 ##
439 ## Translation Nodes
440 ##
441
442 #macro (maybeAddTranslationsNode $documentId $siblings)
443 #set ($documentReference = $services.model.resolveDocument($documentId))
444 #if ($services.security.authorization.hasAccess('view', $documentReference))
445 #addTranslationsNode($documentReference $siblings)
446 #end
447 #end
448
449 #macro (addTranslationsNode $documentReference $siblings)
450 #set ($discard = $children.add({
451 'id': "translations:${documentReference}",
452 'text': 'Translations',
453 'icon': 'fa fa-language',
454 'children': true,
455 'data': {
456 'type': 'translations',
457 'validChildren': ['translation'],
458 'canDelete': $services.security.authorization.hasAccess('delete', $documentReference)
459 }
460 }))
461 #end
462
463 #macro (maybeAddTranslationNode $nodeReference $siblings)
464 #set ($documentId = $stringtool.substringBeforeLast($nodeReference, '/'))
465 #set ($locale = $services.localization.toLocale($stringtool.substringAfterLast($nodeReference, '/')))
466 #set ($documentReference = $services.model.resolveDocument($documentId))
467 #set ($translationReference = $services.model.createDocumentReference($documentReference, $locale))
468 #if ($services.security.authorization.hasAccess('view', $documentReference))
469 #addTranslationNode($translationReference $siblings)
470 #end
471 #end
472
473 #macro (addTranslationNode $translationReference $siblings)
474 #set ($currentLocale = $services.localization.currentLocale)
475 #set ($discard = $siblings.add({
476 'id': "translation:$services.model.serialize($translationReference, 'default')_$translationReference.locale",
477 'text': $translationReference.locale.getDisplayName($currentLocale),
478 'icon': 'fa fa-file-text-o',
479 'children': false,
480 'data': {
481 'type': 'translation',
482 'validChildren': [],
483 'canDelete': $services.security.authorization.hasAccess('delete', $translationReference)
484 },
485 'a_attr': {
486 'href': $xwiki.getURL($translationReference)
487 }
488 }))
489 #end
490
491 ##
492 ## Attachment Nodes
493 ##
494
495 #macro (maybeAddAttachmentsNode $documentId $siblings)
496 #set ($documentReference = $services.model.resolveDocument($documentId))
497 #if ($services.security.authorization.hasAccess('view', $documentReference))
498 #addAttachmentsNode($documentReference $siblings)
499 #end
500 #end
501
502 #macro (addAttachmentsNode $documentReference $siblings)
503 #set ($discard = $siblings.add({
504 'id': "attachments:${documentReference}",
505 'text': 'Attachments',
506 'icon': 'fa fa-paperclip',
507 'children': true,
508 'data': {
509 'type': 'attachments',
510 'validChildren': ['addAttachment', 'attachment', 'pagination'],
511 'hasContextMenu': true,
512 'canDelete': $services.security.authorization.hasAccess('edit', $documentReference)
513 },
514 'a_attr': {
515 'href': $xwiki.getURL($documentReference, 'view', 'viewer=attachments')
516 }
517 }))
518 #end
519
520 #macro (maybeAddAttachmentNode $attachmentId $siblings))
521 #set ($attachmentReference = $services.model.resolveAttachment($attachmentId))
522 #set ($document = $xwiki.getDocument($attachmentReference))
523 #set ($attachment = $document.getAttachment($attachmentReference.name))
524 #if ($attachment)
525 #addAttachmentNode($attachment $siblings)
526 #end
527 #end
528
529 #macro (addAttachmentNode $attachment $siblings)
530 #set ($attachmentReference = $services.model.createAttachmentReference($attachment.document.documentReference,
531 $attachment.filename))
532 #set ($attachmentId = $services.model.serialize($attachmentReference, 'default'))
533 #set ($canEditDoc = $services.security.authorization.hasAccess('edit', $attachmentReference.parent))
534 #getAttachmentIcon($attachment $icon)
535 #set ($discard = $siblings.add({
536 'id': "attachment:$attachmentId",
537 'text': $attachment.filename,
538 'icon': $icon,
539 'children': false,
540 'data': {
541 'id': $attachmentId,
542 'type': 'attachment',
543 'validChildren': [],
544 'hasContextMenu': true,
545 'draggable': true,
546 'canRename': $canEditDoc,
547 'canDelete': $canEditDoc,
548 'canMove': $canEditDoc,
549 'canCopy': true,
550 'deleteURL': $attachment.document.getAttachmentURL($attachment.filename, 'delattachment'),
551 'mimetype': $attachment.mimeType
552 },
553 'a_attr': {
554 'href': $attachment.document.getAttachmentURL($attachment.filename)
555 }
556 }))
557 #end
558
559 #set ($fileIconByMediaType = {
560 'text': ['text/', 'application/xml', 'application/javascript', 'application/ecmascript', 'application/json', 'application/x-sh', '+xml'],
561 'image': ['image/'],
562 'audio': ['audio/'],
563 'video': ['video/'],
564 'pdf': ['application/pdf', 'application/postscript'],
565 'word': ['application/msword', 'application/vnd.ms-word.', 'application/vnd.oasis.opendocument.text', 'application/vnd.openxmlformats-officedocument.word'],
566 'powerpoint': ['application/vnd.ms-powerpoint', 'application/vnd.oasis.opendocument.presentation', 'application/vnd.openxmlformats-officedocument.presentation'],
567 'excel': ['application/vnd.ms-excel', 'application/vnd.oasis.opendocument.spreadsheet', 'application/vnd.openxmlformats-officedocument.spreadsheet'],
568 'archive': ['application/zip', 'application/x-gzip', 'application/x-bzip', 'application/x-tar', 'application/x-gtar', 'application/vnd.xara', '-archive', '-compressed', '-package', '+zip']
569 })
570
571 #macro (getAttachmentIcon $attachment $return)
572 #set ($mediaType = $attachment.mimeType)
573 #set ($icon = $NULL)
574 #foreach ($entry in $fileIconByMediaType.entrySet())
575 #foreach ($pattern in $entry.value)
576 #if ($mediaType.startsWith($pattern) || $mediaType.endsWith($pattern))
577 #set ($icon = $entry.key)
578 #break
579 #end
580 #end
581 #if ($icon)
582 #break
583 #end
584 #end
585 #set ($suffix = $stringtool.substringAfterLast($attachment.filename, '.'))
586 #set ($codeSuffixes = ['html', 'css', 'js', 'java', 'c', 'cpp', 'c++', 'cs', 'h', 'sql', 'php', 'ruby'])
587 #if (!$icon)
588 #set ($icon = 'fa fa-paperclip')
589 #elseif ($icon == 'text' && $codeSuffixes.contains($suffix))
590 #set ($icon = 'fa fa-file-code-o')
591 #else
592 #set ($icon = "fa fa-file-${icon}-o")
593 #end
594 #set ($return = $NULL)
595 #setVariable("$return" $icon)
596 #end
597
598 #macro (maybeAddAddAttachmentNode $documentId $siblings)
599 #set ($documentReference = $services.model.resolveDocument($documentId))
600 #if ($services.security.authorization.hasAccess('edit', $documentReference))
601 #addAddAttachmentNode($documentReference $siblings)
602 #end
603 #end
604
605 #macro (addAddAttachmentNode $documentReference $siblings)
606 #set ($discard = $siblings.add({
607 'id': "addAttachment:$documentReference",
608 'text': 'Upload file...',
609 'icon': 'fa fa-plus-circle',
610 'children': false,
611 'data': {
612 'type': 'addAttachment',
613 'validChildren': []
614 },
615 'a_attr': {
616 'href': $xwiki.getURL($documentReference, 'view', 'viewer=attachments')
617 }
618 }))
619 #end
620
621 ##
622 ## Class Property Nodes
623 ##
624
625 #macro (maybeAddClassPropertiesNode $documentId $siblings)
626 #set ($documentReference = $services.model.resolveDocument($documentId))
627 #if ($services.security.authorization.hasAccess('view', $documentReference))
628 #addClassPropertiesNode($documentReference $siblings)
629 #end
630 #end
631
632 #macro (addClassPropertiesNode $documentReference $siblings)
633 #set ($discard = $children.add({
634 'id': "classProperties:${documentReference}",
635 'text': 'Class Properties',
636 'icon': 'fa fa-gears',
637 'children': true,
638 'data': {
639 'type': 'classProperties',
640 'validChildren': ['classProperty'],
641 'canDelete': $services.security.authorization.hasAccess('edit', $documentReference)
642 }
643 }))
644 #end
645
646 #set ($iconByPropertyType = {
647 'Boolean': 'check-square-o',
648 'Date': 'calendar-o',
649 'DBList': 'database',
650 'Groups': 'group',
651 'Password': 'asterisk',
652 'Levels': 'lock',
653 'StaticList': 'list',
654 'TextArea': 'paragraph',
655 'DBTreeList': 'sitemap',
656 'Users': 'user'
657 })
658
659 #macro (maybeAddClassPropertyNode $classPropertyId $siblings)
660 #set ($classPropertyReference = $services.model.resolveClassProperty($classPropertyId))
661 #if ($services.security.authorization.hasAccess('view', $classPropertyReference.parent))
662 #addClassPropertyNode($classPropertyReference $siblings)
663 #end
664 #end
665
666 #macro (addClassPropertyNode $classPropertyReference $siblings)
667 #set ($classPropertyId = $services.model.serialize($classPropertyReference, 'default'))
668 #set ($xclass = $xwiki.getDocument($classPropertyReference).getxWikiClass())
669 #set ($property = $xclass.get($classPropertyReference.name))
670 #set ($icon = $iconByPropertyType.get($property.classType))
671 #if (!$icon)
672 #set ($icon = 'gear')
673 #end
674 #set ($discard = $siblings.add({
675 'id': "classProperty:$classPropertyId",
676 'text': $property.name,
677 'icon': "fa fa-$icon",
678 'children': false,
679 'data': {
680 'id': $classPropertyId,
681 'type': 'classProperty',
682 'validChildren': []
683 }
684 }))
685 #end
686
687 ##
688 ## Object Nodes
689 ##
690
691 #macro (maybeAddObjectsNode $documentId $siblings)
692 #set ($documentReference = $services.model.resolveDocument($documentId))
693 #if ($services.security.authorization.hasAccess('view', $documentReference))
694 #addObjectsNode($documentReference $siblings)
695 #end
696 #end
697
698 #macro (addObjectsNode $documentReference $siblings)
699 #set ($discard = $children.add({
700 'id': "objects:${documentReference}",
701 'text': 'Objects',
702 'icon': 'fa fa-cubes',
703 'children': true,
704 'data': {
705 'type': 'objects',
706 'validChildren': ['objectsOfType'],
707 'canDelete': $services.security.authorization.hasAccess('edit', $documentReference)
708 }
709 }))
710 #end
711
712 #macro (maybeAddObjectsOfTypeNode $nodeReference $siblings)
713 #set ($parts = $nodeReference.split('/', 2))
714 #if ($parts && $parts.size() == 2)
715 #set ($documentReference = $services.model.resolveDocument($parts.get(0)))
716 #set ($classReference = $services.model.resolveDocument($parts.get(1)))
717 #if ($services.security.authorization.hasAccess('view', $documentReference))
718 #set ($discard = $children.add({
719 'id': "objectsOfType:$documentReference/$classReference",
720 'text': $services.model.serialize($classReference, 'local'),
721 'icon': 'fa fa-cubes',
722 'children': true,
723 'data': {
724 'type': 'objectsOfType',
725 'validChildren': ['object', 'pagination'],
726 'canDelete': $services.security.authorization.hasAccess('edit', $documentReference)
727 }
728 }))
729 #end
730 #end
731 #end
732
733 #macro (maybeAddObjectNode $objectId $siblings)
734 #set ($objectReference = $services.model.resolveObject($objectId))
735 #getXObject($objectReference)
736 #if ($object)
737 #addObjectNode($object $objectReference $siblings)
738 #end
739 #end
740
741 #macro (getXObject $objectReference)
742 ## Object name is: Space.Class[index]
743 #set ($separatorIndex = $objectReference.name.lastIndexOf('['))
744 #set ($classId = $objectReference.name.substring(0, $separatorIndex))
745 #set ($objectNumber = $numbertool.toNumber($objectReference.name.substring($mathtool.add($separatorIndex, 1),
746 $mathtool.sub($objectReference.name.length(), 1))).intValue())
747 #set ($document = $xwiki.getDocument($objectReference))
748 #set ($object = $document.getObject($classId, $objectNumber))
749 #end
750
751 #macro (addObjectNode $object $objectReference $siblings)
752 #set ($objectId = $services.model.serialize($objectReference, 'default'))
753 #set ($discard = $children.add({
754 'id': "object:$objectId",
755 'text': "[$object.number]",
756 'icon': 'fa fa-cube',
757 'children': true,
758 'data': {
759 'id': $objectId,
760 'type': 'object',
761 'validChildren': ['objectProperty'],
762 'canDelete': $services.security.authorization.hasAccess('edit', $objectReference.parent)
763 }
764 }))
765 #end
766
767 #macro (maybeAddObjectPropertyNode $objectPropertyId $siblings)
768 #set ($objectPropertyReference = $services.model.resolveObjectProperty($objectPropertyId))
769 #set ($objectReference = $objectPropertyReference.parent)
770 #getXObject($objectReference)
771 #set ($property = $object.getProperty($objectPropertyReference.name))
772 #if ($property)
773 #addObjectPropertyNode($property $objectReference $siblings)
774 #end
775 #end
776
777 #macro (addObjectPropertyNode $property $objRef $siblings)
778 #set ($classId = $stringtool.substringBeforeLast($objRef.name, '['))
779 #set ($classRef = $services.model.resolveDocument($classId, 'explicit', $objRef))
780 #set ($xclass = $xwiki.getDocument($classRef).getxWikiClass())
781 #set ($icon = $iconByPropertyType.get($xclass.get($property.name).classType))
782 #if (!$icon)
783 #set ($icon = 'gear')
784 #end
785 #set ($objectPropertyReference = $services.model.createEntityReference($property.name, 'OBJECT_PROPERTY', $objRef))
786 #set ($objectPropertyId = $services.model.serialize($objectPropertyReference, 'default'))
787 #set ($discard = $siblings.add({
788 'id': "objectProperty:$objectPropertyId",
789 'text': $property.name,
790 'icon': "fa fa-$icon",
791 'children': false,
792 'data': {
793 'id': $objectPropertyId,
794 'type': 'objectProperty',
795 'validChildren': []
796 }
797 }))
798 #end
799
800 ##
801 ## Pagination Nodes
802 ##
803
804 #macro (addPaginationNode $parentId $offset $totalCount $siblings)
805 #set ($discard = $siblings.add({
806 'id': "pagination:$parentId",
807 'text': $services.localization.render('index.documentTree.more', $!mathtool.sub($totalCount, $offset)),
808 'icon': 'fa fa-eye',
809 'children': false,
810 'data': {
811 'type': 'pagination',
812 'validChildren': [],
813 'canDelete': true,
814 'offset': $offset
815 }
816 }))
817 #end
818
819 ##
820 ## Empty Tree Node
821 ##
822
823 #macro (addEmptyTreeNode $siblings)
824 #set ($discard = $siblings.add({
825 'id': "empty",
826 'text': $services.localization.render('index.documentTree.empty'),
827 'icon': 'fa fa-info-circle',
828 'children': false,
829 'data': {
830 'type': 'empty',
831 'validChildren': []
832 }
833 }))
834 #end
835
836 ##------------------------------------------------------------
837 ## Path
838 ##------------------------------------------------------------
839
840 #macro (getPath $nodeId $return)
841 #set ($path = [])
842 #if ($docTreeConfig.showRoot)
843 #maybeAddNode($docTreeConfig.root $path {})
844 #end
845 #foreach ($pathElement in $tree.getPath($nodeId))
846 #maybeAddNode($pathElement $path {})
847 #end
848 #set ($return = $NULL)
849 #setVariable("$return" $path)
850 #end
851
852 ##------------------------------------------------------------
853 ## Context Menu
854 ##------------------------------------------------------------
855
856 #macro (getContextMenu $return)
857 #set ($contextMenuByNodeType = {})
858 #if ($docTreeConfig.showSpaces)
859 #addSpaceContextMenu($contextMenuByNodeType)
860 #end
861 #addDocumentContextMenu($contextMenuByNodeType)
862 #if ($docTreeConfig.showAttachments)
863 #addAttachmentsContextMenu($contextMenuByNodeType)
864 #addAttachmentContextMenu($contextMenuByNodeType)
865 #end
866 #set ($return = $NULL)
867 #setVariable("$return" $contextMenuByNodeType)
868 #end
869
870 #macro (addSpaceContextMenu $contextMenuByNodeType)
871 #set ($contextMenuByNodeType.space = {
872 'createDocument': {
873 'label': 'New Page',
874 'icon': 'fa fa-file-o',
875 'action': 'openLink',
876 'parameters': {
877 'urlProperty': 'createDocumentURL'
878 }
879 },
880 'openLink': {
881 'separator_before': true,
882 'label': 'Go to Space',
883 'icon': 'fa fa-external-link'
884 },
885 'refresh': {
886 'label': 'Refresh',
887 'icon': 'fa fa-refresh'
888 },
889 'paste': {
890 'separator_before': true,
891 'label': 'Paste Into Space',
892 'icon': 'fa fa-clipboard'
893 },
894 'rename': {
895 'label': 'Rename...',
896 'icon': 'fa fa-pencil-square-o'
897 },
898 'remove': {
899 'label': 'Delete',
900 'icon': 'fa fa-trash-o',
901 'parameters': {
902 'confirmationMessage': 'Are you sure you want to move ALL the documents from this space to the recycle bin? If there are hidden documents in this space they will also be deleted.'
903 }
904 }
905 })
906 #end
907
908 #macro (addDocumentContextMenu $contextMenuByNodeType)
909 #set ($contextMenuByNodeType.document = {
910 'createDocument': {
911 'label': 'New Page',
912 'icon': 'fa fa-file-o',
913 'action': 'openLink',
914 'parameters': {
915 'urlProperty': 'createDocumentURL'
916 }
917 },
918 'openLink': {
919 'separator_before': true,
920 'label': 'Go to Page',
921 'icon': 'fa fa-external-link'
922 },
923 'refresh': {
924 'label': 'Refresh',
925 'icon': 'fa fa-refresh'
926 },
927 'cut': {
928 'separator_before': true,
929 'label': 'Cut',
930 'icon': 'fa fa-scissors'
931 },
932 'copy': {
933 'label': 'Copy',
934 'icon': 'fa fa-files-o'
935 },
936 'paste': {
937 'label': 'Paste',
938 'icon': 'fa fa-clipboard'
939 },
940 'remove': {
941 'separator_before': true,
942 'label': 'Delete',
943 'icon': 'fa fa-trash-o',
944 'parameters': {
945 'confirmationMessage': 'Are you sure you want to move this document to the recycle bin? All child documents will become orphan as a result.'
946 }
947 }
948 })
949 #end
950
951 #macro (addAttachmentsContextMenu $contextMenuByNodeType)
952 #set ($contextMenuByNodeType.attachments = {
953 'openLink': {
954 'label': 'Go to Attachments',
955 'icon': 'fa fa-external-link'
956 },
957 'refresh': {
958 'label': 'Refresh',
959 'icon': 'fa fa-refresh'
960 },
961 'paste': {
962 'separator_before': true,
963 'label': 'Paste',
964 'icon': 'fa fa-clipboard'
965 },
966 'remove': {
967 'label': 'Delete All',
968 'icon': 'fa fa-trash-o',
969 'parameters': {
970 'confirmationMessage': 'Are you sure you want to delete all the attachments of this page?'
971 }
972 }
973 })
974 #end
975
976 #macro (addAttachmentContextMenu $contextMenuByNodeType)
977 #set ($contextMenuByNodeType.attachment = {
978 'openLink': {
979 'label': 'Go to Attachment',
980 'icon': 'fa fa-external-link'
981 },
982 'cut': {
983 'separator_before': true,
984 'label': 'Cut',
985 'icon': 'fa fa-scissors'
986 },
987 'copy': {
988 'label': 'Copy',
989 'icon': 'fa fa-files-o'
990 },
991 'rename': {
992 'separator_before': true,
993 'label': 'Rename...',
994 'icon': 'fa fa-pencil-square-o'
995 },
996 'remove': {
997 'label': 'Delete',
998 'icon': 'fa fa-trash-o',
999 'parameters': {
1000 'confirmationMessage': 'Are you sure you want to delete this attachment?'
1001 }
1002 }
1003 })
1004 #end
1005
1006 ##------------------------------------------------------------
1007 ## Finder Suggestions
1008 ##------------------------------------------------------------
1009
1010 #macro (getSuggestions $return)
1011 #set ($limit = 6)
1012 #set ($text = "$!request.query")
1013 #set ($lists = [])
1014 #getRootReference
1015 #set ($ancestorsOf = {
1016 'space': ['farm', 'wiki', 'space'],
1017 'document': ['farm', 'wiki', 'space', 'document'],
1018 'attachment': ['farm', 'wiki', 'space', 'document', 'attachments']
1019 })
1020 #if ((!$docTreeConfig.showSpaces || $docTreeConfig.showTerminalDocuments)
1021 && $ancestorsOf.document.contains($rootType))
1022 #addDocumentSuggestions($text $limit $lists)
1023 #end
1024 #if ($docTreeConfig.showAttachments && $ancestorsOf.attachment.contains($rootType))
1025 #addAttachmentSuggestions($text $limit $lists)
1026 #end
1027 #if ($docTreeConfig.showSpaces && $ancestorsOf.space.contains($rootType))
1028 #addSpaceSuggestions($text $limit $lists)
1029 #end
1030 #limitTotalCount($lists $limit)
1031 #set ($output = [])
1032 #foreach ($list in $lists)
1033 #foreach ($node in $list)
1034 ## Use the node path as suggestion info.
1035 #getPath($node.id $path)
1036 ## The path is empty when the node is not found in the tree. This happens if the tree finder doesn't restrict the
1037 ## search to the nodes that are available in the tree.
1038 #if ($path.size() > 0)
1039 #displayPath($path)
1040 #set ($node.data.info = $stringtool.join($path.subList(0, $mathtool.sub($path.size(), 1)), ' / '))
1041 #set ($discard = $output.add($node))
1042 #end
1043 #end
1044 #end
1045 #set ($return = $NULL)
1046 #setVariable("$return" $output)
1047 #end
1048
1049 #macro (getRootReference)
1050 #set ($parts = $docTreeConfig.root.split(':', 2))
1051 #if ($parts.size() == 2)
1052 #set ($rootType = $parts[0])
1053 #set ($rootReference = $parts[1])
1054 #if ($rootType == 'wiki')
1055 #set ($rootReference = $services.model.createWikiReference($parts[1]))
1056 #elseif ($rootType == 'space')
1057 #set ($rootReference = $services.model.resolveSpace($parts[1]))
1058 #elseif ($rootType == 'document' || $rootType == 'attachments')
1059 #set ($rootReference = $services.model.resolveDocument($parts[1]))
1060 #end
1061 #else
1062 #set ($rootType = 'unknown')
1063 #set ($rootReference = $parts[0])
1064 #end
1065 #end
1066
1067 #macro (addSpaceSuggestions $text $limit $suggestions)
1068 #searchSpaces($text $limit $spaceReferences)
1069 #set ($spaceSuggestions = [])
1070 #foreach ($spaceReference in $spaceReferences)
1071 #maybeAddSpaceNode($spaceReference $spaceSuggestions)
1072 #end
1073 #set ($discard = $suggestions.add($spaceSuggestions))
1074 #end
1075
1076 #macro (searchSpaces $text $limit $return)
1077 #set ($constraints = ["upper(space.name) like upper(:spaceNamePattern) escape '!'"])
1078 #set ($params = {'spaceNamePattern': "%$!text.replaceAll('([%_!])', '!$1')%"})
1079 #addSpaceLocationDatabaseConstraint($rootReference $constraints $params 'space.reference')
1080 #set ($statement = "select space.reference from XWikiSpace space where $stringtool.join($constraints, ' and ') "
1081 + "order by lower(space.reference), space.reference")
1082 #set ($query = $services.query.hql($statement).setLimit($limit))
1083 #addWikiLocationDatabaseConstraint($rootReference $query)
1084 #if ($docTreeConfig.filterHiddenDocuments)
1085 #set ($query = $query.addFilter('hidden/space'))
1086 #end
1087 #foreach ($entry in $params.entrySet())
1088 #set ($query = $query.bindValue($entry.key, $entry.value))
1089 #end
1090 #set ($spaceReferences = [])
1091 #foreach ($localSpaceRef in $query.execute())
1092 #set ($discard = $spaceReferences.add($services.model.resolveSpace($localSpaceRef)))
1093 #end
1094 #set ($return = $NULL)
1095 #setVariable("$return" $spaceReferences)
1096 #end
1097
1098 #macro (addDocumentSuggestions $text $limit $suggestions)
1099 #searchDocuments($text $limit $documentReferences)
1100 #set ($docSuggestions = [])
1101 #foreach ($documentReference in $documentReferences)
1102 #maybeAddDocumentNode($documentReference $docSuggestions)
1103 #end
1104 #set ($discard = $suggestions.add($docSuggestions))
1105 #end
1106
1107 #macro (searchDocuments $text $limit $return)
1108 #if ($xwiki.exists('XWiki.SuggestSolrMacros'))
1109 #searchDocumentsSolr($text $limit $return)
1110 #else
1111 #searchDocumentsDatabase($text $limit $return)
1112 #end
1113 #end
1114
1115 #macro (searchDocumentsSolr $text $limit $return)
1116 #set ($params = [
1117 'fq=type:DOCUMENT',
1118 'fq=doclocale:""',
1119 'qf=title^6 name^4 doccontent^2 doccontentraw',
1120 'fl=wiki spaces name'
1121 ])
1122 #addCommonDocTreeSolrParams($params)
1123 #set ($params = $stringtool.join($params, $util.newline))
1124 #createSearchSuggestQuery($params $text $query)
1125 #set ($discard = $query.setLimit($limit))
1126 #set ($documentReferences = [])
1127 #foreach ($result in $query.execute()[0].results)
1128 #set ($discard = $documentReferences.add($services.solr.resolveDocument($result)))
1129 #end
1130 #set ($return = $NULL)
1131 #setVariable("$return" $documentReferences)
1132 #end
1133
1134 #macro (searchDocumentsDatabase $text $limit $return)
1135 #set ($constraints = [
1136 'doc.translation = 0',
1137 'doc.space = space.reference'
1138 ])
1139 #set ($defaultDocumentName = $services.model.getEntityReference('DOCUMENT', 'default').name)
1140 #set ($matchDocTitle = "upper(doc.title) like upper(:text) escape '!'")
1141 #set ($params = {'text': "%$!text.replaceAll('([%_!])', '!$1')%"})
1142 #if ($docTreeConfig.showTerminalDocuments)
1143 #set ($matchDocName = "(doc.name <> '$defaultDocumentName' and upper(doc.name) like upper(:text) escape '!')")
1144 #set ($matchSpaceName = "(doc.name = '$defaultDocumentName' and upper(space.name) like upper(:text) escape '!')")
1145 #set ($discard = $constraints.add("($matchDocTitle or $matchDocName or $matchSpaceName)"))
1146 #else
1147 #set ($matchSpaceName = "upper(space.name) like upper(:text) escape '!'")
1148 #set ($discard = $constraints.addAll([
1149 "doc.name = '$defaultDocumentName'",
1150 "($matchDocTitle or $matchSpaceName)"
1151 ]))
1152 #end
1153 #addDocumentLocationDatabaseConstraint($rootReference $constraints $params)
1154 #set ($constraints = $stringtool.join($constraints, ' and '))
1155 #set ($statement = "select doc.fullName from XWikiDocument doc, XWikiSpace space where $constraints")
1156 #set ($query = $services.query.hql($statement).setLimit($limit))
1157 #foreach ($entry in $params.entrySet())
1158 #set ($query = $query.bindValue($entry.key, $entry.value))
1159 #end
1160 #addWikiLocationDatabaseConstraint($rootReference $query)
1161 #if ($docTreeConfig.filterHiddenDocuments)
1162 #set ($query = $query.addFilter('hidden/document'))
1163 #end
1164 #set ($documentReferences = [])
1165 #foreach ($docFullName in $query.execute())
1166 #set ($discard = $documentReferences.add($services.model.resolveDocument($docFullName)))
1167 #end
1168 #set ($return = $NULL)
1169 #setVariable("$return" $documentReferences)
1170 #end
1171
1172 #macro (addAttachmentSuggestions $text $limit $suggestions)
1173 #searchAttachments($text $limit $attachmentReferences)
1174 #set ($attachmentSuggestions = [])
1175 #foreach ($attachmentReference in $attachmentReferences)
1176 #set ($attachment = $xwiki.getDocument($attachmentReference.parent).getAttachment($attachmentReference.name))
1177 #addAttachmentNode($attachment $attachmentSuggestions)
1178 #end
1179 #set ($discard = $suggestions.add($attachmentSuggestions))
1180 #end
1181
1182 #macro (searchAttachments $text $limit $return)
1183 #if ($xwiki.exists('XWiki.SuggestSolrMacros'))
1184 #searchAttachmentsSolr($text $limit $return)
1185 #else
1186 #searchAttachmentsDatabase($text $limit $return)
1187 #end
1188 #end
1189
1190 #macro (searchAttachmentsSolr $text $limit $return)
1191 #set ($params = [
1192 'fq=type:ATTACHMENT',
1193 'fq=locale:*',
1194 'qf=filename^4 attcontent',
1195 'fl=type wiki spaces name filename'
1196 ])
1197 #addCommonDocTreeSolrParams($params)
1198 #set ($params = $stringtool.join($params, $util.newline))
1199 #createSearchSuggestQuery($params $text $query)
1200 #set ($discard = $query.setLimit($limit))
1201 #set ($attachmentReferences = [])
1202 #foreach ($result in $query.execute()[0].results)
1203 #set ($discard = $attachmentReferences.add($services.solr.resolve($result)))
1204 #end
1205 #set ($return = $NULL)
1206 #setVariable("$return" $attachmentReferences)
1207 #end
1208
1209 #macro (searchAttachmentsDatabase $text $limit $return)
1210 #set ($constraints = ["upper(attach.filename) like upper(:text) escape'!'"])
1211 #set ($params = {'text': "%$!text.replaceAll('([%_!])', '!$1')%"})
1212 #if ($docTreeConfig.filterHiddenDocuments && "$!xwiki.getUserPreference('displayHiddenDocuments')" != '1')
1213 #set ($discard = $constraints.add("(doc.hidden <> true or doc.hidden is null)"))
1214 #end
1215 #set ($exactMatch = $rootType == 'attachments')
1216 #addDocumentLocationDatabaseConstraint($rootReference $constraints $params $exactMatch)
1217 #set ($statement = "where $stringtool.join($constraints, ' and ')")
1218 ##
1219 ## Convert named parameters to positional parameters.
1220 #set ($paramList = [])
1221 #foreach ($item in $regextool.findAll($statement, ':(\w+)'))
1222 #set ($paramName = $item.get(1).getGroup())
1223 #set ($discard = $paramList.add($params.get($paramName)))
1224 #end
1225 #set ($statement = $statement.replaceAll(':\w+', '\?'))
1226 ##
1227 ## TODO: Search in the wiki that corresponds to the root node.
1228 #set ($attachments = $xwiki.searchAttachments($statement, $limit, 0, $paramList))
1229 #set ($attachmentReferences = [])
1230 #foreach ($attachment in $attachments)
1231 #set ($discard = $attachmentReferences.add($services.model.createAttachmentReference(
1232 $attachment.document.documentReference, $attachment.filename)))
1233 #end
1234 #set ($return = $NULL)
1235 #setVariable("$return" $attachmentReferences)
1236 #end
1237
1238 #macro (addCommonDocTreeSolrParams $params)
1239 #if ($rootType == 'wiki')
1240 ## Limit the search to the specified wiki.
1241 #addWikiLocationSolrParams($rootReference $params)
1242 #elseif ($rootType == 'space')
1243 ## Limit the search to the specified space.
1244 #addSpaceLocationSolrParams($rootReference $params)
1245 #elseif ($rootType == 'document')
1246 ## Limit the search to the specified document.
1247 #addDocumentLocationSolrParams($rootReference $params)
1248 #elseif ($rootType == 'attachments')
1249 ## Limit the search to the attachments of the specified document.
1250 #addDocumentLocationSolrParams($rootReference $params true)
1251 #end
1252 #if (!$docTreeConfig.showTerminalDocuments)
1253 #set ($defaultDocumentName = $services.model.getEntityReference('DOCUMENT', 'default').name)
1254 #set ($discard = $params.add("fq=name:$defaultDocumentName"))
1255 #end
1256 #if (!$docTreeConfig.filterHiddenDocuments)
1257 ## Force the inclusion of the hidden documents.
1258 #set ($discard = $params.add("fq=hidden:*"))
1259 #end
1260 #end
1261
1262 #macro (addWikiLocationSolrParams $rootReference $params)
1263 #set ($wikiReference = $rootReference.extractReference('WIKI'))
1264 #if ($wikiReference)
1265 #set ($discard = $params.add("fq=wiki:$wikiReference.name"))
1266 #end
1267 #end
1268
1269 #macro (addWikiLocationDatabaseConstraint $rootReference $query)
1270 #set ($wikiReference = $rootReference.extractReference('WIKI'))
1271 #if ($wikiReference)
1272 #set ($query = $query.setWiki($wikiReference.name))
1273 #end
1274 #end
1275
1276 #macro (addSpaceLocationSolrParams $rootReference $params $exactMatch)
1277 #addWikiLocationSolrParams($rootReference $params)
1278 #set ($spaceReference = $rootReference.extractReference('SPACE'))
1279 #if ($spaceReference && ($docTreeConfig.showSpaces || $docTreeConfig.hierarchyMode == 'reference'))
1280 #set ($localSpaceReference = $services.model.serialize($spaceReference, 'local'))
1281 #set ($spaceField = 'space_prefix')
1282 #if ($exactMatch)
1283 #set ($spaceField = 'space_exact')
1284 #end
1285 #set ($discard = $params.add("fq=$spaceField:""$localSpaceReference"""))
1286 #end
1287 #end
1288
1289 #macro (addSpaceLocationDatabaseConstraint $rootReference $constraints $params $field)
1290 #set ($spaceReference = $rootReference.extractReference('SPACE'))
1291 #if ($spaceReference && ($docTreeConfig.showSpaces || $docTreeConfig.hierarchyMode == 'reference'))
1292 #set ($discard = $constraints.add("($field = :localSpaceReference or $field like :spaceReferencePattern escape '!')"))
1293 #set ($localSpaceReference = $services.model.serialize($spaceReference, 'local'))
1294 #set ($discard = $params.put('localSpaceReference', $localSpaceReference))
1295 #set ($spaceReferencePattern = $services.model.createEntityReference('x', 'SPACE', $spaceReference))
1296 #set ($spaceReferencePattern = $services.model.serialize($spaceReferencePattern, 'local'))
1297 #set ($spaceReferencePattern = $stringtool.removeEnd($spaceReferencePattern, 'x').replaceAll('([%_!])', '!$1'))
1298 #set ($discard = $params.put('spaceReferencePattern', "$spaceReferencePattern%"))
1299 #end
1300 #end
1301
1302 #macro (addDocumentLocationSolrParams $rootReference $params $exactMatch)
1303 #set ($documentReference = $rootReference.extractReference('DOCUMENT'))
1304 #set ($defaultDocumentName = $services.model.getEntityReference('DOCUMENT', 'default').name)
1305 #set ($macro.exactMatch = $exactMatch || ($docTreeConfig.hierarchyMode == 'reference'
1306 && $documentReference && $documentReference.name != $defaultDocumentName))
1307 #addSpaceLocationSolrParams($rootReference $params $macro.exactMatch)
1308 #if ($documentReference && $macro.exactMatch)
1309 #set ($discard = $params.add("fq=name_exact:""$documentReference.name"""))
1310 #end
1311 #end
1312
1313 #macro (addDocumentLocationDatabaseConstraint $rootReference $constraints $params $exactMatch)
1314 #set ($documentReference = $rootReference.extractReference('DOCUMENT'))
1315 #set ($defaultDocumentName = $services.model.getEntityReference('DOCUMENT', 'default').name)
1316 #set ($macro.exactMatch = $exactMatch || ($docTreeConfig.hierarchyMode == 'reference'
1317 && $documentReference && $documentReference.name != $defaultDocumentName))
1318 #if ($documentReference && $macro.exactMatch)
1319 #set ($localDocumentReference = $services.model.serialize($documentReference, 'local'))
1320 #set ($discard = $constraints.add('doc.fullName = :localDocumentReference'))
1321 #set ($discard = $params.put('localDocumentReference', $localDocumentReference))
1322 #elseif (!$macro.exactMatch)
1323 #addSpaceLocationDatabaseConstraint($rootReference $constraints $params 'doc.space')
1324 #end
1325 #end
1326
1327 #macro (displayPath $path)
1328 #foreach ($node in $path)
1329 #set ($discard = $path.set($foreach.index, $node.text))
1330 #end
1331 #end
1332
1333 #macro (limitTotalCount $lists $limit)
1334 ## Prepare the input.
1335 #set ($input = [])
1336 #foreach ($list in $lists)
1337 ## We use queues to be able to easily remove items from the start.
1338 #set ($queue = $collectiontool.queue)
1339 #set ($discard = $queue.addAll($list))
1340 #set ($discard = $input.add($queue))
1341 ## We will add (part of) the items back later.
1342 #set ($discard = $list.clear())
1343 #end
1344 ## Limit the total item count.
1345 #set ($index = -1)
1346 #foreach ($count in [1..$limit])
1347 #foreach ($i in [1..$input.size()])
1348 #set ($newIndex = ($index + $i) % $input.size())
1349 #if ($input.get($newIndex).size() > 0)
1350 #set ($index = $newIndex)
1351 #break
1352 #end
1353 #end
1354 #if ($index < 0 || $input.get($index).isEmpty())
1355 #break
1356 #else
1357 #set ($discard = $lists.get($index).add($input.get($index).poll()))
1358 #end
1359 #end
1360 #end
1361 {{/velocity}}