From 755271cc42bf25c108f10c16db73da68332808c9 Mon Sep 17 00:00:00 2001
From: "Brian M. Long"
Date: Fri, 3 Mar 2023 16:02:39 -0500
Subject: [PATCH] initial checkin
---
.gitignore | 13 +
pom.xml | 88 +++++
rad.ps1 | 74 +++++
rad.sh | 71 ++++
.../alfresco/bulk/AbstractNodesWebScript.java | 136 ++++++++
.../alfresco/bulk/CreateNodesWebScript.java | 305 ++++++++++++++++++
.../alfresco/bulk/UpdateNodesWebScript.java | 305 ++++++++++++++++++
.../alfresco/bulk/model/AssociationBody.java | 73 +++++
.../bulk/model/ChildAssociationBody.java | 73 +++++
.../alfresco/bulk/model/ContentDataBody.java | 73 +++++
.../alfresco/bulk/model/NodeBodyCreate.java | 230 +++++++++++++
.../bulk/model/NodeBodyCreateAssociation.java | 51 +++
.../bulk/model/NodeBodyCreateExt.java | 49 +++
.../alfresco/bulk/model/NodeBodyUpdate.java | 154 +++++++++
.../bulk/model/NodeBodyUpdateExt.java | 99 ++++++
.../bulk/model/PermissionElement.java | 134 ++++++++
.../bulk/model/PermissionsBodyUpdate.java | 79 +++++
.../alfresco/bulk/createNodes.post.desc.xml | 18 ++
.../alfresco/bulk/updateNodes.put.desc.xml | 18 ++
.../alfresco-global.properties | 8 +
.../log4j.properties | 1 +
.../module-context.xml | 13 +
.../module.properties | 4 +
.../alfresco/extension/debug-log4j.properties | 8 +
.../disable-webscript-caching-context.xml | 63 ++++
25 files changed, 2140 insertions(+)
create mode 100644 .gitignore
create mode 100644 pom.xml
create mode 100644 rad.ps1
create mode 100644 rad.sh
create mode 100644 src/main/java/com/inteligr8/alfresco/bulk/AbstractNodesWebScript.java
create mode 100644 src/main/java/com/inteligr8/alfresco/bulk/CreateNodesWebScript.java
create mode 100644 src/main/java/com/inteligr8/alfresco/bulk/UpdateNodesWebScript.java
create mode 100644 src/main/java/com/inteligr8/alfresco/bulk/model/AssociationBody.java
create mode 100644 src/main/java/com/inteligr8/alfresco/bulk/model/ChildAssociationBody.java
create mode 100644 src/main/java/com/inteligr8/alfresco/bulk/model/ContentDataBody.java
create mode 100644 src/main/java/com/inteligr8/alfresco/bulk/model/NodeBodyCreate.java
create mode 100644 src/main/java/com/inteligr8/alfresco/bulk/model/NodeBodyCreateAssociation.java
create mode 100644 src/main/java/com/inteligr8/alfresco/bulk/model/NodeBodyCreateExt.java
create mode 100644 src/main/java/com/inteligr8/alfresco/bulk/model/NodeBodyUpdate.java
create mode 100644 src/main/java/com/inteligr8/alfresco/bulk/model/NodeBodyUpdateExt.java
create mode 100644 src/main/java/com/inteligr8/alfresco/bulk/model/PermissionElement.java
create mode 100644 src/main/java/com/inteligr8/alfresco/bulk/model/PermissionsBodyUpdate.java
create mode 100644 src/main/resources/alfresco/extension/templates/webscripts/com/inteligr8/alfresco/bulk/createNodes.post.desc.xml
create mode 100644 src/main/resources/alfresco/extension/templates/webscripts/com/inteligr8/alfresco/bulk/updateNodes.put.desc.xml
create mode 100644 src/main/resources/alfresco/module/com.inteligr8.alfresco.bulk-acs-module/alfresco-global.properties
create mode 100644 src/main/resources/alfresco/module/com.inteligr8.alfresco.bulk-acs-module/log4j.properties
create mode 100644 src/main/resources/alfresco/module/com.inteligr8.alfresco.bulk-acs-module/module-context.xml
create mode 100644 src/main/resources/alfresco/module/com.inteligr8.alfresco.bulk-acs-module/module.properties
create mode 100644 src/test/resources/alfresco/extension/debug-log4j.properties
create mode 100644 src/test/resources/alfresco/extension/disable-webscript-caching-context.xml
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4e28014
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,13 @@
+# Maven
+target
+pom.xml.versionsBackup
+
+# Eclipse
+.settings
+.project
+.classpath
+
+# Visual Studio Code
+.factorypath
+.vscode
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..fd769c6
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,88 @@
+
+ 4.0.0
+
+ com.inteligr8.alfresco
+ bulk-platform-module
+ 1.0-SNAPSHOT
+ jar
+
+ bulk ACS Platform Module
+
+
+ UTF-8
+ 8
+ 8
+
+ 4.2.0
+ 6.2.0-ga
+
+
+
+
+
+ org.alfresco
+ acs-community-packaging
+ ${alfresco.platform.version}
+ pom
+ import
+
+
+
+
+
+
+
+
+ org.alfresco
+ alfresco-repository
+ provided
+
+
+ io.swagger
+ swagger-jaxrs
+ 1.6.2
+ provided
+
+
+
+
+
+
+ io.repaint.maven
+ tiles-maven-plugin
+ 2.26
+ true
+
+
+
+ com.inteligr8.ootbee:beedk-acs-platform-self-rad-tile:[1.0.0,2.0.0)
+
+ com.inteligr8.ootbee:beedk-acs-platform-module-tile:[1.0.0,2.0.0)
+
+
+
+
+
+
+
+
+
+ inteligr8-releases
+ http://repos.inteligr8.com/nexus/repository/inteligr8-public
+
+
+ alfresco-public
+ https://artifacts.alfresco.com/nexus/content/groups/public
+
+
+
+
+
+ inteligr8-releases
+ http://repos.inteligr8.com/nexus/repository/inteligr8-public
+
+
+
\ No newline at end of file
diff --git a/rad.ps1 b/rad.ps1
new file mode 100644
index 0000000..61bcb2f
--- /dev/null
+++ b/rad.ps1
@@ -0,0 +1,74 @@
+
+function discoverArtifactId {
+ $script:ARTIFACT_ID=(mvn -q -Dexpression=project"."artifactId -DforceStdout help:evaluate)
+}
+
+function rebuild {
+ echo "Rebuilding project ..."
+ mvn process-classes
+}
+
+function start_ {
+ echo "Rebuilding project and starting Docker containers to support rapid application development ..."
+ mvn -Drad process-classes
+}
+
+function start_log {
+ echo "Rebuilding project and starting Docker containers to support rapid application development ..."
+ mvn -Drad "-Ddocker.showLogs" process-classes
+}
+
+function stop_ {
+ discoverArtifactId
+ echo "Stopping Docker containers that supported rapid application development ..."
+ docker container ls --filter name=${ARTIFACT_ID}-*
+ echo "Stopping containers ..."
+ docker container stop (docker container ls -q --filter name=${ARTIFACT_ID}-*)
+ echo "Removing containers ..."
+ docker container rm (docker container ls -aq --filter name=${ARTIFACT_ID}-*)
+}
+
+function tail_logs {
+ param (
+ $container
+ )
+
+ discoverArtifactId
+ docker container logs -f (docker container ls -q --filter name=${ARTIFACT_ID}-${container})
+}
+
+function list {
+ discoverArtifactId
+ docker container ls --filter name=${ARTIFACT_ID}-*
+}
+
+switch ($args[0]) {
+ "start" {
+ start_
+ }
+ "start_log" {
+ start_log
+ }
+ "stop" {
+ stop_
+ }
+ "restart" {
+ stop_
+ start_
+ }
+ "rebuild" {
+ rebuild
+ }
+ "tail" {
+ tail_logs $args[1]
+ }
+ "containers" {
+ list
+ }
+ default {
+ echo "Usage: .\rad.ps1 [ start | start_log | stop | restart | rebuild | tail {container} | containers ]"
+ }
+}
+
+echo "Completed!"
+
diff --git a/rad.sh b/rad.sh
new file mode 100644
index 0000000..7cb0a80
--- /dev/null
+++ b/rad.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+discoverArtifactId() {
+ ARTIFACT_ID=`mvn -q -Dexpression=project.artifactId -DforceStdout help:evaluate`
+}
+
+rebuild() {
+ echo "Rebuilding project ..."
+ mvn process-classes
+}
+
+start() {
+ echo "Rebuilding project and starting Docker containers to support rapid application development ..."
+ mvn -Drad process-classes
+}
+
+start_log() {
+ echo "Rebuilding project and starting Docker containers to support rapid application development ..."
+ mvn -Drad -Ddocker.showLogs process-classes
+}
+
+stop() {
+ discoverArtifactId
+ echo "Stopping Docker containers that supported rapid application development ..."
+ docker container ls --filter name=${ARTIFACT_ID}-*
+ echo "Stopping containers ..."
+ docker container stop `docker container ls -q --filter name=${ARTIFACT_ID}-*`
+ echo "Removing containers ..."
+ docker container rm `docker container ls -aq --filter name=${ARTIFACT_ID}-*`
+}
+
+tail_logs() {
+ discoverArtifactId
+ docker container logs -f `docker container ls -q --filter name=${ARTIFACT_ID}-$1`
+}
+
+list() {
+ discoverArtifactId
+ docker container ls --filter name=${ARTIFACT_ID}-*
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ start_log)
+ start_log
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ stop
+ start
+ ;;
+ rebuild)
+ rebuild
+ ;;
+ tail)
+ tail_logs $2
+ ;;
+ containers)
+ list
+ ;;
+ *)
+ echo "Usage: ./rad.sh [ start | start_log | stop | restart | rebuild | tail {container} | containers ]"
+ exit 1
+esac
+
+echo "Completed!"
+
diff --git a/src/main/java/com/inteligr8/alfresco/bulk/AbstractNodesWebScript.java b/src/main/java/com/inteligr8/alfresco/bulk/AbstractNodesWebScript.java
new file mode 100644
index 0000000..c5f2d10
--- /dev/null
+++ b/src/main/java/com/inteligr8/alfresco/bulk/AbstractNodesWebScript.java
@@ -0,0 +1,136 @@
+/*
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+package com.inteligr8.alfresco.bulk;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.policy.BehaviourFilter;
+import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.cmr.repository.ContentReader;
+import org.alfresco.service.cmr.repository.ContentService;
+import org.alfresco.service.cmr.repository.MimetypeService;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.transaction.TransactionService;
+import org.apache.tika.Tika;
+import org.apache.tika.config.TikaConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.extensions.webscripts.AbstractWebScript;
+import org.springframework.extensions.webscripts.WebScriptException;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.inteligr8.alfresco.bulk.model.ContentDataBody;
+
+public abstract class AbstractNodesWebScript extends AbstractWebScript {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ @Value("${inteligr8.bulk.enableTika:false}")
+ private boolean enableTika;
+
+ @Value("${inteligr8.bulk.allowOctetStream:false}")
+ protected boolean allowOctetStream;
+
+ @Autowired
+ protected ContentService contentService;
+
+ @Autowired
+ protected DictionaryService dictionaryService;
+
+ @Autowired
+ protected MimetypeService mimeTypeService;
+
+ @Autowired
+ protected NamespaceService namespaceService;
+
+ @Autowired
+ protected NodeService nodeService;
+
+ @Autowired
+ protected BehaviourFilter behaviorFilter;
+
+ @Autowired
+ protected TransactionService txService;
+
+ protected final ObjectMapper om = new ObjectMapper();
+
+ protected final Collection jsonMediaTypes =
+ MediaType.parseMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON_VALUE, "applicatin/*+json"));
+
+ protected List wrapContents(ContentDataBody contentData, List contents) {
+ List allcontents = new ArrayList<>(contents == null ? 1 : (contents.size() + 1));
+ if (contentData != null)
+ allcontents.add(contentData);
+ if (contents != null)
+ allcontents.addAll(contents);
+ return allcontents;
+ }
+
+ protected void validateRequest(List> nodes) {
+ if (nodes == null || nodes.isEmpty())
+ throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), "At least one node is required");
+ }
+
+ protected String determineMimeType(QName contentPropName, ContentReader creader, String name) {
+ if (this.enableTika) {
+ this.logger.trace("Using Tika to detect the MIME type: {} => {}", creader.getContentUrl(), name);
+
+ try {
+ InputStream istream = creader.getContentInputStream();
+ try {
+ Tika tika = new Tika(TikaConfig.getDefaultConfig());
+ String mimeType = tika.detect(istream, name);
+ this.logger.trace("Tika detected MIME type: {} => {}", creader.getContentUrl(), mimeType);
+
+ if (!MediaType.APPLICATION_OCTET_STREAM_VALUE.equals(mimeType))
+ return mimeType;
+ } finally {
+ istream.close();
+ }
+ } catch (IOException ie) {
+ this.logger.warn("Tika MIME detection failed: {}: {}", creader.getContentUrl(), ie.getMessage());
+ }
+ }
+
+ if (!contentPropName.equals(ContentModel.PROP_CONTENT)) {
+ this.logger.debug("Unable to auto-detect a content property other than the default 'cm:content': {}", contentPropName);
+ return null;
+ }
+
+ int pos = name.lastIndexOf('.');
+ if (pos < 0) {
+ this.logger.debug("Unable to auto-detect a MIME type without a filename extension: {}", name);
+ return null;
+ }
+
+ String extension = name.substring(pos+1);
+ String mimeType = this.mimeTypeService.getMimetype(extension);
+ this.logger.debug("Detected file extension and MIME type: {} => {}", extension, mimeType);
+ return mimeType;
+ }
+
+}
diff --git a/src/main/java/com/inteligr8/alfresco/bulk/CreateNodesWebScript.java b/src/main/java/com/inteligr8/alfresco/bulk/CreateNodesWebScript.java
new file mode 100644
index 0000000..ccdce21
--- /dev/null
+++ b/src/main/java/com/inteligr8/alfresco/bulk/CreateNodesWebScript.java
@@ -0,0 +1,305 @@
+/*
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+package com.inteligr8.alfresco.bulk;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.ContentData;
+import org.alfresco.service.cmr.repository.ContentReader;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.extensions.surf.util.Content;
+import org.springframework.extensions.webscripts.WebScriptException;
+import org.springframework.extensions.webscripts.WebScriptRequest;
+import org.springframework.extensions.webscripts.WebScriptResponse;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.inteligr8.alfresco.bulk.model.AssociationBody;
+import com.inteligr8.alfresco.bulk.model.ChildAssociationBody;
+import com.inteligr8.alfresco.bulk.model.ContentDataBody;
+import com.inteligr8.alfresco.bulk.model.NodeBodyCreateExt;
+
+@Component(value = "webscript.com.inteligr8.alfresco.bulk.createNodes.post")
+public class CreateNodesWebScript extends AbstractNodesWebScript {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ @Override
+ public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException {
+ this.logger.trace("execute()");
+
+ String folderNodeId = req.getServiceMatch().getTemplateVars().get("folderNodeId");
+
+ MediaType contentType = this.validateAndGetContentType(req.getContentType());
+ boolean multipart = MediaType.MULTIPART_FORM_DATA.equals(contentType);
+
+ List createdNodeIds;
+ if (multipart) {
+ throw new WebScriptException(HttpStatus.NOT_IMPLEMENTED.value(), "This service does not yet support multipart content");
+ } else {
+ List nodes = this.parseRequest(req.getContent());
+ this.validateRequest(nodes);
+
+ createdNodeIds = this.createNodes(folderNodeId, nodes);
+ }
+
+ res.setStatus(HttpStatus.OK.value());
+ res.setContentType(MediaType.APPLICATION_JSON_VALUE);
+ res.setContentEncoding("utf-8");
+ this.om.writeValue(res.getWriter(), createdNodeIds);
+ }
+
+ protected List createNodes(String parentNodeId, List nodes) {
+ RetryingTransactionCallback
+
As with any bulk transaction, if this operation fails on any one node, all changes will rollback.
+
TODO
+ ]]>
+
+ /inteligr8/bulk/node/{folderNodeId}/nodes
+ /inteligr8/bulk/nodes/{folderNodeId}
+
+
+ user
+ Inteligr8 Bulk
+
diff --git a/src/main/resources/alfresco/extension/templates/webscripts/com/inteligr8/alfresco/bulk/updateNodes.put.desc.xml b/src/main/resources/alfresco/extension/templates/webscripts/com/inteligr8/alfresco/bulk/updateNodes.put.desc.xml
new file mode 100644
index 0000000..df2efac
--- /dev/null
+++ b/src/main/resources/alfresco/extension/templates/webscripts/com/inteligr8/alfresco/bulk/updateNodes.put.desc.xml
@@ -0,0 +1,18 @@
+
+
+ Update Nodes
+ This REST API updates a set of nodes in a single transaction.
+ It extends the NodeBodyUpdate model of the ACS Public REST API for maximum reusability.
+ The extension allows for updates to associations and content meta-data.
+ It also supports the update of node types, regardless of whether the update follows the specialization hierarchy.
+
As with any bulk transaction, if this operation fails on any one node, all changes will rollback.
+
TODO
+ ]]>
+
+ /inteligr8/bulk/nodes
+
+
+ user
+ Inteligr8 Bulk
+
diff --git a/src/main/resources/alfresco/module/com.inteligr8.alfresco.bulk-acs-module/alfresco-global.properties b/src/main/resources/alfresco/module/com.inteligr8.alfresco.bulk-acs-module/alfresco-global.properties
new file mode 100644
index 0000000..f750f0b
--- /dev/null
+++ b/src/main/resources/alfresco/module/com.inteligr8.alfresco.bulk-acs-module/alfresco-global.properties
@@ -0,0 +1,8 @@
+
+# Enable to use Tika to synchronously detect the MIME type when MIME type is not provided
+# This opens a stream to the binary file and reads at least part of it to help determine the MIME type
+# This will slow down imports when the filename extension may be enough to determine the MIME type
+inteligr8.bulk.enableTika=false
+
+# Enable to allow content to be undetermined, which means the MIME type would be 'application/octet-stream'.
+inteligr8.bulk.allowOctetStream=false
diff --git a/src/main/resources/alfresco/module/com.inteligr8.alfresco.bulk-acs-module/log4j.properties b/src/main/resources/alfresco/module/com.inteligr8.alfresco.bulk-acs-module/log4j.properties
new file mode 100644
index 0000000..579fa41
--- /dev/null
+++ b/src/main/resources/alfresco/module/com.inteligr8.alfresco.bulk-acs-module/log4j.properties
@@ -0,0 +1 @@
+log4j.logger.com.inteligr8.alfresco.bulk=info
diff --git a/src/main/resources/alfresco/module/com.inteligr8.alfresco.bulk-acs-module/module-context.xml b/src/main/resources/alfresco/module/com.inteligr8.alfresco.bulk-acs-module/module-context.xml
new file mode 100644
index 0000000..29891f2
--- /dev/null
+++ b/src/main/resources/alfresco/module/com.inteligr8.alfresco.bulk-acs-module/module-context.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/alfresco/module/com.inteligr8.alfresco.bulk-acs-module/module.properties b/src/main/resources/alfresco/module/com.inteligr8.alfresco.bulk-acs-module/module.properties
new file mode 100644
index 0000000..3f654aa
--- /dev/null
+++ b/src/main/resources/alfresco/module/com.inteligr8.alfresco.bulk-acs-module/module.properties
@@ -0,0 +1,4 @@
+module.id=${project.artifactId}
+module.title=${project.name}
+module.description=${project.description}
+module.version=${project.version}
diff --git a/src/test/resources/alfresco/extension/debug-log4j.properties b/src/test/resources/alfresco/extension/debug-log4j.properties
new file mode 100644
index 0000000..c2becae
--- /dev/null
+++ b/src/test/resources/alfresco/extension/debug-log4j.properties
@@ -0,0 +1,8 @@
+# Module debugging
+log4j.logger.com.inteligr8.alfresco.bulk=trace
+
+# WebScript debugging
+log4j.logger.org.springframework.extensions.webscripts.ScriptLogger=debug
+
+# non-WebScript JavaScript execution debugging
+log4j.logger.org.alfresco.repo.jscript.ScriptLogger=debug
diff --git a/src/test/resources/alfresco/extension/disable-webscript-caching-context.xml b/src/test/resources/alfresco/extension/disable-webscript-caching-context.xml
new file mode 100644
index 0000000..07829ea
--- /dev/null
+++ b/src/test/resources/alfresco/extension/disable-webscript-caching-context.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+ javascript
+
+
+ js
+
+
+
+ false
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+ ${spaces.store}
+
+
+ ${spaces.company_home.childname}
+
+
+
+
+