use of org.structr.web.entity.dom.DOMNode 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;
}
use of org.structr.web.entity.dom.DOMNode in project structr by structr.
the class DeployCommand method exportComponents.
private void exportComponents(final Path target, final Path configTarget) throws FrameworkException {
logger.info("Exporting components (unchanged components will be skipped)");
final Map<String, Object> configuration = new TreeMap<>();
final App app = StructrApp.getInstance();
try (final Tx tx = app.tx()) {
final ShadowDocument shadowDocument = app.nodeQuery(ShadowDocument.class).getFirst();
if (shadowDocument != null) {
for (final DOMNode node : shadowDocument.getElements()) {
final boolean hasParent = node.getParent() != null;
final boolean inTrash = node.inTrash();
boolean doExport = true;
// skip nodes in trash and non-toplevel nodes
if (inTrash || hasParent) {
continue;
}
final String content = node.getContent(RenderContext.EditMode.DEPLOYMENT);
if (content != null) {
String name = node.getProperty(AbstractNode.name);
if (name == null) {
name = node.getUuid();
}
final Map<String, Object> properties = new TreeMap<>();
final Path targetFile = target.resolve(name + ".html");
if (Files.exists(targetFile)) {
try {
final String existingContent = new String(Files.readAllBytes(targetFile), "utf-8");
doExport = !existingContent.equals(content);
} catch (IOException ignore) {
}
}
configuration.put(name, properties);
exportConfiguration(node, properties);
if (doExport) {
try (final OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(targetFile.toFile()))) {
writer.write(content);
writer.flush();
writer.close();
} catch (IOException ioex) {
logger.warn("", ioex);
}
}
}
}
}
tx.success();
}
try (final Writer fos = new OutputStreamWriter(new FileOutputStream(configTarget.toFile()))) {
getGson().toJson(configuration, fos);
} catch (IOException ioex) {
logger.warn("", ioex);
}
}
use of org.structr.web.entity.dom.DOMNode in project structr by structr.
the class ComponentImportVisitor method createComponent.
private void createComponent(final Path file, final String fileName) throws IOException, FrameworkException {
final String name = StringUtils.substringBeforeLast(fileName, ".html");
final DOMNode existingComponent = getExistingComponent(name);
final boolean byId = DeployCommand.isUuid(name);
try (final Tx tx = app.tx(true, false, false)) {
final PropertyMap properties = getPropertiesForComponent(name);
if (properties == null) {
logger.info("Ignoring {} (not in components.json)", fileName);
} else {
if (existingComponent != null) {
final PropertyKey<String> contentKey = StructrApp.key(Template.class, "content");
properties.put(contentKey, existingComponent.getProperty(contentKey));
existingComponent.setOwnerDocument(null);
if (existingComponent instanceof Template) {
properties.put(contentKey, existingComponent.getProperty(contentKey));
existingComponent.setOwnerDocument(null);
} else {
deleteComponent(app, name);
}
}
final String src = new String(Files.readAllBytes(file), Charset.forName("UTF-8"));
boolean visibleToPublic = get(properties, GraphObject.visibleToPublicUsers, false);
boolean visibleToAuth = get(properties, GraphObject.visibleToAuthenticatedUsers, false);
final Importer importer = new Importer(securityContext, src, null, name, visibleToPublic, visibleToAuth);
// enable literal import of href attributes
importer.setIsDeployment(true);
final boolean parseOk = importer.parse(false);
if (parseOk) {
logger.info("Importing component {} from {}..", new Object[] { name, fileName });
// set comment handler that can parse and apply special Structr comments in HTML source files
importer.setCommentHandler(new DeploymentCommentHandler());
// parse page
final ShadowDocument shadowDocument = CreateComponentCommand.getOrCreateHiddenDocument();
final DOMNode rootElement = importer.createComponentChildNodes(shadowDocument);
if (rootElement != null) {
if (byId) {
// set UUID
rootElement.unlockSystemPropertiesOnce();
rootElement.setProperty(GraphObject.id, name);
} else {
// set name
rootElement.setProperty(AbstractNode.name, name);
}
// store properties from components.json if present
rootElement.setProperties(securityContext, properties);
}
}
}
tx.success();
}
}
use of org.structr.web.entity.dom.DOMNode in project structr by structr.
the class ComponentImportVisitor method getExistingComponent.
// ----- private methods -----
private DOMNode getExistingComponent(final String name) {
final App app = StructrApp.getInstance();
DOMNode result = null;
try (final Tx tx = app.tx()) {
if (DeployCommand.isUuid(name)) {
result = (DOMNode) StructrApp.getInstance().nodeQuery(DOMNode.class).and(GraphObject.id, name).getFirst();
} else {
result = Importer.findSharedComponentByName(name);
}
tx.success();
} catch (FrameworkException fex) {
logger.warn("Unable to determine if component {} already exists, ignoring.", name);
}
return result;
}
use of org.structr.web.entity.dom.DOMNode in project structr by structr.
the class HtmlServlet method doHead.
@Override
protected void doHead(final HttpServletRequest request, final HttpServletResponse response) {
final Authenticator auth = getConfig().getAuthenticator();
SecurityContext securityContext;
List<Page> pages = null;
boolean requestUriContainsUuids = false;
final App app;
try {
assertInitialized();
String path = request.getPathInfo();
// 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");
response.setContentLength(0);
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;
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
// Look for a file
File 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
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)) {
// 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) {
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;
renderContext.pushSecurityContext(authResult.getSecurityContext());
} 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:
tx.success();
return;
// no Basic Auth for given path, go on
case NoBasicAuth:
break;
}
}
// 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) {
// no content
response.setContentLength(0);
response.getOutputStream().close();
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;
}
}
if (securityContext.isVisible(rootElement)) {
if (!EditMode.WIDGET.equals(edit) && !dontCache && notModifiedSince(request, response, rootElement, dontCache)) {
response.getOutputStream().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);
response.getOutputStream().close();
}
} else {
notFound(response, securityContext);
response.getOutputStream().close();
}
tx.success();
} catch (Throwable fex) {
logger.error("Exception while processing request", fex);
}
} catch (FrameworkException t) {
logger.error("Exception while processing request", t);
UiAuthenticator.writeInternalServerError(response);
}
}
Aggregations