0 Votes

Wiki source code of Document Tree Macros

Last modified by ztx lyghters on 2025/10/28 11:47

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