Исходный код вики Document Tree Macros
Версия 1.1 от Андрей Ганьков на 2020/03/30 19:01
Скрыть последних авторов
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 | ## Make sure the displayed title is not affected by the sheet request parameter (e.g. when $translatedDocument is | ||
320 | ## the current document). By setting the title (even if we don't change it) the internal document instance is cloned | ||
321 | ## so it's going to be different than the current document instance (which is the target of the sheet parameter). | ||
322 | #set ($discard = $translatedDocument.setTitle($translatedDocument.title)) | ||
323 | #set ($plainTitle = $translatedDocument.plainTitle) | ||
324 | #if (!$stringtool.isBlank($plainTitle)) | ||
325 | #set ($label = $plainTitle) | ||
326 | #end | ||
327 | #end | ||
328 | #set ($hasChildren = $tree.getChildCount("document:$documentId") > 0) | ||
329 | #set ($discard = $siblings.add({ | ||
330 | 'id': "document:$documentId", | ||
331 | 'text': $label, | ||
332 | 'icon': 'fa fa-file-o', | ||
333 | 'children': $hasChildren, | ||
334 | 'data': { | ||
335 | 'id': $services.model.serialize($documentReference, 'default'), | ||
336 | 'type': 'document', | ||
337 | 'validChildren': ['translations', 'attachments', 'attachment', 'classProperties', 'objects', 'document', 'pagination'], | ||
338 | 'hasContextMenu': true, | ||
339 | 'draggable': $canViewDoc, | ||
340 | 'canDelete': $canDeleteDoc, | ||
341 | 'canMove': $canDeleteDoc, | ||
342 | 'canCopy': $canViewDoc, | ||
343 | 'createDocumentURL': $xwiki.getURL($documentReference, 'create', $NULL) | ||
344 | }, | ||
345 | 'a_attr': { | ||
346 | 'href': $xwiki.getURL($documentReference) | ||
347 | } | ||
348 | })) | ||
349 | #end | ||
350 | |||
351 | #macro (maybeAddAddDocumentNode $documentId $siblings) | ||
352 | #set ($documentReference = $services.model.resolveDocument($documentId)) | ||
353 | #if ($services.security.authorization.hasAccess('edit', $documentReference.parent)) | ||
354 | #addAddDocumentNode($documentReference $siblings) | ||
355 | #end | ||
356 | #end | ||
357 | |||
358 | #macro (addAddDocumentNode $documentReference $siblings) | ||
359 | #set ($discard = $siblings.add({ | ||
360 | 'id': "addDocument:$services.model.serialize($documentReference, 'default')", | ||
361 | 'text': 'New page...', | ||
362 | 'icon': 'fa fa-plus-circle', | ||
363 | 'children': false, | ||
364 | 'data': { | ||
365 | 'type': 'addDocument', | ||
366 | 'validChildren': [] | ||
367 | }, | ||
368 | 'a_attr': { | ||
369 | 'href': $xwiki.getURL($documentReference, 'create') | ||
370 | } | ||
371 | })) | ||
372 | #end | ||
373 | |||
374 | ## | ||
375 | ## Translation Nodes | ||
376 | ## | ||
377 | |||
378 | #macro (maybeAddTranslationsNode $documentId $siblings) | ||
379 | #set ($documentReference = $services.model.resolveDocument($documentId)) | ||
380 | #if ($services.security.authorization.hasAccess('view', $documentReference)) | ||
381 | #addTranslationsNode($documentReference $siblings) | ||
382 | #end | ||
383 | #end | ||
384 | |||
385 | #macro (addTranslationsNode $documentReference $siblings) | ||
386 | #set ($discard = $children.add({ | ||
387 | 'id': "translations:${documentReference}", | ||
388 | 'text': 'Translations', | ||
389 | 'icon': 'fa fa-language', | ||
390 | 'children': true, | ||
391 | 'data': { | ||
392 | 'type': 'translations', | ||
393 | 'validChildren': ['translation'], | ||
394 | 'canDelete': $services.security.authorization.hasAccess('delete', $documentReference) | ||
395 | } | ||
396 | })) | ||
397 | #end | ||
398 | |||
399 | #macro (maybeAddTranslationNode $nodeReference $siblings) | ||
400 | #set ($documentId = $stringtool.substringBeforeLast($nodeReference, '/')) | ||
401 | #set ($locale = $services.localization.toLocale($stringtool.substringAfterLast($nodeReference, '/'))) | ||
402 | #set ($documentReference = $services.model.resolveDocument($documentId)) | ||
403 | #set ($translationReference = $services.model.createDocumentReference($documentReference, $locale)) | ||
404 | #if ($services.security.authorization.hasAccess('view', $documentReference)) | ||
405 | #addTranslationNode($translationReference $siblings) | ||
406 | #end | ||
407 | #end | ||
408 | |||
409 | #macro (addTranslationNode $translationReference $siblings) | ||
410 | #set ($currentLocale = $services.localization.currentLocale) | ||
411 | #set ($discard = $siblings.add({ | ||
412 | 'id': "translation:$services.model.serialize($translationReference, 'default')_$translationReference.locale", | ||
413 | 'text': $translationReference.locale.getDisplayName($currentLocale), | ||
414 | 'icon': 'fa fa-file-text-o', | ||
415 | 'children': false, | ||
416 | 'data': { | ||
417 | 'type': 'translation', | ||
418 | 'validChildren': [], | ||
419 | 'canDelete': $services.security.authorization.hasAccess('delete', $translationReference) | ||
420 | }, | ||
421 | 'a_attr': { | ||
422 | 'href': $xwiki.getURL($translationReference) | ||
423 | } | ||
424 | })) | ||
425 | #end | ||
426 | |||
427 | ## | ||
428 | ## Attachment Nodes | ||
429 | ## | ||
430 | |||
431 | #macro (maybeAddAttachmentsNode $documentId $siblings) | ||
432 | #set ($documentReference = $services.model.resolveDocument($documentId)) | ||
433 | #if ($services.security.authorization.hasAccess('view', $documentReference)) | ||
434 | #addAttachmentsNode($documentReference $siblings) | ||
435 | #end | ||
436 | #end | ||
437 | |||
438 | #macro (addAttachmentsNode $documentReference $siblings) | ||
439 | #set ($discard = $siblings.add({ | ||
440 | 'id': "attachments:${documentReference}", | ||
441 | 'text': 'Attachments', | ||
442 | 'icon': 'fa fa-paperclip', | ||
443 | 'children': true, | ||
444 | 'data': { | ||
445 | 'type': 'attachments', | ||
446 | 'validChildren': ['addAttachment', 'attachment', 'pagination'], | ||
447 | 'hasContextMenu': true, | ||
448 | 'canDelete': $services.security.authorization.hasAccess('edit', $documentReference) | ||
449 | }, | ||
450 | 'a_attr': { | ||
451 | 'href': $xwiki.getURL($documentReference, 'view', 'viewer=attachments') | ||
452 | } | ||
453 | })) | ||
454 | #end | ||
455 | |||
456 | #macro (maybeAddAttachmentNode $attachmentId $siblings)) | ||
457 | #set ($attachmentReference = $services.model.resolveAttachment($attachmentId)) | ||
458 | #set ($document = $xwiki.getDocument($attachmentReference)) | ||
459 | #set ($attachment = $document.getAttachment($attachmentReference.name)) | ||
460 | #if ($attachment) | ||
461 | #addAttachmentNode($attachment $siblings) | ||
462 | #end | ||
463 | #end | ||
464 | |||
465 | #macro (addAttachmentNode $attachment $siblings) | ||
466 | #set ($attachmentReference = $services.model.createAttachmentReference($attachment.document.documentReference, | ||
467 | $attachment.filename)) | ||
468 | #set ($attachmentId = $services.model.serialize($attachmentReference, 'default')) | ||
469 | #set ($canEditDoc = $services.security.authorization.hasAccess('edit', $attachmentReference.parent)) | ||
470 | #getAttachmentIcon($attachment $icon) | ||
471 | #set ($discard = $siblings.add({ | ||
472 | 'id': "attachment:$attachmentId", | ||
473 | 'text': $attachment.filename, | ||
474 | 'icon': $icon, | ||
475 | 'children': false, | ||
476 | 'data': { | ||
477 | 'id': $attachmentId, | ||
478 | 'type': 'attachment', | ||
479 | 'validChildren': [], | ||
480 | 'hasContextMenu': true, | ||
481 | 'draggable': true, | ||
482 | 'canRename': $canEditDoc, | ||
483 | 'canDelete': $canEditDoc, | ||
484 | 'canMove': $canEditDoc, | ||
485 | 'canCopy': true, | ||
486 | 'deleteURL': $attachment.document.getAttachmentURL($attachment.filename, 'delattachment') | ||
487 | }, | ||
488 | 'a_attr': { | ||
489 | 'href': $attachment.document.getAttachmentURL($attachment.filename) | ||
490 | } | ||
491 | })) | ||
492 | #end | ||
493 | |||
494 | #set ($fileIconByMediaType = { | ||
495 | 'text': ['text/', 'application/xml', 'application/javascript', 'application/ecmascript', 'application/json', 'application/x-sh', '+xml'], | ||
496 | 'image': ['image/'], | ||
497 | 'audio': ['audio/'], | ||
498 | 'video': ['video/'], | ||
499 | 'pdf': ['application/pdf', 'application/postscript'], | ||
500 | 'word': ['application/msword', 'application/vnd.ms-word.', 'application/vnd.oasis.opendocument.text', 'application/vnd.openxmlformats-officedocument.word'], | ||
501 | 'powerpoint': ['application/vnd.ms-powerpoint', 'application/vnd.oasis.opendocument.presentation', 'application/vnd.openxmlformats-officedocument.presentation'], | ||
502 | 'excel': ['application/vnd.ms-excel', 'application/vnd.oasis.opendocument.spreadsheet', 'application/vnd.openxmlformats-officedocument.spreadsheet'], | ||
503 | 'archive': ['application/zip', 'application/x-gzip', 'application/x-bzip', 'application/x-tar', 'application/x-gtar', 'application/vnd.xara', '-archive', '-compressed', '-package', '+zip'] | ||
504 | }) | ||
505 | |||
506 | #macro (getAttachmentIcon $attachment $return) | ||
507 | #set ($mediaType = $attachment.mimeType) | ||
508 | #set ($icon = $NULL) | ||
509 | #foreach ($entry in $fileIconByMediaType.entrySet()) | ||
510 | #foreach ($pattern in $entry.value) | ||
511 | #if ($mediaType.startsWith($pattern) || $mediaType.endsWith($pattern)) | ||
512 | #set ($icon = $entry.key) | ||
513 | #break | ||
514 | #end | ||
515 | #end | ||
516 | #if ($icon) | ||
517 | #break | ||
518 | #end | ||
519 | #end | ||
520 | #set ($suffix = $stringtool.substringAfterLast($attachment.filename, '.')) | ||
521 | #set ($codeSuffixes = ['html', 'css', 'js', 'java', 'c', 'cpp', 'c++', 'cs', 'h', 'sql', 'php', 'ruby']) | ||
522 | #if (!$icon) | ||
523 | #set ($icon = 'fa fa-paperclip') | ||
524 | #elseif ($icon == 'text' && $codeSuffixes.contains($suffix)) | ||
525 | #set ($icon = 'fa fa-file-code-o') | ||
526 | #else | ||
527 | #set ($icon = "fa fa-file-${icon}-o") | ||
528 | #end | ||
529 | #set ($return = $NULL) | ||
530 | #setVariable("$return" $icon) | ||
531 | #end | ||
532 | |||
533 | #macro (maybeAddAddAttachmentNode $documentId $siblings) | ||
534 | #set ($documentReference = $services.model.resolveDocument($documentId)) | ||
535 | #if ($services.security.authorization.hasAccess('edit', $documentReference)) | ||
536 | #addAddAttachmentNode($documentReference $siblings) | ||
537 | #end | ||
538 | #end | ||
539 | |||
540 | #macro (addAddAttachmentNode $documentReference $siblings) | ||
541 | #set ($discard = $siblings.add({ | ||
542 | 'id': "addAttachment:$documentReference", | ||
543 | 'text': 'Upload file...', | ||
544 | 'icon': 'fa fa-plus-circle', | ||
545 | 'children': false, | ||
546 | 'data': { | ||
547 | 'type': 'addAttachment', | ||
548 | 'validChildren': [] | ||
549 | }, | ||
550 | 'a_attr': { | ||
551 | 'href': $xwiki.getURL($documentReference, 'view', 'viewer=attachments') | ||
552 | } | ||
553 | })) | ||
554 | #end | ||
555 | |||
556 | ## | ||
557 | ## Class Property Nodes | ||
558 | ## | ||
559 | |||
560 | #macro (maybeAddClassPropertiesNode $documentId $siblings) | ||
561 | #set ($documentReference = $services.model.resolveDocument($documentId)) | ||
562 | #if ($services.security.authorization.hasAccess('view', $documentReference)) | ||
563 | #addClassPropertiesNode($documentReference $siblings) | ||
564 | #end | ||
565 | #end | ||
566 | |||
567 | #macro (addClassPropertiesNode $documentReference $siblings) | ||
568 | #set ($discard = $children.add({ | ||
569 | 'id': "classProperties:${documentReference}", | ||
570 | 'text': 'Class Properties', | ||
571 | 'icon': 'fa fa-gears', | ||
572 | 'children': true, | ||
573 | 'data': { | ||
574 | 'type': 'classProperties', | ||
575 | 'validChildren': ['classProperty'], | ||
576 | 'canDelete': $services.security.authorization.hasAccess('edit', $documentReference) | ||
577 | } | ||
578 | })) | ||
579 | #end | ||
580 | |||
581 | #set ($iconByPropertyType = { | ||
582 | 'Boolean': 'check-square-o', | ||
583 | 'Date': 'calendar-o', | ||
584 | 'DBList': 'database', | ||
585 | 'Groups': 'group', | ||
586 | 'Password': 'asterisk', | ||
587 | 'Levels': 'lock', | ||
588 | 'StaticList': 'list', | ||
589 | 'TextArea': 'paragraph', | ||
590 | 'DBTreeList': 'sitemap', | ||
591 | 'Users': 'user' | ||
592 | }) | ||
593 | |||
594 | #macro (maybeAddClassPropertyNode $classPropertyId $siblings) | ||
595 | #set ($classPropertyReference = $services.model.resolveClassProperty($classPropertyId)) | ||
596 | #if ($services.security.authorization.hasAccess('view', $classPropertyReference.parent)) | ||
597 | #addClassPropertyNode($classPropertyReference $siblings) | ||
598 | #end | ||
599 | #end | ||
600 | |||
601 | #macro (addClassPropertyNode $classPropertyReference $siblings) | ||
602 | #set ($classPropertyId = $services.model.serialize($classPropertyReference, 'default')) | ||
603 | #set ($xclass = $xwiki.getDocument($classPropertyReference).getxWikiClass()) | ||
604 | #set ($property = $xclass.get($classPropertyReference.name)) | ||
605 | #set ($icon = $iconByPropertyType.get($property.classType)) | ||
606 | #if (!$icon) | ||
607 | #set ($icon = 'gear') | ||
608 | #end | ||
609 | #set ($discard = $siblings.add({ | ||
610 | 'id': "classProperty:$classPropertyId", | ||
611 | 'text': $property.name, | ||
612 | 'icon': "fa fa-$icon", | ||
613 | 'children': false, | ||
614 | 'data': { | ||
615 | 'id': $classPropertyId, | ||
616 | 'type': 'classProperty', | ||
617 | 'validChildren': [] | ||
618 | } | ||
619 | })) | ||
620 | #end | ||
621 | |||
622 | ## | ||
623 | ## Object Nodes | ||
624 | ## | ||
625 | |||
626 | #macro (maybeAddObjectsNode $documentId $siblings) | ||
627 | #set ($documentReference = $services.model.resolveDocument($documentId)) | ||
628 | #if ($services.security.authorization.hasAccess('view', $documentReference)) | ||
629 | #addObjectsNode($documentReference $siblings) | ||
630 | #end | ||
631 | #end | ||
632 | |||
633 | #macro (addObjectsNode $documentReference $siblings) | ||
634 | #set ($discard = $children.add({ | ||
635 | 'id': "objects:${documentReference}", | ||
636 | 'text': 'Objects', | ||
637 | 'icon': 'fa fa-cubes', | ||
638 | 'children': true, | ||
639 | 'data': { | ||
640 | 'type': 'objects', | ||
641 | 'validChildren': ['objectsOfType'], | ||
642 | 'canDelete': $services.security.authorization.hasAccess('edit', $documentReference) | ||
643 | } | ||
644 | })) | ||
645 | #end | ||
646 | |||
647 | #macro (maybeAddObjectsOfTypeNode $nodeReference $siblings) | ||
648 | #set ($parts = $nodeReference.split('/', 2)) | ||
649 | #if ($parts && $parts.size() == 2) | ||
650 | #set ($documentReference = $services.model.resolveDocument($parts.get(0))) | ||
651 | #set ($classReference = $services.model.resolveDocument($parts.get(1))) | ||
652 | #if ($services.security.authorization.hasAccess('view', $documentReference)) | ||
653 | #set ($discard = $children.add({ | ||
654 | 'id': "objectsOfType:$documentReference/$classReference", | ||
655 | 'text': $services.model.serialize($classReference, 'local'), | ||
656 | 'icon': 'fa fa-cubes', | ||
657 | 'children': true, | ||
658 | 'data': { | ||
659 | 'type': 'objectsOfType', | ||
660 | 'validChildren': ['object', 'pagination'], | ||
661 | 'canDelete': $services.security.authorization.hasAccess('edit', $documentReference) | ||
662 | } | ||
663 | })) | ||
664 | #end | ||
665 | #end | ||
666 | #end | ||
667 | |||
668 | #macro (maybeAddObjectNode $objectId $siblings) | ||
669 | #set ($objectReference = $services.model.resolveObject($objectId)) | ||
670 | #getXObject($objectReference) | ||
671 | #if ($object) | ||
672 | #addObjectNode($object $objectReference $siblings) | ||
673 | #end | ||
674 | #end | ||
675 | |||
676 | #macro (getXObject $objectReference) | ||
677 | ## Object name is: Space.Class[index] | ||
678 | #set ($separatorIndex = $objectReference.name.lastIndexOf('[')) | ||
679 | #set ($classId = $objectReference.name.substring(0, $separatorIndex)) | ||
680 | #set ($objectNumber = $numbertool.toNumber($objectReference.name.substring($mathtool.add($separatorIndex, 1), | ||
681 | $mathtool.sub($objectReference.name.length(), 1))).intValue()) | ||
682 | #set ($document = $xwiki.getDocument($objectReference)) | ||
683 | #set ($object = $document.getObject($classId, $objectNumber)) | ||
684 | #end | ||
685 | |||
686 | #macro (addObjectNode $object $objectReference $siblings) | ||
687 | #set ($objectId = $services.model.serialize($objectReference, 'default')) | ||
688 | #set ($discard = $children.add({ | ||
689 | 'id': "object:$objectId", | ||
690 | 'text': "[$object.number]", | ||
691 | 'icon': 'fa fa-cube', | ||
692 | 'children': true, | ||
693 | 'data': { | ||
694 | 'id': $objectId, | ||
695 | 'type': 'object', | ||
696 | 'validChildren': ['objectProperty'], | ||
697 | 'canDelete': $services.security.authorization.hasAccess('edit', $objectReference.parent) | ||
698 | } | ||
699 | })) | ||
700 | #end | ||
701 | |||
702 | #macro (maybeAddObjectPropertyNode $objectPropertyId $siblings) | ||
703 | #set ($objectPropertyReference = $services.model.resolveObjectProperty($objectPropertyId)) | ||
704 | #set ($objectReference = $objectPropertyReference.parent) | ||
705 | #getXObject($objectReference) | ||
706 | #set ($property = $object.getProperty($objectPropertyReference.name)) | ||
707 | #if ($property) | ||
708 | #addObjectPropertyNode($property $objectReference $siblings) | ||
709 | #end | ||
710 | #end | ||
711 | |||
712 | #macro (addObjectPropertyNode $property $objRef $siblings) | ||
713 | #set ($classId = $stringtool.substringBeforeLast($objRef.name, '[')) | ||
714 | #set ($classRef = $services.model.resolveDocument($classId, 'explicit', $objRef)) | ||
715 | #set ($xclass = $xwiki.getDocument($classRef).getxWikiClass()) | ||
716 | #set ($icon = $iconByPropertyType.get($xclass.get($property.name).classType)) | ||
717 | #if (!$icon) | ||
718 | #set ($icon = 'gear') | ||
719 | #end | ||
720 | #set ($objectPropertyReference = $services.model.createEntityReference($property.name, 'OBJECT_PROPERTY', $objRef)) | ||
721 | #set ($objectPropertyId = $services.model.serialize($objectPropertyReference, 'default')) | ||
722 | #set ($discard = $siblings.add({ | ||
723 | 'id': "objectProperty:$objectPropertyId", | ||
724 | 'text': $property.name, | ||
725 | 'icon': "fa fa-$icon", | ||
726 | 'children': false, | ||
727 | 'data': { | ||
728 | 'id': $objectPropertyId, | ||
729 | 'type': 'objectProperty', | ||
730 | 'validChildren': [] | ||
731 | } | ||
732 | })) | ||
733 | #end | ||
734 | |||
735 | ## | ||
736 | ## Pagination Nodes | ||
737 | ## | ||
738 | |||
739 | #macro (addPaginationNode $parentId $offset $totalCount $siblings) | ||
740 | #set ($discard = $siblings.add({ | ||
741 | 'id': "pagination:$parentId", | ||
742 | 'text': $services.localization.render('index.documentTree.more', $!mathtool.sub($totalCount, $offset)), | ||
743 | 'icon': 'fa fa-eye', | ||
744 | 'children': false, | ||
745 | 'data': { | ||
746 | 'type': 'pagination', | ||
747 | 'validChildren': [], | ||
748 | 'canDelete': true, | ||
749 | 'offset': $offset | ||
750 | } | ||
751 | })) | ||
752 | #end | ||
753 | |||
754 | ## | ||
755 | ## Empty Tree Node | ||
756 | ## | ||
757 | |||
758 | #macro (addEmptyTreeNode $siblings) | ||
759 | #set ($discard = $siblings.add({ | ||
760 | 'id': "empty", | ||
761 | 'text': $services.localization.render('index.documentTree.empty'), | ||
762 | 'icon': 'fa fa-info-circle', | ||
763 | 'children': false, | ||
764 | 'data': { | ||
765 | 'type': 'empty', | ||
766 | 'validChildren': [] | ||
767 | } | ||
768 | })) | ||
769 | #end | ||
770 | |||
771 | ##------------------------------------------------------------ | ||
772 | ## Path | ||
773 | ##------------------------------------------------------------ | ||
774 | |||
775 | #macro (getPath $nodeId $return) | ||
776 | #set ($path = []) | ||
777 | #if ($docTreeConfig.showRoot) | ||
778 | #maybeAddNode($docTreeConfig.root $path {}) | ||
779 | #end | ||
780 | #foreach ($pathElement in $tree.getPath($nodeId)) | ||
781 | #maybeAddNode($pathElement $path {}) | ||
782 | #end | ||
783 | #set ($return = $NULL) | ||
784 | #setVariable("$return" $path) | ||
785 | #end | ||
786 | |||
787 | ##------------------------------------------------------------ | ||
788 | ## Context Menu | ||
789 | ##------------------------------------------------------------ | ||
790 | |||
791 | #macro (getContextMenu $return) | ||
792 | #set ($contextMenuByNodeType = {}) | ||
793 | #if ($docTreeConfig.showSpaces) | ||
794 | #addSpaceContextMenu($contextMenuByNodeType) | ||
795 | #end | ||
796 | #addDocumentContextMenu($contextMenuByNodeType) | ||
797 | #if ($docTreeConfig.showAttachments) | ||
798 | #addAttachmentsContextMenu($contextMenuByNodeType) | ||
799 | #addAttachmentContextMenu($contextMenuByNodeType) | ||
800 | #end | ||
801 | #set ($return = $NULL) | ||
802 | #setVariable("$return" $contextMenuByNodeType) | ||
803 | #end | ||
804 | |||
805 | #macro (addSpaceContextMenu $contextMenuByNodeType) | ||
806 | #set ($contextMenuByNodeType.space = { | ||
807 | 'createDocument': { | ||
808 | 'label': 'New Page', | ||
809 | 'icon': 'fa fa-file-o', | ||
810 | 'action': 'openLink', | ||
811 | 'parameters': { | ||
812 | 'urlProperty': 'createDocumentURL' | ||
813 | } | ||
814 | }, | ||
815 | 'openLink': { | ||
816 | 'separator_before': true, | ||
817 | 'label': 'Go to Space', | ||
818 | 'icon': 'fa fa-external-link' | ||
819 | }, | ||
820 | 'refresh': { | ||
821 | 'label': 'Refresh', | ||
822 | 'icon': 'fa fa-refresh' | ||
823 | }, | ||
824 | 'paste': { | ||
825 | 'separator_before': true, | ||
826 | 'label': 'Paste Into Space', | ||
827 | 'icon': 'fa fa-clipboard' | ||
828 | }, | ||
829 | 'rename': { | ||
830 | 'label': 'Rename...', | ||
831 | 'icon': 'fa fa-pencil-square-o' | ||
832 | }, | ||
833 | 'remove': { | ||
834 | 'label': 'Delete', | ||
835 | 'icon': 'fa fa-trash-o', | ||
836 | 'parameters': { | ||
837 | '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.' | ||
838 | } | ||
839 | } | ||
840 | }) | ||
841 | #end | ||
842 | |||
843 | #macro (addDocumentContextMenu $contextMenuByNodeType) | ||
844 | #set ($contextMenuByNodeType.document = { | ||
845 | 'createDocument': { | ||
846 | 'label': 'New Page', | ||
847 | 'icon': 'fa fa-file-o', | ||
848 | 'action': 'openLink', | ||
849 | 'parameters': { | ||
850 | 'urlProperty': 'createDocumentURL' | ||
851 | } | ||
852 | }, | ||
853 | 'openLink': { | ||
854 | 'separator_before': true, | ||
855 | 'label': 'Go to Page', | ||
856 | 'icon': 'fa fa-external-link' | ||
857 | }, | ||
858 | 'refresh': { | ||
859 | 'label': 'Refresh', | ||
860 | 'icon': 'fa fa-refresh' | ||
861 | }, | ||
862 | 'cut': { | ||
863 | 'separator_before': true, | ||
864 | 'label': 'Cut', | ||
865 | 'icon': 'fa fa-scissors' | ||
866 | }, | ||
867 | 'copy': { | ||
868 | 'label': 'Copy', | ||
869 | 'icon': 'fa fa-files-o' | ||
870 | }, | ||
871 | 'paste': { | ||
872 | 'label': 'Paste', | ||
873 | 'icon': 'fa fa-clipboard' | ||
874 | }, | ||
875 | 'remove': { | ||
876 | 'separator_before': true, | ||
877 | 'label': 'Delete', | ||
878 | 'icon': 'fa fa-trash-o', | ||
879 | 'parameters': { | ||
880 | 'confirmationMessage': 'Are you sure you want to move this document to the recycle bin? All child documents will become orphan as a result.' | ||
881 | } | ||
882 | } | ||
883 | }) | ||
884 | #end | ||
885 | |||
886 | #macro (addAttachmentsContextMenu $contextMenuByNodeType) | ||
887 | #set ($contextMenuByNodeType.attachments = { | ||
888 | 'openLink': { | ||
889 | 'label': 'Go to Attachments', | ||
890 | 'icon': 'fa fa-external-link' | ||
891 | }, | ||
892 | 'refresh': { | ||
893 | 'label': 'Refresh', | ||
894 | 'icon': 'fa fa-refresh' | ||
895 | }, | ||
896 | 'paste': { | ||
897 | 'separator_before': true, | ||
898 | 'label': 'Paste', | ||
899 | 'icon': 'fa fa-clipboard' | ||
900 | }, | ||
901 | 'remove': { | ||
902 | 'label': 'Delete All', | ||
903 | 'icon': 'fa fa-trash-o', | ||
904 | 'parameters': { | ||
905 | 'confirmationMessage': 'Are you sure you want to delete all the attachments of this page?' | ||
906 | } | ||
907 | } | ||
908 | }) | ||
909 | #end | ||
910 | |||
911 | #macro (addAttachmentContextMenu $contextMenuByNodeType) | ||
912 | #set ($contextMenuByNodeType.attachment = { | ||
913 | 'openLink': { | ||
914 | 'label': 'Go to Attachment', | ||
915 | 'icon': 'fa fa-external-link' | ||
916 | }, | ||
917 | 'cut': { | ||
918 | 'separator_before': true, | ||
919 | 'label': 'Cut', | ||
920 | 'icon': 'fa fa-scissors' | ||
921 | }, | ||
922 | 'copy': { | ||
923 | 'label': 'Copy', | ||
924 | 'icon': 'fa fa-files-o' | ||
925 | }, | ||
926 | 'rename': { | ||
927 | 'separator_before': true, | ||
928 | 'label': 'Rename...', | ||
929 | 'icon': 'fa fa-pencil-square-o' | ||
930 | }, | ||
931 | 'remove': { | ||
932 | 'label': 'Delete', | ||
933 | 'icon': 'fa fa-trash-o', | ||
934 | 'parameters': { | ||
935 | 'confirmationMessage': 'Are you sure you want to delete this attachment?' | ||
936 | } | ||
937 | } | ||
938 | }) | ||
939 | #end | ||
940 | |||
941 | ##------------------------------------------------------------ | ||
942 | ## Finder Suggestions | ||
943 | ##------------------------------------------------------------ | ||
944 | |||
945 | #macro (getSuggestions $return) | ||
946 | #set ($limit = 6) | ||
947 | #set ($text = "$!request.query") | ||
948 | #set ($lists = []) | ||
949 | #getRootReference | ||
950 | #set ($ancestorsOf = { | ||
951 | 'space': ['farm', 'wiki', 'space'], | ||
952 | 'document': ['farm', 'wiki', 'space', 'document'], | ||
953 | 'attachment': ['farm', 'wiki', 'space', 'document', 'attachments'] | ||
954 | }) | ||
955 | #if ((!$docTreeConfig.showSpaces || $docTreeConfig.showTerminalDocuments) | ||
956 | && $ancestorsOf.document.contains($rootType)) | ||
957 | #addDocumentSuggestions($text $limit $lists) | ||
958 | #end | ||
959 | #if ($docTreeConfig.showAttachments && $ancestorsOf.attachment.contains($rootType)) | ||
960 | #addAttachmentSuggestions($text $limit $lists) | ||
961 | #end | ||
962 | #if ($docTreeConfig.showSpaces && $ancestorsOf.space.contains($rootType)) | ||
963 | #addSpaceSuggestions($text $limit $lists) | ||
964 | #end | ||
965 | #limitTotalCount($lists $limit) | ||
966 | #set ($output = []) | ||
967 | #foreach ($list in $lists) | ||
968 | #foreach ($node in $list) | ||
969 | ## Use the node path as suggestion info. | ||
970 | #getPath($node.id $path) | ||
971 | ## The path is empty when the node is not found in the tree. This happens if the tree finder doesn't restrict the | ||
972 | ## search to the nodes that are available in the tree. | ||
973 | #if ($path.size() > 0) | ||
974 | #displayPath($path) | ||
975 | #set ($node.data.info = $stringtool.join($path.subList(0, $mathtool.sub($path.size(), 1)), ' / ')) | ||
976 | #set ($discard = $output.add($node)) | ||
977 | #end | ||
978 | #end | ||
979 | #end | ||
980 | #set ($return = $NULL) | ||
981 | #setVariable("$return" $output) | ||
982 | #end | ||
983 | |||
984 | #macro (getRootReference) | ||
985 | #set ($parts = $docTreeConfig.root.split(':', 2)) | ||
986 | #if ($parts.size() == 2) | ||
987 | #set ($rootType = $parts[0]) | ||
988 | #set ($rootReference = $parts[1]) | ||
989 | #if ($rootType == 'wiki') | ||
990 | #set ($rootReference = $services.model.createWikiReference($parts[1])) | ||
991 | #elseif ($rootType == 'space') | ||
992 | #set ($rootReference = $services.model.resolveSpace($parts[1])) | ||
993 | #elseif ($rootType == 'document' || $rootType == 'attachments') | ||
994 | #set ($rootReference = $services.model.resolveDocument($parts[1])) | ||
995 | #end | ||
996 | #else | ||
997 | #set ($rootType = 'unknown') | ||
998 | #set ($rootReference = $parts[0]) | ||
999 | #end | ||
1000 | #end | ||
1001 | |||
1002 | #macro (addSpaceSuggestions $text $limit $suggestions) | ||
1003 | #searchSpaces($text $limit $spaceReferences) | ||
1004 | #set ($spaceSuggestions = []) | ||
1005 | #foreach ($spaceReference in $spaceReferences) | ||
1006 | #maybeAddSpaceNode($spaceReference $spaceSuggestions) | ||
1007 | #end | ||
1008 | #set ($discard = $suggestions.add($spaceSuggestions)) | ||
1009 | #end | ||
1010 | |||
1011 | #macro (searchSpaces $text $limit $return) | ||
1012 | #set ($constraints = ["upper(space.name) like upper(:spaceNamePattern) escape '!'"]) | ||
1013 | #set ($params = {'spaceNamePattern': "%$!text.replaceAll('([%_!])', '!$1')%"}) | ||
1014 | #addSpaceLocationDatabaseConstraint($rootReference $constraints $params 'space.reference') | ||
1015 | #set ($statement = "select space.reference from XWikiSpace space where $stringtool.join($constraints, ' and ') " | ||
1016 | + "order by lower(space.reference), space.reference") | ||
1017 | #set ($query = $services.query.hql($statement).setLimit($limit)) | ||
1018 | #addWikiLocationDatabaseConstraint($rootReference $query) | ||
1019 | #if ($docTreeConfig.filterHiddenDocuments) | ||
1020 | #set ($query = $query.addFilter('hidden/space')) | ||
1021 | #end | ||
1022 | #foreach ($entry in $params.entrySet()) | ||
1023 | #set ($query = $query.bindValue($entry.key, $entry.value)) | ||
1024 | #end | ||
1025 | #set ($spaceReferences = []) | ||
1026 | #foreach ($localSpaceRef in $query.execute()) | ||
1027 | #set ($discard = $spaceReferences.add($services.model.resolveSpace($localSpaceRef))) | ||
1028 | #end | ||
1029 | #set ($return = $NULL) | ||
1030 | #setVariable("$return" $spaceReferences) | ||
1031 | #end | ||
1032 | |||
1033 | #macro (addDocumentSuggestions $text $limit $suggestions) | ||
1034 | #searchDocuments($text $limit $documentReferences) | ||
1035 | #set ($docSuggestions = []) | ||
1036 | #foreach ($documentReference in $documentReferences) | ||
1037 | #maybeAddDocumentNode($documentReference $docSuggestions) | ||
1038 | #end | ||
1039 | #set ($discard = $suggestions.add($docSuggestions)) | ||
1040 | #end | ||
1041 | |||
1042 | #macro (searchDocuments $text $limit $return) | ||
1043 | #if ($xwiki.exists('XWiki.SuggestSolrMacros')) | ||
1044 | #searchDocumentsSolr($text $limit $return) | ||
1045 | #else | ||
1046 | #searchDocumentsDatabase($text $limit $return) | ||
1047 | #end | ||
1048 | #end | ||
1049 | |||
1050 | #macro (searchDocumentsSolr $text $limit $return) | ||
1051 | #set ($params = [ | ||
1052 | 'fq=type:DOCUMENT', | ||
1053 | 'fq=doclocale:""', | ||
1054 | 'qf=title^6 name^4 doccontent^2 doccontentraw', | ||
1055 | 'fl=wiki spaces name' | ||
1056 | ]) | ||
1057 | #addCommonDocTreeSolrParams($params) | ||
1058 | #set ($params = $stringtool.join($params, $util.newline)) | ||
1059 | #createSearchSuggestQuery($params $text $query) | ||
1060 | #set ($discard = $query.setLimit($limit)) | ||
1061 | #set ($documentReferences = []) | ||
1062 | #foreach ($result in $query.execute()[0].results) | ||
1063 | #set ($discard = $documentReferences.add($services.solr.resolveDocument($result))) | ||
1064 | #end | ||
1065 | #set ($return = $NULL) | ||
1066 | #setVariable("$return" $documentReferences) | ||
1067 | #end | ||
1068 | |||
1069 | #macro (searchDocumentsDatabase $text $limit $return) | ||
1070 | #set ($constraints = [ | ||
1071 | 'doc.translation = 0', | ||
1072 | 'doc.space = space.reference' | ||
1073 | ]) | ||
1074 | #set ($defaultDocumentName = $services.model.getEntityReference('DOCUMENT', 'default').name) | ||
1075 | #set ($matchDocTitle = "upper(doc.title) like upper(:text) escape '!'") | ||
1076 | #set ($params = {'text': "%$!text.replaceAll('([%_!])', '!$1')%"}) | ||
1077 | #if ($docTreeConfig.showTerminalDocuments) | ||
1078 | #set ($matchDocName = "(doc.name <> '$defaultDocumentName' and upper(doc.name) like upper(:text) escape '!')") | ||
1079 | #set ($matchSpaceName = "(doc.name = '$defaultDocumentName' and upper(space.name) like upper(:text) escape '!')") | ||
1080 | #set ($discard = $constraints.add("($matchDocTitle or $matchDocName or $matchSpaceName)")) | ||
1081 | #else | ||
1082 | #set ($matchSpaceName = "upper(space.name) like upper(:text) escape '!'") | ||
1083 | #set ($discard = $constraints.addAll([ | ||
1084 | "doc.name = '$defaultDocumentName'", | ||
1085 | "($matchDocTitle or $matchSpaceName)" | ||
1086 | ])) | ||
1087 | #end | ||
1088 | #addDocumentLocationDatabaseConstraint($rootReference $constraints $params) | ||
1089 | #set ($constraints = $stringtool.join($constraints, ' and ')) | ||
1090 | #set ($statement = "select doc.fullName from XWikiDocument doc, XWikiSpace space where $constraints") | ||
1091 | #set ($query = $services.query.hql($statement).setLimit($limit)) | ||
1092 | #foreach ($entry in $params.entrySet()) | ||
1093 | #set ($query = $query.bindValue($entry.key, $entry.value)) | ||
1094 | #end | ||
1095 | #addWikiLocationDatabaseConstraint($rootReference $query) | ||
1096 | #if ($docTreeConfig.filterHiddenDocuments) | ||
1097 | #set ($query = $query.addFilter('hidden/document')) | ||
1098 | #end | ||
1099 | #set ($documentReferences = []) | ||
1100 | #foreach ($docFullName in $query.execute()) | ||
1101 | #set ($discard = $documentReferences.add($services.model.resolveDocument($docFullName))) | ||
1102 | #end | ||
1103 | #set ($return = $NULL) | ||
1104 | #setVariable("$return" $documentReferences) | ||
1105 | #end | ||
1106 | |||
1107 | #macro (addAttachmentSuggestions $text $limit $suggestions) | ||
1108 | #searchAttachments($text $limit $attachmentReferences) | ||
1109 | #set ($attachmentSuggestions = []) | ||
1110 | #foreach ($attachmentReference in $attachmentReferences) | ||
1111 | #set ($attachment = $xwiki.getDocument($attachmentReference.parent).getAttachment($attachmentReference.name)) | ||
1112 | #addAttachmentNode($attachment $attachmentSuggestions) | ||
1113 | #end | ||
1114 | #set ($discard = $suggestions.add($attachmentSuggestions)) | ||
1115 | #end | ||
1116 | |||
1117 | #macro (searchAttachments $text $limit $return) | ||
1118 | #if ($xwiki.exists('XWiki.SuggestSolrMacros')) | ||
1119 | #searchAttachmentsSolr($text $limit $return) | ||
1120 | #else | ||
1121 | #searchAttachmentsDatabase($text $limit $return) | ||
1122 | #end | ||
1123 | #end | ||
1124 | |||
1125 | #macro (searchAttachmentsSolr $text $limit $return) | ||
1126 | #set ($params = [ | ||
1127 | 'fq=type:ATTACHMENT', | ||
1128 | 'qf=filename^4 attcontent', | ||
1129 | 'fl=type wiki spaces name filename' | ||
1130 | ]) | ||
1131 | #addCommonDocTreeSolrParams($params) | ||
1132 | #set ($params = $stringtool.join($params, $util.newline)) | ||
1133 | #createSearchSuggestQuery($params $text $query) | ||
1134 | #set ($discard = $query.setLimit($limit)) | ||
1135 | #set ($attachmentReferences = []) | ||
1136 | #foreach ($result in $query.execute()[0].results) | ||
1137 | #set ($discard = $attachmentReferences.add($services.solr.resolve($result))) | ||
1138 | #end | ||
1139 | #set ($return = $NULL) | ||
1140 | #setVariable("$return" $attachmentReferences) | ||
1141 | #end | ||
1142 | |||
1143 | #macro (searchAttachmentsDatabase $text $limit $return) | ||
1144 | #set ($constraints = ["upper(attach.filename) like upper(:text) escape'!'"]) | ||
1145 | #set ($params = {'text': "%$!text.replaceAll('([%_!])', '!$1')%"}) | ||
1146 | #if ($docTreeConfig.filterHiddenDocuments && "$!xwiki.getUserPreference('displayHiddenDocuments')" != '1') | ||
1147 | #set ($discard = $constraints.add("(doc.hidden <> true or doc.hidden is null)")) | ||
1148 | #end | ||
1149 | #set ($exactMatch = $rootType == 'attachments') | ||
1150 | #addDocumentLocationDatabaseConstraint($rootReference $constraints $params $exactMatch) | ||
1151 | #set ($statement = "where $stringtool.join($constraints, ' and ')") | ||
1152 | ## | ||
1153 | ## Convert named parameters to positional parameters. | ||
1154 | #set ($paramList = []) | ||
1155 | #foreach ($item in $regextool.findAll($statement, ':(\w+)')) | ||
1156 | #set ($paramName = $item.get(1).getGroup()) | ||
1157 | #set ($discard = $paramList.add($params.get($paramName))) | ||
1158 | #end | ||
1159 | #set ($statement = $statement.replaceAll(':\w+', '\?')) | ||
1160 | ## | ||
1161 | ## TODO: Search in the wiki that corresponds to the root node. | ||
1162 | #set ($attachments = $xwiki.searchAttachments($statement, $limit, 0, $paramList)) | ||
1163 | #set ($attachmentReferences = []) | ||
1164 | #foreach ($attachment in $attachments) | ||
1165 | #set ($discard = $attachmentReferences.add($services.model.createAttachmentReference( | ||
1166 | $attachment.document.documentReference, $attachment.filename))) | ||
1167 | #end | ||
1168 | #set ($return = $NULL) | ||
1169 | #setVariable("$return" $attachmentReferences) | ||
1170 | #end | ||
1171 | |||
1172 | #macro (addCommonDocTreeSolrParams $params) | ||
1173 | #if ($rootType == 'wiki') | ||
1174 | ## Limit the search to the specified wiki. | ||
1175 | #addWikiLocationSolrParams($rootReference $params) | ||
1176 | #elseif ($rootType == 'space') | ||
1177 | ## Limit the search to the specified space. | ||
1178 | #addSpaceLocationSolrParams($rootReference $params) | ||
1179 | #elseif ($rootType == 'document') | ||
1180 | ## Limit the search to the specified document. | ||
1181 | #addDocumentLocationSolrParams($rootReference $params) | ||
1182 | #elseif ($rootType == 'attachments') | ||
1183 | ## Limit the search to the attachments of the specified document. | ||
1184 | #addDocumentLocationSolrParams($rootReference $params true) | ||
1185 | #end | ||
1186 | #if (!$docTreeConfig.showTerminalDocuments) | ||
1187 | #set ($defaultDocumentName = $services.model.getEntityReference('DOCUMENT', 'default').name) | ||
1188 | #set ($discard = $params.add("fq=name:$defaultDocumentName")) | ||
1189 | #end | ||
1190 | #if (!$docTreeConfig.filterHiddenDocuments) | ||
1191 | ## Force the inclusion of the hidden documents. | ||
1192 | #set ($discard = $params.add("fq=hidden:*")) | ||
1193 | #end | ||
1194 | #end | ||
1195 | |||
1196 | #macro (addWikiLocationSolrParams $rootReference $params) | ||
1197 | #set ($wikiReference = $rootReference.extractReference('WIKI')) | ||
1198 | #if ($wikiReference) | ||
1199 | #set ($discard = $params.add("fq=wiki:$wikiReference.name")) | ||
1200 | #end | ||
1201 | #end | ||
1202 | |||
1203 | #macro (addWikiLocationDatabaseConstraint $rootReference $query) | ||
1204 | #set ($wikiReference = $rootReference.extractReference('WIKI')) | ||
1205 | #if ($wikiReference) | ||
1206 | #set ($query = $query.setWiki($wikiReference.name)) | ||
1207 | #end | ||
1208 | #end | ||
1209 | |||
1210 | #macro (addSpaceLocationSolrParams $rootReference $params $exactMatch) | ||
1211 | #addWikiLocationSolrParams($rootReference $params) | ||
1212 | #set ($spaceReference = $rootReference.extractReference('SPACE')) | ||
1213 | #if ($spaceReference && ($docTreeConfig.showSpaces || $docTreeConfig.hierarchyMode == 'reference')) | ||
1214 | #set ($localSpaceReference = $services.model.serialize($spaceReference, 'local')) | ||
1215 | #set ($spaceField = 'space_prefix') | ||
1216 | #if ($exactMatch) | ||
1217 | #set ($spaceField = 'space_exact') | ||
1218 | #end | ||
1219 | #set ($discard = $params.add("fq=$spaceField:""$localSpaceReference""")) | ||
1220 | #end | ||
1221 | #end | ||
1222 | |||
1223 | #macro (addSpaceLocationDatabaseConstraint $rootReference $constraints $params $field) | ||
1224 | #set ($spaceReference = $rootReference.extractReference('SPACE')) | ||
1225 | #if ($spaceReference && ($docTreeConfig.showSpaces || $docTreeConfig.hierarchyMode == 'reference')) | ||
1226 | #set ($discard = $constraints.add("($field = :localSpaceReference or $field like :spaceReferencePattern escape '!')")) | ||
1227 | #set ($localSpaceReference = $services.model.serialize($spaceReference, 'local')) | ||
1228 | #set ($discard = $params.put('localSpaceReference', $localSpaceReference)) | ||
1229 | #set ($spaceReferencePattern = $services.model.createEntityReference('x', 'SPACE', $spaceReference)) | ||
1230 | #set ($spaceReferencePattern = $services.model.serialize($spaceReferencePattern, 'local')) | ||
1231 | #set ($spaceReferencePattern = $stringtool.removeEnd($spaceReferencePattern, 'x').replaceAll('([%_!])', '!$1')) | ||
1232 | #set ($discard = $params.put('spaceReferencePattern', "$spaceReferencePattern%")) | ||
1233 | #end | ||
1234 | #end | ||
1235 | |||
1236 | #macro (addDocumentLocationSolrParams $rootReference $params $exactMatch) | ||
1237 | #set ($documentReference = $rootReference.extractReference('DOCUMENT')) | ||
1238 | #set ($defaultDocumentName = $services.model.getEntityReference('DOCUMENT', 'default').name) | ||
1239 | #set ($macro.exactMatch = $exactMatch || ($docTreeConfig.hierarchyMode == 'reference' | ||
1240 | && $documentReference && $documentReference.name != $defaultDocumentName)) | ||
1241 | #addSpaceLocationSolrParams($rootReference $params $macro.exactMatch) | ||
1242 | #if ($documentReference && $macro.exactMatch) | ||
1243 | #set ($discard = $params.add("fq=name_exact:""$documentReference.name""")) | ||
1244 | #end | ||
1245 | #end | ||
1246 | |||
1247 | #macro (addDocumentLocationDatabaseConstraint $rootReference $constraints $params $exactMatch) | ||
1248 | #set ($documentReference = $rootReference.extractReference('DOCUMENT')) | ||
1249 | #set ($defaultDocumentName = $services.model.getEntityReference('DOCUMENT', 'default').name) | ||
1250 | #set ($macro.exactMatch = $exactMatch || ($docTreeConfig.hierarchyMode == 'reference' | ||
1251 | && $documentReference && $documentReference.name != $defaultDocumentName)) | ||
1252 | #if ($documentReference && $macro.exactMatch) | ||
1253 | #set ($localDocumentReference = $services.model.serialize($documentReference, 'local')) | ||
1254 | #set ($discard = $constraints.add('doc.fullName = :localDocumentReference')) | ||
1255 | #set ($discard = $params.put('localDocumentReference', $localDocumentReference)) | ||
1256 | #elseif (!$macro.exactMatch) | ||
1257 | #addSpaceLocationDatabaseConstraint($rootReference $constraints $params 'doc.space') | ||
1258 | #end | ||
1259 | #end | ||
1260 | |||
1261 | #macro (displayPath $path) | ||
1262 | #foreach ($node in $path) | ||
1263 | #set ($discard = $path.set($foreach.index, $node.text)) | ||
1264 | #end | ||
1265 | #end | ||
1266 | |||
1267 | #macro (limitTotalCount $lists $limit) | ||
1268 | ## Prepare the input. | ||
1269 | #set ($input = []) | ||
1270 | #foreach ($list in $lists) | ||
1271 | ## We use queues to be able to easily remove items from the start. | ||
1272 | #set ($queue = $collectiontool.queue) | ||
1273 | #set ($discard = $queue.addAll($list)) | ||
1274 | #set ($discard = $input.add($queue)) | ||
1275 | ## We will add (part of) the items back later. | ||
1276 | #set ($discard = $list.clear()) | ||
1277 | #end | ||
1278 | ## Limit the total item count. | ||
1279 | #set ($index = -1) | ||
1280 | #foreach ($count in [1..$limit]) | ||
1281 | #foreach ($i in [1..$input.size()]) | ||
1282 | #set ($newIndex = ($index + $i) % $input.size()) | ||
1283 | #if ($input.get($newIndex).size() > 0) | ||
1284 | #set ($index = $newIndex) | ||
1285 | #break | ||
1286 | #end | ||
1287 | #end | ||
1288 | #if ($index < 0 || $input.get($index).isEmpty()) | ||
1289 | #break | ||
1290 | #else | ||
1291 | #set ($discard = $lists.get($index).add($input.get($index).poll())) | ||
1292 | #end | ||
1293 | #end | ||
1294 | #end | ||
1295 | {{/velocity}} |