0 Votes

Wiki source code of Document Tree Macros

Last modified by ztx lyghters on 2025/01/31 15:59

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