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

Java tutorial

Introduction

Here is the source code for org.structr.web.maintenance.deploy.PageImportVisitor.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.graph.Tx;
import org.structr.core.property.PropertyKey;
import org.structr.core.property.PropertyMap;
import org.structr.web.common.FileHelper;
import org.structr.web.entity.dom.DOMNode;
import org.structr.web.entity.dom.Page;
import org.structr.web.importer.Importer;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

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

    private static final Logger logger = LoggerFactory.getLogger(PageImportVisitor.class.getName());
    private static final String DoctypeString = "<!DOCTYPE";

    private Map<String, Object> pagesConfiguration = null;
    private SecurityContext securityContext = null;
    private Path basePath = null;
    private App app = null;

    public PageImportVisitor(final Path basePath, final Map<String, Object> pagesConfiguration) {

        this.pagesConfiguration = pagesConfiguration;
        this.securityContext = SecurityContext.getSuperUserInstance();
        this.securityContext.setDoTransactionNotifications(false);
        this.basePath = basePath;
        this.app = StructrApp.getInstance(this.securityContext);
    }

    @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 {

        try {

            if (attrs.isDirectory()) {

                createFolder(file);

            } else if (attrs.isRegularFile()) {

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

                    try {

                        createPage(file, fileName);

                    } catch (FrameworkException fex) {
                        logger.warn("Exception while importing page {}: {}",
                                new Object[] { fileName, fex.toString() });
                    }
                }
            }

        } catch (Throwable t) {

            logger.warn("", t);
        }

        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 Page getExistingPage(final String name) throws FrameworkException {
        return StructrApp.getInstance().nodeQuery(Page.class).andName(name).getFirst();
    }

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

        final Page page = app.nodeQuery(Page.class).andName(name).getFirst();
        if (page != null) {

            for (final DOMNode child : page.getElements()) {
                app.delete(child);
            }

            app.delete(page);
        }
    }

    private PropertyMap getPropertiesForPage(final String name) {

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

            try {

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

            } catch (FrameworkException ex) {
                logger.warn("Unable to resolve properties for page: {}", 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 createFolder(final Path file) {

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

            // create folder
            FileHelper.createFolderPath(securityContext, basePath.relativize(file).toString());

            tx.success();

        } catch (FrameworkException fex) {
            logger.warn("", fex);
        }
    }

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

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

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

            final PropertyMap properties = getPropertiesForPage(name);

            if (properties == null) {

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

                final Page existingPage = getExistingPage(name);

                if (existingPage != null) {

                    deletePage(app, name);
                }

                final String src = new String(Files.readAllBytes(file), Charset.forName("UTF-8"));
                final String contentType = get(properties, StructrApp.key(Page.class, "contentType"), "text/html");

                boolean visibleToPublic = get(properties, GraphObject.visibleToPublicUsers, false);
                boolean visibleToAuth = get(properties, GraphObject.visibleToAuthenticatedUsers, false);

                final Importer importer = new Importer(securityContext, src, null, name, visibleToPublic,
                        visibleToAuth, false);

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

                if (StringUtils.startsWithIgnoreCase(src, DoctypeString) && "text/html".equals(contentType)) {

                    // Import document starts with <!DOCTYPE> definition, so we treat it as an HTML
                    // document and use the Structr HTML importer.

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

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

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

                        // parse page
                        final Page newPage = importer.readPage();

                        // remove duplicate elements
                        fixDocumentElements(newPage);

                        // store properties from pages.json
                        newPage.setProperties(securityContext, properties);
                    }

                } else {

                    // Import document does NOT start with a <!DOCTYPE> definition, so we assume a
                    // template or shared component that we need to parse.

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

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

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

                        // parse page
                        final Page newPage = app.create(Page.class, name);

                        // store properties from pages.json
                        newPage.setProperties(securityContext, properties);

                        // add children
                        importer.createChildNodes(newPage, newPage);
                    }
                }
            }

            tx.success();
        }
    }

    /**
     * Remove duplicate Head element from import process.
     * @param page
     */
    private void fixDocumentElements(final Page page) {

        final NodeList heads = page.getElementsByTagName("head");
        if (heads.getLength() > 1) {

            final Node head1 = heads.item(0);
            final Node head2 = heads.item(1);
            final Node parent = head1.getParentNode();

            final boolean h1 = head1.hasChildNodes();
            final boolean h2 = head2.hasChildNodes();

            if (h1 && h2) {

                // merge
                for (Node child = head2.getFirstChild(); child != null; child = child.getNextSibling()) {

                    head2.removeChild(child);
                    head1.appendChild(child);
                }

                parent.removeChild(head2);

            } else if (h1 && !h2) {

                // remove head2
                parent.removeChild(head2);

            } else if (!h1 && h2) {

                // remove head1
                parent.removeChild(head1);

            } else {

                // remove first, doesn't matter
                parent.removeChild(head1);
            }
        }
    }
}