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

Версия 3.1 от Андрей Ганьков на 2023/04/03 09:29

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