/** * Copyright (C) 2010-2017 Structr GmbH * * This file is part of Structr <http://structr.org>. * * Structr is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * Structr is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Structr. If not, see <http://www.gnu.org/licenses/>. */ package org.structr.web.advanced; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; import java.util.HashMap; import java.util.Map; import java.util.function.Function; import org.junit.Assert; import static org.junit.Assert.fail; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.structr.common.AccessMode; import org.structr.common.Permission; import org.structr.common.PropertyView; import org.structr.common.SecurityContext; import org.structr.common.error.FrameworkException; import org.structr.core.GraphObject; import org.structr.core.app.App; import org.structr.core.app.StructrApp; import org.structr.core.entity.AbstractNode; import org.structr.core.entity.Localization; import org.structr.core.entity.MailTemplate; import org.structr.core.entity.Principal; import org.structr.core.entity.SchemaMethod; import org.structr.core.entity.SchemaNode; import org.structr.core.entity.Security; import org.structr.core.entity.relationship.PrincipalOwnsNode; import org.structr.core.graph.NodeAttribute; import org.structr.core.graph.NodeInterface; import org.structr.core.graph.Tx; import org.structr.core.property.PropertyKey; import org.structr.core.property.PropertyMap; import org.structr.core.property.StartNode; import org.structr.core.property.StringProperty; import org.structr.dynamic.File; import org.structr.schema.export.StructrSchema; import org.structr.schema.json.JsonSchema; import org.structr.schema.json.JsonType; import org.structr.web.StructrUiTest; import org.structr.web.common.FileHelper; import org.structr.web.entity.FileBase; import org.structr.web.entity.Folder; import org.structr.web.entity.User; import org.structr.web.entity.Widget; import org.structr.web.entity.dom.Content; import org.structr.web.entity.dom.DOMElement; import org.structr.web.entity.dom.DOMNode; import org.structr.web.entity.dom.Page; import org.structr.web.entity.dom.ShadowDocument; import org.structr.web.entity.dom.Template; import org.structr.web.entity.html.Body; import org.structr.web.entity.html.Div; import org.structr.web.entity.html.Head; import org.structr.web.entity.html.Html; import org.structr.web.entity.html.Li; import org.structr.web.entity.html.Link; import org.structr.web.entity.html.Option; import org.structr.web.entity.html.P; import org.structr.web.entity.html.Script; import org.structr.web.entity.html.Select; import org.structr.web.entity.html.Table; import org.structr.web.entity.html.Tbody; import org.structr.web.entity.html.Td; import org.structr.web.entity.html.Thead; import org.structr.web.entity.html.Tr; import org.structr.web.entity.html.Ul; import org.structr.web.importer.Importer; import org.structr.web.maintenance.DeployCommand; import org.structr.web.maintenance.deploy.DeploymentCommentHandler; import org.structr.websocket.command.CloneComponentCommand; import org.structr.websocket.command.CreateComponentCommand; import org.w3c.dom.Node; public class DeploymentTest extends StructrUiTest { private static final Logger logger = LoggerFactory.getLogger(DeploymentTest.class.getName()); @Test public void test01SimplePage() { // setup try (final Tx tx = app.tx()) { Page.createSimplePage(securityContext, "test01"); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true); } @Test public void test02Visibilities() { // setup try (final Tx tx = app.tx()) { final Page page = Page.createNewPage(securityContext, "test02"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test02"); final Body body = createElement(page, html, "body"); // create a div for admin only { final Div div1 = createElement(page, body, "div"); createElement(page, div1, "h1", "private - ${find('User')}"); div1.setProperties(div1.getSecurityContext(), new PropertyMap(DOMNode.showConditions, "me.isAdmin")); } // create a private div { final Div div1 = createElement(page, body, "div"); createElement(page, div1, "h1", "private - test abcdefghjiklmnopqrstuvwyzöäüßABCDEFGHIJKLMNOPQRSTUVWXYZÖÄÜ?\"'"); div1.setProperties(div1.getSecurityContext(), new PropertyMap(DOMNode.showConditions, "me.isAdmin")); } // create a protected div { final Div div1 = createElement(page, body, "div"); createElement(page, div1, "h1", "protected - $%&/()=?¼½¬{[]}"); final PropertyMap div1Properties = new PropertyMap(); div1Properties.put(DOMNode.visibleToPublicUsers, false); div1Properties.put(DOMNode.visibleToAuthenticatedUsers, true); div1.setProperties(div1.getSecurityContext(), div1Properties); } // create a public div { final Div div1 = createElement(page, body, "div"); createElement(page, div1, "h1", "public"); final PropertyMap div1Properties = new PropertyMap(); div1Properties.put(DOMNode.visibleToPublicUsers, true); div1Properties.put(DOMNode.visibleToAuthenticatedUsers, true); div1.setProperties(div1.getSecurityContext(), div1Properties); } // create a public only div { final Div div1 = createElement(page, body, "div"); createElement(page, div1, "h1", "public only"); final PropertyMap div1Properties = new PropertyMap(); div1Properties.put(DOMNode.visibleToPublicUsers, true); div1Properties.put(DOMNode.visibleToAuthenticatedUsers, false); div1.setProperties(div1.getSecurityContext(), div1Properties); } tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true); } @Test public void test03ContentTypes() { // setup try (final Tx tx = app.tx()) { final Page page = Page.createNewPage(securityContext, "test03"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test03"); final Body body = createElement(page, html, "body"); final Div div1 = createElement(page, body, "div"); final Script script = createElement(page, div1, "script"); final Content content = createContent(page, script, "$(function () {\n\n" + "$('a[data-toggle=\"tab\"]').on('click', function (e) {\n\n" + "var id = $(e.target).attr(\"href\").substr(1) // activated tab\n" + "window.location.hash = id;\n" + "});\n\n" + "});" ); // workaround for strange importer behaviour script.setProperties(script.getSecurityContext(), new PropertyMap(Script._type, "text/javascript")); content.setProperties(content.getSecurityContext(), new PropertyMap(Content.contentType, "text/javascript")); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true); } @Test public void test04ContentTypes() { // setup try (final Tx tx = app.tx()) { final Page page = Page.createNewPage(securityContext, "test04"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test04"); createElement(page, head, "link"); createElement(page, head, "link"); createComment(page, head, "commentöäüÖÄÜß+#"); final Link link3 = createElement(page, head, "link"); final PropertyMap link3Properties = new PropertyMap(); link3Properties.put(Link._href, "/"); link3Properties.put(Link._media, "screen"); link3Properties.put(Link._type, "stylesheet"); link3.setProperties(link3.getSecurityContext(), link3Properties); final Body body = createElement(page, html, "body"); final Div div1 = createElement(page, body, "div"); createElement(page, div1, "h1", "private"); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true); } @Test public void test05SimpleTemplateInPage() { // setup try (final Tx tx = app.tx()) { final Page page = Page.createNewPage(securityContext, "test05"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test05"); final Body body = createElement(page, html, "body"); final Div div1 = createElement(page, body, "div"); final Template template = createTemplate(page, div1, "template source - öäüÖÄÜß'\"'`"); final PropertyMap templateProperties = new PropertyMap(); templateProperties.put(Template.functionQuery, "find('User')"); templateProperties.put(Template.dataKey, "user"); template.setProperties(template.getSecurityContext(), templateProperties); // append children to template object createElement(page, template, "div"); createElement(page, template, "div"); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true); } @Test public void test06SimpleTemplateInSharedComponents() { // setup try (final Tx tx = app.tx()) { final Page page = Page.createNewPage(securityContext, "test06"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test06"); final Body body = createElement(page, html, "body"); createElement(page, body, "div"); final ShadowDocument shadowDocument = CreateComponentCommand.getOrCreateHiddenDocument(); createTemplate(shadowDocument, null, "template source - öäüÖÄÜß'\"'`"); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true); } @Test public void test07SimpleSharedTemplate() { // setup try (final Tx tx = app.tx()) { final Page page = Page.createNewPage(securityContext, "test07"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test07"); final Body body = createElement(page, html, "body"); final Div div1 = createElement(page, body, "div"); final Template template = createTemplate(page, div1, "template source - öäüÖÄÜß'\"'`"); createComponent(template); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true); } @Test public void test08SharedTemplateInTwoPages() { // setup try (final Tx tx = app.tx()) { // create first page final Page page1 = Page.createNewPage(securityContext, "test08_1"); final Html html1 = createElement(page1, page1, "html"); final Head head1 = createElement(page1, html1, "head"); createElement(page1, head1, "title", "test08_1"); final Body body1 = createElement(page1, html1, "body"); final Div div1 = createElement(page1, body1, "div"); final Template template1 = createTemplate(page1, div1, "template source - öäüÖÄÜß'\"'`"); final Template component = createComponent(template1); // create second page final Page page2 = Page.createNewPage(securityContext, "test08_2"); final Html html2 = createElement(page2, page2, "html"); final Head head2 = createElement(page2, html2, "head"); createElement(page2, head2, "title", "test08_2"); final Body body2 = createElement(page2, html2, "body"); final Div div2 = createElement(page2, body2, "div"); // re-use template from above cloneComponent(component, div2); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true); } @Test public void test09SharedTemplatesWithChildren() { // setup try (final Tx tx = app.tx()) { // create first page final Page page1 = Page.createNewPage(securityContext, "test09_1"); final Html html1 = createElement(page1, page1, "html"); final Head head1 = createElement(page1, html1, "head"); createElement(page1, head1, "title", "test09_1"); final Body body1 = createElement(page1, html1, "body"); final Div div1 = createElement(page1, body1, "div"); final Template template1 = createTemplate(page1, div1, "template source - öäüÖÄÜß'\"'`"); createElement(page1, template1, "div", "test1"); createElement(page1, template1, "div", "test1"); final Template component = createComponent(template1); // create second page final Page page2 = Page.createNewPage(securityContext, "test09_2"); final Html html2 = createElement(page2, page2, "html"); final Head head2 = createElement(page2, html2, "head"); createElement(page2, head2, "title", "test09_2"); final Body body2 = createElement(page2, html2, "body"); final Div div2 = createElement(page2, body2, "div"); // re-use template from above cloneComponent(component, div2); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true); } @Test public void test10SharedComponent() { // setup try (final Tx tx = app.tx()) { // create first page final Page page1 = Page.createNewPage(securityContext, "test10_1"); final Html html1 = createElement(page1, page1, "html"); final Head head1 = createElement(page1, html1, "head"); createElement(page1, head1, "title", "test10_1"); final Body body1 = createElement(page1, html1, "body"); final Div div1 = createElement(page1, body1, "div"); createElement(page1, div1, "div", "test1"); createElement(page1, div1, "div", "test1"); final Div component = createComponent(div1); // create second page final Page page2 = Page.createNewPage(securityContext, "test10_2"); final Html html2 = createElement(page2, page2, "html"); final Head head2 = createElement(page2, html2, "head"); createElement(page2, head2, "title", "test10_2"); final Body body2 = createElement(page2, html2, "body"); final Div div2 = createElement(page2, body2, "div"); // re-use template from above cloneComponent(component, div2); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true); } @Test public void test11TemplateInTbody() { // setup try (final Tx tx = app.tx()) { // create first page final Page page1 = Page.createNewPage(securityContext, "test11"); final Html html1 = createElement(page1, page1, "html"); final Head head1 = createElement(page1, html1, "head"); createElement(page1, head1, "title", "test11_1"); final Body body1 = createElement(page1, html1, "body"); final Table table = createElement(page1, body1, "table"); final Tbody tbody = createElement(page1, table, "tbody"); final Template template1 = createTemplate(page1, tbody, "<tr><td>${user.name}</td></tr>"); final PropertyMap template1Properties = new PropertyMap(); template1Properties.put(DOMNode.functionQuery, "find('User')"); template1Properties.put(DOMNode.dataKey, "user"); template1.setProperties(template1.getSecurityContext(), template1Properties); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true); } @Test public void test12EmptyContentElementWithContentType() { // setup try (final Tx tx = app.tx()) { // create first page final Page page = Page.createNewPage(securityContext, "test12"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test12"); final Body body = createElement(page, html, "body"); final Script script1 = createElement(page, body, "script"); final Script script2 = createElement(page, body, "script"); script1.setProperty(Script._type, "text/javascript"); createContent(page, script1, ""); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true); } @Test public void test13EmptyContentElement() { // setup try (final Tx tx = app.tx()) { // create first page final Page page = Page.createNewPage(securityContext, "test13"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test13"); final Body body = createElement(page, html, "body"); final Script script1 = createElement(page, body, "script", ""); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), false); } @Test public void test14FileAttributesInFolders() { final String folderPath = "/deeply/nested/Folder Structure/with spaces"; final String fileName = "test14.txt"; // setup try (final Tx tx = app.tx()) { final Folder folder = FileHelper.createFolderPath(securityContext, folderPath); final FileBase file = FileHelper.createFile(securityContext, "test".getBytes("utf-8"), "text/plain", File.class, fileName); final Folder rootFolder = getRootFolder(folder); Assert.assertNotNull("Root folder should not be null", rootFolder); // root folder needs to have "includeInFrontendExport" set rootFolder.setProperty(Folder.includeInFrontendExport, true); file.setProperty(File.parent, folder); file.setProperty(File.visibleToPublicUsers, true); file.setProperty(File.visibleToAuthenticatedUsers, true); file.setProperty(File.enableBasicAuth, true); file.setProperty(File.useAsJavascriptLibrary, true); tx.success(); } catch (IOException | FrameworkException fex) { logger.warn("", fex); fail("Unexpected exception."); } // test doImportExportRoundtrip(true); // check try (final Tx tx = app.tx()) { final Folder folder = app.nodeQuery(Folder.class).andName("with spaces").getFirst(); Assert.assertNotNull("Invalid deployment result", folder); final FileBase file = app.nodeQuery(File.class).and(File.parent, folder).and(File.name, fileName).getFirst(); Assert.assertNotNull("Invalid deployment result", file); Assert.assertEquals("Deployment import does not restore attributes correctly", folder, file.getProperty(File.parent)); Assert.assertTrue("Deployment import does not restore attributes correctly", file.getProperty(File.visibleToPublicUsers)); Assert.assertTrue("Deployment import does not restore attributes correctly", file.getProperty(File.visibleToAuthenticatedUsers)); Assert.assertTrue("Deployment import does not restore attributes correctly", file.getProperty(File.enableBasicAuth)); Assert.assertTrue("Deployment import does not restore attributes correctly", file.getProperty(File.useAsJavascriptLibrary)); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } } @Test public void test15FileAttributesOnUpdate() { final String folderPath = "/deeply/nested/Folder Structure/with spaces"; final String fileName = "test15.txt"; // setup try (final Tx tx = app.tx()) { final Folder folder = FileHelper.createFolderPath(securityContext, folderPath); final FileBase file = FileHelper.createFile(securityContext, "test".getBytes("utf-8"), "text/plain", File.class, fileName); final Folder rootFolder = getRootFolder(folder); Assert.assertNotNull("Root folder should not be null", rootFolder); // root folder needs to have "includeInFrontendExport" set rootFolder.setProperty(Folder.includeInFrontendExport, true); file.setProperty(File.parent, folder); file.setProperty(File.visibleToPublicUsers, true); file.setProperty(File.visibleToAuthenticatedUsers, true); file.setProperty(File.enableBasicAuth, true); file.setProperty(File.useAsJavascriptLibrary, true); file.setProperty(File.includeInFrontendExport, true); tx.success(); } catch (IOException | FrameworkException fex) { logger.warn("", fex); fail("Unexpected exception."); } // test, don't clean the database but modify the file flags doImportExportRoundtrip(true, false, new Function() { @Override public Object apply(Object t) { try (final Tx tx = app.tx()) { final FileBase file = app.nodeQuery(File.class).and(File.name, fileName).getFirst(); file.setProperty(File.visibleToPublicUsers, false); file.setProperty(File.visibleToAuthenticatedUsers, false); file.setProperty(File.enableBasicAuth, false); file.setProperty(File.useAsJavascriptLibrary, false); file.setProperty(File.includeInFrontendExport, false); tx.success(); } catch (FrameworkException fex) {} return null; } }); // check try (final Tx tx = app.tx()) { final Folder folder = app.nodeQuery(Folder.class).andName("with spaces").getFirst(); Assert.assertNotNull("Invalid deployment result", folder); final FileBase file = app.nodeQuery(File.class).and(File.parent, folder).and(File.name, fileName).getFirst(); Assert.assertNotNull("Invalid deployment result", file); Assert.assertEquals("Deployment import of existing file does not restore attributes correctly", folder, file.getProperty(File.parent)); Assert.assertTrue("Deployment import of existing file does not restore attributes correctly", file.getProperty(File.visibleToPublicUsers)); Assert.assertTrue("Deployment import of existing file does not restore attributes correctly", file.getProperty(File.visibleToAuthenticatedUsers)); Assert.assertTrue("Deployment import of existing file does not restore attributes correctly", file.getProperty(File.enableBasicAuth)); Assert.assertTrue("Deployment import of existing file does not restore attributes correctly", file.getProperty(File.useAsJavascriptLibrary)); Assert.assertTrue("Deployment import of existing file does not restore attributes correctly", file.getProperty(File.includeInFrontendExport)); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } } @Test public void test16SharedTemplateWithChildren() { // setup try (final Tx tx = app.tx()) { final Page page = Page.createNewPage(securityContext, "test16"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test16"); final Body body = createElement(page, html, "body"); final Div div1 = createElement(page, body, "div"); final Template template = createTemplate(page, div1, "template source - öäüÖÄÜß'\"'`"); createElement(page, template, "div"); final DOMNode table = createElement(page, template, "table"); final DOMNode tr = createElement(page, table, "tr"); createElement(page, tr, "td"); createElement(page, tr, "td"); createComponent(template); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true, false); } @Test public void test17NamedNonSharedTemplateWithChildren() { // setup try (final Tx tx = app.tx()) { final Page page = Page.createNewPage(securityContext, "test17"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test17"); final Body body = createElement(page, html, "body"); final Template template = createTemplate(page, body, "${render(children)}"); template.setProperty(AbstractNode.name, "a-template"); final Template sharedTemplate = createComponent(template); // remove original template from page app.delete(template); createElement(page, sharedTemplate, "div"); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true, false); } @Test public void test18NonNamedNonSharedTemplateWithChildren() { // setup try (final Tx tx = app.tx()) { final Page page = Page.createNewPage(securityContext, "test18"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test18"); final Body body = createElement(page, html, "body"); final Template template = createTemplate(page, body, "${render(children)}"); final Template sharedTemplate = createComponent(template); // remove original template from page app.delete(template); createElement(page, sharedTemplate, "div"); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true, false); } @Test public void test19HtmlEntities() { // setup try (final Tx tx = app.tx()) { final Page page = Page.createNewPage(securityContext, "test19"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test19"); final Body body = createElement(page, html, "body"); final Div div1 = createElement(page, body, "div"); final Content content = createContent(page, div1, "<b>Test</b>"); content.setProperty(Content.contentType, "text/html"); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true, false); } @Test public void test20ExportOwnership() { Principal user1 = null; Principal user2 = null; try (final Tx tx = app.tx()) { user1 = createTestNode(User.class, new NodeAttribute<>(AbstractNode.name, "user1")); user2 = createTestNode(User.class, new NodeAttribute<>(AbstractNode.name, "user2")); tx.success(); } catch (FrameworkException ex) { fail("Unexpected exception."); } Assert.assertNotNull("User was not created, test cannot continue", user1); Assert.assertNotNull("User was not created, test cannot continue", user2); // setup final SecurityContext context1 = SecurityContext.getInstance(user1, AccessMode.Backend); final App app1 = StructrApp.getInstance(context1); try (final Tx tx = app1.tx()) { final Page page = Page.createNewPage(context1, "test20"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test20"); final Body body = createElement(page, html, "body"); final Div div1 = createElement(page, body, "div"); final Content content = createContent(page, div1, "<b>Test</b>"); content.setProperty(Content.contentType, "text/html"); // set owner to different user div1.setProperty(AbstractNode.owner, user2); content.setProperty(AbstractNode.owner, user2); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true, false); } @Test public void test21ExportGrants() { Principal user1 = null; Principal user2 = null; try (final Tx tx = app.tx()) { user1 = createTestNode(User.class, new NodeAttribute<>(AbstractNode.name, "user1")); user2 = createTestNode(User.class, new NodeAttribute<>(AbstractNode.name, "user2")); tx.success(); } catch (FrameworkException ex) { fail("Unexpected exception."); } Assert.assertNotNull("User was not created, test cannot continue", user1); Assert.assertNotNull("User was not created, test cannot continue", user2); // setup final SecurityContext context1 = SecurityContext.getInstance(user1, AccessMode.Backend); final App app1 = StructrApp.getInstance(context1); try (final Tx tx = app1.tx()) { final Page page = Page.createNewPage(context1, "test21"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test21"); final Body body = createElement(page, html, "body"); final Div div1 = createElement(page, body, "div"); final Content content = createContent(page, div1, "<b>Test</b>"); content.setProperty(Content.contentType, "text/html"); // create grants page.grant(Permission.read, user2); div1.grant(Permission.read, user2); content.grant(Permission.read, user2); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true, false); } @Test public void test22TemplateOwnershipAndGrants() { Principal user1 = null; Principal user2 = null; try (final Tx tx = app.tx()) { user1 = createTestNode(User.class, new NodeAttribute<>(AbstractNode.name, "user1")); user2 = createTestNode(User.class, new NodeAttribute<>(AbstractNode.name, "user2")); tx.success(); } catch (FrameworkException ex) { fail("Unexpected exception."); } Assert.assertNotNull("User was not created, test cannot continue", user1); Assert.assertNotNull("User was not created, test cannot continue", user2); // setup try (final Tx tx = app.tx()) { // create first page final Page page1 = Page.createNewPage(securityContext, "test22_1"); final Html html1 = createElement(page1, page1, "html"); final Head head1 = createElement(page1, html1, "head"); createElement(page1, head1, "title", "test22_1"); final Body body1 = createElement(page1, html1, "body"); final Div div1 = createElement(page1, body1, "div"); createElement(page1, div1, "div", "test1"); createElement(page1, div1, "div", "test1"); final Div component = createComponent(div1); // create second page final Page page2 = Page.createNewPage(securityContext, "test22_2"); final Html html2 = createElement(page2, page2, "html"); final Head head2 = createElement(page2, html2, "head"); createElement(page2, head2, "title", "test22_2"); final Body body2 = createElement(page2, html2, "body"); final Div div2 = createElement(page2, body2, "div"); // re-use template from above final Div cloned = cloneComponent(component, div2); component.grant(Permission.read, user1); cloned.grant(Permission.read, user2); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test doImportExportRoundtrip(true, true, new Function() { @Override public Object apply(Object t) { try (final Tx tx = app.tx()) { createTestNode(User.class, new NodeAttribute<>(AbstractNode.name, "user1")); createTestNode(User.class, new NodeAttribute<>(AbstractNode.name, "user2")); tx.success(); } catch (FrameworkException ex) { fail("Unexpected exception."); } return null; } }); } @Test public void test23FileOwnershipAndGrants() { Principal user1 = null; Principal user2 = null; try (final Tx tx = app.tx()) { user1 = createTestNode(User.class, new NodeAttribute<>(AbstractNode.name, "user1")); user2 = createTestNode(User.class, new NodeAttribute<>(AbstractNode.name, "user2")); tx.success(); } catch (FrameworkException ex) { fail("Unexpected exception."); } Assert.assertNotNull("User was not created, test cannot continue", user1); Assert.assertNotNull("User was not created, test cannot continue", user2); // setup try (final Tx tx = app.tx()) { // create some files and folders final Folder folder1 = app.create(Folder.class, new NodeAttribute<>(Folder.name, "Folder1"), new NodeAttribute<>(Folder.includeInFrontendExport, true)); final Folder folder2 = app.create(Folder.class, new NodeAttribute<>(Folder.name, "Folder2"), new NodeAttribute<>(Folder.parent, folder1)); final FileBase file1 = FileHelper.createFile(securityContext, "test".getBytes(), "text/plain", File.class, "test1.txt"); final FileBase file2 = FileHelper.createFile(securityContext, "test".getBytes(), "text/plain", File.class, "test2.txt"); file1.setProperty(FileBase.parent, folder2); file2.setProperty(FileBase.parent, folder2); folder1.setProperty(Folder.owner, user1); folder1.grant(Permission.read, user2); folder2.setProperty(Folder.owner, user2); folder2.grant(Permission.write, user1); file1.setProperty(File.owner, user1); file2.setProperty(File.owner, user2); file1.setProperty(Folder.owner, user1); file1.grant(Permission.read, user2); file2.setProperty(Folder.owner, user2); file2.grant(Permission.write, user1); tx.success(); } catch (IOException | FrameworkException fex) { fail("Unexpected exception."); } // test doImportExportRoundtrip(true, true, new Function() { @Override public Object apply(Object t) { try (final Tx tx = app.tx()) { createTestNode(User.class, new NodeAttribute<>(AbstractNode.name, "user1")); createTestNode(User.class, new NodeAttribute<>(AbstractNode.name, "user2")); tx.success(); } catch (FrameworkException ex) { fail("Unexpected exception."); } return null; } }); } @Test public void test24ContentShowConditions() { // setup try (final Tx tx = app.tx()) { final Page page = Page.createNewPage(securityContext, "test24"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test24"); final Body body = createElement(page, html, "body"); final Div div1 = createElement(page, body, "div"); final Content content1 = createContent(page, div1, "${current.type}"); final Content content2 = createContent(page, div1, "${find('User', 'name', '@structr')[0].id}"); final Content content3 = createContent(page, div1, "${find('User', 'name', '@structr')[0].id}"); content1.setProperty(DOMNode.showConditions, "eq(current.type, 'MyTestFolder')"); content2.setProperty(DOMNode.showConditions, "if(equal(extract(first(find('User', 'name' 'structr')), 'name'), '@structr'), true, false)"); content3.setProperty(DOMNode.showConditions, "(((((([]))))))"); // for testing only tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), true); } @Test public void test25ExtendedBuiltinTypes() { /* This method tests whether files, folders and images that are * considered part of application data (derived from built-in * types) are ignored in the deployment process. */ // setup try (final Tx tx = app.tx()) { // create extended folder class app.create(SchemaNode.class, new NodeAttribute<>(SchemaNode.name, "ExtendedFolder"), new NodeAttribute<>(SchemaNode.extendsClass, "org.structr.dynamic.Folder") ); // create extended file class app.create(SchemaNode.class, new NodeAttribute<>(SchemaNode.name, "ExtendedFile"), new NodeAttribute<>(SchemaNode.extendsClass, "org.structr.dynamic.File") ); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // setup try (final Tx tx = app.tx()) { final NodeInterface folder1 = app.create(StructrApp.getConfiguration().getNodeEntityClass("ExtendedFolder"), "folder1"); final NodeInterface folder2 = app.create(StructrApp.getConfiguration().getNodeEntityClass("ExtendedFolder"), new NodeAttribute<>(Folder.name, "folder2"), new NodeAttribute(Folder.parent, folder1) ); app.create(StructrApp.getConfiguration().getNodeEntityClass("ExtendedFile"), new NodeAttribute<>(File.name, "file1.txt"), new NodeAttribute(File.parent, folder1) ); app.create(StructrApp.getConfiguration().getNodeEntityClass("ExtendedFile"), new NodeAttribute<>(File.name, "file2.txt"), new NodeAttribute(File.parent, folder2) ); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } // test compare(calculateHash(), false, true); } @Test public void test26Escaping() { // setup try (final Tx tx = app.tx()) { final Page page = Page.createNewPage(securityContext, "test25"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test25"); final Body body = createElement(page, html, "body"); final Div div1 = createElement(page, body, "div"); final Content content1 = createContent(page, div1, "<div><script>var test = '<h3>Title</h3>';</script></div>"); content1.setProperty(Content.contentType, "text/html"); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } compare(calculateHash(), true); } @Test public void test27FileAttributes() { final String fileName1 = "test27_1.txt"; final String fileName2 = "test27_2.txt"; // setup try (final Tx tx = app.tx()) { final FileBase file1 = FileHelper.createFile(securityContext, "test".getBytes("utf-8"), "text/plain", File.class, fileName1); final FileBase file2 = FileHelper.createFile(securityContext, "test".getBytes("utf-8"), "text/plain", File.class, fileName2); file1.setProperty(File.visibleToPublicUsers, true); file1.setProperty(File.visibleToAuthenticatedUsers, true); file1.setProperty(File.enableBasicAuth, true); file1.setProperty(File.useAsJavascriptLibrary, true); file2.setProperty(File.visibleToPublicUsers, true); file2.setProperty(File.visibleToAuthenticatedUsers, true); file2.setProperty(File.enableBasicAuth, true); file2.setProperty(File.includeInFrontendExport, true); tx.success(); } catch (IOException | FrameworkException fex) { logger.warn("", fex); fail("Unexpected exception."); } // test doImportExportRoundtrip(true); // check try (final Tx tx = app.tx()) { final FileBase file1 = app.nodeQuery(File.class).and(File.name, fileName1).getFirst(); final FileBase file2 = app.nodeQuery(File.class).and(File.name, fileName2).getFirst(); Assert.assertNotNull("Invalid deployment result", file1); Assert.assertNotNull("Invalid deployment result", file2); Assert.assertTrue("Deployment import does not restore attributes correctly", file1.getProperty(File.visibleToPublicUsers)); Assert.assertTrue("Deployment import does not restore attributes correctly", file1.getProperty(File.visibleToAuthenticatedUsers)); Assert.assertTrue("Deployment import does not restore attributes correctly", file1.getProperty(File.enableBasicAuth)); Assert.assertTrue("Deployment import does not restore attributes correctly", file1.getProperty(File.useAsJavascriptLibrary)); Assert.assertFalse("Deployment import does not restore attributes correctly", file1.getProperty(File.includeInFrontendExport)); Assert.assertTrue("Deployment import does not restore attributes correctly", file2.getProperty(File.visibleToPublicUsers)); Assert.assertTrue("Deployment import does not restore attributes correctly", file2.getProperty(File.visibleToAuthenticatedUsers)); Assert.assertTrue("Deployment import does not restore attributes correctly", file2.getProperty(File.enableBasicAuth)); Assert.assertFalse("Deployment import does not restore attributes correctly", file2.getProperty(File.useAsJavascriptLibrary)); Assert.assertTrue("Deployment import does not restore attributes correctly" , file2.getProperty(File.includeInFrontendExport)); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } } @Test public void test28MailTemplates() { // setup try (final Tx tx = app.tx()) { app.create(MailTemplate.class, new NodeAttribute<>(MailTemplate.name, "template1"), new NodeAttribute<>(MailTemplate.locale, "de_DE"), new NodeAttribute<>(MailTemplate.text, "text1"), new NodeAttribute<>(MailTemplate.visibleToPublicUsers, true) ); app.create(MailTemplate.class, new NodeAttribute<>(MailTemplate.name, "template2"), new NodeAttribute<>(MailTemplate.locale, "en"), new NodeAttribute<>(MailTemplate.text, "text2"), new NodeAttribute<>(MailTemplate.visibleToAuthenticatedUsers, true) ); tx.success(); } catch (FrameworkException fex) { logger.warn("", fex); fail("Unexpected exception."); } // test doImportExportRoundtrip(true); // check try (final Tx tx = app.tx()) { final MailTemplate template1 = app.nodeQuery(MailTemplate.class).and(MailTemplate.name, "template1").getFirst(); final MailTemplate template2 = app.nodeQuery(MailTemplate.class).and(MailTemplate.name, "template2").getFirst(); Assert.assertNotNull("Invalid deployment result", template1); Assert.assertNotNull("Invalid deployment result", template2); Assert.assertEquals("Invalid MailTemplate deployment result", "template1", template1.getProperty(MailTemplate.name)); Assert.assertEquals("Invalid MailTemplate deployment result", "de_DE", template1.getProperty(MailTemplate.locale)); Assert.assertEquals("Invalid MailTemplate deployment result", "text1", template1.getProperty(MailTemplate.text)); Assert.assertEquals("Invalid MailTemplate deployment result", true, template1.getProperty(MailTemplate.visibleToPublicUsers)); Assert.assertEquals("Invalid MailTemplate deployment result", false, template1.getProperty(MailTemplate.visibleToAuthenticatedUsers)); Assert.assertEquals("Invalid MailTemplate deployment result", "template2", template2.getProperty(MailTemplate.name)); Assert.assertEquals("Invalid MailTemplate deployment result", "en", template2.getProperty(MailTemplate.locale)); Assert.assertEquals("Invalid MailTemplate deployment result", "text2", template2.getProperty(MailTemplate.text)); Assert.assertEquals("Invalid MailTemplate deployment result", false, template2.getProperty(MailTemplate.visibleToPublicUsers)); Assert.assertEquals("Invalid MailTemplate deployment result", true, template2.getProperty(MailTemplate.visibleToAuthenticatedUsers)); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } } @Test public void test29Localizations() { // setup try (final Tx tx = app.tx()) { app.create(Localization.class, new NodeAttribute<>(Localization.name, "localization1"), new NodeAttribute<>(Localization.domain, "domain1"), new NodeAttribute<>(Localization.locale, "de_DE"), new NodeAttribute<>(Localization.localizedName, "localizedName1") ); app.create(Localization.class, new NodeAttribute<>(Localization.name, "localization2"), new NodeAttribute<>(Localization.domain, "domain2"), new NodeAttribute<>(Localization.locale, "en"), new NodeAttribute<>(Localization.localizedName, "localizedName2") ); tx.success(); } catch (FrameworkException fex) { logger.warn("", fex); fail("Unexpected exception."); } // test doImportExportRoundtrip(true); // check try (final Tx tx = app.tx()) { final Localization localization1 = app.nodeQuery(Localization.class).and(Localization.name, "localization1").getFirst(); final Localization localization2 = app.nodeQuery(Localization.class).and(Localization.name, "localization2").getFirst(); Assert.assertNotNull("Invalid deployment result", localization1); Assert.assertNotNull("Invalid deployment result", localization2); Assert.assertEquals("Invalid Localization deployment result", "localization1", localization1.getProperty(Localization.name)); Assert.assertEquals("Invalid Localization deployment result", "domain1", localization1.getProperty(Localization.domain)); Assert.assertEquals("Invalid Localization deployment result", "de_DE", localization1.getProperty(Localization.locale)); Assert.assertEquals("Invalid Localization deployment result", "localizedName1", localization1.getProperty(Localization.localizedName)); Assert.assertEquals("Invalid Localization deployment result", true, localization1.getProperty(Localization.visibleToPublicUsers)); Assert.assertEquals("Invalid Localization deployment result", true, localization1.getProperty(Localization.visibleToAuthenticatedUsers)); Assert.assertEquals("Invalid Localization deployment result", "localization2", localization2.getProperty(Localization.name)); Assert.assertEquals("Invalid Localization deployment result", "domain2", localization2.getProperty(Localization.domain)); Assert.assertEquals("Invalid Localization deployment result", "en", localization2.getProperty(Localization.locale)); Assert.assertEquals("Invalid Localization deployment result", "localizedName2", localization2.getProperty(Localization.localizedName)); Assert.assertEquals("Invalid Localization deployment result", true, localization2.getProperty(Localization.visibleToPublicUsers)); Assert.assertEquals("Invalid Localization deployment result", true, localization2.getProperty(Localization.visibleToAuthenticatedUsers)); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } } @Test public void test30IncreasingIndentationCountInRoundtrip() { // setup try (final Tx tx = app.tx()) { final Page page = Page.createNewPage(securityContext, "test30"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test30"); final Body body = createElement(page, html, "body"); final Div div1 = createElement(page, body, "div"); final Div div2 = createElement(page, div1, "div", "This is a test."); final Table table1 = createElement(page, div2, "table"); final Thead thead = createElement(page, table1, "thead"); final Tbody tbody = createElement(page, table1, "tbody"); final Tr tr1 = createElement(page, thead, "tr"); final Tr tr2 = createElement(page, tbody, "tr"); final Td td11 = createElement(page, tr1, "td", "content11", "Content before <select>"); final Td td12 = createElement(page, tr1, "td", "content12"); final Td td21 = createElement(page, tr2, "td", "content21"); final Td td22 = createElement(page, tr2, "td", "content22"); final Select select = createElement(page, td11, "select"); final Option option1 = createElement(page, select, "option", "value1"); final Option option2 = createElement(page, select, "option", "value2"); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } compare(calculateHash(), true); } @Test public void test31RoundtripWithEmptyContentElements() { // setup try (final Tx tx = app.tx()) { final Page page = Page.createNewPage(securityContext, "test31"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test31"); final Body body = createElement(page, html, "body"); final Div div1 = createElement(page, body, "div"); final Div div2 = createElement(page, div1, "div", ""); final Table table1 = createElement(page, div2, "table"); final Thead thead = createElement(page, table1, "thead"); final Tbody tbody = createElement(page, table1, "tbody"); final Tr tr1 = createElement(page, thead, "tr"); final Tr tr2 = createElement(page, tbody, "tr"); final Td td11 = createElement(page, tr1, "td"); final Content c1 = createContent(page, td11, ""); final Td td12 = createElement(page, tr1, "td", "content12"); final P p1 = createElement(page, td12, "p", ""); final Ul ul = createElement(page, p1, "ul"); final Li li = createElement(page, ul, "li", ""); final Td td21 = createElement(page, tr2, "td", "content21"); final Td td22 = createElement(page, tr2, "td", "content22"); final Select select = createElement(page, td11, "select"); final Option option1 = createElement(page, select, "option", ""); final Option option2 = createElement(page, select, "option", "value2"); final Content c2 = createContent(page, div2, ""); final Table table2 = createElement(page, div2, "table"); // include visibility flags page.setProperty(AbstractNode.visibleToAuthenticatedUsers, true); c1.setProperty(AbstractNode.visibleToAuthenticatedUsers, true); c2.setProperty(AbstractNode.visibleToAuthenticatedUsers, true); // modify visibility to produce two consecutive deployment instruction comments td12.setProperty(AbstractNode.visibleToPublicUsers, true); table2.setProperty(AbstractNode.visibleToPublicUsers, true); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } compare(calculateHash(), true); } @Test public void test32RoundtripWithEmptyContentElements() { // setup try (final Tx tx = app.tx()) { final Page page = Page.createNewPage(securityContext, "test32"); final Html html = createElement(page, page, "html"); final Head head = createElement(page, html, "head"); createElement(page, head, "title", "test32"); final Body body = createElement(page, html, "body"); final Div div1 = createElement(page, body, "div"); final Div div2 = createElement(page, div1, "div", " "); final Ul ul = createElement(page, div1, "ul"); final Li li = createElement(page, ul, "li", " "); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } compare(calculateHash(), true); } @Test public void test33SchemaMethods() { // setup try (final Tx tx = app.tx()) { app.create(SchemaMethod.class, new NodeAttribute<>(SchemaMethod.name, "method1"), new NodeAttribute<>(SchemaMethod.comment, "comment1"), new NodeAttribute<>(SchemaMethod.source, "source1"), new NodeAttribute<>(SchemaMethod.virtualFileName, "virtualFileName1"), new NodeAttribute<>(SchemaMethod.visibleToPublicUsers, true), new NodeAttribute<>(SchemaMethod.visibleToAuthenticatedUsers, false) ); app.create(SchemaMethod.class, new NodeAttribute<>(SchemaMethod.name, "method2"), new NodeAttribute<>(SchemaMethod.comment, "comment2"), new NodeAttribute<>(SchemaMethod.source, "source2"), new NodeAttribute<>(SchemaMethod.virtualFileName, "virtualFileName2"), new NodeAttribute<>(SchemaMethod.visibleToPublicUsers, false), new NodeAttribute<>(SchemaMethod.visibleToAuthenticatedUsers, true) ); tx.success(); } catch (FrameworkException fex) { logger.warn("", fex); fail("Unexpected exception."); } // test doImportExportRoundtrip(true); // check try (final Tx tx = app.tx()) { final SchemaMethod method1 = app.nodeQuery(SchemaMethod.class).and(SchemaMethod.name, "method1").getFirst(); final SchemaMethod method2 = app.nodeQuery(SchemaMethod.class).and(SchemaMethod.name, "method2").getFirst(); Assert.assertNotNull("Invalid deployment result", method1); Assert.assertNotNull("Invalid deployment result", method2); Assert.assertEquals("Invalid SchemaMethod deployment result", "method1", method1.getProperty(SchemaMethod.name)); Assert.assertEquals("Invalid SchemaMethod deployment result", "comment1", method1.getProperty(SchemaMethod.comment)); Assert.assertEquals("Invalid SchemaMethod deployment result", "source1", method1.getProperty(SchemaMethod.source)); Assert.assertEquals("Invalid SchemaMethod deployment result", "virtualFileName1", method1.getProperty(SchemaMethod.virtualFileName)); Assert.assertEquals("Invalid SchemaMethod deployment result", true, method1.getProperty(SchemaMethod.visibleToPublicUsers)); Assert.assertEquals("Invalid SchemaMethod deployment result", false, method1.getProperty(SchemaMethod.visibleToAuthenticatedUsers)); Assert.assertEquals("Invalid SchemaMethod deployment result", "method2", method2.getProperty(SchemaMethod.name)); Assert.assertEquals("Invalid SchemaMethod deployment result", "comment2", method2.getProperty(SchemaMethod.comment)); Assert.assertEquals("Invalid SchemaMethod deployment result", "source2", method2.getProperty(SchemaMethod.source)); Assert.assertEquals("Invalid SchemaMethod deployment result", "virtualFileName2", method2.getProperty(SchemaMethod.virtualFileName)); Assert.assertEquals("Invalid SchemaMethod deployment result", false, method2.getProperty(SchemaMethod.visibleToPublicUsers)); Assert.assertEquals("Invalid SchemaMethod deployment result", true, method2.getProperty(SchemaMethod.visibleToAuthenticatedUsers)); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } } @Test public void test34WidgetWithTemplate() { // setup try (final Tx tx = app.tx()) { Page testPage = app.create(Page.class, "WidgetTestPage"); Html html = app.create(Html.class); Head head = app.create(Head.class); Body body = app.create(Body.class); Div div = app.create(Div.class,"WidgetTestPage-Div"); Div div2 = app.create(Div.class,"WidgetTestPage-Div2"); testPage.appendChild(html); html.appendChild(head); html.appendChild(body); body.appendChild(div); body.appendChild(div2); Widget widgetToImport = app.create(Widget.class, new NodeAttribute<>(Widget.name,"TestWidget"), new NodeAttribute<>(Widget.source,"<!-- @structr:content(text/html) --><structr:template>${{Structr.print(\"<div>Test</div>\");}}</structr:template>"), new NodeAttribute<>(Widget.configuration,"{\"processDeploymentInfo\": true}"), new NodeAttribute<>(Widget.visibleToPublicUsers,true), new NodeAttribute<>(Widget.visibleToAuthenticatedUsers,true) ); Importer importer = new Importer(securityContext, widgetToImport.getProperty(new StringProperty("source")), null, null, true, true); importer.setIsDeployment(true); importer.setCommentHandler(new DeploymentCommentHandler()); importer.parse(true); DOMNode template = importer.createComponentChildNodes(div, testPage); div.appendChild(template); makePublic(testPage, html,head, body, div, div2, template); tx.success(); } catch (FrameworkException fex) { logger.warn("", fex); fail("Unexpected exception."); } // test try (final Tx tx = app.tx()) { Div div = (Div)app.nodeQuery().andName("WidgetTestPage-Div").getFirst(); Assert.assertEquals(1, div.treeGetChildCount()); Object obj = div.treeGetFirstChild(); Assert.assertEquals(Template.class, obj.getClass()); Template template = (Template)obj; Assert.assertEquals("${{Structr.print(\"<div>Test</div>\");}}", template.getTextContent()); Assert.assertEquals("text/html", template.getProperty(Template.contentType)); tx.success(); } catch (FrameworkException fex) { logger.warn("", fex); fail("Unexpected exception."); } } @Test public void test35WidgetWithSharedComponentCreation() { // setup try (final Tx tx = app.tx()) { Page testPage = app.create(Page.class, "WidgetTestPage"); Html html = app.create(Html.class); Head head = app.create(Head.class); Body body = app.create(Body.class); Div div = app.create(Div.class,"WidgetTestPage-Div"); Div div2 = app.create(Div.class,"WidgetTestPage-Div2"); testPage.appendChild(html); html.appendChild(head); html.appendChild(body); body.appendChild(div); body.appendChild(div2); Widget widgetToImport = app.create(Widget.class, new NodeAttribute<>(Widget.name,"TestWidget"), new NodeAttribute<>(Widget.source, "<structr:component src=\"TestComponent\">\n" + " <div data-structr-meta-name=\"TestComponent\">\n" + " Test123\n" + " </div>\n" + "</structr:component>"), new NodeAttribute<>(Widget.configuration,""), new NodeAttribute<>(Widget.visibleToPublicUsers,true), new NodeAttribute<>(Widget.visibleToAuthenticatedUsers,true) ); Map<String,Object> paramMap = new HashMap<>(); paramMap.put("widgetHostBaseUrl", "https://widgets.structr.org/structr/rest/widgets"); paramMap.put("parentId", widgetToImport.getProperty(new StartNode<>("owner", PrincipalOwnsNode.class))); paramMap.put("source", widgetToImport.getProperty(new StringProperty("source"))); paramMap.put("processDeploymentInfo", false); Widget.expandWidget(securityContext, testPage, div, baseUri, paramMap, false); Widget.expandWidget(securityContext, testPage, div, baseUri, paramMap, false); makePublic(testPage, html,head, body, div, div2); tx.success(); } catch (FrameworkException fex) { logger.warn("", fex); fail("Unexpected exception."); } // test try (final Tx tx = app.tx()) { Div div = app.nodeQuery(Div.class).andName("WidgetTestPage-Div").getFirst(); Assert.assertEquals(2, div.treeGetChildCount()); Object obj = null; for(DOMNode n: div.getAllChildNodes()){ obj = n; break; } Assert.assertEquals(Div.class, obj.getClass()); Div clonedNode = (Div)obj; Assert.assertEquals(0, clonedNode.getChildNodes().getLength()); Assert.assertEquals(3, app.nodeQuery(Div.class).andName("TestComponent").getResult().size()); tx.success(); } catch (FrameworkException fex) { logger.warn("", fex); fail("Unexpected exception."); } } @Test public void test36BuiltInTypesWithProperties() { // setup schema try (final Tx tx = app.tx()) { final JsonSchema schema = StructrSchema.createFromDatabase(app); Assert.assertNotNull("StructrSchema must return a valid schema object", schema); final JsonType pageType = schema.getType("Page"); final JsonType fileType = schema.getType("File"); Assert.assertNotNull("Type Page must exist in every schema", pageType); Assert.assertNotNull("Type File must exist in every schema", fileType); pageType.addIntegerProperty("displayPosition"); pageType.addStringProperty("icon"); fileType.addIntegerProperty("test1"); fileType.addStringProperty("test2"); // install schema StructrSchema.replaceDatabaseSchema(app, schema); tx.success(); } catch (FrameworkException | URISyntaxException fex) { fail("Unexpected exception."); } // setup try (final Tx tx = app.tx()) { final Page page = Page.createSimplePage(securityContext, "page1"); page.setProperty(StructrApp.getConfiguration().getPropertyKeyForJSONName(Page.class, "displayPosition"), 12); page.setProperty(StructrApp.getConfiguration().getPropertyKeyForJSONName(Page.class, "icon"), "icon"); final Folder folder = app.create(Folder.class, "files"); folder.setProperty(Folder.includeInFrontendExport, true); // create test file with custom attributes app.create(File.class, new NodeAttribute<>(File.name, "test.txt"), new NodeAttribute<>(File.parent, folder), new NodeAttribute<>(File.contentType, "text/plain"), new NodeAttribute<>(StructrApp.getConfiguration().getPropertyKeyForDatabaseName(File.class, "test1"), 123), new NodeAttribute<>(StructrApp.getConfiguration().getPropertyKeyForDatabaseName(File.class, "test2"), "testString") ); tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception."); } compare(calculateHash(), true); } // ----- private methods ----- private void compare(final String sourceHash, final boolean deleteTestDirectory) { compare(sourceHash, deleteTestDirectory, true); } private void compare(final String sourceHash, final boolean deleteTestDirectory, final boolean cleanDatabase) { doImportExportRoundtrip(deleteTestDirectory, cleanDatabase, null); final String roundtripHash = calculateHash(); System.out.println("Expected: " + sourceHash); System.out.println("Actual: " + roundtripHash); if (!sourceHash.equals(roundtripHash)) { System.out.println("Expected: " + sourceHash); System.out.println("Actual: " + roundtripHash); fail("Invalid deployment roundtrip result"); } } private void doImportExportRoundtrip(final boolean deleteTestDirectory) { doImportExportRoundtrip(deleteTestDirectory, true, null); } private void doImportExportRoundtrip(final boolean deleteTestDirectory, final boolean cleanDatabase, final Function callback) { final DeployCommand cmd = app.command(DeployCommand.class); final Path tmp = Paths.get("/tmp/structr-deployment-test" + System.currentTimeMillis() + System.nanoTime()); try { if (tmp != null) { // export to temp directory final Map<String, Object> firstExportParams = new HashMap<>(); firstExportParams.put("mode", "export"); firstExportParams.put("target", tmp.toString()); // execute deploy command cmd.execute(firstExportParams); if (cleanDatabase) { cleanDatabase(); } // apply callback if present if (callback != null) { callback.apply(null); } // import from exported source final Map<String, Object> firstImportParams = new HashMap<>(); firstImportParams.put("source", tmp.toString()); // execute deploy command cmd.execute(firstImportParams); } else { fail("Unable to create temporary directory."); } } catch (FrameworkException fex) { logger.warn("", fex); } finally { if (deleteTestDirectory) { try { // clean directories Files.walkFileTree(tmp, new DeletingFileVisitor()); Files.delete(tmp); } catch (IOException ioex) {} } } } private String calculateHash() { final StringBuilder buf = new StringBuilder(); try (final Tx tx = app.tx()) { for (final Page page : app.nodeQuery(Page.class).sort(AbstractNode.name).getAsList()) { System.out.print("############################# "); calculateHash(page, buf, 0); } for (final Folder folder : app.nodeQuery(Folder.class).sort(AbstractNode.name).getAsList()) { if (DeployCommand.okToExport(folder)) { System.out.print("############################# "); calculateHash(folder, buf, 0); } } for (final FileBase file : app.nodeQuery(File.class).sort(AbstractNode.name).getAsList()) { if (DeployCommand.okToExport(file)) { System.out.print("############################# "); calculateHash(file, buf, 0); } } tx.success(); } catch (FrameworkException fex) { fail("Unexpected exception"); } return buf.toString();//DigestUtils.md5Hex(buf.toString()); } private void calculateHash(final AbstractNode start, final StringBuilder buf, final int depth) { buf.append(start.getType()).append("{"); hash(start, buf); // indent for (int i=0; i<depth; i++) { System.out.print(" "); } System.out.println(start.getType() + ": " + start.getUuid().substring(0, 5)); if (start instanceof ShadowDocument) { for (final DOMNode child : ((ShadowDocument)start).getProperty(Page.elements)) { // only include toplevel elements of the shadow document if (child.getProperty(DOMNode.parent) == null) { calculateHash(child, buf, depth+1); } } } else { for (final DOMNode child : start.getProperty(DOMNode.children)) { calculateHash(child, buf, depth+1); } } buf.append("}"); } private void hash(final AbstractNode node, final StringBuilder buf) { // AbstractNode buf.append(valueOrEmpty(node, AbstractNode.type)); buf.append(valueOrEmpty(node, AbstractNode.name)); buf.append(valueOrEmpty(node, AbstractNode.visibleToPublicUsers)); buf.append(valueOrEmpty(node, AbstractNode.visibleToAuthenticatedUsers)); // include owner in content hash generation! final Principal owner = node.getOwnerNode(); if (owner != null) { buf.append(valueOrEmpty(owner, AbstractNode.name)); } // include grants in content hash generation! for (final Security r : node.getSecurityRelationships()) { if (r != null) { buf.append(r.getSourceNode().getName()); buf.append(r.getPermissions()); } } // DOMNode buf.append(valueOrEmpty(node, DOMNode.showConditions)); buf.append(valueOrEmpty(node, DOMNode.hideConditions)); buf.append(valueOrEmpty(node, DOMNode.showForLocales)); buf.append(valueOrEmpty(node, DOMNode.hideForLocales)); buf.append(valueOrEmpty(node, DOMNode.hideOnIndex)); buf.append(valueOrEmpty(node, DOMNode.hideOnDetail)); buf.append(valueOrEmpty(node, DOMNode.renderDetails)); buf.append(valueOrEmpty(node, DOMNode.sharedComponentConfiguration)); final Page ownerDocument = node.getProperty(DOMNode.ownerDocument); if (ownerDocument != null) { buf.append(valueOrEmpty(ownerDocument, AbstractNode.name)); } // DOMElement buf.append(valueOrEmpty(node, DOMElement.dataKey)); buf.append(valueOrEmpty(node, DOMElement.restQuery)); buf.append(valueOrEmpty(node, DOMElement.cypherQuery)); buf.append(valueOrEmpty(node, DOMElement.xpathQuery)); buf.append(valueOrEmpty(node, DOMElement.functionQuery)); // Content buf.append(valueOrEmpty(node, Content.contentType)); buf.append(valueOrEmpty(node, Content.content)); // Page buf.append(valueOrEmpty(node, Page.cacheForSeconds)); buf.append(valueOrEmpty(node, Page.dontCache)); buf.append(valueOrEmpty(node, Page.pageCreatesRawData)); buf.append(valueOrEmpty(node, Page.position)); buf.append(valueOrEmpty(node, Page.showOnErrorCodes)); // HTML attributes if (node instanceof DOMElement) { for (final PropertyKey key : ((DOMElement)node).getHtmlAttributes()) { buf.append(valueOrEmpty(node, key)); } } for (final PropertyKey key : node.getPropertyKeys(PropertyView.All)) { if (key.isDynamic()) { buf.append(valueOrEmpty(node, key)); } } } private String valueOrEmpty(final GraphObject obj, final PropertyKey key) { final Object value = obj.getProperty(key); if (value != null) { return key.jsonName() + "=" + value.toString() + ";"; } return ""; } private <T extends Node> T createElement(final Page page, final DOMNode parent, final String tag, final String... content) { final T child = (T)page.createElement(tag); parent.appendChild((DOMNode)child); if (content != null && content.length > 0) { for (final String text : content) { final Node node = page.createTextNode(text); child.appendChild(node); } } return child; } private Template createTemplate(final Page page, final DOMNode parent, final String content) throws FrameworkException { final Template template = StructrApp.getInstance().create(Template.class, new NodeAttribute<>(Template.content, content), new NodeAttribute<>(Template.ownerDocument, page) ); if (parent != null) { parent.appendChild((DOMNode)template); } return template; } private <T> T createContent(final Page page, final DOMNode parent, final String content) { final T child = (T)page.createTextNode(content); parent.appendChild((DOMNode)child); return child; } private <T> T createComment(final Page page, final DOMNode parent, final String comment) { final T child = (T)page.createComment(comment); parent.appendChild((DOMNode)child); return child; } private <T> T createComponent(final DOMNode node) throws FrameworkException { return (T) new CreateComponentCommand().create(node); } private <T> T cloneComponent(final DOMNode node, final DOMNode parentNode) throws FrameworkException { return (T) new CloneComponentCommand().cloneComponent(node, parentNode); } private Folder getRootFolder(final Folder folder) { Folder parent = folder; boolean root = false; while (parent != null && !root) { if (parent.getProperty(Folder.parent) != null) { parent = parent.getProperty(Folder.parent); } else { root = true; } } return parent; } // ----- nested classes ----- private static class DeletingFileVisitor implements FileVisitor<Path> { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.delete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { Files.delete(dir); return FileVisitResult.CONTINUE; } } }