Search in sources :

Example 1 with Linkable

use of org.structr.web.entity.Linkable in project structr by structr.

the class DeployCommand method doImport.

// ----- private methods -----
private void doImport(final Map<String, Object> attributes) throws FrameworkException {
    final long startTime = System.currentTimeMillis();
    customHeaders.put("start", new Date(startTime).toString());
    final String path = (String) attributes.get("source");
    final SecurityContext ctx = SecurityContext.getSuperUserInstance();
    final App app = StructrApp.getInstance(ctx);
    ctx.setDoTransactionNotifications(false);
    ctx.disableEnsureCardinality();
    ctx.disableModificationOfAccessTime();
    final Map<String, Object> componentsConf = new HashMap<>();
    final Map<String, Object> templatesConf = new HashMap<>();
    final Map<String, Object> pagesConf = new HashMap<>();
    final Map<String, Object> filesConf = new HashMap<>();
    if (StringUtils.isBlank(path)) {
        throw new FrameworkException(422, "Please provide 'source' attribute for deployment source directory path.");
    }
    final Path source = Paths.get(path);
    if (!Files.exists(source)) {
        throw new FrameworkException(422, "Source path " + path + " does not exist.");
    }
    if (!Files.isDirectory(source)) {
        throw new FrameworkException(422, "Source path " + path + " is not a directory.");
    }
    final Map<String, Object> broadcastData = new HashMap();
    broadcastData.put("type", DEPLOYMENT_IMPORT_STATUS);
    broadcastData.put("subtype", DEPLOYMENT_STATUS_BEGIN);
    broadcastData.put("start", startTime);
    broadcastData.put("source", source);
    TransactionCommand.simpleBroadcastGenericMessage(broadcastData);
    // apply configuration
    final Path preDeployConf = source.resolve("pre-deploy.conf");
    if (Files.exists(preDeployConf)) {
        try (final Tx tx = app.tx()) {
            final String confSource = new String(Files.readAllBytes(preDeployConf), Charset.forName("utf-8")).trim();
            if (confSource.length() > 0) {
                info("Applying pre-deployment configuration from {}", preDeployConf);
                publishDeploymentProgressMessage(DEPLOYMENT_IMPORT_STATUS, "Applying pre-deployment configuration");
                Scripting.evaluate(new ActionContext(ctx), null, confSource, "pre-deploy.conf");
            } else {
                info("Ignoring empty pre-deployment configuration {}", preDeployConf);
            }
            tx.success();
        } catch (Throwable t) {
            logger.warn("", t);
            publishDeploymentWarningMessage("Exception caught while importing pre-deploy.conf", t.toString());
        }
    }
    // backup previous value of change log setting
    // temporary disable creation of change log
    final boolean changeLogEnabled = Settings.ChangelogEnabled.getValue();
    Settings.ChangelogEnabled.setValue(false);
    // read grants.json
    publishDeploymentProgressMessage(DEPLOYMENT_IMPORT_STATUS, "Importing resource access grants");
    final Path grantsConf = source.resolve("security/grants.json");
    if (Files.exists(grantsConf)) {
        info("Reading {}", grantsConf);
        importListData(ResourceAccess.class, readConfigList(grantsConf));
    }
    // read schema-methods.json
    final Path schemaMethodsConf = source.resolve("schema-methods.json");
    if (Files.exists(schemaMethodsConf)) {
        info("Reading {}", schemaMethodsConf);
        final String title = "Deprecation warning";
        final String text = "Found file 'schema-methods.json'. Newer versions store global schema methods in the schema snapshot file. Recreate the export with the current version to avoid compatibility issues. Support for importing this file will be dropped in future versions.";
        info(title + ": " + text);
        publishDeploymentWarningMessage(title, text);
        importListData(SchemaMethod.class, readConfigList(schemaMethodsConf));
    }
    // read mail-templates.json
    final Path mailTemplatesConf = source.resolve("mail-templates.json");
    if (Files.exists(mailTemplatesConf)) {
        info("Reading {}", mailTemplatesConf);
        publishDeploymentProgressMessage(DEPLOYMENT_IMPORT_STATUS, "Importing mail templates");
        importListData(MailTemplate.class, readConfigList(mailTemplatesConf));
    }
    // read widgets.json
    final Path widgetsConf = source.resolve("widgets.json");
    if (Files.exists(widgetsConf)) {
        info("Reading {}", widgetsConf);
        publishDeploymentProgressMessage(DEPLOYMENT_IMPORT_STATUS, "Importing widgets");
        importListData(Widget.class, readConfigList(widgetsConf));
    }
    // read localizations.json
    final Path localizationsConf = source.resolve("localizations.json");
    if (Files.exists(localizationsConf)) {
        final PropertyMap additionalData = new PropertyMap();
        // Question: shouldn't this be true? No, 'imported' is a flag for legacy-localization which
        // have been imported from a legacy-system which was replaced by structr.
        // it is a way to differentiate between new and old localization strings
        additionalData.put(StructrApp.key(Localization.class, "imported"), false);
        info("Reading {}", localizationsConf);
        publishDeploymentProgressMessage(DEPLOYMENT_IMPORT_STATUS, "Importing localizations");
        importListData(Localization.class, readConfigList(localizationsConf), additionalData);
    }
    // read files.conf
    final Path filesConfFile = source.resolve("files.json");
    if (Files.exists(filesConfFile)) {
        info("Reading {}", filesConfFile);
        filesConf.putAll(readConfigMap(filesConfFile));
    }
    // read pages.conf
    final Path pagesConfFile = source.resolve("pages.json");
    if (Files.exists(pagesConfFile)) {
        info("Reading {}", pagesConfFile);
        pagesConf.putAll(readConfigMap(pagesConfFile));
    }
    // read components.conf
    final Path componentsConfFile = source.resolve("components.json");
    if (Files.exists(componentsConfFile)) {
        info("Reading {}", componentsConfFile);
        componentsConf.putAll(readConfigMap(componentsConfFile));
    }
    // read templates.conf
    final Path templatesConfFile = source.resolve("templates.json");
    if (Files.exists(templatesConfFile)) {
        info("Reading {}", templatesConfFile);
        templatesConf.putAll(readConfigMap(templatesConfFile));
    }
    // import schema
    final Path schema = source.resolve("schema");
    if (Files.exists(schema)) {
        try {
            info("Importing data from schema/ directory");
            publishDeploymentProgressMessage(DEPLOYMENT_IMPORT_STATUS, "Importing schema");
            Files.walkFileTree(schema, new SchemaImportVisitor(schema));
        } catch (IOException ioex) {
            logger.warn("Exception while importing schema", ioex);
        }
    }
    // import files
    final Path files = source.resolve("files");
    if (Files.exists(files)) {
        try {
            info("Importing files (unchanged files will be skipped)");
            publishDeploymentProgressMessage(DEPLOYMENT_IMPORT_STATUS, "Importing files");
            FileImportVisitor fiv = new FileImportVisitor(files, filesConf);
            Files.walkFileTree(files, fiv);
            fiv.handleDeferredFiles();
        } catch (IOException ioex) {
            logger.warn("Exception while importing files", ioex);
        }
    }
    for (StructrModule module : StructrApp.getConfiguration().getModules().values()) {
        if (module.hasDeploymentData()) {
            info("Importing deployment data for module {}", module.getName());
            publishDeploymentProgressMessage(DEPLOYMENT_IMPORT_STATUS, "Importing deployment data for module " + module.getName());
            final Path moduleFolder = source.resolve("modules/" + module.getName() + "/");
            module.importDeploymentData(moduleFolder, getGson());
        }
    }
    // construct paths
    final Path templates = source.resolve("templates");
    final Path components = source.resolve("components");
    final Path pages = source.resolve("pages");
    // an empty directory was specified accidentially).
    if (Files.exists(templates) && Files.exists(components) && Files.exists(pages)) {
        try (final Tx tx = app.tx()) {
            final String tenantIdentifier = app.getDatabaseService().getTenantIdentifier();
            info("Removing pages, templates and components");
            publishDeploymentProgressMessage(DEPLOYMENT_IMPORT_STATUS, "Removing pages, templates and components");
            if (tenantIdentifier != null) {
                app.cypher("MATCH (n:" + tenantIdentifier + ":DOMNode) DETACH DELETE n", null);
            } else {
                app.cypher("MATCH (n:DOMNode) DETACH DELETE n", null);
            }
            FlushCachesCommand.flushAll();
            tx.success();
        }
    } else {
        logger.info("Import directory does not seem to contain pages, templates or components, NOT removing any data.");
    }
    // import templates, must be done before pages so the templates exist
    if (Files.exists(templates)) {
        try {
            info("Importing templates");
            publishDeploymentProgressMessage(DEPLOYMENT_IMPORT_STATUS, "Importing templates");
            Files.walkFileTree(templates, new TemplateImportVisitor(templatesConf));
        } catch (IOException ioex) {
            logger.warn("Exception while importing templates", ioex);
        }
    }
    // import components, must be done before pages so the shared components exist
    if (Files.exists(components)) {
        try {
            info("Importing shared components");
            publishDeploymentProgressMessage(DEPLOYMENT_IMPORT_STATUS, "Importing shared components");
            Files.walkFileTree(components, new ComponentImportVisitor(componentsConf));
        } catch (IOException ioex) {
            logger.warn("Exception while importing shared components", ioex);
        }
    }
    // import pages
    if (Files.exists(pages)) {
        try {
            info("Importing pages");
            publishDeploymentProgressMessage(DEPLOYMENT_IMPORT_STATUS, "Importing pages");
            Files.walkFileTree(pages, new PageImportVisitor(pages, pagesConf));
        } catch (IOException ioex) {
            logger.warn("Exception while importing pages", ioex);
        }
    }
    try (final Tx tx = app.tx()) {
        deferredPageLinks.forEach((String linkableUUID, String pagePath) -> {
            try {
                final DOMNode page = StructrApp.getInstance().get(DOMNode.class, linkableUUID);
                final Linkable linkedPage = StructrApp.getInstance().nodeQuery(Linkable.class).and(StructrApp.key(Page.class, "path"), pagePath).or(Page.name, pagePath).getFirst();
                ((LinkSource) page).setLinkable(linkedPage);
            } catch (FrameworkException ex) {
            }
        });
        deferredPageLinks.clear();
        tx.success();
    }
    // apply configuration
    final Path postDeployConf = source.resolve("post-deploy.conf");
    if (Files.exists(postDeployConf)) {
        try (final Tx tx = app.tx()) {
            final String confSource = new String(Files.readAllBytes(postDeployConf), Charset.forName("utf-8")).trim();
            if (confSource.length() > 0) {
                info("Applying post-deployment configuration from {}", postDeployConf);
                publishDeploymentProgressMessage(DEPLOYMENT_IMPORT_STATUS, "Applying post-deployment configuration");
                Scripting.evaluate(new ActionContext(ctx), null, confSource, "post-deploy.conf");
            } else {
                info("Ignoring empty post-deployment configuration {}", postDeployConf);
            }
            tx.success();
        } catch (Throwable t) {
            logger.warn("", t);
            publishDeploymentWarningMessage("Exception caught while importing post-deploy.conf", t.toString());
        }
    }
    // restore saved value
    Settings.ChangelogEnabled.setValue(changeLogEnabled);
    final long endTime = System.currentTimeMillis();
    DecimalFormat decimalFormat = new DecimalFormat("0.00", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
    final String duration = decimalFormat.format(((endTime - startTime) / 1000.0)) + "s";
    customHeaders.put("end", new Date(endTime).toString());
    customHeaders.put("duration", duration);
    info("Import from {} done. (Took {})", source.toString(), duration);
    broadcastData.put("subtype", DEPLOYMENT_STATUS_END);
    broadcastData.put("end", endTime);
    broadcastData.put("duration", duration);
    TransactionCommand.simpleBroadcastGenericMessage(broadcastData);
}
Also used : App(org.structr.core.app.App) StructrApp(org.structr.core.app.StructrApp) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) LinkSource(org.structr.web.entity.LinkSource) FileImportVisitor(org.structr.web.maintenance.deploy.FileImportVisitor) DecimalFormat(java.text.DecimalFormat) Page(org.structr.web.entity.dom.Page) PageImportVisitor(org.structr.web.maintenance.deploy.PageImportVisitor) SchemaImportVisitor(org.structr.web.maintenance.deploy.SchemaImportVisitor) Localization(org.structr.core.entity.Localization) DOMNode(org.structr.web.entity.dom.DOMNode) Path(java.nio.file.Path) FrameworkException(org.structr.common.error.FrameworkException) Tx(org.structr.core.graph.Tx) ComponentImportVisitor(org.structr.web.maintenance.deploy.ComponentImportVisitor) IOException(java.io.IOException) ActionContext(org.structr.schema.action.ActionContext) Date(java.util.Date) StructrModule(org.structr.module.StructrModule) PropertyMap(org.structr.core.property.PropertyMap) SecurityContext(org.structr.common.SecurityContext) TemplateImportVisitor(org.structr.web.maintenance.deploy.TemplateImportVisitor) Linkable(org.structr.web.entity.Linkable)

Example 2 with Linkable

use of org.structr.web.entity.Linkable in project structr by structr.

the class DOMNode method getLinkableInstructions.

static void getLinkableInstructions(final DOMNode thisNode, final Set<String> instructions) {
    if (thisNode instanceof LinkSource) {
        final LinkSource linkSourceElement = (LinkSource) thisNode;
        final Linkable linkable = linkSourceElement.getLinkable();
        if (linkable != null) {
            final String linkableInstruction = (linkable instanceof Page) ? "pagelink" : "link";
            String path = linkable.getPath();
            if (linkable instanceof Page && path == null) {
                path = linkable.getName();
            }
            if (path != null) {
                instructions.add("@structr:" + linkableInstruction + "(" + path + ")");
            } else {
                logger.warn("Cannot export linkable relationship, no path.");
            }
        }
    }
}
Also used : LinkSource(org.structr.web.entity.LinkSource) Linkable(org.structr.web.entity.Linkable)

Example 3 with Linkable

use of org.structr.web.entity.Linkable in project structr by structr.

the class HtmlServlet method doGet.

@Override
protected void doGet(final HttpServletRequest request, final HttpServletResponse response) {
    final Authenticator auth = getConfig().getAuthenticator();
    List<Page> pages = null;
    boolean requestUriContainsUuids = false;
    SecurityContext securityContext;
    final App app;
    try {
        assertInitialized();
        final String path = request.getPathInfo() != null ? request.getPathInfo() : "/";
        // check for registration (has its own tx because of write access
        if (checkRegistration(auth, request, response, path)) {
            return;
        }
        // check for registration (has its own tx because of write access
        if (checkResetPassword(auth, request, response, path)) {
            return;
        }
        // isolate request authentication in a transaction
        try (final Tx tx = StructrApp.getInstance().tx()) {
            securityContext = auth.initializeAndExamineRequest(request, response);
            tx.success();
        } catch (AuthenticationException aex) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }
        app = StructrApp.getInstance(securityContext);
        try (final Tx tx = app.tx()) {
            // Ensure access mode is frontend
            securityContext.setAccessMode(AccessMode.Frontend);
            request.setCharacterEncoding("UTF-8");
            // Important: Set character encoding before calling response.getWriter() !!, see Servlet Spec 5.4
            response.setCharacterEncoding("UTF-8");
            boolean dontCache = false;
            logger.debug("Path info {}", path);
            // don't continue on redirects
            if (response.getStatus() == 302) {
                tx.success();
                return;
            }
            final Principal user = securityContext.getUser(false);
            if (user != null) {
                // Don't cache if a user is logged in
                dontCache = true;
            }
            final RenderContext renderContext = RenderContext.getInstance(securityContext, request, response);
            renderContext.setResourceProvider(config.getResourceProvider());
            final EditMode edit = renderContext.getEditMode(user);
            DOMNode rootElement = null;
            AbstractNode dataNode = null;
            final String[] uriParts = PathHelper.getParts(path);
            if ((uriParts == null) || (uriParts.length == 0)) {
                // find a visible page
                rootElement = findIndexPage(securityContext, pages, edit);
                logger.debug("No path supplied, trying to find index page");
            } else {
                if (rootElement == null) {
                    rootElement = findPage(securityContext, pages, path, edit);
                } else {
                    dontCache = true;
                }
            }
            if (rootElement == null) {
                // No page found
                // In case of a file, try to find a file with the query string in the filename
                final String queryString = request.getQueryString();
                // Look for a file, first include the query string
                File file = findFile(securityContext, request, path + (queryString != null ? "?" + queryString : ""));
                // If no file with query string in the file name found, try without query string
                if (file == null) {
                    file = findFile(securityContext, request, path);
                }
                if (file != null) {
                    streamFile(securityContext, file, request, response, edit);
                    tx.success();
                    return;
                }
                if (uriParts != null) {
                    // store remaining path parts in request
                    final Matcher matcher = threadLocalUUIDMatcher.get();
                    for (int i = 0; i < uriParts.length; i++) {
                        request.setAttribute(uriParts[i], i);
                        matcher.reset(uriParts[i]);
                        // set to "true" if part matches UUID pattern
                        requestUriContainsUuids |= matcher.matches();
                    }
                }
                if (!requestUriContainsUuids) {
                    // Try to find a data node by name
                    dataNode = findFirstNodeByName(securityContext, request, path);
                } else {
                    dataNode = findNodeByUuid(securityContext, PathHelper.getName(path));
                }
                // if (dataNode != null && !(dataNode instanceof Linkable)) {
                if (dataNode != null) {
                    // Last path part matches a data node
                    // Remove last path part and try again searching for a page
                    // clear possible entry points
                    request.removeAttribute(POSSIBLE_ENTRY_POINTS_KEY);
                    rootElement = findPage(securityContext, pages, StringUtils.substringBeforeLast(path, PathHelper.PATH_SEP), edit);
                    renderContext.setDetailsDataObject(dataNode);
                    // Start rendering on data node
                    if (rootElement == null && dataNode instanceof DOMNode) {
                        // check visibleForSite here as well
                        if (!(dataNode instanceof Page) || isVisibleForSite(request, (Page) dataNode)) {
                            rootElement = ((DOMNode) dataNode);
                        }
                    }
                }
            }
            // look for pages with HTTP Basic Authentication (must be done as superuser)
            if (rootElement == null) {
                final HttpBasicAuthResult authResult = checkHttpBasicAuth(request, response, path);
                switch(authResult.authState()) {
                    // Element with Basic Auth found and authentication succeeded
                    case Authenticated:
                        final Linkable result = authResult.getRootElement();
                        if (result instanceof Page) {
                            rootElement = (DOMNode) result;
                            securityContext = authResult.getSecurityContext();
                            renderContext.pushSecurityContext(securityContext);
                        } else if (result instanceof File) {
                            streamFile(authResult.getSecurityContext(), (File) result, request, response, EditMode.NONE);
                            tx.success();
                            return;
                        }
                        break;
                    // Page with Basic Auth found but not yet authenticated
                    case MustAuthenticate:
                        final Page errorPage = StructrApp.getInstance().nodeQuery(Page.class).and(StructrApp.key(Page.class, "showOnErrorCodes"), "401", false).getFirst();
                        if (errorPage != null && isVisibleForSite(request, errorPage)) {
                            // set error page
                            rootElement = errorPage;
                            // don't cache the error page
                            dontCache = true;
                        } else {
                            // send error
                            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
                            tx.success();
                            return;
                        }
                        break;
                    // no Basic Auth for given path, go on
                    case NoBasicAuth:
                        break;
                }
            }
            // Still nothing found, do error handling
            if (rootElement == null) {
                rootElement = notFound(response, securityContext);
            }
            if (rootElement == null) {
                tx.success();
                return;
            }
            // check dont cache flag on page (if root element is a page)
            // but don't modify true to false
            dontCache |= rootElement.dontCache();
            if (EditMode.WIDGET.equals(edit) || dontCache) {
                setNoCacheHeaders(response);
            }
            if (!securityContext.isVisible(rootElement)) {
                rootElement = notFound(response, securityContext);
                if (rootElement == null) {
                    tx.success();
                    return;
                }
            } else {
                if (!EditMode.WIDGET.equals(edit) && !dontCache && notModifiedSince(request, response, rootElement, dontCache)) {
                    ServletOutputStream out = response.getOutputStream();
                    out.flush();
                    // response.flushBuffer();
                    out.close();
                } else {
                    // prepare response
                    response.setCharacterEncoding("UTF-8");
                    String contentType = rootElement.getProperty(StructrApp.key(Page.class, "contentType"));
                    if (contentType == null) {
                        // Default
                        contentType = "text/html;charset=UTF-8";
                    }
                    if (contentType.equals("text/html")) {
                        contentType = contentType.concat(";charset=UTF-8");
                    }
                    response.setContentType(contentType);
                    setCustomResponseHeaders(response);
                    final boolean createsRawData = rootElement.getProperty(StructrApp.key(Page.class, "pageCreatesRawData"));
                    // async or not?
                    if (isAsync && !createsRawData) {
                        final AsyncContext async = request.startAsync();
                        final ServletOutputStream out = async.getResponse().getOutputStream();
                        final AtomicBoolean finished = new AtomicBoolean(false);
                        final DOMNode rootNode = rootElement;
                        threadPool.submit(new Runnable() {

                            @Override
                            public void run() {
                                try (final Tx tx = app.tx()) {
                                    // render
                                    rootNode.render(renderContext, 0);
                                    finished.set(true);
                                    tx.success();
                                } catch (Throwable t) {
                                    t.printStackTrace();
                                    logger.warn("Error while rendering page {}: {}", rootNode.getName(), t.getMessage());
                                    try {
                                        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                                        finished.set(true);
                                    } catch (IOException ex) {
                                        logger.warn("", ex);
                                    }
                                }
                            }
                        });
                        // start output write listener
                        out.setWriteListener(new WriteListener() {

                            @Override
                            public void onWritePossible() throws IOException {
                                try {
                                    final Queue<String> queue = renderContext.getBuffer().getQueue();
                                    while (out.isReady()) {
                                        String buffer = null;
                                        synchronized (queue) {
                                            buffer = queue.poll();
                                        }
                                        if (buffer != null) {
                                            out.print(buffer);
                                        } else {
                                            if (finished.get()) {
                                                async.complete();
                                                // prevent this block from being called again
                                                break;
                                            }
                                            Thread.sleep(1);
                                        }
                                    }
                                } catch (Throwable t) {
                                    logger.warn("", t);
                                }
                            }

                            @Override
                            public void onError(Throwable t) {
                                logger.warn("", t);
                            }
                        });
                    } else {
                        final StringRenderBuffer buffer = new StringRenderBuffer();
                        renderContext.setBuffer(buffer);
                        // render
                        rootElement.render(renderContext, 0);
                        try {
                            response.getOutputStream().write(buffer.getBuffer().toString().getBytes("utf-8"));
                            response.getOutputStream().flush();
                            response.getOutputStream().close();
                        } catch (IOException ioex) {
                            logger.warn("", ioex);
                        }
                    }
                }
            }
            tx.success();
        } catch (FrameworkException fex) {
            logger.error("Exception while processing request: {}", fex.getMessage());
        }
    } catch (FrameworkException fex) {
        logger.error("Exception while processing request: {}", fex.getMessage());
        UiAuthenticator.writeFrameworkException(response, fex);
    } catch (IOException ioex) {
        logger.error("Exception while processing request: {}", ioex.getMessage());
        UiAuthenticator.writeInternalServerError(response);
    }
}
Also used : App(org.structr.core.app.App) StructrApp(org.structr.core.app.StructrApp) StringRenderBuffer(org.structr.web.common.StringRenderBuffer) AuthenticationException(org.structr.core.auth.exception.AuthenticationException) AbstractNode(org.structr.core.entity.AbstractNode) Matcher(java.util.regex.Matcher) ThreadLocalMatcher(org.structr.common.ThreadLocalMatcher) ServletOutputStream(javax.servlet.ServletOutputStream) Page(org.structr.web.entity.dom.Page) AsyncContext(javax.servlet.AsyncContext) DOMNode(org.structr.web.entity.dom.DOMNode) WriteListener(javax.servlet.WriteListener) Queue(java.util.Queue) UiAuthenticator(org.structr.web.auth.UiAuthenticator) Authenticator(org.structr.core.auth.Authenticator) RenderContext(org.structr.web.common.RenderContext) Tx(org.structr.core.graph.Tx) FrameworkException(org.structr.common.error.FrameworkException) IOException(java.io.IOException) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) SecurityContext(org.structr.common.SecurityContext) EditMode(org.structr.web.common.RenderContext.EditMode) Linkable(org.structr.web.entity.Linkable) AbstractFile(org.structr.web.entity.AbstractFile) File(org.structr.web.entity.File) Principal(org.structr.core.entity.Principal)

Example 4 with Linkable

use of org.structr.web.entity.Linkable in project structr by structr.

the class Importer method createChildNodes.

private DOMNode createChildNodes(final Node startNode, final DOMNode parent, final Page page, final boolean removeHashAttribute, final int depth) throws FrameworkException {
    DOMNode rootElement = null;
    Linkable res = null;
    String instructions = null;
    final List<Node> children = startNode.childNodes();
    for (Node node : children) {
        String tag = node.nodeName();
        // clean tag, remove non-word characters except : and #
        if (tag != null) {
            tag = tag.replaceAll("[^a-zA-Z0-9#:.-_]+", "");
        }
        final StringBuilder classString = new StringBuilder();
        final String type = CaseHelper.toUpperCamelCase(tag);
        String comment = null;
        String content = null;
        String id = null;
        boolean isNewTemplateOrComponent = false;
        if (ignoreElementNames.contains(type)) {
            continue;
        }
        if (node instanceof Element) {
            final Element el = ((Element) node);
            final Set<String> classes = el.classNames();
            for (String cls : classes) {
                classString.append(cls).append(" ");
            }
            id = el.id();
            // do not download files when called from DeployCommand!
            if (!isDeployment) {
                String downloadAddressAttr = srcElements.contains(tag) ? "src" : hrefElements.contains(tag) ? "href" : null;
                if (downloadAddressAttr != null && StringUtils.isNotBlank(node.attr(downloadAddressAttr))) {
                    String downloadAddress = node.attr(downloadAddressAttr);
                    res = downloadFile(downloadAddress, originalUrl);
                } else {
                    res = null;
                }
            }
            if (removeHashAttribute) {
                // Remove data-structr-hash attribute
                node.removeAttr("data-structr-hash");
            }
        }
        // Data and comment nodes: Trim the text and put it into the "content" field without changes
        if (type.equals("#comment")) {
            comment = ((Comment) node).getData();
            tag = "";
            // Don't add content node for whitespace
            if (StringUtils.isBlank(comment)) {
                continue;
            }
            // store for later use
            commentSource.append(comment).append("\n");
            // check if comment contains instructions
            if (commentHandler != null && commentHandler.containsInstructions(comment)) {
                if (instructions != null) {
                    // unhandled instructions from previous iteration => empty content element
                    createEmptyContentNode(page, parent, commentHandler, instructions);
                }
                instructions = comment;
                continue;
            }
        } else if (type.equals("#data")) {
            tag = "";
            content = ((DataNode) node).getWholeData();
            // Don't add content node for whitespace
            if (StringUtils.isBlank(content)) {
                continue;
            }
        } else // Text-only nodes: Trim the text and put it into the "content" field
        {
            if (type.equals("#text")) {
                tag = "";
                if (isDeployment) {
                    content = trimTrailingNewline(((TextNode) node).getWholeText());
                    if (content == null || content.length() == 0) {
                        continue;
                    }
                } else {
                    content = trimTrailingNewline(((TextNode) node).text());
                    if (StringUtils.isBlank(content)) {
                        continue;
                    }
                }
            }
        }
        org.structr.web.entity.dom.DOMNode newNode = null;
        // create node
        if (StringUtils.isBlank(tag)) {
            if (page != null) {
                // create comment or content node
                if (!StringUtils.isBlank(comment)) {
                    final PropertyKey<String> contentTypeKey = StructrApp.key(Content.class, "contentType");
                    newNode = (DOMNode) page.createComment(comment);
                    newNode.setProperty(contentTypeKey, "text/html");
                } else {
                    newNode = (Content) page.createTextNode(content);
                }
            }
        } else if ("structr:template".equals(tag)) {
            final String src = node.attr("src");
            if (src != null) {
                DOMNode template = null;
                if (DeployCommand.isUuid(src)) {
                    template = (DOMNode) StructrApp.getInstance().nodeQuery(NodeInterface.class).and(GraphObject.id, src).getFirst();
                    if (template == null) {
                        System.out.println("##################################### template with UUID " + src + " not found, this is a known bug");
                    }
                } else if (DeployCommand.endsWithUuid(src)) {
                    final String uuid = src.substring(src.length() - 32);
                    template = (DOMNode) StructrApp.getInstance().nodeQuery(NodeInterface.class).and(GraphObject.id, uuid).getFirst();
                    if (template == null) {
                        System.out.println("##################################### template with UUID " + uuid + " not found, this is a known bug");
                    }
                } else {
                    template = Importer.findSharedComponentByName(src);
                    if (template == null) {
                        template = Importer.findTemplateByName(src);
                        if (template == null) {
                            template = createNewTemplateNode(parent, node.childNodes());
                            isNewTemplateOrComponent = true;
                        }
                    }
                }
                if (template != null) {
                    newNode = template;
                    if (template.isSharedComponent()) {
                        newNode = (DOMNode) template.cloneNode(false);
                        newNode.setSharedComponent(template);
                        newNode.setOwnerDocument(page);
                    } else if (page != null) {
                        newNode.setOwnerDocument(page);
                    }
                } else {
                    logger.warn("Unable to find template or shared component {}, template ignored!", src);
                }
            } else {
                logger.warn("Invalid template definition, missing src attribute!");
            }
        } else if ("structr:component".equals(tag)) {
            final String src = node.attr("src");
            if (src != null) {
                DOMNode component = null;
                if (DeployCommand.isUuid(src)) {
                    component = app.nodeQuery(DOMNode.class).and(GraphObject.id, src).getFirst();
                } else {
                    component = Importer.findSharedComponentByName(src);
                }
                if (component == null) {
                    component = createSharedComponent(node);
                }
                isNewTemplateOrComponent = true;
                if (component != null) {
                    newNode = (DOMNode) component.cloneNode(false);
                    newNode.setSharedComponent(component);
                    newNode.setOwnerDocument(page);
                } else {
                    logger.warn("Unable to find shared component {} - ignored!", src);
                }
            } else {
                logger.warn("Invalid component definition, missing src attribute!");
            }
        } else {
            if (page != null) {
                newNode = (org.structr.web.entity.dom.DOMElement) page.createElement(tag, true);
            }
            if (newNode == null) {
                final PropertyKey<Boolean> hideOnDetailKey = StructrApp.key(DOMNode.class, "hideOnDetail");
                final PropertyKey<Boolean> hideOnIndexKey = StructrApp.key(DOMNode.class, "hideOnIndex");
                final PropertyKey<String> tagKey = StructrApp.key(DOMElement.class, "tag");
                // experimental: create DOM element with literal tag
                newNode = (DOMElement) app.create(DOMElement.class, new NodeAttribute(tagKey, node.nodeName()), new NodeAttribute(hideOnDetailKey, false), new NodeAttribute(hideOnIndexKey, false));
                if (newNode != null && page != null) {
                    newNode.doAdopt(page);
                }
            /* disabled / replaced by implementation above
					newNode = createNewHTMLTemplateNodeForUnsupportedTag(parent, node);
					isNewTemplateOrComponent = true;
					*/
            }
        }
        if (newNode != null) {
            // save root element for later use
            if (rootElement == null && !(newNode instanceof org.structr.web.entity.dom.Comment)) {
                rootElement = newNode;
            }
            // set linkable
            if (res != null && newNode instanceof LinkSource) {
                ((LinkSource) newNode).setLinkable(res);
            }
            // container for bulk setProperties()
            final PropertyMap newNodeProperties = new PropertyMap();
            final Class newNodeType = newNode.getClass();
            newNodeProperties.put(AbstractNode.visibleToPublicUsers, publicVisible);
            newNodeProperties.put(AbstractNode.visibleToAuthenticatedUsers, authVisible);
            // "id" attribute: Put it into the "_html_id" field
            if (StringUtils.isNotBlank(id)) {
                newNodeProperties.put(StructrApp.key(DOMElement.class, "_html_id"), id);
            }
            if (StringUtils.isNotBlank(classString.toString())) {
                newNodeProperties.put(StructrApp.key(DOMElement.class, "_html_class"), StringUtils.trim(classString.toString()));
            }
            for (Attribute nodeAttr : node.attributes()) {
                final String key = nodeAttr.getKey();
                if (!key.equals("text")) {
                    // Don't add text attribute as _html_text because the text is already contained in the 'content' attribute
                    final String value = nodeAttr.getValue();
                    if (key.startsWith("data-")) {
                        if (key.startsWith(DATA_META_PREFIX)) {
                            // convert data-structr-meta-* attributes to local camel case properties on the node,
                            int l = DATA_META_PREFIX.length();
                            String upperCaseKey = WordUtils.capitalize(key.substring(l), new char[] { '-' }).replaceAll("-", "");
                            String camelCaseKey = key.substring(l, l + 1).concat(upperCaseKey.substring(1));
                            if (value != null) {
                                // store value using actual input converter
                                final PropertyKey actualKey = StructrApp.getConfiguration().getPropertyKeyForJSONName(newNodeType, camelCaseKey, false);
                                if (actualKey != null) {
                                    final PropertyConverter converter = actualKey.inputConverter(securityContext);
                                    if (converter != null) {
                                        final Object convertedValue = converter.convert(value);
                                        newNodeProperties.put(actualKey, convertedValue);
                                    } else {
                                        newNodeProperties.put(actualKey, value);
                                    }
                                } else {
                                    logger.warn("Unknown meta property key {}, ignoring.", camelCaseKey);
                                }
                            }
                        } else if (key.startsWith(DATA_STRUCTR_PREFIX)) {
                            // don't convert data-structr-* attributes as they are internal
                            final PropertyKey propertyKey = StructrApp.getConfiguration().getPropertyKeyForJSONName(newNodeType, key);
                            if (propertyKey != null) {
                                final PropertyConverter inputConverter = propertyKey.inputConverter(securityContext);
                                if (value != null && inputConverter != null) {
                                    newNodeProperties.put(propertyKey, propertyKey.inputConverter(securityContext).convert(value));
                                } else {
                                    newNodeProperties.put(propertyKey, value);
                                }
                            }
                        } else {
                            // store data-* attributes in node
                            final PropertyKey propertyKey = new StringProperty(key);
                            if (value != null) {
                                newNodeProperties.put(propertyKey, value);
                            }
                        }
                    } else {
                        boolean notBlank = StringUtils.isNotBlank(value);
                        boolean isAnchor = notBlank && value.startsWith("#");
                        boolean isLocal = notBlank && !value.startsWith("http");
                        boolean isActive = notBlank && value.contains("${");
                        boolean isStructrLib = notBlank && value.startsWith("/structr/js/");
                        if ("link".equals(tag) && "href".equals(key) && isLocal && !isActive && !isDeployment) {
                            newNodeProperties.put(new StringProperty(PropertyView.Html.concat(key)), "${link.path}?${link.version}");
                        } else if (("href".equals(key) || "src".equals(key)) && isLocal && !isActive && !isAnchor && !isStructrLib && !isDeployment) {
                            newNodeProperties.put(new StringProperty(PropertyView.Html.concat(key)), "${link.path}");
                        } else {
                            newNodeProperties.put(new StringProperty(PropertyView.Html.concat(key)), value);
                        }
                    }
                }
            }
            // bulk set properties on new node
            newNode.setProperties(securityContext, newNodeProperties);
            if ("script".equals(tag)) {
                final PropertyKey<String> typeKey = StructrApp.key(Input.class, "_html_type");
                final String contentType = newNode.getProperty(typeKey);
                if (contentType == null) {
                    // Set default type of script tag to "text/javascript" to ensure inline JS gets imported properly
                    newNode.setProperty(typeKey, "text/javascript");
                } else if (contentType.equals("application/schema+json")) {
                    for (final Node scriptContentNode : node.childNodes()) {
                        final String source = scriptContentNode.toString();
                        // Import schema JSON
                        SchemaJsonImporter.importSchemaJson(source);
                    }
                } else if (contentType.equals("application/x-structr-script")) {
                    for (final Node scriptContentNode : node.childNodes()) {
                        final String source = scriptContentNode.toString();
                        try {
                            Actions.execute(securityContext, null, source, null);
                        } catch (UnlicensedException ex) {
                            ex.log(logger);
                        }
                    }
                    continue;
                } else if (contentType.equals("application/x-structr-javascript")) {
                    for (final Node scriptContentNode : node.childNodes()) {
                        final String source = scriptContentNode.toString();
                        try {
                            Actions.execute(securityContext, null, source, null);
                        } catch (UnlicensedException ex) {
                            ex.log(logger);
                        }
                    }
                    continue;
                }
            } else if ("style".equals(tag)) {
                final PropertyKey<String> typeKey = StructrApp.key(Input.class, "_html_type");
                final String contentType = newNode.getProperty(typeKey);
                if ("text/css".equals(contentType)) {
                    // parse content of style elements and add referenced files to list of resources to be downloaded
                    for (final Node styleContentNode : node.childNodes()) {
                        final String source = styleContentNode.toString();
                        try {
                            // Import referenced resources
                            processCss(source, originalUrl);
                        } catch (IOException ex) {
                            logger.warn("Couldn't process CSS source", ex);
                        }
                    }
                }
            }
            if (instructions != null) {
                if (instructions.contains("@structr:content") && !(newNode instanceof Content)) {
                    // unhandled instructions from previous iteration => empty content element
                    createEmptyContentNode(page, parent, commentHandler, instructions);
                } else {
                    // apply instructions to new DOM element
                    if (commentHandler != null) {
                        commentHandler.handleComment(page, newNode, instructions, true);
                    }
                }
                instructions = null;
            }
            // allow parent to be null to prevent direct child relationship
            if (parent != null) {
                // special handling for <head> elements
                if (newNode instanceof Head && parent instanceof Body) {
                    final org.w3c.dom.Node html = parent.getParentNode();
                    html.insertBefore(newNode, parent);
                } else {
                    parent.appendChild(newNode);
                }
            }
            // Step down and process child nodes except for newly created templates
            if (!isNewTemplateOrComponent) {
                createChildNodes(node, newNode, page, removeHashAttribute, depth + 1);
            }
        }
    }
    // reset instructions when leaving a level
    if (instructions != null) {
        createEmptyContentNode(page, parent, commentHandler, instructions);
        instructions = null;
    }
    return rootElement;
}
Also used : LinkSource(org.structr.web.entity.LinkSource) NodeAttribute(org.structr.core.graph.NodeAttribute) Attribute(org.jsoup.nodes.Attribute) Node(org.jsoup.nodes.Node) DOMNode(org.structr.web.entity.dom.DOMNode) DataNode(org.jsoup.nodes.DataNode) TextNode(org.jsoup.nodes.TextNode) AbstractNode(org.structr.core.entity.AbstractNode) DOMElement(org.structr.web.entity.dom.DOMElement) Element(org.jsoup.nodes.Element) StringProperty(org.structr.core.property.StringProperty) DOMElement(org.structr.web.entity.dom.DOMElement) Input(org.structr.web.entity.html.Input) UnlicensedException(org.structr.common.error.UnlicensedException) DataNode(org.jsoup.nodes.DataNode) PropertyConverter(org.structr.core.converter.PropertyConverter) DOMNode(org.structr.web.entity.dom.DOMNode) Body(org.structr.web.entity.html.Body) NodeInterface(org.structr.core.graph.NodeInterface) NodeAttribute(org.structr.core.graph.NodeAttribute) Head(org.structr.web.entity.html.Head) IOException(java.io.IOException) PropertyMap(org.structr.core.property.PropertyMap) Content(org.structr.web.entity.dom.Content) DOMNode(org.structr.web.entity.dom.DOMNode) Linkable(org.structr.web.entity.Linkable) GraphObject(org.structr.core.GraphObject) PropertyKey(org.structr.core.property.PropertyKey)

Example 5 with Linkable

use of org.structr.web.entity.Linkable in project structr by structr.

the class HtmlServlet method checkHttpBasicAuth.

private HttpBasicAuthResult checkHttpBasicAuth(final HttpServletRequest request, final HttpServletResponse response, final String path) throws IOException, FrameworkException {
    final PropertyKey<Boolean> basicAuthKey = StructrApp.key(Linkable.class, "enableBasicAuth");
    final PropertyKey<Integer> positionKey = StructrApp.key(Page.class, "position");
    final PropertyKey<String> filePathKey = StructrApp.key(File.class, "path");
    final PropertyKey<String> pagePathKey = StructrApp.key(Page.class, "path");
    // Look for renderable objects using a SuperUserSecurityContext,
    // but dont actually render the page. We're only interested in
    // the authentication settings.
    Linkable possiblePage = null;
    // try the different methods..
    if (possiblePage == null) {
        possiblePage = StructrApp.getInstance().nodeQuery(Page.class).and(pagePathKey, path).and(basicAuthKey, true).sort(positionKey).getFirst();
    }
    if (possiblePage == null) {
        possiblePage = StructrApp.getInstance().nodeQuery(Page.class).and(Page.name, PathHelper.getName(path)).and(basicAuthKey, true).sort(positionKey).getFirst();
    }
    if (possiblePage == null) {
        possiblePage = StructrApp.getInstance().nodeQuery(File.class).and(filePathKey, path).and(basicAuthKey, true).getFirst();
    }
    if (possiblePage == null) {
        possiblePage = StructrApp.getInstance().nodeQuery(File.class).and(File.name, PathHelper.getName(path)).and(basicAuthKey, true).getFirst();
    }
    if (possiblePage != null) {
        String realm = possiblePage.getBasicAuthRealm();
        if (realm == null) {
            realm = possiblePage.getName();
        }
        // check Http Basic Authentication headers
        final Principal principal = getPrincipalForAuthorizationHeader(request.getHeader("Authorization"));
        if (principal != null) {
            final SecurityContext securityContext = SecurityContext.getInstance(principal, AccessMode.Frontend);
            if (securityContext != null) {
                // find and instantiate the page again so that the SuperUserSecurityContext
                // can not leak into any of the children of the given page. This is dangerous..
                final Linkable page = StructrApp.getInstance(securityContext).get(Linkable.class, possiblePage.getUuid());
                if (page != null) {
                    securityContext.setRequest(request);
                    securityContext.setResponse(response);
                    return new HttpBasicAuthResult(AuthState.Authenticated, securityContext, page);
                }
            }
        }
        // fallback: the following code will be executed if no Authorization
        // header was sent, OR if the authentication failed
        response.setHeader("WWW-Authenticate", "BASIC realm=\"" + realm + "\"");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        // no Authorization header sent by client
        return HttpBasicAuthResult.MUST_AUTHENTICATE;
    }
    // no Http Basic Auth enabled for any page
    return HttpBasicAuthResult.NO_BASIC_AUTH;
}
Also used : SecurityContext(org.structr.common.SecurityContext) Linkable(org.structr.web.entity.Linkable) Page(org.structr.web.entity.dom.Page) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) Principal(org.structr.core.entity.Principal)

Aggregations

Linkable (org.structr.web.entity.Linkable)9 Page (org.structr.web.entity.dom.Page)5 SecurityContext (org.structr.common.SecurityContext)4 FrameworkException (org.structr.common.error.FrameworkException)4 LinkSource (org.structr.web.entity.LinkSource)4 DOMNode (org.structr.web.entity.dom.DOMNode)4 IOException (java.io.IOException)3 App (org.structr.core.app.App)3 StructrApp (org.structr.core.app.StructrApp)3 AbstractNode (org.structr.core.entity.AbstractNode)3 Principal (org.structr.core.entity.Principal)3 Tx (org.structr.core.graph.Tx)3 LinkedList (java.util.LinkedList)2 List (java.util.List)2 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)2 Matcher (java.util.regex.Matcher)2 ThreadLocalMatcher (org.structr.common.ThreadLocalMatcher)2 Result (org.structr.core.Result)2 Query (org.structr.core.app.Query)2 Authenticator (org.structr.core.auth.Authenticator)2