use of org.structr.web.entity.dom.DOMNode 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);
}
use of org.structr.web.entity.dom.DOMNode in project structr by structr.
the class UiSyncCommand method doImport.
private void doImport(final String fileName) throws FrameworkException {
final App app = StructrApp.getInstance();
final DatabaseService graphDb = app.getDatabaseService();
SyncCommand.importFromFile(graphDb, securityContext, fileName, true);
// import done, now the ShadowDocument needs some special care. :(
try (final Tx tx = app.tx()) {
final List<ShadowDocument> shadowDocuments = app.nodeQuery(ShadowDocument.class).includeDeletedAndHidden().getAsList();
if (shadowDocuments.size() > 1) {
final PropertyKey<List<DOMNode>> elementsKey = StructrApp.key(Page.class, "elements");
final List<DOMNode> collectiveChildren = new LinkedList<>();
// sort by node id (higher node ID is newer entity)
Collections.sort(shadowDocuments, new Comparator<ShadowDocument>() {
@Override
public int compare(final ShadowDocument t1, final ShadowDocument t2) {
return Long.valueOf(t2.getId()).compareTo(t1.getId());
}
});
final ShadowDocument previousShadowDoc = shadowDocuments.get(0);
final ShadowDocument newShadowDoc = shadowDocuments.get(1);
// collect children of both shadow documents
collectiveChildren.addAll(previousShadowDoc.getProperty(elementsKey));
collectiveChildren.addAll(newShadowDoc.getProperty(elementsKey));
// delete old shadow document
app.delete(previousShadowDoc);
// add children to new shadow document
newShadowDoc.setProperties(securityContext, new PropertyMap(elementsKey, collectiveChildren));
}
tx.success();
}
}
use of org.structr.web.entity.dom.DOMNode in project structr by structr.
the class CreateOperation method apply.
// ----- interface InvertibleModificationOperation -----
@Override
public void apply(final App app, final Page sourcePage, final Page newPage) throws FrameworkException {
final InsertPosition insertPosition = findInsertPosition(sourcePage, parentHash, siblingHashes, newNode);
if (insertPosition != null) {
final DOMNode parent = insertPosition.getParent();
final DOMNode sibling = insertPosition.getSibling();
if (parent != null && !parent.isSynced()) {
if (sourcePage != null) {
sourcePage.adoptNode(newNode);
}
parent.insertBefore(newNode, sibling);
// make existing node known to other operations
hashMappedExistingNodes.put(newNode.getIdHashOrProperty(), newNode);
// remove children of new node so that existing nodes can be moved later
for (final RelationshipInterface childRel : newNode.getChildRelationships()) {
app.delete(childRel);
}
}
}
}
use of org.structr.web.entity.dom.DOMNode in project structr by structr.
the class InvertibleModificationOperation method findInsertPosition.
protected InsertPosition findInsertPosition(final Page sourcePage, final String parentHash, final List<String> siblingHashes, final DOMNode newNode) {
final DOMNode newParent = hashMappedExistingNodes.get(parentHash);
DOMNode newSibling = null;
// to use the one after
for (final String siblingHash : siblingHashes) {
newSibling = hashMappedExistingNodes.get(siblingHash);
if (newSibling != null) {
break;
}
}
if (newParent == null) {
// new parent did not exist in source document, what do?
// System.out.println("TODO: new parent not found in source document.");
}
return new InsertPosition(newParent, newSibling);
}
use of org.structr.web.entity.dom.DOMNode 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);
}
}
Aggregations