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