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