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

Редактировал(а) Давид Гавриков 2024/10/22 15:18

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