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

Редактировал(а) Георгий Глущенко 2026/07/02 17:46

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