diff --git a/structr-base/src/main/java/org/structr/web/entity/ContentContainer.java b/structr-base/src/main/java/org/structr/web/entity/ContentContainer.java deleted file mode 100644 index 394ae301d2..0000000000 --- a/structr-base/src/main/java/org/structr/web/entity/ContentContainer.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2010-2024 Structr GmbH - * - * This file is part of Structr . - * - * Structr is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Structr. If not, see . - */ -package org.structr.web.entity; - -import org.structr.api.graph.Cardinality; -import org.structr.api.schema.JsonObjectType; -import org.structr.api.schema.JsonSchema; -import org.structr.common.ConstantBooleanTrue; -import org.structr.common.PropertyView; -import org.structr.core.graph.NodeInterface; -import org.structr.schema.SchemaService; -import org.structr.web.property.ContentPathProperty; - -import java.net.URI; - -/** - * Base class for all content containers. - */ -public interface ContentContainer extends NodeInterface { - - static class Impl { static { - - final JsonSchema schema = SchemaService.getDynamicSchema(); - final JsonObjectType type = schema.addType("ContentContainer"); - final JsonObjectType item = schema.addType("ContentItem"); - - type.setImplements(URI.create("https://structr.org/v1.1/definitions/ContentContainer")); - type.setCategory("ui"); - - type.addBooleanProperty("isContentContainer", PropertyView.Public, PropertyView.Ui).setReadOnly(true).addTransformer(ConstantBooleanTrue.class.getName()); - type.addIntegerProperty("position", PropertyView.Public, PropertyView.Ui).setIndexed(true); - type.addCustomProperty("path", ContentPathProperty.class.getName(), PropertyView.Public, PropertyView.Ui).setTypeHint("String").setReadOnly(true).setIndexed(true); - - type.addPropertyGetter("parent", ContentContainer.class); - type.addPropertyGetter("items", Iterable.class); - - type.relate(type, "CONTAINS", Cardinality.OneToMany, "parent", "childContainers"); - type.relate(item, "CONTAINS", Cardinality.ManyToMany, "containers", "items"); - - // view configuration - type.addViewProperty(PropertyView.Public, "childContainers"); - type.addViewProperty(PropertyView.Public, "items"); - type.addViewProperty(PropertyView.Public, "name"); - type.addViewProperty(PropertyView.Public, "owner"); - type.addViewProperty(PropertyView.Public, "parent"); - - type.addViewProperty(PropertyView.Ui, "childContainers"); - type.addViewProperty(PropertyView.Ui, "items"); - type.addViewProperty(PropertyView.Ui, "name"); - type.addViewProperty(PropertyView.Ui, "owner"); - type.addViewProperty(PropertyView.Ui, "parent"); - }} - - ContentContainer getParent(); - Iterable getItems(); - - - /* - - public static final Property> items = new EndNodes<>("items", ContainerContentItems.class); - public static final Property parent = new StartNode<>("parent", ContainerContentContainer.class); - public static final Property> childContainers = new EndNodes<>("childContainers", ContainerContentContainer.class); - public static final Property isContentContainer = new ConstantBooleanProperty("isContentContainer", true); - public static final Property path = new ContentPathProperty("path").indexed().readOnly(); - - public static final View publicView = new View(Folder.class, PropertyView.Public, id, type, name, path, owner, items, parent, childContainers, isContentContainer); - public static final View uiView = new View(Folder.class, PropertyView.Ui, id, type, name, path, owner, items, parent, childContainers, isContentContainer); - - */ -} diff --git a/structr-base/src/main/java/org/structr/web/entity/ContentItem.java b/structr-base/src/main/java/org/structr/web/entity/ContentItem.java deleted file mode 100644 index 61689f53e6..0000000000 --- a/structr-base/src/main/java/org/structr/web/entity/ContentItem.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2010-2024 Structr GmbH - * - * This file is part of Structr . - * - * Structr is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Structr. If not, see . - */ -package org.structr.web.entity; - -import org.structr.api.schema.JsonObjectType; -import org.structr.api.schema.JsonSchema; -import org.structr.common.ConstantBooleanTrue; -import org.structr.common.PropertyView; -import org.structr.core.graph.NodeInterface; -import org.structr.schema.SchemaService; - -import java.net.URI; - -/** - * Base class for all content items. - */ -public interface ContentItem extends NodeInterface { - - static class Impl { static { - - final JsonSchema schema = SchemaService.getDynamicSchema(); - final JsonObjectType type = schema.addType("ContentItem"); - - type.setImplements(URI.create("https://structr.org/v1.1/definitions/ContentItem")); - type.setCategory("ui"); - - type.addBooleanProperty("isContentItem", PropertyView.Public, PropertyView.Ui).setReadOnly(true).addTransformer(ConstantBooleanTrue.class.getName()); - type.addIntegerProperty("position", PropertyView.Public, PropertyView.Ui).setIndexed(true); - - type.addViewProperty(PropertyView.Public, "containers"); - type.addViewProperty(PropertyView.Public, "name"); - type.addViewProperty(PropertyView.Public, "owner"); - - type.addViewProperty(PropertyView.Ui, "containers"); - type.addViewProperty(PropertyView.Ui, "name"); - type.addViewProperty(PropertyView.Ui, "owner"); - }} - - /* - public static final Property> containers = new StartNodes<>("containers", ContainerContentItems.class); - public static final Property isContentItem = new ConstantBooleanProperty("isContentItem", true); - - public static final View publicView = new View(Folder.class, PropertyView.Public, id, type, name, owner, containers, isContentItem); - public static final View uiView = new View(Folder.class, PropertyView.Ui, id, type, name, owner, containers, isContentItem); - */ - -} diff --git a/structr-base/src/main/java/org/structr/web/maintenance/DeployDataCommand.java b/structr-base/src/main/java/org/structr/web/maintenance/DeployDataCommand.java index d3d4dcc13e..3e6bf6f976 100644 --- a/structr-base/src/main/java/org/structr/web/maintenance/DeployDataCommand.java +++ b/structr-base/src/main/java/org/structr/web/maintenance/DeployDataCommand.java @@ -116,8 +116,8 @@ public void doExport(final Map parameters) throws FrameworkExcep if (StringUtils.isBlank(types)) { - publishWarningMessage("Data export not started", "Please provide a comma-separated list of type(s) to export. (e.g. 'ContentContainer,ContentItem')"); - throw new FrameworkException(422, "Please provide a comma-separated list of type(s) to export. (e.g. 'ContentContainer,ContentItem')"); + publishWarningMessage("Data export not started", "Please provide a comma-separated list of type(s) to export."); + throw new FrameworkException(422, "Please provide a comma-separated list of type(s) to export."); } final Path target = Paths.get(path); diff --git a/structr-base/src/main/java/org/structr/web/property/ContentPathProperty.java b/structr-base/src/main/java/org/structr/web/property/ContentPathProperty.java deleted file mode 100644 index 823c5fac81..0000000000 --- a/structr-base/src/main/java/org/structr/web/property/ContentPathProperty.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010-2024 Structr GmbH - * - * This file is part of Structr . - * - * Structr is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Structr. If not, see . - */ -package org.structr.web.property; - -import org.structr.api.Predicate; -import org.structr.api.search.SortType; -import org.structr.common.SecurityContext; -import org.structr.core.GraphObject; -import org.structr.core.property.AbstractReadOnlyProperty; -import org.structr.web.entity.AbstractFile; -import org.structr.web.entity.ContentContainer; - -import java.util.Map; - -/** - * A property which returns the complete folder path of a content container or item - * including name. The path consists of the names of the parent elements, - * concatenated by "/" as path separator. - * - * - */ -public class ContentPathProperty extends AbstractReadOnlyProperty { - - public ContentPathProperty(String name) { - super(name); - } - - @Override - public Class relatedType() { - return null; - } - - @Override - public Class valueType() { - return String.class; - } - - @Override - public String typeName() { - return "String"; - } - - @Override - public String getProperty(SecurityContext securityContext, GraphObject obj, boolean applyConverter) { - return getProperty(securityContext, obj, applyConverter, null); - } - - @Override - public String getProperty(SecurityContext securityContext, GraphObject obj, boolean applyConverter, final Predicate predicate) { - - ContentContainer parentContainer = ((ContentContainer)obj).getParent(); - String containerPath = obj.getProperty(AbstractFile.name); - - if (containerPath == null) { - containerPath = obj.getProperty(GraphObject.id); - } - - while (parentContainer != null && !parentContainer.equals(obj) && !parentContainer.equals(parentContainer.getParent())) { - - containerPath = parentContainer.getName().concat("/").concat(containerPath); - parentContainer = parentContainer.getParent(); - } - - return "/".concat(containerPath); - } - - @Override - public boolean isCollection() { - return false; - } - - @Override - public SortType getSortType() { - return SortType.Integer; - } - - // ----- OpenAPI ----- - @Override - public Object getExampleValue(final String type, final String viewName) { - return null; - } - - @Override - public Map describeOpenAPIOutputSchema(String type, String viewName) { - return null; - } -} diff --git a/structr-base/src/main/java/org/structr/websocket/command/AppendContentItemCommand.java b/structr-base/src/main/java/org/structr/websocket/command/AppendContentItemCommand.java deleted file mode 100644 index 9878208853..0000000000 --- a/structr-base/src/main/java/org/structr/websocket/command/AppendContentItemCommand.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2010-2024 Structr GmbH - * - * This file is part of Structr . - * - * Structr is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Structr. If not, see . - */ -package org.structr.websocket.command; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.structr.api.util.Iterables; -import org.structr.common.error.FrameworkException; -import org.structr.core.app.StructrApp; -import org.structr.core.entity.AbstractNode; -import org.structr.core.graph.NodeInterface; -import org.structr.core.graph.TransactionCommand; -import org.structr.web.entity.ContentContainer; -import org.structr.web.entity.ContentItem; -import org.structr.websocket.StructrWebSocket; -import org.structr.websocket.message.MessageBuilder; -import org.structr.websocket.message.WebSocketMessage; - -import java.util.List; - -/** - * Append a content item to a content container. - * - * Note that - unlike folders/files - content items can be contained in multiple containers. - * - */ -public class AppendContentItemCommand extends AbstractCommand { - - private static final Logger logger = LoggerFactory.getLogger(AppendContentItemCommand.class.getName()); - - static { - - StructrWebSocket.addCommand(AppendContentItemCommand.class); - } - - @Override - public void processMessage(final WebSocketMessage webSocketData) { - - setDoTransactionNotifications(true); - - String id = webSocketData.getId(); - String parentId = webSocketData.getNodeDataStringValue("parentId"); - - // check node to append - if (id == null) { - - getWebSocket().send(MessageBuilder.status().code(422).message("Cannot append node, no id is given").build(), true); - - return; - - } - - // check for parent ID - if (parentId == null) { - - getWebSocket().send(MessageBuilder.status().code(422).message("Cannot append node without parentId").build(), true); - - return; - - } - - // never append to self - if (parentId.equals(id)) { - - getWebSocket().send(MessageBuilder.status().code(422).message("Cannot append node as its own child.").build(), true); - - return; - - } - - - // check if parent node with given ID exists - AbstractNode parentNode = getNode(parentId); - - if (parentNode == null) { - - getWebSocket().send(MessageBuilder.status().code(404).message("Parent node not found").build(), true); - - return; - - } - - if (parentNode instanceof ContentContainer) { - - final ContentContainer container = (ContentContainer) parentNode; - final NodeInterface node = (NodeInterface) getNode(id); - - if (node != null) { - - try { - - if (node instanceof ContentItem) { - - final ContentItem item = (ContentItem) node; - - final List items = Iterables.toList(container.getItems()); - - items.add(item); - - container.setProperty(StructrApp.key(ContentContainer.class, "items"), items); - - } else if (node instanceof ContentContainer) { - - final ContentContainer child = (ContentContainer) node; - - child.setProperty(StructrApp.key(ContentContainer.class, "parent"), container); - - } else { - - // send exception - getWebSocket().send(MessageBuilder.status().code(422).message("Given object is not of type ContentItem or ContentContainer").build(), true); - return; - } - - TransactionCommand.registerNodeCallback(node, callback); - - } catch (FrameworkException ex) { - logger.error("", ex); - getWebSocket().send(MessageBuilder.status().code(422).message("Cannot append content item").build(), true); - } - } - - } else { - - // send exception - getWebSocket().send(MessageBuilder.status().code(422).message("Parent node is not instance of ContentContainer").build(), true); - } - } - - @Override - public String getCommand() { - - return "APPEND_CONTENT_ITEM"; - - } - -} diff --git a/structr-base/src/main/resources/structr/css/contents.css b/structr-base/src/main/resources/structr/css/contents.css deleted file mode 100644 index 7e48d4ec91..0000000000 --- a/structr-base/src/main/resources/structr/css/contents.css +++ /dev/null @@ -1,167 +0,0 @@ -/** - * Copyright (C) 2010-2024 Structr GmbH - * - * This file is part of Structr . - * - * Structr is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Structr. If not, see . - */ -.folder-path { - font-size: 1.5rem; - line-height: 2rem; - margin: 2rem .75rem; -} - -#contents-contents { - position: relative; -} - -#contents-contents table th { - text-align: left; - padding-left: 1rem; -} - -#contents-contents .thumbnail { - color: var(--gray-aaa); -} - -#contents-contents .thumbnailZoom { - max-width: 300px; - max-height: 300px; - position: absolute; - z-index: 4; - display: none; -} - -#contents-contents tr[id]:hover { - background-color: var(--light-gray-bg-f3) ! important; -} - -#contents-contents .nodeActive { - background-color: var(--light-gray-bg-f3) ! important; -} - -#contents-contents .nodeHover, .image.nodeHover { - color: var(--gray-333) ! important; - z-index: 1; -} - -#contents-contents table { - table-layout: fixed; -} - -#contents-contents table th:nth-child(1) { - width: 24px; -} - -#contents-contents table th:nth-child(2) { - width: 40%; -} - -#contents-contents table th:nth-child(3) { - width: 10%; -} - -#contents-contents table th:nth-child(4) { - width: 20%; -} - -#contents-contents table th:nth-child(5) { - width: calc(20% - 24px); -} - -#contents-contents table th:nth-child(6) { - width: 10%; -} - -/* - * Files table - */ -#files-table { - margin-bottom: 1rem; -} - -#files-table tr:nth-child(even) { - background-color: var(--main-background); -} - -#files-table tr:nth-child(odd) { - background-color: var(--solid-white); -} - -#files-table th { - padding: 0 0 1rem .5rem; - text-align: left; -} - -#files-table tbody tr td .editable { - min-width: 100px; - min-height: 14px; -} - -#files-table tbody tr td .node { - min-width: 6rem; - padding-right: .5rem; -} - -#files-table tr[id]:hover .node-action-icon { - display: inline-block; -} - -#files-table td.file-icon { - cursor: pointer; -} - -#files-table td { - text-align: left; - padding-left: 1rem; - height: 2rem; -} - -#files-table td a { - text-decoration: none; -} - -#files-table td a:hover { - text-decoration: underline; -} - -#files-table .file, #files-table .folder { - margin: 0; -} - -/* start dragndrop classes in contents */ -.node:hover { - cursor: pointer; -} - -.node.drag-over:not(.drop-not-possible) { -} -.node.drag-background-node-highlight { - background: var(--hover-highlight-color-active); -} - -.node.drag-background-drop-forbidden { - background-image: var(--striped-background); - background-size: 20px 20px; -} - -.jstree-default .jstree-wholerow.drag-background-node-highlight { - background: var(--hover-highlight-color-active) ! important; -} - -.jstree-default .jstree-wholerow.drag-background-drop-forbidden { - background-image: var(--striped-background); - background-size: 20px 20px; -} -/* end dragndrop classes in contents */ \ No newline at end of file diff --git a/structr-base/src/main/resources/structr/css/main.css b/structr-base/src/main/resources/structr/css/main.css index 959112eb8b..b92a0ea1fd 100644 --- a/structr-base/src/main/resources/structr/css/main.css +++ b/structr-base/src/main/resources/structr/css/main.css @@ -2975,62 +2975,6 @@ i.fa-sitemap { left: 300px; } -/** - * Styles for the 'Contents' area - */ -.trumbowyg-box { - background-color: var(--solid-white); - border: none; - margin: 0 auto; -} - -.trumbowyg-editor { - min-height: 274px; -} - -.trumbowyg-button-pane { - background-color: var(--medium-light-gray-e0); -} - -.trumbowyg-button-pane button { - width: 32px; -} - -.trumbowyg-fullscreen.trumbowyg-box { - width: 100%; -} - -.value-container { - display: inline-block; - width: calc(100% - 236px); -} - -.value-container input[type=text], .value-container textarea { - width: 100%; - /*font-size: 1.2em;*/ - padding: 4px; -} - -.value-container textarea { - margin: 6px 0; - vertical-align: middle; -} - -.value-container.related-nodes { - background-color: var(--solid-white); - border: 1px solid var(--black-opacity-125); - padding: 4px; -} - -.value-container.related-nodes i.add { - vertical-align: text-bottom; -} - -.item-title:hover b { - cursor: pointer; -} - - /** * Favorites */ diff --git a/structr-base/src/main/resources/structr/index.html b/structr-base/src/main/resources/structr/index.html index b790380265..847c65fd14 100644 --- a/structr-base/src/main/resources/structr/index.html +++ b/structr-base/src/main/resources/structr/index.html @@ -11,7 +11,6 @@ - @@ -37,7 +36,6 @@ - @@ -54,7 +52,6 @@ - diff --git a/structr-base/src/main/resources/structr/js/command.js b/structr-base/src/main/resources/structr/js/command.js index c584a7669b..5c188bae63 100644 --- a/structr-base/src/main/resources/structr/js/command.js +++ b/structr-base/src/main/resources/structr/js/command.js @@ -483,19 +483,6 @@ let Command = { }; return StructrWS.sendObj(obj, callback); }, - /** - * Send an APPEND_CONTENT_ITEM command to the server. - */ - appendContentItem: function(id, parentId, callback) { - let obj = { - command: 'APPEND_CONTENT_ITEM', - id: id, - data: { - parentId: parentId - } - }; - return StructrWS.sendObj(obj, callback); - }, /** * Send an UNARCHIVE command to the server. * diff --git a/structr-base/src/main/resources/structr/js/contents.js b/structr-base/src/main/resources/structr/js/contents.js deleted file mode 100644 index 6e5c88db4a..0000000000 --- a/structr-base/src/main/resources/structr/js/contents.js +++ /dev/null @@ -1,1112 +0,0 @@ -/* - * Copyright (C) 2010-2024 Structr GmbH - * - * This file is part of Structr . - * - * Structr is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Structr. If not, see . - */ -document.addEventListener("DOMContentLoaded", () => { - Structr.registerModule(_Contents); -}); - -let _Contents = { - _moduleName: 'contents', - searchField: undefined, - contentsMain: undefined, - contentTree: undefined, - contentsContents: undefined, - currentContentContainer: undefined, - containerPageSize: 10000, - containerPage: 1, - currentContentContainerKey: 'structrCurrentContentContainer_' + location.port, - contentsResizerLeftKey: 'structrContentsResizerLeftKey_' + location.port, - selectedElements: [], - - init: () => { - Structr.adaptUiToAvailableFeatures(); - }, - resize: () => { - _Contents.moveResizer(); - }, - prevAnimFrameReqId_moveResizer: undefined, - moveResizer: (left) => { - - _Helpers.requestAnimationFrameWrapper(_Contents.prevAnimFrameReqId_moveResizer, () => { - - left = left || LSWrapper.getItem(_Contents.contentsResizerLeftKey) || 300; - $('.column-resizer', _Contents.contentsMain).css({left: left}); - - _Contents.contentTree.css({width: left - 14 + 'px'}); - }); - }, - onload: () => { - - Structr.setMainContainerHTML(_Contents.templates.contents()); - - _Contents.init(); - - Structr.updateMainHelpLink(_Helpers.getDocumentationURLForTopic('contents')); - - _Contents.contentsMain = $('#contents-main'); - _Contents.contentTree = $('#contents-tree'); - _Contents.contentsContents = $('#contents-contents'); - - _Contents.moveResizer(); - Structr.initVerticalSlider($('.column-resizer', _Contents.contentsMain), _Contents.contentsResizerLeftKey, 204, _Contents.moveResizer); - - let initFunctionBar = async () => { - - let contentContainerTypes = await _Schema.getDerivedTypes('org.structr.dynamic.ContentContainer', []); - let contentItemTypes = await _Schema.getDerivedTypes('org.structr.dynamic.ContentItem', []); - - Structr.setFunctionBarHTML(_Contents.templates.functions({ containerTypes: contentContainerTypes, itemTypes: contentItemTypes })); - - // UISettings.showSettingsForCurrentModule(); - - let itemTypeSelect = document.querySelector('select#content-item-type'); - let addItemButton = document.getElementById('add-item-button'); - let containerTypeSelect = document.querySelector('select#content-container-type'); - let addContainerButton = document.getElementById('add-container-button'); - - addItemButton.addEventListener('click', () => { - let containers = (_Contents.currentContentContainer ? [ { id : _Contents.currentContentContainer.id } ] : null); - Command.create({ type: itemTypeSelect.value, containers: containers }, (f) => { - _Contents.appendItemOrContainerRow(f); - }); - }); - - addContainerButton.addEventListener('click', () => { - let parent = (_Contents.currentContentContainer ? _Contents.currentContentContainer.id : null); - Command.create({ type: containerTypeSelect.value, parent: parent }, (f) => { - _Contents.appendItemOrContainerRow(f); - _Contents.refreshTree(); - }); - }); - - itemTypeSelect.addEventListener('change', () => { - addItemButton.querySelector('span').textContent = `Add ${itemTypeSelect.value}`; - }); - - containerTypeSelect.addEventListener('change', () => { - addContainerButton.querySelector('span').textContent = `Add ${containerTypeSelect.value}`; - }); - - if (contentItemTypes.length === 0) { - _Helpers.appendInfoTextToElement({ - text: "It is recommended to create a custom type extending org.structr.web.entity.ContentItem to create ContentItems.

If only one type of ContentItem is required, custom attributes can be added to the type ContentItem in the schema.", - element: $(itemTypeSelect).parent(), - after: true, - css: { - marginLeft: '-.75rem', - marginRight: '1rem' - } - }); - } - }; - initFunctionBar(); // run async (do not await) so it can execute while jstree is initialized - - $.jstree.defaults.core.themes.dots = false; - $.jstree.defaults.dnd.inside_pos = 'last'; - $.jstree.defaults.dnd.large_drop_target = true; - - _Contents.contentTree.on('ready.jstree', function() { - - _TreeHelper.makeAllTreeElementsDroppable(_Contents.contentTree, _Dragndrop.contents.enableTreeElementDroppable); - - _Contents.loadAndSetWorkingDir(function() { - if (_Contents.currentContentContainer) { - _Contents.deepOpen(_Contents.currentContentContainer); - } - }); - }); - - _Contents.contentTree.on('select_node.jstree', function(evt, data) { - - _Contents.setCurrentContentContainer(data.node.id); - _Contents.displayContainerContents(data.node.id, data.node.parent, data.node.original.path, data.node.parents); - }); - - _Contents.contentTree.on('after_open.jstree', (evt, data) => { - _TreeHelper.makeAllTreeElementsDroppable(_Contents.contentTree, _Dragndrop.contents.enableTreeElementDroppable); - }); - - _Contents.contentTree.on('refresh_node.jstree', (evt, data) => { - _TreeHelper.makeAllTreeElementsDroppable(_Contents.contentTree, _Dragndrop.contents.enableTreeElementDroppable); - }); - - _TreeHelper.initTree(_Contents.contentTree, _Contents.treeInitFunction, 'structr-ui-contents'); - - Structr.mainMenu.unblock(100); - - Structr.resize(); - }, - handleNodeRefresh: (node) => { - - - if (node.isContentContainer && node.name) { - - let folderContents = _Contents.contentsContents[0]; - - // update breadcrumb - if (folderContents.dataset['currentContainer'] === node.id) { - folderContents.querySelector('#current-container-name')?.replaceChildren(node.name); - } - - // update tree element - let treeNode = _Contents.contentTree.jstree().get_node(node.id); - - if (treeNode) { - _Contents.contentTree.jstree().get_node(node.id).text = _Contents.getContainerDisplayName(node); - _Contents.contentTree.jstree().refresh_node(node.id); - } - } - }, - getContextMenuElements: (div, entity) => { - - let elements = []; - const isContentContainer = entity.isContentContainer; - const isContentItem = entity.isContentItem; - let selectedElements = document.querySelectorAll('.node.selected'); - - // there is a difference when right-clicking versus clicking the kebab icon - let contentNode = div; - if (contentNode.classList.contains('icons-container')) { - contentNode = div.closest('.node'); - } else if (!contentNode.classList.contains('node')) { - contentNode = div.querySelector('.node'); - } - - if (contentNode && !contentNode.classList.contains('selected')) { - - for (let selNode of document.querySelectorAll('.node.selected')) { - selNode.classList.remove('selected'); - } - contentNode.classList.add('selected'); - - selectedElements = document.querySelectorAll('.node.selected'); - } - - let isMultiSelect = selectedElements.length > 1; - - if (isContentItem && isMultiSelect === false) { - - elements.push({ - icon: _Icons.getMenuSvgIcon(_Icons.iconPencilEdit), - name: 'Edit', - clickHandler: () => { - _Contents.editItem(entity); - } - }); - } - - // TODO: Inheriting types do not have containers as UI-view attribute - if (contentNode) { - let containers = entity?.containers ?? [entity.parent]; - if (_Contents.currentContentContainer?.id && containers.length > 0) { - - elements.push({ - icon: _Icons.getMenuSvgIcon(_Icons.iconFolderRemove), - name: 'Remove from container', - clickHandler: () => { - - let removePromises = [...selectedElements].map(el => new Promise((resolve, reject) => { - - let node = StructrModel.obj(Structr.getId(el)); - - if (node.isContentContainer) { - _Entities.setProperty(node.id, 'parent', null, false, resolve); - } else { - _Entities.setProperty(node.id, 'containers', containers.filter(c => c.id !== _Contents.currentContentContainer.id), false, resolve); - } - })); - - Promise.all(removePromises).then(values => { - _Contents.refreshTree(); - }); - } - }); - } - } - - elements.push({ - name: 'Properties', - clickHandler: () => { - _Entities.showProperties(entity, 'ui'); - } - }); - - if (!isMultiSelect) { - - _Elements.contextMenu.appendContextMenuSeparator(elements); - - _Elements.contextMenu.appendSecurityContextMenuItems(elements, entity); - } - - _Elements.contextMenu.appendContextMenuSeparator(elements); - - if (contentNode) { - - elements.push({ - icon: _Icons.getMenuSvgIcon(_Icons.iconTrashcan), - classes: ['menu-bolder', 'danger'], - name: 'Delete ' + (isMultiSelect ? 'selected' : entity.type), - clickHandler: () => { - - let nodesToDelete = [...selectedElements].map(el => Structr.entityFromElement(el)); - - // [recursive=false] because deleting recursively does not make sense and can not work because it is a n:m relationship - _Entities.deleteNodes(nodesToDelete, false, () => { - _Contents.refreshTree(); - }); - } - }); - } - - _Elements.contextMenu.appendContextMenuSeparator(elements); - - return elements; - }, - deepOpen: (d, dirs) => { - - _TreeHelper.deepOpen(_Contents.contentTree, d, dirs, 'parent', (_Contents.currentContentContainer ? _Contents.currentContentContainer.id : 'root')); - }, - refreshTree: () => { - - _TreeHelper.refreshTree(_Contents.contentTree, () =>{ - }); - }, - treeInitFunction: (obj, callback) => { - - switch (obj.id) { - - case '#': - - let defaultEntries = [{ - id: 'root', - text: '/', - children: true, - icon: _Icons.nonExistentEmptyIcon, - data: { - svgIcon: _Icons.getSvgIcon(_Icons.iconStructrSSmall, 18, 24) - }, - path: '/', - state: { - opened: true, - selected: true - } - }]; - - callback(defaultEntries); - - break; - - case 'root': - _Contents.load(null, callback); - break; - - default: - _Contents.load(obj.id, callback); - break; - } - }, - unload: () => { - }, - fulltextSearch: (searchString) => { - - _Contents.contentsContents.children().hide(); - - _Contents.displaySearchResultsForURL(`${Structr.rootUrl}ContentItem/ui?${Structr.getRequestParameterName('loose')}=1${searchString.split(' ').map((str) => '&name=' + str)}`); - }, - clearSearch: () => { - Structr.mainContainer.querySelector('.search').value = ''; - $('#search-results').remove(); - _Contents.contentsContents.children().show(); - }, - loadAndSetWorkingDir: (callback) => { - - _Contents.currentContentContainer = LSWrapper.getItem(_Contents.currentContentContainerKey); - callback(); - }, - load: (id, callback) => { - - let filter = { - parent: (id ? id : null) - }; - - let customView = 'id,name,items,isContentContainer,childContainers,path,parent'; - Command.queryPromise('ContentContainer', _Contents.containerPageSize, _Contents.containerPage, 'position', 'asc', filter, true, null, customView).then(containers => { - - return containers.map(d => { - - StructrModel.createOrUpdateFromData(d, null, false); - - return { - id: d.id, - text: _Contents.getContainerDisplayName(d), - children: d.isContentContainer && d.childContainers.length > 0, - icon: _Icons.nonExistentEmptyIcon, - data: { svgIcon: _Icons.getSvgIcon(_Icons.iconFolderClosed, 16, 24) }, - path: d.path - }; - }); - - return list; - - }).then(callback).catch(e => { - // silently ignore - this is usually because the tree is being refreshed as a whole while we want to refresh the node - }); - }, - getContainerDisplayName: (d) => { - return (d?.name ?? '[unnamed]') + ((d.items && d.items.length > 0) ? ` (${d.items.length})` : ''); - }, - setCurrentContentContainer: (id) => { - - if (id === 'root') { - _Contents.currentContentContainer = null; - } else { - _Contents.currentContentContainer = { id: id }; - } - - LSWrapper.setItem(_Contents.currentContentContainerKey, _Contents.currentContentContainer); - }, - displayContainerContents: (id, parentId, nodePath, parents) => { - - _Helpers.fastRemoveAllChildren(_Contents.contentsContents[0]); - - let isRootFolder = (id === 'root'); - let parentIsRoot = (parentId === '#'); - - // store current folder id so we can filter slow requests - _Contents.contentsContents[0].dataset['currentContainer'] = id; - - let handleChildren = (children) => { - - let currentFolder = _Contents.contentsContents[0].dataset['currentContainer']; - - if (currentFolder === id) { - - children.map(_Contents.appendItemOrContainerRow); - - Structr.resize(); - } - }; - - let handleContainerChildren = (containers) => { - - handleChildren(containers); - - _Contents.registerFolderLinks(); - }; - - Command.query('ContentContainer', 1000, 1, 'position', 'asc', { parent: (isRootFolder ? null : id ) }, handleContainerChildren, true, 'ui'); - - _Pager.initPager('contents-items', 'ContentItem', 1, 25, 'position', 'asc'); - _Pager.page['ContentItem'] = 1; - let filterOptions = { - containers: [], - }; - if (!isRootFolder) { - filterOptions.containers = [id]; - } - _Pager.initFilters('contents-items', 'ContentItem', filterOptions); - - let itemsPager = _Pager.addPager('contents-items', _Contents.contentsContents[0], false, 'ContentItem', 'ui', handleChildren, undefined, undefined, undefined, true); - - itemsPager.cleanupFunction = () => { - let toRemove = itemsPager.el.querySelectorAll('.node.item'); - for (let item of toRemove) { - _Helpers.fastRemoveElement(item.closest('tr')); - } - }; - - itemsPager.appendFilterElements(` - Filter: - - - `); - itemsPager.activateFilterElements(); - itemsPager.setIsPaused(false); - itemsPager.refresh(); - - _Contents.insertBreadCrumbNavigation(parents, nodePath, id); - - _Contents.contentsContents.append(` - - - - ${(isRootFolder ? '' : ` - - - - - - - - ` - )} - -
 NameSizeTypeOwnerModified
- `); - - if (!isRootFolder) { - - // allow drop on ".." element - let parentObj = (parentId === 'root') ? { id: parentId, type: 'fake' } : StructrModel.obj(parentId); - - _Dragndrop.contents.enableDroppable(parentObj, _Contents.contentsContents[0].querySelector('#parent-container-link .node')); - } - }, - registerFolderLinks: () => { - - let openTargetNode = (targetId) => { - _Contents.contentTree.jstree('open_node', targetId, () => { - _Contents.contentTree.jstree('activate_node', targetId); - }); - }; - - for (let folderLink of _Contents.contentsContents[0].querySelectorAll('.is-folder.file-icon')) { - - folderLink.addEventListener('click', (e) => { - e.preventDefault(); - e.stopPropagation(); - - let targetId = folderLink.dataset['targetId']; - let parentId = folderLink.dataset['parentId']; - - if (!parentId || _Contents.contentTree.jstree('is_open', parentId)) { - openTargetNode(targetId); - } else { - _Contents.contentTree.jstree('open_node', parentId, openTargetNode); - } - }); - } - }, - insertBreadCrumbNavigation: (parents, nodePath, id) => { - - if (parents) { - - let preventOldFolderNameInBreadcrumbs = () => { - let modelObj = StructrModel.obj(id); - if (modelObj && modelObj.path) { - nodePath = modelObj.path; - } - }; - preventOldFolderNameInBreadcrumbs(); - - parents = [].concat(parents).reverse().slice(1); - - let pathNames = (nodePath === '/') ? ['/'] : [''].concat(nodePath.slice(1).split('/')); - - _Contents.contentsContents.append(` -
- ${parents.map((parent, idx) => `${pathNames[idx]}/`).join('')}${pathNames.pop()} - -
- `); - - if (id != 'root') { - - Command.getPromise(id, null).then(obj => { - let ctxMenuContainer = _Contents.contentsContents[0].querySelector('.context-menu-container'); - _Entities.appendContextMenuIcon(ctxMenuContainer, obj, true); - }); - } - - $('.breadcrumb-entry').click(function (e) { - e.preventDefault(); - - $('#' + $(this).data('folderId') + '_anchor').click(); - }); - } - }, - appendItemOrContainerRow: (d) => { - - // add container/item to global model - StructrModel.createFromData(d, null, false); - - let tableBody = $('#files-table-body'); - - $(`#row${d.id}`, tableBody).remove(); - - let items = d.items || []; - let containers = d.containers || []; - let size = d.isContentContainer ? containers.length + items.length : (d?.size ?? '-'); - - let rowId = `row${d.id}`; - tableBody.append(``); - - let row = $(`#${rowId}`); - let name = (d?.name ?? '[unnamed]'); - let icon = (d.isContentContainer ? _Icons.iconFolderClosed : _Icons.iconFileTypeEmpty); - - row.append(` - ${_Icons.getSvgIcon(icon, 16, 16)} - -
- -
-
- - ${size} - ${d.type} - ${(d.owner ? (d.owner?.name ?? '[unnamed]') : '')} - ${moment(d.lastModifiedDate).calendar()} - `); - - // Change working dir by click on folder icon - $(`#id_${d.id}.container`).parent().prev().on('click', function(e) { - - e.preventDefault(); - e.stopPropagation(); - - if (d.parentId) { - - _Contents.contentTree.jstree('open_node', $(`#${d.parentId}`), () => { - - if (d.name === '..') { - $(`#${d.parentId}_anchor`).click(); - } else { - $(`#${d.id}_anchor`).click(); - } - }); - - } else { - - $(`#${d.id}_anchor`).click(); - } - - return false; - }); - - let div = Structr.node(d.id); - let iconsContainer = $('.icons-container', div); - - if (!div || !div.length) - return; - - let nameElement = $('b.name_', row); - nameElement[0].textContent = name; - nameElement[0].title = name; - nameElement.off('click').on('click', function(e) { - e.stopPropagation(); - _Entities.makeNameEditable(div); - }); - - // even though it can be a file, enable droppable so we can react - _Dragndrop.contents.enableDroppable(d, div[0]); - - _Dragndrop.enableDraggable(d, div[0], _Dragndrop.dropActions.contents, false, (e) => { - - let draggedElementIsSelected = div.hasClass('selected'); - - if (!draggedElementIsSelected) { - $('.node.selected').removeClass('selected'); - } - - _Contents.selectedElements = draggedElementIsSelected ? $('.node.selected') : []; - - _Dragndrop.contents.draggedEntityIds = draggedElementIsSelected ? [...document.querySelectorAll('.node.selected')].map(x => Structr.getId(x)) : [d.id]; - - let dragIcon = (draggedElementIsSelected && _Dragndrop.contents.draggedEntityIds.length > 1) ? _Icons.iconFilesStack : icon; - let dragCnt = (draggedElementIsSelected && _Dragndrop.contents.draggedEntityIds.length > 1) ? _Dragndrop.contents.draggedEntityIds.length : _Dragndrop.dragEntity.name; - let dragIconHTML = _Icons.getSvgIcon(dragIcon, 20, 20, 'mr-1'); - let dragImgElement = _Helpers.createSingleDOMElementFromHTML(`${dragIconHTML}
${dragCnt}
`); - - // element must be in document to be displayed... - Structr.mainContainer.appendChild(dragImgElement); - - let rect = dragImgElement.getBoundingClientRect(); - - e.dataTransfer.setDragImage(dragImgElement, rect.width, rect.height); - - // ...remove it in the next animation frame - requestAnimationFrame(() => { - dragImgElement.remove(); - }); - }, () => { - - delete _Dragndrop.contents.draggedEntityIds; - }); - - if (!d.isContentContainer) { - - _Contents.appendEditContentItemIcon(iconsContainer, d); - - let dblclickHandler = (e) => { - _Contents.editItem(d); - }; - - if (div) { - let node = div[0].closest('.node'); - node.removeEventListener('dblclick', dblclickHandler); - node.addEventListener('dblclick', dblclickHandler); - } - } - - _Entities.appendContextMenuIcon(iconsContainer[0], d); - _Entities.appendNewAccessControlIcon(iconsContainer, d); - _Entities.makeSelectable(div); - _Elements.contextMenu.enableContextMenuOnElement(div[0], d); - }, - handleMoveObjectsAction: (targetContainerId, draggedObjectIds) => { - - /** - * handles a drop action to a ContentContainer located in the contents area... - * and a drop action to a ContentContainer located in the tree - * - * must take into account the selected elements in the contents area - */ - _Contents.moveObjectsToTargetContainer(targetContainerId, draggedObjectIds).then((info) => { - - if (info.skipped.length > 0) { - let text = `The following containers were not moved to prevent inconsistencies:

${info.skipped.map(id => StructrModel.obj(id)?.name ?? 'unknown').join('
')}`; - new InfoMessage().title('Skipped some objects').text(text).show(); - } - - // only reload whole tree if we moved a container (the name and count is changed via the model) - if (info.movedContentContainer) { - - _Contents.refreshTree(); - } - }); - }, - moveObjectsToTargetContainer: async (targetContainerId, objectIds) => { - - /** - * initial promise.all returns a list of moved object ids - * skipped objects have the "skipped_" prefix before the uuid - * result of the promise is an object telling us the skipped objects, the moved objects, and if there was a folder which has been moved - */ - let skippedPrefix = 'skipped_'; - let movePromises = objectIds.map(objectId => new Promise((resolve, reject) => { - - // prevent moving element to self - if (objectId !== targetContainerId) { - - let nodeToMove = StructrModel.obj(objectId); - - if (nodeToMove.isContentContainer) { - _Entities.setProperty(nodeToMove.id, 'parent', targetContainerId, false, () => { - resolve(objectId); - }); - } else { - _Entities.addToCollection(nodeToMove.id, targetContainerId, 'containers', () => { - resolve(objectId); - }); - } - - } else { - resolve(skippedPrefix + objectId); - } - })); - - return Promise.all(movePromises).then(values => { - - let movedObjectIds = values.filter(v => !v.startsWith(skippedPrefix)); - - return { - movedContentContainer: movedObjectIds.map(id => StructrModel.obj(id)).some(obj => obj?.type === 'ContentContainer'), - moved: movedObjectIds, - skipped: values.filter(v => v.startsWith(skippedPrefix)).map(text => text.slice(skippedPrefix.length)) - }; - }); - }, - checkValueHasChanged: (oldVal, newVal, buttons) => { - - _Helpers.disableElements((newVal === oldVal), ...buttons); - }, - editItem: (item) => { - - let { dialogText } = _Dialogs.custom.openDialog(`Edit ${item.name}`); - - Command.get(item.id, null, (entity) => { - - let saveAndCloseButtonLocal = _Dialogs.custom.updateOrCreateDialogSaveAndCloseButton(['action']); - let saveButtonLocal = _Dialogs.custom.updateOrCreateDialogSaveButton(['action']); - let refreshButton = _Dialogs.custom.appendCustomDialogButton(''); - - _Entities.getSchemaProperties(entity.type, 'custom', (properties) => { - - let props = Object.values(properties); - let nonCypherProperties = props.filter(prop => prop.className !== 'org.structr.core.property.CypherQueryProperty'); - - for (let prop of nonCypherProperties) { - - let isRelated = 'relatedType' in prop; - let key = prop.jsonName; - let isCollection = prop.isCollection || false; - let isReadOnly = prop.readOnly || false; - let isSystem = prop.system || false; - let oldVal = entity[key]; - - let cntr = _Helpers.createSingleDOMElementFromHTML(`
`); - dialogText.appendChild(cntr); - - let div = $(cntr); - - if (prop.type === 'Boolean') { - - div.removeClass('value').append(`
`); - let checkbox = div.find(`input[type="checkbox"].${key}_`); - - Command.getProperty(entity.id, key, (val) => { - - if (val) { - checkbox.prop('checked', true); - } - - if ((!isReadOnly || StructrWS.isAdmin) && !isSystem) { - - checkbox.on('change', function() { - let checked = checkbox.prop('checked'); - _Contents.checkValueHasChanged(oldVal, checked || false, [saveButtonLocal, saveAndCloseButtonLocal]); - }); - - } else { - - checkbox.prop('disabled', 'disabled'); - checkbox.addClass('readOnly'); - checkbox.addClass('disabled'); - } - }); - - } else if (prop.type === 'Date' && !isReadOnly) { - - div.append('
'); - let valueInput = _Entities.appendDatePicker($('.value-container', div), entity, key, prop.format || "yyyy-MM-dd'T'HH:mm:ssZ"); - - valueInput.on('change', function(e) { - if (e.keyCode !== 27) { - Command.get(entity.id, key, (newEntity) => { - _Contents.checkValueHasChanged(newEntity[key], valueInput.val() || null, [saveButtonLocal, saveAndCloseButtonLocal]); - }); - } - }); - - } else if (isRelated) { - - let relatedNodesList = $(` - - `); - div.append(relatedNodesList); - - $(relatedNodesList).children('.add').on('click', function() { - - let { dialogText } = _Dialogs.custom.openDialog(`Add ${prop.type}`, () => { - _Contents.editItem(item); - }); - - _Entities.displaySearch(entity.id, key, prop.type, $(dialogText), isCollection); - }); - - if (entity[key]) { - - let relatedNodes = $('.related-nodes', div); - - if (!isCollection) { - - let nodeId = entity[key].id || entity[key]; - - Command.get(nodeId, 'id,type,tag,isContent,content,name', (node) => { - - _Entities.appendRelatedNode(relatedNodes, node, (nodeEl) => { - - $('.remove', nodeEl).on('click', function(e) { - e.preventDefault(); - - _Entities.setProperty(entity.id, key, null, false, (newVal) => { - - if (!newVal) { - - _Helpers.blinkGreen(relatedNodes); - _Dialogs.custom.showAndHideInfoBoxMessage(`Related node "${node.name || node.id}" was removed from property "${key}".`, 'success', 2000, 1000); - nodeEl.remove(); - - } else { - - _Helpers.blinkRed(relatedNodes); - } - }); - - return false; - }); - }); - }); - - } else { - - entity[key].forEach(function(obj) { - - let nodeId = obj.id || obj; - - Command.get(nodeId, 'id,type,tag,isContent,content,name', (node) => { - - _Entities.appendRelatedNode(relatedNodes, node, (nodeEl) => { - - $('.remove', nodeEl).on('click', function(e) { - e.preventDefault(); - - Command.removeFromCollection(entity.id, key, node.id, function() { - let nodeEl = $('._' + node.id, relatedNodes); - nodeEl.remove(); - _Helpers.blinkGreen(relatedNodes); - _Dialogs.custom.showAndHideInfoBoxMessage(`Related node "${node.name || node.id}" was removed from property "${key}".`, 'success', 2000, 1000); - }); - return false; - }); - }); - }); - }); - } - } - - } else { - - if (prop.contentType && prop.contentType === 'text/html') { - - div.append(`
${oldVal || ''}
`); - let editArea = $('.edit-area', div); - - editArea.trumbowyg({ - //btns: ['strong', 'em', '|', 'insertImage'], - //autogrow: true - }).on('tbwchange', function() { - Command.get(entity.id, key, (newEntity) => { - _Contents.checkValueHasChanged(newEntity[key], editArea.trumbowyg('html') || null, [saveButtonLocal, saveAndCloseButtonLocal]); - }); - }).on('tbwpaste', function() { - Command.get(entity.id, key, (newEntity) => { - _Contents.checkValueHasChanged(newEntity[key], editArea.trumbowyg('html') || null, [saveButtonLocal, saveAndCloseButtonLocal]); - }); - }); - - } else { - - div.append('
'); - let valueContainer = $('.value-container', div); - let valueInput; - - valueContainer.append(_Helpers.formatValueInputField(key, oldVal, false, prop.readOnly, prop.format === 'multi-line')); - valueInput = valueContainer.find(`[name=${key}]`); - - valueInput.on('keyup', function(e) { - if (e.keyCode !== 27) { - Command.get(entity.id, key, (newEntity) => { - _Contents.checkValueHasChanged(newEntity[key], valueInput.val() || null, [saveButtonLocal, saveAndCloseButtonLocal]); - }); - } - }); - } - } - } - - }, true); - - saveButtonLocal.addEventListener('click', (e) => { - - Structr.ignoreKeyUp = false; - - e.preventDefault(); - e.stopPropagation(); - - _Entities.getSchemaProperties(entity.type, 'custom', (properties) => { - - let props = Object.values(properties); - let nonCypherProperties = props.filter(prop => prop.className !== 'org.structr.core.property.CypherQueryProperty'); - - for (let prop of nonCypherProperties) { - - let key = prop.jsonName; - let newVal; - let oldVal = entity[key]; - - if (prop.contentType && prop.contentType === 'text/html') { - newVal = $(`#prop-${key} .edit-area`).trumbowyg('html') || null; - } else if (prop.propertyType === 'Boolean') { - newVal = $(`#prop-${key} .value-container input`).prop('checked') || false; - } else { - if (prop.format === 'multi-line') { - newVal = $(`#prop-${key} .value-container textarea`).val() || null; - } else { - newVal = $(`#prop-${key} .value-container input`).val() || null; - } - } - - if (!prop.relatedType && newVal !== oldVal) { - - Command.setProperty(entity.id, key, newVal, false, () => { - - oldVal = newVal; - - _Helpers.disableElements(true, saveButtonLocal, saveAndCloseButtonLocal); - - // update title in list - if (key === 'title') { - let f = $(`#row${entity.id} .item-title b`); - f.text(newVal); - _Helpers.blinkGreen(f); - } - }); - } - } - - window.setTimeout(() => { - refreshButton.click(); - }, 500); - - }, true); - - _Helpers.disableElements(true, saveButtonLocal, saveAndCloseButtonLocal); - }); - - saveAndCloseButtonLocal.addEventListener('click', (e) => { - e.stopPropagation(); - saveButtonLocal.click(); - window.setTimeout(() => { - _Dialogs.custom.clickDialogCancelButton(); - }, 1000); - }); - - refreshButton.addEventListener('click', (e) => { - e.stopPropagation(); - _Contents.editItem(item); - }); - - }, 'all'); - }, - displaySearchResultsForURL: (url) => { - - $('#search-results').remove(); - _Contents.contentsContents.append('
'); - - let searchString = Structr.functionBar.querySelector('.search').value; - let container = $('#search-results'); - _Contents.contentsContents.on('scroll', () => { - window.history.pushState('', '', '#contents'); - }); - - fetch(url).then(async response => { - - let data = await response.json(); - - if (response.ok) { - - if (!data.result || data.result.length === 0) { - - container.append(` -

No results for "${searchString}"

-

Press ESC or click here to clear empty result list.

- `); - - $('.clear-results', container).on('click', _Contents.clearSearch); - - } else { - - container.append(` -

${data.result.length} search results:

- - - - - - - - - ${data.result.map(d => ` - - - - - - `).join('')} - -
TypeName
${_Icons.getSvgIcon((d.isContentContainer ? _Icons.iconFolderClosed : _Icons.iconFileTypeEmpty), 16, 16)} ${d.type}${(d.isFile && d.contentType ? ` (${d.contentType})` : '')}${d.name}
- `); - } - } - }); - }, - appendEditContentItemIcon: (parent, d) => { - - let iconClass = 'svg_edit_item_icon'; - - let icon = $('.' + iconClass, parent); - if (!(icon && icon.length)) { - - icon = $(_Icons.getSvgIcon(_Icons.iconPencilEdit, 16, 16, _Icons.getSvgIconClassesNonColorIcon([iconClass, 'node-action-icon']))); - parent.append(icon); - - icon.on('click', (e) => { - e.stopPropagation(); - _Contents.editItem(d); - }); - } - - return icon; - }, - - templates: { - contents: config => ` - - -
-
- -
-
- -
-
- -
-
- -
-
-
- `, - functions: config => ` -
- -
- - - - - - - - - -
-
- `, - } -}; \ No newline at end of file diff --git a/structr-base/src/main/resources/structr/js/dragndrop.js b/structr-base/src/main/resources/structr/js/dragndrop.js index 02b254d584..c429f33a2c 100644 --- a/structr-base/src/main/resources/structr/js/dragndrop.js +++ b/structr-base/src/main/resources/structr/js/dragndrop.js @@ -845,173 +845,6 @@ let _Dragndrop = { }); }, }, - contents: { - enableDroppable: (entity, node) => { - - let entityIsContentContainer = (entity.isContentContainer === true || entity.id === 'root'); - let cnt = 0; - - node.addEventListener('dragenter', (e) => { - - cnt++; - if (!_Dragndrop.contents.draggedEntityIds || !_Dragndrop.dragActive) { - return; - } - - e.stopPropagation(); - e.preventDefault(); - - _Dragndrop.clearDragNDropClasses(); - - node.classList.add(_Dragndrop.cssClasses.dragOverClass); - - if (entityIsContentContainer) { - - e.dataTransfer.dropEffect = 'copy'; - node.classList.add(_Dragndrop.cssClasses.backgroundNodeHighlight); - _Dragndrop.setDragHover(node); - - } else { - - e.dataTransfer.dropEffect = 'none'; - node.classList.add(_Dragndrop.cssClasses.backgroundDropForbidden); - node.classList.add(_Dragndrop.cssClasses.dragNodeDropNotPossibleClass); - } - }); - - node.addEventListener('dragover', (e) => { - - if (!_Dragndrop.contents.draggedEntityIds || !_Dragndrop.dragActive) { - return; - } - - if (entityIsContentContainer) { - // preventDefault indicates that this is a valid drop target - e.preventDefault(); - } - - e.stopPropagation(); - }); - - node.addEventListener('dragleave', (e) => { - cnt--; - if (!_Dragndrop.contents.draggedEntityIds || !_Dragndrop.dragActive) { - return; - } - - e.preventDefault(); - e.stopPropagation(); - - if (cnt === 0) { - _Dragndrop.removeBackgroundFromNode(node); - } - }); - - if (entityIsContentContainer) { - - node.addEventListener('drop', async (e) => { - - e.preventDefault(); - e.stopPropagation(); - - await _Dragndrop.dropActions.baseDropAction({ targetEntity: entity }); - - return false; - }); - } - }, - isAnyDraggedEntityAParentOfTarget: (targetEntity) => { - - let anyDraggedElementInTargetHierarchy = false; - let draggedContainerIds = _Dragndrop.contents.draggedEntityIds.filter(id => StructrModel.obj(id).isContentContainer); - - // can only happen for containers - if (draggedContainerIds.length > 0) { - - let curTargetEntity = targetEntity; - while (curTargetEntity && anyDraggedElementInTargetHierarchy === false) { - - anyDraggedElementInTargetHierarchy = draggedContainerIds.includes(curTargetEntity.id); - - // walk up hierarchy - curTargetEntity = StructrModel.obj(curTargetEntity.parent?.id); - } - } - - return anyDraggedElementInTargetHierarchy; - }, - enableTreeElementDroppable: (entity, element) => { - - let highlightElement = element.querySelector('.jstree-wholerow'); - - element.addEventListener('dragenter', (e) => { - - if (!_Dragndrop.contents.draggedEntityIds || !_Dragndrop.dragActive) { - return; - } - - e.stopPropagation(); - e.preventDefault(); - - _Dragndrop.clearDragNDropClasses(); - - element.classList.add(_Dragndrop.cssClasses.dragOverClass); - - let isDraggedElementInTargetHierarchy = _Dragndrop.contents.isAnyDraggedEntityAParentOfTarget(entity); - - if (isDraggedElementInTargetHierarchy) { - - e.dataTransfer.dropEffect = 'none'; - highlightElement.classList.add(_Dragndrop.cssClasses.backgroundDropForbidden); - element.classList.add(_Dragndrop.cssClasses.dragNodeDropNotPossibleClass); - - } else { - - e.dataTransfer.dropEffect = 'move'; - highlightElement.classList.add(_Dragndrop.cssClasses.backgroundNodeHighlight); - _Dragndrop.setDragHover(element); - } - }); - - element.addEventListener('dragover', (e) => { - - if (!_Dragndrop.contents.draggedEntityIds || !_Dragndrop.dragActive) { - return; - } - - let isDraggedElementInTargetHierarchy = _Dragndrop.contents.isAnyDraggedEntityAParentOfTarget(entity); - - if (!isDraggedElementInTargetHierarchy) { - // preventDefault indicates that this is a valid drop target - e.preventDefault(); - } - - e.stopPropagation(); - }); - - element.addEventListener('dragleave', (e) => { - - if (!_Dragndrop.contents.draggedEntityIds || !_Dragndrop.dragActive) { - return; - } - - e.preventDefault(); - e.stopPropagation(); - - // cannot use simple method because this runs after dragenter for next node - _Dragndrop.removeBackgroundFromNode(element); - }); - - element.addEventListener('drop', async (e) => { - e.preventDefault(); - e.stopPropagation(); - - await _Dragndrop.dropActions.baseDropAction({ targetEntity: entity }); - - return false; - }); - }, - }, dropActions: { baseDropAction: async (data) => { @@ -1104,12 +937,6 @@ let _Dragndrop = { _Files.handleMoveObjectsAction(targetId, _Dragndrop.files.draggedEntityIds); } - }, - contents: ({ targetEntity }) => { - - let targetId = (targetEntity.type === 'fake' && targetEntity.id === 'root') ? null : targetEntity.id; - - _Contents.handleMoveObjectsAction(targetId, _Dragndrop.contents.draggedEntityIds); } } }; \ No newline at end of file diff --git a/structr-base/src/main/resources/structr/js/init.js b/structr-base/src/main/resources/structr/js/init.js index fd06f7c6f3..75b472a2b6 100644 --- a/structr-base/src/main/resources/structr/js/init.js +++ b/structr-base/src/main/resources/structr/js/init.js @@ -961,12 +961,15 @@ let Structr = { if (menuConfig) { for (let entry of menuConfig.main) { - hamburger.before(menu.querySelector(`li[data-name="${entry}"]`)) + hamburger.before(menu.querySelector(`li[data-name="${entry}"]`)); } // sort submenu for (let entry of menuConfig.sub) { - submenu.querySelector('li:last-of-type').after(menu.querySelector(`li[data-name="${entry}"]`)) + let element = submenu.querySelector('li:last-of-type'); + if (element.length > 0) { + element.after(menu.querySelector(`li[data-name="${entry}"]`)); + } } } }, @@ -1929,7 +1932,6 @@ let Structr = {