diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c340ced --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# Various temporary and editor files +.fuse_hidden +.sass-cache +bower_components +node_modules +*.DS_Store +/.idea +.remote-sync.json diff --git a/Classes/DataSource/NodeTypeDataSource.php b/Classes/DataSource/NodeTypeDataSource.php new file mode 100644 index 0000000..07f358f --- /dev/null +++ b/Classes/DataSource/NodeTypeDataSource.php @@ -0,0 +1,109 @@ +getContext()->getCurrentSite()->getSiteResourcesPackageKey(); + + $data = []; + foreach($this->getStyleguideObjects($sitePackageKey) as $prototypeName => $styleguideObject) { + $data[] = array( + 'label' => $styleguideObject['title'], + 'value' => $prototypeName, + 'icon' => $styleguideObject['structure']['icon'] + ); + } + + return $data; + } + + /** + * @param $sitePackageKey + * @param $styleguideObject + * @return array + * @throws \Neos\Neos\Domain\Exception + */ + protected function getStyleguideObjects($sitePackageKey): array + { + $fusionAst = $this->fusionService->getMergedFusionObjectTreeForSitePackage($sitePackageKey); + $styleguideObjects = $this->fusionService->getStyleguideObjectsFromFusionAst($fusionAst); + $prototypeStructures = $this->configurationService->getSiteConfiguration($sitePackageKey, 'ui.structure'); + + foreach ($styleguideObjects as $prototypeName => &$styleguideObject) { + $styleguideObject['structure'] = $this->getStructureForPrototypeName($prototypeStructures, $prototypeName); + } + + $hiddenPrototypeNamePatterns = $this->configurationService->getSiteConfiguration($sitePackageKey, 'hiddenPrototypeNamePatterns'); + if (is_array($hiddenPrototypeNamePatterns)) { + $alwaysShowPrototypes = $this->configurationService->getSiteConfiguration($sitePackageKey, 'alwaysShowPrototypes'); + foreach ($hiddenPrototypeNamePatterns as $pattern) { + $styleguideObjects = array_filter( + $styleguideObjects, + function ($prototypeName) use ($pattern, $alwaysShowPrototypes) { + if (in_array($prototypeName, $alwaysShowPrototypes, true)) { + return true; + } + return fnmatch($pattern, $prototypeName) === false; + }, + ARRAY_FILTER_USE_KEY + ); + } + } + return $styleguideObjects; + } + + /** + * Find the matching structure for a prototype + * + * @param $prototypeStructures + * @param $prototypeName + * @return array + */ + protected function getStructureForPrototypeName($prototypeStructures, $prototypeName) + { + foreach ($prototypeStructures as $structure) { + if (preg_match(sprintf('!%s!', $structure['match']), $prototypeName)) { + return $structure; + } + } + + return [ + 'label' => 'Other', + 'icon' => 'icon-question', + 'color' => 'white' + ]; + } +} diff --git a/Configuration/NodeTypes.Content.PrototypeRendererNode.yaml b/Configuration/NodeTypes.Content.PrototypeRendererNode.yaml new file mode 100644 index 0000000..e2971ef --- /dev/null +++ b/Configuration/NodeTypes.Content.PrototypeRendererNode.yaml @@ -0,0 +1,55 @@ +'CodeQ.NeosMonocleRenderer:Content.MonocleRendererNode': + label: "${String.trim(String.substr(q(node).property('prototypeName'), String.lastIndexOf(q(node).property('prototypeName'), '.') + 1))}" + superTypes: + 'Neos.Neos:Content': true + ui: + label: i18n + icon: 'icon-code' + position: 900 + inspector: + groups: + prototype: + label: i18n + icon: 'icon-cogs' + position: 10 + creationDialog: + elements: + prototypeName: + type: string + ui: + label: i18n + editor: 'Content/Inspector/Editors/SelectBoxEditor' + editorOptions: + dataSourceIdentifier: 'codeq-neosmonoclerenderer-nodetype' + validation: + 'Neos.Neos/Validation/NotEmptyValidator': [] + options: + template: + properties: + prototypeName: '${data.prototypeName}' + properties: + prototypeName: + type: string + ui: + label: i18n + reloadIfChanged: true + reloadPageIfChanged: true + inspector: + group: 'prototype' + editor: 'Content/Inspector/Editors/SelectBoxEditor' + editorOptions: + dataSourceIdentifier: 'codeq-neosmonoclerenderer-nodetype' + validation: + 'Neos.Neos/Validation/NotEmptyValidator': [] + json: + type: string + defaultValue: '{}' + ui: + label: i18n + reloadIfChanged: true + inspector: + group: 'prototype' + editor: 'Neos.Neos/Inspector/Editors/CodeEditor' + editorOptions: + buttonLabel: i18n + highlightingMode: 'text/json' diff --git a/Configuration/Policy.yaml b/Configuration/Policy.yaml new file mode 100644 index 0000000..bf5ca06 --- /dev/null +++ b/Configuration/Policy.yaml @@ -0,0 +1,56 @@ +# restrict creation and editing of `CodeQ.NeosMonocleRenderer:Content.PrototypeRendererNode` nodetype to non-admins +privilegeTargets: + 'Neos\ContentRepository\Security\Authorization\Privilege\Node\CreateNodePrivilege': + 'CodeQ.NeosMonocleRenderer:CreatePrototypeRendererNode': + label: 'Create a new PrototypeRendererNode' + matcher: 'createdNodeIsOfType("CodeQ.NeosMonocleRenderer:Content.PrototypeRendererNode")' + 'Neos\ContentRepository\Security\Authorization\Privilege\Node\EditNodePrivilege': + 'CodeQ.NeosMonocleRenderer:EditPrototypeRendererNode': + label: 'Edit a PrototypeRendererNode' + matcher: 'nodeIsOfType("CodeQ.NeosMonocleRenderer:Content.PrototypeRendererNode")' + 'Neos\ContentRepository\Security\Authorization\Privilege\Node\RemoveNodePrivilege': + 'CodeQ.NeosMonocleRenderer:RemovePrototypeRendererNode': + label: 'Remove a PrototypeRendererNode' + matcher: 'nodeIsOfType("CodeQ.NeosMonocleRenderer:Content.PrototypeRendererNode")' + 'Neos\ContentRepository\Security\Authorization\Privilege\Node\EditNodePropertyPrivilege': + 'CodeQ.NeosMonocleRenderer:EditPrototypeRendererNodeProperty': + label: 'Edit a PrototypeRendererNode property' + matcher: 'nodeIsOfType("CodeQ.NeosMonocleRenderer:Content.PrototypeRendererNode")' + +roles: + ## + # By default this role is abstract, feel free to use it however you want. + # This role is considered public API + ## + 'CodeQ.NeosMonocleRenderer:PrototypeRendererNodeEditor': + abstract: true + label: 'Prototype Renderer Node Editor' + description: 'A user with this role is able to render any Prototype as content node.' + privileges: + - + privilegeTarget: 'CodeQ.NeosMonocleRenderer:CreatePrototypeRendererNode' + permission: GRANT + - + privilegeTarget: 'CodeQ.NeosMonocleRenderer:EditPrototypeRendererNode' + permission: GRANT + - + privilegeTarget: 'CodeQ.NeosMonocleRenderer:RemovePrototypeRendererNode' + permission: GRANT + - + privilegeTarget: 'CodeQ.NeosMonocleRenderer:EditPrototypeRendererNodeProperty' + permission: GRANT + + 'Neos.Neos:Administrator': + privileges: + - + privilegeTarget: 'CodeQ.NeosMonocleRenderer:CreatePrototypeRendererNode' + permission: GRANT + - + privilegeTarget: 'CodeQ.NeosMonocleRenderer:EditPrototypeRendererNode' + permission: GRANT + - + privilegeTarget: 'CodeQ.NeosMonocleRenderer:RemovePrototypeRendererNode' + permission: GRANT + - + privilegeTarget: 'CodeQ.NeosMonocleRenderer:EditPrototypeRendererNodeProperty' + permission: GRANT diff --git a/Configuration/Settings.yaml b/Configuration/Settings.yaml new file mode 100644 index 0000000..8a266b9 --- /dev/null +++ b/Configuration/Settings.yaml @@ -0,0 +1,13 @@ + +Neos: + Neos: + fusion: + autoInclude: + CodeQ.NeosMonocleRenderer: true + userInterface: + translation: + autoInclude: + CodeQ.NeosMonocleRenderer: + - 'Fusion/*' + - 'NodeTypes/*' + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bdf240d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Roland Schütz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Resources/Private/Fusion/Content/PrototypeRendererNode/MonocleRendererNode.fusion b/Resources/Private/Fusion/Content/PrototypeRendererNode/MonocleRendererNode.fusion new file mode 100644 index 0000000..e461a33 --- /dev/null +++ b/Resources/Private/Fusion/Content/PrototypeRendererNode/MonocleRendererNode.fusion @@ -0,0 +1,53 @@ +prototype(CodeQ.NeosMonocleRenderer:Content.MonocleRendererNode) < prototype(Neos.Neos:ContentComponent) { + @context { + prototypeName = ${String.trim(q(node).property('prototypeName'))} + prototypeProps = ${Json.parse(q(node).property('json') || '{}')} + } + + renderer = Neos.Fusion:Case { + noPrototypeName { + condition = ${!prototypeName && node.context.inBackend} + renderer = CodeQ.PrototypeRendererNode:Content.PrototypeRendererNode.ErrorMessage { + name = 'noPrototypeNameNotice' + } + } +// jsonIsInvalid { +// condition = ${!prototypeProps && node.context.inBackend} +// renderer = Neos.Fusion:Debug { +// foo = ${prototypeProps} +// } +//// renderer = CodeQ.PrototypeRendererNode:Content.PrototypeRendererNode.ErrorMessage { +//// name = 'jsonIsInvalidNotice' +//// } +// } + canRender { + condition = Neos.Fusion:CanRender { + type = ${prototypeName} + } + renderer = Neos.Fusion:Renderer { + type = ${prototypeName} + element.@apply.props = ${prototypeProps} + } + } + prototypeDoesNotExist { + condition = ${node.context.inBackend} + renderer = CodeQ.PrototypeRendererNode:Content.PrototypeRendererNode.ErrorMessage { + name = 'prototypeDoesNotExistNotice' + } + } + frontend { + condition = true + renderer = false + } + } +} + +prototype(CodeQ.PrototypeRendererNode:Content.PrototypeRendererNode.ErrorMessage) < prototype(Neos.Fusion:Component) { + name = '' + + renderer = afx` +
+ {I18n.translate('CodeQ.PrototypeRendererNode:NodeTypes.Content.PrototypeRendererNode:' + props.name)} +
+ ` +} diff --git a/Resources/Private/Fusion/Root.fusion b/Resources/Private/Fusion/Root.fusion new file mode 100644 index 0000000..c84160b --- /dev/null +++ b/Resources/Private/Fusion/Root.fusion @@ -0,0 +1,9 @@ +############################################## +############################################## +#### #### +#### !!!DO NOT EDIT THIS FILE!!!! #### +#### #### +############################################## +############################################## + +include: **/*.fusion diff --git a/Resources/Private/Translations/de/NodeTypes/Content/MonocleRendererNode.xlf b/Resources/Private/Translations/de/NodeTypes/Content/MonocleRendererNode.xlf new file mode 100644 index 0000000..efb0332 --- /dev/null +++ b/Resources/Private/Translations/de/NodeTypes/Content/MonocleRendererNode.xlf @@ -0,0 +1,44 @@ + + + + + + Monocle Renderer + Monocle Renderer + + + Prototype Name + Prototype Name + + + Prototype Name + Prototype Name + + + Prototype Props as JSON + Prototype Props als JSON + + + Edit Props + Props bearbeiten + + + Settings + Einstellungen + + + + Please define a prototype name to render. + Bitte gib einen Prototype-Namen ein. + + + Your JSON for the Prototype props is invalid + Das Prototype-Props JSON ist ungültig. + + + The Prototype does not exist. + Der Prototype existiert nicht. + + + + diff --git a/Resources/Private/Translations/en/NodeTypes/Content/MonocleRendererNode.xlf b/Resources/Private/Translations/en/NodeTypes/Content/MonocleRendererNode.xlf new file mode 100644 index 0000000..12dd9d9 --- /dev/null +++ b/Resources/Private/Translations/en/NodeTypes/Content/MonocleRendererNode.xlf @@ -0,0 +1,35 @@ + + + + + + Monocle Renderer + + + Prototype Name + + + Prototype Name + + + Prototype Props as JSON + + + Edit Props + + + Settings + + + + Please define a prototype name to render. + + + Your JSON for the Prototype props is invalid + + + The Prototype does not exist. + + + + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..036172a --- /dev/null +++ b/composer.json @@ -0,0 +1,39 @@ +{ + "name": "codeq/neos-monocle-renderer", + "description": "Monocle Renderer Content Node for Neos CMS", + "type": "neos-package", + "license": "MIT", + "keywords": ["flow", "neos", "fusion", "html"], + "require": { + "neos/neos": "~3.2 || ~4.0 || ~5.0 || ~7.0 || dev-master", + "neos/fusion-afx": "^1.0 ||~7.0 || dev-master", + "flowpack/nodetemplates": "~1.0 || dev-master", + "sitegeist/monocle": "^7.5" + }, + "authors": [ + { + "name": "Felix Gradinaru", + "email": "fg@codeq.at", + "homepage": "https://codeq.at", + "role": "Developer" + } + ], + "archive": { + "exclude": [ + ".editorconfig", + ".gitattributes", + ".github", + ".gitignore" + ] + }, + "autoload": { + "psr-4": { + "CodeQ\\NeosMonocleRenderer\\": "Classes" + } + }, + "extra": { + "neos": { + "package-key": "CodeQ.NeosMonocleRenderer" + } + } +}