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