@Override
  public <T> void setProperty(final PropertyKey<T> key, final T value) throws FrameworkException {

    // check for read-only properties
    // if (StructrApp.getConfiguration().isReadOnlyProperty(type, key) ||
    // (StructrApp.getConfiguration().isWriteOnceProperty(type, key) && (dbRelationship != null) &&
    // dbRelationship.hasProperty(key.name()))) {
    if (key.isReadOnly()
        || (key.isWriteOnce()
            && (dbRelationship != null)
            && dbRelationship.hasProperty(key.dbName()))) {

      if (readOnlyPropertiesUnlocked || securityContext.isSuperUser()) {

        // permit write operation once and
        // lock read-only properties again
        readOnlyPropertiesUnlocked = false;

      } else {

        throw new FrameworkException(getClass().getSimpleName(), new ReadOnlyPropertyToken(key));
      }
    }

    key.setProperty(securityContext, this, value);
  }
Esempio n. 2
0
  /**
   * Find the page with the lowest position value which is visible in the current securit context
   *
   * @param securityContext
   * @return
   * @throws FrameworkException
   */
  private Page findIndexPage(final SecurityContext securityContext) throws FrameworkException {

    Result<Page> results =
        StructrApp.getInstance(securityContext)
            .nodeQuery(Page.class)
            .sort(Page.position)
            .order(false)
            .getResult();
    Collections.sort(
        results.getResults(),
        new GraphObjectComparator(Page.position, GraphObjectComparator.ASCENDING));

    // Find first visible page
    Page page = null;

    if (!results.isEmpty()) {

      int i = 0;

      while (page == null || (i < results.size() && !securityContext.isVisible(page))) {

        page = results.get(i++);
      }
    }

    return page;
  }
Esempio n. 3
0
  public ActionContext(
      final SecurityContext securityContext, final Map<String, Object> parameters) {

    if (parameters != null) {
      this.tmpStore.putAll(parameters);
    }

    this.securityContext = securityContext;

    if (securityContext != null) {
      this.locale = securityContext.getEffectiveLocale();
    }
  }
Esempio n. 4
0
  @Override
  protected void setUp() throws Exception {

    init();

    securityContext = SecurityContext.getSuperUserInstance();
    createNodeCommand = Services.command(securityContext, CreateNodeCommand.class);
    createRelationshipCommand = Services.command(securityContext, CreateRelationshipCommand.class);
    deleteNodeCommand = Services.command(securityContext, DeleteNodeCommand.class);
    transactionCommand = Services.command(securityContext, TransactionCommand.class);
    graphDbCommand = Services.command(securityContext, GraphDatabaseCommand.class);
    findNodeCommand = Services.command(securityContext, FindNodeCommand.class);
  }
Esempio n. 5
0
  // ~--- set methods ----------------------------------------------------
  public void setAuthenticated(final String sessionId, final Principal user) {

    this.sessionId = sessionId;

    try {

      this.securityContext = SecurityContext.getInstance(user, AccessMode.Backend);

    } catch (FrameworkException ex) {

      logger.log(Level.WARNING, "Could not get security context instance", ex);
    }
  }
  // ----- private methods -----
  private void updateAccessInformation(
      final SecurityContext securityContext, final PropertyContainer propertyContainer)
      throws FrameworkException {

    try {

      final Principal user = securityContext.getUser(false);
      String modifiedById = null;

      if (user != null) {

        if (user instanceof SuperUser) {

          // "virtual" UUID of superuser
          modifiedById = Principal.SUPERUSER_ID;

        } else {

          modifiedById = user.getUuid();
        }

        propertyContainer.setProperty(AbstractNode.lastModifiedBy.dbName(), modifiedById);
      }

      if (!securityContext.dontModifyAccessTime()) {

        propertyContainer.setProperty(
            AbstractNode.lastModifiedDate.dbName(), System.currentTimeMillis());
      }

    } catch (Throwable t) {

      // fail without throwing an exception here
      logger.warn("", t);
    }
  }
Esempio n. 7
0
  public static Object call(final String key, final Map<String, Object> parameters)
      throws FrameworkException {

    final SecurityContext superUserContext = SecurityContext.getSuperUserInstance();
    final App app = StructrApp.getInstance(superUserContext);

    // we might want to introduce caching here at some point in the future..
    // Cache can be invalidated when the schema is rebuilt for example..

    final List<SchemaMethod> methods = app.nodeQuery(SchemaMethod.class).andName(key).getAsList();
    if (methods.isEmpty()) {

      logger.debug("Tried to call method {} but no SchemaMethod entity was found.", key);

    } else {

      for (final SchemaMethod method : methods) {

        // only call methods that are NOT part of a schema node
        final AbstractSchemaNode entity = method.getProperty(SchemaMethod.schemaNode);
        if (entity == null) {

          final String source = method.getProperty(SchemaMethod.source);
          if (source != null) {

            return Actions.execute(superUserContext, null, "${" + source + "}", parameters);

          } else {

            logger.warn("Schema method {} has no source code, will NOT be executed.", key);
          }

        } else {

          logger.warn("Schema method {} is attached to an entity, will NOT be executed.", key);
        }
      }
    }

    return null;
  }
Esempio n. 8
0
  @Override
  protected void doGet(final HttpServletRequest request, final HttpServletResponse response) {

    final Authenticator auth = config.getAuthenticator();
    final SecurityContext securityContext;
    final App app;

    try {
      String path = request.getPathInfo();

      // check for registration (has its own tx because of write access
      if (checkRegistration(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();
      }

      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.log(Level.FINE, "Path info {0}", path);

        // don't continue on redirects
        if (response.getStatus() == 302) {
          return;
        }

        Principal user = securityContext.getUser(false);
        if (user != null) {

          // Don't cache if a user is logged in
          dontCache = true;
        }

        final RenderContext renderContext =
            RenderContext.getInstance(request, response, getEffectiveLocale(request));

        renderContext.setResourceProvider(config.getResourceProvider());

        EditMode edit = renderContext.getEditMode(user);

        DOMNode rootElement = null;
        AbstractNode dataNode = null;

        String[] uriParts = PathHelper.getParts(path);
        if ((uriParts == null) || (uriParts.length == 0)) {

          // find a visible page
          rootElement = findIndexPage(securityContext);

          logger.log(Level.FINE, "No path supplied, trying to find index page");

        } else {

          if (rootElement == null) {

            rootElement = findPage(securityContext, request, path);

          } else {
            dontCache = true;
          }
        }

        if (rootElement == null) { // No page found

          // Look for a file
          File file = findFile(securityContext, request, path);
          if (file != null) {

            streamFile(securityContext, file, request, response, edit);
            return;
          }

          // store remaining path parts in request
          Matcher matcher = threadLocalUUIDMatcher.get();
          boolean requestUriContainsUuids = false;

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

            // 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);

            rootElement =
                findPage(
                    securityContext,
                    request,
                    StringUtils.substringBeforeLast(path, PathHelper.PATH_SEP));

            renderContext.setDetailsDataObject(dataNode);

            // Start rendering on data node
            if (rootElement == null && dataNode instanceof DOMNode) {

              rootElement = ((DOMNode) dataNode);
            }
          }
        }

        // Still nothing found, do error handling
        if (rootElement == null) {

          // Check if security context has set an 401 status
          if (response.getStatus() == HttpServletResponse.SC_UNAUTHORIZED) {

            try {

              UiAuthenticator.writeUnauthorized(response);

            } catch (IllegalStateException ise) {
            }

          } else {

            rootElement = notFound(response, securityContext);
          }
        }

        if (rootElement == null) {
          return;
        }

        if (EditMode.WIDGET.equals(edit) || dontCache) {

          setNoCacheHeaders(response);
        }

        if (!securityContext.isVisible(rootElement)) {

          rootElement = notFound(response, securityContext);
          if (rootElement == null) {
            return;
          }
        }

        if (securityContext.isVisible(rootElement)) {

          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(Page.contentType);

            if (contentType != null && contentType.equals("text/html")) {

              contentType = contentType.concat(";charset=UTF-8");
              response.setContentType(contentType);

            } else {

              // Default
              response.setContentType("text/html;charset=UTF-8");
            }

            response.setHeader("Strict-Transport-Security", "max-age=60");
            response.setHeader("X-Content-Type-Options", "nosniff");
            response.setHeader("X-Frame-Options", "SAMEORIGIN");
            response.setHeader("X-XSS-Protection", "1; mode=block");

            // async or not?
            boolean isAsync =
                HttpService.parseBoolean(
                    Services.getBaseConfiguration().getProperty(HttpService.ASYNC), true);
            if (isAsync) {

              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()) {

                        // final long start = System.currentTimeMillis();

                        // render
                        rootNode.render(securityContext, renderContext, 0);
                        finished.set(true);

                        // final long end = System.currentTimeMillis();
                        // System.out.println("Done in " + (end-start) + " ms");

                        tx.success();

                      } catch (Throwable t) {
                        t.printStackTrace();
                        final String errorMsg = t.getMessage();
                        try {
                          // response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                          response.sendError(
                              HttpServletResponse.SC_INTERNAL_SERVER_ERROR, errorMsg);
                          finished.set(true);
                        } catch (IOException ex) {
                          ex.printStackTrace();
                        }
                      }
                    }
                  });

              // 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();
                              response.setStatus(HttpServletResponse.SC_OK);

                              // prevent this block from being called again
                              break;
                            }

                            Thread.sleep(1);
                          }
                        }

                      } catch (Throwable t) {
                        t.printStackTrace();
                      }
                    }

                    @Override
                    public void onError(Throwable t) {
                      t.printStackTrace();
                    }
                  });

            } else {

              final StringRenderBuffer buffer = new StringRenderBuffer();
              renderContext.setBuffer(buffer);

              // render
              rootElement.render(securityContext, renderContext, 0);

              response.getOutputStream().write(buffer.getBuffer().toString().getBytes("utf-8"));
              response.getOutputStream().flush();
              response.getOutputStream().close();
            }
          }

        } else {

          notFound(response, securityContext);
        }

        tx.success();

      } catch (FrameworkException fex) {
        fex.printStackTrace();
        logger.log(Level.SEVERE, "Exception while processing request", fex);
      }

    } catch (IOException | FrameworkException t) {

      t.printStackTrace();
      logger.log(Level.SEVERE, "Exception while processing request", t);
      UiAuthenticator.writeInternalServerError(response);
    }
  }
Esempio n. 9
0
  /**
   * This method checks all configured external authentication services.
   *
   * @param request
   * @param response
   * @return user
   */
  protected static Principal checkExternalAuthentication(
      final HttpServletRequest request, final HttpServletResponse response)
      throws FrameworkException {

    final String path = PathHelper.clean(request.getPathInfo());
    final String[] uriParts = PathHelper.getParts(path);

    logger.log(Level.FINE, "Checking external authentication ...");

    if (uriParts == null || uriParts.length != 3 || !("oauth".equals(uriParts[0]))) {

      logger.log(Level.FINE, "Incorrect URI parts for OAuth process, need /oauth/<name>/<action>");
      return null;
    }

    final String name = uriParts[1];
    final String action = uriParts[2];

    // Try to getValue an OAuth2 server for the given name
    final StructrOAuthClient oauthServer = StructrOAuthClient.getServer(name);

    if (oauthServer == null) {

      logger.log(Level.FINE, "No OAuth2 authentication server configured for {0}", path);
      return null;
    }

    if ("login".equals(action)) {

      try {

        response.sendRedirect(oauthServer.getEndUserAuthorizationRequestUri(request));
        return null;

      } catch (Exception ex) {

        logger.log(Level.SEVERE, "Could not send redirect to authorization server", ex);
      }

    } else if ("auth".equals(action)) {

      final String accessToken = oauthServer.getAccessToken(request);
      final SecurityContext superUserContext = SecurityContext.getSuperUserInstance();

      if (accessToken != null) {

        logger.log(Level.FINE, "Got access token {0}", accessToken);
        // securityContext.setAttribute("OAuthAccessToken", accessToken);

        String value = oauthServer.getCredential(request);
        logger.log(Level.FINE, "Got credential value: {0}", new Object[] {value});

        if (value != null) {

          PropertyKey credentialKey = oauthServer.getCredentialKey();

          Principal user = AuthHelper.getPrincipalForCredential(credentialKey, value);

          if (user == null && userAutoCreate) {

            user =
                RegistrationResource.createUser(
                    superUserContext, credentialKey, value, true, userClass);
          }

          if (user != null) {

            AuthHelper.doLogin(request, user);
            HtmlServlet.setNoCacheHeaders(response);

            try {

              logger.log(Level.FINE, "Response status: {0}", response.getStatus());

              response.sendRedirect(oauthServer.getReturnUri());

            } catch (IOException ex) {

              logger.log(
                  Level.SEVERE,
                  "Could not redirect to {0}: {1}",
                  new Object[] {oauthServer.getReturnUri(), ex});
            }
            return user;
          }
        }
      }
    }

    try {

      response.sendRedirect(oauthServer.getErrorUri());

    } catch (IOException ex) {

      logger.log(
          Level.SEVERE,
          "Could not redirect to {0}: {1}",
          new Object[] {oauthServer.getReturnUri(), ex});
    }

    return null;
  }
Esempio n. 10
0
  /**
   * Examine request and try to find a user.
   *
   * <p>First, check session id, then try external (OAuth) authentication, finally, check standard
   * login by credentials.
   *
   * @param request
   * @param response
   * @return security context
   * @throws FrameworkException
   */
  @Override
  public SecurityContext initializeAndExamineRequest(
      final HttpServletRequest request, final HttpServletResponse response)
      throws FrameworkException {

    SecurityContext securityContext;

    Principal user = checkSessionAuthentication(request);

    if (user == null) {

      user = checkExternalAuthentication(request, response);
    }

    if (user == null) {

      user = getUser(request, true);
    }

    if (user == null) {

      // If no user could be determined, assume frontend access
      securityContext = SecurityContext.getInstance(user, request, AccessMode.Frontend);

    } else {

      if (user instanceof SuperUser) {

        securityContext = SecurityContext.getSuperUserInstance(request);

      } else {

        securityContext = SecurityContext.getInstance(user, request, AccessMode.Backend);
      }
    }

    securityContext.setAuthenticator(this);

    // Check CORS settings (Cross-origin resource sharing, see
    // http://en.wikipedia.org/wiki/Cross-origin_resource_sharing)
    final String origin = request.getHeader("Origin");
    if (!StringUtils.isBlank(origin)) {

      final Services services = Services.getInstance();

      response.setHeader("Access-Control-Allow-Origin", origin);

      // allow cross site resource sharing (read only)
      final String maxAge = services.getConfigurationValue(Services.ACCESS_CONTROL_MAX_AGE);
      if (StringUtils.isNotBlank(maxAge)) {
        response.setHeader("Access-Control-MaxAge", maxAge);
      }

      final String allowMethods =
          services.getConfigurationValue(Services.ACCESS_CONTROL_ALLOW_METHODS);
      if (StringUtils.isNotBlank(allowMethods)) {
        response.setHeader("Access-Control-Allow-Methods", allowMethods);
      }

      final String allowHeaders =
          services.getConfigurationValue(Services.ACCESS_CONTROL_ALLOW_HEADERS);
      if (StringUtils.isNotBlank(allowHeaders)) {
        response.setHeader("Access-Control-Allow-Headers", allowHeaders);
      }

      final String allowCredentials =
          services.getConfigurationValue(Services.ACCESS_CONTROL_ALLOW_CREDENTIALS);
      if (StringUtils.isNotBlank(allowCredentials)) {
        response.setHeader("Access-Control-Allow-Credentials", allowCredentials);
      }

      final String exposeHeaders =
          services.getConfigurationValue(Services.ACCESS_CONTROL_EXPOSE_HEADERS);
      if (StringUtils.isNotBlank(exposeHeaders)) {
        response.setHeader("Access-Control-Expose-Headers", exposeHeaders);
      }
    }

    examined = true;
    return securityContext;
  }
Esempio n. 11
0
  @Override
  public void execute(Map<String, Object> attributes) throws FrameworkException {

    final String entityType = (String) attributes.get("type");
    final String relType = (String) attributes.get("relType");
    final GraphDatabaseService graphDb = (GraphDatabaseService) arguments.get("graphDb");
    final SecurityContext superUserContext = SecurityContext.getSuperUserInstance();
    final NodeFactory nodeFactory = new NodeFactory(superUserContext);
    final RelationshipFactory relFactory = new RelationshipFactory(superUserContext);

    if (entityType != null) {

      final Class type = EntityContext.getEntityClassForRawType(entityType);

      if (type != null) {

        // final Result<AbstractNode> result = Services.command(securityContext,
        // SearchNodeCommand.class).execute(true, false, Search.andExactType(type.getSimpleName()));
        final Result<AbstractNode> result =
            nodeFactory.instantiateAll(GlobalGraphOperations.at(graphDb).getAllNodes());
        final List<AbstractNode> nodes = new ArrayList<AbstractNode>();

        for (AbstractNode node : result.getResults()) {

          if (node.getClass().equals(type)) {

            nodes.add(node);
          }
        }

        logger.log(
            Level.INFO,
            "Start (re-)indexing all nodes of type {0}",
            new Object[] {type.getSimpleName()});

        long count =
            bulkGraphOperation(
                securityContext,
                nodes,
                1000,
                "RebuildIndex",
                new BulkGraphOperation<AbstractNode>() {

                  @Override
                  public void handleGraphObject(
                      SecurityContext securityContext, AbstractNode node) {

                    node.updateInIndex();
                  }

                  @Override
                  public void handleThrowable(
                      SecurityContext securityContext, Throwable t, AbstractNode node) {

                    logger.log(
                        Level.WARNING,
                        "Unable to index node {0}: {1}",
                        new Object[] {node, t.getMessage()});
                  }

                  @Override
                  public void handleTransactionFailure(
                      SecurityContext securityContext, Throwable t) {

                    logger.log(Level.WARNING, "Unable to index node: {0}", t.getMessage());
                  }
                });

        logger.log(Level.INFO, "Done with (re-)indexing {0} nodes", count);

        return;
      }

    } else if (relType != null) {

      // final Result<AbstractNode> result = Services.command(securityContext,
      // SearchNodeCommand.class).execute(true, false, Search.andExactType(type.getSimpleName()));
      final List<AbstractRelationship> unfilteredRels =
          relFactory.instantiate(GlobalGraphOperations.at(graphDb).getAllRelationships());
      final List<AbstractRelationship> rels = new ArrayList<AbstractRelationship>();

      for (AbstractRelationship rel : unfilteredRels) {

        if (!rel.getType().equals(relType)) {

          rels.add(rel);
        }
      }

      logger.log(Level.INFO, "Start setting UUID on all rels of type {0}", new Object[] {relType});

      long count =
          bulkGraphOperation(
              securityContext,
              rels,
              1000,
              "SetRelationshipUuid",
              new BulkGraphOperation<AbstractRelationship>() {

                @Override
                public void handleGraphObject(
                    SecurityContext securityContext, AbstractRelationship rel) {

                  rel.updateInIndex();
                }

                @Override
                public void handleThrowable(
                    SecurityContext securityContext, Throwable t, AbstractRelationship rel) {

                  logger.log(
                      Level.WARNING,
                      "Unable to index relationship {0}: {1}",
                      new Object[] {rel, t.getMessage()});
                }

                @Override
                public void handleTransactionFailure(SecurityContext securityContext, Throwable t) {

                  logger.log(Level.WARNING, "Unable to index relationship: {0}", t.getMessage());
                }
              });

      logger.log(Level.INFO, "Done with (re-)indexing {0} relationships", count);

      return;
    }

    logger.log(Level.INFO, "Unable to determine entity type to re-index.");
  }
Esempio n. 12
0
  public static void analyzeSchema() {

    final App app = StructrApp.getInstance();
    final FileBasedHashLongMap<NodeInfo> nodeIdMap =
        new FileBasedHashLongMap<>(userHome + File.separator + ".structrSchemaAnalyzer");
    final GraphDatabaseService graphDb = app.command(GraphDatabaseCommand.class).execute();
    final ConfigurationProvider configuration = Services.getInstance().getConfigurationProvider();
    final Set<NodeInfo> nodeTypes = new LinkedHashSet<>();
    final Set<RelationshipInfo> relationships = new LinkedHashSet<>();
    final Map<String, SchemaNode> schemaNodes = new LinkedHashMap<>();
    final Map<String, List<TypeInfo>> typeInfoTypeMap = new LinkedHashMap<>();
    final List<TypeInfo> reducedTypeInfos = new LinkedList<>();
    final List<TypeInfo> typeInfos = new LinkedList<>();
    Iterator<Relationship> relIterator = null;
    Iterator<Node> nodeIterator = null;

    logger.log(Level.INFO, "Fetching all nodes iterator..");

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

      nodeIterator =
          Iterables.filter(
                  new StructrAndSpatialPredicate(false, false, true),
                  GlobalGraphOperations.at(graphDb).getAllNodes())
              .iterator();
      tx.success();

    } catch (FrameworkException fex) {
      fex.printStackTrace();
    }

    logger.log(Level.INFO, "Starting to analyze nodes..");

    NodeServiceCommand.bulkGraphOperation(
        SecurityContext.getSuperUserInstance(),
        nodeIterator,
        100000,
        "Analyzing nodes",
        new BulkGraphOperation<Node>() {

          @Override
          public void handleGraphObject(final SecurityContext securityContext, final Node node)
              throws FrameworkException {

            final NodeInfo nodeInfo = new NodeInfo(node);

            // hashcode of nodeInfo is derived from its property and type signature!
            nodeTypes.add(nodeInfo);

            // add node ID to our new test datastructure
            nodeIdMap.add(nodeInfo, node.getId());
          }
        });

    logger.log(Level.INFO, "Identifying common base classes..");

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

      // nodeTypes now contains all existing node types and their property sets
      identifyCommonBaseClasses(app, nodeTypes, nodeIdMap, typeInfos);

      tx.success();

    } catch (FrameworkException fex) {
      fex.printStackTrace();
    }

    logger.log(Level.INFO, "Collecting type information..");

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

      // group type infos by type
      collectTypeInfos(typeInfos, typeInfoTypeMap);

      tx.success();

    } catch (FrameworkException fex) {
      fex.printStackTrace();
    }

    logger.log(Level.INFO, "Aggregating type information..");

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

      // reduce type infos with more than one type
      reduceTypeInfos(typeInfoTypeMap, reducedTypeInfos);

      tx.success();

    } catch (FrameworkException fex) {
      fex.printStackTrace();
    }

    logger.log(Level.INFO, "Identifying property sets..");
    try (final Tx tx = app.tx(true, false, false)) {

      // intersect property sets of type infos
      intersectPropertySets(reducedTypeInfos);

      tx.success();

    } catch (FrameworkException fex) {
      fex.printStackTrace();
    }

    logger.log(Level.INFO, "Sorting result..");
    try (final Tx tx = app.tx(false, false, false)) {

      // sort type infos
      Collections.sort(reducedTypeInfos, new HierarchyComparator(false));

      tx.success();

    } catch (FrameworkException fex) {
      fex.printStackTrace();
    }

    final Map<String, TypeInfo> reducedTypeInfoMap = new LinkedHashMap<>();

    for (final TypeInfo info : reducedTypeInfos) {

      final String type = info.getPrimaryType();

      // map TypeInfo to type for later use
      reducedTypeInfoMap.put(type, info);

      logger.log(Level.INFO, "Starting with setting of type and ID for type {0}", type);

      NodeServiceCommand.bulkGraphOperation(
          SecurityContext.getSuperUserInstance(),
          info.getNodeIds().iterator(),
          10000,
          "Setting type and ID",
          new BulkGraphOperation<Long>() {

            @Override
            public void handleGraphObject(SecurityContext securityContext, Long nodeId)
                throws FrameworkException {

              final Node node = graphDb.getNodeById(nodeId);

              node.setProperty(GraphObject.id.dbName(), NodeServiceCommand.getNextUuid());
              node.setProperty(GraphObject.type.dbName(), type);
            }
          });
    }

    logger.log(Level.INFO, "Fetching all relationships iterator..");

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

      relIterator =
          Iterables.filter(
                  new StructrAndSpatialPredicate(false, false, true),
                  GlobalGraphOperations.at(graphDb).getAllRelationships())
              .iterator();
      tx.success();

    } catch (FrameworkException fex) {
      fex.printStackTrace();
    }

    logger.log(Level.INFO, "Starting with analyzing relationships..");

    NodeServiceCommand.bulkGraphOperation(
        SecurityContext.getSuperUserInstance(),
        relIterator,
        10000,
        "Analyzing relationships",
        new BulkGraphOperation<Relationship>() {

          @Override
          public void handleGraphObject(SecurityContext securityContext, Relationship rel)
              throws FrameworkException {

            final Node startNode = rel.getStartNode();
            final Node endNode = rel.getEndNode();

            // make sure node has been successfully identified above
            if (startNode.hasProperty("type") && endNode.hasProperty("type")) {

              final String relationshipType = rel.getType().name();
              final String startNodeType = (String) startNode.getProperty("type");
              final String endNodeType = (String) endNode.getProperty("type");

              relationships.add(new RelationshipInfo(startNodeType, endNodeType, relationshipType));

              // create combined type on imported relationship
              if (startNodeType != null && endNodeType != null) {

                final String combinedType =
                    getCombinedType(startNodeType, relationshipType, endNodeType);

                logger.log(
                    Level.FINE,
                    "Combined relationship type {0} found for rel type {1}, start node type {2}, end node type {3}",
                    new Object[] {combinedType, relationshipType, startNodeType, endNodeType});

                rel.setProperty(GraphObject.type.dbName(), combinedType);
              }

              // create ID on imported relationship
              rel.setProperty(GraphObject.id.dbName(), NodeServiceCommand.getNextUuid());
            }
          }
        });

    logger.log(Level.INFO, "Grouping relationships..");

    // group relationships by type
    final Map<String, List<RelationshipInfo>> relTypeInfoMap = new LinkedHashMap<>();
    for (final RelationshipInfo relInfo : relationships) {

      // final String relType         = relInfo.getRelType();
      final String combinedType =
          getCombinedType(
              relInfo.getStartNodeType(), relInfo.getRelType(), relInfo.getEndNodeType());
      List<RelationshipInfo> infos = relTypeInfoMap.get(combinedType);

      if (infos == null) {

        infos = new LinkedList<>();
        relTypeInfoMap.put(combinedType, infos);
      }

      infos.add(relInfo);
    }

    logger.log(Level.INFO, "Aggregating relationship information..");

    final List<RelationshipInfo> reducedRelationshipInfos = new ArrayList<>();
    if ("true"
        .equals(
            Services.getInstance()
                .getConfigurationValue("importer.inheritancedetection", "true"))) {

      // reduce relationship infos into one
      for (final List<RelationshipInfo> infos : relTypeInfoMap.values()) {

        reducedRelationshipInfos.addAll(reduceNodeTypes(infos, reducedTypeInfoMap));
      }

    } else {

      reducedRelationshipInfos.addAll(relationships);
    }

    logger.log(Level.INFO, "Starting with schema node creation..");

    NodeServiceCommand.bulkGraphOperation(
        SecurityContext.getSuperUserInstance(),
        reducedTypeInfos.iterator(),
        100000,
        "Creating schema nodes",
        new BulkGraphOperation<TypeInfo>() {

          @Override
          public void handleGraphObject(SecurityContext securityContext, TypeInfo typeInfo)
              throws FrameworkException {

            final String type = typeInfo.getPrimaryType();
            if (!"ReferenceNode".equals(type)) {

              final Map<String, Class> props = typeInfo.getPropertySet();
              final PropertyMap propertyMap = new PropertyMap();

              // add properties
              for (final Map.Entry<String, Class> propertyEntry : props.entrySet()) {

                final String propertyName = propertyEntry.getKey();
                final Class propertyType = propertyEntry.getValue();

                // handle array types differently
                String propertyTypeName = propertyType.getSimpleName();
                if (propertyType.isArray()) {

                  // remove "[]" from the end and append "Array" to match the appropriate parser
                  propertyTypeName =
                      propertyTypeName.substring(0, propertyTypeName.length() - 2).concat("Array");
                }

                propertyMap.put(new StringProperty("_".concat(propertyName)), propertyTypeName);
              }

              // set node type which is in "name" property
              propertyMap.put(AbstractNode.name, type);

              // check if there is an existing Structr entity with the same type
              // and make the dynamic class extend the existing class if yes.
              final Class existingType = configuration.getNodeEntityClass(type);
              if (existingType != null) {

                propertyMap.put(SchemaNode.extendsClass, existingType.getName());

              } else if (!typeInfo.getOtherTypes().isEmpty()) {

                // only the first supertype is supported
                propertyMap.put(
                    SchemaNode.extendsClass, typeInfo.getSuperclass(reducedTypeInfoMap));
              }

              final SchemaNode existingNode =
                  app.nodeQuery(SchemaNode.class).andName(type).getFirst();
              if (existingNode != null) {

                for (final Entry<PropertyKey, Object> entry : propertyMap.entrySet()) {

                  existingNode.setProperty(entry.getKey(), entry.getValue());
                }

                schemaNodes.put(type, existingNode);

              } else {

                // create schema node
                schemaNodes.put(type, app.create(SchemaNode.class, propertyMap));
              }
            }
          }
        });

    logger.log(Level.INFO, "Starting with schema relationship creation..");

    NodeServiceCommand.bulkGraphOperation(
        SecurityContext.getSuperUserInstance(),
        reducedRelationshipInfos.iterator(),
        100000,
        "Creating schema relationships",
        new BulkGraphOperation<RelationshipInfo>() {

          @Override
          public void handleGraphObject(SecurityContext securityContext, RelationshipInfo template)
              throws FrameworkException {

            final SchemaNode startNode = schemaNodes.get(template.getStartNodeType());
            final SchemaNode endNode = schemaNodes.get(template.getEndNodeType());
            final String relationshipType = template.getRelType();
            final PropertyMap propertyMap = new PropertyMap();

            propertyMap.put(SchemaRelationshipNode.sourceId, startNode.getUuid());
            propertyMap.put(SchemaRelationshipNode.targetId, endNode.getUuid());
            propertyMap.put(SchemaRelationshipNode.relationshipType, relationshipType);

            app.create(SchemaRelationshipNode.class, propertyMap);
          }
        });

    logger.log(Level.INFO, "Starting with index rebuild..");

    // rebuild index
    app.command(BulkRebuildIndexCommand.class).execute(Collections.EMPTY_MAP);
  }
Esempio n. 13
0
  private void streamFile(
      SecurityContext securityContext,
      final File file,
      HttpServletRequest request,
      HttpServletResponse response,
      final EditMode edit)
      throws IOException {

    if (!securityContext.isVisible(file)) {

      response.sendError(HttpServletResponse.SC_NOT_FOUND);
      return;
    }

    ServletOutputStream out = response.getOutputStream();

    if (!EditMode.WIDGET.equals(edit) && notModifiedSince(request, response, file, false)) {

      out.flush();
      out.close();

    } else {

      // 2b: stream file to response
      InputStream in = file.getInputStream();
      String contentType = file.getContentType();

      if (contentType != null) {

        response.setContentType(contentType);

      } else {

        // Default
        response.setContentType("application/octet-stream");
      }

      response.setStatus(HttpServletResponse.SC_OK);

      try {

        IOUtils.copy(in, out);

      } catch (Throwable t) {

      } finally {

        if (out != null) {

          try {
            // 3: output content
            out.flush();
            out.close();

          } catch (Throwable t) {
          }
        }

        if (in != null) {
          in.close();
        }

        response.setStatus(HttpServletResponse.SC_OK);
      }
    }
  }
Esempio n. 14
0
  @Override
  public List<GraphObject> getData(
      final SecurityContext securityContext,
      final RenderContext renderContext,
      final String restQuery)
      throws FrameworkException {

    Map<Pattern, Class<? extends Resource>> resourceMap = new LinkedHashMap<>();

    ResourceProvider resourceProvider =
        renderContext == null ? null : renderContext.getResourceProvider();
    if (resourceProvider == null) {
      try {
        resourceProvider = UiResourceProvider.class.newInstance();
      } catch (Throwable t) {
        logger.log(Level.SEVERE, "Couldn't establish a resource provider", t);
        return Collections.EMPTY_LIST;
      }
    }

    // inject resources
    resourceMap.putAll(resourceProvider.getResources());

    Value<String> propertyView = new ThreadLocalPropertyView();
    propertyView.set(securityContext, PropertyView.Ui);

    // initialize variables
    // mimic HTTP request
    HttpServletRequest request =
        new HttpServletRequestWrapper(
            renderContext == null ? securityContext.getRequest() : renderContext.getRequest()) {

          @Override
          public Enumeration<String> getParameterNames() {
            return new IteratorEnumeration(getParameterMap().keySet().iterator());
          }

          @Override
          public String getParameter(String key) {
            String[] p = getParameterMap().get(key);
            return p != null ? p[0] : null;
          }

          @Override
          public Map<String, String[]> getParameterMap() {
            String[] parts = StringUtils.split(getQueryString(), "&");
            Map<String, String[]> parameterMap = new HashMap();
            for (String p : parts) {
              String[] kv = StringUtils.split(p, "=");
              if (kv.length > 1) {
                parameterMap.put(kv[0], new String[] {kv[1]});
              }
            }
            return parameterMap;
          }

          @Override
          public String getQueryString() {
            return StringUtils.substringAfter(restQuery, "?");
          }

          @Override
          public String getPathInfo() {
            return StringUtils.substringBefore(restQuery, "?");
          }

          @Override
          public StringBuffer getRequestURL() {
            return new StringBuffer(restQuery);
          }
        };

    // update request in security context
    securityContext.setRequest(request);

    // HttpServletResponse response = renderContext.getResponse();
    Resource resource =
        ResourceHelper.applyViewTransformation(
            request,
            securityContext,
            ResourceHelper.optimizeNestedResourceChain(
                ResourceHelper.parsePath(
                    securityContext, request, resourceMap, propertyView, GraphObject.id),
                GraphObject.id),
            propertyView);

    // TODO: decide if we need to rest the REST request here
    // securityContext.checkResourceAccess(request, resource.getResourceSignature(),
    // resource.getGrant(request, response), PropertyView.Ui);
    // add sorting & paging
    String pageSizeParameter = request.getParameter(JsonRestServlet.REQUEST_PARAMETER_PAGE_SIZE);
    String pageParameter = request.getParameter(JsonRestServlet.REQUEST_PARAMETER_PAGE_NUMBER);
    String offsetId = request.getParameter(JsonRestServlet.REQUEST_PARAMETER_OFFSET_ID);
    String sortOrder = request.getParameter(JsonRestServlet.REQUEST_PARAMETER_SORT_ORDER);
    String sortKeyName = request.getParameter(JsonRestServlet.REQUEST_PARAMETER_SORT_KEY);
    boolean sortDescending = (sortOrder != null && "desc".equals(sortOrder.toLowerCase()));
    int pageSize = parseInt(pageSizeParameter, NodeFactory.DEFAULT_PAGE_SIZE);
    int page = parseInt(pageParameter, NodeFactory.DEFAULT_PAGE);
    PropertyKey sortKey = null;

    // set sort key
    if (sortKeyName != null) {

      Class<? extends GraphObject> type = resource.getEntityClass();
      if (type == null) {

        // fallback to default implementation
        // if no type can be determined
        type = AbstractNode.class;
      }
      sortKey = StructrApp.getConfiguration().getPropertyKeyForDatabaseName(type, sortKeyName);
    }

    // do action
    Result result = resource.doGet(sortKey, sortDescending, pageSize, page, offsetId);
    result.setIsCollection(resource.isCollectionResource());
    result.setIsPrimitiveArray(resource.isPrimitiveArray());

    // Integer rawResultCount = (Integer) Services.getAttribute(NodeFactory.RAW_RESULT_COUNT +
    // Thread.currentThread().getId());
    PagingHelper.addPagingParameter(result, pageSize, page);

    List<GraphObject> res = result.getResults();

    if (renderContext != null) {
      renderContext.setResult(result);
    }

    return res != null ? res : Collections.EMPTY_LIST;
  }
Esempio n. 15
0
  @Override
  public Object apply(final ActionContext ctx, final GraphObject entity, final Object[] sources)
      throws FrameworkException {

    if (arrayHasMinLengthAndMaxLengthAndAllElementsNotNull(sources, 1, 2)) {

      final String cacheKey = cacheKey(sources);
      String value = getCachedValue(cacheKey);

      if (value == null) {

        final SecurityContext superUserSecurityContext = SecurityContext.getSuperUserInstance();
        final String locale = ctx.getLocale().toString();
        final String name = sources[0].toString();

        Query query =
            StructrApp.getInstance(superUserSecurityContext)
                .nodeQuery(Localization.class)
                .and(Localization.locale, locale)
                .and(Localization.name, name);
        List<Localization> localizations;

        final Locale ctxLocale = ctx.getLocale();
        final String fullLocale = ctxLocale.toString();
        final String lang = ctxLocale.getLanguage();

        if (sources.length == 2) {

          final String domain = sources[1].toString();

          // with domain
          query.and(Localization.domain, domain);

          localizations = query.getAsList();

          if (localizations.isEmpty() && fullLocale.contains("_")) {

            // no language-specific localization found, try language code only

            query =
                StructrApp.getInstance(superUserSecurityContext)
                    .nodeQuery(Localization.class)
                    .and(Localization.locale, lang)
                    .and(Localization.name, name)
                    .and(Localization.domain, domain);

            localizations = query.getAsList();
          }

        } else {

          // without domain
          query.blank(Localization.domain);

          localizations = query.getAsList();

          if (localizations.isEmpty() && fullLocale.contains("_")) {

            // no language-specific localization found, try language code only

            query =
                StructrApp.getInstance(superUserSecurityContext)
                    .nodeQuery(Localization.class)
                    .and(Localization.locale, lang)
                    .and(Localization.name, name)
                    .blank(Localization.domain);

            localizations = query.getAsList();
          }
        }

        if (localizations.size() > 1) {

          // Ambiguous localization found
          if (sources.length > 1) {

            logger.warn(
                "Found ambiguous localization for key \"{}\" and domain \"{}\". Please fix. Parameters: {}",
                new Object[] {
                  sources[0].toString(), sources[1].toString(), getParametersAsString(sources)
                });

          } else {

            logger.warn(
                "Found ambiguous localization for key \"{}\". Please fix. Parameters: {}",
                new Object[] {sources[0].toString(), getParametersAsString(sources)});
          }
        }

        // return first localization
        if (localizations.isEmpty()) {

          // no localization found - return the key
          value = name;

        } else {

          value = localizations.get(0).getProperty(Localization.localizedName);
        }

        cacheValue(cacheKey, value);
      }

      return value;

    } else if (sources.length == 1 || sources.length == 2) {

      logParameterError(entity, sources, ctx.isJavaScriptContext());

      // silently ignore null values
      return "";

    } else {

      logParameterError(entity, sources, ctx.isJavaScriptContext());

      // only show the error message for wrong parameter count
      return usage(ctx.isJavaScriptContext());
    }
  }
  @Override
  public Object setProperty(
      final SecurityContext securityContext, final GraphObject obj, final T value)
      throws FrameworkException {

    final PropertyConverter converter = databaseConverter(securityContext, obj);
    final Object convertedValue;

    if (converter != null) {

      convertedValue = converter.convert(value);

    } else {

      convertedValue = value;
    }

    final PropertyContainer propertyContainer = obj.getPropertyContainer();
    if (propertyContainer != null) {

      if (!TransactionCommand.inTransaction()) {

        throw new NotInTransactionException("setProperty outside of transaction");
      }

      boolean internalSystemPropertiesUnlocked = (obj instanceof CreationContainer);

      // notify only non-system properties

      // collect modified properties
      if (obj instanceof AbstractNode) {

        if (!unvalidated) {

          TransactionCommand.nodeModified(
              securityContext.getCachedUser(),
              (AbstractNode) obj,
              AbstractPrimitiveProperty.this,
              propertyContainer.hasProperty(dbName())
                  ? propertyContainer.getProperty(dbName())
                  : null,
              value);
        }

        internalSystemPropertiesUnlocked = ((AbstractNode) obj).internalSystemPropertiesUnlocked;

      } else if (obj instanceof AbstractRelationship) {

        if (!unvalidated) {

          TransactionCommand.relationshipModified(
              securityContext.getCachedUser(),
              (AbstractRelationship) obj,
              AbstractPrimitiveProperty.this,
              propertyContainer.hasProperty(dbName())
                  ? propertyContainer.getProperty(dbName())
                  : null,
              value);
        }

        internalSystemPropertiesUnlocked =
            ((AbstractRelationship) obj).internalSystemPropertiesUnlocked;
      }

      // catch all sorts of errors and wrap them in a FrameworkException
      try {

        // save space
        if (convertedValue == null) {

          propertyContainer.removeProperty(dbName());

        } else {

          if (!isSystemInternal() || internalSystemPropertiesUnlocked) {

            propertyContainer.setProperty(dbName(), convertedValue);

          } else {

            logger.warn(
                "Tried to set internal system property {} to {}. Action was denied.",
                new Object[] {dbName(), convertedValue});
          }
        }

        updateAccessInformation(securityContext, propertyContainer);

      } catch (Throwable t) {

        // throw FrameworkException with the given cause
        final FrameworkException fex =
            new FrameworkException(
                500,
                "Unable to set property "
                    + jsonName()
                    + " on entity with ID "
                    + obj.getUuid()
                    + ": "
                    + t.toString());
        fex.initCause(t);

        throw fex;
      }

      if (isIndexed()) {

        // do indexing, needs to be done after
        // setProperty to make spatial index
        // work
        if (!isPassivelyIndexed()) {

          index(obj, convertedValue);
        }
      }
    }

    return null;
  }
Esempio n. 17
0
 @Override
 public SecurityContext initializeAndExamineRequest(
     HttpServletRequest request, HttpServletResponse response) throws FrameworkException {
   return SecurityContext.getSuperUserInstance(request);
 }
Esempio n. 18
0
  public Principal getCurrentUser() {

    return (securityContext == null ? null : securityContext.getUser(false));
  }
Esempio n. 19
0
  public Object evaluate(
      final GraphObject entity,
      final String key,
      final Object data,
      final String defaultValue,
      final int depth)
      throws FrameworkException {

    Object value = constants.get(key);
    if (value == null) {

      // special HttpServletRequest handling
      if (data instanceof HttpServletRequest) {
        value = ((HttpServletRequest) data).getParameter(key);
      }

      // special handling of maps..
      if (data instanceof Map) {
        value = ((Map) data).get(key);
      }

      if (data != null) {

        if (data instanceof GraphObject) {

          value = ((GraphObject) data).evaluate(securityContext, key, defaultValue);

        } else {

          switch (key) {
            case "size":
              if (data instanceof Collection) {
                return ((Collection) data).size();
              }
              if (data.getClass().isArray()) {
                return ((Object[]) data).length;
              }
              break;
          }
        }

      } else {

        // "data-less" keywords to start the evaluation chain
        switch (key) {
          case "request":
            return securityContext.getRequest();

          case "host":
            return securityContext.getRequest().getServerName();

          case "port":
            return securityContext.getRequest().getServerPort();

          case "pathInfo":
          case "path_info":
            return securityContext.getRequest().getPathInfo();

          case "parameterMap":
          case "parameter_map":
            return securityContext.getRequest().getParameterMap();

          case "remoteAddress":
          case "remote_address":
            final String remoteAddress = securityContext.getRequest().getHeader("X-FORWARDED-FOR");
            if (remoteAddress == null) {
              return securityContext.getRequest().getRemoteAddr();
            }
            return remoteAddress;

          case "response":
            if (securityContext != null) {
              final HttpServletResponse response = securityContext.getResponse();
              if (response != null) {

                try {
                  // return output stream of HTTP response for streaming
                  return response.getOutputStream();

                } catch (IOException ioex) {
                  logger.warn("", ioex);
                }
              }
            }
            return null;

          case "now":
            return DatePropertyParser.format(new Date(), DateProperty.DEFAULT_FORMAT);

          case "me":
            return securityContext.getUser(false);

          case "element":
            logger.warn(
                "The \"element\" keyword is deprecated! Please use \"this\" instead. Used in {}",
                entity.getProperty(GraphObject.id));

          case "this":
            return entity;

          case "locale":
            return locale != null ? locale.toString() : null;
        }
      }
    }

    if (value == null && defaultValue != null) {
      return Function.numberOrString(defaultValue);
    }

    return value;
  }
Esempio n. 20
0
  @Override
  public void render(SecurityContext securityContext, RenderContext renderContext, int depth)
      throws FrameworkException {

    if (isDeleted()
        || isHidden()
        || !displayForLocale(renderContext)
        || !displayForConditions(securityContext, renderContext)) {
      return;
    }

    String id = getUuid();
    EditMode edit = renderContext.getEditMode(securityContext.getUser(false));
    boolean inBody = renderContext.inBody();
    StringBuilder buffer = renderContext.getBuffer();

    String _contentType = getProperty(contentType);

    // fetch content with variable replacement
    String _content =
        getPropertyWithVariableReplacement(securityContext, renderContext, Content.content);

    if (!(EditMode.RAW.equals(edit))
        && (_contentType == null || ("text/plain".equals(_contentType)))) {

      _content = escapeForHtml(_content);
    }

    if (EditMode.CONTENT.equals(edit)
        && inBody
        && securityContext.isAllowed(this, Permission.write)) {

      if ("text/javascript".equals(_contentType)) {

        // Javascript will only be given some local vars
        // TODO: Is this neccessary?
        buffer
            .append("// data-structr-type='")
            .append(getType())
            .append("'\n// data-structr-id='")
            .append(id)
            .append("'\n");

      } else if ("text/css".equals(_contentType)) {

        // CSS will only be given some local vars
        // TODO: Is this neccessary?
        buffer
            .append("/* data-structr-type='")
            .append(getType())
            .append("'*/\n/* data-structr-id='")
            .append(id)
            .append("'*/\n");

      } else {

        //				// In edit mode, add an artificial 'span' tag around content nodes within body to make
        // them editable
        //				buffer.append("<span data-structr-raw-value=\"").append(getProperty(Content.content))
        //					//.append("\"
        // data-structr-content-type=\"").append(StringUtils.defaultString(getProperty(Content.contentType), ""))
        //					.append("\" data-structr-type=\"").append(getType())
        //					.append("\" data-structr-id=\"").append(id).append("\">");

        //				int l = buffer.length();
        //				buffer.replace(l-1, l, " data-structr-raw-value=\""
        //					.concat(getProperty(Content.content))
        //					.concat("\" data-structr-type=\"").concat(getType())
        //					.concat("\" data-structr-id=\"").concat(id).concat("\">"));

        buffer.append(
            "<!--data-structr-id=\""
                .concat(id)
                .concat("\" data-structr-raw-value=\"")
                .concat(getProperty(Content.content).replaceAll("\n", "\\\\n"))
                .concat("\"-->"));
        // .concat("\"
        // data-structr-raw-value=\"").concat(getProperty(Content.content)).concat("\"-->"));

      }
    }

    // No contentType-specific rendering in DATA edit mode
    // if (!edit.equals(EditMode.DATA)) {

    // examine content type and apply converter

    if (_contentType != null) {

      Adapter<String, String> converter = contentConverters.get(_contentType);

      if (converter != null) {

        try {

          // apply adapter
          _content = converter.adapt(_content);
        } catch (FrameworkException fex) {

          logger.log(Level.WARNING, "Unable to convert content: {0}", fex.getMessage());
        }
      }
    }

    // replace newlines with <br /> for rendering
    if (((_contentType == null) || _contentType.equals("text/plain"))
        && (_content != null)
        && !_content.isEmpty()) {

      _content = _content.replaceAll("[\\n]{1}", "<br>");
    }
    // }

    if (_content != null) {

      // buffer.append(indent(depth, true)).append(_content);

      // insert whitespace to make element clickable
      if (EditMode.CONTENT.equals(edit) && _content.length() == 0) {
        _content = "--- empty ---";
      }

      buffer.append(_content);
    }

    if (EditMode.CONTENT.equals(edit)
        && inBody
        && !("text/javascript".equals(getProperty(contentType)))
        && !("text/css".equals(getProperty(contentType)))) {

      //			buffer.append("</span>");
      buffer.append("<!---->");
    }
  }
  @Override
  public void processMessage(WebSocketMessage webSocketData) {

    final Map<String, Object> nodeData = webSocketData.getNodeData();
    final String parentId = (String) nodeData.get("parentId");
    final String childContent = (String) nodeData.get("childContent");
    final String pageId = webSocketData.getPageId();

    nodeData.remove("parentId");

    if (pageId != null) {

      // check for parent ID before creating any nodes
      if (parentId == null) {

        getWebSocket()
            .send(
                MessageBuilder.status()
                    .code(422)
                    .message("Cannot add node without parentId")
                    .build(),
                true);
        return;
      }

      // check if parent node with given ID exists
      final DOMNode parentNode = getDOMNode(parentId);
      if (parentNode == null) {

        getWebSocket()
            .send(MessageBuilder.status().code(404).message("Parent node not found").build(), true);
        return;
      }

      final Document document = getPage(pageId);
      if (document != null) {

        final String tagName = (String) nodeData.get("tagName");
        final App app = StructrApp.getInstance();

        nodeData.remove("tagName");

        try {
          app.beginTx();

          DOMNode newNode;

          if (tagName != null && !tagName.isEmpty()) {

            newNode = (DOMNode) document.createElement(tagName);

          } else {

            newNode = (DOMNode) document.createTextNode("#text");
          }

          // append new node to parent
          if (newNode != null) {

            parentNode.appendChild(newNode);

            for (Entry entry : nodeData.entrySet()) {

              String key = (String) entry.getKey();
              Object val = entry.getValue();

              PropertyKey propertyKey =
                  StructrApp.getConfiguration()
                      .getPropertyKeyForDatabaseName(newNode.getClass(), key);
              if (propertyKey != null) {

                try {
                  Object convertedValue = val;

                  PropertyConverter inputConverter =
                      propertyKey.inputConverter(SecurityContext.getSuperUserInstance());
                  if (inputConverter != null) {

                    convertedValue = inputConverter.convert(val);
                  }

                  // newNode.unlockReadOnlyPropertiesOnce();
                  newNode.setProperty(propertyKey, convertedValue);

                } catch (FrameworkException fex) {

                  logger.log(
                      Level.WARNING,
                      "Unable to set node property {0} of node {1} to {2}: {3}",
                      new Object[] {propertyKey, newNode.getUuid(), val, fex.getMessage()});
                }
              }
            }

            // create a child text node if content is given
            if (StringUtils.isNotBlank(childContent)) {

              DOMNode childNode = (DOMNode) document.createTextNode(childContent);

              if (newNode != null) {

                newNode.appendChild(childNode);
              }
            }
          }
          app.commitTx();

        } catch (DOMException dex) {

          // send DOM exception
          getWebSocket()
              .send(MessageBuilder.status().code(422).message(dex.getMessage()).build(), true);

        } catch (FrameworkException ex) {

          Logger.getLogger(CreateAndAppendDOMNodeCommand.class.getName())
              .log(Level.SEVERE, null, ex);

        } finally {

          app.finishTx();
        }

      } else {

        getWebSocket()
            .send(MessageBuilder.status().code(404).message("Page not found").build(), true);
      }

    } else {

      getWebSocket()
          .send(
              MessageBuilder.status()
                  .code(422)
                  .message("Cannot create node without pageId")
                  .build(),
              true);
    }
  }