org.structr.web.maintenance.deploy.ComponentImportVisitor.java Source code

Java tutorial

Introduction

Here is the source code for org.structr.web.maintenance.deploy.ComponentImportVisitor.java

Source

/**
 * Copyright (C) 2010-2018 Structr GmbH
 *
 * This file is part of Structr <http://structr.org>.
 *
 * Structr is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * Structr 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Structr.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.structr.web.maintenance.deploy;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.structr.common.SecurityContext;
import org.structr.common.error.FrameworkException;
import org.structr.core.GraphObject;
import org.structr.core.app.App;
import org.structr.core.app.StructrApp;
import org.structr.core.entity.AbstractNode;
import org.structr.core.graph.Tx;
import org.structr.core.property.PropertyKey;
import org.structr.core.property.PropertyMap;
import org.structr.web.entity.dom.DOMNode;
import org.structr.web.entity.dom.ShadowDocument;
import org.structr.web.entity.dom.Template;
import org.structr.web.importer.Importer;
import org.structr.web.maintenance.DeployCommand;
import org.structr.websocket.command.CreateComponentCommand;

/**
 *
 */
public class ComponentImportVisitor implements FileVisitor<Path> {

    private static final Logger logger = LoggerFactory.getLogger(ComponentImportVisitor.class.getName());

    private Map<String, Object> configuration = null;
    private SecurityContext securityContext = null;
    private App app = null;

    public ComponentImportVisitor(final Map<String, Object> pagesConfiguration) {

        this.configuration = pagesConfiguration;
        this.securityContext = SecurityContext.getSuperUserInstance();
        this.app = StructrApp.getInstance();
    }

    @Override
    public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException {
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {

        if (attrs.isRegularFile()) {

            final String fileName = file.getFileName().toString();
            if (fileName.endsWith(".html")) {

                try {

                    createComponent(file, fileName);

                } catch (FrameworkException fex) {
                    logger.warn("Exception while importing shared component {}: {}", fileName, fex.toString());
                }
            }

        } else {

            logger.warn("Unexpected directory {} found in components/ directory, ignoring",
                    file.getFileName().toString());
        }

        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult visitFileFailed(final Path file, final IOException exc) throws IOException {

        logger.warn("Exception while importing file {}: {}", new Object[] { file.toString(), exc.getMessage() });
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
        return FileVisitResult.CONTINUE;
    }

    // ----- private methods -----
    private DOMNode getExistingComponent(final String name) {

        final App app = StructrApp.getInstance();
        DOMNode result = null;

        try (final Tx tx = app.tx()) {

            if (DeployCommand.isUuid(name)) {

                result = (DOMNode) StructrApp.getInstance().nodeQuery(DOMNode.class).and(GraphObject.id, name)
                        .getFirst();
            } else {

                result = Importer.findSharedComponentByName(name);
            }

            tx.success();

        } catch (FrameworkException fex) {
            logger.warn("Unable to determine if component {} already exists, ignoring.", name);
        }

        return result;
    }

    private void deleteComponent(final App app, final String name) throws FrameworkException {

        final DOMNode node = getExistingComponent(name);
        if (node != null) {

            deleteRecursively(app, node);
        }
    }

    private void deleteRecursively(final App app, final DOMNode node) throws FrameworkException {

        for (DOMNode child : node.getChildren()) {
            deleteRecursively(app, child);
        }

        for (DOMNode sync : node.getSyncedNodes()) {

            deleteRecursively(app, sync);
        }

        app.delete(node);
    }

    private PropertyMap getPropertiesForComponent(final String name) {

        final Object data = configuration.get(name);
        if (data != null && data instanceof Map) {

            try {

                return PropertyMap.inputTypeToJavaType(SecurityContext.getSuperUserInstance(), DOMNode.class,
                        (Map<String, Object>) data);

            } catch (FrameworkException ex) {
                logger.warn("Unable to resolve properties for shared component: {}", ex.getMessage());
            }
        }

        return null;
    }

    private <T> T get(final PropertyMap src, final PropertyKey<T> key, final T defaultValue) {

        if (src != null) {

            final T t = src.get(key);
            if (t != null) {

                return t;
            }
        }

        return defaultValue;
    }

    private void createComponent(final Path file, final String fileName) throws IOException, FrameworkException {

        final String componentName = StringUtils.substringBeforeLast(fileName, ".html");

        // either the template was exported with name + uuid or just the uuid
        final boolean byNameAndId = DeployCommand.endsWithUuid(componentName);
        final boolean byId = DeployCommand.isUuid(componentName);

        try (final Tx tx = app.tx(true, false, false)) {

            final DOMNode existingComponent = (byId ? app.get(DOMNode.class, componentName)
                    : (byNameAndId ? app.get(DOMNode.class, componentName.substring(componentName.length() - 32))
                            : getExistingComponent(componentName)));

            final PropertyMap properties = getPropertiesForComponent(componentName);

            if (properties == null) {

                logger.info("Ignoring {} (not in components.json)", fileName);

            } else {

                if (existingComponent != null) {

                    final PropertyKey<String> contentKey = StructrApp.key(Template.class, "content");

                    properties.put(contentKey, existingComponent.getProperty(contentKey));

                    existingComponent.setOwnerDocument(null);

                    if (existingComponent instanceof Template) {

                        properties.put(contentKey, existingComponent.getProperty(contentKey));
                        existingComponent.setOwnerDocument(null);

                    } else {

                        deleteComponent(app, componentName);
                    }
                }

                final String src = new String(Files.readAllBytes(file), Charset.forName("UTF-8"));
                boolean visibleToPublic = get(properties, GraphObject.visibleToPublicUsers, false);
                boolean visibleToAuth = get(properties, GraphObject.visibleToAuthenticatedUsers, false);
                final Importer importer = new Importer(securityContext, src, null, componentName, visibleToPublic,
                        visibleToAuth, false);

                // enable literal import of href attributes
                importer.setIsDeployment(true);

                final boolean parseOk = importer.parse(false);
                if (parseOk) {

                    logger.info("Importing component {} from {}..", new Object[] { componentName, fileName });

                    // set comment handler that can parse and apply special Structr comments in HTML source files
                    importer.setCommentHandler(new DeploymentCommentHandler());

                    // parse page
                    final ShadowDocument shadowDocument = CreateComponentCommand.getOrCreateHiddenDocument();
                    final DOMNode rootElement = importer.createComponentChildNodes(shadowDocument);

                    if (rootElement != null) {

                        if (byId) {

                            // set UUID
                            rootElement.unlockSystemPropertiesOnce();
                            rootElement.setProperty(GraphObject.id, componentName);

                        } else if (byNameAndId) {

                            // the last characters in the name string are the uuid
                            final String uuid = componentName.substring(componentName.length() - 32);
                            final String name = componentName.substring(0, componentName.length() - 33);

                            rootElement.unlockSystemPropertiesOnce();
                            rootElement.setProperty(GraphObject.id, uuid);
                            properties.put(AbstractNode.name, name);

                        } else {

                            // set name
                            rootElement.setProperty(AbstractNode.name, componentName);
                        }

                        // store properties from components.json if present
                        rootElement.setProperties(securityContext, properties);
                    }
                }
            }

            tx.success();
        }
    }
}