From 8e50788fc151ddd6554899c947feeba240ebef18 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Wed, 5 Jun 2024 15:45:44 +0200 Subject: [PATCH 001/122] FEATURE: New workspace overview UX --- .../Controller/WorkspaceController.php | 1 + .../Configuration/Settings.Neos.yaml | 3 + Neos.Workspace.Ui/Configuration/Views.yaml | 7 ++ .../Fusion/Components/FlashMessages.fusion | 31 +++++++ .../Private/Fusion/Components/Footer.fusion | 26 ++++++ .../Private/Fusion/Components/Icon.fusion | 21 +++++ .../Fusion/Components/WorkspaceTable.fusion | 38 ++++++++ .../Components/WorkspaceTableRow.fusion | 92 +++++++++++++++++++ .../Components/WorkspaceTreeNode.fusion | 29 ++++++ .../Resources/Private/Fusion/Root.fusion | 1 + .../Private/Fusion/Views/Index.fusion | 20 ++++ .../Resources/Public/Styles/Module.css | 78 ++++++++++++++++ 12 files changed, 347 insertions(+) create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/FlashMessages.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/Footer.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/Icon.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTable.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTreeNode.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Root.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Views/Index.fusion create mode 100644 Neos.Workspace.Ui/Resources/Public/Styles/Module.css diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 3c2a6d93d14..188158e0251 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -156,6 +156,7 @@ public function indexAction(): void $this->view->assign('userWorkspace', $userWorkspace); $this->view->assign('workspacesAndChangeCounts', $workspacesAndCounts); + $this->view->assign('flashMessages', $this->controllerContext->getFlashMessageContainer()->getMessagesAndFlush()); } diff --git a/Neos.Workspace.Ui/Configuration/Settings.Neos.yaml b/Neos.Workspace.Ui/Configuration/Settings.Neos.yaml index a52bfc23045..501297b6ca8 100644 --- a/Neos.Workspace.Ui/Configuration/Settings.Neos.yaml +++ b/Neos.Workspace.Ui/Configuration/Settings.Neos.yaml @@ -9,6 +9,9 @@ Neos: description: 'Neos.Workspace.Ui:Main:workspaces.description' icon: fas fa-th-large mainStylesheet: 'Lite' + additionalResources: + styleSheets: + 'Neos.Workspace.Ui': 'resource://Neos.Workspace.Ui/Public/Styles/Module.css' userInterface: translation: diff --git a/Neos.Workspace.Ui/Configuration/Views.yaml b/Neos.Workspace.Ui/Configuration/Views.yaml index 2754bc057b0..dff1a09d1d9 100644 --- a/Neos.Workspace.Ui/Configuration/Views.yaml +++ b/Neos.Workspace.Ui/Configuration/Views.yaml @@ -4,3 +4,10 @@ partialRootPaths: - 'resource://Neos.Workspace.Ui/Private/Partials' - 'resource://Neos.Neos/Private/Partials' + +- requestFilter: 'isPackage("Neos.Workspace.Ui") && isController("Workspace") && isFormat("html") && isAction("index")' + viewObjectName: 'Neos\Fusion\View\FusionView' + options: + fusionPathPatterns: + - 'resource://Neos.Fusion/Private/Fusion' + - 'resource://Neos.Workspace.Ui/Private/Fusion' diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/FlashMessages.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/FlashMessages.fusion new file mode 100644 index 00000000000..23233b73c40 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/FlashMessages.fusion @@ -0,0 +1,31 @@ +prototype(Neos.Workspace.Ui:Component.FlashMessages) < prototype(Neos.Fusion:Component) { + flashMessages = ${[]} + + renderer = afx` +
+ + + +
+ ` +} + +prototype(Neos.Workspace.Ui:Component.FlashMessages.Message) < prototype(Neos.Fusion:Component) { + message = ${{}} + + severity = ${String.toLowerCase(this.message.severity)} + severity.@process.replaceOKStatus = ${value == 'ok' ? 'success' : value} + severity.@process.replaceNoticeStatus = ${value == 'notice' ? 'info' : value} + + renderer = afx` +
+
+ +
+ {props.message.title || props.message.message} +
+
{props.message.message}
+
+
+ ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Footer.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Footer.fusion new file mode 100644 index 00000000000..672b880fca9 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Footer.fusion @@ -0,0 +1,26 @@ +prototype(Neos.Workspace.Ui:Component.Footer) < prototype(Neos.Fusion:Component) { + workspaceCount = ${{}} + + @private { + newAction = Neos.Fusion:UriBuilder { + action = 'new' + } + } + + renderer = afx` + + ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Icon.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Icon.fusion new file mode 100644 index 00000000000..321236dd664 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Icon.fusion @@ -0,0 +1,21 @@ +prototype(Neos.Workspace.Ui:Component.Icon) < prototype(Neos.Fusion:Component) { + icon = '' + secondaryIcon = '' + spin = false + rotate = false + style = '' + + renderer = afx` + + + + + + ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTable.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTable.fusion new file mode 100644 index 00000000000..4b85b2f426d --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTable.fusion @@ -0,0 +1,38 @@ +prototype(Neos.Workspace.Ui:Component.WorkspaceTable) < prototype(Neos.Fusion:Component) { + userWorkspace = '' + workspacesAndChangeCounts = ${{}} + + renderer = afx` + + + + + + + + + + + + + + + + +
+ + {I18n.translate('table.header.description', 'Description', [], 'Main', 'Neos.Workspace.Ui')}{I18n.translate('table.header.owner', 'Owner', [], 'Main', 'Neos.Workspace.Ui')} + + {I18n.translate('table.header.status', 'Status', [], 'Main', 'Neos.Workspace.Ui')}{I18n.translate('table.header.changes', 'Changes', [], 'Main', 'Neos.Workspace.Ui')}{I18n.translate('table.header.actions', 'Actions', [], 'Main', 'Neos.Workspace.Ui')}
+ ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion new file mode 100644 index 00000000000..e26f364d42a --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion @@ -0,0 +1,92 @@ +prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion:Component) { + userWorkspace = '' + workspaceName = '' + workspaceData = ${{}} + level = 0 + + @private { + workspaceStatus = Neos.Fusion:Case { + personal-workspace { + condition = ${props.workspaceName == props.userWorkspace.workspaceName.value} + renderer = 'personal-workspace' + } + stale { + // TODO: Calculate stale status based on last change data + condition = false + renderer = 'stale' + } + withAcl { + // TODO: Calculate acl status on whether the workspace is shared with selected users + condition = false + renderer = 'with-acl' + } + private { + condition = ${props.workspaceData.workspace.workspaceOwner} + renderer = 'private' + } + default { + condition = true + renderer = 'internal' + } + } + workspaceStatusLabel = Neos.Fusion:Match { + @subject = ${private.workspaceStatus} + personal-workspace = ${I18n.translate('badge.isPersonalWorkspace', 'This is your personal workspace', [], 'Main', 'Neos.Workspace.Ui')} + stale = ${I18n.translate('badge.isStale', 'This workspace has not been used for a long time', [], 'Main', 'Neos.Workspace.Ui')} + with-acl = ${I18n.translate('table.column.access.acl', 'This workspace is owned by ' + props.workspaceData.workspaceOwnerHumanReadable + ' but allows access to additional users', [], 'Main', 'Neos.Workspace.Ui')} + private = ${I18n.translate('table.column.access.private', 'This workspace is owned by ' + props.workspaceData.workspaceOwnerHumanReadable, {}, 'Main', 'Neos.Workspace.Ui')} + @default = ${I18n.translate('table.column.access.internal', 'Internal workspace', [], 'Main', 'Neos.Workspace.Ui')} + } + workspaceStatusIcon = Neos.Fusion:Match { + @subject = ${private.workspaceStatus} + internal = 'users' + with-acl = 'user-plus' + @default = 'user' + } + showWorkspaceUri = Neos.Fusion:UriBuilder { + action = 'show' + arguments { + workspace = ${props.workspaceName} + } + } + } + + renderer = afx` + + + + + + 1}> + {props.workspaceData.workspace.workspaceTitle.value} + + ({I18n.translate('badge.isUserWorkspace', 'This is your workspace', [], 'Main', 'Neos.Workspace.Ui')}) + + + {props.workspaceData.workspace.workspaceDescription.value || '–'} + {props.workspaceData.workspaceOwnerHumanReadable || '–'} + – + {props.workspaceData.workspace.status.value || '–'} + – + + + {I18n.translate('table.column.action.show', 'Show', [], 'Main', 'Neos.Workspace.Ui')} + + + + + ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTreeNode.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTreeNode.fusion new file mode 100644 index 00000000000..689a45e37dd --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTreeNode.fusion @@ -0,0 +1,29 @@ +prototype(Neos.Workspace.Ui:Component.WorkspaceTreeNode) < prototype(Neos.Fusion:Component) { + userWorkspace = '' + workspaceName = '' + workspacesAndChangeCounts = ${{}} + level = 0 + + @private { + workspaceData = ${props.workspacesAndChangeCounts[props.workspaceName]} + subWorkspacesAndChangeCounts = ${Array.filter(props.workspacesAndChangeCounts, (workspaceData) => workspaceData.workspace.baseWorkspaceName.value == props.workspaceName)} + } + + renderer = afx` + + + + + ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Root.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Root.fusion new file mode 100644 index 00000000000..a92a174cc05 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Root.fusion @@ -0,0 +1 @@ +include: **/* diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Index.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Index.fusion new file mode 100644 index 00000000000..301812eefc3 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Index.fusion @@ -0,0 +1,20 @@ +Neos.Workspace.Ui.WorkspaceController { + index = Neos.Fusion:Component { + userWorkspace = ${userWorkspace} + workspacesAndChangeCounts = ${workspacesAndChangeCounts} + flashMessages = ${flashMessages} + + renderer = afx` +
+
+ + + +
+
+ ` + } +} diff --git a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css new file mode 100644 index 00000000000..19d3d8af7a5 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css @@ -0,0 +1,78 @@ +.workspace-table { + --column-color: var(--grayDark); + --indent-level: 0; + + margin-top: 1em; + border-spacing: 0; + position: relative; + width: 100%; +} + +.workspace-table th { + padding: 0.5em; + position: sticky; + top: 40px; + user-select: none; + background: var(--grayDark); + border-bottom: 1px solid var(--grayDark); + z-index: 1; +} + +.workspace-table td { + padding: 0 0.5em; + border-top: 1px solid var(--grayDark); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + background-color: var(--column-color); + min-height: 40px; +} + +.workspace-type-column { + padding: 0 0.5ch; + text-align: center; +} + +.workspace-type-column > * { + width: 20px !important; +} + + /** + * The arrow icon to show sub-workspace indentation + */ +.workspace-label-column .fas { + transform: rotate(90deg); + vertical-align: middle; + margin-left: calc((var(--indent-level) - 2) * 1.2ch); + margin-right: 0.5ch; +} + +.workspace__info-text { + font-size: 0.8em; + font-style: italic; + margin-left: 0.5ch; + user-select: none; +} + +.icon-button { + background: none; + border: none; + padding: 0; + margin: 0; + cursor: pointer; + outline: none; + color: var(--textOnGray); +} + +.icon--secondary { + left: 0.5em; + top: 0.5em; + text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.4); +} + +.workspace--stale { + --column-color: var(--grayDark); +} +.workspace--personal-workspace { + --column-color: var(--blueDark); +} From 4473b686a999df49941f99632f4cb2af75d826cc Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Mon, 17 Jun 2024 15:50:13 +0200 Subject: [PATCH 002/122] TASK: Improve workspace column styling --- .../Components/WorkspaceTableRow.fusion | 4 +-- .../Resources/Public/Styles/Module.css | 25 ++++++++++++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion index e26f364d42a..3372935ad5d 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion @@ -66,12 +66,12 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion ({I18n.translate('badge.isUserWorkspace', 'This is your workspace', [], 'Main', 'Neos.Workspace.Ui')}) - {props.workspaceData.workspace.workspaceDescription.value || '–'} + {props.workspaceData.workspace.workspaceDescription.value || '–'} {props.workspaceData.workspaceOwnerHumanReadable || '–'} – {props.workspaceData.workspace.status.value || '–'} – - + Date: Mon, 17 Jun 2024 16:48:39 +0200 Subject: [PATCH 003/122] TASK: Implement workspace delete modal as popover --- Neos.Workspace.Ui/Configuration/Views.yaml | 1 + .../Fusion/Components/Modals/Delete.fusion | 50 +++++++++++++++++ .../Components/WorkspaceTableRow.fusion | 31 ++++++++--- .../Resources/Public/Styles/Module.css | 53 +++++++++++++++++++ 4 files changed, 127 insertions(+), 8 deletions(-) create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Delete.fusion diff --git a/Neos.Workspace.Ui/Configuration/Views.yaml b/Neos.Workspace.Ui/Configuration/Views.yaml index dff1a09d1d9..170fe56e3ed 100644 --- a/Neos.Workspace.Ui/Configuration/Views.yaml +++ b/Neos.Workspace.Ui/Configuration/Views.yaml @@ -10,4 +10,5 @@ options: fusionPathPatterns: - 'resource://Neos.Fusion/Private/Fusion' + - 'resource://Neos.Fusion.Form/Private/Fusion' - 'resource://Neos.Workspace.Ui/Private/Fusion' diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Delete.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Delete.fusion new file mode 100644 index 00000000000..bb360fd3417 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Delete.fusion @@ -0,0 +1,50 @@ +prototype(Neos.Workspace.Ui:Component.Modal.Delete) < prototype(Neos.Fusion:Component) { + workspaceName = '' + + @private { + popoverId = ${'workspace-modal-delete-' + props.workspaceName} + } + + renderer = afx` +
+
+ +
+ {I18n.translate('workspaces.dialog.confirmWorkspaceDeletion', 'Do you really want to delete the workspace?', [props.workspaceTitle], 'Main', 'Neos.Workspace.Ui')} +
+
+
+

{I18n.translate('workspaces.dialog.thisWillDeleteTheWorkspace', 'This will delete the workspace including all unpublished content. This operation cannot be undone.', [], 'Modules', 'Neos.Workspace.Ui')}

+
+
+ + + + +
+
+ ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion index 3372935ad5d..9726e28edf8 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion @@ -2,6 +2,7 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion userWorkspace = '' workspaceName = '' workspaceData = ${{}} + canManage = false level = 0 @private { @@ -73,19 +74,33 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion –
- {I18n.translate('table.column.action.show', 'Show', [], 'Main', 'Neos.Workspace.Ui')} + {I18n.translate('table.column.action.show', 'Show', [], 'Main', 'Neos.Workspace.Ui')} - + + ` diff --git a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css index 4c635287f38..69ef42ec936 100644 --- a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css +++ b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css @@ -89,3 +89,56 @@ td.workspace-action-column { .workspace--personal-workspace { --column-color: var(--blueDark); } + +/** + * Popover styles + */ +[popover] { + /* Undo css reset */ + margin: auto; + + /* Component styles */ + color: white; + background: var(--grayDark); + border: 1px solid var(--grayLight); + padding: 0; + border-radius: 0; + outline: none; +} + +[popover]::backdrop { + backdrop-filter: blur(3px); +} + +[popover] header { + font-size: calc(var(--generalFontSize) + 2px); + margin: var(--defaultMargin); + line-height: calc(var(--generalFontSize) + 4px); + box-sizing: border-box; +} + +[popover] section { + margin: var(--defaultMargin); + color: var(--textSubtleLight); +} + +[popover] .neos-close { + color: white; + font-size: calc(var(--generalFontSize) + 4px); + background: transparent; + width: var(--unit); + height: var(--unit); + position: absolute; + right: 0; + top: 0; + border-left: none; + text-shadow: none; +} + +[popover] footer { + background: transparent; + margin: var(--defaultMargin); + display: flex; + gap: var(--spacing-Full); + justify-content: flex-end; +} From cfdf61f066e6591b1891adbee25ec7a3a6809602 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Mon, 17 Jun 2024 16:53:29 +0200 Subject: [PATCH 004/122] TASK: Extract review script from show template --- .../Private/Templates/Workspace/Show.html | 60 +------------------ .../Resources/Public/Scripts/Review.js | 53 ++++++++++++++++ 2 files changed, 54 insertions(+), 59 deletions(-) create mode 100644 Neos.Workspace.Ui/Resources/Public/Scripts/Review.js diff --git a/Neos.Workspace.Ui/Resources/Private/Templates/Workspace/Show.html b/Neos.Workspace.Ui/Resources/Private/Templates/Workspace/Show.html index 5ae008b0a30..54d73464fd9 100644 --- a/Neos.Workspace.Ui/Resources/Private/Templates/Workspace/Show.html +++ b/Neos.Workspace.Ui/Resources/Private/Templates/Workspace/Show.html @@ -1,9 +1,6 @@ {namespace neos=Neos\Neos\ViewHelpers} - - - @@ -95,6 +92,7 @@ +
@@ -136,62 +134,6 @@
- - - {neos:backend.translate(id: 'workspaces.unpublishedChanges', source: 'Main', package: 'Neos.Workspace.Ui', arguments: {0: selectedWorkspaceLabel.value})} diff --git a/Neos.Workspace.Ui/Resources/Public/Scripts/Review.js b/Neos.Workspace.Ui/Resources/Public/Scripts/Review.js new file mode 100644 index 00000000000..71fb501b470 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Public/Scripts/Review.js @@ -0,0 +1,53 @@ +window.addEventListener('DOMContentLoaded', (event) => { + jQuery(function ($) { + jQuery('#check-all').change(function () { + var value = false; + if (jQuery(this).is(':checked')) { + value = true; + jQuery('.batch-action').removeClass('neos-hidden').removeClass('neos-discardAllChanges').removeClass('neos-disabled').removeAttr('disabled'); + jQuery('.review-button-action').addClass('neos-hidden').addClass('neos-disabled').attr('disabled', 'disabled'); + } else { + jQuery('.batch-action').addClass('neos-hidden').addClass('neos-disabled').attr('disabled', 'disabled'); + jQuery('.review-button-action').removeClass('neos-hidden').removeClass('neos-disabled').removeAttr('disabled', 'disabled'); + } + jQuery('tbody input[type="checkbox"]').prop('checked', value); + }); + + jQuery('.neos-check-document').change(function () { + var documentIdentifier = jQuery(this).val(); + var checked = jQuery(this).prop('checked'); + jQuery(this).closest('table').find('tr.neos-change.document-' + documentIdentifier + ' td.check input').prop('checked', checked); + }); + + jQuery('tbody input[type="checkbox"]').change(function () { + if (jQuery(this).closest('tr').data('ismoved') === true || jQuery(this).closest('tr').data('isnew') === true) { + var currentNodePath = jQuery(this).closest('tr').attr('data-nodepath') + '/'; + var checked = jQuery(this).prop('checked'); + + function nodePathStartsWith(nodePath) { + return function (index, element) { + return nodePath.indexOf(jQuery(element).data('nodepath')) === 0; + } + } + + var movedOrNewParentDocuments = jQuery(this).closest('table').find('.neos-document[data-ismoved="true"], .neos-document[data-isnew="true"]').filter(nodePathStartsWith(currentNodePath)); + jQuery(movedOrNewParentDocuments).each(function (index, movedParentDocument) { + jQuery('tr[data-nodepath^="' + jQuery(movedParentDocument).data('nodepath') + '"] td.check input').prop('checked', checked); + }); + } + + if (jQuery('tbody input[type="checkbox"]:checked').length > 0) { + jQuery('.batch-action').removeClass('neos-hidden').removeClass('neos-disabled').removeAttr('disabled'); + jQuery('.review-button-action').addClass('neos-hidden').addClass('neos-disabled').attr('disabled', 'disabled'); + } else { + jQuery('.batch-action').addClass('neos-hidden').addClass('neos-disabled').attr('disabled', 'disabled'); + jQuery('.review-button-action').removeClass('neos-hidden').removeClass('neos-disabled').removeAttr('disabled', 'disabled'); + } + }); + + jQuery('.fold-toggle').click(function () { + jQuery(this).toggleClass('fas fa-chevron-down fas fa-chevron-up'); + jQuery('tr.' + jQuery(this).data('toggle')).toggle(); + }); + }); +}); From 0cb57be423fa8a55dfb3ed91964c91ae0a5e813e Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Mon, 17 Jun 2024 22:41:39 +0200 Subject: [PATCH 005/122] TASK: Refactor create workspace dialog to Fusion --- Neos.Workspace.Ui/Configuration/Views.yaml | 2 +- .../Private/Fusion/Components/Footer.fusion | 21 +-- .../Private/Fusion/Views/Index.fusion | 53 ++++-- .../Resources/Private/Fusion/Views/New.fusion | 98 +++++++++++ .../Private/Templates/Workspace/Index.html | 159 ------------------ .../Private/Templates/Workspace/New.html | 64 ------- .../Resources/Public/Styles/Module.css | 23 +++ 7 files changed, 160 insertions(+), 260 deletions(-) create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion delete mode 100644 Neos.Workspace.Ui/Resources/Private/Templates/Workspace/Index.html delete mode 100644 Neos.Workspace.Ui/Resources/Private/Templates/Workspace/New.html diff --git a/Neos.Workspace.Ui/Configuration/Views.yaml b/Neos.Workspace.Ui/Configuration/Views.yaml index 170fe56e3ed..2371306a213 100644 --- a/Neos.Workspace.Ui/Configuration/Views.yaml +++ b/Neos.Workspace.Ui/Configuration/Views.yaml @@ -5,7 +5,7 @@ - 'resource://Neos.Workspace.Ui/Private/Partials' - 'resource://Neos.Neos/Private/Partials' -- requestFilter: 'isPackage("Neos.Workspace.Ui") && isController("Workspace") && isFormat("html") && isAction("index")' +- requestFilter: 'isPackage("Neos.Workspace.Ui") && isController("Workspace") && isFormat("html") && (isAction("index") || isAction("new"))' viewObjectName: 'Neos\Fusion\View\FusionView' options: fusionPathPatterns: diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Footer.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Footer.fusion index 672b880fca9..306c206ef7d 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Footer.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Footer.fusion @@ -1,26 +1,9 @@ prototype(Neos.Workspace.Ui:Component.Footer) < prototype(Neos.Fusion:Component) { - workspaceCount = ${{}} - - @private { - newAction = Neos.Fusion:UriBuilder { - action = 'new' - } - } + children = '' renderer = afx` ` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Index.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Index.fusion index 301812eefc3..038cb2d4763 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Index.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Index.fusion @@ -1,20 +1,39 @@ -Neos.Workspace.Ui.WorkspaceController { - index = Neos.Fusion:Component { - userWorkspace = ${userWorkspace} - workspacesAndChangeCounts = ${workspacesAndChangeCounts} - flashMessages = ${flashMessages} +Neos.Workspace.Ui.WorkspaceController.index = Neos.Fusion:Component { + userWorkspace = ${userWorkspace} + workspacesAndChangeCounts = ${workspacesAndChangeCounts} + flashMessages = ${flashMessages} - renderer = afx` -
-
- - - -
-
- ` + totalNumberOfWorkspaces = ${Array.length(this.workspacesAndChangeCounts)} + numberOfPrivateWorkspaces = 0 + numberOfPublicWorkspaces = 0 + + newAction = Neos.Fusion:UriBuilder { + action = 'new' } + + renderer = afx` + +
+
+ +
+
+ + + {I18n.translate('footer.action.create', 'Create new workspace', [], 'Main', 'Neos.Workspace.Ui')} + +
+ {I18n.translate('footer.workspaceCount', props.totalNumberOfWorkspaces + ' workspaces (' + props.numberOfPublicWorkspaces + ' public, ' + props.numberOfPrivateWorkspaces + ' private)', [props.numberOfPublicWorkspaces, props.numberOfPrivateWorkspaces], 'Main', 'Neos.Workspace.Ui')} +
+
+ ` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion new file mode 100644 index 00000000000..ce3615a8d5c --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion @@ -0,0 +1,98 @@ +Neos.Workspace.Ui.WorkspaceController.new = Neos.Fusion:Component { + flashMessages = ${flashMessages} + baseWorkspaceOptions = ${baseWorkspaceOptions} + workspace = ${workspace} + + prototype(Neos.Fusion.Form:LabelRenderer) { + translationPackage = 'Neos.Workspace.Ui' + translationSource = 'Main' + } + + renderer = afx` + +
+

{I18n.translate('workspaces.createNewWorkspace', 'Create new workspace', [], 'Main', 'Neos.Workspace.Ui')}

+ +
+ + + + + + + + + + + + {workspaceTitle} + + + +
+
+ +

+ {I18n.translate('workspaces.workspace.visibility', 'Visibility', [], 'Main', 'Neos.Workspace.Ui')} +

+ + + + + {I18n.translate('workspaces.workspace.visibility.private', 'Private', [], 'Main', 'Neos.Workspace.Ui')} + + {I18n.translate('workspaces.workspace.visibility.private.help', 'Only reviewers and administrators can access and modify this workspace', [], 'Main', 'Neos.Workspace.Ui')} + + + +
+ + + + {I18n.translate('workspaces.workspace.visibility.internal', 'Internal', [], 'Main', 'Neos.Workspace.Ui')} + + {I18n.translate('workspaces.workspace.visibility.internal.help', 'Any logged in editor can see and modify this workspace.', [], 'Main', 'Neos.Workspace.Ui')} + + + +
+
+ + + + + {I18n.translate('workspaces.createWorkspace', 'Create workspace', [], 'Main', 'Neos.Workspace.Ui')} + +
+
+ + + + {I18n.translate('cancel', 'Cancel', [], 'Main', 'Neos.Workspace.Ui')} + + +
+ ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Templates/Workspace/Index.html b/Neos.Workspace.Ui/Resources/Private/Templates/Workspace/Index.html deleted file mode 100644 index d65d207cbd7..00000000000 --- a/Neos.Workspace.Ui/Resources/Private/Templates/Workspace/Index.html +++ /dev/null @@ -1,159 +0,0 @@ -{namespace neos=Neos\Neos\ViewHelpers} - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
 {neos:backend.translate(id: 'workspaces.workspace.title', source: 'Main', package: 'Neos.Workspace.Ui')}{neos:backend.translate(id: 'workspaces.workspace.baseWorkspace', source: 'Main', package: 'Neos.Workspace.Ui')}{neos:backend.translate(id: 'workspaces.workspace.owner', source: 'Main', package: 'Neos.Workspace.Ui')}{neos:backend.translate(id: 'workspaces.workspace.changes', source: 'Main', package: 'Neos.Workspace.Ui')} 
- - - - - - - - - - - - - - - - - - - - - - - {workspace.workspaceTitle.value -> f:format.crop(maxCharacters: 25, append: '…')} - - - {workspace.baseWorkspaceName.value -> f:format.crop(maxCharacters: 25, append: '…')} - - - - - - - - - - {workspaceAndCounts.workspaceOwnerHumanReadable} - - - {workspaceAndCounts.workspaceOwnerHumanReadable} - - - - - -
- - - - - - - - - - -
-
- - - - {neos:backend.translate(id: 'workspaces.review', source: 'Main', package: 'Neos.Workspace.Ui')} - - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- -
{neos:backend.translate(id: 'workspaces.dialog.confirmWorkspaceDeletion', source: 'Main', package: 'Neos.Workspace.Ui', arguments: {0: workspace.workspaceTitle.value})}
-
-
-

{neos:backend.translate(id: 'workspaces.dialog.thisWillDeleteTheWorkspace', source: 'Main', package: 'Neos.Workspace.Ui')}

-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
diff --git a/Neos.Workspace.Ui/Resources/Private/Templates/Workspace/New.html b/Neos.Workspace.Ui/Resources/Private/Templates/Workspace/New.html deleted file mode 100644 index 6c4e619e1b4..00000000000 --- a/Neos.Workspace.Ui/Resources/Private/Templates/Workspace/New.html +++ /dev/null @@ -1,64 +0,0 @@ -{namespace neos=Neos\Neos\ViewHelpers} - - - -

{neos:backend.translate(id: 'workspaces.createNewWorkspace', source: 'Main', package: 'Neos.Workspace.Ui')}

-
- - - -
-
- -
- - -
-
- -
- -
- - -
-
- -
- -
- -
-
- - - -
- -
- -
-
- -
-
-
- - - -
-
- - -
-
diff --git a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css index 69ef42ec936..d0d316392e2 100644 --- a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css +++ b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css @@ -64,6 +64,15 @@ td.workspace-action-column { user-select: none; } +.workspace-count { + display: inline-block; + color: var(--textSubtleLight); + font-size: var(--generalFontSize); + line-height: var(--unit); + vertical-align: middle; + margin-left: var(--spacing-Full); +} + .icon-button { background: none; border: none; @@ -142,3 +151,17 @@ td.workspace-action-column { gap: var(--spacing-Full); justify-content: flex-end; } + +/** + * Form styles + */ +.neos-control-label { + margin-bottom: var(--spacing-Full); +} + +.neos-control-group textarea, +.neos-control-group select, +.neos-control-group input[type="text"] { + width: 100%; + max-width: 500px; +} From f007a4285ea5e7c2a45e1e5f276772290ba020f6 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Thu, 27 Jun 2024 13:57:19 +0200 Subject: [PATCH 006/122] TASK: Improve new workspace template --- .../Resources/Private/Fusion/Views/New.fusion | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion index ce3615a8d5c..23009730452 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion @@ -2,35 +2,47 @@ Neos.Workspace.Ui.WorkspaceController.new = Neos.Fusion:Component { flashMessages = ${flashMessages} baseWorkspaceOptions = ${baseWorkspaceOptions} workspace = ${workspace} + i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} prototype(Neos.Fusion.Form:LabelRenderer) { translationPackage = 'Neos.Workspace.Ui' translationSource = 'Main' } + prototype(Neos.Fusion.Form:Neos.BackendModule.FieldContainer) { + translation.label { + package = 'Neos.Workspace.Ui' + source = 'Main' + } + } + renderer = afx`
-

{I18n.translate('workspaces.createNewWorkspace', 'Create new workspace', [], 'Main', 'Neos.Workspace.Ui')}

- +

{props.i18n.id('workspaces.createNewWorkspace')}

+
- - - + + - - + - {workspaceTitle} - +

- {I18n.translate('workspaces.workspace.visibility', 'Visibility', [], 'Main', 'Neos.Workspace.Ui')} + {props.i18n.id('workspaces.workspace.visibility')}

- - + - {I18n.translate('workspaces.workspace.visibility.private', 'Private', [], 'Main', 'Neos.Workspace.Ui')} + {props.i18n.id('workspaces.workspace.visibility.private')} - {I18n.translate('workspaces.workspace.visibility.private.help', 'Only reviewers and administrators can access and modify this workspace', [], 'Main', 'Neos.Workspace.Ui')} + {props.i18n.id('workspaces.workspace.visibility.private.help')} @@ -67,13 +79,13 @@ Neos.Workspace.Ui.WorkspaceController.new = Neos.Fusion:Component { - {I18n.translate('workspaces.workspace.visibility.internal', 'Internal', [], 'Main', 'Neos.Workspace.Ui')} + {props.i18n.id('workspaces.workspace.visibility.internal')} - {I18n.translate('workspaces.workspace.visibility.internal.help', 'Any logged in editor can see and modify this workspace.', [], 'Main', 'Neos.Workspace.Ui')} + {props.i18n.id('workspaces.workspace.visibility.internal.help')} - +
- {I18n.translate('workspaces.createWorkspace', 'Create workspace', [], 'Main', 'Neos.Workspace.Ui')} + {props.i18n.id('workspaces.createWorkspace')}
- {I18n.translate('cancel', 'Cancel', [], 'Main', 'Neos.Workspace.Ui')} + {props.i18n.id('cancel')}
From e4974a13e6158ca9b44317414cf6918db9766e8b Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Thu, 27 Jun 2024 15:00:15 +0200 Subject: [PATCH 007/122] FEATURE: Refactor edit template to AFX --- .../Controller/WorkspaceController.php | 4 +- Neos.Workspace.Ui/Configuration/Views.yaml | 2 +- .../Components/WorkspaceTableRow.fusion | 15 +- .../Private/Fusion/Views/Edit.fusion | 147 ++++++++++++++++++ .../Resources/Private/Fusion/Views/New.fusion | 4 +- .../Private/Templates/Workspace/Edit.html | 65 -------- .../Private/Translations/en/Main.xlf | 3 + 7 files changed, 166 insertions(+), 74 deletions(-) create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion delete mode 100644 Neos.Workspace.Ui/Resources/Private/Templates/Workspace/Edit.html diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 188158e0251..d221fa3ebfd 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -288,14 +288,14 @@ public function editAction(WorkspaceName $workspaceName): void * @param WorkspaceName $workspaceName * @param WorkspaceTitle $title Human friendly title of the workspace, for example "Christmas Campaign" * @param WorkspaceDescription $description A description explaining the purpose of the new workspace - * @param string $workspaceOwner Id of the owner of the workspace + * @param string|null $workspaceOwner Id of the owner of the workspace * @return void */ public function updateAction( WorkspaceName $workspaceName, WorkspaceTitle $title, WorkspaceDescription $description, - ?string $workspaceOwner + ?string $workspaceOwner = null ): void { $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest()) ->contentRepositoryId; diff --git a/Neos.Workspace.Ui/Configuration/Views.yaml b/Neos.Workspace.Ui/Configuration/Views.yaml index 2371306a213..dccdf779490 100644 --- a/Neos.Workspace.Ui/Configuration/Views.yaml +++ b/Neos.Workspace.Ui/Configuration/Views.yaml @@ -5,7 +5,7 @@ - 'resource://Neos.Workspace.Ui/Private/Partials' - 'resource://Neos.Neos/Private/Partials' -- requestFilter: 'isPackage("Neos.Workspace.Ui") && isController("Workspace") && isFormat("html") && (isAction("index") || isAction("new"))' +- requestFilter: 'isPackage("Neos.Workspace.Ui") && isController("Workspace") && isFormat("html") && (isAction("index") || isAction("new") || isAction("edit"))' viewObjectName: 'Neos\Fusion\View\FusionView' options: fusionPathPatterns: diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion index 9726e28edf8..ca221982d63 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion @@ -50,6 +50,12 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion workspace = ${props.workspaceName} } } + editWorkspaceUri = Neos.Fusion:UriBuilder { + action = 'edit' + arguments { + workspaceName = ${props.workspaceName} + } + } } renderer = afx` @@ -81,15 +87,14 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion {I18n.translate('table.column.action.show', 'Show', [], 'Main', 'Neos.Workspace.Ui')} - + @@ -57,7 +57,7 @@ Neos.Workspace.Ui.WorkspaceController.index = Neos.Fusion:Component { ` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion index a78388700b4..15460df993c 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion @@ -40,6 +40,7 @@ Neos.Workspace.Ui.WorkspaceController.new = Neos.Fusion:Component { attributes.hx-on--after-request={'document.getElementById("' + props.popoverId + '").hidePopover()'} >
+ ` } diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf index 453b5cc1974..40e4c1822a9 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf @@ -40,7 +40,7 @@ Delete workspace "{0}" - {total} workspaces + {0} workspaces Workspace "{0}" has unpublished changes @@ -129,9 +129,6 @@ Outdated - - Outdated (has conflict) - Changes From cc0b8a91296447e1bef896b81800e91b7469946f Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Mon, 21 Oct 2024 14:03:45 +0200 Subject: [PATCH 028/122] WIP: Pass `userWorkspaceName` instead of full workspace --- .../Classes/Controller/WorkspaceController.php | 4 ++-- .../Private/Fusion/Components/WorkspaceTable.fusion | 6 +++--- .../Private/Fusion/Components/WorkspaceTableRow.fusion | 8 ++++---- .../Private/Fusion/Components/WorkspaceTreeNode.fusion | 8 ++++---- .../Resources/Private/Fusion/Views/Index.fusion | 8 ++++---- .../Resources/Private/Fusion/Views/Update.fusion | 6 +++--- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 2b40b4b7038..53f034478ad 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -140,7 +140,7 @@ public function indexAction(): void $workspaceListItems = $this->getWorkspaceListItems($userWorkspace, $contentRepository); $this->view->assignMultiple([ - 'userWorkspace' => $userWorkspace, + 'userWorkspaceName' => $userWorkspace->workspaceName->value, 'workspaceListItems' => $workspaceListItems, 'flashMessages' => $this->controllerContext->getFlashMessageContainer()->getMessagesAndFlush(), ]); @@ -327,7 +327,7 @@ public function updateAction( $workspaceListItems = $this->getWorkspaceListItems($userWorkspace, $contentRepository); $this->view->assignMultiple([ - 'userWorkspace' => $userWorkspace, + 'userWorkspaceName' => $userWorkspace->workspaceName->value, 'workspaceListItems' => $workspaceListItems, ]); } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTable.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTable.fusion index 18b67699b22..03e9bc37ca2 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTable.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTable.fusion @@ -2,8 +2,8 @@ # Renders a list of workspaces # prototype(Neos.Workspace.Ui:Component.WorkspaceTable) < prototype(Neos.Fusion:Component) { - /// Neos\ContentRepository\Core\Projection\Workspace\Workspace - userWorkspace = null + /// string + userWorkspaceName = null /// \Neos\Workspace\Ui\ViewModel\WorkspaceListItems workspaceListItems = ${{}} @@ -37,7 +37,7 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTable) < prototype(Neos.Fusion:Co diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion index 7c2e30c917c..12e54daf01d 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion @@ -2,8 +2,8 @@ # Renders a single workspace list item # prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion:Component) { - /// Neos\ContentRepository\Core\Projection\Workspace\Workspace - userWorkspace = '' + /// string + userWorkspaceName = null /// Neos\Workspace\Ui\ViewModel\WorkspaceListItem workspaceListItem = ${[]} /// boolean todo unused!!! @@ -19,7 +19,7 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion // todo move state calculation to php workspaceStatus = Neos.Fusion:Case { personal-workspace { - condition = ${private.workspaceName == props.userWorkspace.workspaceName.value} + condition = ${private.workspaceName == props.userWorkspaceName} renderer = 'personal-workspace' } stale { @@ -90,7 +90,7 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion 1}/> {props.workspaceListItem.title} - + ({private.i18n.id('workspaces.workspace.userWorkspace')}) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTreeNode.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTreeNode.fusion index 3e926d0b590..5a2e8f169b4 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTreeNode.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTreeNode.fusion @@ -2,8 +2,8 @@ # Renders a single workspace list item and its subworkspaces # prototype(Neos.Workspace.Ui:Component.WorkspaceTreeNode) < prototype(Neos.Fusion:Component) { - /// Neos\ContentRepository\Core\Projection\Workspace\Workspace - userWorkspace = null + /// string + userWorkspaceName = null /// Neos\Workspace\Ui\ViewModel\WorkspaceListItem workspaceListItem = null /// Neos\Workspace\Ui\ViewModel\WorkspaceListItems @@ -18,14 +18,14 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTreeNode) < prototype(Neos.Fusion renderer = afx`
@@ -56,7 +56,7 @@ Neos.Workspace.Ui.WorkspaceController.index = Neos.Fusion:Component { htmx = afx` ` diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Update.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Update.fusion index f88b20d0312..175d3f01bae 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Update.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Update.fusion @@ -2,14 +2,14 @@ # Update response after a workspace has been edited via the edit modal # Neos.Workspace.Ui.WorkspaceController.update = Neos.Fusion:Component { - /// Neos\ContentRepository\Core\Projection\Workspace\Workspace - userWorkspace = ${userWorkspace} + /// string + userWorkspaceName = ${userWorkspaceName} /// Neos\Workspace\Ui\ViewModel\WorkspaceListItems workspaceListItems = ${workspaceListItems} renderer = afx` ` From 1e216e134a4f963e1d5f95748355011c27ddd1d5 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Mon, 21 Oct 2024 14:11:50 +0200 Subject: [PATCH 029/122] WIP: Remove occurrence of `.workspace` in show action We dont pass the full workspace object anymore --- .../Components/WorkspaceTableRow.fusion | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion index 12e54daf01d..344221afdca 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion @@ -14,12 +14,11 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} workspace = ${props.workspaceListItem.workspace} - workspaceName = ${private.workspace.workspaceName.value} - workspaceTableRowId = ${'workspace-row-' + private.workspaceName} + workspaceTableRowId = ${'workspace-row-' + props.workspaceListItem.name} // todo move state calculation to php workspaceStatus = Neos.Fusion:Case { personal-workspace { - condition = ${private.workspaceName == props.userWorkspaceName} + condition = ${props.workspaceListItem.name == props.userWorkspaceName} renderer = 'personal-workspace' } stale { @@ -42,6 +41,7 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion } } workspaceStatusLabel = Neos.Fusion:Match { + // todo status private doesnt exist anymore @subject = ${private.workspaceStatus} @default = ${private.i18n.id('table.column.access.internal').value('Internal workspace')} personal-workspace = ${private.i18n.id('badge.isPersonalWorkspace').value('This is your personal workspace')} @@ -59,20 +59,20 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion action = 'show' format = 'html' arguments { - workspace = ${private.workspaceName} + workspace = ${props.workspaceListItem.name} } } editWorkspaceUri = Neos.Fusion:ActionUri { action = 'edit' arguments { - workspaceName = ${private.workspaceName} + workspaceName = ${props.workspaceListItem.name} } } deleteWorkspaceUri = Neos.Fusion:ActionUri { action = 'delete' format = 'htmx' arguments { - workspaceName = ${private.workspaceName} + workspaceName = ${props.workspaceListItem.name} } } deleteWorkspacePopoverId = 'workspace-delete-modal' @@ -90,7 +90,7 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion 1}/> {props.workspaceListItem.title} - + ({private.i18n.id('workspaces.workspace.userWorkspace')}) @@ -122,13 +122,13 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion 0} attributes.hx-get={private.deleteWorkspaceUri} From 9a0377b80b021303f03eaf190d0c7288715e6240 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Mon, 21 Oct 2024 14:12:43 +0200 Subject: [PATCH 030/122] WIP: Remove old view models --- .../Classes/Model/WorkspaceDetails.php | 36 ------------- .../Model/WorkspaceDetailsCollection.php | 50 ------------------- 2 files changed, 86 deletions(-) delete mode 100644 Neos.Workspace.Ui/Classes/Model/WorkspaceDetails.php delete mode 100644 Neos.Workspace.Ui/Classes/Model/WorkspaceDetailsCollection.php diff --git a/Neos.Workspace.Ui/Classes/Model/WorkspaceDetails.php b/Neos.Workspace.Ui/Classes/Model/WorkspaceDetails.php deleted file mode 100644 index 6cd1ee0e7c8..00000000000 --- a/Neos.Workspace.Ui/Classes/Model/WorkspaceDetails.php +++ /dev/null @@ -1,36 +0,0 @@ - $items - */ - public function __construct( - public array $items = [] - ) { - $this->counts = [ - 'total' => $this->count() - ]; - } - - /** - * @return \Traversable - */ - public function getIterator(): \Traversable - { - yield from $this->items; - } - - public function count(): int - { - return count($this->items); - } -} From 49554c3446ad45bd48dac303ed89195c489eb092 Mon Sep 17 00:00:00 2001 From: Paula Kallert Date: Mon, 21 Oct 2024 21:26:28 +0200 Subject: [PATCH 031/122] Feature: Move a little bit of review to fusion --- .../Controller/WorkspaceController.php | 40 ++++-- Neos.Workspace.Ui/Configuration/Views.yaml | 10 +- .../Fusion/Components/ReviewChangeDiff.fusion | 125 ++++++++++++++++++ .../Components/ReviewChangeTableRow.fusion | 61 +++++++++ .../Components/ReviewDocumentTableRow.fusion | 73 ++++++++++ .../Fusion/Components/ReviewTable.fusion | 47 +++++++ .../Components/WorkspaceTableRow.fusion | 3 +- .../Private/Fusion/Views/Review.fusion | 47 +++++++ .../Private/Templates/Workspace/Show.html | 6 +- 9 files changed, 392 insertions(+), 20 deletions(-) create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewTable.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 53f034478ad..fc009819b6f 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -56,6 +56,7 @@ use Neos\Neos\Domain\Model\WorkspaceRole; use Neos\Neos\Domain\Model\WorkspaceRoleAssignment; use Neos\Neos\Domain\Model\WorkspaceTitle; +use Neos\Neos\Domain\NodeLabel\NodeLabelGeneratorInterface; use Neos\Neos\Domain\Repository\SiteRepository; use Neos\Neos\Domain\Service\NodeTypeNameFactory; use Neos\Neos\Domain\Service\UserService; @@ -106,9 +107,14 @@ class WorkspaceController extends AbstractModuleController #[Flow\Inject] protected WorkspaceService $workspaceService; + #[Flow\Inject] + protected NodeLabelGeneratorInterface $nodeLabelGenerator; + #[Flow\Inject] protected Translator $translator; + + /** * Display a list of unpublished content */ @@ -146,7 +152,7 @@ public function indexAction(): void ]); } - public function showAction(WorkspaceName $workspace): void + public function reviewAction(WorkspaceName $workspace): void { $currentUser = $this->userService->getCurrentUser(); if ($currentUser === null) { @@ -170,7 +176,7 @@ public function showAction(WorkspaceName $workspace): void $baseWorkspacePermissions = $this->workspaceService->getWorkspacePermissionsForUser($contentRepositoryId, $baseWorkspace->workspaceName, $currentUser); } $this->view->assignMultiple([ - 'selectedWorkspace' => $workspaceObj, + 'selectedWorkspaceName' => $workspaceObj->workspaceName->value, 'selectedWorkspaceLabel' => $workspaceMetadata->title->value, 'baseWorkspaceName' => $workspaceObj->baseWorkspaceName, 'baseWorkspaceLabel' => $baseWorkspaceMetadata?->title->value, @@ -429,11 +435,15 @@ public function deleteAction(WorkspaceName $workspaceName): void * Rebase the current users personal workspace onto the given $targetWorkspace and then * redirects to the $targetNode in the content module. */ - public function rebaseAndRedirectAction(string $targetNode, Workspace $targetWorkspace): void + public function rebaseAndRedirectAction(string $targetNode, WorkspaceName $targetWorkspaceName): void { $targetNodeAddress = NodeAddress::fromJsonString( $targetNode ); + $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; + $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); + + $targetWorkspace = $contentRepository->findWorkspaceByName($targetWorkspaceName); $user = $this->userService->getCurrentUser(); if ($user === null) { @@ -504,7 +514,7 @@ public function publishNodeAction(string $nodeAddress, WorkspaceName $selectedWo $contentRepository->handle($command); $this->addFlashMessage($this->getModuleLabel('workspaces.selectedChangeHasBeenPublished')); - $this->redirect('show', null, null, ['workspace' => $selectedWorkspace->value]); + $this->redirect('review', null, null, ['workspace' => $selectedWorkspace->value]); } /** @@ -516,7 +526,6 @@ public function publishNodeAction(string $nodeAddress, WorkspaceName $selectedWo public function discardNodeAction(string $nodeAddress, WorkspaceName $selectedWorkspace): void { $nodeAddress = NodeAddress::fromJsonString($nodeAddress); - $contentRepository = $this->contentRepositoryRegistry->get($nodeAddress->contentRepositoryId); $command = DiscardIndividualNodesFromWorkspace::create( @@ -531,7 +540,6 @@ public function discardNodeAction(string $nodeAddress, WorkspaceName $selectedWo $contentRepository->handle($command); $this->addFlashMessage($this->getModuleLabel('workspaces.selectedChangeHasBeenDiscarded')); - $this->redirect('show', null, null, ['workspace' => $selectedWorkspace->value]); } /** @@ -579,7 +587,7 @@ public function publishOrDiscardNodesAction(array $nodes, string $action, string throw new \RuntimeException('Invalid action "' . htmlspecialchars($action) . '" given.', 1346167441); } - $this->redirect('show', null, null, ['workspace' => $selectedWorkspaceName->value]); + $this->redirect('review', null, null, ['workspace' => $selectedWorkspaceName->value]); } /** @@ -688,6 +696,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos $nodePathSegments = []; $documentPathSegments = []; + $documentPathSegmentsNames = []; foreach ($ancestors as $ancestor) { $pathSegment = $ancestor->name ?: NodeName::fromString($ancestor->aggregateId->value); // Don't include `sites` path as they are not needed @@ -697,6 +706,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos } if ($this->getNodeType($ancestor)->isOfType(NodeTypeNameFactory::NAME_DOCUMENT)) { $documentPathSegments[] = $pathSegment; + $documentPathSegmentsNames[] = $this->nodeLabelGenerator->getLabel($ancestor); if (is_null($documentNode)) { $documentNode = $ancestor; } @@ -732,16 +742,27 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos ) ) ); + if (!isset($siteChanges[$siteNodeName]['siteNode'])) { $siteChanges[$siteNodeName]['siteNode'] = $this->siteRepository->findOneByNodeName(SiteNodeName::fromString($siteNodeName)); } - + $documentNodeAddress = NodeAddress::create( + $contentRepository->id, + $selectedWorkspace->workspaceName, + $documentNode->originDimensionSpacePoint->toDimensionSpacePoint(), + $documentNode->aggregateId + ); $siteChanges[$siteNodeName]['documents'][$documentPath]['documentNode'] = $documentNode; + $siteChanges[$siteNodeName]['documents'][$documentPath]['documentBreadCrumb'] = array_reverse($documentPathSegmentsNames); + + $siteChanges[$siteNodeName]['documents'][$documentPath]['documentNodeAddress'] = $documentNodeAddress->toJson(); + // We need to set `isNew` and `isMoved` on document level to make our JS behave as before. if ($documentNode->equals($node)) { $siteChanges[$siteNodeName]['documents'][$documentPath]['isNew'] = $change->created; $siteChanges[$siteNodeName]['documents'][$documentPath]['isMoved'] = $change->moved; + $siteChanges[$siteNodeName]['documents'][$documentPath]['isDeleted'] = $change->deleted; } // As for changes of type `delete` we are using nodes from the live workspace @@ -754,6 +775,8 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos $change->nodeAggregateId ); + + $change = [ 'node' => $node, 'serializedNodeAddress' => $nodeAddress->toJson(), @@ -772,6 +795,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos } $siteChanges[$siteNodeName]['documents'][$documentPath]['changes'][$relativePath] = $change; } + $siteChanges[$siteNodeName]['documents'][$documentPath]['changesCount'] = count($siteChanges[$siteNodeName]['documents'][$documentPath]['changes']); } } diff --git a/Neos.Workspace.Ui/Configuration/Views.yaml b/Neos.Workspace.Ui/Configuration/Views.yaml index 77333872b5d..11e5bd5d9fe 100644 --- a/Neos.Workspace.Ui/Configuration/Views.yaml +++ b/Neos.Workspace.Ui/Configuration/Views.yaml @@ -1,12 +1,4 @@ -- requestFilter: 'isPackage("Neos.Workspace.Ui") && isController("Workspace") && isFormat("html") && isAction("show")' - viewObjectName: 'Neos\FluidAdaptor\View\StandaloneView' - options: - layoutRootPathPattern: 'resource://Neos.Neos/Private/Layouts' - partialRootPaths: - - 'resource://Neos.Workspace.Ui/Private/Partials' - - 'resource://Neos.Neos/Private/Partials' - -- requestFilter: 'isPackage("Neos.Workspace.Ui") && isController("Workspace") && !isAction("show")' +- requestFilter: 'isPackage("Neos.Workspace.Ui") && isController("Workspace")' viewObjectName: 'Neos\Fusion\View\FusionView' options: fusionPathPatterns: diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion new file mode 100644 index 00000000000..616b439c3e9 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion @@ -0,0 +1,125 @@ +## +# Renders a document for review +# +prototype(Neos.Workspace.Ui:Component.ReviewChangeDiff) < prototype(Neos.Fusion:Component) { + + relativePath = '' + change = '' + document = '' + canPublishToBaseWorkspace = ${canPublishToBaseWorkspace} + selectedWorkspaceName = ${selectedWorkspaceName} + + @private { + i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} + discardChangeAction = Neos.Fusion:ActionUri { + action = 'discardNode' + arguments { + nodeAddress = ${change.serializedNodeAddress} + selectedWorkspace = ${selectedWorkspaceName} + + } + } + + } + + renderer = afx` +
+ + + + + + + + + + + + + + + +
+ + {change.node.label} + + + {change.node.lastModificationDateTime} +
{private.i18n.id('lastModification')}
+ + + + + + + + + + + +
+ + {line} + + + + {line} + +
+ +
+
+ ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion new file mode 100644 index 00000000000..1f9b8d59462 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion @@ -0,0 +1,61 @@ +## +# Renders a document for review +# +prototype(Neos.Workspace.Ui:Component.ReviewChangeTableRow) < prototype(Neos.Fusion:Component) { + + relativePath = '' + change = ${change} + document = ${document} + canPublishToBaseWorkspace = ${canPublishToBaseWorkspace} + selectedWorkspaceName = ${selectedWorkspaceName} + + @private { + i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} + discardChangeAction = Neos.Fusion:ActionUri { + action = 'discardNode' + arguments { + nodeAddress = ${change.serializedNodeAddress} + selectedWorkspace = ${selectedWorkspaceName} + + } + } + + } + + renderer = afx` + + + + + + + + + +
+ +
+ + + + + + + ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion new file mode 100644 index 00000000000..5c9d85ff6c3 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion @@ -0,0 +1,73 @@ +## +# Renders a document for review +# +prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.Fusion:Component) { + + document = null + documentPath = '' + selectedWorkspaceName = ${selectedWorkspaceName} + selectedWorkspaceLabel = ${selectedWorkspaceLabel} + + @private { + i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} + + openNodeInWorkspaceAction = Neos.Fusion:ActionUri { + action = 'rebaseAndRedirect' + arguments { + targetWorkspaceName = ${selectedWorkspaceName} + targetNode = ${document.documentNodeAddress} + + } + } + openNodeInPreviewAction = Neos.Fusion:ActionUri { + request=${request.mainRequest} + package='Neos.Neos' + controller='Frontend\\\Node' + action = 'preview' + // todo fixme + @process.addQuery = ${value + '?node=' + document.documentNodeAddress} + } + + } + + renderer = afx` + + + + + +
+
+ + {crumb} + +
+ +
+ + + + + + + +
+
+ + + + + + ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewTable.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewTable.fusion new file mode 100644 index 00000000000..fb5326c5d99 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewTable.fusion @@ -0,0 +1,47 @@ +## +# Shows a list of changes in a workspace +# +prototype(Neos.Workspace.Ui:Component.ReviewTable) < prototype(Neos.Fusion:Component) { + + siteChanges = null + canPublishToBaseWorkspace = true + selectedWorkspaceName = null + selectedWorkspaceLabel = '' + + @private { + i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} + + } + + renderer = afx` + + + + + + + + + + + + + + + + + + + +
+ + {private.i18n.id('workspaces.selectAllCurrentChanges')}
+ ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion index 344221afdca..0a7e6604c79 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion @@ -56,8 +56,7 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion with-acl = 'user-plus' } showWorkspaceUri = Neos.Fusion:ActionUri { - action = 'show' - format = 'html' + action = 'review' arguments { workspace = ${props.workspaceListItem.name} } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion new file mode 100644 index 00000000000..ed31e438818 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion @@ -0,0 +1,47 @@ +Neos.Workspace.Ui.WorkspaceController.review = Neos.Fusion:Component { + ///todo Neos\ContentRepository\Core\Projection\Workspace\Workspace + selectedWorkspaceName = ${selectedWorkspaceName} + + selectedWorkspaceLabel = ${selectedWorkspaceLabel} + + baseWorkspaceName = ${baseWorkspaceName} + + baseWorkspaceLabel = ${baseWorkspaceLabel} + + canPublishToBaseWorkspace = ${canPublishToBaseWorkspace} + + siteChanges = ${siteChanges} + + contentDimensions = ${contentDimensions} + /// array + flashMessages = ${flashMessages} + + i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} + + renderer = Neos.Fusion:Match { + @subject = ${request.format} + @default = afx` +
+ + + +
+
+ + {props.i18n.id('workspaces.unpublishedChanges').arguments([selectedWorkspaceLabel]).translate()} + + +
+
+
+ ` + htmx = afx` + + + ` + } +} diff --git a/Neos.Workspace.Ui/Resources/Private/Templates/Workspace/Show.html b/Neos.Workspace.Ui/Resources/Private/Templates/Workspace/Show.html index 54d73464fd9..c071f2a9942 100644 --- a/Neos.Workspace.Ui/Resources/Private/Templates/Workspace/Show.html +++ b/Neos.Workspace.Ui/Resources/Private/Templates/Workspace/Show.html @@ -46,7 +46,11 @@
- + + + +
From 9f9dea8547d4e7206fc67828be0e681d4aba08b8 Mon Sep 17 00:00:00 2001 From: Paula Kallert Date: Tue, 22 Oct 2024 12:02:57 +0200 Subject: [PATCH 032/122] Fix: Naming of workspaces in overview --- .../Controller/WorkspaceController.php | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 53f034478ad..967655b4fee 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -1116,8 +1116,8 @@ protected function getWorkspaceListItems( Workspace $userWorkspace, ContentRepository $contentRepository ): WorkspaceListItems { - $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepository->id, $userWorkspace->workspaceName); - $workspacesPermissions = $this->workspaceService->getWorkspacePermissionsForUser( + $userWorkspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepository->id, $userWorkspace->workspaceName); + $userWorkspacesPermissions = $this->workspaceService->getWorkspacePermissionsForUser( $contentRepository->id, $userWorkspace->workspaceName, $this->userService->getCurrentUser() @@ -1128,17 +1128,17 @@ protected function getWorkspaceListItems( $workspaceListItems = []; $workspaceListItems[$userWorkspace->workspaceName->value] = new WorkspaceListItem( $userWorkspace->workspaceName->value, - $workspaceMetadata->classification->value, + $userWorkspaceMetadata->classification->value, $userWorkspace->status->value, - $workspaceMetadata->title->value, - $workspaceMetadata->description->value, + $userWorkspaceMetadata->title->value, + $userWorkspaceMetadata->description->value, $userWorkspace->baseWorkspaceName?->value, $this->computePendingChanges($userWorkspace, $contentRepository), !$allWorkspaces->getDependantWorkspaces($userWorkspace->workspaceName)->isEmpty(), - $workspaceMetadata->ownerUserId ? $this->userService->findUserById( - UserId::fromString($workspaceMetadata->ownerUserId->value) + $userWorkspaceMetadata->ownerUserId ? $this->userService->findUserById( + UserId::fromString($userWorkspaceMetadata->ownerUserId->value) )?->getLabel() : null, - $workspacesPermissions, + $userWorkspacesPermissions, ); foreach ($allWorkspaces as $workspace) { @@ -1147,9 +1147,11 @@ protected function getWorkspaceListItems( $workspace->workspaceName, $this->userService->getCurrentUser() ); - if (!$workspacesPermissions->manage || !$workspacesPermissions->read) { // todo check corrrect? + + if (!$workspacesPermissions->read) { continue; } + $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepository->id, $userWorkspace->workspaceName); $workspaceListItems[$workspace->workspaceName->value] = new WorkspaceListItem( $workspace->workspaceName->value, From 69f8b0e82784ab082ceba1e2f4195e11ee1387af Mon Sep 17 00:00:00 2001 From: Paula Kallert Date: Tue, 22 Oct 2024 12:06:38 +0200 Subject: [PATCH 033/122] Feature: Apply stash --- Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index f125c56ab91..08a2ac70dfe 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -1140,6 +1140,7 @@ protected function getWorkspaceListItems( Workspace $userWorkspace, ContentRepository $contentRepository ): WorkspaceListItems { + $userWorkspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepository->id, $userWorkspace->workspaceName); $userWorkspacesPermissions = $this->workspaceService->getWorkspacePermissionsForUser( $contentRepository->id, From 641f29fa4259f144e16dc44cc63b42d3f5204447 Mon Sep 17 00:00:00 2001 From: Robert Baruck Date: Tue, 22 Oct 2024 14:50:53 +0200 Subject: [PATCH 034/122] TASK: Workspace management edit action --- .../Classes/Domain/Service/UserService.php | 1 + .../Domain/Service/WorkspaceService.php | 14 +++++ .../Controller/WorkspaceController.php | 44 ++++++++++----- .../Classes/ViewModel/EditWorkspaceDto.php | 30 ++++++++++ .../Fusion/Components/Modals/Edit.fusion | 55 +++++++------------ .../Private/Fusion/Views/Edit.fusion | 13 ++--- 6 files changed, 103 insertions(+), 54 deletions(-) create mode 100644 Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceDto.php diff --git a/Neos.Neos/Classes/Domain/Service/UserService.php b/Neos.Neos/Classes/Domain/Service/UserService.php index dfa1616d003..ea3f627c86d 100644 --- a/Neos.Neos/Classes/Domain/Service/UserService.php +++ b/Neos.Neos/Classes/Domain/Service/UserService.php @@ -14,6 +14,7 @@ namespace Neos\Neos\Domain\Service; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\Flow\Annotations as Flow; use Neos\Flow\Persistence\Exception\IllegalObjectTypeException; use Neos\Flow\Persistence\PersistenceManagerInterface; diff --git a/Neos.Neos/Classes/Domain/Service/WorkspaceService.php b/Neos.Neos/Classes/Domain/Service/WorkspaceService.php index c34636a77a6..551865b24ab 100644 --- a/Neos.Neos/Classes/Domain/Service/WorkspaceService.php +++ b/Neos.Neos/Classes/Domain/Service/WorkspaceService.php @@ -83,6 +83,13 @@ public function getWorkspaceMetadata(ContentRepositoryId $contentRepositoryId, W */ public function setWorkspaceTitle(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName, WorkspaceTitle $newWorkspaceTitle): void { + $workspaceMetadata = $this->loadWorkspaceMetadata($contentRepositoryId, $workspaceName); + + // WHY: Do not update if the title is the same + if ($workspaceMetadata->title->equals($newWorkspaceTitle)) { + return; + } + $this->updateWorkspaceMetadata($contentRepositoryId, $workspaceName, [ 'title' => $newWorkspaceTitle->value, ]); @@ -93,6 +100,13 @@ public function setWorkspaceTitle(ContentRepositoryId $contentRepositoryId, Work */ public function setWorkspaceDescription(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName, WorkspaceDescription $newWorkspaceDescription): void { + $workspaceMetadata = $this->loadWorkspaceMetadata($contentRepositoryId, $workspaceName); + + // WHY: Do not update if the description is the same + if ($workspaceMetadata->description->equals($newWorkspaceDescription)) { + return; + } + $this->updateWorkspaceMetadata($contentRepositoryId, $workspaceName, [ 'description' => $newWorkspaceDescription->value, ]); diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 53f034478ad..a5b9bc1f64f 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -65,6 +65,7 @@ use Neos\Neos\FrontendRouting\SiteDetection\SiteDetectionResult; use Neos\Neos\PendingChangesProjection\ChangeFinder; use Neos\Neos\Utility\NodeTypeWithFallbackProvider; +use Neos\Workspace\Ui\ViewModel\EditWorkspaceDto; use Neos\Workspace\Ui\ViewModel\PendingChanges; use Neos\Workspace\Ui\ViewModel\WorkspaceListItem; use Neos\Workspace\Ui\ViewModel\WorkspaceListItems; @@ -244,6 +245,8 @@ public function createAction( /** * Edit a workspace + * + * Renders /Resource/Private/Fusion/Views/Edit.fusion */ public function editAction(WorkspaceName $workspaceName): void { @@ -262,13 +265,27 @@ public function editAction(WorkspaceName $workspaceName): void $this->throwStatus(404, 'Workspace does not exist'); } - $this->view->assign('workspace', $workspace); + $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspace->workspaceName); + + $workspaceDto = new EditWorkspaceDto( + workspaceName: $workspace->workspaceName->value, + workspaceTitle: $workspaceMetadata->title->value, + workspaceDescription: $workspaceMetadata->description->value, + workspaceOwnerId: $workspaceMetadata->ownerUserId->value, + ); + + + $this->view->assign('workspace', $workspaceDto); $this->view->assign('baseWorkspaceOptions', $this->prepareBaseWorkspaceOptions($contentRepository, $workspaceName)); - // TODO: $this->view->assign('disableBaseWorkspaceSelector', + // TODO + $this->view->assign('disableBaseWorkspaceSelector', true); + // $this->publishingService->getUnpublishedNodesCount($workspace) > 0); - // TODO fix $this->userService->currentUserCanTransferOwnershipOfWorkspace($workspace) - $this->view->assign('showOwnerSelector', false); + // TODO + // This has been here: $this->userService->currentUserCanTransferOwnershipOfWorkspace($workspace) + // We need to calc this from the new permissions model + $this->view->assign('showOwnerSelector', true); $this->view->assign('ownerOptions', $this->prepareOwnerOptions()); } @@ -1116,8 +1133,8 @@ protected function getWorkspaceListItems( Workspace $userWorkspace, ContentRepository $contentRepository ): WorkspaceListItems { - $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepository->id, $userWorkspace->workspaceName); - $workspacesPermissions = $this->workspaceService->getWorkspacePermissionsForUser( + $userWorkspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepository->id, $userWorkspace->workspaceName); + $userWorkspacesPermissions = $this->workspaceService->getWorkspacePermissionsForUser( $contentRepository->id, $userWorkspace->workspaceName, $this->userService->getCurrentUser() @@ -1128,26 +1145,27 @@ protected function getWorkspaceListItems( $workspaceListItems = []; $workspaceListItems[$userWorkspace->workspaceName->value] = new WorkspaceListItem( $userWorkspace->workspaceName->value, - $workspaceMetadata->classification->value, + $userWorkspaceMetadata->classification->value, $userWorkspace->status->value, - $workspaceMetadata->title->value, - $workspaceMetadata->description->value, + $userWorkspaceMetadata->title->value, + $userWorkspaceMetadata->description->value, $userWorkspace->baseWorkspaceName?->value, $this->computePendingChanges($userWorkspace, $contentRepository), !$allWorkspaces->getDependantWorkspaces($userWorkspace->workspaceName)->isEmpty(), - $workspaceMetadata->ownerUserId ? $this->userService->findUserById( - UserId::fromString($workspaceMetadata->ownerUserId->value) + $userWorkspaceMetadata->ownerUserId ? $this->userService->findUserById( + UserId::fromString($userWorkspaceMetadata->ownerUserId->value) )?->getLabel() : null, - $workspacesPermissions, + $userWorkspacesPermissions, ); foreach ($allWorkspaces as $workspace) { + $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepository->id, $workspace->workspaceName); $workspacesPermissions = $this->workspaceService->getWorkspacePermissionsForUser( $contentRepository->id, $workspace->workspaceName, $this->userService->getCurrentUser() ); - if (!$workspacesPermissions->manage || !$workspacesPermissions->read) { // todo check corrrect? + if (!$workspacesPermissions->read) { // todo check corrrect? continue; } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceDto.php b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceDto.php new file mode 100644 index 00000000000..29c6896305d --- /dev/null +++ b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceDto.php @@ -0,0 +1,30 @@ + baseWorkspaceOptions = ${[]} - /// boolean - showOwnerSelector = true - /// array - ownerOptions = ${[]} - /// Neos\ContentRepository\Core\Projection\Workspace\Workspace - workspace = null + + workspaceName = '' + workspaceTitle = '' + workspaceDescription = '' + baseWorkspaceName = '' + + isPrivateWorkspace = false + isInternalWorkspace = false @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} popoverId = 'workspace-edit-modal' - workspaceTableRowId = ${'workspace-row-' + props.workspace.workspaceName.value} + workspaceTableRowId = ${'workspace-row-' + props.workspaceName} } prototype(Neos.Fusion.Form:LabelRenderer) { @@ -38,14 +42,14 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Compon
- {private.i18n.id('workspaces.editWorkspace').arguments([props.workspace.workspaceTitle.value])} + {private.i18n.id('workspaces.editWorkspace').arguments([props.workspaceTitle])}
@@ -62,12 +66,13 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Compon attributes.required attributes.pattern="/^[\p{L}\p{P}\d \.]{1,200}$/u" attributes.autofocus + attributes.placeholder={props.workspaceName} /> @@ -78,41 +83,23 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Compon {workspaceTitle} - - - - - - {ownerTitle} - - - -
@@ -128,7 +115,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Compon id="visibility.private" field.value="private" attributes.disabled - attributes.checked={props.workspace.privateWorkspace} + attributes.checked={props.isPrivateWorkspace} > @@ -144,7 +131,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Compon id="visibility.internal" field.value="internal" attributes.disabled - attributes.checked={props.workspace.internalWorkspace} + attributes.checked={props.isInternalWorkspace} > diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion index 0ad7fefbe16..3c6e52179d2 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion @@ -1,19 +1,18 @@ Neos.Workspace.Ui.WorkspaceController.edit = Neos.Fusion:Component { + disableBaseWorkspaceSelector = ${disableBaseWorkspaceSelector} /// array baseWorkspaceOptions = ${baseWorkspaceOptions} /// boolean - showOwnerSelector = ${showOwnerSelector} - /// array - ownerOptions = ${ownerOptions} - /// Neos\ContentRepository\Core\Projection\Workspace\Workspace workspace = ${workspace} renderer = afx` ` } From 95677e1a6ef2f0888772210ffa8b6a2739ca10de Mon Sep 17 00:00:00 2001 From: Paula Kallert Date: Tue, 22 Oct 2024 14:53:55 +0200 Subject: [PATCH 035/122] Feature: finish change diff --- .../Controller/WorkspaceController.php | 15 ++- Neos.Workspace.Ui/Configuration/Views.yaml | 3 +- .../Fusion/Components/ReviewChangeDiff.fusion | 99 +++++++++---------- .../Components/ReviewChangeTableRow.fusion | 1 + .../Resources/Public/Styles/Module.css | 4 + 5 files changed, 60 insertions(+), 62 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 08a2ac70dfe..033f21940eb 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -188,6 +188,7 @@ public function reviewAction(WorkspaceName $workspace): void public function newAction(ContentRepositoryId $contentRepositoryId): void { + $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); $this->view->assign('baseWorkspaceOptions', $this->prepareBaseWorkspaceOptions($contentRepository)); @@ -774,7 +775,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos $change->originDimensionSpacePoint->toDimensionSpacePoint(), $change->nodeAggregateId ); - + $nodeType = $contentRepository->getNodeTypeManager()->getNodeType($node->nodeTypeName); $change = [ @@ -783,16 +784,16 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos 'isRemoved' => $change->deleted, 'isNew' => $change->created, 'isMoved' => $change->moved, + 'dimensions' => $node->dimensionSpacePoint->toJson(), + 'lastModificationDateTime' => $node->timestamps->lastModified, + 'label' => $this->nodeLabelGenerator->getLabel($node), + 'icon' => $nodeType->getFullConfiguration()['ui']['icon'], 'contentChanges' => $this->renderContentChanges( $node, $change->contentStreamId, $contentRepository ) ]; - $nodeType = $this->getNodeType($node); - if ($nodeType->isOfType('Neos.Neos:Node')) { - $change['configuration'] = $nodeType->getFullConfiguration(); - } $siteChanges[$siteNodeName]['documents'][$documentPath]['changes'][$relativePath] = $change; } $siteChanges[$siteNodeName]['documents'][$documentPath]['changesCount'] = count($siteChanges[$siteNodeName]['documents'][$documentPath]['changes']); @@ -1173,9 +1174,7 @@ protected function getWorkspaceListItems( $this->userService->getCurrentUser() ); - if (!$workspacesPermissions->read) { - continue; - } + $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepository->id, $userWorkspace->workspaceName); $workspaceListItems[$workspace->workspaceName->value] = new WorkspaceListItem( diff --git a/Neos.Workspace.Ui/Configuration/Views.yaml b/Neos.Workspace.Ui/Configuration/Views.yaml index 11e5bd5d9fe..247ad31b632 100644 --- a/Neos.Workspace.Ui/Configuration/Views.yaml +++ b/Neos.Workspace.Ui/Configuration/Views.yaml @@ -1,7 +1,8 @@ -- requestFilter: 'isPackage("Neos.Workspace.Ui") && isController("Workspace")' +- requestFilter: 'isFormat("html") && isPackage("Neos.Workspace.Ui") && isController("Workspace")' viewObjectName: 'Neos\Fusion\View\FusionView' options: fusionPathPatterns: + - 'resource://Neos.Neos/Private/Fusion' - 'resource://Neos.Fusion/Private/Fusion' - 'resource://Neos.Fusion.Form/Private/Fusion' - 'resource://Neos.Workspace.Ui/Private/Fusion' diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion index 616b439c3e9..44b75449305 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion @@ -27,12 +27,12 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeDiff) < prototype(Neos.Fusion: + @@ -71,54 +71,47 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeDiff) < prototype(Neos.Fusion:
- - {change.node.label} + + {change.label} + {change.dimensions} - - {change.node.lastModificationDateTime} + + {Date.format(change.lastModificationDateTime, 'Y-m-d H:i')}
- + + + + + +
+ + + +
+ + + + + +
+ + + {contentChanges.original.resource.filename} + + + + + + {contentChanges.changed.resource.filename} + + +
+ + + + + +
+ {Date.format(contentChanges.original, 'Y-m-d H:i')} + + {Date.format(contentChanges.changed, 'Y-m-d H:i')} +
- ` diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion index 1f9b8d59462..f35b834cbba 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion @@ -13,6 +13,7 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeTableRow) < prototype(Neos.Fus i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} discardChangeAction = Neos.Fusion:ActionUri { action = 'discardNode' + format = 'json' arguments { nodeAddress = ${change.serializedNodeAddress} selectedWorkspace = ${selectedWorkspaceName} diff --git a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css index 82f15910654..97276ce39e3 100644 --- a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css +++ b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css @@ -238,3 +238,7 @@ button .icon:not(:last-child) { width: 100%; } } + +.neos-content-diff table { + width: 100%; +} From 2f6b9da005f510d8cfb93fe6703fa7806b4f34c9 Mon Sep 17 00:00:00 2001 From: Paula Kallert Date: Tue, 22 Oct 2024 16:43:10 +0200 Subject: [PATCH 036/122] Feature: Add styling --- .../Controller/WorkspaceController.php | 14 +- .../Fusion/Components/ReviewChangeDiff.fusion | 15 +- .../Components/ReviewChangeTableRow.fusion | 13 +- .../Workspaces/ContentChangeAttributes.html | 20 -- .../Workspaces/ContentChangeDiff.html | 95 --------- .../Private/Templates/Workspace/Show.html | 192 ------------------ .../Resources/Public/Styles/Module.css | 112 ++++++++++ 7 files changed, 139 insertions(+), 322 deletions(-) delete mode 100644 Neos.Workspace.Ui/Resources/Private/Partials/Workspaces/ContentChangeAttributes.html delete mode 100644 Neos.Workspace.Ui/Resources/Private/Partials/Workspaces/ContentChangeDiff.html delete mode 100644 Neos.Workspace.Ui/Resources/Private/Templates/Workspace/Show.html diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 55fa7c04252..a4fe2553cff 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -16,6 +16,9 @@ use Doctrine\DBAL\Exception as DBALException; use Neos\ContentRepository\Core\ContentRepository; +use Neos\ContentRepository\Core\Dimension\ContentDimensionId; +use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTag; +use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTags; use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Exception\WorkspaceAlreadyExists; use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\DeleteWorkspace; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\DiscardIndividualNodesFromWorkspace; @@ -791,15 +794,18 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos $change->nodeAggregateId ); $nodeType = $contentRepository->getNodeTypeManager()->getNodeType($node->nodeTypeName); - - + $dimensions = []; + foreach ($node->dimensionSpacePoint->coordinates as $id => $coordinate) { + $contentDimension = new ContentDimensionId($id); + $dimensions[] = $contentRepository->getContentDimensionSource()->getDimension($contentDimension)->getValue($coordinate)->configuration['label']; + } $change = [ - 'node' => $node, 'serializedNodeAddress' => $nodeAddress->toJson(), + 'hidden' => $node->tags->contain(SubtreeTag::disabled()), 'isRemoved' => $change->deleted, 'isNew' => $change->created, 'isMoved' => $change->moved, - 'dimensions' => $node->dimensionSpacePoint->toJson(), + 'dimensions' => $dimensions, 'lastModificationDateTime' => $node->timestamps->lastModified, 'label' => $this->nodeLabelGenerator->getLabel($node), 'icon' => $nodeType->getFullConfiguration()['ui']['icon'], diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion index 44b75449305..f7d6a472943 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion @@ -32,22 +32,19 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeDiff) < prototype(Neos.Fusion: icon={change.icon} /> {change.label} - {change.dimensions} + + + {dimension} + - - + {Date.format(change.lastModificationDateTime, 'Y-m-d H:i')} - {private.i18n.id('lastModification')} + {contentChanges.propertyLabel} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion index f35b834cbba..aacc6ba7f90 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion @@ -31,7 +31,16 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeTableRow) < prototype(Neos.Fus - + +
- diff --git a/Neos.Workspace.Ui/Resources/Private/Partials/Workspaces/ContentChangeAttributes.html b/Neos.Workspace.Ui/Resources/Private/Partials/Workspaces/ContentChangeAttributes.html deleted file mode 100644 index 0568d7d1c06..00000000000 --- a/Neos.Workspace.Ui/Resources/Private/Partials/Workspaces/ContentChangeAttributes.html +++ /dev/null @@ -1,20 +0,0 @@ -{namespace neos=Neos\Neos\ViewHelpers} - - class="neos-content-change legend-deleted" data-nodepath="{nodepath}" title="{neos:backend.translate(id: 'workspaces.legend.deleted', source: 'Modules', package: 'Neos.Neos')}" - - - class="neos-content-change legend-created" data-nodepath="{nodepath}" title="{neos:backend.translate(id: 'workspaces.legend.created', source: 'Modules', package: 'Neos.Neos')}" - - - class="neos-content-change legend-moved" data-nodepath="{nodepath}" title="{neos:backend.translate(id: 'workspaces.legend.moved', source: 'Modules', package: 'Neos.Neos')}" - - - class="neos-content-change legend-hidden" data-nodepath="{nodepath}" title="{neos:backend.translate(id: 'workspaces.legend.hidden', source: 'Modules', package: 'Neos.Neos')}" - class="neos-content-change legend-edited" data-nodepath="{nodepath}" title="{neos:backend.translate(id: 'workspaces.legend.edited', source: 'Modules', package: 'Neos.Neos')}" - - - - - - - diff --git a/Neos.Workspace.Ui/Resources/Private/Partials/Workspaces/ContentChangeDiff.html b/Neos.Workspace.Ui/Resources/Private/Partials/Workspaces/ContentChangeDiff.html deleted file mode 100644 index d29dbd43dde..00000000000 --- a/Neos.Workspace.Ui/Resources/Private/Partials/Workspaces/ContentChangeDiff.html +++ /dev/null @@ -1,95 +0,0 @@ -{namespace neos=Neos\Neos\ViewHelpers} -{namespace m=Neos\Media\ViewHelpers} -
- - - - - - - - - - - - - - - - -
- - - - {change.node.label} - - - - {change.node.lastModificationDateTime} -
{neos:backend.translate(id: contentChanges.propertyLabel)}
- - - - - - - - - - -
- - {line -> f:format.raw()} - - - - {line -> f:format.raw()} - -
-
- - - - - - -
- - - - - - - -
-
- - - - - - -
- - {contentChanges.original.resource.filename} - - - - {contentChanges.changed.resource.filename} - -
-
- - - - - - -
- - - -
-
-
-
diff --git a/Neos.Workspace.Ui/Resources/Private/Templates/Workspace/Show.html b/Neos.Workspace.Ui/Resources/Private/Templates/Workspace/Show.html deleted file mode 100644 index c071f2a9942..00000000000 --- a/Neos.Workspace.Ui/Resources/Private/Templates/Workspace/Show.html +++ /dev/null @@ -1,192 +0,0 @@ -{namespace neos=Neos\Neos\ViewHelpers} - - - - - - - - - {neos:backend.translate(id: 'workspaces.unpublishedChanges', source: 'Main', package: 'Neos.Workspace.Ui', arguments: {0: selectedWorkspaceLabel.value})} -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - {neos:backend.translate(id: 'workspaces.selectAllCurrentChanges', source: 'Main', package: 'Neos.Workspace.Ui')}
- - - - - - - -
-
- {neos:backend.translate(id: 'pathCaption', source: 'Main', package: 'Neos.Workspace.Ui')}: - -
- -
- - - - - - - - -
-
-
- -
- - - - - - - -
- -
-
- - - -
-
- -
-
- - -
- -
-
-
-
- -
{neos:backend.translate(id: 'workspaces.discardAllChangesInWorkspaceConfirmation', arguments: {0: selectedWorkspaceLabel.value}, source: 'Main', package: 'Neos.Workspace.Ui')}
-
- -
-
-
-
- -
-
-
-
- -
{neos:backend.translate(id: 'workspaces.discardSelectedChangesInWorkspaceConfirmation', arguments: {0: selectedWorkspaceLabel.value}, source: 'Main', package: 'Neos.Workspace.Ui')}
-
- -
-
-
-
- - -
- - {neos:backend.translate(id: 'workspaces.unpublishedChanges', source: 'Main', package: 'Neos.Workspace.Ui', arguments: {0: selectedWorkspaceLabel.value})} -

{neos:backend.translate(id: 'workspaces.thereAreNoUnpublishedChanges', source: 'Main', package: 'Neos.Workspace.Ui')}

- -
-
-
- - - - diff --git a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css index 97276ce39e3..7f13f15a614 100644 --- a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css +++ b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css @@ -239,6 +239,118 @@ button .icon:not(:last-child) { } } +tr.neos-change + tr.neos-change td.neos-content-change { + border-top: 1px solid var(--grayDark); +} +.neos .neos-table tbody td.neos-content-change { + background-color: var(--grayLighter); + color: var(--textOnWhite); +} .neos-content-diff table { + table-layout: fixed; width: 100%; } +.neos-content-diff table.neos-content-diff td, +.neos.neos-module .neos-content-diff table.neos-content-diff th { + height: auto; + width: 50%; + vertical-align: top; + line-height: 20px; + padding: 10px 20px 10px 10px !important; + border-top: none; + white-space: normal; + background-color: var(--grayLighter); + color: var(--textOnWhite); +} +.neos-content-diff table.neos-content-diff td img, +.neos-content-diff table.neos-content-diff th img { + max-width: 100%; + min-width: 50%; + max-height: 500px; + border: 20px solid var(--white); + box-sizing: border-box; +} +td.neos-folder i[class*="icon"] { + height: var(--unit); + line-height: var(--unit); + padding: 0 var(--defaultMargin); + margin: 0; + text-align: center; + vertical-align: middle; +} +td.neos-folder i[class*="icon"]:hover { + background: var(--blue); +} +.path-caption { + padding-left: 15px !important; + padding-right: 0; +} +.fold-toggle { + cursor: pointer; + margin-top: -1px; + margin-right: 11px; +} +.neos-content-change { + border-left: 8px solid var(--orange); +} +.neos-content-change.legend-deleted { + border-left: 8px solid var(--warning); +} +.neos-content-change.legend-created { + border-left: 8px solid var(--green); +} +.neos-content-change.legend-moved { + border-left: 8px solid var(--blue); +} +.neos-content-change.legend-hidden { + border-left: 8px solid var(--white); +} +td.actions { + width: 144px; +} +td.actions button { + display: inline-block; +} +tfoot { + font-size: var(--fontSizeSmall); + color: var(--textSubtle); +} +tfoot .legend { + margin-left: 15px; + text-align: center; + padding-right: var(--tightMargin); +} +.neos-change-stats { + width: 100px; + height: 40px; + position: relative; + display: inline-block; +} +.neos-change-stats span { + display: inline-block; + height: 8px; + position: relative; + overflow: hidden; +} +.neos-change-stats .new { + background-color: var(--green); +} +.neos-change-stats .changed { + background-color: var(--orange); +} +.neos-change-stats .removed { + background-color: var(--warning); +} +.neos-change-stats .unchanged { + background-color: var(--grayLight); + width: 100%; +} +ins, ins a { + color: var(--green); + text-decoration: none; +} +del, del a { + color: var(--red); + text-decoration: none; +} + From 31c574c05eb0aa6f006dc09a2f08723d751a7cd9 Mon Sep 17 00:00:00 2001 From: Paula Kallert Date: Tue, 22 Oct 2024 17:42:40 +0200 Subject: [PATCH 037/122] Feature: Add reload after discard --- .../Controller/WorkspaceController.php | 11 ++-- Neos.Workspace.Ui/Configuration/Views.yaml | 2 +- .../Fusion/Components/ReviewActions.fusion | 54 +++++++++++++++++++ .../Fusion/Components/ReviewChangeDiff.fusion | 8 --- .../Components/ReviewChangeTableRow.fusion | 4 +- .../Private/Fusion/Views/Review.fusion | 5 ++ 6 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index a4fe2553cff..c9f6b07c873 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -15,10 +15,10 @@ namespace Neos\Workspace\Ui\Controller; use Doctrine\DBAL\Exception as DBALException; +use GuzzleHttp\Psr7\Response; use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\Dimension\ContentDimensionId; use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTag; -use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTags; use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Exception\WorkspaceAlreadyExists; use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\DeleteWorkspace; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\DiscardIndividualNodesFromWorkspace; @@ -40,6 +40,7 @@ use Neos\Diff\Renderer\Html\HtmlArrayRenderer; use Neos\Error\Messages\Message; use Neos\Flow\Annotations as Flow; +use Neos\Flow\Exception as FlowException; use Neos\Flow\I18n\Exception\IndexOutOfBoundsException; use Neos\Flow\I18n\Exception\InvalidFormatPlaceholderException; use Neos\Flow\I18n\Translator; @@ -536,15 +537,14 @@ public function publishNodeAction(string $nodeAddress, WorkspaceName $selectedWo } /** - * Discard a a single node + * Discard a single node * * @param string $nodeAddress * @param WorkspaceName $selectedWorkspace */ - public function discardNodeAction(string $nodeAddress, WorkspaceName $selectedWorkspace): void + public function discardNodeAction(string $nodeAddress, WorkspaceName $selectedWorkspace): Response { $nodeAddress = NodeAddress::fromJsonString($nodeAddress); - $contentRepository = $this->contentRepositoryRegistry->get($nodeAddress->contentRepositoryId); $command = DiscardIndividualNodesFromWorkspace::create( @@ -559,6 +559,9 @@ public function discardNodeAction(string $nodeAddress, WorkspaceName $selectedWo $contentRepository->handle($command); $this->addFlashMessage($this->getModuleLabel('workspaces.selectedChangeHasBeenDiscarded')); + + $this->redirect('review', null, null, ['workspace' => $selectedWorkspace->value]); + } /** diff --git a/Neos.Workspace.Ui/Configuration/Views.yaml b/Neos.Workspace.Ui/Configuration/Views.yaml index 247ad31b632..90a59214b4f 100644 --- a/Neos.Workspace.Ui/Configuration/Views.yaml +++ b/Neos.Workspace.Ui/Configuration/Views.yaml @@ -1,4 +1,4 @@ -- requestFilter: 'isFormat("html") && isPackage("Neos.Workspace.Ui") && isController("Workspace")' +- requestFilter: 'isPackage("Neos.Workspace.Ui") && isController("Workspace")' viewObjectName: 'Neos\Fusion\View\FusionView' options: fusionPathPatterns: diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion new file mode 100644 index 00000000000..6f4586b13ec --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion @@ -0,0 +1,54 @@ +## +# Renders a document for review +# +prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Component) { + + relativePath = '' + + @private { + i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} + + } + + renderer = afx` +
+ +
+ ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion index f7d6a472943..6e438c11686 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion @@ -11,14 +11,6 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeDiff) < prototype(Neos.Fusion: @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} - discardChangeAction = Neos.Fusion:ActionUri { - action = 'discardNode' - arguments { - nodeAddress = ${change.serializedNodeAddress} - selectedWorkspace = ${selectedWorkspaceName} - - } - } } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion index aacc6ba7f90..cd003ae796d 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion @@ -15,7 +15,7 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeTableRow) < prototype(Neos.Fus action = 'discardNode' format = 'json' arguments { - nodeAddress = ${change.serializedNodeAddress} + nodeAddress = ${props.change.serializedNodeAddress} selectedWorkspace = ${selectedWorkspaceName} } @@ -64,6 +64,8 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeTableRow) < prototype(Neos.Fus icon="fas fa-trash-alt icon-white" attributes.hx-get={private.discardChangeAction} attributes.hx-swap='innerHTML' + attributes.hx-target="#workspaceReview" + attributes.hx-swap="innerHTML" /> diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion index ed31e438818..b6e2ecc2752 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion @@ -24,6 +24,7 @@ Neos.Workspace.Ui.WorkspaceController.review = Neos.Fusion:Component {
@@ -34,7 +35,11 @@ Neos.Workspace.Ui.WorkspaceController.review = Neos.Fusion:Component { {props.i18n.id('workspaces.unpublishedChanges').arguments([selectedWorkspaceLabel]).translate()} +
+ {props.i18n.id('workspaces.reviewWorkspace.disabled').arguments([selectedWorkspaceLabel]).translate()} +
From f35efffc1e40069fb0eda0416845e23c2c83db12 Mon Sep 17 00:00:00 2001 From: Paula Kallert Date: Tue, 22 Oct 2024 21:51:54 +0200 Subject: [PATCH 038/122] Feature: discard action --- .../Classes/Controller/WorkspaceController.php | 16 ++++++++-------- .../Configuration/Settings.Neos.yaml | 1 + .../Components/ReviewChangeTableRow.fusion | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index c9f6b07c873..9eee2d3e2cd 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -542,19 +542,19 @@ public function publishNodeAction(string $nodeAddress, WorkspaceName $selectedWo * @param string $nodeAddress * @param WorkspaceName $selectedWorkspace */ - public function discardNodeAction(string $nodeAddress, WorkspaceName $selectedWorkspace): Response + public function discardNodeAction(string $nodeAddress, WorkspaceName $selectedWorkspace): void { $nodeAddress = NodeAddress::fromJsonString($nodeAddress); $contentRepository = $this->contentRepositoryRegistry->get($nodeAddress->contentRepositoryId); - + $nodeIdsToDiscard = NodeIdsToPublishOrDiscard::create( + new NodeIdToPublishOrDiscard( + $nodeAddress->aggregateId, + $nodeAddress->dimensionSpacePoint + ) + ); $command = DiscardIndividualNodesFromWorkspace::create( $selectedWorkspace, - NodeIdsToPublishOrDiscard::create( - new NodeIdToPublishOrDiscard( - $nodeAddress->aggregateId, - $nodeAddress->dimensionSpacePoint - ) - ), + $nodeIdsToDiscard ); $contentRepository->handle($command); diff --git a/Neos.Workspace.Ui/Configuration/Settings.Neos.yaml b/Neos.Workspace.Ui/Configuration/Settings.Neos.yaml index f4a2e9a860e..5a6eded8a75 100644 --- a/Neos.Workspace.Ui/Configuration/Settings.Neos.yaml +++ b/Neos.Workspace.Ui/Configuration/Settings.Neos.yaml @@ -13,6 +13,7 @@ Neos: javaScripts: 'HtmxLibrary': 'resource://Neos.Workspace.Ui/Public/Scripts/htmx.min.js' 'Module': 'resource://Neos.Workspace.Ui/Public/Scripts/Module.js' + 'Review': 'resource://Neos.Workspace.Ui/Public/Scripts/Review.js' styleSheets: 'Module': 'resource://Neos.Workspace.Ui/Public/Styles/Module.css' diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion index cd003ae796d..222587f994f 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion @@ -58,7 +58,7 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeTableRow) < prototype(Neos.Fus form="postHelper" formaction="{f:uri.action(action: 'publishNode', arguments: {nodeAddress: change.serializedNodeAddress, selectedWorkspace: selectedWorkspace.workspaceName.value})}" type="submit" class="neos-button neos-button-primary neos-pull-right" title={private.i18n.id('publish')} data-neos-toggle="tooltip"> - + Date: Tue, 22 Oct 2024 22:20:22 +0200 Subject: [PATCH 039/122] TASK: Workspace management create and delete action --- .../Controller/WorkspaceController.php | 23 +++++------ .../Fusion/Components/Modals/Delete.fusion | 10 ++--- .../Private/Fusion/Views/Delete.fusion | 7 ++-- .../Resources/Private/Fusion/Views/New.fusion | 39 ------------------- 4 files changed, 21 insertions(+), 58 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index a5b9bc1f64f..66fcef6bb93 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -181,16 +181,15 @@ public function showAction(WorkspaceName $workspace): void ]); } - public function newAction(ContentRepositoryId $contentRepositoryId): void + public function newAction(): void { + $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); $this->view->assign('baseWorkspaceOptions', $this->prepareBaseWorkspaceOptions($contentRepository)); - $this->view->assign('contentRepositoryId', $contentRepositoryId->value); } public function createAction( - ContentRepositoryId $contentRepositoryId, WorkspaceTitle $title, WorkspaceName $baseWorkspace, WorkspaceDescription $description, @@ -199,7 +198,10 @@ public function createAction( if ($currentUser === null) { throw new \RuntimeException('No user authenticated', 1718303756); } + + $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; $workspaceName = $this->workspaceService->getUniqueWorkspaceName($contentRepositoryId, $title->value); + try { $this->workspaceService->createSharedWorkspace( $contentRepositoryId, @@ -421,10 +423,8 @@ public function deleteAction(WorkspaceName $workspaceName): void ); $this->addFlashMessage($message, '', Message::SEVERITY_WARNING); $this->throwStatus(403, 'Workspace has unpublished nodes'); - } - - // Render a confirmation form if the request is not a POST request - if ($this->request->getHttpRequest()->getMethod() === 'POST') { + // delete workspace on POST + } elseif ($this->request->getHttpRequest()->getMethod() === 'POST') { $contentRepository->handle( DeleteWorkspace::create( $workspaceName, @@ -437,8 +437,10 @@ public function deleteAction(WorkspaceName $workspaceName): void [$workspaceMetadata->title->value], ) ); + // Render a confirmation form if the request is not a POST request } else { - $this->view->assign('workspace', $workspace); + $this->view->assign('workspaceName', $workspace->workspaceName->value); + $this->view->assign('workspaceTitle', $workspaceMetadata->title->value); } } @@ -1165,9 +1167,8 @@ protected function getWorkspaceListItems( $workspace->workspaceName, $this->userService->getCurrentUser() ); - if (!$workspacesPermissions->read) { // todo check corrrect? - continue; - } + + // TODO: check permissions (read?, admin?) $workspaceListItems[$workspace->workspaceName->value] = new WorkspaceListItem( $workspace->workspaceName->value, diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Delete.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Delete.fusion index 278262bb420..12f9f26a197 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Delete.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Delete.fusion @@ -1,11 +1,11 @@ prototype(Neos.Workspace.Ui:Component.Modal.Delete) < prototype(Neos.Fusion:Component) { - /// Neos\ContentRepository\Core\Projection\Workspace\Workspace - workspace = null + workspaceName = null + workspaceTitle = null @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} popoverId = 'workspace-delete-modal' - workspaceTableRowId = ${'workspace-row-' + props.workspace.workspaceName.value} + workspaceTableRowId = ${'workspace-row-' + props.workspaceName} } renderer = afx` @@ -20,7 +20,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.Delete) < prototype(Neos.Fusion:Comp
- {private.i18n.id('workspaces.dialog.confirmWorkspaceDeletion').arguments([props.workspace.workspaceTitle.value])} + {private.i18n.id('workspaces.dialog.confirmWorkspaceDeletion').arguments([props.workspaceTitle])}
@@ -38,7 +38,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.Delete) < prototype(Neos.Fusion:Comp ` # Empty template for the delete response as the payload is contained in the HTTP headers diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion index 15460df993c..76c827c1735 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion @@ -78,45 +78,6 @@ Neos.Workspace.Ui.WorkspaceController.new = Neos.Fusion:Component {
- - -

- {props.i18n.id('workspaces.workspace.visibility')} -

- - - - - {props.i18n.id('workspaces.workspace.visibility.private').translate()} - - {props.i18n.id('workspaces.workspace.visibility.private.help')} - - - -
- - - - {props.i18n.id('workspaces.workspace.visibility.internal').translate()} - - {props.i18n.id('workspaces.workspace.visibility.internal.help')} - - - -
-
- - - - {props.i18n.id('workspaces.createWorkspace')} From 5c47933317b872c453ba32a67a78cd69ac73eb6a Mon Sep 17 00:00:00 2001 From: Robert Baruck Date: Tue, 22 Oct 2024 22:32:42 +0200 Subject: [PATCH 040/122] TASK: Fix EditWorkspaceDto --- .../Classes/Controller/WorkspaceController.php | 8 ++++---- .../Classes/ViewModel/EditWorkspaceDto.php | 10 ++++++---- .../Resources/Private/Fusion/Views/Edit.fusion | 8 ++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 66fcef6bb93..233e5178c4c 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -270,10 +270,9 @@ public function editAction(WorkspaceName $workspaceName): void $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspace->workspaceName); $workspaceDto = new EditWorkspaceDto( - workspaceName: $workspace->workspaceName->value, - workspaceTitle: $workspaceMetadata->title->value, - workspaceDescription: $workspaceMetadata->description->value, - workspaceOwnerId: $workspaceMetadata->ownerUserId->value, + workspaceName: $workspace->workspaceName, + workspaceTitle: $workspaceMetadata->title, + workspaceDescription: $workspaceMetadata->description, ); @@ -425,6 +424,7 @@ public function deleteAction(WorkspaceName $workspaceName): void $this->throwStatus(403, 'Workspace has unpublished nodes'); // delete workspace on POST } elseif ($this->request->getHttpRequest()->getMethod() === 'POST') { + // TODO: WorkspaceMetadata is not being removed with this $contentRepository->handle( DeleteWorkspace::create( $workspaceName, diff --git a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceDto.php b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceDto.php index 29c6896305d..a35e14674b8 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceDto.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceDto.php @@ -14,16 +14,18 @@ namespace Neos\Workspace\Ui\ViewModel; +use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\Flow\Annotations as Flow; +use Neos\Neos\Domain\Model\WorkspaceDescription; +use Neos\Neos\Domain\Model\WorkspaceTitle; #[Flow\Proxy(false)] final readonly class EditWorkspaceDto { public function __construct( - public string $workspaceName, - public string $workspaceTitle, - public string $workspaceDescription, - public string $workspaceOwnerId, + public WorkspaceName $workspaceName, + public WorkspaceTitle $workspaceTitle, + public WorkspaceDescription $workspaceDescription, ) { } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion index 3c6e52179d2..b5c8a206202 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion @@ -2,14 +2,14 @@ Neos.Workspace.Ui.WorkspaceController.edit = Neos.Fusion:Component { disableBaseWorkspaceSelector = ${disableBaseWorkspaceSelector} /// array baseWorkspaceOptions = ${baseWorkspaceOptions} - /// boolean + /// \Neos\Workspace\Ui\ViewModel\EditWorkspaceDto workspace = ${workspace} renderer = afx` Date: Wed, 23 Oct 2024 10:57:12 +0200 Subject: [PATCH 041/122] TASK: change baseWorkspace in edit workspace --- .../Domain/Service/WorkspaceService.php | 16 +++++++++ .../Controller/WorkspaceController.php | 35 +++++++++---------- ...spaceDto.php => EditWorkspaceFormData.php} | 8 ++++- .../Fusion/Components/Modals/Edit.fusion | 20 +++++++---- .../Private/Fusion/Views/Edit.fusion | 17 ++++----- .../Private/Translations/en/Main.xlf | 3 ++ 6 files changed, 62 insertions(+), 37 deletions(-) rename Neos.Workspace.Ui/Classes/ViewModel/{EditWorkspaceDto.php => EditWorkspaceFormData.php} (71%) diff --git a/Neos.Neos/Classes/Domain/Service/WorkspaceService.php b/Neos.Neos/Classes/Domain/Service/WorkspaceService.php index 551865b24ab..7e82fe9cac4 100644 --- a/Neos.Neos/Classes/Domain/Service/WorkspaceService.php +++ b/Neos.Neos/Classes/Domain/Service/WorkspaceService.php @@ -21,6 +21,7 @@ use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Command\CreateRootWorkspace; use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Command\CreateWorkspace; use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Exception\WorkspaceAlreadyExists; +use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\ChangeBaseWorkspace; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; @@ -332,6 +333,21 @@ public function getUniqueWorkspaceName(ContentRepositoryId $contentRepositoryId, throw new \RuntimeException(sprintf('Failed to find unique workspace name for "%s" after %d attempts.', $candidate, $attempt - 1), 1725975479); } + public function setBaseWorkspace(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName, WorkspaceName $baseWorkspaceName): void + { + $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); + $workspace = $this->requireWorkspace($contentRepositoryId, $workspaceName); + + if ($workspace->workspaceName->equals($baseWorkspaceName)) { + // WHY: Do not update if the base workspace is the same + return; + } + + $contentRepository->handle( + ChangeBaseWorkspace::create($workspaceName, $baseWorkspaceName) + ); + } + // ------------------ private function loadWorkspaceMetadata(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName): ?WorkspaceMetadata diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 233e5178c4c..5e5d20c7b00 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -65,7 +65,7 @@ use Neos\Neos\FrontendRouting\SiteDetection\SiteDetectionResult; use Neos\Neos\PendingChangesProjection\ChangeFinder; use Neos\Neos\Utility\NodeTypeWithFallbackProvider; -use Neos\Workspace\Ui\ViewModel\EditWorkspaceDto; +use Neos\Workspace\Ui\ViewModel\EditWorkspaceFormData; use Neos\Workspace\Ui\ViewModel\PendingChanges; use Neos\Workspace\Ui\ViewModel\WorkspaceListItem; use Neos\Workspace\Ui\ViewModel\WorkspaceListItems; @@ -269,26 +269,15 @@ public function editAction(WorkspaceName $workspaceName): void $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspace->workspaceName); - $workspaceDto = new EditWorkspaceDto( + $editWorkspaceDto = new EditWorkspaceFormData( workspaceName: $workspace->workspaceName, workspaceTitle: $workspaceMetadata->title, workspaceDescription: $workspaceMetadata->description, + workspaceHasChanges: $this->computePendingChanges($workspace, $contentRepository)->total > 0, + baseWorkspaceOptions: $this->prepareBaseWorkspaceOptions($contentRepository, $workspaceName), ); - - $this->view->assign('workspace', $workspaceDto); - $this->view->assign('baseWorkspaceOptions', $this->prepareBaseWorkspaceOptions($contentRepository, $workspaceName)); - // TODO - $this->view->assign('disableBaseWorkspaceSelector', true); - - // $this->publishingService->getUnpublishedNodesCount($workspace) > 0); - - // TODO - // This has been here: $this->userService->currentUserCanTransferOwnershipOfWorkspace($workspace) - // We need to calc this from the new permissions model - $this->view->assign('showOwnerSelector', true); - - $this->view->assign('ownerOptions', $this->prepareOwnerOptions()); + $this->view->assign('editWorkspaceFormData', $editWorkspaceDto); } /** @@ -304,6 +293,7 @@ public function updateAction( WorkspaceName $workspaceName, WorkspaceTitle $title, WorkspaceDescription $description, + WorkspaceName $baseWorkspace, ): void { $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); @@ -321,6 +311,8 @@ public function updateAction( ); $this->throwStatus(404, 'Workspace does not exist'); } + + // Update Metadata $this->workspaceService->setWorkspaceTitle( $contentRepositoryId, $workspaceName, @@ -331,6 +323,14 @@ public function updateAction( $workspaceName, $description, ); + + // Update Base Workspace + $this->workspaceService->setBaseWorkspace( + $contentRepositoryId, + $workspaceName, + $baseWorkspace, + ); + $this->addFlashMessage( $this->getModuleLabel( 'workspaces.workspaceHasBeenUpdated', @@ -338,9 +338,6 @@ public function updateAction( ) ); - $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest()) - ->contentRepositoryId; - $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); $userWorkspace = $this->getUserWorkspace($contentRepository); $workspaceListItems = $this->getWorkspaceListItems($userWorkspace, $contentRepository); diff --git a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceDto.php b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php similarity index 71% rename from Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceDto.php rename to Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php index a35e14674b8..de89c3a0536 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceDto.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php @@ -20,12 +20,18 @@ use Neos\Neos\Domain\Model\WorkspaceTitle; #[Flow\Proxy(false)] -final readonly class EditWorkspaceDto +final readonly class EditWorkspaceFormData { public function __construct( public WorkspaceName $workspaceName, public WorkspaceTitle $workspaceTitle, public WorkspaceDescription $workspaceDescription, + public bool $workspaceHasChanges, + /** + * Options for the baseWorkspace selector where the key is the workspace name and the value is the workspace title. + * @var array + */ + public array $baseWorkspaceOptions, ) { } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion index 35ef800bb4a..3d41ceb6f35 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion @@ -1,7 +1,6 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Component) { - /// bool - disableBaseWorkspaceSelector = true - /// array + workspaceHasChanges = false + /// array baseWorkspaceOptions = ${[]} workspaceName = '' @@ -9,9 +8,6 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Compon workspaceDescription = '' baseWorkspaceName = '' - isPrivateWorkspace = false - isInternalWorkspace = false - @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} popoverId = 'workspace-edit-modal' @@ -88,7 +84,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Compon class="neos-control-group" > +

+ {' '} + {private.i18n.id('workspaces.cantChangeBaseWorkspaceBecauseWorkspaceContainsChanges')} +

diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion index b5c8a206202..967136ca41f 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion @@ -1,18 +1,15 @@ Neos.Workspace.Ui.WorkspaceController.edit = Neos.Fusion:Component { - disableBaseWorkspaceSelector = ${disableBaseWorkspaceSelector} - /// array - baseWorkspaceOptions = ${baseWorkspaceOptions} - /// \Neos\Workspace\Ui\ViewModel\EditWorkspaceDto - workspace = ${workspace} + /// \Neos\Workspace\Ui\ViewModel\EditWorkspaceFormData + editWorkspaceFormData = ${editWorkspaceFormData} renderer = afx` ` } diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf index 40e4c1822a9..c1a210cee26 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf @@ -231,6 +231,9 @@ Your personal workspace contains changes, please publish or discard them first. + + You cannot change the base workspace of workspace with unpublished changes. + Did not delete workspace "{0}" because it currently contains {1} node. From 55dc0df9f2fdf0f5da3a74ce74cbd3c62d5db3a9 Mon Sep 17 00:00:00 2001 From: Paula Kallert Date: Wed, 23 Oct 2024 11:04:46 +0200 Subject: [PATCH 042/122] Feature: Add action footer back --- .../Fusion/Components/ReviewActions.fusion | 106 ++++++++++++------ .../Components/WorkspaceTableRow.fusion | 4 +- .../Private/Fusion/Views/Review.fusion | 1 + 3 files changed, 75 insertions(+), 36 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion index 6f4586b13ec..a69bf23b669 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion @@ -3,52 +3,90 @@ # prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Component) { - relativePath = '' + canPublishToBaseWorkspace = ${canPublishToBaseWorkspace} + selectedWorkspaceName = ${selectedWorkspaceName} + baseWorkspaceLabel = ${baseWorkspaceLabel} @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} + indexWorkspaceUri = Neos.Fusion:ActionUri { + action = 'index' + + } + discardWorkspaceUri = Neos.Fusion:ActionUri { + action = 'discardWorkspace' + arguments { + workspace = ${props.selectedWorkspaceName} + } + } + publishWorkspaceUri = Neos.Fusion:ActionUri { + action = 'publishWorkspace' + arguments { + workspace = ${props.selectedWorkspaceName} + } + } + discardSelectedChanges = Neos.Fusion:ActionUri { + action = 'PublishOrDiscardNodes' + arguments { + action = 'discard' + workspace = ${props.selectedWorkspaceName} + } + } + publishSelectedChangesTo = Neos.Fusion:ActionUri { + action = 'PublishOrDiscardNodes' + arguments { + action = 'publish' + workspace = ${props.selectedWorkspaceName} + } + } } renderer = afx` -
-
` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion index 0a7e6604c79..9837c269db3 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion @@ -55,7 +55,7 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion // TODO removed internal = 'users' with-acl = 'user-plus' } - showWorkspaceUri = Neos.Fusion:ActionUri { + reviewWorkspaceUri = Neos.Fusion:ActionUri { action = 'review' arguments { workspace = ${props.workspaceListItem.name} @@ -123,7 +123,7 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion label={private.i18n.id('workspaces.reviewWorkspace.label')} title={private.i18n.id(props.workspaceListItem.pendingChanges.total ? 'workspaces.reviewWorkspace' : 'workspaces.reviewWorkspace.noChanges').arguments([props.workspaceListItem.title])} attributes.disabled={false && props.workspaceListItem.pendingChanges.total == 0} - attributes.hx-get={private.showWorkspaceUri} + attributes.hx-get={private.reviewWorkspaceUri} attributes.hx-target="body" /> {props.i18n.id('workspaces.reviewWorkspace.disabled').arguments([selectedWorkspaceLabel]).translate()} + From 7f26ffa33fb6bbde2834d4565b5977831c4d54e6 Mon Sep 17 00:00:00 2001 From: Paula Kallert Date: Wed, 23 Oct 2024 11:06:12 +0200 Subject: [PATCH 043/122] Feature: remove unused class --- Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index e56fef7b8d4..05a4c863921 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -15,7 +15,6 @@ namespace Neos\Workspace\Ui\Controller; use Doctrine\DBAL\Exception as DBALException; -use GuzzleHttp\Psr7\Response; use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\Dimension\ContentDimensionId; use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTag; From 8557831352dccaff1f5565484a546a31d3d287cf Mon Sep 17 00:00:00 2001 From: Paula Kallert Date: Wed, 23 Oct 2024 13:33:09 +0200 Subject: [PATCH 044/122] Feature: Make publish Documents work --- .../Controller/WorkspaceController.php | 33 +++++------------ .../Fusion/Components/ReviewActions.fusion | 4 +- .../Components/ReviewChangeTableRow.fusion | 32 +--------------- .../Components/ReviewDocumentTableRow.fusion | 37 +++++++++++++++++++ .../Private/Fusion/Views/Review.fusion | 2 +- 5 files changed, 51 insertions(+), 57 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 7ad33b4a37e..4f910500c3b 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -513,22 +513,15 @@ public function rebaseAndRedirectAction(string $targetNode, WorkspaceName $targe * @param string $nodeAddress * @param WorkspaceName $selectedWorkspace */ - public function publishNodeAction(string $nodeAddress, WorkspaceName $selectedWorkspace): void + public function publishDocumentAction(string $nodeAddress, WorkspaceName $selectedWorkspace): void { $nodeAddress = NodeAddress::fromJsonString($nodeAddress); - - $contentRepository = $this->contentRepositoryRegistry->get($nodeAddress->contentRepositoryId); - - $command = PublishIndividualNodesFromWorkspace::create( + $contentRepositoryId = $nodeAddress->contentRepositoryId; + $this->workspacePublishingService->publishChangesInDocument( + $contentRepositoryId, $selectedWorkspace, - NodeIdsToPublishOrDiscard::create( - new NodeIdToPublishOrDiscard( - $nodeAddress->aggregateId, - $nodeAddress->dimensionSpacePoint - ) - ), + $nodeAddress->aggregateId ); - $contentRepository->handle($command); $this->addFlashMessage($this->getModuleLabel('workspaces.selectedChangeHasBeenPublished')); $this->redirect('review', null, null, ['workspace' => $selectedWorkspace->value]); @@ -540,21 +533,15 @@ public function publishNodeAction(string $nodeAddress, WorkspaceName $selectedWo * @param string $nodeAddress * @param WorkspaceName $selectedWorkspace */ - public function discardNodeAction(string $nodeAddress, WorkspaceName $selectedWorkspace): void + public function discardDocumentAction(string $nodeAddress, WorkspaceName $selectedWorkspace): void { $nodeAddress = NodeAddress::fromJsonString($nodeAddress); - $contentRepository = $this->contentRepositoryRegistry->get($nodeAddress->contentRepositoryId); - $nodeIdsToDiscard = NodeIdsToPublishOrDiscard::create( - new NodeIdToPublishOrDiscard( - $nodeAddress->aggregateId, - $nodeAddress->dimensionSpacePoint - ) - ); - $command = DiscardIndividualNodesFromWorkspace::create( + $contentRepositoryId = $nodeAddress->contentRepositoryId; + $this->workspacePublishingService->discardChangesInDocument( + $contentRepositoryId, $selectedWorkspace, - $nodeIdsToDiscard + $nodeAddress->aggregateId ); - $contentRepository->handle($command); $this->addFlashMessage($this->getModuleLabel('workspaces.selectedChangeHasBeenDiscarded')); diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion index a69bf23b669..20d092c14e8 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion @@ -78,9 +78,9 @@ prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Com /> diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion index 222587f994f..370ad4efe73 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion @@ -11,16 +11,6 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeTableRow) < prototype(Neos.Fus @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} - discardChangeAction = Neos.Fusion:ActionUri { - action = 'discardNode' - format = 'json' - arguments { - nodeAddress = ${props.change.serializedNodeAddress} - selectedWorkspace = ${selectedWorkspaceName} - - } - } - } renderer = afx` @@ -46,27 +36,7 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeTableRow) < prototype(Neos.Fus relativePath={relativePath} document={document}/> - - -
- -
- - - - + ` diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion index 5c9d85ff6c3..dde022ceef8 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion @@ -26,6 +26,23 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F action = 'preview' // todo fixme @process.addQuery = ${value + '?node=' + document.documentNodeAddress} + + } + discardDocumentAction = Neos.Fusion:ActionUri { + action = 'discardDocument' + format = 'json' + arguments { + nodeAddress = ${document.documentNodeAddress} + selectedWorkspace = ${selectedWorkspaceName} + } + } + publishDocumentAction = Neos.Fusion:ActionUri { + action = 'publishDocument' + format = 'json' + arguments { + nodeAddress = ${document.documentNodeAddress} + selectedWorkspace = ${selectedWorkspaceName} + } } } @@ -48,6 +65,26 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F - + From 089d382ead204b541d6ef835a7895a3b482adee4 Mon Sep 17 00:00:00 2001 From: Paula Kallert Date: Wed, 23 Oct 2024 13:39:11 +0200 Subject: [PATCH 045/122] Feature: Remove single node publish --- .../Private/Fusion/Components/ReviewChangeTableRow.fusion | 4 ---- .../Private/Fusion/Components/ReviewDocumentTableRow.fusion | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion index 370ad4efe73..d082c564cc8 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion @@ -16,10 +16,6 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeTableRow) < prototype(Neos.Fus renderer = afx` - - +
+
+ {private.i18n.id('applyChanges')} - +
-
- -
` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion index 8bd6c36feaa..48bc6012bc0 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion @@ -7,10 +7,16 @@ Neos.Workspace.Ui.WorkspaceController.edit = Neos.Fusion:Component { workspaceName={props.editWorkspaceFormData.workspaceName.value} workspaceTitle={props.editWorkspaceFormData.workspaceTitle.value} workspaceDescription={props.editWorkspaceFormData.workspaceDescription.value} + baseWorkspaceName={props.editWorkspaceFormData.baseWorkspaceName.value} workspaceHasChanges={props.editWorkspaceFormData.workspaceHasChanges} - baseWorkspaceName={props.editWorkspaceFormData.baseWorkspaceName.value} baseWorkspaceOptions={props.editWorkspaceFormData.baseWorkspaceOptions} + + roleAssignmentsVisible={props.editWorkspaceFormData.roleAssignmentsVisible} + roleAssignmentsEditable={props.editWorkspaceFormData.roleAssignmentsEditable} + roleAssignments={props.editWorkspaceFormData.roleAssignments} + roleAssignmentUserOptions={props.editWorkspaceFormData.roleAssignmentUserOptions} + roleAssignmentGroupOptions={props.editWorkspaceFormData.roleAssignmentGroupOptions} /> ` } diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf index 74818870c22..310373dcdb4 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf @@ -81,6 +81,27 @@ Last modified + + Managed by Users + + + Managed by Groups + + + Permissions + + + Typ + + + Name + + + Role + + + Actions + {0} node was added From 005ce332a4222574448e8685fb58659e8fcf7655 Mon Sep 17 00:00:00 2001 From: Paula Kallert Date: Fri, 25 Oct 2024 11:30:07 +0200 Subject: [PATCH 053/122] Feature: Adjust SiteChanges --- .../Controller/WorkspaceController.php | 73 +++++++++---------- .../Classes/ViewModel/DocumentChangeItem.php | 7 +- .../Classes/ViewModel/DocumentItem.php | 28 +++++++ .../Components/ReviewChangeTableRow.fusion | 2 +- .../Components/ReviewDocumentTableRow.fusion | 18 ++--- 5 files changed, 72 insertions(+), 56 deletions(-) create mode 100644 Neos.Workspace.Ui/Classes/ViewModel/DocumentItem.php diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 38b2c4dae34..1511f7ed134 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -68,6 +68,7 @@ use Neos\Neos\FrontendRouting\NodeUriBuilderFactory; use Neos\Neos\FrontendRouting\SiteDetection\SiteDetectionResult; use Neos\Neos\PendingChangesProjection\ChangeFinder; +use Neos\Neos\PendingChangesProjection\Changes; use Neos\Neos\Utility\NodeTypeWithFallbackProvider; use Neos\Workspace\Ui\ViewModel\ChangeItem; use Neos\Workspace\Ui\ViewModel\ContentChangeItem; @@ -78,6 +79,7 @@ use Neos\Workspace\Ui\ViewModel\ContentChanges\AssetContentChange; use Neos\Workspace\Ui\ViewModel\ContentChanges\DateTimeContentChange; use Neos\Workspace\Ui\ViewModel\DocumentChangeItem; +use Neos\Workspace\Ui\ViewModel\DocumentItem; use Neos\Workspace\Ui\ViewModel\EditWorkspaceFormData; use Neos\Workspace\Ui\ViewModel\PendingChanges; use Neos\Workspace\Ui\ViewModel\WorkspaceListItem; @@ -663,20 +665,12 @@ public function discardWorkspaceAction(WorkspaceName $workspace): void protected function computePendingChanges(Workspace $selectedWorkspace, ContentRepository $contentRepository): PendingChanges { $changesCount = ['new' => 0, 'changed' => 0, 'removed' => 0]; - foreach ($this->computeSiteChanges($selectedWorkspace, $contentRepository) as $siteChanges) { - foreach ($siteChanges['documents'] as $documentChanges) { - foreach ($documentChanges['changes'] as $change) { - if ($change['isRemoved'] === true) { - $changesCount['removed']++; - } elseif ($change['isNew']) { - $changesCount['new']++; - } else { - $changesCount['changed']++; - } - } - } + foreach($this->getChangesFromWorkspace($selectedWorkspace, $contentRepository) as $change) { + if($change->deleted) $changesCount['removed']++; + elseif($change->created) $changesCount['new']++; + else $changesCount['changed']++; } - return new PendingChanges(new: $changesCount['new'], changed: $changesCount['changed'], removed: $changesCount['removed']); + return new PendingChanges(new: $changesCount['new'], changed: $changesCount['changed'], removed:$changesCount['removed']); } /** @@ -686,11 +680,7 @@ protected function computePendingChanges(Workspace $selectedWorkspace, ContentRe protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepository $contentRepository): array { $siteChanges = []; - $changes = $contentRepository->projectionState(ChangeFinder::class) - ->findByContentStreamId( - $selectedWorkspace->currentContentStreamId - ); - + $changes = $this->getChangesFromWorkspace($selectedWorkspace, $contentRepository); foreach ($changes as $change) { $workspaceName = $selectedWorkspace->workspaceName; if ($change->deleted) { @@ -741,6 +731,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos // Neither $documentNode, $siteNode or its cannot really be null, this is just for type checks; // We should probably throw an exception though + if ($documentNode !== null && $siteNode !== null && $siteNode->name) { $siteNodeName = $siteNode->name->value; // Reverse `$documentPathSegments` to start with the site node. @@ -766,27 +757,27 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos ) ); - if (!isset($siteChanges[$siteNodeName]['siteNode'])) { - $siteChanges[$siteNodeName]['siteNode'] - = $this->siteRepository->findOneByNodeName(SiteNodeName::fromString($siteNodeName)); + if(!isset($siteChanges[$siteNodeName]['documents'][$documentPath]['document'])) { + $documentNodeAddress = NodeAddress::create( + $contentRepository->id, + $selectedWorkspace->workspaceName, + $documentNode->originDimensionSpacePoint->toDimensionSpacePoint(), + $documentNode->aggregateId + ); + $siteChanges[$siteNodeName]['documents'][$documentPath]['document'] = new DocumentItem( + documentBreadCrumb: array_reverse($documentPathSegmentsNames), + aggregateId: $documentNodeAddress->aggregateId->value, + documentNodeAddress: $documentNodeAddress->toJson() + ); } - $documentNodeAddress = NodeAddress::create( - $contentRepository->id, - $selectedWorkspace->workspaceName, - $documentNode->originDimensionSpacePoint->toDimensionSpacePoint(), - $documentNode->aggregateId - ); - - $siteChanges[$siteNodeName]['documents'][$documentPath]['documentNode'] = $documentNode; - $siteChanges[$siteNodeName]['documents'][$documentPath]['documentBreadCrumb'] = array_reverse($documentPathSegmentsNames); - $siteChanges[$siteNodeName]['documents'][$documentPath]['aggregateId'] = $documentNodeAddress->aggregateId; - $siteChanges[$siteNodeName]['documents'][$documentPath]['documentNodeAddress'] = $documentNodeAddress->toJson(); // We need to set `isNew` and `isMoved` on document level to make our JS behave as before. if ($documentNode->equals($node)) { - $siteChanges[$siteNodeName]['documents'][$documentPath]['isNew'] = $change->created; - $siteChanges[$siteNodeName]['documents'][$documentPath]['isMoved'] = $change->moved; - $siteChanges[$siteNodeName]['documents'][$documentPath]['isDeleted'] = $change->deleted; + $siteChanges[$siteNodeName]['documents'][$documentPath]['documentChanges'] = new DocumentChangeItem( + isRemoved: $change->deleted, + isNew: $change->created, + isMoved: $change->moved + ); } // As for changes of type `delete` we are using nodes from the live workspace @@ -804,7 +795,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos $contentDimension = new ContentDimensionId($id); $dimensions[] = $contentRepository->getContentDimensionSource()->getDimension($contentDimension)->getValue($coordinate)->configuration['label']; } - $change = new ChangeItem ( + $siteChanges[$siteNodeName]['documents'][$documentPath]['changes'][$relativePath] = new ChangeItem ( serializedNodeAddress: $nodeAddress->toJson(), hidden: $node->tags->contain(SubtreeTag::disabled()), isRemoved: $change->deleted, @@ -820,11 +811,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos $contentRepository ) ); - - $siteChanges[$siteNodeName]['documents'][$documentPath]['changes'][$relativePath] = $change; } - - $siteChanges[$siteNodeName]['documents'][$documentPath]['changesCount'] = count($siteChanges[$siteNodeName]['documents'][$documentPath]['changes']); } } @@ -1251,4 +1238,10 @@ protected function getWorkspaceListItems( } return WorkspaceListItems::fromArray($workspaceListItems); } + protected function getChangesFromWorkspace(Workspace $selectedWorkspace,ContentRepository $contentRepository ): Changes{ + return $contentRepository->projectionState(ChangeFinder::class) + ->findByContentStreamId( + $selectedWorkspace->currentContentStreamId + ); + } } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/DocumentChangeItem.php b/Neos.Workspace.Ui/Classes/ViewModel/DocumentChangeItem.php index 8a18db10524..82772dca877 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/DocumentChangeItem.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/DocumentChangeItem.php @@ -20,14 +20,9 @@ readonly class DocumentChangeItem { public function __construct( - public array $documentBreadCrumb, - public string $aggregateId, - public string $documentNodeAddress, public bool $isRemoved, public bool $isNew, - public bool $isMoved, - public ChangeItems $changes, - public int $changesCount, + public bool $isMoved ) { } } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/DocumentItem.php b/Neos.Workspace.Ui/Classes/ViewModel/DocumentItem.php new file mode 100644 index 00000000000..f2480e3e824 --- /dev/null +++ b/Neos.Workspace.Ui/Classes/ViewModel/DocumentItem.php @@ -0,0 +1,28 @@ + + +
@@ -56,13 +56,13 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F
-
- + {crumb} @@ -91,7 +91,7 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F target="_blank" title={private.i18n.id('edit')} class="neos-button neos-button-primary" - @if.notRemoved={!document.isDeleted}> + @if.notRemoved={!document.documentChanges.isRemoved}> Date: Fri, 25 Oct 2024 13:56:11 +0200 Subject: [PATCH 054/122] Feature: Add tag logic --- .../Controller/WorkspaceController.php | 28 ++++++- .../Classes/ViewModel/ChangeItem.php | 3 +- .../ContentChanges/TagContentChange.php | 27 +++++++ .../Classes/ViewModel/DocumentChangeItem.php | 3 +- .../Classes/ViewModel/DocumentItem.php | 3 +- .../Fusion/Components/ReviewChangeDiff.fusion | 22 ++++-- .../Components/ReviewChangeTableRow.fusion | 1 - .../Components/ReviewDocumentTableRow.fusion | 8 +- .../Private/Translations/en/Main.xlf | 3 + .../Resources/Public/Styles/Module.css | 78 +++++++++++++++---- 10 files changed, 147 insertions(+), 29 deletions(-) create mode 100644 Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TagContentChange.php diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 1511f7ed134..4a7450e74f4 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -78,6 +78,7 @@ use Neos\Workspace\Ui\ViewModel\ContentChanges\TextContentChange; use Neos\Workspace\Ui\ViewModel\ContentChanges\AssetContentChange; use Neos\Workspace\Ui\ViewModel\ContentChanges\DateTimeContentChange; +use Neos\Workspace\Ui\ViewModel\ContentChanges\TagContentChange; use Neos\Workspace\Ui\ViewModel\DocumentChangeItem; use Neos\Workspace\Ui\ViewModel\DocumentItem; use Neos\Workspace\Ui\ViewModel\EditWorkspaceFormData; @@ -661,6 +662,10 @@ public function discardWorkspaceAction(WorkspaceName $workspace): void /** * Computes the number of added, changed and removed nodes for the given workspace + * + * @param Workspace $selectedWorkspace + * @param ContentRepository $contentRepository + * @return PendingChanges */ protected function computePendingChanges(Workspace $selectedWorkspace, ContentRepository $contentRepository): PendingChanges { @@ -764,10 +769,12 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos $documentNode->originDimensionSpacePoint->toDimensionSpacePoint(), $documentNode->aggregateId ); + $documentType = $contentRepository->getNodeTypeManager()->getNodeType($documentNode->nodeTypeName); $siteChanges[$siteNodeName]['documents'][$documentPath]['document'] = new DocumentItem( documentBreadCrumb: array_reverse($documentPathSegmentsNames), aggregateId: $documentNodeAddress->aggregateId->value, - documentNodeAddress: $documentNodeAddress->toJson() + documentNodeAddress: $documentNodeAddress->toJson(), + documentIcon: $documentType->getFullConfiguration()['ui']['icon'] ); } @@ -776,7 +783,8 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos $siteChanges[$siteNodeName]['documents'][$documentPath]['documentChanges'] = new DocumentChangeItem( isRemoved: $change->deleted, isNew: $change->created, - isMoved: $change->moved + isMoved: $change->moved, + isHidden: $documentNode->tags->contain(SubtreeTag::disabled()), ); } @@ -802,7 +810,8 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos isNew: $change->created, isMoved: $change->moved, dimensions: $dimensions, - lastModificationDateTime: $node->timestamps->lastModified->format('Y-m-d H:i'), + lastModificationDateTime: $node->timestamps->lastModified?->format('Y-m-d H:i'), + createdDateTime: $node->timestamps->created?->format('Y-m-d H:i'), label: $this->nodeLabelGenerator->getLabel($node), icon: $nodeType->getFullConfiguration()['ui']['icon'], contentChanges: $this->renderContentChanges( @@ -862,12 +871,23 @@ protected function renderContentChanges( $originalNode = $this->getOriginalNode($changedNode, $baseWorkspace->workspaceName, $contentRepository); } - $contentChanges = []; $changeNodePropertiesDefaults = $this->getNodeType($changedNode)->getDefaultValuesForProperties(); $renderer = new HtmlArrayRenderer(); + if($originalNode?->tags->toStringArray() != $changedNode?->tags->toStringArray()) { + $contentChanges['tags'] = new ContentChangeItem( + properties: new ContentChangeProperties( + type: 'tags', + propertyLabel: $this->getModuleLabel('workspaces.changedTags'), + ), + changes: new TagContentChange( + addedTags: array_diff($changedNode->tags->toStringArray(), $originalNode->tags->toStringArray()), + removedTags: array_diff($originalNode->tags->toStringArray(), $changedNode->tags->toStringArray()), + ) + ); + } foreach ($changedNode->properties as $propertyName => $changedPropertyValue) { if ( ($originalNode === null && empty($changedPropertyValue)) diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ChangeItem.php b/Neos.Workspace.Ui/Classes/ViewModel/ChangeItem.php index 2ec7738d339..38e7ac344b4 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ChangeItem.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ChangeItem.php @@ -26,7 +26,8 @@ public function __construct( public bool $isNew, public bool $isMoved, public array $dimensions, - public string $lastModificationDateTime, + public ?string $lastModificationDateTime, + public ?string $createdDateTime, public string $label, public string $icon, public ContentChangeItems $contentChanges, diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TagContentChange.php b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TagContentChange.php new file mode 100644 index 00000000000..403f7cf9f15 --- /dev/null +++ b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TagContentChange.php @@ -0,0 +1,27 @@ +
- +
+ + +
{change.label}
@@ -46,8 +49,7 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeDiff) < prototype(Neos.Fusion: - - + @@ -88,6 +90,16 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeDiff) < prototype(Neos.Fusion: {Date.format(contentChanges.changes.changed, 'Y-m-d H:i')} + + + {tag} + + + {tag} + + + + diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion index a713058d109..3f59d685d64 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion @@ -23,7 +23,6 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeTableRow) < prototype(Neos.Fus deleted={change.isRemoved ? 'legend-deleted' : ''} created={change.isNew ? 'legend-created' : ''} moved={change.isMoved ? 'legend-moved' : ''} - hidden={change.hidden ? 'legend-hidden': ''} /> - {crumb} + + + + + + + {crumb} diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf index 74818870c22..746887e373d 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf @@ -275,6 +275,9 @@ The workspace "{0}" has been created. + + New element status + diff --git a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css index 7b87769c5a7..333a1348ab4 100644 --- a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css +++ b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css @@ -239,60 +239,78 @@ button .icon:not(:last-child) { } } -.neos.neos-module #workspaceReview table.neos-table td:not(.diff-property-label):not(.neos-content-change){ +.neos.neos-module #workspaceReview table.neos-table td:not(.diff-property-label):not(.neos-content-change) { border-top-width: 0; } + .neos-content-diff { width: 100%; - margin: 0 var(--spacing-Full) ; + margin: 0 var(--spacing-Full); } + #workspaceReview .diff-property-label, -#workspaceReview .diff-node-details{ +#workspaceReview .diff-node-details { padding-left: var(--spacing-Half) !important; } -#workspaceReview .diff-property-label{ + +#workspaceReview .diff-property-label { height: auto !important; padding-bottom: 0 !important; } -#workspaceReview .neos-content-change{ + +#workspaceReview .neos-content-change { padding-left: 0 !important; } -#workspaceReview i + span{ + +#workspaceReview i + span { padding-left: var(--spacing-Half); } + #workspaceReview .neos-document, tr.neos-change + tr.neos-change td.neos-content-change { border-top: 1px solid var(--grayDark); } -#workspaceReview .workspace-action i{ + +#workspaceReview .workspace-action i { padding-right: var(--spacing-Half); } + #workspaceReview .toggle-document { display: inline-block; padding: 0 var(--spacing-Full); cursor: pointer; } -#workspaceReview .document-details i{ + +#workspaceReview .document-details > i { padding: 0 var(--spacing-Half); } + +#workspaceReview .document-details > span > i { + padding-right: var(--spacing-Half); +} + #workspaceReview thead th, -#workspaceReview .neos-document td{ +#workspaceReview .neos-document td { padding: 0 !important; } -#workspaceReview .check-document + span{ +#workspaceReview .check-document + span { margin-right: 0; } -#workspaceReview .table-action{ + +#workspaceReview .table-action { padding: 0 var(--spacing-Full); } -#workspaceReview .table-action:hover{ + +#workspaceReview .table-action:hover { color: var(--blue); cursor: pointer; } + #workspaceReview .document-actions { width: 184px; } + #workspaceReview .neos-content-diff table { table-layout: fixed; width: 100%; @@ -307,6 +325,7 @@ tr.neos-change + tr.neos-change td.neos-content-change { border-top: none; white-space: normal; } + .neos-content-diff table.neos-content-diff td img, .neos-content-diff table.neos-content-diff th img { max-width: 100%; @@ -315,6 +334,7 @@ tr.neos-change + tr.neos-change td.neos-content-change { border: 20px solid var(--white); box-sizing: border-box; } + #workspaceReview .document-details { padding-left: 0 !important; padding-right: 0; @@ -323,33 +343,61 @@ tr.neos-change + tr.neos-change td.neos-content-change { .neos-content-change { border-left: 4px solid var(--orange); } + .neos-content-change.legend-deleted { border-left: 4px solid var(--warning); } + .neos-content-change.legend-created { border-left: 4px solid var(--green); } + .neos-content-change.legend-moved { border-left: 4px solid var(--blue); } -#workspaceReview td.actions { + +.neos-content-change.legend-hidden { + border-left: 4px solid var(--grayLighter) +} + +#workspaceReview td.actions { width: 144px; } + #workspaceReview ins, #workspaceReview ins a { color: var(--green); text-decoration: none; } + #workspaceReview del, #workspaceReview del a { color: var(--warning); text-decoration: none; } + #workspaceReview .diff-property-content td:first-child, -#workspaceReview th.neos-action{ +#workspaceReview th.neos-action { width: 30px } -#workspaceReview .fa-level-up-alt{ + +#workspaceReview .fa-level-up-alt { transform: rotate(90deg); } +#workspaceReview .node-icons { + display: inline-block; + position: relative; +} + +#workspaceReview .node-icons .icon-red { + position: absolute; + top: 50%; + left: 40%; + color: var(--warning); + font-size: 12px; +} + +#workspaceReview .node-icons { + margin-right: 7px; +} From 541a2fa481315f382a1abce99e27591afe85a671 Mon Sep 17 00:00:00 2001 From: Robert Baruck Date: Mon, 28 Oct 2024 10:05:53 +0100 Subject: [PATCH 055/122] WIP: WorkspaceRoleAssignment edit, add, delete --- .../Controller/WorkspaceController.php | 168 ++++++++++++------ ...mDeleteWorkspaceRoleAssignmentFormData.php | 34 ++++ ...CreateWorkspaceRoleAssignmentsFormData.php | 59 ++++++ .../ViewModel/EditWorkspaceFormData.php | 17 -- .../EditWorkspaceRoleAssignmentsFormData.php | 42 +++++ ...ignment.php => RoleAssignmentListItem.php} | 3 +- .../Fusion/Components/HtmxConfig.fusion | 5 + ...onfirmDeleteWorkspaceRoleAssignment.fusion | 55 ++++++ .../CreateWorkspaceRoleAssignment.fusion | 75 ++++++++ .../Fusion/Components/Modals/Edit.fusion | 63 +------ .../EditWorkspaceRoleAssignments.fusion | 75 ++++++++ .../Components/Presentationals/Button.fusion | 2 +- .../WorkspaceRoleAssignmentTableRow.fusion | 50 ++++++ .../Components/WorkspaceTableRow.fusion | 19 +- ...nfirmDeleteWorkspaceRoleAssignments.fusion | 12 ++ .../CreateWorkspaceRoleAssignment.fusion | 15 ++ .../Views/EditWorkspaceRoleAssignments.fusion | 19 ++ .../Private/Fusion/Views/Index.fusion | 6 +- .../Private/Translations/en/Main.xlf | 6 + 19 files changed, 587 insertions(+), 138 deletions(-) create mode 100644 Neos.Workspace.Ui/Classes/ViewModel/ConfirmDeleteWorkspaceRoleAssignmentFormData.php create mode 100644 Neos.Workspace.Ui/Classes/ViewModel/CreateWorkspaceRoleAssignmentsFormData.php create mode 100644 Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceRoleAssignmentsFormData.php rename Neos.Workspace.Ui/Classes/ViewModel/{EditWorkspaceRoleAssignment.php => RoleAssignmentListItem.php} (91%) create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDeleteWorkspaceRoleAssignment.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/CreateWorkspaceRoleAssignment.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/EditWorkspaceRoleAssignments.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceRoleAssignmentTableRow.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDeleteWorkspaceRoleAssignments.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Views/CreateWorkspaceRoleAssignment.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Views/EditWorkspaceRoleAssignments.fusion diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 994a3756174..09f88aafcfd 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -70,6 +70,8 @@ use Neos\Neos\PendingChangesProjection\ChangeFinder; use Neos\Neos\PendingChangesProjection\Changes; use Neos\Neos\Utility\NodeTypeWithFallbackProvider; +use Neos\Workspace\Ui\ViewModel\ConfirmDeleteWorkspaceRoleAssignmentFormData; +use Neos\Workspace\Ui\ViewModel\CreateWorkspaceRoleAssignmentsFormData; use Neos\Workspace\Ui\ViewModel\ChangeItem; use Neos\Workspace\Ui\ViewModel\ContentChangeItem; use Neos\Workspace\Ui\ViewModel\ContentChangeItems; @@ -82,8 +84,9 @@ use Neos\Workspace\Ui\ViewModel\DocumentChangeItem; use Neos\Workspace\Ui\ViewModel\DocumentItem; use Neos\Workspace\Ui\ViewModel\EditWorkspaceFormData; -use Neos\Workspace\Ui\ViewModel\EditWorkspaceRoleAssignment; +use Neos\Workspace\Ui\ViewModel\EditWorkspaceRoleAssignmentsFormData; use Neos\Workspace\Ui\ViewModel\PendingChanges; +use Neos\Workspace\Ui\ViewModel\RoleAssignmentListItem; use Neos\Workspace\Ui\ViewModel\WorkspaceListItem; use Neos\Workspace\Ui\ViewModel\WorkspaceListItems; @@ -289,42 +292,6 @@ public function editAction(WorkspaceName $workspaceName): void $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspace->workspaceName); - // TODO can current user see/edit role assignments? - $roleAssignmentsVisible = true; - $roleAssignmentsEditable = true; - - /** @var array $workspaceRoleAssignments */ - $workspaceRoleAssignments = []; - - foreach ($this->workspaceService->getWorkspaceRoleAssignments($contentRepositoryId, $workspace->workspaceName) as $workspaceRoleAssignment) { - $subjectLabel = match ($workspaceRoleAssignment->subjectType) { - WorkspaceRoleSubjectType::USER => $this->userService->findUserById(UserId::fromString($workspaceRoleAssignment->subject->value))?->getLabel(), - default => $workspaceRoleAssignment->subject->value, - }; - - $roleLabel = $workspaceRoleAssignment->role->value; - - $workspaceRoleAssignments[] = new EditWorkspaceRoleAssignment( - subjectLabel: $subjectLabel, - subjectTypeValue: $workspaceRoleAssignment->subjectType->value, - roleLabel: $roleLabel, - ); - } - - $roleAssignmentUserOptions = []; - foreach ($this->userService->getUsers()->toArray() as $user) { - $roleAssignmentUserOptions[$user->getId()->value] = $user->getLabel(); - } - - // TODO: get a set of configured groups in the system - // \Neos\Flow\Security\Policy\PolicyService::getRoles - $roleAssignmentGroupOptions = [ - 'Neos.Neos:AbstractEditor', - 'Neos.Neos:Editor', - 'Neos.Neos:LimitedEditor', - 'Neos.Neos:LivePublisher', - ]; - $editWorkspaceDto = new EditWorkspaceFormData( workspaceName: $workspace->workspaceName, workspaceTitle: $workspaceMetadata->title, @@ -332,11 +299,6 @@ public function editAction(WorkspaceName $workspaceName): void workspaceHasChanges: $this->computePendingChanges($workspace, $contentRepository)->total > 0, baseWorkspaceName: $workspace->baseWorkspaceName, baseWorkspaceOptions: $this->prepareBaseWorkspaceOptions($contentRepository, $workspaceName), - roleAssignmentsVisible: $roleAssignmentsVisible, - roleAssignmentsEditable: $roleAssignmentsEditable, - roleAssignments: $workspaceRoleAssignments, - roleAssignmentUserOptions: $roleAssignmentUserOptions, - roleAssignmentGroupOptions: $roleAssignmentGroupOptions, ); $this->view->assign('editWorkspaceFormData', $editWorkspaceDto); @@ -356,8 +318,6 @@ public function updateAction( WorkspaceTitle $title, WorkspaceDescription $description, WorkspaceName $baseWorkspace, - array $workspaceManagerUsers, - array $workspaceManagerGroups, ): void { $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); @@ -401,20 +361,6 @@ public function updateAction( $baseWorkspace, ); - // TODO: Update Workspace Managers - // remove all removed role assignments - $currentAssignments = $this->workspaceService->getWorkspaceRoleAssignments($contentRepositoryId, $workspaceName); - $newAssignments = []; - - // add new role assignments - foreach ($workspaceManagerUsers as $userId) { - $userAssignment = WorkspaceRoleAssignment::createForUser(UserId::fromString($userId), WorkspaceRole::MANAGER); - - if (!$currentAssignments->contains($userAssignment)) { - $this->workspaceService->assignWorkspaceRole($contentRepositoryId, $workspaceName, $userAssignment); - } - } - $this->addFlashMessage( $this->getModuleLabel( 'workspaces.workspaceHasBeenUpdated', @@ -520,6 +466,112 @@ public function deleteAction(WorkspaceName $workspaceName): void } } + + public function editWorkspaceRoleAssignmentsAction(WorkspaceName $workspaceName): void + { + $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; + $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspaceName); + + // TODO: Render a form to edit role assignments + // TODO can current user see/edit role assignments? + $roleAssignmentsVisible = true; + $roleAssignmentsEditable = true; + + /** @var array $workspaceRoleAssignments */ + $workspaceRoleAssignments = []; + + foreach ($this->workspaceService->getWorkspaceRoleAssignments($contentRepositoryId, $workspaceName) as $workspaceRoleAssignment) { + $subjectLabel = match ($workspaceRoleAssignment->subjectType) { + WorkspaceRoleSubjectType::USER => $this->userService->findUserById(UserId::fromString($workspaceRoleAssignment->subject->value))?->getLabel(), + default => $workspaceRoleAssignment->subject->value, + }; + + $roleLabel = $workspaceRoleAssignment->role->value; + + $workspaceRoleAssignments[] = new RoleAssignmentListItem( + subjectValue: $workspaceRoleAssignment->subject->value, + subjectLabel: $subjectLabel, + subjectTypeValue: $workspaceRoleAssignment->subjectType->value, + roleLabel: $roleLabel, + ); + } + + + + $editWorkspaceRoleAssignmentsFormData = new EditWorkspaceRoleAssignmentsFormData( + workspaceName: $workspaceName, + workspaceTitle: $workspaceMetadata->title, + roleAssignmentsEditable: $roleAssignmentsEditable, + roleAssignments: $workspaceRoleAssignments, + ); + + $this->view->assign('editWorkspaceRoleAssignmentsFormData', $editWorkspaceRoleAssignmentsFormData); + } + + public function createWorkspaceRoleAssignmentAction(WorkspaceName $workspaceName): void + { + $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; + $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspaceName); + + $userOptions = []; + foreach ($this->userService->getUsers()->toArray() as $user) { + $userOptions[$user->getId()->value] = $user->getLabel(); + } + + // TODO: get a set of configured groups in the system + // \Neos\Flow\Security\Policy\PolicyService::getRoles + $groupOptions = [ + 'Neos.Neos:AbstractEditor', + 'Neos.Neos:Editor', + 'Neos.Neos:LimitedEditor', + 'Neos.Neos:LivePublisher', + ]; + + $subjectTypeOptions = [ + WorkspaceRoleSubjectType::USER->value => 'User', + WorkspaceRoleSubjectType::GROUP->value => 'Group', + ]; + + $roleOptions = [ + WorkspaceRole::MANAGER->value => 'Manager', + WorkspaceRole::COLLABORATOR->value => 'Collaborator', + ]; + + $this->view->assign('createWorkspaceRoleAssignmentsFormData', new CreateWorkspaceRoleAssignmentsFormData( + workspaceName: $workspaceName, + workspaceTitle: $workspaceMetadata->title, + userOptions: $userOptions, + groupOptions: $groupOptions, + subjectTypeOptions: $subjectTypeOptions, + roleOptions: $roleOptions, + )); + } + + public function addRoleAssignmentAction(WorkspaceName $workspaceName, EditWorkspaceRoleAssignmentsFormData $editWorkspaceRoleAssignmentData): void + { + // TODO: Add a new role assignment + } + + public function confirmDeleteWorkspaceRoleAssignmentAction(string $workspaceName, string $subjectValue): void + { + $workspaceName = WorkspaceName::fromString($workspaceName); + $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; + $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspaceName); + + $confirmDeleteWorkspaceRoleAssignmentFormData = new ConfirmDeleteWorkspaceRoleAssignmentFormData( + workspaceName: $workspaceName, + workspaceTitle: $workspaceMetadata->title, + subjectValue: $subjectValue, + ); + + $this->view->assign('confirmDeleteWorkspaceRoleAssignmentFormData', $confirmDeleteWorkspaceRoleAssignmentFormData); + } + + public function deleteWorkspaceRoleAssignmentAction(WorkspaceName $workspaceName, string $subjectValue): void + { + $this->redirect('editWorkspaceRoleAssignments', null, null, ['workspaceName' => $workspaceName->value]); + } + /** * Rebase the current users personal workspace onto the given $targetWorkspace and then * redirects to the $targetNode in the content module. diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ConfirmDeleteWorkspaceRoleAssignmentFormData.php b/Neos.Workspace.Ui/Classes/ViewModel/ConfirmDeleteWorkspaceRoleAssignmentFormData.php new file mode 100644 index 00000000000..007d0604b2a --- /dev/null +++ b/Neos.Workspace.Ui/Classes/ViewModel/ConfirmDeleteWorkspaceRoleAssignmentFormData.php @@ -0,0 +1,34 @@ + + */ + public array $userOptions, + /** + * Options for the workspaceManager selector where the value is the group. + * @var array + */ + public array $groupOptions, + /** + * TODO: translate subject type labels? + * Options for the workspaceManager selector where the key is the subject type and the value is the subject label. + * @var array + */ + public array $subjectTypeOptions, + /** + * TODO: translate role labels? + * Options for the workspaceManager selector where the key is the role identifier and the value is the role label. + * @var array + */ + public array $roleOptions + ) + { + } +} diff --git a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php index 8d23e0b2d04..0553c4f0053 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php @@ -17,7 +17,6 @@ use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\Flow\Annotations as Flow; use Neos\Neos\Domain\Model\WorkspaceDescription; -use Neos\Neos\Domain\Model\WorkspaceRoleAssignment; use Neos\Neos\Domain\Model\WorkspaceTitle; #[Flow\Proxy(false)] @@ -34,22 +33,6 @@ public function __construct( * @var array */ public array $baseWorkspaceOptions, - public bool $roleAssignmentsVisible, - public bool $roleAssignmentsEditable, - /** - * @var array - */ - public array $roleAssignments, - /** - * Options for the workspaceManager selector where the key is the user identifier and the value is the user name. - * @var array - */ - public array $roleAssignmentUserOptions, - /** - * Options for the workspaceManager selector where the value is the group. - * @var array - */ - public array $roleAssignmentGroupOptions, ) { } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceRoleAssignmentsFormData.php b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceRoleAssignmentsFormData.php new file mode 100644 index 00000000000..8cf61071f96 --- /dev/null +++ b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceRoleAssignmentsFormData.php @@ -0,0 +1,42 @@ + + */ + public array $roleAssignments, + ) + { + } +} diff --git a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceRoleAssignment.php b/Neos.Workspace.Ui/Classes/ViewModel/RoleAssignmentListItem.php similarity index 91% rename from Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceRoleAssignment.php rename to Neos.Workspace.Ui/Classes/ViewModel/RoleAssignmentListItem.php index 339adec214a..36f9f05537c 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceRoleAssignment.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/RoleAssignmentListItem.php @@ -24,9 +24,10 @@ * - role should be internationalized, maybe */ #[Flow\Proxy(false)] -final readonly class EditWorkspaceRoleAssignment +final readonly class RoleAssignmentListItem { public function __construct( + public string $subjectValue, public string $subjectLabel, public string $subjectTypeValue, public string $roleLabel, diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/HtmxConfig.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/HtmxConfig.fusion index 117f866e6ec..21fbc64763c 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/HtmxConfig.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/HtmxConfig.fusion @@ -21,6 +21,11 @@ prototype(Neos.Workspace.Ui:Component.HTMXConfig) < prototype(Neos.Fusion:Compon swap = false error = true } + // 30X responses are swapped + redirects { + code = '[30]..' + swap = true + } } renderer = afx` diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDeleteWorkspaceRoleAssignment.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDeleteWorkspaceRoleAssignment.fusion new file mode 100644 index 00000000000..53d140823ad --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDeleteWorkspaceRoleAssignment.fusion @@ -0,0 +1,55 @@ +prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDeleteWorkspaceRoleAssignment) < prototype(Neos.Fusion:Component) { + workspaceName='' + workspaceTitle='' + subjectValue='' + + # TODO: maybe just use system confirm dialog... + + @private { + i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} + popoverId = 'confirm-delete-workspace-role-assignment-popover' + editWorkspaceRoleAssignmentsPopoverId = 'workspace-edit-role-assignments-modal' + deleteWorkspaceRoleAssignmentUri = Neos.Fusion:ActionUri { + action = 'deleteWorkspaceRoleAssignment' + format = 'htmx' + arguments { + workspaceName = ${props.workspaceName} + subjectValue = ${props.subjectValue} + } + } + } + + renderer = afx` +
+
+ +
+ {private.i18n.id('workspaces.createWorkspaceRoleAssignment').arguments([props.workspaceTitle])} +
+
+
+ + +
+
+ ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/CreateWorkspaceRoleAssignment.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/CreateWorkspaceRoleAssignment.fusion new file mode 100644 index 00000000000..cd8146cf393 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/CreateWorkspaceRoleAssignment.fusion @@ -0,0 +1,75 @@ +prototype(Neos.Workspace.Ui:Component.Modal.CreateWorkspaceRoleAssignment) < prototype(Neos.Fusion:Component) { + workspaceName='' + workspaceTitle='' + + userOptions=${[]} + groupOptions=${[]} + subjectTypeOptions=${[]} + roleOptions=${[]} + + @private { + i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} + popoverId = 'create-workspace-role-assignment-popover' + } + + prototype(Neos.Fusion.Form:LabelRenderer) { + translationPackage = 'Neos.Workspace.Ui' + translationSource = 'Main' + } + + prototype(Neos.Fusion.Form:Neos.BackendModule.FieldContainer) { + translation.label { + package = 'Neos.Workspace.Ui' + source = 'Main' + } + } + + renderer = afx` +
+
+ +
+ {private.i18n.id('workspaces.createWorkspaceRoleAssignment').arguments([props.workspaceTitle])} +
+
+
+ + + + + + +
+
+ ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion index 6288ca027c4..420b615c8a8 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion @@ -5,18 +5,9 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Compon baseWorkspaceName = '' workspaceHasChanges = false - /// array + /// array baseWorkspaceOptions = ${[]} - roleAssignmentsVisible = false - roleAssignmentsEditable = false - /// array - roleAssignmentUserOptions = ${[]} - /// array - roleAssignmentGroupOptions = ${[]} - /// array<\Neos\Workspace\Ui\ViewModel\EditWorkspaceRoleAssignment> - roleAssignments = ${[]} - @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} popoverId = 'workspace-edit-modal' @@ -117,53 +108,10 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Compon

-
- {private.i18n.id('workspaces.workspace.permissions')} - - - - - - - - - - - - - - - - - - - -
{private.i18n.id('workspaces.workspace.workspaceRoleAssignment.subjectType')}{private.i18n.id('workspaces.workspace.workspaceRoleAssignment.subject')}{private.i18n.id('workspaces.workspace.workspaceRoleAssignment.role')} - {private.i18n.id('workspaces.workspace.workspaceRoleAssignment.actions')} -
- - {roleAssignment.subjectLabel}{roleAssignment.roleLabel} - -
- -
+ + {private.i18n.id('applyChanges')} + - - {private.i18n.id('applyChanges')} -
diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/EditWorkspaceRoleAssignments.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/EditWorkspaceRoleAssignments.fusion new file mode 100644 index 00000000000..1c2e8d83397 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/EditWorkspaceRoleAssignments.fusion @@ -0,0 +1,75 @@ +prototype(Neos.Workspace.Ui:Component.Modal.EditWorkspaceRoleAssignments) < prototype(Neos.Fusion:Component) { + workspaceName='' + workspaceTitle='' + roleAssignmentsEditable=false + + /// array + roleAssignments=${[]} + + @private { + i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} + popoverId = 'workspace-edit-role-assignments-modal' + workspaceTableRowId = ${'workspace-row-' + props.workspaceName} + + confirmDeleteWorkspaceRoleAssignmentPopoverId = 'confirm-delete-workspace-role-assignment-popover' + createWorkspaceRoleAssignmentPopoverId = 'create-workspace-role-assignment-popover' + createWorkspaceRoleAssignmentUri = Neos.Fusion:ActionUri { + action = 'createWorkspaceRoleAssignment' + arguments { + workspaceName = ${props.workspaceName} + } + } + } + + renderer = afx` +
+
+ +
+ {private.i18n.id('workspaces.editWorkspaceRoleAssignments').arguments([props.workspaceTitle])} +
+
+
+ + + + + + + + + + + + + + +
{private.i18n.id('workspaces.workspace.workspaceRoleAssignment.subjectType')}{private.i18n.id('workspaces.workspace.workspaceRoleAssignment.subject')}{private.i18n.id('workspaces.workspace.workspaceRoleAssignment.role')} + {private.i18n.id('workspaces.workspace.workspaceRoleAssignment.actions')} +
+ +
+
+
+
+ ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Presentationals/Button.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Presentationals/Button.fusion index dc5a88fd3a9..a4b50333da5 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Presentationals/Button.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Presentationals/Button.fusion @@ -11,7 +11,7 @@ prototype(Neos.Workspace.Ui:Component.Button) < prototype(Neos.Fusion:Component) isPrimary = false ///boolean isSuccess = false - /// boolean + /// boolean TODO: unused api? disabled = false /// string icon = '' diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceRoleAssignmentTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceRoleAssignmentTableRow.fusion new file mode 100644 index 00000000000..9f8056ff7da --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceRoleAssignmentTableRow.fusion @@ -0,0 +1,50 @@ +## +# Renders a single WorkspaceRoleAssignment list item +# +prototype(Neos.Workspace.Ui:Component.WorkspaceRoleAssignmentTableRow) < prototype(Neos.Fusion:Component) { + /// string + workspaceName = null + /// bool + roleAssignmentsEditable = false + /// Neos\Workspace\Ui\ViewModel\RoleAssignmentListItem + roleAssignmentFormData = null + + @private { + i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} + confirmDeleteWorkspaceRoleAssignmentPopoverId = 'confirm-delete-workspace-role-assignment-popover' + confirmDeleteWorkspaceRoleAssignmentUri = Neos.Fusion:ActionUri { + action = 'confirmDeleteWorkspaceRoleAssignment' + format = 'htmx' + arguments { + workspaceName = ${props.workspaceName} + subjectValue = ${props.roleAssignmentFormData.subjectValue} + } + } + } + + renderer = afx` + + + + + {roleAssignment.subjectLabel} + {roleAssignment.roleLabel} + + + + + ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion index 9c7e1602bb3..e5204ca7f25 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion @@ -65,6 +65,12 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion workspaceName = ${props.workspaceListItem.name} } } + editWorkspaceRoleAssignmentsUri = Neos.Fusion:ActionUri { + action = 'editWorkspaceRoleAssignments' + arguments { + workspaceName = ${props.workspaceListItem.name} + } + } deleteWorkspaceUri = Neos.Fusion:ActionUri { action = 'delete' format = 'htmx' @@ -74,6 +80,8 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion } deleteWorkspacePopoverId = 'workspace-delete-modal' editWorkspacePopoverId = 'workspace-edit-modal' + editWorkspaceRoleAssignmentsPopoverId = 'workspace-edit-role-assignments-modal' + deleteWorkspaceRoleAssignmentPopoverId = 'confirm-delete-workspace-role-assignment-popover' } renderer = afx` @@ -133,6 +141,15 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion attributes.hx-on--after-request={'document.getElementById("' + private.editWorkspacePopoverId + '").showPopover()'} attributes.disabled={props.workspaceListItem.permissions.manage == false} /> + diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDeleteWorkspaceRoleAssignments.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDeleteWorkspaceRoleAssignments.fusion new file mode 100644 index 00000000000..e8ee8cd5872 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDeleteWorkspaceRoleAssignments.fusion @@ -0,0 +1,12 @@ +Neos.Workspace.Ui.WorkspaceController.confirmDeleteWorkspaceRoleAssignment = Neos.Fusion:Component { + /// \Neos\Workspace\Ui\ViewModel\ConfirmDeleteWorkspaceRoleAssignmentFormData + confirmDeleteWorkspaceRoleAssignmentFormData = ${confirmDeleteWorkspaceRoleAssignmentFormData} + + renderer = afx` + + ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/CreateWorkspaceRoleAssignment.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/CreateWorkspaceRoleAssignment.fusion new file mode 100644 index 00000000000..8bac8dde26b --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/CreateWorkspaceRoleAssignment.fusion @@ -0,0 +1,15 @@ +Neos.Workspace.Ui.WorkspaceController.createWorkspaceRoleAssignment = Neos.Fusion:Component { + createWorkspaceRoleAssignmentFormData = ${createWorkspaceRoleAssignmentFormData} + + renderer = afx` + + ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/EditWorkspaceRoleAssignments.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/EditWorkspaceRoleAssignments.fusion new file mode 100644 index 00000000000..4c42008960b --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/EditWorkspaceRoleAssignments.fusion @@ -0,0 +1,19 @@ +Neos.Workspace.Ui.WorkspaceController.editWorkspaceRoleAssignments = Neos.Fusion:Component { + /// \Neos\Workspace\Ui\ViewModel\EditWorkspaceRoleAssignmentsFormData + editWorkspaceRoleAssignmentsFormData = ${editWorkspaceRoleAssignmentsFormData} + + renderer = afx` + + ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Index.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Index.fusion index a4df161ebd2..4758d036662 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Index.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Index.fusion @@ -51,10 +51,14 @@ Neos.Workspace.Ui.WorkspaceController.index = Neos.Fusion:Component { /> -
+
` + // TODO: we should put the count into the htmx rendered content as well htmx = afx` + Edit workspace "{0}" + + Edit workspace permissions of "{0}" + + + Create Role Assignment for workspace "{0}" + Personal workspace From 62576e33ad0d4cb7e3a439e28dc86dc28bf75ae2 Mon Sep 17 00:00:00 2001 From: Robert Baruck Date: Thu, 31 Oct 2024 14:49:50 +0100 Subject: [PATCH 056/122] TASK: Fix delete workspace, render workspace count oob --- Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php | 3 +++ .../Resources/Private/Fusion/Components/WorkspaceCount.fusion | 2 +- .../Private/Fusion/Components/WorkspaceTableRow.fusion | 3 +-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 09f88aafcfd..6edf2a0f49c 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -459,6 +459,9 @@ public function deleteAction(WorkspaceName $workspaceName): void [$workspaceMetadata->title->value], ) ); + + // WHY: Redirect to refresh data on page (e.g. workspace list & count) + $this->redirect('index'); // Render a confirmation form if the request is not a POST request } else { $this->view->assign('workspaceName', $workspace->workspaceName->value); diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceCount.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceCount.fusion index 009c6d46c02..b528640ffb9 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceCount.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceCount.fusion @@ -6,7 +6,7 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceCount) < prototype(Neos.Fusion:Co } renderer = afx` -
+
{private.i18n.id('workspaces.numberOfWorkspaces').arguments([props.workspaceCount]).translate()}
` diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion index e5204ca7f25..ea8cdeecb93 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion @@ -81,7 +81,6 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion deleteWorkspacePopoverId = 'workspace-delete-modal' editWorkspacePopoverId = 'workspace-edit-modal' editWorkspaceRoleAssignmentsPopoverId = 'workspace-edit-role-assignments-modal' - deleteWorkspaceRoleAssignmentPopoverId = 'confirm-delete-workspace-role-assignment-popover' } renderer = afx` @@ -158,7 +157,7 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion attributes.hx-get={private.deleteWorkspaceUri} attributes.hx-target='#popover-container' attributes.hx-swap='innerHTML' - attributes.hx-on--after-request={'document.getElementById("' + private.deleteWorkspaceRoleAssignmentPopoverId + '").showPopover()'} + attributes.hx-on--after-request={'document.getElementById("' + private.deleteWorkspacePopoverId + '").showPopover()'} /> From 649786dffc2a7a1800ffecc13d876444f6b7c74c Mon Sep 17 00:00:00 2001 From: Robert Baruck Date: Fri, 1 Nov 2024 14:22:28 +0100 Subject: [PATCH 057/122] TASK: Fix edit workspace pattern, but user help still needed --- .../Resources/Private/Fusion/Components/Modals/Edit.fusion | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion index 420b615c8a8..04560dc4553 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion @@ -60,7 +60,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Compon > @@ -73,7 +73,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Compon class="neos-control-group" > From eedfc59964e3bbce21a68a27a2df1f02aac3f58f Mon Sep 17 00:00:00 2001 From: Robert Baruck Date: Fri, 1 Nov 2024 15:32:14 +0100 Subject: [PATCH 058/122] TASK: Fix edit & remove WorkspaceRoleAssignment --- .../Domain/Service/WorkspaceService.php | 3 +++ .../Controller/WorkspaceController.php | 26 ++++++++++++++++--- ...mDeleteWorkspaceRoleAssignmentFormData.php | 10 +++---- .../ViewModel/RoleAssignmentListItem.php | 2 ++ ...onfirmDeleteWorkspaceRoleAssignment.fusion | 5 ++-- .../EditWorkspaceRoleAssignments.fusion | 3 ++- .../WorkspaceRoleAssignmentTableRow.fusion | 9 ++++--- .../Components/WorkspaceTableRow.fusion | 2 ++ ...nfirmDeleteWorkspaceRoleAssignments.fusion | 1 + 9 files changed, 45 insertions(+), 16 deletions(-) diff --git a/Neos.Neos/Classes/Domain/Service/WorkspaceService.php b/Neos.Neos/Classes/Domain/Service/WorkspaceService.php index a76667cb41d..eb05c94855c 100644 --- a/Neos.Neos/Classes/Domain/Service/WorkspaceService.php +++ b/Neos.Neos/Classes/Domain/Service/WorkspaceService.php @@ -376,6 +376,9 @@ public function deleteWorkspace(ContentRepositoryId $contentRepositoryId, Worksp } // ------------------ + public function removeWorkspaceRoleAssignment(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName, WorkspaceRoleSubject $fromString) + { + } private function deleteWorkspaceMetadata(ContentRepositoryId $contentRepositoryId, $workspaceName): void { diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 6edf2a0f49c..24831629688 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -57,6 +57,7 @@ use Neos\Neos\Domain\Model\WorkspaceDescription; use Neos\Neos\Domain\Model\WorkspaceRole; use Neos\Neos\Domain\Model\WorkspaceRoleAssignment; +use Neos\Neos\Domain\Model\WorkspaceRoleSubject; use Neos\Neos\Domain\Model\WorkspaceRoleSubjectType; use Neos\Neos\Domain\Model\WorkspaceTitle; use Neos\Neos\Domain\NodeLabel\NodeLabelGeneratorInterface; @@ -496,6 +497,7 @@ public function editWorkspaceRoleAssignmentsAction(WorkspaceName $workspaceName) subjectLabel: $subjectLabel, subjectTypeValue: $workspaceRoleAssignment->subjectType->value, roleLabel: $roleLabel, + subjectType: $workspaceRoleAssignment->subjectType->value, ); } @@ -555,9 +557,8 @@ public function addRoleAssignmentAction(WorkspaceName $workspaceName, EditWorksp // TODO: Add a new role assignment } - public function confirmDeleteWorkspaceRoleAssignmentAction(string $workspaceName, string $subjectValue): void + public function confirmDeleteWorkspaceRoleAssignmentAction(WorkspaceName $workspaceName, string $subjectValue, string $subjectType): void { - $workspaceName = WorkspaceName::fromString($workspaceName); $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspaceName); @@ -565,13 +566,32 @@ public function confirmDeleteWorkspaceRoleAssignmentAction(string $workspaceName workspaceName: $workspaceName, workspaceTitle: $workspaceMetadata->title, subjectValue: $subjectValue, + subjectType: $subjectType, ); $this->view->assign('confirmDeleteWorkspaceRoleAssignmentFormData', $confirmDeleteWorkspaceRoleAssignmentFormData); } - public function deleteWorkspaceRoleAssignmentAction(WorkspaceName $workspaceName, string $subjectValue): void + public function deleteWorkspaceRoleAssignmentAction(WorkspaceName $workspaceName, string $subjectValue, string $subjectType): void { + $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; + try { + $this->workspaceService->unassignWorkspaceRole( + $contentRepositoryId, + $workspaceName, + WorkspaceRoleSubjectType::from($subjectType), + WorkspaceRoleSubject::fromString($subjectValue), + ); + } catch (\Exception $e) { + // TODO: error handling + $this->addFlashMessage( + $this->getModuleLabel('workspaces.roleAssignmentCouldNotBeDeleted'), + '', + Message::SEVERITY_ERROR + ); + $this->throwStatus(500, 'Role assignment could not be deleted'); + } + $this->redirect('editWorkspaceRoleAssignments', null, null, ['workspaceName' => $workspaceName->value]); } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ConfirmDeleteWorkspaceRoleAssignmentFormData.php b/Neos.Workspace.Ui/Classes/ViewModel/ConfirmDeleteWorkspaceRoleAssignmentFormData.php index 007d0604b2a..9f67e88719f 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ConfirmDeleteWorkspaceRoleAssignmentFormData.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ConfirmDeleteWorkspaceRoleAssignmentFormData.php @@ -16,18 +16,16 @@ use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\Flow\Annotations as Flow; -use Neos\Neos\Domain\Model\WorkspaceDescription; -use Neos\Neos\Domain\Model\WorkspaceRoleAssignment; -use Neos\Neos\Domain\Model\WorkspaceRoleSubject; use Neos\Neos\Domain\Model\WorkspaceTitle; #[Flow\Proxy(false)] final readonly class ConfirmDeleteWorkspaceRoleAssignmentFormData { public function __construct( - public WorkspaceName $workspaceName, - public WorkspaceTitle $workspaceTitle, - public string $subjectValue, + public WorkspaceName $workspaceName, + public WorkspaceTitle $workspaceTitle, + public string $subjectValue, + public string $subjectType, ) { } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/RoleAssignmentListItem.php b/Neos.Workspace.Ui/Classes/ViewModel/RoleAssignmentListItem.php index 36f9f05537c..bc054aadb88 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/RoleAssignmentListItem.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/RoleAssignmentListItem.php @@ -15,6 +15,7 @@ namespace Neos\Workspace\Ui\ViewModel; use Neos\Flow\Annotations as Flow; +use Neos\Neos\Domain\Model\WorkspaceRoleSubjectType; /** * Derived from Neos\Neos\Domain\Model\WorkspaceRoleAssignment @@ -31,6 +32,7 @@ public function __construct( public string $subjectLabel, public string $subjectTypeValue, public string $roleLabel, + public string $subjectType, ) { } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDeleteWorkspaceRoleAssignment.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDeleteWorkspaceRoleAssignment.fusion index 53d140823ad..a8871e05a4d 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDeleteWorkspaceRoleAssignment.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDeleteWorkspaceRoleAssignment.fusion @@ -2,6 +2,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDeleteWorkspaceRoleAssignment workspaceName='' workspaceTitle='' subjectValue='' + subjectType='' # TODO: maybe just use system confirm dialog... @@ -15,12 +16,13 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDeleteWorkspaceRoleAssignment arguments { workspaceName = ${props.workspaceName} subjectValue = ${props.subjectValue} + subjectType = ${props.subjectType} } } } renderer = afx` -
+
diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/EditWorkspaceRoleAssignments.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/EditWorkspaceRoleAssignments.fusion index 1c2e8d83397..01623f706d8 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/EditWorkspaceRoleAssignments.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/EditWorkspaceRoleAssignments.fusion @@ -15,6 +15,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.EditWorkspaceRoleAssignments) < prot createWorkspaceRoleAssignmentPopoverId = 'create-workspace-role-assignment-popover' createWorkspaceRoleAssignmentUri = Neos.Fusion:ActionUri { action = 'createWorkspaceRoleAssignment' + format = 'htmx' arguments { workspaceName = ${props.workspaceName} } @@ -22,7 +23,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.EditWorkspaceRoleAssignments) < prot } renderer = afx` -
+
@@ -78,14 +80,15 @@ prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Com isDanger={true} attributes.hx-get={private.discardSelectedChanges} attributes.hx-target="body" + attributes.disabled={!props.canPublishToWorkspace} />
diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion index 7b044f66982..50d7792f351 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion @@ -6,7 +6,6 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeDiff) < prototype(Neos.Fusion: relativePath = '' change = '' document = '' - canPublishToBaseWorkspace = ${canPublishToBaseWorkspace} selectedWorkspaceName = ${selectedWorkspaceName} @private { diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion index 9b77031a06e..42f17bc4494 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion @@ -7,6 +7,8 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F documentPath = '' selectedWorkspaceName = ${selectedWorkspaceName} selectedWorkspaceLabel = ${selectedWorkspaceLabel} + canPublishToBaseWorkspace = ${canPublishToBaseWorkspace} + canPublishToWorkspace = ${canPublishToWorkspace} @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} @@ -48,7 +50,14 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F } renderer = afx` - + +
diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf index 86300c18780..3baaebc6f64 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf @@ -96,6 +96,9 @@ Permissions + + Workspace "{0}" does not exist + Typ @@ -302,6 +305,15 @@ New element status + + You do not have permission to publish to the base workspace + + + You do not have permission to publish to the this workspace + + + You do not have permission to see this workspace + diff --git a/Neos.Workspace.Ui/Resources/Public/Scripts/Review.js b/Neos.Workspace.Ui/Resources/Public/Scripts/Review.js index 2a2a61adb25..50e1c39e75a 100644 --- a/Neos.Workspace.Ui/Resources/Public/Scripts/Review.js +++ b/Neos.Workspace.Ui/Resources/Public/Scripts/Review.js @@ -1,77 +1,83 @@ window.addEventListener('DOMContentLoaded', (event) => { document.body.addEventListener('htmx:afterOnLoad', function (evt) { - const input = document.getElementById("check-all"); + initReviewFunctions(); + }); +}); +window.addEventListener('DOMContentLoaded', (event) => { + initReviewFunctions(); +}); +function initReviewFunctions(){ + const input = document.getElementById("check-all"); - // Attach event listener after input is loaded - if (input) { - input.addEventListener( - 'change', function (event) { - document.getElementById('batch-actions').classList.toggle('neos-hidden'); - document.getElementById('all-actions').classList.toggle('neos-hidden'); - for (const checkbox of document.querySelectorAll('tbody input[type="checkbox"]')) { - checkbox.checked = input.checked; - } + // Attach event listener after input is loaded + if (input) { + input.addEventListener( + 'change', function (event) { + document.getElementById('batch-actions').classList.toggle('neos-hidden'); + document.getElementById('all-actions').classList.toggle('neos-hidden'); + for (const checkbox of document.querySelectorAll('tbody input[type="checkbox"]')) { + checkbox.checked = input.checked; } - ) - for (const checkbox of document.querySelectorAll('tbody input[type="checkbox"]')) { - checkbox.addEventListener( 'change', function(){ - if(!checkbox.checked){ - input.checked = false; - } - if(document.querySelectorAll('tbody input[type="checkbox"]:checked').length === 0){ - document.getElementById('batch-actions').classList.add('neos-hidden'); - document.getElementById('all-actions').classList.remove('neos-hidden'); - } - }); - } - for (const toggleDocument of document.querySelectorAll('.toggle-document')) { - toggleDocument.addEventListener( 'click', function(){ + ) + for (const checkbox of document.querySelectorAll('tbody input[type="checkbox"]')) { + checkbox.addEventListener( 'change', function(){ + if(!checkbox.checked){ + input.checked = false; + } + if(document.querySelectorAll('tbody input[type="checkbox"]:checked').length === 0){ + document.getElementById('batch-actions').classList.add('neos-hidden'); + document.getElementById('all-actions').classList.remove('neos-hidden'); + } + }); - toggleDocument.children[0].classList.toggle('fa-chevron-down'); - toggleDocument.children[0].classList.toggle('fa-chevron-up'); + } + for (const toggleDocument of document.querySelectorAll('.toggle-document')) { + toggleDocument.addEventListener( 'click', function(){ - let nextElement = toggleDocument.closest('.neos-document').nextElementSibling; - do{ - nextElement.classList.toggle('neos-hidden') - nextElement = nextElement.nextElementSibling; - } - while (nextElement && !nextElement.classList.contains('neos-document')) - }); + toggleDocument.children[0].classList.toggle('fa-chevron-down'); + toggleDocument.children[0].classList.toggle('fa-chevron-up'); - } - document.getElementById('collapse-all').addEventListener( - 'click', function (event) { - const collapseButton = document.getElementById('collapse-all'); - let status = (collapseButton.dataset.toggled === 'true'); - console.log(status); - if(status){ - for (const toggle of document.querySelectorAll('.toggle-document')) { - toggle.children[0].classList.remove('fa-chevron-down'); - toggle.children[0].classList.add('fa-chevron-up'); - } - for (const change of document.querySelectorAll('.neos-change')) { - change.classList.add('neos-hidden'); - } + let nextElement = toggleDocument.closest('.neos-document').nextElementSibling; + do{ + nextElement.classList.toggle('neos-hidden') + nextElement = nextElement.nextElementSibling; + } + while (nextElement && !nextElement.classList.contains('neos-document')) + }); - } else { - for (const toggle of document.querySelectorAll('.toggle-document')) { - toggle.children[0].classList.add('fa-chevron-down'); - toggle.children[0].classList.remove('fa-chevron-up'); - } - for (const change of document.querySelectorAll('.neos-change')) { - change.classList.remove('neos-hidden'); - } + } + document.getElementById('collapse-all').addEventListener( + 'click', function (event) { + const collapseButton = document.getElementById('collapse-all'); + let status = (collapseButton.dataset.toggled === 'true'); + console.log(status); + if(status){ + for (const toggle of document.querySelectorAll('.toggle-document')) { + toggle.children[0].classList.remove('fa-chevron-down'); + toggle.children[0].classList.add('fa-chevron-up'); + } + for (const change of document.querySelectorAll('.neos-change')) { + change.classList.add('neos-hidden'); } - collapseButton.childNodes[0].classList.toggle('fa-up-right-and-down-left-from-center'); - collapseButton.childNodes[0].classList.toggle('fa-down-left-and-up-right-to-center') - collapseButton.dataset.toggled = !status; - + } else { + for (const toggle of document.querySelectorAll('.toggle-document')) { + toggle.children[0].classList.add('fa-chevron-down'); + toggle.children[0].classList.remove('fa-chevron-up'); + } + for (const change of document.querySelectorAll('.neos-change')) { + change.classList.remove('neos-hidden'); + } } - ) - } - }); -}); + collapseButton.childNodes[0].classList.toggle('fa-up-right-and-down-left-from-center'); + collapseButton.childNodes[0].classList.toggle('fa-down-left-and-up-right-to-center') + collapseButton.dataset.toggled = !status; + + } + ) + + } +} diff --git a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css index 333a1348ab4..abc13185679 100644 --- a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css +++ b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css @@ -253,6 +253,10 @@ button .icon:not(:last-child) { padding-left: var(--spacing-Half) !important; } +#workspaceReview .diff-node-details .neos-label { + margin-top: 11px; +} + #workspaceReview .diff-property-label { height: auto !important; padding-bottom: 0 !important; @@ -340,23 +344,24 @@ tr.neos-change + tr.neos-change td.neos-content-change { padding-right: 0; } -.neos-content-change { +.neos-content-change, +.neos-document{ border-left: 4px solid var(--orange); } -.neos-content-change.legend-deleted { +#workspaceReview .legend-deleted { border-left: 4px solid var(--warning); } -.neos-content-change.legend-created { +#workspaceReview .legend-created { border-left: 4px solid var(--green); } -.neos-content-change.legend-moved { +#workspaceReview .legend-moved { border-left: 4px solid var(--blue); } -.neos-content-change.legend-hidden { +#workspaceReview .legend-hidden { border-left: 4px solid var(--grayLighter) } @@ -381,7 +386,11 @@ tr.neos-change + tr.neos-change td.neos-content-change { width: 30px } -#workspaceReview .fa-level-up-alt { +#workspaceReview tr.diff-property-content > td { + line-height: 20px !important; +} + + #workspaceReview .fa-level-up-alt { transform: rotate(90deg); } From 7e55243c55bbcb6c0c87fa6b2a6aa88a2466ea57 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Sat, 16 Nov 2024 13:35:48 +0100 Subject: [PATCH 060/122] TASK: Flip cancel and confirm buttons and adjust position in modals --- .../Fusion/Components/Modals/Edit.fusion | 50 ++++---- .../Resources/Private/Fusion/Views/New.fusion | 120 +++++++++--------- .../Resources/Public/Styles/Module.css | 5 + 3 files changed, 89 insertions(+), 86 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion index 04560dc4553..3fc31e1d94a 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion @@ -41,16 +41,16 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Compon {private.i18n.id('workspaces.editWorkspace').arguments([props.workspaceTitle])}
-
- + +
-
- - {private.i18n.id('applyChanges')} - - -
- -
+
+
+ + + {private.i18n.id('applyChanges')} + +
+
` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion index 76c827c1735..7434850f337 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion @@ -31,69 +31,67 @@ Neos.Workspace.Ui.WorkspaceController.new = Neos.Fusion:Component { {props.i18n.id('workspaces.createNewWorkspace')}
-
- -
- - - - + +
+
+ + + + - - - + + + - - - - {workspaceTitle} - - - -
-
- - {props.i18n.id('workspaces.createWorkspace')} - -
- -
-
- -
+ + + + {workspaceTitle} + + + +
+
+
+ + + {props.i18n.id('workspaces.createWorkspace')} + +
+
` } diff --git a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css index abc13185679..cf154c10c66 100644 --- a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css +++ b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css @@ -166,6 +166,11 @@ button .icon:not(:last-child) { color: var(--textSubtleLight); } +[popover] form { + width: 400px; + max-width: 90vw; +} + [popover] .neos-close { color: white; font-size: calc(var(--generalFontSize) + 4px); From 5e9b10fc2b147455604c5c2e02561dac8898c24c Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Sat, 16 Nov 2024 18:26:17 +0100 Subject: [PATCH 061/122] TASK: Add basic user stories for the workspace ui --- .../Documentation/UserStories.rst | 87 +++++++++++++++++++ Neos.Workspace.Ui/Readme.rst | 5 ++ 2 files changed, 92 insertions(+) create mode 100644 Neos.Workspace.Ui/Documentation/UserStories.rst diff --git a/Neos.Workspace.Ui/Documentation/UserStories.rst b/Neos.Workspace.Ui/Documentation/UserStories.rst new file mode 100644 index 00000000000..788e829b0bd --- /dev/null +++ b/Neos.Workspace.Ui/Documentation/UserStories.rst @@ -0,0 +1,87 @@ +---------------------------------------- +The workspace module feature description +---------------------------------------- + +This document describes the user stories for the workspace management module in Neos CMS. +If new features are added to the module, they should be described as user stories in this document. +If features are removed or changed, the user stories should be updated accordingly. +The user stories should be written in a way that is understandable to non-technical users. + +List workspaces +--------------- + +As an editor, I want to see a list of workspaces, so that I can see which workspaces are available and what their status is. + +The list should show the following information for each workspace: + +* The workspace title / name +* A description of the workspace to explain its purpose +* The last modification date of its content (TODO) +* The creator of the workspace (TODO) +* The status of the workspace (e.g. "Published", "Stale", "Outdated") +* The number of pending changes in the workspace and their type (e.g. "New content", "Modified content", "Deleted content") +* A list of actions that can be performed on the workspace (e.g. "Review", "Edit", "Delete") + +As an administrator or workspace manager I want to be able to see which workspaces are actively used, so that I can clean up unused or stale workspaces. +The list should therefore be sortable by the last modification date of the workspace and stale workspaces visually highlighted. + +Additional requirements: +######################## + +* The list should work well with 100 workspaces +* The list should be able to show nested workspaces with up to 4 levels of nesting + + +Create a new workspace +---------------------- + +As an editor, I want to be able to create a new workspace, so that I can work on changes without affecting the live site. + +When creating a new workspace, I should be able to specify the following information: + +* The title of the workspace +* A description of the workspace to explain its purpose +* The parent workspace that the new workspace should be based on +* The initial visibility of the workspace (e.g. "Shared", "Private") + +Advanced configuration can be done after the workspace has been created. + +Edit a workspace +---------------- + +As an editor, I want to be able to edit the properties of a workspace, +so that I can update its title, description, parent workspace, and visibility. + +When editing a workspace, I should be able to change the following information: + +* The title of the workspace +* A description of the workspace to explain its purpose +* The parent workspace that the workspace should be based on +* The visibility of the workspace (e.g. "Shared", "Private") and its access control list (ACL) + +Changing the base workspace +########################### + +When changing the parent workspace, the user should be able to choose from a list of possible workspaces. + +* If the edited workspace has sub-workspaces (shared, personal or private) that are based on the edited workspace, +the user should be warned that the changes will affect the sub-workspaces as well. The names of the affected workspaces +should be displayed if possible. +* If the user has no access to the sub-workspaces, the user see the number of affected workspaces. + +Review a workspace +------------------ + +As an editor, I want to be able to review the changes in a workspace, so that I can see what has been changed +and decide whether to publish the changes to the live site. + +Delete a workspace +------------------ + +As an editor, I want to be able to delete a workspace, so that I can clean up unused or stale workspaces. + +When deleting a workspace, the user should be warned that the action cannot be undone and that all changes in the workspace will be lost. +The number of pending changes in the workspace should be displayed to help the user decide whether to delete the workspace. +When the workspace has sub-workspaces, the user should be warned that the sub-workspaces will be deleted as well. +The names of the affected workspaces should be displayed if possible. +If the user has no access to the sub-workspaces, the user see the number of affected workspaces. diff --git a/Neos.Workspace.Ui/Readme.rst b/Neos.Workspace.Ui/Readme.rst index 65935b43e5b..a3d0dea86e3 100644 --- a/Neos.Workspace.Ui/Readme.rst +++ b/Neos.Workspace.Ui/Readme.rst @@ -7,6 +7,11 @@ The Neos CMS workspace management module If you want to use Neos, please have a look at the `Neos documentation `_ +User stories +------------ + +See :file:`Documentation/UserStories.rst`. + Contribute ---------- From a5de169796ad45a2fb5072b1b989653009b46005 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Sat, 16 Nov 2024 18:34:57 +0100 Subject: [PATCH 062/122] BUGFIX: Show back button in workspace review when there are no changes --- .../Private/Fusion/Components/ReviewActions.fusion | 13 ++++++------- .../Fusion/Components/WorkspaceTableRow.fusion | 2 +- .../Resources/Private/Fusion/Views/Review.fusion | 7 +++++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion index cdb198bdc33..a546d99a6d6 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion @@ -2,11 +2,11 @@ # Renders a document for review # prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Component) { - - canPublishToBaseWorkspace = ${canPublishToBaseWorkspace} - canPublishToWorkspace = ${canPublishToWorkspace} - selectedWorkspaceName = ${selectedWorkspaceName} - baseWorkspaceLabel = ${baseWorkspaceLabel} + hasSiteChanges = false + canPublishToBaseWorkspace = false + canPublishToWorkspace = false + selectedWorkspaceName = '' + baseWorkspaceLabel = '' @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} @@ -40,7 +40,6 @@ prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Com workspace = ${props.selectedWorkspaceName} } } - } renderer = afx` @@ -54,7 +53,7 @@ prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Com attributes.hx-swap="innerHTML" />
-
+
+ canPublishToWorkspace={props.canPublishToWorkspace} + selectedWorkspaceName={props.selectedWorkspaceName} + baseWorkspaceLabel={props.baseWorkspaceLabel} + />
From b358b91960be98971bea557e4c0398cb70ea1a40 Mon Sep 17 00:00:00 2001 From: Paula Kallert Date: Mon, 18 Nov 2024 10:24:11 +0100 Subject: [PATCH 063/122] Feature: Consider parents and children when using checkboxes --- .../Classes/Controller/WorkspaceController.php | 1 + .../Components/ReviewChangeTableRow.fusion | 3 +-- .../Components/ReviewDocumentTableRow.fusion | 4 ++-- .../Resources/Public/Scripts/Review.js | 18 +++++++++++++++++- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 7f6d96c8d88..e96f0f2651c 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -915,6 +915,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos ) ); + //ToDo: Consider dimensions if(!isset($siteChanges[$siteNodeName]['documents'][$documentPath]['document'])) { $documentNodeAddress = NodeAddress::create( $contentRepository->id, diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion index 3f59d685d64..1f51fcbe89f 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion @@ -15,8 +15,7 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeTableRow) < prototype(Neos.Fus renderer = afx` - - + + Date: Fri, 29 Nov 2024 10:48:40 +0100 Subject: [PATCH 064/122] Task: Move removeWorkspaceRoleAssignment and deleteWorkspaceMetadata to metadataAndRoleRepository --- .../WorkspaceMetadataAndRoleRepository.php | 53 +++++++ .../Domain/Service/WorkspaceService.php | 139 +----------------- 2 files changed, 55 insertions(+), 137 deletions(-) diff --git a/Neos.Neos/Classes/Domain/Repository/WorkspaceMetadataAndRoleRepository.php b/Neos.Neos/Classes/Domain/Repository/WorkspaceMetadataAndRoleRepository.php index ed6c928f6ca..6f67bc7cfb6 100644 --- a/Neos.Neos/Classes/Domain/Repository/WorkspaceMetadataAndRoleRepository.php +++ b/Neos.Neos/Classes/Domain/Repository/WorkspaceMetadataAndRoleRepository.php @@ -311,4 +311,57 @@ public function findPrimaryWorkspaceNameForUser(ContentRepositoryId $contentRepo ]); return $workspaceName === false ? null : WorkspaceName::fromString($workspaceName); } + + public function deleteWorkspaceMetadata(ContentRepositoryId $contentRepositoryId, $workspaceName): void + { + $table = self::TABLE_NAME_WORKSPACE_METADATA; + $query = <<dbal->executeStatement($query, [ + 'contentRepositoryId' => $contentRepositoryId->value, + 'workspaceName' => $workspaceName->value, + ]); + } catch (DbalException $e) { + throw new \RuntimeException(sprintf( + 'Failed to delete metadata for workspace "%s" (Content Repository "%s"): %s', + $workspaceName->value, + $contentRepositoryId->value, + $e->getMessage() + ), 1726821159, $e); + } + } + + public function deleteWorkspaceRoleAssignments(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName): void + { + $table = self::TABLE_NAME_WORKSPACE_ROLE; + $query = <<dbal->executeStatement($query, [ + 'contentRepositoryId' => $contentRepositoryId->value, + 'workspaceName' => $workspaceName->value, + ]); + } catch (DbalException $e) { + throw new \RuntimeException(sprintf( + 'Failed to delete role assignments for workspace "%s" (Content Repository "%s"): %s', + $workspaceName->value, + $contentRepositoryId->value, + $e->getMessage() + ), 1726821159, $e); + } + } + } diff --git a/Neos.Neos/Classes/Domain/Service/WorkspaceService.php b/Neos.Neos/Classes/Domain/Service/WorkspaceService.php index 9fe8a829c76..cbd4df47853 100644 --- a/Neos.Neos/Classes/Domain/Service/WorkspaceService.php +++ b/Neos.Neos/Classes/Domain/Service/WorkspaceService.php @@ -282,40 +282,11 @@ public function deleteWorkspace(ContentRepositoryId $contentRepositoryId, Worksp ) ); - $this->deleteWorkspaceMetadata($contentRepositoryId, $workspaceName); - $this->deleteWorkspaceRoleAssignments($contentRepositoryId, $workspaceName); + $this->metadataAndRoleRepository->deleteWorkspaceMetadata($contentRepositoryId, $workspaceName); + $this->metadataAndRoleRepository->deleteWorkspaceRoleAssignments($contentRepositoryId, $workspaceName); } // ------------------ - public function removeWorkspaceRoleAssignment(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName, WorkspaceRoleSubject $fromString) - { - } - - private function deleteWorkspaceMetadata(ContentRepositoryId $contentRepositoryId, $workspaceName): void - { - $table = self::TABLE_NAME_WORKSPACE_METADATA; - $query = <<dbal->executeStatement($query, [ - 'contentRepositoryId' => $contentRepositoryId->value, - 'workspaceName' => $workspaceName->value, - ]); - } catch (DbalException $e) { - throw new \RuntimeException(sprintf( - 'Failed to delete metadata for workspace "%s" (Content Repository "%s"): %s', - $workspaceName->value, - $contentRepositoryId->value, - $e->getMessage() - ), 1726821159, $e); - } - } private function createWorkspace(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName, WorkspaceTitle $title, WorkspaceDescription $description, WorkspaceName $baseWorkspaceName, UserId|null $ownerId, WorkspaceClassification $classification): void { @@ -330,112 +301,6 @@ private function createWorkspace(ContentRepositoryId $contentRepositoryId, Works $this->metadataAndRoleRepository->addWorkspaceMetadata($contentRepositoryId, $workspaceName, $title, $description, $classification, $ownerId); } - private function addWorkspaceMetadata(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName, WorkspaceTitle $title, WorkspaceDescription $description, WorkspaceClassification $classification, UserId|null $ownerUserId): void - { - try { - $this->dbal->insert(self::TABLE_NAME_WORKSPACE_METADATA, [ - 'content_repository_id' => $contentRepositoryId->value, - 'workspace_name' => $workspaceName->value, - 'title' => $title->value, - 'description' => $description->value, - 'classification' => $classification->value, - 'owner_user_id' => $ownerUserId?->value, - ]); - } catch (DbalException $e) { - throw new \RuntimeException(sprintf('Failed to add metadata for workspace "%s" (Content Repository "%s"): %s', $workspaceName->value, $contentRepositoryId->value, $e->getMessage()), 1727084068, $e); - } - } - - private function findPrimaryWorkspaceNameForUser(ContentRepositoryId $contentRepositoryId, UserId $userId): ?WorkspaceName - { - $tableMetadata = self::TABLE_NAME_WORKSPACE_METADATA; - $query = <<dbal->fetchOne($query, [ - 'contentRepositoryId' => $contentRepositoryId->value, - 'personalWorkspaceClassification' => WorkspaceClassification::PERSONAL->value, - 'userId' => $userId->value, - ]); - return $workspaceName === false ? null : WorkspaceName::fromString($workspaceName); - } - - private function deleteWorkspaceRoleAssignments(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName): void - { - $table = self::TABLE_NAME_WORKSPACE_ROLE; - $query = <<dbal->executeStatement($query, [ - 'contentRepositoryId' => $contentRepositoryId->value, - 'workspaceName' => $workspaceName->value, - ]); - } catch (DbalException $e) { - throw new \RuntimeException(sprintf( - 'Failed to delete role assignments for workspace "%s" (Content Repository "%s"): %s', - $workspaceName->value, - $contentRepositoryId->value, - $e->getMessage() - ), 1726821159, $e); - } - } - - /** - * @param array $userRoles - */ - private function loadWorkspaceRoleOfUser(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName, UserId $userId, array $userRoles): ?WorkspaceRole - { - $tableRole = self::TABLE_NAME_WORKSPACE_ROLE; - $query = <<dbal->fetchOne($query, [ - 'contentRepositoryId' => $contentRepositoryId->value, - 'workspaceName' => $workspaceName->value, - 'userSubjectType' => WorkspaceRoleSubjectType::USER->value, - 'userId' => $userId->value, - 'groupSubjectType' => WorkspaceRoleSubjectType::GROUP->value, - 'groupSubjects' => $userRoles, - ], [ - 'groupSubjects' => ArrayParameterType::STRING, - ]); - if ($role === false) { - return null; - } - return WorkspaceRole::from($role); - } - private function requireWorkspace(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName): Workspace { $workspace = $this->contentRepositoryRegistry From d21d9cb034c19fb3d352780a45001aa0dfffbc20 Mon Sep 17 00:00:00 2001 From: Robert Baruck Date: Fri, 29 Nov 2024 11:26:01 +0100 Subject: [PATCH 065/122] WIP: WorkspaceRoleAssignment edit, add, delete --- .../Controller/WorkspaceController.php | 101 ++++++++++++++---- ...CreateWorkspaceRoleAssignmentFormData.php} | 2 +- .../CreateWorkspaceRoleAssignment.fusion | 89 +++++++++++---- .../Private/Translations/en/Main.xlf | 14 ++- 4 files changed, 160 insertions(+), 46 deletions(-) rename Neos.Workspace.Ui/Classes/ViewModel/{CreateWorkspaceRoleAssignmentsFormData.php => CreateWorkspaceRoleAssignmentFormData.php} (96%) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index faafbf746d9..3476ee37b80 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -48,6 +48,7 @@ use Neos\Flow\Package\PackageManager; use Neos\Flow\Property\PropertyMapper; use Neos\Flow\Security\Context; +use Neos\Flow\Security\Policy\PolicyService; use Neos\Fusion\View\FusionView; use Neos\Media\Domain\Model\AssetInterface; use Neos\Media\Domain\Model\ImageInterface; @@ -74,7 +75,7 @@ use Neos\Neos\Utility\NodeTypeWithFallbackProvider; use Neos\Neos\Security\Authorization\ContentRepositoryAuthorizationService; use Neos\Workspace\Ui\ViewModel\ConfirmDeleteWorkspaceRoleAssignmentFormData; -use Neos\Workspace\Ui\ViewModel\CreateWorkspaceRoleAssignmentsFormData; +use Neos\Workspace\Ui\ViewModel\CreateWorkspaceRoleAssignmentFormData; use Neos\Workspace\Ui\ViewModel\ChangeItem; use Neos\Workspace\Ui\ViewModel\ContentChangeItem; use Neos\Workspace\Ui\ViewModel\ContentChangeItems; @@ -136,6 +137,9 @@ class WorkspaceController extends AbstractModuleController #[Flow\Inject] protected Translator $translator; + #[Flow\Inject] + protected PolicyService $policyService; + #[Flow\Inject] protected ContentRepositoryAuthorizationService $authorizationService; @@ -544,26 +548,27 @@ public function createWorkspaceRoleAssignmentAction(WorkspaceName $workspaceName $userOptions[$user->getId()->value] = $user->getLabel(); } - // TODO: get a set of configured groups in the system - // \Neos\Flow\Security\Policy\PolicyService::getRoles - $groupOptions = [ - 'Neos.Neos:AbstractEditor', - 'Neos.Neos:Editor', - 'Neos.Neos:LimitedEditor', - 'Neos.Neos:LivePublisher', - ]; - - $subjectTypeOptions = [ - WorkspaceRoleSubjectType::USER->value => 'User', - WorkspaceRoleSubjectType::GROUP->value => 'Group', - ]; - - $roleOptions = [ - WorkspaceRole::MANAGER->value => 'Manager', - WorkspaceRole::COLLABORATOR->value => 'Collaborator', - ]; - - $this->view->assign('createWorkspaceRoleAssignmentsFormData', new CreateWorkspaceRoleAssignmentsFormData( + $rolesInSystem = $this->policyService->getRoles(); + $groupOptions = []; + foreach ($rolesInSystem as $role) { + $groupOptions[$role->getIdentifier()] = $role->getLabel(); + } + + $workspaceRoleSubjectTypes = WorkspaceRoleSubjectType::cases(); + /** @var array $subjectTypeOptions where key is the Id and value is the translated label of the SubjectType */ + $subjectTypeOptions = []; + foreach ($workspaceRoleSubjectTypes as $workspaceRoleSubjectType) { + $subjectTypeOptions[$workspaceRoleSubjectType->value] = $this->getModuleLabel("workspaces.workspace.workspaceRoleAssignment.subjectType.label.$workspaceRoleSubjectType->value"); + } + + $workspaceRoles = WorkspaceRole::cases(); + /** @var array $roleOptions where key is the Id and value is the translated label of the Role */ + $roleOptions = []; + foreach ($workspaceRoles as $workspaceRole) { + $roleOptions[$workspaceRole->value] = $this->getModuleLabel("workspaces.workspace.workspaceRoleAssignment.role.label.$workspaceRole->value"); + } + + $this->view->assign('createWorkspaceRoleAssignmentFormData', new CreateWorkspaceRoleAssignmentFormData( workspaceName: $workspaceName, workspaceTitle: $workspaceMetadata->title, userOptions: $userOptions, @@ -573,9 +578,31 @@ public function createWorkspaceRoleAssignmentAction(WorkspaceName $workspaceName )); } - public function addRoleAssignmentAction(WorkspaceName $workspaceName, EditWorkspaceRoleAssignmentsFormData $editWorkspaceRoleAssignmentData): void + public function addWorkspaceRoleAssignmentAction( + WorkspaceName $workspaceName, + string $subjectValue, + string $subjectTypeValue, + string $roleValue, + ): void { - // TODO: Add a new role assignment + // TODO: Validate if user can add role assignment to workspace + + $subject = WorkspaceRoleSubject::fromString($subjectValue); + $subjectType = WorkspaceRoleSubjectType::from($subjectTypeValue); + $role = WorkspaceRole::from($roleValue); + + if ($subjectType === WorkspaceRoleSubjectType::USER) { + $this->addUserRoleAssignment($workspaceName, $subject, $role); + } elseif ($subjectType === WorkspaceRoleSubjectType::GROUP) { + $this->addGroupRoleAssignment($workspaceName, $subject, $role); + } else { + $this->addFlashMessage( + $this->getModuleLabel('workspaces.roleAssignmentCouldNotBeAdded'), + '', + Message::SEVERITY_ERROR + ); + $this->throwStatus(400, 'Invalid subject type'); + } } public function confirmDeleteWorkspaceRoleAssignmentAction(WorkspaceName $workspaceName, string $subjectValue, string $subjectType): void @@ -1401,4 +1428,32 @@ protected function getChangesFromWorkspace(Workspace $selectedWorkspace,ContentR $selectedWorkspace->currentContentStreamId ); } + + private function addUserRoleAssignment(WorkspaceName $workspaceName, WorkspaceRoleSubject $subject, WorkspaceRole $role): void + { + if ($this->userService->findUserById(UserId::fromString($subject->value)) === null) { + $this->addFlashMessage( + $this->getModuleLabel('workspaces.roleAssignmentCouldNotBeAdded'), + '', + Message::SEVERITY_ERROR + ); + $this->throwStatus(400, 'Invalid user'); + } + + $this->workspaceService->assignWorkspaceRole( + SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId, + $workspaceName, + WorkspaceRoleAssignment::createForUser(UserId::fromString($subject->value), $role) + ); + } + + private function addGroupRoleAssignment(WorkspaceName $workspaceName, WorkspaceRoleSubject $subject, WorkspaceRole $role) + { + // TODO check if group exists? + $this->workspaceService->assignWorkspaceRole( + SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId, + $workspaceName, + WorkspaceRoleAssignment::createForGroup($subject->value, $role) + ); + } } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/CreateWorkspaceRoleAssignmentsFormData.php b/Neos.Workspace.Ui/Classes/ViewModel/CreateWorkspaceRoleAssignmentFormData.php similarity index 96% rename from Neos.Workspace.Ui/Classes/ViewModel/CreateWorkspaceRoleAssignmentsFormData.php rename to Neos.Workspace.Ui/Classes/ViewModel/CreateWorkspaceRoleAssignmentFormData.php index 5a70d84f045..657d772c31a 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/CreateWorkspaceRoleAssignmentsFormData.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/CreateWorkspaceRoleAssignmentFormData.php @@ -26,7 +26,7 @@ * - role should be internationalized, maybe */ #[Flow\Proxy(false)] -final readonly class CreateWorkspaceRoleAssignmentsFormData +final readonly class CreateWorkspaceRoleAssignmentFormData { public function __construct( public WorkspaceName $workspaceName, diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/CreateWorkspaceRoleAssignment.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/CreateWorkspaceRoleAssignment.fusion index cd8146cf393..40457d95bc0 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/CreateWorkspaceRoleAssignment.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/CreateWorkspaceRoleAssignment.fusion @@ -10,6 +10,9 @@ prototype(Neos.Workspace.Ui:Component.Modal.CreateWorkspaceRoleAssignment) < pro @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} popoverId = 'create-workspace-role-assignment-popover' + + // Default SubjectType is USER. + defaultSubjectType = 'USER' } prototype(Neos.Fusion.Form:LabelRenderer) { @@ -24,6 +27,8 @@ prototype(Neos.Workspace.Ui:Component.Modal.CreateWorkspaceRoleAssignment) < pro } } + // TODO: hide/show user/group select based on subjectType + renderer = afx`
@@ -47,27 +52,69 @@ prototype(Neos.Workspace.Ui:Component.Modal.CreateWorkspaceRoleAssignment) < pro attributes.class="neos-inline" attributes.hx-post={form.getTarget()} > - - - - +
+ + + + + {subjectTypeTitle} + + + + + + + + + + + {userName} + + + + + + + + + + {roleName} + + + + +
+
+ +
diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf index 3baaebc6f64..1b722139922 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf @@ -100,14 +100,26 @@ Workspace "{0}" does not exist - Typ + Type + + Group + + + User + Name Role + + Manager + + + Collaborator + Actions From b60d4954e02c81f486e2328524b5001260ddc901 Mon Sep 17 00:00:00 2001 From: pKallert Date: Fri, 29 Nov 2024 15:44:28 +0100 Subject: [PATCH 066/122] Feature: Add publishAll and DiscardAll functions --- .../Classes/Domain/Service/UserService.php | 1 - .../Controller/WorkspaceController.php | 97 +++++++++++++++++-- .../Modals/ConfirmDiscardAllChanges.fusion | 53 ++++++++++ .../ConfirmDiscardSelectedChanges.fusion | 51 ++++++++++ .../Modals/ConfirmPublishAllChanges.fusion | 50 ++++++++++ .../ConfirmPublishSelectedChanges.fusion | 50 ++++++++++ .../Fusion/Components/ReviewActions.fusion | 50 ++++++---- .../Views/ConfirmDiscardAllChanges.fusion | 9 ++ .../ConfirmDiscardSelectedChanges.fusion | 10 ++ .../Views/ConfirmPublishAllChanges.fusion | 9 ++ .../ConfirmPublishSelectedChanges.fusion | 9 ++ .../Private/Fusion/Views/Review.fusion | 1 + .../Private/Translations/en/Main.xlf | 3 + .../Resources/Public/Scripts/Review.js | 7 +- 14 files changed, 372 insertions(+), 28 deletions(-) create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardAllChanges.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardSelectedChanges.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishAllChanges.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishSelectedChanges.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDiscardAllChanges.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDiscardSelectedChanges.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmPublishAllChanges.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmPublishSelectedChanges.fusion diff --git a/Neos.Neos/Classes/Domain/Service/UserService.php b/Neos.Neos/Classes/Domain/Service/UserService.php index ea3f627c86d..dfa1616d003 100644 --- a/Neos.Neos/Classes/Domain/Service/UserService.php +++ b/Neos.Neos/Classes/Domain/Service/UserService.php @@ -14,7 +14,6 @@ namespace Neos\Neos\Domain\Service; -use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\Flow\Annotations as Flow; use Neos\Flow\Persistence\Exception\IllegalObjectTypeException; use Neos\Flow\Persistence\PersistenceManagerInterface; diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index faafbf746d9..08c845d4626 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -731,9 +731,8 @@ public function discardDocumentAction(string $nodeAddress, WorkspaceName $select * @throws InvalidFormatPlaceholderException * @throws StopActionException */ - public function publishOrDiscardNodesAction(array $nodes, string $action, string $selectedWorkspace): void + public function publishOrDiscardNodesAction(array $nodes, string $action, WorkspaceName $selectedWorkspace): void { - $selectedWorkspaceName = WorkspaceName::fromString($selectedWorkspace); $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest()) ->contentRepositoryId; $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); @@ -750,7 +749,7 @@ public function publishOrDiscardNodesAction(array $nodes, string $action, string switch ($action) { case 'publish': $command = PublishIndividualNodesFromWorkspace::create( - $selectedWorkspaceName, + $selectedWorkspace, NodeIdsToPublishOrDiscard::create(...$nodesToPublishOrDiscard), ); $contentRepository->handle($command); @@ -760,7 +759,7 @@ public function publishOrDiscardNodesAction(array $nodes, string $action, string break; case 'discard': $command = DiscardIndividualNodesFromWorkspace::create( - $selectedWorkspaceName, + $selectedWorkspace, NodeIdsToPublishOrDiscard::create(...$nodesToPublishOrDiscard), ); $contentRepository->handle($command); @@ -792,18 +791,103 @@ public function publishWorkspaceAction(WorkspaceName $workspace): void ], ) ); + //todo make redirect work $this->redirect('index'); } + public function confirmPublishAllChangesAction(WorkspaceName $workspaceName): void + { + $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; + $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); + $workspace = $contentRepository->findWorkspaceByName($workspaceName); + if ($workspace === null) { + $this->addFlashMessage( + $this->getModuleLabel('workspaces.workspaceDoesNotExist'), + '', + Message::SEVERITY_ERROR + ); + $this->throwStatus(404, 'Workspace does not exist'); + } + + $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspace->workspaceName); + $this->view->assignMultiple([ + 'workspaceName' => $workspaceName->value, + 'workspaceTitle' => $workspaceMetadata->title->value, + ]); + } + public function confirmDiscardAllChangesAction(WorkspaceName $workspaceName): void + { + $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; + $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); + $workspace = $contentRepository->findWorkspaceByName($workspaceName); + if ($workspace === null) { + $this->addFlashMessage( + $this->getModuleLabel('workspaces.workspaceDoesNotExist'), + '', + Message::SEVERITY_ERROR + ); + $this->throwStatus(404, 'Workspace does not exist'); + } + + $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspace->workspaceName); + $this->view->assignMultiple([ + 'workspaceName' => $workspaceName->value, + 'workspaceTitle' => $workspaceMetadata->title->value, + ]); + } + + public function confirmPublishSelectedChangesAction(WorkspaceName $workspaceName): void + { + $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; + $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); + $workspace = $contentRepository->findWorkspaceByName($workspaceName); + if ($workspace === null) { + $this->addFlashMessage( + $this->getModuleLabel('workspaces.workspaceDoesNotExist'), + '', + Message::SEVERITY_ERROR + ); + $this->throwStatus(404, 'Workspace does not exist'); + } + + $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspace->workspaceName); + $this->view->assignMultiple([ + 'workspaceName' => $workspaceName->value, + 'workspaceTitle' => $workspaceMetadata->title->value, + ]); + $this->view->assign('workspaceName', $workspaceName); + } + public function confirmDiscardSelectedChangesAction(WorkspaceName $workspaceName): void + { + $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; + $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); + $workspace = $contentRepository->findWorkspaceByName($workspaceName); + if ($workspace === null) { + $this->addFlashMessage( + $this->getModuleLabel('workspaces.workspaceDoesNotExist'), + '', + Message::SEVERITY_ERROR + ); + $this->throwStatus(404, 'Workspace does not exist'); + } + + $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspace->workspaceName); + $this->view->assignMultiple([ + 'workspaceName' => $workspaceName->value, + 'workspaceTitle' => $workspaceMetadata->title->value, + ]); + $this->view->assign('workspaceName', $workspaceName); + } + /** * Discards content of the whole workspace * - * TODO: Adjust param to workspaceName * @param WorkspaceName $workspace */ public function discardWorkspaceAction(WorkspaceName $workspace): void { $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; + $this->workspacePublishingService->discardAllWorkspaceChanges( $contentRepositoryId, $workspace, @@ -814,7 +898,8 @@ public function discardWorkspaceAction(WorkspaceName $workspace): void [htmlspecialchars($workspace->value)], ) ); - $this->redirect('index'); + //todo make redirect to index work + $this->redirect('review', null, null, ['workspace' => $workspace->value]); } /** diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardAllChanges.fusion new file mode 100644 index 00000000000..fe75615bd8c --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardAllChanges.fusion @@ -0,0 +1,53 @@ +prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDiscardAllChanges) < prototype(Neos.Fusion:Component) { + workspaceTitle = '' + + @private { + i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} + popoverId = 'confirm-discard-all-changes-popover' + + discardWorkspaceUri = Neos.Fusion:ActionUri { + action = 'discardWorkspace' + arguments { + workspace = ${workspaceName} + } + } + indexWorkspaceUri = Neos.Fusion:ActionUri { + action = 'index' + + } + + } + + renderer = afx` +
+
+ +
+ {private.i18n.id('workspaces.discardAllChangesInWorkspaceConfirmation').arguments([props.workspaceTitle])} +
+
+
+ + +
+
+ ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardSelectedChanges.fusion new file mode 100644 index 00000000000..50b91d877be --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardSelectedChanges.fusion @@ -0,0 +1,51 @@ +prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDiscardSelectedChanges) < prototype(Neos.Fusion:Component) { + workspaceName='' + workspaceTitle='' + + @private { + i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} + popoverId = 'confirm-discard-selected-changes-popover' + + confirmDiscardSelectedChanges = Neos.Fusion:ActionUri { + action = 'publishOrDiscardNodesAction' + format = 'htmx' + arguments { + selectedWorkspace = ${props.workspaceName} + action = 'discard' + } + } + } + + renderer = afx` +
+
+ +
+ {private.i18n.id('workspaces.createWorkspaceRoleAssignment').arguments([props.workspaceTitle])} +
+
+
+ + +
+
+ ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishAllChanges.fusion new file mode 100644 index 00000000000..1f741532316 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishAllChanges.fusion @@ -0,0 +1,50 @@ +prototype(Neos.Workspace.Ui:Component.Modal.ConfirmPublishAllChanges) < prototype(Neos.Fusion:Component) { + workspaceTitle = '' + + + @private { + i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} + popoverId = 'confirm-publish-all-changes-popover' + + publishWorkspaceUri = Neos.Fusion:ActionUri { + action = 'publishWorkspace' + format = 'htmx' + arguments { + workspace = ${workspaceName} + } + } + } + + renderer = afx` +
+
+ +
+ {private.i18n.id('workspaces.publishAllChangesInWorkspaceConfirmation').arguments([props.workspaceTitle])} +
+
+
+ + +
+
+ ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishSelectedChanges.fusion new file mode 100644 index 00000000000..5a2f314a76b --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishSelectedChanges.fusion @@ -0,0 +1,50 @@ +prototype(Neos.Workspace.Ui:Component.Modal.ConfirmPublishSelectedChanges) < prototype(Neos.Fusion:Component) { + workspaceName='' + workspaceTitle='' + @private { + i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} + popoverId = 'confirm-publish-selected-changes-popover' + + confirmPublishSelectedChanges = Neos.Fusion:ActionUri { + action = 'publishOrDiscardNodesAction' + format = 'htmx' + arguments { + selectedWorkspace = ${props.workspaceName} + action = 'publish' + } + } + } + + renderer = afx` +
+
+ +
+ {private.i18n.id('workspaces.createWorkspaceRoleAssignment').arguments([props.workspaceTitle])} +
+
+
+ + +
+
+ ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion index a546d99a6d6..cdf3d34f401 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion @@ -14,32 +14,38 @@ prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Com action = 'index' } - discardWorkspaceUri = Neos.Fusion:ActionUri { - action = 'discardWorkspace' + confirmDiscardAllChanges = Neos.Fusion:ActionUri { + action = 'confirmDiscardAllChanges' + format = 'htmx' arguments { - workspace = ${props.selectedWorkspaceName} + workspaceName = ${props.selectedWorkspaceName} } } - publishWorkspaceUri = Neos.Fusion:ActionUri { - action = 'publishWorkspace' + confirmPublishAllChanges = Neos.Fusion:ActionUri { + action = 'confirmPublishAllChanges' + format = 'htmx' arguments { - workspace = ${props.selectedWorkspaceName} + workspaceName = ${props.selectedWorkspaceName} } } discardSelectedChanges = Neos.Fusion:ActionUri { - action = 'PublishOrDiscardNodes' + action = 'confirmDiscardSelectedChanges' arguments { - action = 'discard' - workspace = ${props.selectedWorkspaceName} + workspaceName = ${props.selectedWorkspaceName} } } - publishSelectedChangesTo = Neos.Fusion:ActionUri { - action = 'PublishOrDiscardNodes' + confirmPublishSelectedChanges= Neos.Fusion:ActionUri { + action = 'confirmPublishSelectedChanges' + format = 'htmx' arguments { - action = 'publish' - workspace = ${props.selectedWorkspaceName} + workspaceName = ${props.selectedWorkspaceName} } } + + discardAllChangesPopoverId = 'confirm-discard-all-changes-popover' + publishAllChangesPopoverId = 'confirm-publish-all-changes-popover' + publishSelectedChangesPopoverId = 'confirm-publish-selected-changes-popover' + discardSelectedChangesPopoverId = 'confirm-discard-selected-changes-popover' } renderer = afx` @@ -59,16 +65,20 @@ prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Com label={private.i18n.id('workspaces.discardAllChanges')} icon="fas fa-trash-alt icon-white" isDanger={true} - attributes.hx-get={private.discardWorkspaceUri} - attributes.hx-target="body" + attributes.hx-get={private.confirmDiscardAllChanges} + attributes.hx-target='#popover-container' + attributes.hx-swap='innerHTML' + attributes.hx-on--after-request={'document.getElementById("' + private.discardAllChangesPopoverId + '").showPopover()'} attributes.disabled={!props.canPublishToWorkspace} />
@@ -85,8 +95,10 @@ prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Com label={private.i18n.id('workspaces.publishSelectedChangesTo').arguments([props.baseWorkspaceLabel])} icon="fas fa-check icon-white" isPrimary={true} - attributes.hx-get={private.publishSelectedChangesTo} - attributes.hx-target="body" + attributes.hx-get={private.confirmPublishSelectedChanges} + attributes.hx-target='#popover-container' + attributes.hx-swap='innerHTML' + attributes.hx-on--after-request={'document.getElementById("' + private.publishSelectedChangesPopoverId + '").showPopover()'} attributes.disabled={!props.canPublishToBaseWorkspace} />
diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDiscardAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDiscardAllChanges.fusion new file mode 100644 index 00000000000..49716589c7a --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDiscardAllChanges.fusion @@ -0,0 +1,9 @@ +Neos.Workspace.Ui.WorkspaceController.confirmDiscardAllChanges = Neos.Fusion:Component { + workspaceTitle = ${workspaceTitle} + + renderer = afx` + + ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDiscardSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDiscardSelectedChanges.fusion new file mode 100644 index 00000000000..96dd2a7b310 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDiscardSelectedChanges.fusion @@ -0,0 +1,10 @@ +Neos.Workspace.Ui.WorkspaceController.confirmDiscardSelectedChanges = Neos.Fusion:Component { + workspaceName = ${workspaceName} + + renderer = afx` + + ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmPublishAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmPublishAllChanges.fusion new file mode 100644 index 00000000000..01a8387973f --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmPublishAllChanges.fusion @@ -0,0 +1,9 @@ +Neos.Workspace.Ui.WorkspaceController.confirmPublishAllChanges = Neos.Fusion:Component { + workspaceTitle = ${workspaceTitle} + + renderer = afx` + + ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmPublishSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmPublishSelectedChanges.fusion new file mode 100644 index 00000000000..424f1646a32 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmPublishSelectedChanges.fusion @@ -0,0 +1,9 @@ +Neos.Workspace.Ui.WorkspaceController.confirmPublishSelectedChanges = Neos.Fusion:Component { + workspaceName = ${workspaceName} + + renderer = afx` + + ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion index a82ace12a44..d74d47cf898 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion @@ -56,6 +56,7 @@ Neos.Workspace.Ui.WorkspaceController.review = Neos.Fusion:Component {
+
` htmx = afx` diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf index 3baaebc6f64..0ffce4cf1c1 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf @@ -249,6 +249,9 @@ Do you really want to discard all changes in the "{0}" workspace? + + Do you really want to publish all changes in the "{0}" workspace? + A workspace with this title already exists. diff --git a/Neos.Workspace.Ui/Resources/Public/Scripts/Review.js b/Neos.Workspace.Ui/Resources/Public/Scripts/Review.js index 953fe8f7844..e00d15c08f2 100644 --- a/Neos.Workspace.Ui/Resources/Public/Scripts/Review.js +++ b/Neos.Workspace.Ui/Resources/Public/Scripts/Review.js @@ -26,12 +26,15 @@ function initReviewFunctions(){ if(!checkbox.checked){ input.checked = false; } - if(document.querySelectorAll('tbody input[type="checkbox"]:checked').length === 0){ + if(document.querySelectorAll('tbody input[type="checkbox"]:checked').length === document.querySelectorAll('tbody input[type="checkbox"]').length){ document.getElementById('batch-actions').classList.add('neos-hidden'); document.getElementById('all-actions').classList.remove('neos-hidden'); + } else { + document.getElementById('batch-actions').classList.remove('neos-hidden'); + document.getElementById('all-actions').classList.add('neos-hidden'); } const neosDocument = checkbox.closest('.neos-document'); - console.log(neosDocument.dataset.documentpath); + if(neosDocument.hasAttribute('data-isNew') || neosDocument.hasAttribute('data-isMoved')){ if(checkbox.checked){ neosDocument.dataset.documentpath.split('/').forEach(function (parentDocumentId) { From d817eb00a8841b2ae85893a127bf079458cc83dc Mon Sep 17 00:00:00 2001 From: pKallert Date: Sun, 1 Dec 2024 11:03:59 +0100 Subject: [PATCH 067/122] Feature: Make Discard and publish selected changes work --- .../Controller/WorkspaceController.php | 52 ++++++++----------- .../Modals/ConfirmDiscardAllChanges.fusion | 19 ++++--- .../ConfirmDiscardSelectedChanges.fusion | 32 +++++++----- .../Modals/ConfirmPublishAllChanges.fusion | 19 ++++--- .../ConfirmPublishSelectedChanges.fusion | 37 +++++++------ .../Fusion/Components/ReviewActions.fusion | 17 +++--- .../Components/ReviewDocumentTableRow.fusion | 2 +- .../ConfirmDiscardSelectedChanges.fusion | 4 +- .../ConfirmPublishSelectedChanges.fusion | 4 +- .../Private/Fusion/Views/Review.fusion | 10 ++++ .../Resources/Public/Scripts/Review.js | 13 +++-- 11 files changed, 120 insertions(+), 89 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 6396fad853b..1159d41b585 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -19,10 +19,6 @@ use Neos\ContentRepository\Core\Dimension\ContentDimensionId; use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTag; use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Exception\WorkspaceAlreadyExists; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\DiscardIndividualNodesFromWorkspace; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\PublishIndividualNodesFromWorkspace; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdsToPublishOrDiscard; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Exception\WorkspaceRebaseFailed; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindAncestorNodesFilter; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; @@ -758,45 +754,42 @@ public function discardDocumentAction(string $nodeAddress, WorkspaceName $select * @throws InvalidFormatPlaceholderException * @throws StopActionException */ - public function publishOrDiscardNodesAction(array $nodes, string $action, WorkspaceName $selectedWorkspace): void + public function publishOrDiscardNodesAction(array $nodes, string $action, WorkspaceName $workspace): void { $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest()) ->contentRepositoryId; - $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); - - $nodesToPublishOrDiscard = []; - foreach ($nodes as $node) { - $nodeAddress = NodeAddress::fromJsonString($node); - $nodesToPublishOrDiscard[] = new NodeIdToPublishOrDiscard( - $nodeAddress->aggregateId, - $nodeAddress->dimensionSpacePoint - ); - } switch ($action) { case 'publish': - $command = PublishIndividualNodesFromWorkspace::create( - $selectedWorkspace, - NodeIdsToPublishOrDiscard::create(...$nodesToPublishOrDiscard), - ); - $contentRepository->handle($command); + foreach ($nodes as $node) { + $nodeAddress = NodeAddress::fromJsonString($node); + $this->workspacePublishingService->publishChangesInDocument( + $contentRepositoryId, + $workspace, + $nodeAddress->aggregateId + ); + } + //todo: make flashmessage work with htmx $this->addFlashMessage( $this->getModuleLabel('workspaces.selectedChangesHaveBeenPublished') ); break; case 'discard': - $command = DiscardIndividualNodesFromWorkspace::create( - $selectedWorkspace, - NodeIdsToPublishOrDiscard::create(...$nodesToPublishOrDiscard), - ); - $contentRepository->handle($command); + foreach ($nodes as $node) { + $nodeAddress = NodeAddress::fromJsonString($node); + $this->workspacePublishingService->discardChangesInDocument( + $contentRepositoryId, + $workspace, + $nodeAddress->aggregateId + ); + } $this->addFlashMessage($this->getModuleLabel('workspaces.selectedChangesHaveBeenDiscarded')); break; default: throw new \RuntimeException('Invalid action "' . htmlspecialchars($action) . '" given.', 1346167441); } - $this->redirect('review', null, null, ['workspace' => $selectedWorkspaceName->value]); + $this->redirect('review', null, null, ['workspace' => $workspace->value]); } /** @@ -876,13 +869,13 @@ public function confirmPublishSelectedChangesAction(WorkspaceName $workspaceName ); $this->throwStatus(404, 'Workspace does not exist'); } + $baseWorkspace = $this->getBaseWorkspaceWhenSureItExists($workspace, $contentRepository); - $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspace->workspaceName); + $baseWorkspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $baseWorkspace->workspaceName); $this->view->assignMultiple([ 'workspaceName' => $workspaceName->value, - 'workspaceTitle' => $workspaceMetadata->title->value, + 'baseWorkspaceTitle' => $baseWorkspaceMetadata->title->value, ]); - $this->view->assign('workspaceName', $workspaceName); } public function confirmDiscardSelectedChangesAction(WorkspaceName $workspaceName): void { @@ -903,7 +896,6 @@ public function confirmDiscardSelectedChangesAction(WorkspaceName $workspaceName 'workspaceName' => $workspaceName->value, 'workspaceTitle' => $workspaceMetadata->title->value, ]); - $this->view->assign('workspaceName', $workspaceName); } /** diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardAllChanges.fusion index fe75615bd8c..bbf008cd10e 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardAllChanges.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardAllChanges.fusion @@ -39,14 +39,17 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDiscardAllChanges) < prototyp attributes.popovertarget={private.popoverId} attributes.popovertargetaction="close" /> - + +
+ +
` diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardSelectedChanges.fusion index 50b91d877be..a9ba419a799 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardSelectedChanges.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardSelectedChanges.fusion @@ -1,5 +1,4 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDiscardSelectedChanges) < prototype(Neos.Fusion:Component) { - workspaceName='' workspaceTitle='' @private { @@ -7,10 +6,9 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDiscardSelectedChanges) < pro popoverId = 'confirm-discard-selected-changes-popover' confirmDiscardSelectedChanges = Neos.Fusion:ActionUri { - action = 'publishOrDiscardNodesAction' - format = 'htmx' + action = 'publishOrDiscardNodes' arguments { - selectedWorkspace = ${props.workspaceName} + workspace = ${workspaceName} action = 'discard' } } @@ -28,7 +26,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDiscardSelectedChanges) < pro
- {private.i18n.id('workspaces.createWorkspaceRoleAssignment').arguments([props.workspaceTitle])} + {private.i18n.id('workspaces.discardSelectedChanges')}
@@ -37,14 +35,22 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDiscardSelectedChanges) < pro attributes.popovertarget={private.popoverId} attributes.popovertargetaction="close" /> - +
+ +
` diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishAllChanges.fusion index 1f741532316..69a2898c339 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishAllChanges.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishAllChanges.fusion @@ -36,14 +36,17 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmPublishAllChanges) < prototyp attributes.popovertarget={private.popoverId} attributes.popovertargetaction="close" /> - + +
+ +
` diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishSelectedChanges.fusion index 5a2f314a76b..a87c5e14720 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishSelectedChanges.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishSelectedChanges.fusion @@ -1,15 +1,14 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmPublishSelectedChanges) < prototype(Neos.Fusion:Component) { - workspaceName='' - workspaceTitle='' + baseWorkspaceTitle='' + @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} popoverId = 'confirm-publish-selected-changes-popover' - confirmPublishSelectedChanges = Neos.Fusion:ActionUri { - action = 'publishOrDiscardNodesAction' - format = 'htmx' + publishSelectedChanges = Neos.Fusion:ActionUri { + action = 'publishOrDiscardNodes' arguments { - selectedWorkspace = ${props.workspaceName} + workspace = ${workspaceName} action = 'publish' } } @@ -27,7 +26,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmPublishSelectedChanges) < pro
- {private.i18n.id('workspaces.createWorkspaceRoleAssignment').arguments([props.workspaceTitle])} + {private.i18n.id('workspaces.publishSelectedChangesTo').arguments([props.baseWorkspaceTitle])}
@@ -36,14 +35,22 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmPublishSelectedChanges) < pro attributes.popovertarget={private.popoverId} attributes.popovertargetaction="close" /> - +
+ +
` diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion index cdf3d34f401..99ff5944640 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion @@ -12,7 +12,6 @@ prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Com i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} indexWorkspaceUri = Neos.Fusion:ActionUri { action = 'index' - } confirmDiscardAllChanges = Neos.Fusion:ActionUri { action = 'confirmDiscardAllChanges' @@ -28,8 +27,9 @@ prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Com workspaceName = ${props.selectedWorkspaceName} } } - discardSelectedChanges = Neos.Fusion:ActionUri { + confirmDiscardSelectedChanges = Neos.Fusion:ActionUri { action = 'confirmDiscardSelectedChanges' + format = 'htmx' arguments { workspaceName = ${props.selectedWorkspaceName} } @@ -57,6 +57,7 @@ prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Com attributes.hx-get={private.indexWorkspaceUri} attributes.hx-target="body" attributes.hx-swap="innerHTML" + attributes.hx-replace-url={private.indexWorkspaceUri} />
@@ -87,17 +88,19 @@ prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Com label={private.i18n.id('workspaces.discardSelectedChanges')} icon="fas fa-trash-alt icon-white" isDanger={true} - attributes.hx-get={private.discardSelectedChanges} - attributes.hx-target="body" - attributes.disabled={!props.canPublishToWorkspace} + attributes.hx-get={private.confirmDiscardSelectedChanges} + attributes.hx-target='#popover-container' + attributes.hx-swap='innerHTML' + attributes.hx-on--after-request={'document.getElementById("' + private.discardSelectedChangesPopoverId + '").showPopover()'} + attributes.disabled={!props.canPublishToBaseWorkspace} /> diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion index 0dfde5e9992..188c3eadfa4 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion @@ -66,7 +66,7 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F
diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDiscardSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDiscardSelectedChanges.fusion index 96dd2a7b310..110cafc595f 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDiscardSelectedChanges.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDiscardSelectedChanges.fusion @@ -1,9 +1,9 @@ Neos.Workspace.Ui.WorkspaceController.confirmDiscardSelectedChanges = Neos.Fusion:Component { - workspaceName = ${workspaceName} + workspaceTitle = ${workspaceTitle} renderer = afx` ` diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmPublishSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmPublishSelectedChanges.fusion index 424f1646a32..6e49efaf472 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmPublishSelectedChanges.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmPublishSelectedChanges.fusion @@ -1,9 +1,9 @@ Neos.Workspace.Ui.WorkspaceController.confirmPublishSelectedChanges = Neos.Fusion:Component { - workspaceName = ${workspaceName} + baseWorkspaceTitle = ${baseWorkspaceTitle} renderer = afx` ` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion index d74d47cf898..988e074b4d5 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion @@ -36,6 +36,15 @@ Neos.Workspace.Ui.WorkspaceController.review = Neos.Fusion:Component { {props.i18n.id('workspaces.unpublishedChanges').arguments([selectedWorkspaceLabel]).translate()} + + +
diff --git a/Neos.Workspace.Ui/Resources/Public/Scripts/Review.js b/Neos.Workspace.Ui/Resources/Public/Scripts/Review.js index e00d15c08f2..c584d8dfaa1 100644 --- a/Neos.Workspace.Ui/Resources/Public/Scripts/Review.js +++ b/Neos.Workspace.Ui/Resources/Public/Scripts/Review.js @@ -14,11 +14,17 @@ function initReviewFunctions(){ if (input) { input.addEventListener( 'change', function (event) { - document.getElementById('batch-actions').classList.toggle('neos-hidden'); - document.getElementById('all-actions').classList.toggle('neos-hidden'); for (const checkbox of document.querySelectorAll('tbody input[type="checkbox"]')) { checkbox.checked = input.checked; } + if(document.querySelectorAll('tbody input[type="checkbox"]:checked').length === document.querySelectorAll('tbody input[type="checkbox"]').length + || document.querySelectorAll('tbody input[type="checkbox"]:checked').length === 0){ + document.getElementById('batch-actions').classList.add('neos-hidden'); + document.getElementById('all-actions').classList.remove('neos-hidden'); + } else { + document.getElementById('batch-actions').classList.remove('neos-hidden'); + document.getElementById('all-actions').classList.add('neos-hidden'); + } } ) for (const checkbox of document.querySelectorAll('tbody input[type="checkbox"]')) { @@ -26,7 +32,8 @@ function initReviewFunctions(){ if(!checkbox.checked){ input.checked = false; } - if(document.querySelectorAll('tbody input[type="checkbox"]:checked').length === document.querySelectorAll('tbody input[type="checkbox"]').length){ + if(document.querySelectorAll('tbody input[type="checkbox"]:checked').length === document.querySelectorAll('tbody input[type="checkbox"]').length + || document.querySelectorAll('tbody input[type="checkbox"]:checked').length === 0){ document.getElementById('batch-actions').classList.add('neos-hidden'); document.getElementById('all-actions').classList.remove('neos-hidden'); } else { From fda3b2d0b9c2d0608cd736d38816317f97550d02 Mon Sep 17 00:00:00 2001 From: pKallert Date: Mon, 2 Dec 2024 10:34:25 +0100 Subject: [PATCH 068/122] Feature: Separate changes in doucment into different dimensions --- .../Classes/Controller/WorkspaceController.php | 3 ++- .../Fusion/Components/ReviewDocumentTableRow.fusion | 1 - .../Private/Fusion/Components/ReviewTable.fusion | 13 ++++++++----- .../Resources/Public/Styles/Module.css | 4 ++++ 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 1159d41b585..66d5a9238a7 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -1064,7 +1064,8 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos $contentDimension = new ContentDimensionId($id); $dimensions[] = $contentRepository->getContentDimensionSource()->getDimension($contentDimension)->getValue($coordinate)->configuration['label']; } - $siteChanges[$siteNodeName]['documents'][$documentPath]['changes'][$relativePath] = new ChangeItem ( + $dimensionString = implode('_', $dimensions); + $siteChanges[$siteNodeName]['documents'][$documentPath]['changes'][$dimensionString][$relativePath] = new ChangeItem ( serializedNodeAddress: $nodeAddress->toJson(), hidden: $node->tags->contain(SubtreeTag::disabled()), isRemoved: $change->deleted, diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion index 188c3eadfa4..a4c25ff2ac0 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion @@ -85,7 +85,6 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F - - - + + + + + diff --git a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css index cf154c10c66..a5f67d76bb7 100644 --- a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css +++ b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css @@ -415,3 +415,7 @@ tr.neos-change + tr.neos-change td.neos-content-change { #workspaceReview .node-icons { margin-right: 7px; } + +#workspaceReview #publishOrDiscardNodesForm { + min-width: 100%; +} From 081a99571b9449c84a94cd889c9c9f3cd3ea3a6b Mon Sep 17 00:00:00 2001 From: pKallert Date: Mon, 2 Dec 2024 15:01:44 +0100 Subject: [PATCH 069/122] Feature: Change folder structure --- .../Modals/{ => Review}/ConfirmDiscardAllChanges.fusion | 0 .../Modals/{ => Review}/ConfirmDiscardSelectedChanges.fusion | 0 .../Modals/{ => Review}/ConfirmPublishAllChanges.fusion | 0 .../Modals/{ => Review}/ConfirmPublishSelectedChanges.fusion | 0 .../ConfirmDeleteWorkspaceRoleAssignment.fusion | 0 .../{ => Workspace}/CreateWorkspaceRoleAssignment.fusion | 0 .../Fusion/Components/Modals/{ => Workspace}/Delete.fusion | 0 .../Fusion/Components/Modals/{ => Workspace}/Edit.fusion | 0 .../{ => Workspace}/EditWorkspaceRoleAssignments.fusion | 0 .../Fusion/Views/{ => Review}/ConfirmDiscardAllChanges.fusion | 0 .../Views/{ => Review}/ConfirmDiscardSelectedChanges.fusion | 0 .../Fusion/Views/{ => Review}/ConfirmPublishAllChanges.fusion | 0 .../Views/{ => Review}/ConfirmPublishSelectedChanges.fusion | 0 .../Resources/Private/Fusion/Views/{ => Review}/Review.fusion | 4 ++-- .../Fusion/{Components => Views/Review}/ReviewActions.fusion | 0 .../{Components => Views/Review}/ReviewChangeDiff.fusion | 0 .../{Components => Views/Review}/ReviewChangeTableRow.fusion | 0 .../Review}/ReviewDocumentTableRow.fusion | 0 .../Fusion/{Components => Views/Review}/ReviewTable.fusion | 0 .../ConfirmDeleteWorkspaceRoleAssignments.fusion | 0 .../{ => Workspace}/CreateWorkspaceRoleAssignment.fusion | 0 .../Private/Fusion/Views/{ => Workspace}/Delete.fusion | 0 .../Private/Fusion/Views/{ => Workspace}/Edit.fusion | 0 .../Views/{ => Workspace}/EditWorkspaceRoleAssignments.fusion | 0 .../Private/Fusion/Views/{ => Workspace}/Index.fusion | 1 + .../Resources/Private/Fusion/Views/{ => Workspace}/New.fusion | 0 .../Private/Fusion/Views/{ => Workspace}/Update.fusion | 0 .../{Components => Views/Workspace}/WorkspaceCount.fusion | 0 .../Workspace}/WorkspaceRoleAssignmentTableRow.fusion | 0 .../{Components => Views/Workspace}/WorkspaceTable.fusion | 0 .../{Components => Views/Workspace}/WorkspaceTableRow.fusion | 0 .../{Components => Views/Workspace}/WorkspaceTreeNode.fusion | 0 32 files changed, 3 insertions(+), 2 deletions(-) rename Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/{ => Review}/ConfirmDiscardAllChanges.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/{ => Review}/ConfirmDiscardSelectedChanges.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/{ => Review}/ConfirmPublishAllChanges.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/{ => Review}/ConfirmPublishSelectedChanges.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/{ => Workspace}/ConfirmDeleteWorkspaceRoleAssignment.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/{ => Workspace}/CreateWorkspaceRoleAssignment.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/{ => Workspace}/Delete.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/{ => Workspace}/Edit.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/{ => Workspace}/EditWorkspaceRoleAssignments.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Views/{ => Review}/ConfirmDiscardAllChanges.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Views/{ => Review}/ConfirmDiscardSelectedChanges.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Views/{ => Review}/ConfirmPublishAllChanges.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Views/{ => Review}/ConfirmPublishSelectedChanges.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Views/{ => Review}/Review.fusion (96%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components => Views/Review}/ReviewActions.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components => Views/Review}/ReviewChangeDiff.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components => Views/Review}/ReviewChangeTableRow.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components => Views/Review}/ReviewDocumentTableRow.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components => Views/Review}/ReviewTable.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Views/{ => Workspace}/ConfirmDeleteWorkspaceRoleAssignments.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Views/{ => Workspace}/CreateWorkspaceRoleAssignment.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Views/{ => Workspace}/Delete.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Views/{ => Workspace}/Edit.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Views/{ => Workspace}/EditWorkspaceRoleAssignments.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Views/{ => Workspace}/Index.fusion (98%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Views/{ => Workspace}/New.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/Views/{ => Workspace}/Update.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components => Views/Workspace}/WorkspaceCount.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components => Views/Workspace}/WorkspaceRoleAssignmentTableRow.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components => Views/Workspace}/WorkspaceTable.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components => Views/Workspace}/WorkspaceTableRow.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components => Views/Workspace}/WorkspaceTreeNode.fusion (100%) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Review/ConfirmDiscardAllChanges.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardAllChanges.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Review/ConfirmDiscardAllChanges.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Review/ConfirmDiscardSelectedChanges.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDiscardSelectedChanges.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Review/ConfirmDiscardSelectedChanges.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Review/ConfirmPublishAllChanges.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishAllChanges.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Review/ConfirmPublishAllChanges.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Review/ConfirmPublishSelectedChanges.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmPublishSelectedChanges.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Review/ConfirmPublishSelectedChanges.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDeleteWorkspaceRoleAssignment.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/ConfirmDeleteWorkspaceRoleAssignment.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/ConfirmDeleteWorkspaceRoleAssignment.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/ConfirmDeleteWorkspaceRoleAssignment.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/CreateWorkspaceRoleAssignment.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/CreateWorkspaceRoleAssignment.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/CreateWorkspaceRoleAssignment.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/CreateWorkspaceRoleAssignment.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Delete.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/Delete.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Delete.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/Delete.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/Edit.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Edit.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/Edit.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/EditWorkspaceRoleAssignments.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/EditWorkspaceRoleAssignments.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/EditWorkspaceRoleAssignments.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/EditWorkspaceRoleAssignments.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDiscardAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ConfirmDiscardAllChanges.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDiscardAllChanges.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ConfirmDiscardAllChanges.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDiscardSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ConfirmDiscardSelectedChanges.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDiscardSelectedChanges.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ConfirmDiscardSelectedChanges.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmPublishAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ConfirmPublishAllChanges.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmPublishAllChanges.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ConfirmPublishAllChanges.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmPublishSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ConfirmPublishSelectedChanges.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmPublishSelectedChanges.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ConfirmPublishSelectedChanges.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/Review.fusion similarity index 96% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/Review.fusion index 988e074b4d5..48403fc7154 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/Review.fusion @@ -26,12 +26,12 @@ Neos.Workspace.Ui.WorkspaceController.review = Neos.Fusion:Component {
-
+
{props.i18n.id('workspaces.unpublishedChanges').arguments([selectedWorkspaceLabel]).translate()} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewActions.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewActions.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewActions.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewChangeDiff.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeDiff.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewChangeDiff.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewChangeTableRow.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewChangeTableRow.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewChangeTableRow.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewDocumentTableRow.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewDocumentTableRow.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewDocumentTableRow.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewTable.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewTable.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/ReviewTable.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewTable.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDeleteWorkspaceRoleAssignments.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/ConfirmDeleteWorkspaceRoleAssignments.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/ConfirmDeleteWorkspaceRoleAssignments.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/ConfirmDeleteWorkspaceRoleAssignments.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/CreateWorkspaceRoleAssignment.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/CreateWorkspaceRoleAssignment.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/CreateWorkspaceRoleAssignment.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/CreateWorkspaceRoleAssignment.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Delete.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/Delete.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Delete.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/Delete.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/Edit.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Edit.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/Edit.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/EditWorkspaceRoleAssignments.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/EditWorkspaceRoleAssignments.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/EditWorkspaceRoleAssignments.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/EditWorkspaceRoleAssignments.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Index.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/Index.fusion similarity index 98% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Index.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/Index.fusion index 4758d036662..a76bb678e55 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Index.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/Index.fusion @@ -21,6 +21,7 @@ Neos.Workspace.Ui.WorkspaceController.index = Neos.Fusion:Component {
diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/New.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/New.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/New.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Update.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/Update.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Update.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/Update.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceCount.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceCount.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceCount.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceCount.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceRoleAssignmentTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceRoleAssignmentTableRow.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceRoleAssignmentTableRow.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceRoleAssignmentTableRow.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTable.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceTable.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTable.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceTable.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceTableRow.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTableRow.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceTableRow.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTreeNode.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceTreeNode.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/WorkspaceTreeNode.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceTreeNode.fusion From a555db84fdbdf8b7c1c4b4af08af546dd780c9b2 Mon Sep 17 00:00:00 2001 From: pKallert Date: Mon, 2 Dec 2024 16:50:30 +0100 Subject: [PATCH 070/122] Fix: Make menu work on review page --- .../Private/Fusion/Views/Review/Review.fusion | 17 ++++------------- .../Views/Workspace/WorkspaceTableRow.fusion | 12 ++++++++++-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/Review.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/Review.fusion index 48403fc7154..f0741adf60d 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/Review.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/Review.fusion @@ -20,18 +20,16 @@ Neos.Workspace.Ui.WorkspaceController.review = Neos.Fusion:Component { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} - renderer = Neos.Fusion:Match { - @subject = ${request.format} - @default = afx` + renderer = afx`
-
+
{props.i18n.id('workspaces.unpublishedChanges').arguments([selectedWorkspaceLabel]).translate()} @@ -66,13 +64,6 @@ Neos.Workspace.Ui.WorkspaceController.review = Neos.Fusion:Component {
-
+
` - htmx = afx` - - - ` - } } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceTableRow.fusion index 24e18e5dfbf..93a6e525d55 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceTableRow.fusion @@ -54,6 +54,13 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion with-acl = 'user-plus' } reviewWorkspaceUri = Neos.Fusion:ActionUri { + action = 'review' + format = 'htmx' + arguments { + workspace = ${props.workspaceListItem.name} + } + } + reviewWorkspaceFullUri = Neos.Fusion:ActionUri { action = 'review' arguments { workspace = ${props.workspaceListItem.name} @@ -130,8 +137,9 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion title={private.i18n.id(props.workspaceListItem.pendingChanges.total ? 'workspaces.reviewWorkspace' : 'workspaces.reviewWorkspace.noChanges').arguments([props.workspaceListItem.title])} attributes.disabled={props.workspaceListItem.pendingChanges.total == 0} attributes.hx-get={private.reviewWorkspaceUri} - attributes.hx-replace-url={private.reviewWorkspaceUri} - attributes.hx-target="body" + attributes.hx-replace-url={private.reviewWorkspaceFullUri} + attributes.hx-target="#workspace-module-content" + attributes.hx-swap="outerHTML" /> Date: Mon, 2 Dec 2024 17:30:21 +0100 Subject: [PATCH 071/122] Fix: Show the right labels for properties --- .../Controller/WorkspaceController.php | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 66d5a9238a7..a9439b3e7bf 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -1288,7 +1288,28 @@ protected function getPropertyLabel($propertyName, Node $changedNode) ) { return $propertyName; } - return $properties[$propertyName]['ui']['label']; + $packageKey = 'Neos.Neos'; + $source = 'Main'; + $idParts = explode(':', $properties[$propertyName]['ui']['label'], 3); + switch (count($idParts)) { + case 2: + $packageKey = $idParts[0]; + $id = $idParts[1]; + break; + case 3: + $packageKey = $idParts[0]; + $source = str_replace('.', '/', $idParts[1]); + $id = $idParts[2]; + break; + } + return $this->translator->translateById( + $id, + [], + null, + null, + $source, + $packageKey + ) ?: $properties[$propertyName]['ui']['label']; } /** From 9930da0144a61ebdbfc23cf688fa4239bfccbe52 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Tue, 3 Dec 2024 16:35:43 +0100 Subject: [PATCH 072/122] TASK: Change fusion folder structure --- .../Private/Fusion/{Components => Common}/FlashMessages.fusion | 0 .../Resources/Private/Fusion/{Components => Common}/Footer.fusion | 0 .../Private/Fusion/{Components => Common}/HtmxConfig.fusion | 0 .../Fusion/{Components => Common}/Presentationals/Badge.fusion | 0 .../Fusion/{Components => Common}/Presentationals/Button.fusion | 0 .../Fusion/{Components => Common}/Presentationals/Icon.fusion | 0 .../{Components => Common}/Presentationals/Indicator.fusion | 0 .../Review/Actions}/ConfirmDiscardAllChanges.fusion | 0 .../Review/Actions}/ConfirmDiscardSelectedChanges.fusion | 0 .../Review/Actions}/ConfirmPublishAllChanges.fusion | 0 .../Review/Actions}/ConfirmPublishSelectedChanges.fusion | 0 .../{Views/Review => Features/Review/Actions}/Review.fusion | 0 .../Review => Features/Review/Components}/ReviewActions.fusion | 0 .../Review => Features/Review/Components}/ReviewChangeDiff.fusion | 0 .../Review/Components}/ReviewChangeTableRow.fusion | 0 .../Review/Components}/ReviewDocumentTableRow.fusion | 0 .../Review => Features/Review/Components}/ReviewTable.fusion | 0 .../Review/Modals}/ConfirmDiscardAllChanges.fusion | 0 .../Review/Modals}/ConfirmDiscardSelectedChanges.fusion | 0 .../Review/Modals}/ConfirmPublishAllChanges.fusion | 0 .../Review/Modals}/ConfirmPublishSelectedChanges.fusion | 0 .../Actions}/ConfirmDeleteWorkspaceRoleAssignments.fusion | 0 .../Workspace/Actions}/CreateWorkspaceRoleAssignment.fusion | 0 .../{Views/Workspace => Features/Workspace/Actions}/Delete.fusion | 0 .../{Views/Workspace => Features/Workspace/Actions}/Edit.fusion | 0 .../Workspace/Actions}/EditWorkspaceRoleAssignments.fusion | 0 .../{Views/Workspace => Features/Workspace/Actions}/Index.fusion | 0 .../{Views/Workspace => Features/Workspace/Actions}/New.fusion | 0 .../{Views/Workspace => Features/Workspace/Actions}/Update.fusion | 0 .../Workspace/Components}/WorkspaceCount.fusion | 0 .../Workspace/Components}/WorkspaceRoleAssignmentTableRow.fusion | 0 .../Workspace/Components}/WorkspaceTable.fusion | 0 .../Workspace/Components}/WorkspaceTableRow.fusion | 0 .../Workspace/Components}/WorkspaceTreeNode.fusion | 0 .../Workspace/Modals}/ConfirmDeleteWorkspaceRoleAssignment.fusion | 0 .../Workspace/Modals}/CreateWorkspaceRoleAssignment.fusion | 0 .../Modals/Workspace => Features/Workspace/Modals}/Delete.fusion | 0 .../Modals/Workspace => Features/Workspace/Modals}/Edit.fusion | 0 .../Workspace/Modals}/EditWorkspaceRoleAssignments.fusion | 0 39 files changed, 0 insertions(+), 0 deletions(-) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components => Common}/FlashMessages.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components => Common}/Footer.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components => Common}/HtmxConfig.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components => Common}/Presentationals/Badge.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components => Common}/Presentationals/Button.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components => Common}/Presentationals/Icon.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components => Common}/Presentationals/Indicator.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Review => Features/Review/Actions}/ConfirmDiscardAllChanges.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Review => Features/Review/Actions}/ConfirmDiscardSelectedChanges.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Review => Features/Review/Actions}/ConfirmPublishAllChanges.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Review => Features/Review/Actions}/ConfirmPublishSelectedChanges.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Review => Features/Review/Actions}/Review.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Review => Features/Review/Components}/ReviewActions.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Review => Features/Review/Components}/ReviewChangeDiff.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Review => Features/Review/Components}/ReviewChangeTableRow.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Review => Features/Review/Components}/ReviewDocumentTableRow.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Review => Features/Review/Components}/ReviewTable.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components/Modals/Review => Features/Review/Modals}/ConfirmDiscardAllChanges.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components/Modals/Review => Features/Review/Modals}/ConfirmDiscardSelectedChanges.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components/Modals/Review => Features/Review/Modals}/ConfirmPublishAllChanges.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components/Modals/Review => Features/Review/Modals}/ConfirmPublishSelectedChanges.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Workspace => Features/Workspace/Actions}/ConfirmDeleteWorkspaceRoleAssignments.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Workspace => Features/Workspace/Actions}/CreateWorkspaceRoleAssignment.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Workspace => Features/Workspace/Actions}/Delete.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Workspace => Features/Workspace/Actions}/Edit.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Workspace => Features/Workspace/Actions}/EditWorkspaceRoleAssignments.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Workspace => Features/Workspace/Actions}/Index.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Workspace => Features/Workspace/Actions}/New.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Workspace => Features/Workspace/Actions}/Update.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Workspace => Features/Workspace/Components}/WorkspaceCount.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Workspace => Features/Workspace/Components}/WorkspaceRoleAssignmentTableRow.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Workspace => Features/Workspace/Components}/WorkspaceTable.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Workspace => Features/Workspace/Components}/WorkspaceTableRow.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Views/Workspace => Features/Workspace/Components}/WorkspaceTreeNode.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components/Modals/Workspace => Features/Workspace/Modals}/ConfirmDeleteWorkspaceRoleAssignment.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components/Modals/Workspace => Features/Workspace/Modals}/CreateWorkspaceRoleAssignment.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components/Modals/Workspace => Features/Workspace/Modals}/Delete.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components/Modals/Workspace => Features/Workspace/Modals}/Edit.fusion (100%) rename Neos.Workspace.Ui/Resources/Private/Fusion/{Components/Modals/Workspace => Features/Workspace/Modals}/EditWorkspaceRoleAssignments.fusion (100%) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/FlashMessages.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/FlashMessages.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/FlashMessages.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Common/FlashMessages.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Footer.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Footer.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Footer.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Common/Footer.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/HtmxConfig.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/HtmxConfig.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/HtmxConfig.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Common/HtmxConfig.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Presentationals/Badge.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Badge.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Presentationals/Badge.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Badge.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Presentationals/Button.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Button.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Presentationals/Button.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Button.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Presentationals/Icon.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Icon.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Presentationals/Icon.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Icon.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Presentationals/Indicator.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Indicator.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Presentationals/Indicator.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Indicator.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ConfirmDiscardAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/ConfirmDiscardAllChanges.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ConfirmDiscardAllChanges.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/ConfirmDiscardAllChanges.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ConfirmDiscardSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/ConfirmDiscardSelectedChanges.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ConfirmDiscardSelectedChanges.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/ConfirmDiscardSelectedChanges.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ConfirmPublishAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/ConfirmPublishAllChanges.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ConfirmPublishAllChanges.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/ConfirmPublishAllChanges.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ConfirmPublishSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/ConfirmPublishSelectedChanges.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ConfirmPublishSelectedChanges.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/ConfirmPublishSelectedChanges.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/Review.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Review.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/Review.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Review.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewActions.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewActions.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewChangeDiff.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewChangeDiff.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewChangeDiff.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewChangeDiff.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewChangeTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewChangeTableRow.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewChangeTableRow.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewChangeTableRow.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewDocumentTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewDocumentTableRow.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewTable.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewTable.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Review/ReviewTable.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewTable.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Review/ConfirmDiscardAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Review/ConfirmDiscardAllChanges.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Review/ConfirmDiscardSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardSelectedChanges.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Review/ConfirmDiscardSelectedChanges.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardSelectedChanges.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Review/ConfirmPublishAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmPublishAllChanges.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Review/ConfirmPublishAllChanges.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmPublishAllChanges.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Review/ConfirmPublishSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmPublishSelectedChanges.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Review/ConfirmPublishSelectedChanges.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmPublishSelectedChanges.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/ConfirmDeleteWorkspaceRoleAssignments.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/ConfirmDeleteWorkspaceRoleAssignments.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/ConfirmDeleteWorkspaceRoleAssignments.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/ConfirmDeleteWorkspaceRoleAssignments.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/CreateWorkspaceRoleAssignment.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/CreateWorkspaceRoleAssignment.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/CreateWorkspaceRoleAssignment.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/CreateWorkspaceRoleAssignment.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/Delete.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Delete.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/Delete.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Delete.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Edit.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/Edit.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Edit.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/EditWorkspaceRoleAssignments.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/EditWorkspaceRoleAssignments.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/EditWorkspaceRoleAssignments.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/EditWorkspaceRoleAssignments.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/Index.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/Index.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/New.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/New.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/New.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/New.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/Update.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Update.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/Update.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Update.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceCount.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceCount.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceCount.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceCount.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceRoleAssignmentTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceRoleAssignmentTableRow.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceRoleAssignmentTableRow.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceRoleAssignmentTableRow.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceTable.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceTable.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceTableRow.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceTreeNode.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTreeNode.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Views/Workspace/WorkspaceTreeNode.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTreeNode.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/ConfirmDeleteWorkspaceRoleAssignment.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/ConfirmDeleteWorkspaceRoleAssignment.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/ConfirmDeleteWorkspaceRoleAssignment.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/ConfirmDeleteWorkspaceRoleAssignment.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/CreateWorkspaceRoleAssignment.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/CreateWorkspaceRoleAssignment.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/CreateWorkspaceRoleAssignment.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/CreateWorkspaceRoleAssignment.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/Delete.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Delete.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/Delete.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Delete.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Edit.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/Edit.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Edit.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/EditWorkspaceRoleAssignments.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/EditWorkspaceRoleAssignments.fusion similarity index 100% rename from Neos.Workspace.Ui/Resources/Private/Fusion/Components/Modals/Workspace/EditWorkspaceRoleAssignments.fusion rename to Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/EditWorkspaceRoleAssignments.fusion From cb7f4ea84fe664c2f0e853af1b199e713449b954 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Tue, 3 Dec 2024 16:36:51 +0100 Subject: [PATCH 073/122] BUGFIX: Resolve nested swap issues --- .../Private/Fusion/Common/HtmxConfig.fusion | 58 ++++++++++--------- .../Review/Components/ReviewActions.fusion | 6 +- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Common/HtmxConfig.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/HtmxConfig.fusion index 21fbc64763c..147783dd929 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Common/HtmxConfig.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/HtmxConfig.fusion @@ -1,34 +1,40 @@ prototype(Neos.Workspace.Ui:Component.HTMXConfig) < prototype(Neos.Fusion:Component) { htmxConfig = Neos.Fusion:DataStructure { - // 204 - No Content by default does nothing, but is not an error - noContent { - code = 204 - swap = false - } - // 200 & 300 responses are non-errors and are swapped - nonErrors { - code = '[23]..' - swap = true - } - // 422 responses are swapped - invalidRequest { - code = 422 - swap = true - } - // 400 & 500 responses are not swapped and are errors - errors { - code = '[45]..' - swap = false - error = true - } - // 30X responses are swapped - redirects { - code = '[30]..' - swap = true + // See https://htmx.org/docs/#response-handling + responseHandling { + // 204 - No Content by default does nothing, but is not an error + noContent { + code = 204 + swap = false + } + // 200 & 300 responses are non-errors and are swapped + nonErrors { + code = '[23]..' + swap = true + } + // 422 responses are swapped + invalidRequest { + code = 422 + swap = true + } + // 400 & 500 responses are not swapped and are errors + errors { + code = '[45]..' + swap = false + error = true + } + // 30X responses are swapped + redirects { + code = '[30]..' + swap = true + } + @process.toArray = ${Array.values(value)} } + // See https://htmx.org/attributes/hx-swap-oob/#nested-oob-swaps + allowNestedOobSwaps = false } renderer = afx` - + ` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion index 99ff5944640..b466eaaf741 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion @@ -12,6 +12,7 @@ prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Com i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} indexWorkspaceUri = Neos.Fusion:ActionUri { action = 'index' + format = 'html' } confirmDiscardAllChanges = Neos.Fusion:ActionUri { action = 'confirmDiscardAllChanges' @@ -55,9 +56,10 @@ prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Com icon="fas fa-chevron-left" label={props.i18n.id('back')} attributes.hx-get={private.indexWorkspaceUri} - attributes.hx-target="body" - attributes.hx-swap="innerHTML" attributes.hx-replace-url={private.indexWorkspaceUri} + attributes.hx-select="#workspace-module-content" + attributes.hx-target="#workspaceReview" + attributes.hx-swap="outerHTML" />
From 6f4569378660155e89f4c7ce3c47f8ac1043374e Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Tue, 3 Dec 2024 16:37:26 +0100 Subject: [PATCH 074/122] module wrap --- .../Fusion/Common/ModuleWrapper.fusion | 20 +++++++++++++++++++ .../Features/Review/Actions/Review.fusion | 2 +- .../Features/Workspace/Actions/Index.fusion | 6 +++--- 3 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Common/ModuleWrapper.fusion diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Common/ModuleWrapper.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/ModuleWrapper.fusion new file mode 100644 index 00000000000..bec875ad9bc --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/ModuleWrapper.fusion @@ -0,0 +1,20 @@ +## +# Wrapper to be used for each "full" view like "index" or "review" +# +prototype(Neos.Workspace.Ui:Component.ModuleWrapper) < prototype(Neos.Fusion:Component) { + /// string + content = '' + + renderer = afx` + + + +
+ {props.content} +
+ ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Review.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Review.fusion index f0741adf60d..1a5beef3b9e 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Review.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Review.fusion @@ -22,9 +22,9 @@ Neos.Workspace.Ui.WorkspaceController.review = Neos.Fusion:Component { renderer = afx`
diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion index a76bb678e55..69b56210ef0 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion @@ -19,14 +19,14 @@ Neos.Workspace.Ui.WorkspaceController.index = Neos.Fusion:Component { @subject = ${request.format} @default = afx`
-
+
` - // TODO: we should put the count into the htmx rendered content as well + htmx = afx` Date: Tue, 3 Dec 2024 17:03:41 +0100 Subject: [PATCH 075/122] TASK: Introduce module wrapper for workspace module views --- .../Fusion/Common/ModuleWrapper.fusion | 4 ++ .../Features/Review/Actions/Review.fusion | 68 +++++++++---------- .../Review/Components/ReviewActions.fusion | 2 +- .../Features/Workspace/Actions/Index.fusion | 16 ++--- .../Components/WorkspaceTableRow.fusion | 11 +-- 5 files changed, 43 insertions(+), 58 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Common/ModuleWrapper.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/ModuleWrapper.fusion index bec875ad9bc..ff99d993d3d 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Common/ModuleWrapper.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/ModuleWrapper.fusion @@ -2,6 +2,9 @@ # Wrapper to be used for each "full" view like "index" or "review" # prototype(Neos.Workspace.Ui:Component.ModuleWrapper) < prototype(Neos.Fusion:Component) { + /// array + flashMessages = ${[]} + /// string content = '' @@ -16,5 +19,6 @@ prototype(Neos.Workspace.Ui:Component.ModuleWrapper) < prototype(Neos.Fusion:Com > {props.content}
+
` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Review.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Review.fusion index 1a5beef3b9e..964ab2a0b29 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Review.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Review.fusion @@ -1,11 +1,11 @@ Neos.Workspace.Ui.WorkspaceController.review = Neos.Fusion:Component { - ///todo Neos\ContentRepository\Core\Projection\Workspace\Workspace + /// string selectedWorkspaceName = ${selectedWorkspaceName} /// string selectedWorkspaceLabel = ${selectedWorkspaceLabel} - + /// \Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName baseWorkspaceName = ${baseWorkspaceName} - + /// string baseWorkspaceLabel = ${baseWorkspaceLabel} /// bool canPublishToBaseWorkspace = ${canPublishToBaseWorkspace} @@ -21,28 +21,23 @@ Neos.Workspace.Ui.WorkspaceController.review = Neos.Fusion:Component { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} renderer = afx` -
- - - -
-
- - {props.i18n.id('workspaces.unpublishedChanges').arguments([selectedWorkspaceLabel]).translate()} - + +
+
+ + {props.i18n.id('workspaces.unpublishedChanges').arguments([selectedWorkspaceLabel]).translate()} + - + {props.i18n.id('workspaces.reviewWorkspace.disabled').arguments([selectedWorkspaceLabel]).translate()}
- - -
-
-
-
- ` + +
+
+ + + ` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion index b466eaaf741..dd3625572ab 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion @@ -58,7 +58,7 @@ prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Com attributes.hx-get={private.indexWorkspaceUri} attributes.hx-replace-url={private.indexWorkspaceUri} attributes.hx-select="#workspace-module-content" - attributes.hx-target="#workspaceReview" + attributes.hx-target="#workspace-module-content" attributes.hx-swap="outerHTML" /> diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion index 69b56210ef0..7dce63756b0 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion @@ -3,8 +3,6 @@ Neos.Workspace.Ui.WorkspaceController.index = Neos.Fusion:Component { userWorkspaceName = ${userWorkspaceName} /// Neos\Workspace\Ui\ViewModel\WorkspaceListItems workspaceListItems = ${workspaceListItems} - /// array - flashMessages = ${flashMessages} newAction = Neos.Fusion:UriBuilder { action = 'new' @@ -18,15 +16,10 @@ Neos.Workspace.Ui.WorkspaceController.index = Neos.Fusion:Component { renderer = Neos.Fusion:Match { @subject = ${request.format} @default = afx` -
- - - -
+
-
-
+ ` htmx = afx` diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion index 93a6e525d55..994d7c6d059 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion @@ -55,13 +55,7 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion } reviewWorkspaceUri = Neos.Fusion:ActionUri { action = 'review' - format = 'htmx' - arguments { - workspace = ${props.workspaceListItem.name} - } - } - reviewWorkspaceFullUri = Neos.Fusion:ActionUri { - action = 'review' + format = 'html' arguments { workspace = ${props.workspaceListItem.name} } @@ -137,8 +131,9 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion title={private.i18n.id(props.workspaceListItem.pendingChanges.total ? 'workspaces.reviewWorkspace' : 'workspaces.reviewWorkspace.noChanges').arguments([props.workspaceListItem.title])} attributes.disabled={props.workspaceListItem.pendingChanges.total == 0} attributes.hx-get={private.reviewWorkspaceUri} - attributes.hx-replace-url={private.reviewWorkspaceFullUri} + attributes.hx-replace-url={private.reviewWorkspaceUri} attributes.hx-target="#workspace-module-content" + attributes.hx-select="#workspace-module-content" attributes.hx-swap="outerHTML" /> Date: Tue, 3 Dec 2024 17:05:49 +0100 Subject: [PATCH 076/122] BUGFIX: Show flash message on workspace delete --- Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index a9439b3e7bf..f445fde800c 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -481,9 +481,6 @@ public function deleteAction(WorkspaceName $workspaceName): void [$workspaceMetadata->title->value], ) ); - - // WHY: Redirect to refresh data on page (e.g. workspace list & count) - $this->redirect('index'); // Render a confirmation form if the request is not a POST request } else { $this->view->assign('workspaceName', $workspace->workspaceName->value); From 7133a956eb7840774ff74280ae3dac3931cbad02 Mon Sep 17 00:00:00 2001 From: pKallert Date: Mon, 9 Dec 2024 10:10:35 +0100 Subject: [PATCH 077/122] Fix: Do not show duplicate footer on discard/publish single Document --- .../Components/ReviewDocumentTableRow.fusion | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion index a4c25ff2ac0..1d78d893e9b 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion @@ -32,7 +32,6 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F } discardDocumentAction = Neos.Fusion:ActionUri { action = 'discardDocument' - format = 'json' arguments { nodeAddress = ${document.document.documentNodeAddress} selectedWorkspace = ${selectedWorkspaceName} @@ -40,7 +39,6 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F } publishDocumentAction = Neos.Fusion:ActionUri { action = 'publishDocument' - format = 'json' arguments { nodeAddress = ${document.document.documentNodeAddress} selectedWorkspace = ${selectedWorkspaceName} @@ -90,9 +88,9 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F icon="fas fa-trash-alt icon-white" isDanger={true} attributes.hx-get={private.discardDocumentAction} - attributes.hx-swap='innerHTML' - attributes.hx-target="#workspaceReview" - attributes.hx-swap="innerHTML" + attributes.hx-select="#workspace-module-content" + attributes.hx-target="#workspace-module-content" + attributes.hx-swap="outerHTML" attributes.disabled={!props.canPublishToWorkspace } /> From 6560a64a09458276f65130fb6ccaff39e56d8ed9 Mon Sep 17 00:00:00 2001 From: Marc Henry Schultz <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 12 Dec 2024 10:38:32 +0100 Subject: [PATCH 078/122] Adjust to PSR12 code style for parameter spacing Co-authored-by: Bastian Waidelich --- .../Classes/ViewModel/ChangeItem.php | 22 +++++++++---------- ...mDeleteWorkspaceRoleAssignmentFormData.php | 9 ++++---- .../CreateWorkspaceRoleAssignmentFormData.php | 10 ++++----- .../ViewModel/EditWorkspaceFormData.php | 20 ++++++++--------- .../EditWorkspaceRoleAssignmentsFormData.php | 15 ++++++------- 5 files changed, 36 insertions(+), 40 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ChangeItem.php b/Neos.Workspace.Ui/Classes/ViewModel/ChangeItem.php index 38e7ac344b4..54e7aae694a 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ChangeItem.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ChangeItem.php @@ -20,17 +20,17 @@ final readonly class ChangeItem { public function __construct( - public string $serializedNodeAddress, - public bool $hidden, - public bool $isRemoved, - public bool $isNew, - public bool $isMoved, - public array $dimensions, - public ?string $lastModificationDateTime, - public ?string $createdDateTime, - public string $label, - public string $icon, - public ContentChangeItems $contentChanges, + public string $serializedNodeAddress, + public bool $hidden, + public bool $isRemoved, + public bool $isNew, + public bool $isMoved, + public array $dimensions, + public ?string $lastModificationDateTime, + public ?string $createdDateTime, + public string $label, + public string $icon, + public ContentChangeItems $contentChanges, ) { } } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ConfirmDeleteWorkspaceRoleAssignmentFormData.php b/Neos.Workspace.Ui/Classes/ViewModel/ConfirmDeleteWorkspaceRoleAssignmentFormData.php index 9f67e88719f..96dd037be88 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ConfirmDeleteWorkspaceRoleAssignmentFormData.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ConfirmDeleteWorkspaceRoleAssignmentFormData.php @@ -22,11 +22,10 @@ final readonly class ConfirmDeleteWorkspaceRoleAssignmentFormData { public function __construct( - public WorkspaceName $workspaceName, + public WorkspaceName $workspaceName, public WorkspaceTitle $workspaceTitle, - public string $subjectValue, - public string $subjectType, - ) - { + public string $subjectValue, + public string $subjectType, + ) { } } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/CreateWorkspaceRoleAssignmentFormData.php b/Neos.Workspace.Ui/Classes/ViewModel/CreateWorkspaceRoleAssignmentFormData.php index 657d772c31a..03ebfd6de24 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/CreateWorkspaceRoleAssignmentFormData.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/CreateWorkspaceRoleAssignmentFormData.php @@ -29,30 +29,30 @@ final readonly class CreateWorkspaceRoleAssignmentFormData { public function __construct( - public WorkspaceName $workspaceName, + public WorkspaceName $workspaceName, public WorkspaceTitle $workspaceTitle, /** * Options for the workspaceManager selector where the key is the user identifier and the value is the user name. * @var array */ - public array $userOptions, + public array $userOptions, /** * Options for the workspaceManager selector where the value is the group. * @var array */ - public array $groupOptions, + public array $groupOptions, /** * TODO: translate subject type labels? * Options for the workspaceManager selector where the key is the subject type and the value is the subject label. * @var array */ - public array $subjectTypeOptions, + public array $subjectTypeOptions, /** * TODO: translate role labels? * Options for the workspaceManager selector where the key is the role identifier and the value is the role label. * @var array */ - public array $roleOptions + public array $roleOptions ) { } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php index 0553c4f0053..c31f6e9f2b0 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php @@ -22,18 +22,16 @@ #[Flow\Proxy(false)] final readonly class EditWorkspaceFormData { + /** + * @param array $baseWorkspaceOptions Options for the baseWorkspace selector where the key is the workspace name and the value is the workspace title. + */ public function __construct( - public WorkspaceName $workspaceName, - public WorkspaceTitle $workspaceTitle, + public WorkspaceName $workspaceName, + public WorkspaceTitle $workspaceTitle, public WorkspaceDescription $workspaceDescription, - public bool $workspaceHasChanges, - public WorkspaceName $baseWorkspaceName, - /** - * Options for the baseWorkspace selector where the key is the workspace name and the value is the workspace title. - * @var array - */ - public array $baseWorkspaceOptions, - ) - { + public bool $workspaceHasChanges, + public WorkspaceName $baseWorkspaceName, + public array $baseWorkspaceOptions, + ) { } } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceRoleAssignmentsFormData.php b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceRoleAssignmentsFormData.php index 8cf61071f96..08fab42cdad 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceRoleAssignmentsFormData.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceRoleAssignmentsFormData.php @@ -28,15 +28,14 @@ #[Flow\Proxy(false)] final readonly class EditWorkspaceRoleAssignmentsFormData { + /** + * @param array $roleAssignments + */ public function __construct( - public WorkspaceName $workspaceName, + public WorkspaceName $workspaceName, public WorkspaceTitle $workspaceTitle, - public bool $roleAssignmentsEditable, - /** - * @var array - */ - public array $roleAssignments, - ) - { + public bool $roleAssignmentsEditable, + public array $roleAssignments, + ) { } } From 461815cb7cec613749cd4fb95cc20f9d2e4648f7 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 12 Dec 2024 11:54:39 +0100 Subject: [PATCH 079/122] TASK: Adjustments after workspace service fixes see https://github.com/neos/neos-development-collection/pull/5334 --- .../Controller/WorkspaceController.php | 31 ++++++++++--------- .../ViewModel/RoleAssignmentListItem.php | 4 +-- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 25df93fdf4c..7e80fde65de 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -213,9 +213,10 @@ public function reviewAction(WorkspaceName $workspace): void $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspace); $baseWorkspaceMetadata = null; $baseWorkspacePermissions = null; - if ($workspaceObj->baseWorkspaceName !== null) { - $baseWorkspace = $contentRepository->findWorkspaceByName($workspaceObj->baseWorkspaceName); - assert($baseWorkspace !== null); + $baseWorkspace = $workspaceObj->baseWorkspaceName !== null + ? $contentRepository->findWorkspaceByName($workspaceObj->baseWorkspaceName) + : null; + if ($baseWorkspace !== null) { $baseWorkspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $baseWorkspace->workspaceName); $baseWorkspacePermissions = $this->authorizationService->getWorkspacePermissions($contentRepositoryId, $baseWorkspace->workspaceName, $this->securityContext->getRoles(), $currentUser->getId()); } @@ -225,7 +226,7 @@ public function reviewAction(WorkspaceName $workspace): void 'baseWorkspaceName' => $workspaceObj->baseWorkspaceName, 'baseWorkspaceLabel' => $baseWorkspaceMetadata?->title->value, 'canPublishToBaseWorkspace' => $baseWorkspacePermissions?->write ?? false, - 'canPublishToWorkspace' => $workspacePermissions?->write ?? false, + 'canPublishToWorkspace' => $workspacePermissions->write, 'siteChanges' => $this->computeSiteChanges($workspaceObj, $contentRepository), 'contentDimensions' => $contentRepository->getContentDimensionSource()->getContentDimensionsOrderedByPriority() ]); @@ -369,10 +370,10 @@ public function updateAction( ); // Update Base Workspace - $this->workspaceService->setBaseWorkspace( + $this->workspacePublishingService->changeBaseWorkspace( $contentRepositoryId, $workspaceName, - $baseWorkspace, + $baseWorkspace ); $this->addFlashMessage( @@ -495,9 +496,9 @@ public function editWorkspaceRoleAssignmentsAction(WorkspaceName $workspaceName) $workspaceRoleAssignments = []; foreach ($this->workspaceService->getWorkspaceRoleAssignments($contentRepositoryId, $workspaceName) as $workspaceRoleAssignment) { - $subjectLabel = match ($workspaceRoleAssignment->subjectType) { + $subjectLabel = match ($workspaceRoleAssignment->subject->type) { WorkspaceRoleSubjectType::USER => $this->userService->findUserById(UserId::fromString($workspaceRoleAssignment->subject->value))?->getLabel(), - default => $workspaceRoleAssignment->subject->value, + WorkspaceRoleSubjectType::GROUP => $workspaceRoleAssignment->subject->value, }; $roleLabel = $workspaceRoleAssignment->role->value; @@ -505,9 +506,9 @@ public function editWorkspaceRoleAssignmentsAction(WorkspaceName $workspaceName) $workspaceRoleAssignments[] = new RoleAssignmentListItem( subjectValue: $workspaceRoleAssignment->subject->value, subjectLabel: $subjectLabel, - subjectTypeValue: $workspaceRoleAssignment->subjectType->value, + subjectTypeValue: $workspaceRoleAssignment->subject->type->value, roleLabel: $roleLabel, - subjectType: $workspaceRoleAssignment->subjectType->value, + subjectType: $workspaceRoleAssignment->subject->type->value, ); } @@ -572,8 +573,8 @@ public function addWorkspaceRoleAssignmentAction( { // TODO: Validate if user can add role assignment to workspace - $subject = WorkspaceRoleSubject::fromString($subjectValue); $subjectType = WorkspaceRoleSubjectType::from($subjectTypeValue); + $subject = WorkspaceRoleSubject::create($subjectType, $subjectValue); $role = WorkspaceRole::from($roleValue); if ($subjectType === WorkspaceRoleSubjectType::USER) { @@ -612,8 +613,10 @@ public function deleteWorkspaceRoleAssignmentAction(WorkspaceName $workspaceName $this->workspaceService->unassignWorkspaceRole( $contentRepositoryId, $workspaceName, - WorkspaceRoleSubjectType::from($subjectType), - WorkspaceRoleSubject::fromString($subjectValue), + WorkspaceRoleSubject::create( + WorkspaceRoleSubjectType::from($subjectType), + $subjectValue + ) ); } catch (\Exception $e) { // TODO: error handling @@ -1535,7 +1538,7 @@ private function addUserRoleAssignment(WorkspaceName $workspaceName, WorkspaceRo ); } - private function addGroupRoleAssignment(WorkspaceName $workspaceName, WorkspaceRoleSubject $subject, WorkspaceRole $role) + private function addGroupRoleAssignment(WorkspaceName $workspaceName, WorkspaceRoleSubject $subject, WorkspaceRole $role): void { // TODO check if group exists? $this->workspaceService->assignWorkspaceRole( diff --git a/Neos.Workspace.Ui/Classes/ViewModel/RoleAssignmentListItem.php b/Neos.Workspace.Ui/Classes/ViewModel/RoleAssignmentListItem.php index bc054aadb88..3d827a94e9e 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/RoleAssignmentListItem.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/RoleAssignmentListItem.php @@ -27,13 +27,13 @@ #[Flow\Proxy(false)] final readonly class RoleAssignmentListItem { + // todo $subjectType and $subjectTypeValue are exact the same??? public function __construct( public string $subjectValue, public string $subjectLabel, public string $subjectTypeValue, public string $roleLabel, public string $subjectType, - ) - { + ) { } } From fd2a043b4eafdf8b1d1a872fa5e90cb7e008900d Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:59:06 +0100 Subject: [PATCH 080/122] TASK: Add `equals` helper to subtree tags --- .../SubtreeTagging/Dto/SubtreeTags.php | 11 +++++- .../Projection/ContentGraph/NodeTags.php | 6 +++ .../SubtreeTagging/Dto/SubtreeTagsTest.php | 38 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/Neos.ContentRepository.Core/Classes/Feature/SubtreeTagging/Dto/SubtreeTags.php b/Neos.ContentRepository.Core/Classes/Feature/SubtreeTagging/Dto/SubtreeTags.php index 8c22ff8e734..1195de7829a 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/SubtreeTagging/Dto/SubtreeTags.php +++ b/Neos.ContentRepository.Core/Classes/Feature/SubtreeTagging/Dto/SubtreeTags.php @@ -27,7 +27,6 @@ */ private array $tags; - private function __construct(SubtreeTag ...$tags) { $tagsByValue = []; @@ -83,6 +82,11 @@ public function intersection(self $other): self return self::fromArray(array_intersect_key($this->tags, $other->tags)); } + public function difference(self $other): self + { + return self::fromArray(array_diff_key($this->tags, $other->tags)); + } + public function merge(self $other): self { return self::fromArray(array_merge($this->tags, $other->tags)); @@ -106,6 +110,11 @@ public function toStringArray(): array return $this->map(static fn (SubtreeTag $tag) => $tag->value); } + public function equals(SubtreeTags $other): bool + { + return count($this->tags) === count($other->tags) && array_diff_key($this->tags, $other->tags) === []; + } + public function getIterator(): \Traversable { yield from array_values($this->tags); diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/NodeTags.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/NodeTags.php index c22faebd7f4..c511e86b9a9 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/NodeTags.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/NodeTags.php @@ -117,6 +117,12 @@ public function toStringArray(): array return $this->map(static fn (SubtreeTag $tag) => $tag->value); } + public function equals(NodeTags $other): bool + { + return $this->tags->equals($other->tags) + && $this->inheritedTags->equals($other->inheritedTags); + } + public function getIterator(): Traversable { foreach ($this->tags as $tag) { diff --git a/Neos.ContentRepository.Core/Tests/Unit/Feature/SubtreeTagging/Dto/SubtreeTagsTest.php b/Neos.ContentRepository.Core/Tests/Unit/Feature/SubtreeTagging/Dto/SubtreeTagsTest.php index 356e0fcd762..0f810240d58 100644 --- a/Neos.ContentRepository.Core/Tests/Unit/Feature/SubtreeTagging/Dto/SubtreeTagsTest.php +++ b/Neos.ContentRepository.Core/Tests/Unit/Feature/SubtreeTagging/Dto/SubtreeTagsTest.php @@ -132,6 +132,44 @@ public function intersectionTests(array $tags1, array $tags2, array $expectedRes self::assertSame($expectedResult, SubtreeTags::fromStrings(...$tags1)->intersection(SubtreeTags::fromStrings(...$tags2))->toStringArray()); } + public static function differenceDataProvider(): iterable + { + yield 'empty' => ['tags1' => [], 'tags2' => [], 'expectedResult' => []]; + yield 'one empty' => ['tags1' => [], 'tags2' => ['foo'], 'expectedResult' => []]; + yield 'two empty' => ['tags1' => ['foo'], 'tags2' => [], 'expectedResult' => ['foo']]; + yield 'no intersection' => ['tags1' => ['foo', 'bar'], 'tags2' => ['baz', 'foos'], 'expectedResult' => ['foo', 'bar']]; + yield 'with intersection' => ['tags1' => ['foo', 'bar', 'baz'], 'tags2' => ['baz', 'bars', 'foo'], 'expectedResult' => ['bar']]; + yield 'with intersection reversed' => ['tags1' => ['baz', 'bars', 'foo'], 'tags2' => ['foo', 'bar', 'baz'], 'expectedResult' => ['bars']]; + } + + /** + * @test + * @dataProvider differenceDataProvider + */ + public function differenceTests(array $tags1, array $tags2, array $expectedResult): void + { + self::assertSame($expectedResult, SubtreeTags::fromStrings(...$tags1)->difference(SubtreeTags::fromStrings(...$tags2))->toStringArray()); + } + + + public static function equalsDataProvider(): iterable + { + yield 'empty' => ['tags1' => [], 'tags2' => [], 'expectedResult' => true]; + yield 'one empty' => ['tags1' => [], 'tags2' => ['foo'], 'expectedResult' => false]; + yield 'other empty' => ['tags1' => ['foo'], 'tags2' => [], 'expectedResult' => false]; + yield 'equals' => ['tags1' => ['foo', 'bar'], 'tags2' => ['foo', 'bar'], 'expectedResult' => true]; + yield 'equals reversed' => ['tags1' => ['foo', 'bar'], 'tags2' => ['bar', 'foo'], 'expectedResult' => true]; + } + + /** + * @test + * @dataProvider equalsDataProvider + */ + public function equalsTests(array $tags1, array $tags2, bool $expectedResult): void + { + self::assertSame($expectedResult, SubtreeTags::fromStrings(...$tags1)->equals(SubtreeTags::fromStrings(...$tags2))); + } + public static function mergeDataProvider(): iterable { yield 'empty' => ['tags1' => [], 'tags2' => [], 'expectedResult' => []]; From b11a498df2cb70f1b2ba87e381e53cba697c6c8a Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:01:04 +0100 Subject: [PATCH 081/122] BUGFIX: Only compare actual node tags (without inheritance) and use introduced native diff util. Further fixes as `equals` will also be true now if the order is different. --- .../Classes/Controller/WorkspaceController.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 7e80fde65de..487d78f1bf6 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -1130,15 +1130,19 @@ protected function renderContentChanges( $changeNodePropertiesDefaults = $this->getNodeType($changedNode)->getDefaultValuesForProperties(); $renderer = new HtmlArrayRenderer(); - if($originalNode?->tags->toStringArray() != $changedNode?->tags->toStringArray()) { + + $actualOriginalTags = $originalNode?->tags->withoutInherited()->all(); + $actualChangedTags = $changedNode->tags->withoutInherited()->all(); + + if ($actualOriginalTags?->equals($actualChangedTags)) { $contentChanges['tags'] = new ContentChangeItem( properties: new ContentChangeProperties( type: 'tags', propertyLabel: $this->getModuleLabel('workspaces.changedTags'), ), changes: new TagContentChange( - addedTags: array_diff($changedNode->tags->toStringArray(), $originalNode->tags->toStringArray()), - removedTags: array_diff($originalNode->tags->toStringArray(), $changedNode->tags->toStringArray()), + addedTags: $actualChangedTags->difference($actualOriginalTags)->toStringArray(), + removedTags: $actualOriginalTags->difference($actualChangedTags)->toStringArray(), ) ); } From bbd44d050e802e6612fed175d1fbe6956a9006f4 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:01:31 +0100 Subject: [PATCH 082/122] TASK: Basic phpstan adjustments for array types and nullability --- .../Controller/WorkspaceController.php | 19 +++++++++---------- .../Classes/ViewModel/ChangeItem.php | 3 ++- .../ContentChanges/TagContentChange.php | 4 ++++ .../ContentChanges/TextContentChange.php | 1 + .../Classes/ViewModel/DocumentItem.php | 1 + 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 487d78f1bf6..642efb0899c 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -497,7 +497,7 @@ public function editWorkspaceRoleAssignmentsAction(WorkspaceName $workspaceName) foreach ($this->workspaceService->getWorkspaceRoleAssignments($contentRepositoryId, $workspaceName) as $workspaceRoleAssignment) { $subjectLabel = match ($workspaceRoleAssignment->subject->type) { - WorkspaceRoleSubjectType::USER => $this->userService->findUserById(UserId::fromString($workspaceRoleAssignment->subject->value))?->getLabel(), + WorkspaceRoleSubjectType::USER => $this->userService->findUserById(UserId::fromString($workspaceRoleAssignment->subject->value))?->getLabel() ?? $workspaceRoleAssignment->subject->value, WorkspaceRoleSubjectType::GROUP => $workspaceRoleAssignment->subject->value, }; @@ -1057,7 +1057,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos $dimensions[] = $contentRepository->getContentDimensionSource()->getDimension($contentDimension)->getValue($coordinate)->configuration['label']; } $dimensionString = implode('_', $dimensions); - $siteChanges[$siteNodeName]['documents'][$documentPath]['changes'][$dimensionString][$relativePath] = new ChangeItem ( + $siteChanges[$siteNodeName]['documents'][$documentPath]['changes'][$dimensionString][$relativePath] = new ChangeItem( serializedNodeAddress: $nodeAddress->toJson(), hidden: $node->tags->contain(SubtreeTag::disabled()), isRemoved: $change->deleted, @@ -1065,9 +1065,9 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos isMoved: $change->moved, dimensions: $dimensions, lastModificationDateTime: $node->timestamps->lastModified?->format('Y-m-d H:i'), - createdDateTime: $node->timestamps->created?->format('Y-m-d H:i'), + createdDateTime: $node->timestamps->created->format('Y-m-d H:i'), label: $this->nodeLabelGenerator->getLabel($node), - icon: $nodeType->getFullConfiguration()['ui']['icon'], + icon: $nodeType?->getFullConfiguration()['ui']['icon'], contentChanges: $this->renderContentChanges( $node, $change->contentStreamId, @@ -1107,9 +1107,7 @@ protected function getOriginalNode( /** * Renders the difference between the original and the changed content of the given node and returns it, along - * with meta information, in an array. - * - * @return array + * with meta information */ protected function renderContentChanges( Node $changedNode, @@ -1286,7 +1284,8 @@ protected function getPropertyLabel($propertyName, Node $changedNode) } $packageKey = 'Neos.Neos'; $source = 'Main'; - $idParts = explode(':', $properties[$propertyName]['ui']['label'], 3); + $id = $properties[$propertyName]['ui']['label']; + $idParts = explode(':', $id, 3); switch (count($idParts)) { case 2: $packageKey = $idParts[0]; @@ -1455,7 +1454,7 @@ protected function getWorkspaceListItems( $contentRepository->id, $userWorkspace->workspaceName, $this->securityContext->getRoles(), - $this->userService->getCurrentUser()->getId() + $this->userService->getCurrentUser()?->getId() ); $allWorkspaces = $contentRepository->findWorkspaces(); @@ -1484,7 +1483,7 @@ protected function getWorkspaceListItems( $contentRepository->id, $workspace->workspaceName, $this->securityContext->getRoles(), - $this->userService->getCurrentUser()->getId() + $this->userService->getCurrentUser()?->getId() ); // ignore root workspaces, because they will not be shown in the UI diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ChangeItem.php b/Neos.Workspace.Ui/Classes/ViewModel/ChangeItem.php index 54e7aae694a..f644f0ec410 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ChangeItem.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ChangeItem.php @@ -19,6 +19,7 @@ #[Flow\Proxy(false)] final readonly class ChangeItem { + /** @param list $dimensions */ public function __construct( public string $serializedNodeAddress, public bool $hidden, @@ -29,7 +30,7 @@ public function __construct( public ?string $lastModificationDateTime, public ?string $createdDateTime, public string $label, - public string $icon, + public ?string $icon, public ContentChangeItems $contentChanges, ) { } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TagContentChange.php b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TagContentChange.php index 403f7cf9f15..ee028125aef 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TagContentChange.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TagContentChange.php @@ -19,6 +19,10 @@ #[Flow\Proxy(false)] final readonly class TagContentChange extends ContentChange { + /** + * @param list $addedTags + * @param list $removedTags + */ public function __construct( public array $addedTags, public array $removedTags, diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TextContentChange.php b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TextContentChange.php index 982800bd7c0..bfbabf6135c 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TextContentChange.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TextContentChange.php @@ -19,6 +19,7 @@ #[Flow\Proxy(false)] final readonly class TextContentChange extends ContentChange { + /** @param array $diff */ public function __construct( public array $diff, ) { diff --git a/Neos.Workspace.Ui/Classes/ViewModel/DocumentItem.php b/Neos.Workspace.Ui/Classes/ViewModel/DocumentItem.php index 20ba68e4618..63af0f00e8e 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/DocumentItem.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/DocumentItem.php @@ -19,6 +19,7 @@ #[Flow\Proxy(false)] readonly class DocumentItem { + /** @param list $documentBreadCrumb */ public function __construct( public array $documentBreadCrumb, public string $aggregateId, From 116de2af1d507aefdc63ba8cb2a70fed4b9af5ae Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 12 Dec 2024 17:02:03 +0100 Subject: [PATCH 083/122] TASK: Adjust code to pass user to `getWorkspaceListItems` instead --- .../Controller/WorkspaceController.php | 63 ++++++++----------- 1 file changed, 26 insertions(+), 37 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 642efb0899c..b0fb399810c 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -19,9 +19,6 @@ use Neos\ContentRepository\Core\Dimension\ContentDimensionId; use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTag; use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Exception\WorkspaceAlreadyExists; -use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\DeleteWorkspace; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\DiscardIndividualNodesFromWorkspace; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\PublishIndividualNodesFromWorkspace; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Exception\WorkspaceRebaseFailed; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindAncestorNodesFilter; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; @@ -29,7 +26,6 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Node\NodeAddress; -use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; @@ -59,9 +55,9 @@ use Neos\Neos\Domain\Model\WorkspaceDescription; use Neos\Neos\Domain\Model\WorkspaceRole; use Neos\Neos\Domain\Model\WorkspaceRoleAssignment; +use Neos\Neos\Domain\Model\WorkspaceRoleAssignments; use Neos\Neos\Domain\Model\WorkspaceRoleSubject; use Neos\Neos\Domain\Model\WorkspaceRoleSubjectType; -use Neos\Neos\Domain\Model\WorkspaceRoleAssignments; use Neos\Neos\Domain\Model\WorkspaceTitle; use Neos\Neos\Domain\NodeLabel\NodeLabelGeneratorInterface; use Neos\Neos\Domain\Repository\SiteRepository; @@ -73,19 +69,19 @@ use Neos\Neos\FrontendRouting\SiteDetection\SiteDetectionResult; use Neos\Neos\PendingChangesProjection\ChangeFinder; use Neos\Neos\PendingChangesProjection\Changes; -use Neos\Neos\Utility\NodeTypeWithFallbackProvider; use Neos\Neos\Security\Authorization\ContentRepositoryAuthorizationService; -use Neos\Workspace\Ui\ViewModel\ConfirmDeleteWorkspaceRoleAssignmentFormData; -use Neos\Workspace\Ui\ViewModel\CreateWorkspaceRoleAssignmentFormData; +use Neos\Neos\Utility\NodeTypeWithFallbackProvider; use Neos\Workspace\Ui\ViewModel\ChangeItem; +use Neos\Workspace\Ui\ViewModel\ConfirmDeleteWorkspaceRoleAssignmentFormData; use Neos\Workspace\Ui\ViewModel\ContentChangeItem; use Neos\Workspace\Ui\ViewModel\ContentChangeItems; use Neos\Workspace\Ui\ViewModel\ContentChangeProperties; -use Neos\Workspace\Ui\ViewModel\ContentChanges\ImageContentChange; -use Neos\Workspace\Ui\ViewModel\ContentChanges\TextContentChange; use Neos\Workspace\Ui\ViewModel\ContentChanges\AssetContentChange; use Neos\Workspace\Ui\ViewModel\ContentChanges\DateTimeContentChange; +use Neos\Workspace\Ui\ViewModel\ContentChanges\ImageContentChange; use Neos\Workspace\Ui\ViewModel\ContentChanges\TagContentChange; +use Neos\Workspace\Ui\ViewModel\ContentChanges\TextContentChange; +use Neos\Workspace\Ui\ViewModel\CreateWorkspaceRoleAssignmentFormData; use Neos\Workspace\Ui\ViewModel\DocumentChangeItem; use Neos\Workspace\Ui\ViewModel\DocumentItem; use Neos\Workspace\Ui\ViewModel\EditWorkspaceFormData; @@ -171,11 +167,10 @@ public function indexAction(): void $this->view->assign('displayContentRepositorySelector', $numberOfContentRepositories > 1); $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); - $userWorkspace = $this->getUserWorkspace($contentRepository); - $workspaceListItems = $this->getWorkspaceListItems($userWorkspace, $contentRepository); + $workspaceListItems = $this->getWorkspaceListItems($contentRepository, $currentUser); $this->view->assignMultiple([ - 'userWorkspaceName' => $userWorkspace->workspaceName->value, + 'userWorkspaceName' => $this->workspaceService->getPersonalWorkspaceForUser($contentRepositoryId, $currentUser->getId())->workspaceName, 'workspaceListItems' => $workspaceListItems, 'flashMessages' => $this->controllerContext->getFlashMessageContainer()->getMessagesAndFlush(), ]); @@ -334,6 +329,11 @@ public function updateAction( WorkspaceDescription $description, WorkspaceName $baseWorkspace, ): void { + $currentUser = $this->userService->getCurrentUser(); + if ($currentUser === null) { + throw new \RuntimeException('No user is authenticated', 1729505338); + } + $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); @@ -383,11 +383,10 @@ public function updateAction( ) ); - $userWorkspace = $this->getUserWorkspace($contentRepository); - $workspaceListItems = $this->getWorkspaceListItems($userWorkspace, $contentRepository); + $workspaceListItems = $this->getWorkspaceListItems($contentRepository, $currentUser); $this->view->assignMultiple([ - 'userWorkspaceName' => $userWorkspace->workspaceName->value, + 'userWorkspaceName' => $this->workspaceService->getPersonalWorkspaceForUser($contentRepositoryId, $currentUser->getId())->workspaceName, 'workspaceListItems' => $workspaceListItems, ]); } @@ -1344,7 +1343,7 @@ protected function postProcessDiffArray(array &$diffArray): void * * @param ContentRepository $contentRepository * @param WorkspaceName|null $excludedWorkspace - * @return array + * @return array */ protected function prepareBaseWorkspaceOptions( ContentRepository $contentRepository, @@ -1436,33 +1435,23 @@ public function getModuleLabel(string $id, array $arguments = [], mixed $quantit ) ?: $id; } - protected function getUserWorkspace(ContentRepository $contentRepository): Workspace - { - $currentUser = $this->userService->getCurrentUser(); - if ($currentUser === null) { - throw new \RuntimeException('No user is authenticated', 1729505338); - } - return $this->workspaceService->getPersonalWorkspaceForUser($contentRepository->id, $currentUser->getId()); - } - protected function getWorkspaceListItems( - Workspace $userWorkspace, - ContentRepository $contentRepository + ContentRepository $contentRepository, + User $userWorkspaceOwner, ): WorkspaceListItems { + $workspaceListItems = []; + $allWorkspaces = $contentRepository->findWorkspaces(); + // todo this throws "No workspace is assigned to the user with id" for the case user logs first into workspace module before workspace exists!!! + $userWorkspace = $this->workspaceService->getPersonalWorkspaceForUser($contentRepository->id, $userWorkspaceOwner->getId()); $userWorkspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepository->id, $userWorkspace->workspaceName); $userWorkspacesPermissions = $this->authorizationService->getWorkspacePermissions( $contentRepository->id, $userWorkspace->workspaceName, $this->securityContext->getRoles(), - $this->userService->getCurrentUser()?->getId() + $userWorkspaceOwner->getId() ); - $allWorkspaces = $contentRepository->findWorkspaces(); - - $userWorkspaceOwner = $this->userService->findUserById($userWorkspaceMetadata->ownerUserId); - // add user workspace first - $workspaceListItems = []; $workspaceListItems[$userWorkspace->workspaceName->value] = new WorkspaceListItem( $userWorkspace->workspaceName->value, $userWorkspaceMetadata->classification->value, @@ -1472,7 +1461,7 @@ protected function getWorkspaceListItems( $userWorkspace->baseWorkspaceName?->value, $this->computePendingChanges($userWorkspace, $contentRepository), !$allWorkspaces->getDependantWorkspaces($userWorkspace->workspaceName)->isEmpty(), - $userWorkspaceOwner?->getLabel(), + $userWorkspaceOwner->getLabel(), $userWorkspacesPermissions, ); @@ -1487,7 +1476,7 @@ protected function getWorkspaceListItems( ); // ignore root workspaces, because they will not be shown in the UI - if ($workspaceMetadata->classification === WorkspaceClassification::ROOT) { + if ($workspace->isRootWorkspace()) { continue; } @@ -1507,7 +1496,7 @@ protected function getWorkspaceListItems( $workspace->status->value, $workspaceMetadata->title->value, $workspaceMetadata->description->value, - $workspace->baseWorkspaceName?->value, + $workspace->baseWorkspaceName->value, $this->computePendingChanges($workspace, $contentRepository), !$allWorkspaces->getDependantWorkspaces($workspace->workspaceName)->isEmpty(), $workspaceOwner?->getLabel(), From 64119e2a7f9d094549135dcb69e2f71952ea76ab Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 12 Dec 2024 17:24:45 +0100 Subject: [PATCH 084/122] TASK: Further phpstan adjustments (and use hash to index dimensions) --- .../Controller/WorkspaceController.php | 46 ++++++++++++------- .../Classes/ViewModel/DocumentItem.php | 2 +- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index b0fb399810c..5a99b6a60d8 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -170,7 +170,8 @@ public function indexAction(): void $workspaceListItems = $this->getWorkspaceListItems($contentRepository, $currentUser); $this->view->assignMultiple([ - 'userWorkspaceName' => $this->workspaceService->getPersonalWorkspaceForUser($contentRepositoryId, $currentUser->getId())->workspaceName, + // todo remove userWorkspaceName field and add distinction to $workspaceListItems as $workspaceListItems->userWorkspace and $workspaceListItems->otherWorkspaces or something. + 'userWorkspaceName' => $this->workspaceService->getPersonalWorkspaceForUser($contentRepositoryId, $currentUser->getId())->workspaceName->value, 'workspaceListItems' => $workspaceListItems, 'flashMessages' => $this->controllerContext->getFlashMessageContainer()->getMessagesAndFlush(), ]); @@ -300,6 +301,10 @@ public function editAction(WorkspaceName $workspaceName): void $this->throwStatus(404, 'Workspace does not exist'); } + if ($workspace->isRootWorkspace()) { + throw new \RuntimeException(sprintf('Workspace %s does not have a base-workspace.', $workspace->workspaceName->value), 1734019485); + } + $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspace->workspaceName); $editWorkspaceDto = new EditWorkspaceFormData( @@ -386,7 +391,7 @@ public function updateAction( $workspaceListItems = $this->getWorkspaceListItems($contentRepository, $currentUser); $this->view->assignMultiple([ - 'userWorkspaceName' => $this->workspaceService->getPersonalWorkspaceForUser($contentRepositoryId, $currentUser->getId())->workspaceName, + 'userWorkspaceName' => $this->workspaceService->getPersonalWorkspaceForUser($contentRepositoryId, $currentUser->getId())->workspaceName->value, 'workspaceListItems' => $workspaceListItems, ]); } @@ -463,7 +468,7 @@ public function deleteAction(WorkspaceName $workspaceName): void ); $this->addFlashMessage($message, '', Message::SEVERITY_WARNING); $this->throwStatus(403, 'Workspace has unpublished nodes'); - // delete workspace on POST + // delete workspace on POST -> todo make this more FLOW-ig by possibly having a DeleteController with post() and get() _or_ by having deleteAction_post and deleteAction_get?? Or a separate action? } elseif ($this->request->getHttpRequest()->getMethod() === 'POST') { $this->workspaceService->deleteWorkspace($contentRepositoryId, $workspaceName); @@ -860,7 +865,7 @@ public function confirmPublishSelectedChangesAction(WorkspaceName $workspaceName ); $this->throwStatus(404, 'Workspace does not exist'); } - $baseWorkspace = $this->getBaseWorkspaceWhenSureItExists($workspace, $contentRepository); + $baseWorkspace = $this->requireBaseWorkspace($workspace, $contentRepository); $baseWorkspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $baseWorkspace->workspaceName); $this->view->assignMultiple([ @@ -939,6 +944,10 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos $siteChanges = []; $changes = $this->getChangesFromWorkspace($selectedWorkspace, $contentRepository); foreach ($changes as $change) { + if ($change->originDimensionSpacePoint === null) { + // todo implement support for change node type!!! Because originDimensionSpacePoint is null currently for that case. + continue; + } $workspaceName = $selectedWorkspace->workspaceName; if ($change->deleted) { // If we deleted a node, there is no way for us to anymore find the deleted node in the ContentStream @@ -946,7 +955,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos // Thus, to figure out the rootline for display, we check the *base workspace* Content Stream. // // This is safe because the UI basically shows what would be removed once the deletion is published. - $baseWorkspace = $this->getBaseWorkspaceWhenSureItExists($selectedWorkspace, $contentRepository); + $baseWorkspace = $this->requireBaseWorkspace($selectedWorkspace, $contentRepository); $workspaceName = $baseWorkspace->workspaceName; } $subgraph = $contentRepository->getContentGraph($workspaceName)->getSubgraph( @@ -1027,7 +1036,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos documentBreadCrumb: array_reverse($documentPathSegmentsNames), aggregateId: $documentNodeAddress->aggregateId->value, documentNodeAddress: $documentNodeAddress->toJson(), - documentIcon: $documentType->getFullConfiguration()['ui']['icon'] + documentIcon: $documentType?->getFullConfiguration()['ui']['icon'] ?? null ); } @@ -1053,10 +1062,12 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos $dimensions = []; foreach ($node->dimensionSpacePoint->coordinates as $id => $coordinate) { $contentDimension = new ContentDimensionId($id); - $dimensions[] = $contentRepository->getContentDimensionSource()->getDimension($contentDimension)->getValue($coordinate)->configuration['label']; + $dimensions[] = $contentRepository->getContentDimensionSource() + ->getDimension($contentDimension) + ?->getValue($coordinate) + ?->configuration['label'] ?? $coordinate; } - $dimensionString = implode('_', $dimensions); - $siteChanges[$siteNodeName]['documents'][$documentPath]['changes'][$dimensionString][$relativePath] = new ChangeItem( + $siteChanges[$siteNodeName]['documents'][$documentPath]['changes'][$node->dimensionSpacePoint->hash][$relativePath] = new ChangeItem( serializedNodeAddress: $nodeAddress->toJson(), hidden: $node->tags->contain(SubtreeTag::disabled()), isRemoved: $change->deleted, @@ -1118,7 +1129,7 @@ protected function renderContentChanges( ); $originalNode = null; if ($currentWorkspace !== null) { - $baseWorkspace = $this->getBaseWorkspaceWhenSureItExists($currentWorkspace, $contentRepository); + $baseWorkspace = $this->requireBaseWorkspace($currentWorkspace, $contentRepository); $originalNode = $this->getOriginalNode($changedNode, $baseWorkspace->workspaceName, $contentRepository); } @@ -1393,6 +1404,7 @@ protected function prepareBaseWorkspaceOptions( } /** + * Todo remove? * Creates an array of user names and their respective labels which are possible owners for a workspace. * * @return array @@ -1408,15 +1420,17 @@ protected function prepareOwnerOptions(): array return $ownerOptions; } - private function getBaseWorkspaceWhenSureItExists( + private function requireBaseWorkspace( Workspace $workspace, ContentRepository $contentRepository, ): Workspace { - /** @var WorkspaceName $baseWorkspaceName We expect this to exist */ - $baseWorkspaceName = $workspace->baseWorkspaceName; - /** @var Workspace $baseWorkspace We expect this to exist */ - $baseWorkspace = $contentRepository->findWorkspaceByName($baseWorkspaceName); - + if ($workspace->isRootWorkspace()) { + throw new \RuntimeException(sprintf('Workspace %s does not have a base-workspace.', $workspace->workspaceName->value), 1734019485); + } + $baseWorkspace = $contentRepository->findWorkspaceByName($workspace->baseWorkspaceName); + if ($baseWorkspace === null) { + throw new \RuntimeException(sprintf('Base-workspace %s of %s does not exist.', $workspace->baseWorkspaceName->value, $workspace->workspaceName->value), 1734019720); + } return $baseWorkspace; } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/DocumentItem.php b/Neos.Workspace.Ui/Classes/ViewModel/DocumentItem.php index 63af0f00e8e..ef1983c0a69 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/DocumentItem.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/DocumentItem.php @@ -24,7 +24,7 @@ public function __construct( public array $documentBreadCrumb, public string $aggregateId, public string $documentNodeAddress, - public string $documentIcon + public ?string $documentIcon ) { } } From 4aed44146bf8de69d6b0809c77b6e110d2409712 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Tue, 17 Dec 2024 10:03:21 +0100 Subject: [PATCH 085/122] TASK: Revert WIP workspace role assigment Ui (sorry robert :D - well work on this in a another pr - by reverting this change ^^) reverts all actions: - editWorkspaceRoleAssignmentsAction - createWorkspaceRoleAssignmentAction - addWorkspaceRoleAssignmentAction - confirmDeleteWorkspaceRoleAssignmentAction - deleteWorkspaceRoleAssignmentAction --- .../Controller/WorkspaceController.php | 188 +----------------- ...mDeleteWorkspaceRoleAssignmentFormData.php | 31 --- .../CreateWorkspaceRoleAssignmentFormData.php | 59 ------ .../EditWorkspaceRoleAssignmentsFormData.php | 41 ---- .../ViewModel/RoleAssignmentListItem.php | 39 ---- ...nfirmDeleteWorkspaceRoleAssignments.fusion | 13 -- .../CreateWorkspaceRoleAssignment.fusion | 15 -- .../Features/Workspace/Actions/Edit.fusion | 6 - .../EditWorkspaceRoleAssignments.fusion | 19 -- .../WorkspaceRoleAssignmentTableRow.fusion | 51 ----- .../Components/WorkspaceTableRow.fusion | 17 -- ...onfirmDeleteWorkspaceRoleAssignment.fusion | 56 ------ .../CreateWorkspaceRoleAssignment.fusion | 122 ------------ .../EditWorkspaceRoleAssignments.fusion | 76 ------- .../Private/Translations/en/Main.xlf | 30 --- 15 files changed, 1 insertion(+), 762 deletions(-) delete mode 100644 Neos.Workspace.Ui/Classes/ViewModel/ConfirmDeleteWorkspaceRoleAssignmentFormData.php delete mode 100644 Neos.Workspace.Ui/Classes/ViewModel/CreateWorkspaceRoleAssignmentFormData.php delete mode 100644 Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceRoleAssignmentsFormData.php delete mode 100644 Neos.Workspace.Ui/Classes/ViewModel/RoleAssignmentListItem.php delete mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/ConfirmDeleteWorkspaceRoleAssignments.fusion delete mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/CreateWorkspaceRoleAssignment.fusion delete mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/EditWorkspaceRoleAssignments.fusion delete mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceRoleAssignmentTableRow.fusion delete mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/ConfirmDeleteWorkspaceRoleAssignment.fusion delete mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/CreateWorkspaceRoleAssignment.fusion delete mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/EditWorkspaceRoleAssignments.fusion diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 5a99b6a60d8..e588ba20600 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -50,14 +50,9 @@ use Neos\Media\Domain\Model\ImageInterface; use Neos\Neos\Controller\Module\AbstractModuleController; use Neos\Neos\Domain\Model\User; -use Neos\Neos\Domain\Model\UserId; use Neos\Neos\Domain\Model\WorkspaceClassification; use Neos\Neos\Domain\Model\WorkspaceDescription; -use Neos\Neos\Domain\Model\WorkspaceRole; -use Neos\Neos\Domain\Model\WorkspaceRoleAssignment; use Neos\Neos\Domain\Model\WorkspaceRoleAssignments; -use Neos\Neos\Domain\Model\WorkspaceRoleSubject; -use Neos\Neos\Domain\Model\WorkspaceRoleSubjectType; use Neos\Neos\Domain\Model\WorkspaceTitle; use Neos\Neos\Domain\NodeLabel\NodeLabelGeneratorInterface; use Neos\Neos\Domain\Repository\SiteRepository; @@ -72,7 +67,6 @@ use Neos\Neos\Security\Authorization\ContentRepositoryAuthorizationService; use Neos\Neos\Utility\NodeTypeWithFallbackProvider; use Neos\Workspace\Ui\ViewModel\ChangeItem; -use Neos\Workspace\Ui\ViewModel\ConfirmDeleteWorkspaceRoleAssignmentFormData; use Neos\Workspace\Ui\ViewModel\ContentChangeItem; use Neos\Workspace\Ui\ViewModel\ContentChangeItems; use Neos\Workspace\Ui\ViewModel\ContentChangeProperties; @@ -81,13 +75,10 @@ use Neos\Workspace\Ui\ViewModel\ContentChanges\ImageContentChange; use Neos\Workspace\Ui\ViewModel\ContentChanges\TagContentChange; use Neos\Workspace\Ui\ViewModel\ContentChanges\TextContentChange; -use Neos\Workspace\Ui\ViewModel\CreateWorkspaceRoleAssignmentFormData; use Neos\Workspace\Ui\ViewModel\DocumentChangeItem; use Neos\Workspace\Ui\ViewModel\DocumentItem; use Neos\Workspace\Ui\ViewModel\EditWorkspaceFormData; -use Neos\Workspace\Ui\ViewModel\EditWorkspaceRoleAssignmentsFormData; use Neos\Workspace\Ui\ViewModel\PendingChanges; -use Neos\Workspace\Ui\ViewModel\RoleAssignmentListItem; use Neos\Workspace\Ui\ViewModel\WorkspaceListItem; use Neos\Workspace\Ui\ViewModel\WorkspaceListItems; @@ -485,156 +476,6 @@ public function deleteAction(WorkspaceName $workspaceName): void } } - - public function editWorkspaceRoleAssignmentsAction(WorkspaceName $workspaceName): void - { - $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspaceName); - - // TODO: Render a form to edit role assignments - // TODO can current user see/edit role assignments? - $roleAssignmentsVisible = true; - $roleAssignmentsEditable = true; - - /** @var array $workspaceRoleAssignments */ - $workspaceRoleAssignments = []; - - foreach ($this->workspaceService->getWorkspaceRoleAssignments($contentRepositoryId, $workspaceName) as $workspaceRoleAssignment) { - $subjectLabel = match ($workspaceRoleAssignment->subject->type) { - WorkspaceRoleSubjectType::USER => $this->userService->findUserById(UserId::fromString($workspaceRoleAssignment->subject->value))?->getLabel() ?? $workspaceRoleAssignment->subject->value, - WorkspaceRoleSubjectType::GROUP => $workspaceRoleAssignment->subject->value, - }; - - $roleLabel = $workspaceRoleAssignment->role->value; - - $workspaceRoleAssignments[] = new RoleAssignmentListItem( - subjectValue: $workspaceRoleAssignment->subject->value, - subjectLabel: $subjectLabel, - subjectTypeValue: $workspaceRoleAssignment->subject->type->value, - roleLabel: $roleLabel, - subjectType: $workspaceRoleAssignment->subject->type->value, - ); - } - - - - $editWorkspaceRoleAssignmentsFormData = new EditWorkspaceRoleAssignmentsFormData( - workspaceName: $workspaceName, - workspaceTitle: $workspaceMetadata->title, - roleAssignmentsEditable: $roleAssignmentsEditable, - roleAssignments: $workspaceRoleAssignments, - ); - - $this->view->assign('editWorkspaceRoleAssignmentsFormData', $editWorkspaceRoleAssignmentsFormData); - } - - public function createWorkspaceRoleAssignmentAction(WorkspaceName $workspaceName): void - { - $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspaceName); - - $userOptions = []; - foreach ($this->userService->getUsers()->toArray() as $user) { - $userOptions[$user->getId()->value] = $user->getLabel(); - } - - $rolesInSystem = $this->policyService->getRoles(); - $groupOptions = []; - foreach ($rolesInSystem as $role) { - $groupOptions[$role->getIdentifier()] = $role->getLabel(); - } - - $workspaceRoleSubjectTypes = WorkspaceRoleSubjectType::cases(); - /** @var array $subjectTypeOptions where key is the Id and value is the translated label of the SubjectType */ - $subjectTypeOptions = []; - foreach ($workspaceRoleSubjectTypes as $workspaceRoleSubjectType) { - $subjectTypeOptions[$workspaceRoleSubjectType->value] = $this->getModuleLabel("workspaces.workspace.workspaceRoleAssignment.subjectType.label.$workspaceRoleSubjectType->value"); - } - - $workspaceRoles = WorkspaceRole::cases(); - /** @var array $roleOptions where key is the Id and value is the translated label of the Role */ - $roleOptions = []; - foreach ($workspaceRoles as $workspaceRole) { - $roleOptions[$workspaceRole->value] = $this->getModuleLabel("workspaces.workspace.workspaceRoleAssignment.role.label.$workspaceRole->value"); - } - - $this->view->assign('createWorkspaceRoleAssignmentFormData', new CreateWorkspaceRoleAssignmentFormData( - workspaceName: $workspaceName, - workspaceTitle: $workspaceMetadata->title, - userOptions: $userOptions, - groupOptions: $groupOptions, - subjectTypeOptions: $subjectTypeOptions, - roleOptions: $roleOptions, - )); - } - - public function addWorkspaceRoleAssignmentAction( - WorkspaceName $workspaceName, - string $subjectValue, - string $subjectTypeValue, - string $roleValue, - ): void - { - // TODO: Validate if user can add role assignment to workspace - - $subjectType = WorkspaceRoleSubjectType::from($subjectTypeValue); - $subject = WorkspaceRoleSubject::create($subjectType, $subjectValue); - $role = WorkspaceRole::from($roleValue); - - if ($subjectType === WorkspaceRoleSubjectType::USER) { - $this->addUserRoleAssignment($workspaceName, $subject, $role); - } elseif ($subjectType === WorkspaceRoleSubjectType::GROUP) { - $this->addGroupRoleAssignment($workspaceName, $subject, $role); - } else { - $this->addFlashMessage( - $this->getModuleLabel('workspaces.roleAssignmentCouldNotBeAdded'), - '', - Message::SEVERITY_ERROR - ); - $this->throwStatus(400, 'Invalid subject type'); - } - } - - public function confirmDeleteWorkspaceRoleAssignmentAction(WorkspaceName $workspaceName, string $subjectValue, string $subjectType): void - { - $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspaceName); - - $confirmDeleteWorkspaceRoleAssignmentFormData = new ConfirmDeleteWorkspaceRoleAssignmentFormData( - workspaceName: $workspaceName, - workspaceTitle: $workspaceMetadata->title, - subjectValue: $subjectValue, - subjectType: $subjectType, - ); - - $this->view->assign('confirmDeleteWorkspaceRoleAssignmentFormData', $confirmDeleteWorkspaceRoleAssignmentFormData); - } - - public function deleteWorkspaceRoleAssignmentAction(WorkspaceName $workspaceName, string $subjectValue, string $subjectType): void - { - $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - try { - $this->workspaceService->unassignWorkspaceRole( - $contentRepositoryId, - $workspaceName, - WorkspaceRoleSubject::create( - WorkspaceRoleSubjectType::from($subjectType), - $subjectValue - ) - ); - } catch (\Exception $e) { - // TODO: error handling - $this->addFlashMessage( - $this->getModuleLabel('workspaces.roleAssignmentCouldNotBeDeleted'), - '', - Message::SEVERITY_ERROR - ); - $this->throwStatus(500, 'Role assignment could not be deleted'); - } - - $this->redirect('editWorkspaceRoleAssignments', null, null, ['workspaceName' => $workspaceName->value]); - } - /** * Rebase the current users personal workspace onto the given $targetWorkspace and then * redirects to the $targetNode in the content module. @@ -1519,38 +1360,11 @@ protected function getWorkspaceListItems( } return WorkspaceListItems::fromArray($workspaceListItems); } + protected function getChangesFromWorkspace(Workspace $selectedWorkspace,ContentRepository $contentRepository ): Changes{ return $contentRepository->projectionState(ChangeFinder::class) ->findByContentStreamId( $selectedWorkspace->currentContentStreamId ); } - - private function addUserRoleAssignment(WorkspaceName $workspaceName, WorkspaceRoleSubject $subject, WorkspaceRole $role): void - { - if ($this->userService->findUserById(UserId::fromString($subject->value)) === null) { - $this->addFlashMessage( - $this->getModuleLabel('workspaces.roleAssignmentCouldNotBeAdded'), - '', - Message::SEVERITY_ERROR - ); - $this->throwStatus(400, 'Invalid user'); - } - - $this->workspaceService->assignWorkspaceRole( - SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId, - $workspaceName, - WorkspaceRoleAssignment::createForUser(UserId::fromString($subject->value), $role) - ); - } - - private function addGroupRoleAssignment(WorkspaceName $workspaceName, WorkspaceRoleSubject $subject, WorkspaceRole $role): void - { - // TODO check if group exists? - $this->workspaceService->assignWorkspaceRole( - SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId, - $workspaceName, - WorkspaceRoleAssignment::createForGroup($subject->value, $role) - ); - } } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ConfirmDeleteWorkspaceRoleAssignmentFormData.php b/Neos.Workspace.Ui/Classes/ViewModel/ConfirmDeleteWorkspaceRoleAssignmentFormData.php deleted file mode 100644 index 96dd037be88..00000000000 --- a/Neos.Workspace.Ui/Classes/ViewModel/ConfirmDeleteWorkspaceRoleAssignmentFormData.php +++ /dev/null @@ -1,31 +0,0 @@ - - */ - public array $userOptions, - /** - * Options for the workspaceManager selector where the value is the group. - * @var array - */ - public array $groupOptions, - /** - * TODO: translate subject type labels? - * Options for the workspaceManager selector where the key is the subject type and the value is the subject label. - * @var array - */ - public array $subjectTypeOptions, - /** - * TODO: translate role labels? - * Options for the workspaceManager selector where the key is the role identifier and the value is the role label. - * @var array - */ - public array $roleOptions - ) - { - } -} diff --git a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceRoleAssignmentsFormData.php b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceRoleAssignmentsFormData.php deleted file mode 100644 index 08fab42cdad..00000000000 --- a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceRoleAssignmentsFormData.php +++ /dev/null @@ -1,41 +0,0 @@ - $roleAssignments - */ - public function __construct( - public WorkspaceName $workspaceName, - public WorkspaceTitle $workspaceTitle, - public bool $roleAssignmentsEditable, - public array $roleAssignments, - ) { - } -} diff --git a/Neos.Workspace.Ui/Classes/ViewModel/RoleAssignmentListItem.php b/Neos.Workspace.Ui/Classes/ViewModel/RoleAssignmentListItem.php deleted file mode 100644 index 3d827a94e9e..00000000000 --- a/Neos.Workspace.Ui/Classes/ViewModel/RoleAssignmentListItem.php +++ /dev/null @@ -1,39 +0,0 @@ - - ` -} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/CreateWorkspaceRoleAssignment.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/CreateWorkspaceRoleAssignment.fusion deleted file mode 100644 index 8bac8dde26b..00000000000 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/CreateWorkspaceRoleAssignment.fusion +++ /dev/null @@ -1,15 +0,0 @@ -Neos.Workspace.Ui.WorkspaceController.createWorkspaceRoleAssignment = Neos.Fusion:Component { - createWorkspaceRoleAssignmentFormData = ${createWorkspaceRoleAssignmentFormData} - - renderer = afx` - - ` -} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Edit.fusion index 48bc6012bc0..683392bd0b1 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Edit.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Edit.fusion @@ -11,12 +11,6 @@ Neos.Workspace.Ui.WorkspaceController.edit = Neos.Fusion:Component { workspaceHasChanges={props.editWorkspaceFormData.workspaceHasChanges} baseWorkspaceOptions={props.editWorkspaceFormData.baseWorkspaceOptions} - - roleAssignmentsVisible={props.editWorkspaceFormData.roleAssignmentsVisible} - roleAssignmentsEditable={props.editWorkspaceFormData.roleAssignmentsEditable} - roleAssignments={props.editWorkspaceFormData.roleAssignments} - roleAssignmentUserOptions={props.editWorkspaceFormData.roleAssignmentUserOptions} - roleAssignmentGroupOptions={props.editWorkspaceFormData.roleAssignmentGroupOptions} /> ` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/EditWorkspaceRoleAssignments.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/EditWorkspaceRoleAssignments.fusion deleted file mode 100644 index 4c42008960b..00000000000 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/EditWorkspaceRoleAssignments.fusion +++ /dev/null @@ -1,19 +0,0 @@ -Neos.Workspace.Ui.WorkspaceController.editWorkspaceRoleAssignments = Neos.Fusion:Component { - /// \Neos\Workspace\Ui\ViewModel\EditWorkspaceRoleAssignmentsFormData - editWorkspaceRoleAssignmentsFormData = ${editWorkspaceRoleAssignmentsFormData} - - renderer = afx` - - ` -} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceRoleAssignmentTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceRoleAssignmentTableRow.fusion deleted file mode 100644 index 20c9db19bce..00000000000 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceRoleAssignmentTableRow.fusion +++ /dev/null @@ -1,51 +0,0 @@ -## -# Renders a single WorkspaceRoleAssignment list item -# -prototype(Neos.Workspace.Ui:Component.WorkspaceRoleAssignmentTableRow) < prototype(Neos.Fusion:Component) { - /// string - workspaceName = null - /// bool - roleAssignmentsEditable = false - /// Neos\Workspace\Ui\ViewModel\RoleAssignmentListItem - roleAssignmentFormData = null - - @private { - i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} - confirmDeleteWorkspaceRoleAssignmentPopoverId = 'confirm-delete-workspace-role-assignment-popover' - confirmDeleteWorkspaceRoleAssignmentUri = Neos.Fusion:ActionUri { - action = 'confirmDeleteWorkspaceRoleAssignment' - format = 'htmx' - arguments { - workspaceName = ${props.workspaceName} - subjectValue = ${props.roleAssignmentFormData.subjectValue} - subjectType = ${props.roleAssignmentFormData.subjectType} - } - } - } - - renderer = afx` - - - - - {props.roleAssignmentFormData.subjectLabel} - {props.roleAssignmentFormData.roleLabel} - - - - - ` -} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion index 994d7c6d059..991193f7718 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion @@ -67,13 +67,6 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion workspaceName = ${props.workspaceListItem.name} } } - editWorkspaceRoleAssignmentsUri = Neos.Fusion:ActionUri { - action = 'editWorkspaceRoleAssignments' - format = 'htmx' - arguments { - workspaceName = ${props.workspaceListItem.name} - } - } deleteWorkspaceUri = Neos.Fusion:ActionUri { action = 'delete' format = 'htmx' @@ -83,7 +76,6 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion } deleteWorkspacePopoverId = 'workspace-delete-modal' editWorkspacePopoverId = 'workspace-edit-modal' - editWorkspaceRoleAssignmentsPopoverId = 'workspace-edit-role-assignments-modal' } renderer = afx` @@ -145,15 +137,6 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion attributes.hx-on--after-request={'document.getElementById("' + private.editWorkspacePopoverId + '").showPopover()'} attributes.disabled={props.workspaceListItem.permissions.manage == false} /> - -
- -
- {private.i18n.id('workspaces.createWorkspaceRoleAssignment').arguments([props.workspaceTitle])} -
-
-
- - -
- - ` -} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/CreateWorkspaceRoleAssignment.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/CreateWorkspaceRoleAssignment.fusion deleted file mode 100644 index 40457d95bc0..00000000000 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/CreateWorkspaceRoleAssignment.fusion +++ /dev/null @@ -1,122 +0,0 @@ -prototype(Neos.Workspace.Ui:Component.Modal.CreateWorkspaceRoleAssignment) < prototype(Neos.Fusion:Component) { - workspaceName='' - workspaceTitle='' - - userOptions=${[]} - groupOptions=${[]} - subjectTypeOptions=${[]} - roleOptions=${[]} - - @private { - i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} - popoverId = 'create-workspace-role-assignment-popover' - - // Default SubjectType is USER. - defaultSubjectType = 'USER' - } - - prototype(Neos.Fusion.Form:LabelRenderer) { - translationPackage = 'Neos.Workspace.Ui' - translationSource = 'Main' - } - - prototype(Neos.Fusion.Form:Neos.BackendModule.FieldContainer) { - translation.label { - package = 'Neos.Workspace.Ui' - source = 'Main' - } - } - - // TODO: hide/show user/group select based on subjectType - - renderer = afx` -
-
- -
- {private.i18n.id('workspaces.createWorkspaceRoleAssignment').arguments([props.workspaceTitle])} -
-
-
- -
- - - - - {subjectTypeTitle} - - - - - - - - - - - {userName} - - - - - - - - - - {roleName} - - - - -
-
- -
-
-
-
- ` -} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/EditWorkspaceRoleAssignments.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/EditWorkspaceRoleAssignments.fusion deleted file mode 100644 index 01623f706d8..00000000000 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/EditWorkspaceRoleAssignments.fusion +++ /dev/null @@ -1,76 +0,0 @@ -prototype(Neos.Workspace.Ui:Component.Modal.EditWorkspaceRoleAssignments) < prototype(Neos.Fusion:Component) { - workspaceName='' - workspaceTitle='' - roleAssignmentsEditable=false - - /// array - roleAssignments=${[]} - - @private { - i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} - popoverId = 'workspace-edit-role-assignments-modal' - workspaceTableRowId = ${'workspace-row-' + props.workspaceName} - - confirmDeleteWorkspaceRoleAssignmentPopoverId = 'confirm-delete-workspace-role-assignment-popover' - createWorkspaceRoleAssignmentPopoverId = 'create-workspace-role-assignment-popover' - createWorkspaceRoleAssignmentUri = Neos.Fusion:ActionUri { - action = 'createWorkspaceRoleAssignment' - format = 'htmx' - arguments { - workspaceName = ${props.workspaceName} - } - } - } - - renderer = afx` -
-
- -
- {private.i18n.id('workspaces.editWorkspaceRoleAssignments').arguments([props.workspaceTitle])} -
-
-
- - - - - - - - - - - - - - -
{private.i18n.id('workspaces.workspace.workspaceRoleAssignment.subjectType')}{private.i18n.id('workspaces.workspace.workspaceRoleAssignment.subject')}{private.i18n.id('workspaces.workspace.workspaceRoleAssignment.role')} - {private.i18n.id('workspaces.workspace.workspaceRoleAssignment.actions')} -
- -
-
-
-
- ` -} diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf index 0b22c16873a..d114888b4d5 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf @@ -51,12 +51,6 @@ Edit workspace "{0}" - - Edit workspace permissions of "{0}" - - - Create Role Assignment for workspace "{0}" - Personal workspace @@ -99,30 +93,6 @@ Workspace "{0}" does not exist - - Type - - - Group - - - User - - - Name - - - Role - - - Manager - - - Collaborator - - - Actions - {0} node was added From 4ba3a39b4e0216e398735b6b184d0173660d7d6b Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Wed, 4 Dec 2024 12:02:56 +0100 Subject: [PATCH 086/122] TASK: Reformat workspace module js --- .../Resources/Public/Scripts/Module.js | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Public/Scripts/Module.js b/Neos.Workspace.Ui/Resources/Public/Scripts/Module.js index a5cb80d8bce..c7723c51846 100644 --- a/Neos.Workspace.Ui/Resources/Public/Scripts/Module.js +++ b/Neos.Workspace.Ui/Resources/Public/Scripts/Module.js @@ -18,41 +18,41 @@ document.addEventListener('DOMContentLoaded', () => { - if (!window.htmx) { - console.error('htmx is not loaded'); - return; - } - - /** - * Show flash messages after successful requests - */ - htmx.on('htmx:afterRequest', /** @param {HtmxEvent} e */(e) => { - const flashMessagesJson = e.detail.xhr.getResponseHeader('X-Flow-FlashMessages'); - if (!flashMessagesJson) { - return; - } - - /** @type Notification[] */ - const flashMessages = JSON.parse(flashMessagesJson); - flashMessages.forEach(({ severity, title, message }) => { - if (title) { - NeosCMS.Notification[severity.toLowerCase()](title, message); - } else { - NeosCMS.Notification[severity.toLowerCase()](message); - } - }); - }); - - /** - * Show error notifications for failed requests if no flash messages are present - */ - htmx.on('htmx:responseError', /** @param {HtmxEvent} e */(e) => { - const flashMessagesJson = e.detail.xhr.getResponseHeader('X-Flow-FlashMessages'); - if (flashMessagesJson) { - return; - } - - const { status, statusText } = e.detail.xhr; - NeosCMS.Notification.error(`Error ${status}: ${statusText}`); - }); + if (!window.htmx) { + console.error('htmx is not loaded'); + return; + } + + /** + * Show flash messages after successful requests + */ + htmx.on('htmx:afterRequest', /** @param {HtmxEvent} e */(e) => { + const flashMessagesJson = e.detail.xhr.getResponseHeader('X-Flow-FlashMessages'); + if (!flashMessagesJson) { + return; + } + + /** @type Notification[] */ + const flashMessages = JSON.parse(flashMessagesJson); + flashMessages.forEach(({severity, title, message}) => { + if (title) { + NeosCMS.Notification[severity.toLowerCase()](title, message); + } else { + NeosCMS.Notification[severity.toLowerCase()](message); + } + }); + }); + + /** + * Show error notifications for failed requests if no flash messages are present + */ + htmx.on('htmx:responseError', /** @param {HtmxEvent} e */(e) => { + const flashMessagesJson = e.detail.xhr.getResponseHeader('X-Flow-FlashMessages'); + if (flashMessagesJson) { + return; + } + + const {status, statusText} = e.detail.xhr; + NeosCMS.Notification.error(`Error ${status}: ${statusText}`); + }); }); From d505870d7d96b3fe23424ffaeb7154a6ebf05f75 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Tue, 17 Dec 2024 16:14:52 +0100 Subject: [PATCH 087/122] TASK: Translate cancel labels and autofocus primary modal actions --- .../Private/Fusion/Common/Presentationals/Button.fusion | 4 ++++ .../Features/Review/Modals/ConfirmDiscardAllChanges.fusion | 5 ++--- .../Review/Modals/ConfirmDiscardSelectedChanges.fusion | 3 ++- .../Features/Review/Modals/ConfirmPublishAllChanges.fusion | 3 ++- .../Review/Modals/ConfirmPublishSelectedChanges.fusion | 3 ++- .../Private/Fusion/Features/Workspace/Actions/New.fusion | 2 +- .../Private/Fusion/Features/Workspace/Modals/Delete.fusion | 3 ++- .../Private/Fusion/Features/Workspace/Modals/Edit.fusion | 2 +- 8 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Button.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Button.fusion index a4b50333da5..ad5d826eece 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Button.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Button.fusion @@ -15,6 +15,8 @@ prototype(Neos.Workspace.Ui:Component.Button) < prototype(Neos.Fusion:Component) disabled = false /// string icon = '' + /// boolean + autofocus = false /// array attributes = Neos.Fusion:DataStructure @@ -23,6 +25,8 @@ prototype(Neos.Workspace.Ui:Component.Button) < prototype(Neos.Fusion:Component) type="button" title={props.title} class={['neos-button', props.isDanger && 'neos-button-danger', props.isWarning && 'neos-button-warning', props.isPrimary && 'neos-button-primary', props.isSuccess && 'neos-button-success']} + autofocus + autofocus.@if={props.autofocus} {...props.attributes} > diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion index bbf008cd10e..8b73dadea6b 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion @@ -13,9 +13,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDiscardAllChanges) < prototyp } indexWorkspaceUri = Neos.Fusion:ActionUri { action = 'index' - } - } renderer = afx` @@ -35,7 +33,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDiscardAllChanges) < prototyp
@@ -45,6 +43,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDiscardAllChanges) < prototyp isDanger label={private.i18n.id('workspaces.discardAllChanges')} icon="trash-alt icon-white" + autofocus attributes.hx-get={private.discardWorkspaceUri} attributes.hx-target='body' attributes.hx-swap='innerHTML' diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardSelectedChanges.fusion index a9ba419a799..cac7a5142a3 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardSelectedChanges.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardSelectedChanges.fusion @@ -31,13 +31,14 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDiscardSelectedChanges) < pro
@@ -40,6 +40,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmPublishAllChanges) < prototyp
- {I18n.translate('cancel', 'Cancel', [], 'Modules', 'Neos.Workspace.Ui')} + {props.i18n.id('cancel')} {props.i18n.id('workspaces.createWorkspace')} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Delete.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Delete.fusion index 12f9f26a197..aa72e299239 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Delete.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Delete.fusion @@ -33,7 +33,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.Delete) < prototype(Neos.Fusion:Comp popovertarget={private.popoverId} popovertargetaction="close" > - {private.i18n.id('cancel', 'Cancel', [])} + {private.i18n.id('cancel')} {private.i18n.id('applyChanges')} From e94d23a22fab844d5921a446bc640a4d1f4bcce9 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Tue, 17 Dec 2024 17:17:03 +0100 Subject: [PATCH 088/122] BUGFIX: Convert review actions to htmx and make flash messages work --- .../Controller/WorkspaceController.php | 26 +++++++++---------- .../Features/Review/Actions/Redirects.fusion | 10 +++++++ .../Components/ReviewDocumentTableRow.fusion | 4 +-- .../Review/Components/ReviewTable.fusion | 1 - .../Modals/ConfirmDiscardAllChanges.fusion | 1 + .../ConfirmDiscardSelectedChanges.fusion | 1 + .../ConfirmPublishSelectedChanges.fusion | 1 + .../Features/Workspace/Actions/Index.fusion | 2 ++ .../Workspace/Actions/Redirects.fusion | 3 +++ 9 files changed, 32 insertions(+), 17 deletions(-) create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Redirects.fusion create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Redirects.fusion diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index e588ba20600..3f59855a88b 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -185,7 +185,8 @@ public function reviewAction(WorkspaceName $workspace): void '', Message::SEVERITY_ERROR ); - $this->redirect('index'); + $this->indexAction(); + return; } $workspacePermissions = $this->authorizationService->getWorkspacePermissions($contentRepositoryId, $workspace, $this->securityContext->getRoles(), $currentUser->getId()); @@ -195,7 +196,8 @@ public function reviewAction(WorkspaceName $workspace): void '', Message::SEVERITY_ERROR ); - $this->redirect('index'); + $this->indexAction(); + return; } $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspace); $baseWorkspaceMetadata = null; @@ -215,7 +217,8 @@ public function reviewAction(WorkspaceName $workspace): void 'canPublishToBaseWorkspace' => $baseWorkspacePermissions?->write ?? false, 'canPublishToWorkspace' => $workspacePermissions->write, 'siteChanges' => $this->computeSiteChanges($workspaceObj, $contentRepository), - 'contentDimensions' => $contentRepository->getContentDimensionSource()->getContentDimensionsOrderedByPriority() + 'contentDimensions' => $contentRepository->getContentDimensionSource()->getContentDimensionsOrderedByPriority(), + 'flashMessages' => $this->controllerContext->getFlashMessageContainer()->getMessagesAndFlush(), ]); } @@ -267,7 +270,7 @@ public function createAction( $this->throwStatus(500, 'Workspace could not be created'); } $this->addFlashMessage($this->getModuleLabel('workspaces.workspaceHasBeenCreated', [$title->value])); - $this->redirect('index'); + $this->indexAction(); } /** @@ -556,7 +559,7 @@ public function publishDocumentAction(string $nodeAddress, WorkspaceName $select ); $this->addFlashMessage($this->getModuleLabel('workspaces.selectedChangeHasBeenPublished')); - $this->redirect('review', null, null, ['workspace' => $selectedWorkspace->value]); + $this->reviewAction($selectedWorkspace); } /** @@ -580,9 +583,7 @@ public function discardDocumentAction(string $nodeAddress, WorkspaceName $select ); $this->addFlashMessage($this->getModuleLabel('workspaces.selectedChangeHasBeenDiscarded')); - - $this->redirect('review', null, null, ['workspace' => $selectedWorkspace->value]); - + $this->reviewAction($selectedWorkspace); } /** @@ -625,8 +626,7 @@ public function publishOrDiscardNodesAction(array $nodes, string $action, Worksp default: throw new \RuntimeException('Invalid action "' . htmlspecialchars($action) . '" given.', 1346167441); } - - $this->redirect('review', null, null, ['workspace' => $workspace->value]); + $this->reviewAction($workspace); } /** @@ -648,8 +648,7 @@ public function publishWorkspaceAction(WorkspaceName $workspace): void ], ) ); - //todo make redirect work - $this->redirect('index'); + $this->indexAction(); } public function confirmPublishAllChangesAction(WorkspaceName $workspaceName): void @@ -754,8 +753,7 @@ public function discardWorkspaceAction(WorkspaceName $workspace): void [htmlspecialchars($workspace->value)], ) ); - //todo make redirect to index work - $this->redirect('review', null, null, ['workspace' => $workspace->value]); + $this->reviewAction($workspace); } /** diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Redirects.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Redirects.fusion new file mode 100644 index 00000000000..18847e98b55 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Redirects.fusion @@ -0,0 +1,10 @@ +# Actions that update a workspace and redirect to the review view + +# Discard actions +Neos.Workspace.Ui.WorkspaceController.discardWorkspace < Neos.Workspace.Ui.WorkspaceController.review +Neos.Workspace.Ui.WorkspaceController.discardDocument < Neos.Workspace.Ui.WorkspaceController.review + +# Publish actions +Neos.Workspace.Ui.WorkspaceController.publishDocument < Neos.Workspace.Ui.WorkspaceController.review +Neos.Workspace.Ui.WorkspaceController.publishOrDiscardNodes < Neos.Workspace.Ui.WorkspaceController.review +Neos.Workspace.Ui.WorkspaceController.publishWorkspace < Neos.Workspace.Ui.WorkspaceController.review diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion index 1d78d893e9b..12622c01cf1 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion @@ -18,7 +18,6 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F arguments { targetWorkspaceName = ${selectedWorkspaceName} targetNode = ${document.document.documentNodeAddress} - } } openNodeInPreviewAction = Neos.Fusion:ActionUri { @@ -28,10 +27,10 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F action = 'preview' // todo fixme @process.addQuery = ${value + '?node=' + document.document.documentNodeAddress} - } discardDocumentAction = Neos.Fusion:ActionUri { action = 'discardDocument' + format = 'htmx' arguments { nodeAddress = ${document.document.documentNodeAddress} selectedWorkspace = ${selectedWorkspaceName} @@ -39,6 +38,7 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F } publishDocumentAction = Neos.Fusion:ActionUri { action = 'publishDocument' + format = 'htmx' arguments { nodeAddress = ${document.document.documentNodeAddress} selectedWorkspace = ${selectedWorkspaceName} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewTable.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewTable.fusion index e6c27b56cb5..7d64c851b97 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewTable.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewTable.fusion @@ -11,7 +11,6 @@ prototype(Neos.Workspace.Ui:Component.ReviewTable) < prototype(Neos.Fusion:Compo @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} - } renderer = afx` diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion index 8b73dadea6b..2010a5c221f 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion @@ -7,6 +7,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDiscardAllChanges) < prototyp discardWorkspaceUri = Neos.Fusion:ActionUri { action = 'discardWorkspace' + format = 'htmx' arguments { workspace = ${workspaceName} } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardSelectedChanges.fusion index cac7a5142a3..fd9551f660c 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardSelectedChanges.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardSelectedChanges.fusion @@ -7,6 +7,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDiscardSelectedChanges) < pro confirmDiscardSelectedChanges = Neos.Fusion:ActionUri { action = 'publishOrDiscardNodes' + format = 'htmx' arguments { workspace = ${workspaceName} action = 'discard' diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmPublishSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmPublishSelectedChanges.fusion index e69a937cc8e..52076b64d76 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmPublishSelectedChanges.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmPublishSelectedChanges.fusion @@ -7,6 +7,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmPublishSelectedChanges) < pro publishSelectedChanges = Neos.Fusion:ActionUri { action = 'publishOrDiscardNodes' + format = 'htmx' arguments { workspace = ${workspaceName} action = 'publish' diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion index 7dce63756b0..3b6a28579d2 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion @@ -3,6 +3,8 @@ Neos.Workspace.Ui.WorkspaceController.index = Neos.Fusion:Component { userWorkspaceName = ${userWorkspaceName} /// Neos\Workspace\Ui\ViewModel\WorkspaceListItems workspaceListItems = ${workspaceListItems} + /// array + flashMessages = ${flashMessages} newAction = Neos.Fusion:UriBuilder { action = 'new' diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Redirects.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Redirects.fusion new file mode 100644 index 00000000000..8d2c1e52a37 --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Redirects.fusion @@ -0,0 +1,3 @@ +# Actions that update a workspace and redirect to the index view + +Neos.Workspace.Ui.WorkspaceController.create < Neos.Workspace.Ui.WorkspaceController.index From 3cee6fabd3527049e65520480e1d5e65ffce004a Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Tue, 17 Dec 2024 19:24:59 +0100 Subject: [PATCH 089/122] BUGFIX: Hide emtpy diffs --- .../Fusion/Features/Review/Components/ReviewChangeDiff.fusion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewChangeDiff.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewChangeDiff.fusion index 50d7792f351..be26496aa42 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewChangeDiff.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewChangeDiff.fusion @@ -47,7 +47,7 @@ prototype(Neos.Workspace.Ui:Component.ReviewChangeDiff) < prototype(Neos.Fusion: {contentChanges.properties.propertyLabel} - + From 7e33629622481bd0ee9ddfae20f1f4b991bd6120 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Tue, 17 Dec 2024 19:26:08 +0100 Subject: [PATCH 090/122] TASK: Code cleanup --- .../ConfirmDiscardSelectedChanges.fusion | 1 - .../Review/Components/ReviewActions.fusion | 8 +-- .../Components/ReviewDocumentTableRow.fusion | 52 ++++++++++--------- .../Modals/ConfirmDiscardAllChanges.fusion | 3 -- .../Modals/ConfirmPublishAllChanges.fusion | 1 - 5 files changed, 32 insertions(+), 33 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/ConfirmDiscardSelectedChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/ConfirmDiscardSelectedChanges.fusion index 110cafc595f..9c573dfa31f 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/ConfirmDiscardSelectedChanges.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/ConfirmDiscardSelectedChanges.fusion @@ -4,7 +4,6 @@ Neos.Workspace.Ui.WorkspaceController.confirmDiscardSelectedChanges = Neos.Fusio renderer = afx` ` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion index dd3625572ab..4db5f7165bf 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion @@ -67,7 +67,7 @@ prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Com +
- + - + @@ -84,24 +90,24 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F - +
- + - - ` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion index 2010a5c221f..0e5002ead3e 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion @@ -12,9 +12,6 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDiscardAllChanges) < prototyp workspace = ${workspaceName} } } - indexWorkspaceUri = Neos.Fusion:ActionUri { - action = 'index' - } } renderer = afx` diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmPublishAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmPublishAllChanges.fusion index 5567baea372..aa4d58fda44 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmPublishAllChanges.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmPublishAllChanges.fusion @@ -1,7 +1,6 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmPublishAllChanges) < prototype(Neos.Fusion:Component) { workspaceTitle = '' - @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} popoverId = 'confirm-publish-all-changes-popover' From d138d5227462a397b991dcd0f7869be96c62502c Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Tue, 17 Dec 2024 19:26:19 +0100 Subject: [PATCH 091/122] BUGFIX: Add space between icon in button and its label --- .../Private/Fusion/Common/Presentationals/Button.fusion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Button.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Button.fusion index ad5d826eece..a9c0037d7da 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Button.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Button.fusion @@ -30,7 +30,7 @@ prototype(Neos.Workspace.Ui:Component.Button) < prototype(Neos.Fusion:Component) {...props.attributes} > - {props.label} + {props.icon ? ' ' : ''}{props.label} ` } From b22d0c7a463c265738ce4beaea83a49809cb4d58 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Tue, 17 Dec 2024 19:41:00 +0100 Subject: [PATCH 092/122] BUGFIX: Reload review correctly after discard or publish workspace --- .../Features/Review/Actions/Review.fusion | 3 ++- .../Modals/ConfirmDiscardAllChanges.fusion | 27 +++++++++---------- .../Modals/ConfirmPublishAllChanges.fusion | 27 +++++++++---------- .../Resources/Public/Styles/Module.css | 4 +++ 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Review.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Review.fusion index 964ab2a0b29..c51d850f0d9 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Review.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Review.fusion @@ -44,7 +44,8 @@ Neos.Workspace.Ui.WorkspaceController.review = Neos.Fusion:Component { selectedWorkspaceName={props.selectedWorkspaceName} selectedWorkspaceLabel={props.selectedWorkspaceLabel} canPublishToBaseWorkspace={props.canPublishToBaseWorkspace} - canPublishToWorkspace={props.canPublishToWorkspace}/> + canPublishToWorkspace={props.canPublishToWorkspace} + />
{props.i18n.id('workspaces.reviewWorkspace.disabled').arguments([selectedWorkspaceLabel]).translate()}
diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion index 0e5002ead3e..c6ee329e7ce 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmDiscardAllChanges.fusion @@ -29,25 +29,24 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmDiscardAllChanges) < prototyp {private.i18n.id('workspaces.discardAllChangesInWorkspaceConfirmation').arguments([props.workspaceTitle])}
-
+
- -
- -
-
+ +
` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmPublishAllChanges.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmPublishAllChanges.fusion index aa4d58fda44..0b3e42207dd 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmPublishAllChanges.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Modals/ConfirmPublishAllChanges.fusion @@ -29,25 +29,24 @@ prototype(Neos.Workspace.Ui:Component.Modal.ConfirmPublishAllChanges) < prototyp {private.i18n.id('workspaces.publishAllChangesInWorkspaceConfirmation').arguments([props.workspaceTitle])}
-
+
- -
- -
-
+ + ` } diff --git a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css index a5f67d76bb7..3eff7cb812c 100644 --- a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css +++ b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css @@ -419,3 +419,7 @@ tr.neos-change + tr.neos-change td.neos-content-change { #workspaceReview #publishOrDiscardNodesForm { min-width: 100%; } + +#workspaceReview .no-unpublished-changes { + margin-top: 1em; +} From ccdd46cffb8ae0915133a1b95bf28fd94d33d8f6 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Tue, 17 Dec 2024 20:23:30 +0100 Subject: [PATCH 093/122] FEATURE: Set basic visibility on workspace creation --- .../Domain/Model/WorkspaceRoleAssignments.php | 15 +++ .../Controller/WorkspaceController.php | 10 +- .../Features/Workspace/Actions/Delete.fusion | 2 + .../Features/Workspace/Actions/New.fusion | 103 ++------------- .../Features/Workspace/Modals/Create.fusion | 125 ++++++++++++++++++ .../Features/Workspace/Modals/Delete.fusion | 2 + 6 files changed, 162 insertions(+), 95 deletions(-) create mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Create.fusion diff --git a/Neos.Neos/Classes/Domain/Model/WorkspaceRoleAssignments.php b/Neos.Neos/Classes/Domain/Model/WorkspaceRoleAssignments.php index 87b33bc0d3b..c10d1e0ba6e 100644 --- a/Neos.Neos/Classes/Domain/Model/WorkspaceRoleAssignments.php +++ b/Neos.Neos/Classes/Domain/Model/WorkspaceRoleAssignments.php @@ -82,6 +82,21 @@ public static function createForSharedWorkspace(UserId $userId): self ); } + /** + * Default role assignment to be specified at creation via {@see WorkspaceService::createSharedWorkspace()} + * + * The specified user is manager + */ + public static function createForPrivateWorkspace(UserId $userId): self + { + return new self( + WorkspaceRoleAssignment::createForUser( + $userId, + WorkspaceRole::MANAGER, + ) + ); + } + public function isEmpty(): bool { return $this->assignments === []; diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 3f59855a88b..45b4aaded15 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -52,6 +52,8 @@ use Neos\Neos\Domain\Model\User; use Neos\Neos\Domain\Model\WorkspaceClassification; use Neos\Neos\Domain\Model\WorkspaceDescription; +use Neos\Neos\Domain\Model\WorkspaceRole; +use Neos\Neos\Domain\Model\WorkspaceRoleAssignment; use Neos\Neos\Domain\Model\WorkspaceRoleAssignments; use Neos\Neos\Domain\Model\WorkspaceTitle; use Neos\Neos\Domain\NodeLabel\NodeLabelGeneratorInterface; @@ -234,6 +236,7 @@ public function createAction( WorkspaceTitle $title, WorkspaceName $baseWorkspace, WorkspaceDescription $description, + string $visibility = 'shared', ): void { $currentUser = $this->userService->getCurrentUser(); if ($currentUser === null) { @@ -244,15 +247,16 @@ public function createAction( $workspaceName = $this->workspaceService->getUniqueWorkspaceName($contentRepositoryId, $title->value); try { + $assignments = $visibility === 'shared' ? + WorkspaceRoleAssignments::createForSharedWorkspace($currentUser->getId()) : + WorkspaceRoleAssignments::createForPrivateWorkspace($currentUser->getId()); $this->workspaceService->createSharedWorkspace( $contentRepositoryId, $workspaceName, $title, $description, $baseWorkspace, - WorkspaceRoleAssignments::createForSharedWorkspace( - $currentUser->getId() - ) + $assignments ); } catch (WorkspaceAlreadyExists $exception) { $this->addFlashMessage( diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Delete.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Delete.fusion index 39bdb8ed5e2..18600d3651c 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Delete.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Delete.fusion @@ -2,7 +2,9 @@ # Delete confirmation modal with empty POST response # Neos.Workspace.Ui.WorkspaceController.delete = Neos.Fusion:Component { + /// string workspaceName = ${workspaceName} + /// string workspaceTitle = ${workspaceTitle} renderer = Neos.Fusion:Match { diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/New.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/New.fusion index 50698a05999..e74e2f9aa9a 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/New.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/New.fusion @@ -1,97 +1,16 @@ Neos.Workspace.Ui.WorkspaceController.new = Neos.Fusion:Component { + /// array baseWorkspaceOptions = ${baseWorkspaceOptions} - workspace = ${workspace} - i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} - popoverId = 'create-workspace-popover' - prototype(Neos.Fusion.Form:LabelRenderer) { - translationPackage = 'Neos.Workspace.Ui' - translationSource = 'Main' + renderer = Neos.Fusion:Match { + @subject = ${request.httpRequest.method} + # Render the create modal + @default = afx` + + ` + # Empty template for the delete response as the payload is contained in the HTTP headers + POST = '' } - - prototype(Neos.Fusion.Form:Neos.BackendModule.FieldContainer) { - translation.label { - package = 'Neos.Workspace.Ui' - source = 'Main' - } - } - - renderer = afx` -
-
- -
- {props.i18n.id('workspaces.createNewWorkspace')} -
-
- -
-
- - - - - - - - - - - - - {workspaceTitle} - - - -
-
-
- - - {props.i18n.id('workspaces.createWorkspace')} - -
-
-
- ` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Create.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Create.fusion new file mode 100644 index 00000000000..9030bb72b2a --- /dev/null +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Create.fusion @@ -0,0 +1,125 @@ +prototype(Neos.Workspace.Ui:Component.Modal.Create) < prototype(Neos.Fusion:Component) { + /// array + baseWorkspaceOptions = null + + @private { + i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} + popoverId = 'create-workspace-popover' + } + + prototype(Neos.Fusion.Form:LabelRenderer) { + translationPackage = 'Neos.Workspace.Ui' + translationSource = 'Main' + } + + prototype(Neos.Fusion.Form:Neos.BackendModule.FieldContainer) { + translation.label { + package = 'Neos.Workspace.Ui' + source = 'Main' + } + } + + renderer = afx` +
+
+ +
+ {private.i18n.id('workspaces.createNewWorkspace')} +
+
+ +
+
+ + + + + + + + + + + + {workspaceTitle} + + + + + + + {private.i18n.id('workspaces.workspace.visibility.internal')} + +

+ {private.i18n.id('workspaces.workspace.visibility.internal.help')} +

+
+ + {private.i18n.id('workspaces.workspace.visibility.private')} + +

+ {private.i18n.id('workspaces.workspace.visibility.private.help')} +

+
+
+
+
+ + + {private.i18n.id('workspaces.createWorkspace')} + +
+
+
+ ` +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Delete.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Delete.fusion index aa72e299239..6fe25e1582e 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Delete.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Delete.fusion @@ -1,5 +1,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.Delete) < prototype(Neos.Fusion:Component) { + /// string workspaceName = null + /// string workspaceTitle = null @private { From 929949ce1df9843002dc4619ecfeeb47c49aaae2 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Wed, 18 Dec 2024 15:44:49 +0100 Subject: [PATCH 094/122] TASK: Cleanup action redirects by using forward --- .../Controller/WorkspaceController.php | 81 +++---------------- .../Features/Review/Actions/Redirects.fusion | 10 --- .../Features/Workspace/Actions/Edit.fusion | 2 +- .../Features/Workspace/Actions/Index.fusion | 74 +++++++---------- .../Workspace/Actions/Redirects.fusion | 3 - .../Features/Workspace/Modals/Edit.fusion | 35 +++++++- 6 files changed, 75 insertions(+), 130 deletions(-) delete mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Redirects.fusion delete mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Redirects.fusion diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 45b4aaded15..2ac79b140d6 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -14,7 +14,6 @@ namespace Neos\Workspace\Ui\Controller; -use Doctrine\DBAL\Exception as DBALException; use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\Dimension\ContentDimensionId; use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTag; @@ -35,12 +34,8 @@ use Neos\Diff\Renderer\Html\HtmlArrayRenderer; use Neos\Error\Messages\Message; use Neos\Flow\Annotations as Flow; -use Neos\Flow\Http\Exception; -use Neos\Flow\I18n\Exception\IndexOutOfBoundsException; -use Neos\Flow\I18n\Exception\InvalidFormatPlaceholderException; use Neos\Flow\I18n\Translator; use Neos\Flow\Mvc\Exception\StopActionException; -use Neos\Flow\Mvc\Routing\Exception\MissingActionNameException; use Neos\Flow\Package\PackageManager; use Neos\Flow\Property\PropertyMapper; use Neos\Flow\Security\Context; @@ -52,8 +47,6 @@ use Neos\Neos\Domain\Model\User; use Neos\Neos\Domain\Model\WorkspaceClassification; use Neos\Neos\Domain\Model\WorkspaceDescription; -use Neos\Neos\Domain\Model\WorkspaceRole; -use Neos\Neos\Domain\Model\WorkspaceRoleAssignment; use Neos\Neos\Domain\Model\WorkspaceRoleAssignments; use Neos\Neos\Domain\Model\WorkspaceTitle; use Neos\Neos\Domain\NodeLabel\NodeLabelGeneratorInterface; @@ -187,8 +180,7 @@ public function reviewAction(WorkspaceName $workspace): void '', Message::SEVERITY_ERROR ); - $this->indexAction(); - return; + $this->forward('index'); } $workspacePermissions = $this->authorizationService->getWorkspacePermissions($contentRepositoryId, $workspace, $this->securityContext->getRoles(), $currentUser->getId()); @@ -198,8 +190,7 @@ public function reviewAction(WorkspaceName $workspace): void '', Message::SEVERITY_ERROR ); - $this->indexAction(); - return; + $this->forward('index'); } $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspace); $baseWorkspaceMetadata = null; @@ -274,7 +265,7 @@ public function createAction( $this->throwStatus(500, 'Workspace could not be created'); } $this->addFlashMessage($this->getModuleLabel('workspaces.workspaceHasBeenCreated', [$title->value])); - $this->indexAction(); + $this->forward('index'); } /** @@ -321,10 +312,8 @@ public function editAction(WorkspaceName $workspaceName): void * Update a workspace * * @Flow\Validate(argumentName="title", type="\Neos\Flow\Validation\Validator\NotEmptyValidator") - * @param WorkspaceName $workspaceName * @param WorkspaceTitle $title Human friendly title of the workspace, for example "Christmas Campaign" * @param WorkspaceDescription $description A description explaining the purpose of the new workspace - * @return void */ public function updateAction( WorkspaceName $workspaceName, @@ -400,10 +389,7 @@ public function updateAction( * TODO: Add force delete option to ignore unpublished nodes or dependent workspaces, the later should be rebased instead * * @param WorkspaceName $workspaceName A workspace to delete - * @throws IndexOutOfBoundsException - * @throws InvalidFormatPlaceholderException * @throws StopActionException - * @throws DBALException */ public function deleteAction(WorkspaceName $workspaceName): void { @@ -544,13 +530,6 @@ public function rebaseAndRedirectAction(string $targetNode, WorkspaceName $targe /** * Publish a single document node - * - * @param string $nodeAddress - * @param WorkspaceName $selectedWorkspace - * @throws Exception - * @throws MissingActionNameException - * @throws StopActionException - * @throws WorkspaceRebaseFailed */ public function publishDocumentAction(string $nodeAddress, WorkspaceName $selectedWorkspace): void { @@ -563,18 +542,13 @@ public function publishDocumentAction(string $nodeAddress, WorkspaceName $select ); $this->addFlashMessage($this->getModuleLabel('workspaces.selectedChangeHasBeenPublished')); - $this->reviewAction($selectedWorkspace); + $this->forward('review', null, null, ['workspace' => $selectedWorkspace->value]); } /** * Discard a single document node * - * @param string $nodeAddress - * @param WorkspaceName $selectedWorkspace - * @throws StopActionException * @throws WorkspaceRebaseFailed - * @throws Exception - * @throws MissingActionNameException */ public function discardDocumentAction(string $nodeAddress, WorkspaceName $selectedWorkspace): void { @@ -587,14 +561,11 @@ public function discardDocumentAction(string $nodeAddress, WorkspaceName $select ); $this->addFlashMessage($this->getModuleLabel('workspaces.selectedChangeHasBeenDiscarded')); - $this->reviewAction($selectedWorkspace); + $this->forward('review', null, null, ['workspace' => $selectedWorkspace->value]); } /** * @psalm-param list $nodes - * @throws IndexOutOfBoundsException - * @throws InvalidFormatPlaceholderException - * @throws StopActionException */ public function publishOrDiscardNodesAction(array $nodes, string $action, WorkspaceName $workspace): void { @@ -611,7 +582,6 @@ public function publishOrDiscardNodesAction(array $nodes, string $action, Worksp $nodeAddress->aggregateId ); } - //todo: make flashmessage work with htmx $this->addFlashMessage( $this->getModuleLabel('workspaces.selectedChangesHaveBeenPublished') ); @@ -630,7 +600,7 @@ public function publishOrDiscardNodesAction(array $nodes, string $action, Worksp default: throw new \RuntimeException('Invalid action "' . htmlspecialchars($action) . '" given.', 1346167441); } - $this->reviewAction($workspace); + $this->forward('review', null, null, ['workspace' => $workspace->value]); } /** @@ -652,7 +622,7 @@ public function publishWorkspaceAction(WorkspaceName $workspace): void ], ) ); - $this->indexAction(); + $this->forward('index'); } public function confirmPublishAllChangesAction(WorkspaceName $workspaceName): void @@ -757,15 +727,11 @@ public function discardWorkspaceAction(WorkspaceName $workspace): void [htmlspecialchars($workspace->value)], ) ); - $this->reviewAction($workspace); + $this->forward('review', null, null, ['workspace' => $workspace->value]); } /** * Computes the number of added, changed and removed nodes for the given workspace - * - * @param Workspace $selectedWorkspace - * @param ContentRepository $contentRepository - * @return PendingChanges */ protected function computePendingChanges(Workspace $selectedWorkspace, ContentRepository $contentRepository): PendingChanges { @@ -1103,11 +1069,8 @@ protected function renderContentChanges( * Note: It's clear that this method needs to be extracted and moved to a more universal service at some point. * However, since we only implemented diff-view support for this particular controller at the moment, it stays * here for the time being. Once we start displaying diffs elsewhere, we should refactor the diff rendering part. - * - * @param mixed $propertyValue - * @return string */ - protected function renderSlimmedDownContent($propertyValue) + protected function renderSlimmedDownContent(mixed $propertyValue): string { $content = ''; if (is_string($propertyValue)) { @@ -1121,12 +1084,8 @@ protected function renderSlimmedDownContent($propertyValue) /** * Tries to determine a label for the specified property - * - * @param string $propertyName - * @param Node $changedNode - * @return string */ - protected function getPropertyLabel($propertyName, Node $changedNode) + protected function getPropertyLabel(string $propertyName, Node $changedNode): string { $properties = $this->getNodeType($changedNode)->getProperties(); if ( @@ -1168,7 +1127,6 @@ protected function getPropertyLabel($propertyName, Node $changedNode) * do that in these cases. * * @param array &$diffArray - * @return void */ protected function postProcessDiffArray(array &$diffArray): void { @@ -1195,8 +1153,6 @@ protected function postProcessDiffArray(array &$diffArray): void * workspaces. * If $excludedWorkspace is set, this workspace and all its base workspaces will be excluded from the list of returned workspaces * - * @param ContentRepository $contentRepository - * @param WorkspaceName|null $excludedWorkspace * @return array */ protected function prepareBaseWorkspaceOptions( @@ -1246,23 +1202,6 @@ protected function prepareBaseWorkspaceOptions( return $baseWorkspaceOptions; } - /** - * Todo remove? - * Creates an array of user names and their respective labels which are possible owners for a workspace. - * - * @return array - */ - protected function prepareOwnerOptions(): array - { - $ownerOptions = ['' => '-']; - foreach ($this->userService->getUsers() as $user) { - /** @var User $user */ - $ownerOptions[$this->persistenceManager->getIdentifierByObject($user)] = $user->getLabel(); - } - - return $ownerOptions; - } - private function requireBaseWorkspace( Workspace $workspace, ContentRepository $contentRepository, diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Redirects.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Redirects.fusion deleted file mode 100644 index 18847e98b55..00000000000 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Actions/Redirects.fusion +++ /dev/null @@ -1,10 +0,0 @@ -# Actions that update a workspace and redirect to the review view - -# Discard actions -Neos.Workspace.Ui.WorkspaceController.discardWorkspace < Neos.Workspace.Ui.WorkspaceController.review -Neos.Workspace.Ui.WorkspaceController.discardDocument < Neos.Workspace.Ui.WorkspaceController.review - -# Publish actions -Neos.Workspace.Ui.WorkspaceController.publishDocument < Neos.Workspace.Ui.WorkspaceController.review -Neos.Workspace.Ui.WorkspaceController.publishOrDiscardNodes < Neos.Workspace.Ui.WorkspaceController.review -Neos.Workspace.Ui.WorkspaceController.publishWorkspace < Neos.Workspace.Ui.WorkspaceController.review diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Edit.fusion index 683392bd0b1..33cd1b2ca57 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Edit.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Edit.fusion @@ -8,9 +8,9 @@ Neos.Workspace.Ui.WorkspaceController.edit = Neos.Fusion:Component { workspaceTitle={props.editWorkspaceFormData.workspaceTitle.value} workspaceDescription={props.editWorkspaceFormData.workspaceDescription.value} baseWorkspaceName={props.editWorkspaceFormData.baseWorkspaceName.value} - workspaceHasChanges={props.editWorkspaceFormData.workspaceHasChanges} baseWorkspaceOptions={props.editWorkspaceFormData.baseWorkspaceOptions} + visibility={props.editWorkspaceFormData.visibility} /> ` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion index 3b6a28579d2..179f0488c9a 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion @@ -15,49 +15,35 @@ Neos.Workspace.Ui.WorkspaceController.index = Neos.Fusion:Component { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} - renderer = Neos.Fusion:Match { - @subject = ${request.format} - @default = afx` - -
-
- -
-
- - - +
+
+ - - - ` - - htmx = afx` - - - - ` - } +
+
+ + + + +
+ ` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Redirects.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Redirects.fusion deleted file mode 100644 index 8d2c1e52a37..00000000000 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Redirects.fusion +++ /dev/null @@ -1,3 +0,0 @@ -# Actions that update a workspace and redirect to the index view - -Neos.Workspace.Ui.WorkspaceController.create < Neos.Workspace.Ui.WorkspaceController.index diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Edit.fusion index c91f117d0c8..43fa12e5af8 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Edit.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Edit.fusion @@ -1,9 +1,15 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Component) { + /// string workspaceName = '' + /// string workspaceTitle = '' + /// string workspaceDescription = '' + /// string baseWorkspaceName = '' - + /// string + visibility = '' + /// boolean workspaceHasChanges = false /// array baseWorkspaceOptions = ${[]} @@ -107,6 +113,33 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Compon {private.i18n.id('workspaces.cantChangeBaseWorkspaceBecauseWorkspaceContainsChanges')}

+ + + + {private.i18n.id('workspaces.workspace.visibility.internal')} + +

+ {private.i18n.id('workspaces.workspace.visibility.internal.help')} +

+
+ + {private.i18n.id('workspaces.workspace.visibility.private')} + +

+ {private.i18n.id('workspaces.workspace.visibility.private.help')} +

+
From 03c40f09ca18d9ba5424d87cae21a9c2c7193369 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Wed, 18 Dec 2024 16:20:36 +0100 Subject: [PATCH 095/122] TASK: Remove last modification date column until we can have the date available in the api --- .../Features/Workspace/Components/WorkspaceTable.fusion | 6 ------ .../Features/Workspace/Components/WorkspaceTableRow.fusion | 1 - 2 files changed, 7 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion index 9de394f459a..7cb3d983a14 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion @@ -23,12 +23,6 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTable) < prototype(Neos.Fusion:Co {private.i18n.id('workspaces.workspace.description')} - - - {private.i18n.id('workspaces.workspace.status')} {private.i18n.id('workspaces.workspace.changes')} {private.i18n.id('workspaces.workspace.actions')} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion index 991193f7718..e2b231a0d8c 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion @@ -96,7 +96,6 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion {props.workspaceListItem.description || '﹘'} - ﹘ {private.i18n.id('workspaces.workspace.status.' + props.workspaceListItem.status).translate()} Date: Wed, 18 Dec 2024 16:40:54 +0100 Subject: [PATCH 096/122] BUGFIX: Reload workspace list after creation --- .../Private/Fusion/Features/Workspace/Modals/Create.fusion | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Create.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Create.fusion index 9030bb72b2a..1688e9c5ddf 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Create.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Create.fusion @@ -40,6 +40,9 @@ prototype(Neos.Workspace.Ui:Component.Modal.Create) < prototype(Neos.Fusion:Comp attributes.hx-post={form.getTarget()} attributes.hx-disabled-elt="find button" attributes.hx-on--after-request={'document.getElementById("' + private.popoverId + '").hidePopover()'} + attributes.hx-select="#workspace-module-content" + attributes.hx-target="#workspace-module-content" + attributes.hx-swap="outerHTML" >
From 22b8c69415dd0e7d87e0ae4df18e0a9c7ceaa917 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Wed, 18 Dec 2024 16:41:59 +0100 Subject: [PATCH 097/122] FEATURE: Sort workspaces by title --- .../Controller/WorkspaceController.php | 8 ++++++- .../Classes/ViewModel/WorkspaceListItems.php | 9 ++++++++ .../Features/Workspace/Actions/Index.fusion | 6 +++++ .../Components/WorkspaceTable.fusion | 22 +++++++++++++++++-- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 2ac79b140d6..08333636bec 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -129,7 +129,7 @@ class WorkspaceController extends AbstractModuleController /** * Display a list of unpublished content */ - public function indexAction(): void + public function indexAction(string $sortBy = 'title', bool $sortAscending = true): void { $currentUser = $this->userService->getCurrentUser(); if ($currentUser === null) { @@ -155,11 +155,17 @@ public function indexAction(): void $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); $workspaceListItems = $this->getWorkspaceListItems($contentRepository, $currentUser); + if ($sortBy === 'title') { + $workspaceListItems = $workspaceListItems->sortByTitle($sortAscending); + } + $this->view->assignMultiple([ // todo remove userWorkspaceName field and add distinction to $workspaceListItems as $workspaceListItems->userWorkspace and $workspaceListItems->otherWorkspaces or something. 'userWorkspaceName' => $this->workspaceService->getPersonalWorkspaceForUser($contentRepositoryId, $currentUser->getId())->workspaceName->value, 'workspaceListItems' => $workspaceListItems, 'flashMessages' => $this->controllerContext->getFlashMessageContainer()->getMessagesAndFlush(), + 'sortAscending' => $sortAscending, + 'sortBy' => $sortBy, ]); } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItems.php b/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItems.php index 39408c94bf9..8f476e1fcab 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItems.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItems.php @@ -43,6 +43,15 @@ public static function fromArray(array $items): self return new self($items); } + public function sortByTitle(bool $ascending = true): self + { + $items = $this->items; + usort($items, static function (WorkspaceListItem $a, WorkspaceListItem $b) { + return strcasecmp($a->title, $b->title); + }); + return new self($ascending ? $items : array_reverse($items)); + } + public function getIterator(): \Traversable { yield from $this->items; diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion index 179f0488c9a..8b40bf735fd 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion @@ -5,6 +5,10 @@ Neos.Workspace.Ui.WorkspaceController.index = Neos.Fusion:Component { workspaceListItems = ${workspaceListItems} /// array flashMessages = ${flashMessages} + /// string + sortBy = ${sortBy} + /// bool + sortAscending = ${sortAscending} newAction = Neos.Fusion:UriBuilder { action = 'new' @@ -24,6 +28,8 @@ Neos.Workspace.Ui.WorkspaceController.index = Neos.Fusion:Component {
diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion index 7cb3d983a14..6fce74c637d 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion @@ -6,9 +6,20 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTable) < prototype(Neos.Fusion:Co userWorkspaceName = null /// \Neos\Workspace\Ui\ViewModel\WorkspaceListItems workspaceListItems = ${{}} + /// string + sortBy = 'title' + /// bool + sortAscending = true @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} + workspacesUri = Neos.Fusion:ActionUri { + action = 'index' + format = 'html' + arguments { + sortAscending = ${!props.sortAscending} + } + } } renderer = afx` @@ -17,9 +28,16 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTable) < prototype(Neos.Fusion:Co - {private.i18n.id('workspaces.workspace.description')} From 89b7cae5b77f94a548402af68ef37351d28c4d12 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Thu, 19 Dec 2024 11:22:53 +0100 Subject: [PATCH 098/122] BUGFIX: Show correct workspace visibility state in the workspace list --- .../Controller/WorkspaceController.php | 13 ++++++- .../Classes/ViewModel/WorkspaceListItem.php | 36 +++++++++++++++++++ .../Fusion/Common/Presentationals/Icon.fusion | 1 + .../Components/WorkspaceTableRow.fusion | 29 +++++---------- .../Features/Workspace/Modals/Create.fusion | 4 +-- .../Features/Workspace/Modals/Edit.fusion | 22 ++++++------ .../Private/Translations/ar/Main.xlf | 10 +++--- .../Private/Translations/cs/Main.xlf | 14 ++++---- .../Private/Translations/da/Main.xlf | 10 +++--- .../Private/Translations/de/Main.xlf | 22 +++++++++--- .../Private/Translations/el/Main.xlf | 12 +++---- .../Private/Translations/en/Main.xlf | 19 +++++++--- .../Private/Translations/es/Main.xlf | 10 +++--- .../Private/Translations/fi/Main.xlf | 10 +++--- .../Private/Translations/fr/Main.xlf | 10 +++--- .../Private/Translations/hu/Main.xlf | 10 +++--- .../Private/Translations/id_ID/Main.xlf | 14 ++++---- .../Private/Translations/it/Main.xlf | 10 +++--- .../Private/Translations/ja/Main.xlf | 10 +++--- .../Private/Translations/km/Main.xlf | 10 +++--- .../Private/Translations/lv/Main.xlf | 10 +++--- .../Private/Translations/nl/Main.xlf | 10 +++--- .../Private/Translations/no/Main.xlf | 10 +++--- .../Private/Translations/pl/Main.xlf | 10 +++--- .../Private/Translations/pt/Main.xlf | 10 +++--- .../Private/Translations/pt_BR/Main.xlf | 10 +++--- .../Private/Translations/ru/Main.xlf | 10 +++--- .../Private/Translations/sr/Main.xlf | 14 ++++---- .../Private/Translations/sv/Main.xlf | 10 +++--- .../Private/Translations/tl_PH/Main.xlf | 12 +++---- .../Private/Translations/tr/Main.xlf | 10 +++--- .../Private/Translations/uk/Main.xlf | 10 +++--- .../Private/Translations/vi/Main.xlf | 12 +++---- .../Private/Translations/zh/Main.xlf | 12 +++---- .../Private/Translations/zh_TW/Main.xlf | 10 +++--- 35 files changed, 245 insertions(+), 191 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 08333636bec..29c58667e57 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -277,7 +277,7 @@ public function createAction( /** * Edit a workspace * - * Renders /Resource/Private/Fusion/Views/Edit.fusion + * @param WorkspaceName $workspaceName The name of the workspace that is being edited */ public function editAction(WorkspaceName $workspaceName): void { @@ -318,8 +318,10 @@ public function editAction(WorkspaceName $workspaceName): void * Update a workspace * * @Flow\Validate(argumentName="title", type="\Neos\Flow\Validation\Validator\NotEmptyValidator") + * @param WorkspaceName $workspaceName The name of the workspace that is being updated * @param WorkspaceTitle $title Human friendly title of the workspace, for example "Christmas Campaign" * @param WorkspaceDescription $description A description explaining the purpose of the new workspace + * @param WorkspaceName $baseWorkspace A description explaining the purpose of the new workspace */ public function updateAction( WorkspaceName $workspaceName, @@ -651,6 +653,7 @@ public function confirmPublishAllChangesAction(WorkspaceName $workspaceName): vo 'workspaceTitle' => $workspaceMetadata->title->value, ]); } + public function confirmDiscardAllChangesAction(WorkspaceName $workspaceName): void { $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; @@ -693,6 +696,7 @@ public function confirmPublishSelectedChangesAction(WorkspaceName $workspaceName 'baseWorkspaceTitle' => $baseWorkspaceMetadata->title->value, ]); } + public function confirmDiscardSelectedChangesAction(WorkspaceName $workspaceName): void { $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; @@ -1246,6 +1250,10 @@ protected function getWorkspaceListItems( // todo this throws "No workspace is assigned to the user with id" for the case user logs first into workspace module before workspace exists!!! $userWorkspace = $this->workspaceService->getPersonalWorkspaceForUser($contentRepository->id, $userWorkspaceOwner->getId()); $userWorkspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepository->id, $userWorkspace->workspaceName); + $workspaceRoleAssignments = $this->workspaceService->getWorkspaceRoleAssignments( + $contentRepository->id, + $userWorkspace->workspaceName + ); $userWorkspacesPermissions = $this->authorizationService->getWorkspacePermissions( $contentRepository->id, $userWorkspace->workspaceName, @@ -1265,11 +1273,13 @@ protected function getWorkspaceListItems( !$allWorkspaces->getDependantWorkspaces($userWorkspace->workspaceName)->isEmpty(), $userWorkspaceOwner->getLabel(), $userWorkspacesPermissions, + $workspaceRoleAssignments, ); // add other, accessible workspaces foreach ($allWorkspaces as $workspace) { $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepository->id, $workspace->workspaceName); + $workspaceRoleAssignments = $this->workspaceService->getWorkspaceRoleAssignments($contentRepository->id, $workspace->workspaceName); $workspacesPermissions = $this->authorizationService->getWorkspacePermissions( $contentRepository->id, $workspace->workspaceName, @@ -1303,6 +1313,7 @@ protected function getWorkspaceListItems( !$allWorkspaces->getDependantWorkspaces($workspace->workspaceName)->isEmpty(), $workspaceOwner?->getLabel(), $workspacesPermissions, + $workspaceRoleAssignments, ); } return WorkspaceListItems::fromArray($workspaceListItems); diff --git a/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php b/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php index 248357a1e83..10faa8443bd 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php @@ -15,7 +15,10 @@ namespace Neos\Workspace\Ui\ViewModel; use Neos\Flow\Annotations as Flow; +use Neos\Neos\Domain\Model\WorkspaceClassification; use Neos\Neos\Domain\Model\WorkspacePermissions; +use Neos\Neos\Domain\Model\WorkspaceRole; +use Neos\Neos\Domain\Model\WorkspaceRoleAssignments; #[Flow\Proxy(false)] final readonly class WorkspaceListItem @@ -33,6 +36,39 @@ public function __construct( // todo check if necessary, only for personal workspaces that others have permissions to public ?string $owner, public WorkspacePermissions $permissions, + public WorkspaceRoleAssignments $roleAssignments, ) { } + + public function isPersonal(): bool + { + return $this->classification === WorkspaceClassification::PERSONAL->value; + } + + public function isPrivate(): bool + { + if ($this->classification !== WorkspaceClassification::SHARED->value || + $this->roleAssignments->count() > 1) { + return false; + } + foreach ($this->roleAssignments as $roleAssignment) { + if ($roleAssignment->role === WorkspaceRole::COLLABORATOR) { + return false; + } + } + return true; + } + + public function isShared(): bool + { + if ($this->roleAssignments->count() > 1) { + return true; + } + foreach ($this->roleAssignments as $roleAssignment) { + if ($roleAssignment->role === WorkspaceRole::COLLABORATOR) { + return true; + } + } + return false; + } } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Icon.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Icon.fusion index 321236dd664..756dedff4ac 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Icon.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Icon.fusion @@ -3,6 +3,7 @@ prototype(Neos.Workspace.Ui:Component.Icon) < prototype(Neos.Fusion:Component) { secondaryIcon = '' spin = false rotate = false + // TODO: Support colors style = '' renderer = afx` diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion index e2b231a0d8c..04899315a5a 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion @@ -13,24 +13,13 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} workspace = ${props.workspaceListItem.workspace} workspaceTableRowId = ${'workspace-row-' + props.workspaceListItem.name} - // todo move state calculation to php workspaceStatus = Neos.Fusion:Case { - personal-workspace { - condition = ${props.workspaceListItem.name == props.userWorkspaceName} - renderer = 'personal-workspace' - } - stale { - // TODO: Calculate stale status based on last change data - condition = false - renderer = 'stale' - } - withAcl { - // TODO: Calculate acl status on whether the workspace is shared with selected users - condition = false - renderer = 'with-acl' + personal { + condition = ${props.workspaceListItem.personal} + renderer = 'personal' } private { - condition = ${props.workspaceListItem.owner} + condition = ${props.workspaceListItem.private} renderer = 'private' } default { @@ -39,18 +28,16 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion } } workspaceStatusLabel = Neos.Fusion:Match { - // todo status private doesnt exist anymore @subject = ${private.workspaceStatus} - @default = ${private.i18n.id('table.column.access.internal').value('Internal workspace')} - personal-workspace = ${private.i18n.id('badge.isPersonalWorkspace').value('This is your personal workspace')} - stale = ${private.i18n.id('badge.isStale').value('This workspace has not been used for a long time')} - with-acl = ${private.i18n.id('table.column.access.acl').value('This workspace is owned by ' + props.workspaceListItem.owner + ' but allows access to additional users')} - private = ${private.i18n.id('table.column.access.private').value('This workspace is owned by ' + props.workspaceListItem.owner)} + @default = ${private.i18n.id('table.column.access.shared')} + personal = ${private.i18n.id('table.column.access.personal').arguments([props.workspaceListItem.owner])} + private = ${private.i18n.id('table.column.access.private')} } workspaceStatusIcon = Neos.Fusion:Match { @subject = ${private.workspaceStatus} @default = 'user' shared = 'users' + private = 'user-shield' with-acl = 'user-plus' } reviewWorkspaceUri = Neos.Fusion:ActionUri { diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Create.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Create.fusion index 1688e9c5ddf..6ca1a371958 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Create.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Create.fusion @@ -91,10 +91,10 @@ prototype(Neos.Workspace.Ui:Component.Modal.Create) < prototype(Neos.Fusion:Comp field.name="visibility" field.value="shared" > - {private.i18n.id('workspaces.workspace.visibility.internal')} + {private.i18n.id('workspaces.workspace.visibility.shared')}

- {private.i18n.id('workspaces.workspace.visibility.internal.help')} + {private.i18n.id('workspaces.workspace.visibility.shared.help')}


+ - -

- {' '} +
+

+ + {' '} {private.i18n.id('workspaces.cantChangeBaseWorkspaceBecauseWorkspaceContainsChanges')}

@@ -123,10 +121,10 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Compon field.name="visibility" field.value="shared" > - {private.i18n.id('workspaces.workspace.visibility.internal')} + {private.i18n.id('workspaces.workspace.visibility.shared')}

- {private.i18n.id('workspaces.workspace.visibility.internal.help')} + {private.i18n.id('workspaces.workspace.visibility.shared.help')}


Private workspace مجال العمل الخاص - - Internal workspace + + Shared workspace فضاء العمل الداخلي @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace فضاء العمل هذا لا يمكن الوصول إليه وتعديله إلا من طرف المراجعين والمسؤولين فقط - - Internal + + Shared داخلي - + Any logged in editor can see and modify this workspace. يمكن لأي محرر تسجيل الدخول رؤية وتعديل فضاء العمل هذا. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/cs/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/cs/Main.xlf index 4f0d177bfb2..8e473662d88 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/cs/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/cs/Main.xlf @@ -59,9 +59,9 @@ Private workspace Private workspace - - Internal workspace - Internal workspace + + Shared workspace + Shared workspace Read-only workspace @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Only reviewers and administrators can access and modify this workspace - - Internal - Internal + + Shared + Shared - + Any logged in editor can see and modify this workspace. Any logged in editor can see and modify this workspace. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/da/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/da/Main.xlf index fcd1ef9604e..fa0c330dafc 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/da/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/da/Main.xlf @@ -59,8 +59,8 @@ Private workspace Privat arbejdsrum - - Internal workspace + + Shared workspace Internt arbejdsrum @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Kun korrekturlæsere og administratorer kan få adgang til og ændre dette arbejdsrum - - Internal + + Shared Internt - + Any logged in editor can see and modify this workspace. Enhver redaktør som er logget ind kan se og ændre dette arbejdsrum. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/de/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/de/Main.xlf index 2b56aa46d05..cb17c88104a 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/de/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/de/Main.xlf @@ -59,8 +59,8 @@ Private workspace Privater Arbeitsbereich - - Internal workspace + + Shared workspace Interner Arbeitsbereich @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Nur Reviewer und Administratoren können auf diesen Arbeitsbereich zugreifen und ihn bearbeiten - - Internal + + Shared Intern - + Any logged in editor can see and modify this workspace. Jeder angemeldete Redakteur kann diesen Arbeitsbereich sehen und bearbeiten. @@ -269,6 +269,18 @@ Select all current changes Alle aktuellen Änderungen auswählen + + This is a shared workspace + Dies ist ein gemeinsamer Arbeitsbereich + + + This is a private workspace + Dies ist ein privater Arbeitsbereich + + + This is the personal workspace of "{0}" + Dies ist der persönliche Arbeitsbereich von "{0}" + diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/el/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/el/Main.xlf index b52d42417cb..c62edb246b9 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/el/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/el/Main.xlf @@ -59,9 +59,9 @@ Private workspace Private workspace - - Internal workspace - Internal workspace + + Shared workspace + Shared workspace Read-only workspace @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Only reviewers and administrators can access and modify this workspace - - Internal + + Shared Εσωτερικό - + Any logged in editor can see and modify this workspace. Any logged in editor can see and modify this workspace. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf index d114888b4d5..f3e9d49367b 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf @@ -57,8 +57,8 @@ Private workspace - - Internal workspace + + Shared workspace Read-only workspace @@ -126,10 +126,10 @@ Only reviewers and administrators can access and modify this workspace - - Internal + + Shared - + Any logged in editor can see and modify this workspace. @@ -299,6 +299,15 @@ You do not have permission to see this workspace + + This is a shared workspace + + + This is a private workspace + + + This is the personal workspace of "{0}" + diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/es/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/es/Main.xlf index 1447062369f..0700abae071 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/es/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/es/Main.xlf @@ -59,8 +59,8 @@ Private workspace Espacio de trabajo privado - - Internal workspace + + Shared workspace Espacio de trabajo interno @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Solo los revisores y administradores pueden acceder y modificar este espacio de trabajo - - Internal + + Shared Interno - + Any logged in editor can see and modify this workspace. Cualquiera que inicie sesión en el editor puede ver y modificar este espacio de trabajo. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/fi/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/fi/Main.xlf index 4b686ba606f..a9f77b58bf3 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/fi/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/fi/Main.xlf @@ -59,8 +59,8 @@ Private workspace Yksityinen työtila - - Internal workspace + + Shared workspace Sisäinen työtila @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Ainoastaan arvioijat ja pääkäyttäjät voivat käyttää ja muokata tätä työtilaa - - Internal + + Shared Sisäinen - + Any logged in editor can see and modify this workspace. Kaikki sisäänkirjautuneet julkaisijat näkevät tämän työtilan ja voivat muokata sitä. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/fr/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/fr/Main.xlf index 9bae769a3ab..d3aa9c7c9cc 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/fr/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/fr/Main.xlf @@ -59,8 +59,8 @@ Private workspace Espace de travail privé - - Internal workspace + + Shared workspace Espace de travail interne @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Seulement les relecteurs et les administrateurs peuvent accéder et modifier cet espace de travail - - Internal + + Shared Interne - + Any logged in editor can see and modify this workspace. N'importe quel éditeur connecté peut voir et modifier cet espace de travail. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/hu/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/hu/Main.xlf index e77621b6658..a8ef0c3cbdd 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/hu/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/hu/Main.xlf @@ -59,8 +59,8 @@ Private workspace Privát munkafelület - - Internal workspace + + Shared workspace Belső munkafelület @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Csak moderátorok és adminisztrátoroknak van engedélye a tartalom szerkesztéséhez ezen a munkafelületen - - Internal + + Shared Belső - + Any logged in editor can see and modify this workspace. Minden bejelentkezett szerkesztő láthatja és módosíthatja a munkafelületet. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/id_ID/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/id_ID/Main.xlf index e2208d1b025..30e28a4f233 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/id_ID/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/id_ID/Main.xlf @@ -59,9 +59,9 @@ Private workspace Bidang kerja Privat - - Internal workspace - Bidang kerja internal + + Shared workspace + Bidang kerja shared Read-only workspace @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Hanya pengulas dan administrator yang dapat mengakses dan memodifikasi bidang kerja ini - - Internal - Internal + + Shared + Shared - + Any logged in editor can see and modify this workspace. Editor login yang dapat melihat dan mengubah bidang kerja ini. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/it/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/it/Main.xlf index e263dbdbd25..df86b5f092c 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/it/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/it/Main.xlf @@ -59,8 +59,8 @@ Private workspace Spazio di lavoro privato - - Internal workspace + + Shared workspace Spazio di lavoro interno @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Solo i revisori e gli amministratori possono accedere e modificare questo spazio di lavoro - - Internal + + Shared Interno - + Any logged in editor can see and modify this workspace. Ogni editore loggato può vedere e modificare questo spazio di lavoro. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/ja/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/ja/Main.xlf index 771aed88384..963d9240b32 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/ja/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/ja/Main.xlf @@ -59,8 +59,8 @@ Private workspace 民間のワークスペース - - Internal workspace + + Shared workspace 内部のワークスペース @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace のみ応募には管理者アクセスし、修正するこのワークスペース - - Internal + + Shared 内部 - + Any logged in editor can see and modify this workspace. 他のログインエディタで見をいつでも変更することが含まれます。 diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/km/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/km/Main.xlf index ae3342c7b9a..3ab0f2680d6 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/km/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/km/Main.xlf @@ -59,8 +59,8 @@ Private workspace តំបន់ការងារឯកជន - - Internal workspace + + Shared workspace តំបន់ការងារផ្ទៃក្នុង @@ -96,11 +96,11 @@ Only reviewers and administrators can access and modify this workspace មាន​តែអ្នក​ត្រួត​ពិនិត្យនិងអ្នកគ្រប់គ្រងអាចចូលដំណើរការនិងកែប្រែតំបន់ការងារនេះ - - Internal + + Shared ផ្ទៃក្នុង - + Any logged in editor can see and modify this workspace. រាល់ការចូលទេនៅក្នុងកម្មវិធីនិពន្ធអាចមើលឃើញនិងកែប្រែតំបន់ការងារនេះ diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/lv/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/lv/Main.xlf index 650d7922d09..c5e733d8343 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/lv/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/lv/Main.xlf @@ -59,8 +59,8 @@ Private workspace Privāta darba virsma - - Internal workspace + + Shared workspace Iekšējā darba virsma @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Tikai recenzenti un administratori var piekļūt un modificēt šo darba virsmu - - Internal + + Shared Iekšēji - + Any logged in editor can see and modify this workspace. Autorizēts redaktors var redzēt un rediģēt šo darba virsmu. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/nl/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/nl/Main.xlf index 4417d0fff6a..57b1c1bf665 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/nl/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/nl/Main.xlf @@ -51,8 +51,8 @@ Private workspace Privé workspace - - Internal workspace + + Shared workspace Interne workspace @@ -87,11 +87,11 @@ Only reviewers and administrators can access and modify this workspace Alleen reviewers en administrators hebben toegang en kunnen wijzigingen aanbrengen in deze workspace - - Internal + + Shared Intern - + Any logged in editor can see and modify this workspace. Iedere ingelogde redacteur kan deze workspace bekijken en wijzigen. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/no/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/no/Main.xlf index 2190e1ba404..d3d127af595 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/no/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/no/Main.xlf @@ -59,8 +59,8 @@ Private workspace Privat arbeidsområde - - Internal workspace + + Shared workspace Internt arbeidsområde @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Bare korrekturlesere og administratorer kan få tilgang til og endre dette arbeidsområdet - - Internal + + Shared Intern - + Any logged in editor can see and modify this workspace. Alle påloggede redaktører kan se og endre dette arbeidsområdet. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/pl/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/pl/Main.xlf index 02c192ab401..57d358f0d87 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/pl/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/pl/Main.xlf @@ -59,8 +59,8 @@ Private workspace Prywatny obszar roboczy - - Internal workspace + + Shared workspace Wewnętrzny obszar roboczy @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Tylko recenzenci i administratorzy mogą otwierać i modyfikować ten obszar roboczy - - Internal + + Shared Wewnętrzny - + Any logged in editor can see and modify this workspace. Każdy zalogowany redaktor może zobaczyć i modyfikować ten obszar roboczy. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/pt/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/pt/Main.xlf index e5546413fc7..1a11ef1017f 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/pt/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/pt/Main.xlf @@ -59,8 +59,8 @@ Private workspace Área de trabalho privada - - Internal workspace + + Shared workspace Área de trabalho interna @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Apenas os revisores e os administradores podem aceder e modificar esta área de trabalho - - Internal + + Shared Interno - + Any logged in editor can see and modify this workspace. Qualquer editor pode ver e modificar esta área de trabalho. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/pt_BR/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/pt_BR/Main.xlf index 657ecb3c219..d69b93eaf8a 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/pt_BR/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/pt_BR/Main.xlf @@ -59,8 +59,8 @@ Private workspace Espaço de trabalho privado - - Internal workspace + + Shared workspace Espaço de trabalho interno @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Apenas os revisores e os administradores podem acessar e modificar este espaço de trabalho - - Internal + + Shared Interno - + Any logged in editor can see and modify this workspace. Qualquer editor logado pode ver e modificar este espaço de trabalho. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/ru/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/ru/Main.xlf index 6d3ff3831e7..f3478465495 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/ru/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/ru/Main.xlf @@ -59,8 +59,8 @@ Private workspace Приватная рабочая область - - Internal workspace + + Shared workspace Внутренняя рабочая область @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Только рецензенты и администраторы имеют доступ к этой рабочей области и могут изменять её - - Internal + + Shared Внутренняя - + Any logged in editor can see and modify this workspace. Любой вошедший в систему редактор может просматривать и редактировать эту рабочую область. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/sr/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/sr/Main.xlf index 24f4182c0ac..b59c208d8aa 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/sr/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/sr/Main.xlf @@ -59,9 +59,9 @@ Private workspace Private workspace - - Internal workspace - Internal workspace + + Shared workspace + Shared workspace Read-only workspace @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Only reviewers and administrators can access and modify this workspace - - Internal - Internal + + Shared + Shared - + Any logged in editor can see and modify this workspace. Any logged in editor can see and modify this workspace. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/sv/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/sv/Main.xlf index e268756ca14..d437d0e1c22 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/sv/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/sv/Main.xlf @@ -59,8 +59,8 @@ Private workspace Privat arbetsyta - - Internal workspace + + Shared workspace Intern arbetsyta @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Endast förhandsgranskare och administratörer kan komma åt och ändra den här arbetsytan - - Internal + + Shared Internt - + Any logged in editor can see and modify this workspace. En inloggade redigerare kan se och ändra den här arbetsytan. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/tl_PH/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/tl_PH/Main.xlf index 689160914ac..59ea912d751 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/tl_PH/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/tl_PH/Main.xlf @@ -59,9 +59,9 @@ Private workspace Private workspace - - Internal workspace - Internal workspace + + Shared workspace + Shared workspace Read-only workspace @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Only reviewers and administrators can access and modify this workspace - - Internal + + Shared Panloob - + Any logged in editor can see and modify this workspace. Any logged in editor can see and modify this workspace. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/tr/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/tr/Main.xlf index 027eabc46df..3a900426176 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/tr/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/tr/Main.xlf @@ -59,8 +59,8 @@ Private workspace Özel çalışma alanı - - Internal workspace + + Shared workspace İç çalışma alanı @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Bu çalışma alanına yalnızca inceleyiciler ve yöneticiler erişebilir ve değiştirebilir - - Internal + + Shared Dahili - + Any logged in editor can see and modify this workspace. Oturum açmış herhangi bir düzenleyici bu çalışma alanını görebilir ve değiştirebilir. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/uk/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/uk/Main.xlf index 55fc034cc0b..a54fd268511 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/uk/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/uk/Main.xlf @@ -59,8 +59,8 @@ Private workspace Приватне робоче середовище - - Internal workspace + + Shared workspace Внутрішнє робоче середовище @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Робоча область тільки для рецензентів і адміністраторів - - Internal + + Shared Внутрішній - + Any logged in editor can see and modify this workspace. Будь-хто зареєстрований в редакторі може бачити та змінювати робоче середовище. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/vi/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/vi/Main.xlf index f2671f863a6..24f06209f02 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/vi/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/vi/Main.xlf @@ -59,9 +59,9 @@ Private workspace Không gian làm việc riêng - - Internal workspace - Internal workspace + + Shared workspace + Shared workspace Read-only workspace @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace Chỉ có người đánh giá và quản trị viên có thể truy cập và chỉnh sửa không gian làm việc này - - Internal + + Shared Nội bộ - + Any logged in editor can see and modify this workspace. Any logged in editor can see and modify this workspace. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/zh/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/zh/Main.xlf index 8f73159f416..1cd31a4e05f 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/zh/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/zh/Main.xlf @@ -59,8 +59,8 @@ Private workspace 私有工作区 - - Internal workspace + + Shared workspace 内部工作区 @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace 只有审阅者和管理员可以访问和修改此工作区 - - Internal - Internal + + Shared + Shared - + Any logged in editor can see and modify this workspace. Any logged in editor can see and modify this workspace. diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/zh_TW/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/zh_TW/Main.xlf index df4624c22c2..53238365792 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/zh_TW/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/zh_TW/Main.xlf @@ -59,8 +59,8 @@ Private workspace 私人工作區 - - Internal workspace + + Shared workspace 內部工作區 @@ -95,11 +95,11 @@ Only reviewers and administrators can access and modify this workspace 只有審查者和管理員可以連結和修改此工作區 - - Internal + + Shared 內部的 - + Any logged in editor can see and modify this workspace. 任何登入的編輯皆可檢視和修改此工作區。 From 4678c5d16cb709a9cc5a03717063a3281e50e853 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Thu, 19 Dec 2024 13:43:52 +0100 Subject: [PATCH 099/122] TASK: Only match htmx pattern for workspace module --- Neos.Workspace.Ui/Classes/Mvc/HtmxRequestPattern.php | 2 +- Neos.Workspace.Ui/Configuration/Settings.Flow.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Mvc/HtmxRequestPattern.php b/Neos.Workspace.Ui/Classes/Mvc/HtmxRequestPattern.php index 04b190f90e6..356c6b994e7 100644 --- a/Neos.Workspace.Ui/Classes/Mvc/HtmxRequestPattern.php +++ b/Neos.Workspace.Ui/Classes/Mvc/HtmxRequestPattern.php @@ -21,6 +21,6 @@ final class HtmxRequestPattern implements RequestPatternInterface { public function matchRequest(ActionRequest $request): bool { - return $request->getFormat() === 'htmx'; + return $request->getFormat() === 'htmx' && $request->getControllerPackageKey() === 'Neos.Workspace.Ui'; } } diff --git a/Neos.Workspace.Ui/Configuration/Settings.Flow.yaml b/Neos.Workspace.Ui/Configuration/Settings.Flow.yaml index 91a652d7d24..c13ff742363 100644 --- a/Neos.Workspace.Ui/Configuration/Settings.Flow.yaml +++ b/Neos.Workspace.Ui/Configuration/Settings.Flow.yaml @@ -3,7 +3,7 @@ Neos: mvc: flashMessages: containers: - 'httpHeaderFlashMessages': + 'Neos.Workspace.Ui:httpHeaderFlashMessages': storage: 'Neos\Workspace\Ui\Mvc\HttpHeaderFlashMessageStorage' requestPatterns: 'htmx': From 49d23d4a844840ba6514c83ced7e39c3c98e9cc9 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Thu, 19 Dec 2024 14:43:04 +0100 Subject: [PATCH 100/122] BUGFIX: Disable modifying personal workspaces in workspace list --- .../Features/Workspace/Components/WorkspaceTableRow.fusion | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion index 04899315a5a..4ab28a9f0d7 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion @@ -121,13 +121,13 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion attributes.hx-target='#popover-container' attributes.hx-swap='innerHTML' attributes.hx-on--after-request={'document.getElementById("' + private.editWorkspacePopoverId + '").showPopover()'} - attributes.disabled={props.workspaceListItem.permissions.manage == false} + attributes.disabled={props.workspaceListItem.personal || props.workspaceListItem.permissions.manage == false} /> 0 || props.workspaceListItem.permissions.manage == false} + attributes.disabled={props.workspaceListItem.personal || props.workspaceListItem.pendingChanges.total > 0 || props.workspaceListItem.permissions.manage == false} attributes.hx-get={private.deleteWorkspaceUri} attributes.hx-target='#popover-container' attributes.hx-swap='innerHTML' From 22a564ecea8edfba7da72dd1c71f8f7921f5f083 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Thu, 19 Dec 2024 15:02:51 +0100 Subject: [PATCH 101/122] FEATURE: Allow modifying basic workspace visibility in edit dialog --- .../Controller/WorkspaceController.php | 41 ++++++++++++++++++- .../ViewModel/EditWorkspaceFormData.php | 1 + .../Classes/ViewModel/WorkspaceListItem.php | 6 +-- .../Features/Workspace/Actions/Edit.fusion | 2 +- .../Features/Workspace/Modals/Edit.fusion | 18 +++----- .../Resources/Public/Styles/Module.css | 1 + 6 files changed, 50 insertions(+), 19 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 29c58667e57..add9dc837b5 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -47,7 +47,10 @@ use Neos\Neos\Domain\Model\User; use Neos\Neos\Domain\Model\WorkspaceClassification; use Neos\Neos\Domain\Model\WorkspaceDescription; +use Neos\Neos\Domain\Model\WorkspaceRole; +use Neos\Neos\Domain\Model\WorkspaceRoleAssignment; use Neos\Neos\Domain\Model\WorkspaceRoleAssignments; +use Neos\Neos\Domain\Model\WorkspaceRoleSubject; use Neos\Neos\Domain\Model\WorkspaceTitle; use Neos\Neos\Domain\NodeLabel\NodeLabelGeneratorInterface; use Neos\Neos\Domain\Repository\SiteRepository; @@ -301,6 +304,15 @@ public function editAction(WorkspaceName $workspaceName): void } $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspace->workspaceName); + $workspaceRoleAssignments = $this->workspaceService->getWorkspaceRoleAssignments($contentRepositoryId, $workspace->workspaceName); + $isShared = false; + if ($workspaceMetadata->classification === WorkspaceClassification::SHARED) { + foreach ($workspaceRoleAssignments as $roleAssignment) { + if ($roleAssignment->role === WorkspaceRole::COLLABORATOR) { + $isShared = true; + } + } + } $editWorkspaceDto = new EditWorkspaceFormData( workspaceName: $workspace->workspaceName, @@ -309,6 +321,7 @@ public function editAction(WorkspaceName $workspaceName): void workspaceHasChanges: $this->computePendingChanges($workspace, $contentRepository)->total > 0, baseWorkspaceName: $workspace->baseWorkspaceName, baseWorkspaceOptions: $this->prepareBaseWorkspaceOptions($contentRepository, $workspaceName), + isShared: $isShared, ); $this->view->assign('editWorkspaceFormData', $editWorkspaceDto); @@ -321,13 +334,15 @@ public function editAction(WorkspaceName $workspaceName): void * @param WorkspaceName $workspaceName The name of the workspace that is being updated * @param WorkspaceTitle $title Human friendly title of the workspace, for example "Christmas Campaign" * @param WorkspaceDescription $description A description explaining the purpose of the new workspace - * @param WorkspaceName $baseWorkspace A description explaining the purpose of the new workspace + * @param WorkspaceName $baseWorkspace The base workspace to rebase this workspace onto if modified + * @param string $visibility Allow other editors to collaborate on this workspace if set to "shared" */ public function updateAction( WorkspaceName $workspaceName, WorkspaceTitle $title, WorkspaceDescription $description, WorkspaceName $baseWorkspace, + string $visibility, ): void { $currentUser = $this->userService->getCurrentUser(); if ($currentUser === null) { @@ -369,6 +384,30 @@ public function updateAction( $description, ); + $workspaceRoleAssignments = $this->workspaceService->getWorkspaceRoleAssignments($contentRepositoryId, $workspaceName); + $sharedRoleAssignment = WorkspaceRoleAssignment::createForGroup( + 'Neos.Neos:AbstractEditor', + WorkspaceRole::COLLABORATOR, + ); + if ($visibility === 'shared') { + if (!$workspaceRoleAssignments->contains($sharedRoleAssignment)) { + $this->workspaceService->assignWorkspaceRole( + $contentRepositoryId, + $workspaceName, + WorkspaceRoleAssignment::createForGroup( + 'Neos.Neos:AbstractEditor', + WorkspaceRole::COLLABORATOR, + ) + ); + } + } elseif ($visibility === 'private' && $workspaceRoleAssignments->contains($sharedRoleAssignment)) { + $this->workspaceService->unassignWorkspaceRole( + $contentRepositoryId, + $workspaceName, + WorkspaceRoleSubject::createForGroup('Neos.Neos:AbstractEditor'), + ); + } + // Update Base Workspace $this->workspacePublishingService->changeBaseWorkspace( $contentRepositoryId, diff --git a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php index c31f6e9f2b0..977937c5c46 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php @@ -32,6 +32,7 @@ public function __construct( public bool $workspaceHasChanges, public WorkspaceName $baseWorkspaceName, public array $baseWorkspaceOptions, + public bool $isShared, ) { } } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php b/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php index 10faa8443bd..999c422c7cc 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php @@ -47,8 +47,7 @@ public function isPersonal(): bool public function isPrivate(): bool { - if ($this->classification !== WorkspaceClassification::SHARED->value || - $this->roleAssignments->count() > 1) { + if ($this->classification !== WorkspaceClassification::SHARED->value) { return false; } foreach ($this->roleAssignments as $roleAssignment) { @@ -61,9 +60,6 @@ public function isPrivate(): bool public function isShared(): bool { - if ($this->roleAssignments->count() > 1) { - return true; - } foreach ($this->roleAssignments as $roleAssignment) { if ($roleAssignment->role === WorkspaceRole::COLLABORATOR) { return true; diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Edit.fusion index 33cd1b2ca57..805d829c88f 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Edit.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Edit.fusion @@ -10,7 +10,7 @@ Neos.Workspace.Ui.WorkspaceController.edit = Neos.Fusion:Component { baseWorkspaceName={props.editWorkspaceFormData.baseWorkspaceName.value} workspaceHasChanges={props.editWorkspaceFormData.workspaceHasChanges} baseWorkspaceOptions={props.editWorkspaceFormData.baseWorkspaceOptions} - visibility={props.editWorkspaceFormData.visibility} + isShared={props.editWorkspaceFormData.isShared} /> ` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Edit.fusion index 0bf0a44541f..a156231c01c 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Edit.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Edit.fusion @@ -7,12 +7,12 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Compon workspaceDescription = '' /// string baseWorkspaceName = '' - /// string - visibility = '' /// boolean workspaceHasChanges = false /// array baseWorkspaceOptions = ${[]} + /// boolean + isShared = false @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} @@ -50,6 +50,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Compon - + {private.i18n.id('workspaces.workspace.visibility.shared')}

{private.i18n.id('workspaces.workspace.visibility.shared.help')}


- + {private.i18n.id('workspaces.workspace.visibility.private')}

diff --git a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css index 3eff7cb812c..8e01ee7aeef 100644 --- a/Neos.Workspace.Ui/Resources/Public/Styles/Module.css +++ b/Neos.Workspace.Ui/Resources/Public/Styles/Module.css @@ -31,6 +31,7 @@ .workspace-type-column { padding: 0 0.5ch; text-align: center; + width: 20px; } .workspace-type-column > * { From dfd86e34b4e269c95668db73ef8efac32c5671e3 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Fri, 20 Dec 2024 16:30:22 +0100 Subject: [PATCH 102/122] TASK: Use button disabled prop --- .../Private/Fusion/Common/Presentationals/Button.fusion | 3 ++- .../Features/Review/Components/ReviewActions.fusion | 8 ++++---- .../Review/Components/ReviewDocumentTableRow.fusion | 4 ++-- .../Workspace/Components/WorkspaceTableRow.fusion | 6 +++--- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Button.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Button.fusion index a9c0037d7da..fe74214c64a 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Button.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Common/Presentationals/Button.fusion @@ -11,7 +11,7 @@ prototype(Neos.Workspace.Ui:Component.Button) < prototype(Neos.Fusion:Component) isPrimary = false ///boolean isSuccess = false - /// boolean TODO: unused api? + /// boolean disabled = false /// string icon = '' @@ -27,6 +27,7 @@ prototype(Neos.Workspace.Ui:Component.Button) < prototype(Neos.Fusion:Component) class={['neos-button', props.isDanger && 'neos-button-danger', props.isWarning && 'neos-button-warning', props.isPrimary && 'neos-button-primary', props.isSuccess && 'neos-button-success']} autofocus autofocus.@if={props.autofocus} + disabled.@if={props.disabled} {...props.attributes} > diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion index 4db5f7165bf..c13cf4929ea 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewActions.fusion @@ -68,21 +68,21 @@ prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Com label={private.i18n.id('workspaces.discardAllChanges')} icon="fas fa-trash-alt icon-white" isDanger + disabled={!props.canPublishToWorkspace} attributes.hx-get={private.confirmDiscardAllChanges} attributes.hx-target='#popover-container' attributes.hx-swap='innerHTML' attributes.hx-on--after-request={'document.getElementById("' + private.discardAllChangesPopoverId + '").showPopover()'} - attributes.disabled={!props.canPublishToWorkspace} />

@@ -90,21 +90,21 @@ prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Com label={private.i18n.id('workspaces.discardSelectedChanges')} icon="fas fa-trash-alt icon-white" isDanger + disabled={!props.canPublishToBaseWorkspace} attributes.hx-get={private.confirmDiscardSelectedChanges} attributes.hx-target='#popover-container' attributes.hx-swap='innerHTML' attributes.hx-on--after-request={'document.getElementById("' + private.discardSelectedChangesPopoverId + '").showPopover()'} - attributes.disabled={!props.canPublishToBaseWorkspace} />
diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion index 5787ca56d79..c6c3b29a022 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion @@ -93,21 +93,21 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F title={props.canPublishToWorkspace ? private.i18n.id('workspaces.discardAllChangesInThisDocument'): private.i18n.id('workspaces.changes.noPermissionToPublishToWorkspace')} icon="trash-alt icon-white" isDanger + disabled={!props.canPublishToWorkspace } attributes.hx-get={private.discardDocumentAction} attributes.hx-select="#workspace-module-content" attributes.hx-target="#workspace-module-content" attributes.hx-swap="outerHTML" - attributes.disabled={!props.canPublishToWorkspace } /> 0 || props.workspaceListItem.permissions.manage == false} + disabled={props.workspaceListItem.personal || props.workspaceListItem.pendingChanges.total > 0 || props.workspaceListItem.permissions.manage == false} attributes.hx-get={private.deleteWorkspaceUri} attributes.hx-target='#popover-container' attributes.hx-swap='innerHTML' From 0e4660bf703af1c69b8770ead9ce74e4ed172781 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Fri, 20 Dec 2024 16:30:35 +0100 Subject: [PATCH 103/122] TASK: Solve todos from WorkspaceListItem --- Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php b/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php index 999c422c7cc..a731f0ad506 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php @@ -25,7 +25,6 @@ { public function __construct( public string $name, - // todo unused!! public string $classification, public string $status, public string $title, @@ -33,7 +32,6 @@ public function __construct( public ?string $baseWorkspaceName, public PendingChanges $pendingChanges, public bool $hasDependantWorkspaces, - // todo check if necessary, only for personal workspaces that others have permissions to public ?string $owner, public WorkspacePermissions $permissions, public WorkspaceRoleAssignments $roleAssignments, From 93f13174c331d375f47b58fa6fa5c8d5900d898b Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Fri, 20 Dec 2024 16:45:13 +0100 Subject: [PATCH 104/122] TASK: Remove broken rebaseAndRedirect functionality We have to find out if we want to keep the feature and implement it in a more sensible way --- .../Controller/WorkspaceController.php | 59 ------------------- .../Components/ReviewDocumentTableRow.fusion | 14 ----- 2 files changed, 73 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index add9dc837b5..7876e8e1401 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -516,65 +516,6 @@ public function deleteAction(WorkspaceName $workspaceName): void } } - /** - * Rebase the current users personal workspace onto the given $targetWorkspace and then - * redirects to the $targetNode in the content module. - */ - public function rebaseAndRedirectAction(string $targetNode, WorkspaceName $targetWorkspaceName): void - { - $targetNodeAddress = NodeAddress::fromJsonString( - $targetNode - ); - $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); - - $targetWorkspace = $contentRepository->findWorkspaceByName($targetWorkspaceName); - - $user = $this->userService->getCurrentUser(); - if ($user === null) { - throw new \RuntimeException('No account is authenticated', 1710068880); - } - $personalWorkspace = $this->workspaceService->getPersonalWorkspaceForUser($targetNodeAddress->contentRepositoryId, $user->getId()); - - /** @todo do something else - * if ($personalWorkspace !== $targetWorkspace) { - * if ($this->publishingService->getUnpublishedNodesCount($personalWorkspace) > 0) { - * $message = $this->getModuleLabel( - * 'workspaces.cantEditBecauseWorkspaceContainsChanges', - * ); - * $this->addFlashMessage($message, '', Message::SEVERITY_WARNING, [], 1437833387); - * $this->redirect('show', null, null, ['workspace' => $targetWorkspace]); - * } - * $personalWorkspace->setBaseWorkspace($targetWorkspace); - * $this->workspaceFinder->update($personalWorkspace); - * } - */ - - $targetNodeAddressInPersonalWorkspace = NodeAddress::create( - $targetNodeAddress->contentRepositoryId, - $personalWorkspace->workspaceName, - $targetNodeAddress->dimensionSpacePoint, - $targetNodeAddress->aggregateId - ); - - if ($this->packageManager->isPackageAvailable('Neos.Neos.Ui')) { - $mainRequest = $this->controllerContext->getRequest()->getMainRequest(); - $this->uriBuilder->setRequest($mainRequest); - - $this->redirect( - 'index', - 'Backend', - 'Neos.Neos.Ui', - ['node' => $targetNodeAddressInPersonalWorkspace->toJson()] - ); - } - - $this->redirectToUri( - $this->nodeUriBuilderFactory->forActionRequest($this->request) - ->uriFor($targetNodeAddressInPersonalWorkspace) - ); - } - /** * Publish a single document node */ diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion index c6c3b29a022..6497a08b249 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Review/Components/ReviewDocumentTableRow.fusion @@ -13,13 +13,6 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} - openNodeInWorkspaceAction = Neos.Fusion:ActionUri { - action = 'rebaseAndRedirect' - arguments { - targetWorkspaceName = ${selectedWorkspaceName} - targetNode = ${document.document.documentNodeAddress} - } - } openNodeInPreviewAction = Neos.Fusion:ActionUri { request=${request.mainRequest} package='Neos.Neos' @@ -110,13 +103,6 @@ prototype(Neos.Workspace.Ui:Component.ReviewDocumentTableRow) < prototype(Neos.F attributes.hx-swap="outerHTML" /> - - - Date: Fri, 20 Dec 2024 16:54:40 +0100 Subject: [PATCH 105/122] BUGFIX: Hide empty tag changes and improve label --- Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php | 2 +- Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 7876e8e1401..52a3f1c32b2 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -941,7 +941,7 @@ protected function renderContentChanges( $actualOriginalTags = $originalNode?->tags->withoutInherited()->all(); $actualChangedTags = $changedNode->tags->withoutInherited()->all(); - if ($actualOriginalTags?->equals($actualChangedTags)) { + if ($actualOriginalTags?->equals($actualChangedTags) === false) { $contentChanges['tags'] = new ContentChangeItem( properties: new ContentChangeProperties( type: 'tags', diff --git a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf index f3e9d49367b..0af84808b38 100644 --- a/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf +++ b/Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf @@ -288,7 +288,7 @@ The workspace "{0}" has been created.
- New element status + New element tags You do not have permission to publish to the base workspace From 1e06fbe1c2344179ce973c0e62f5b61d2626bba9 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Fri, 20 Dec 2024 17:06:33 +0100 Subject: [PATCH 106/122] TASK: Use workspace classification to handle personal workspaces in the ui --- .../Controller/WorkspaceController.php | 46 ++----------------- .../Features/Workspace/Actions/Index.fusion | 3 -- .../Features/Workspace/Actions/Update.fusion | 3 -- .../Components/WorkspaceTable.fusion | 3 -- .../Components/WorkspaceTableRow.fusion | 4 +- .../Components/WorkspaceTreeNode.fusion | 4 -- 6 files changed, 4 insertions(+), 59 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 52a3f1c32b2..f61c98ad7ea 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -156,15 +156,13 @@ public function indexAction(string $sortBy = 'title', bool $sortAscending = true $this->view->assign('displayContentRepositorySelector', $numberOfContentRepositories > 1); $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); - $workspaceListItems = $this->getWorkspaceListItems($contentRepository, $currentUser); + $workspaceListItems = $this->getWorkspaceListItems($contentRepository); if ($sortBy === 'title') { $workspaceListItems = $workspaceListItems->sortByTitle($sortAscending); } $this->view->assignMultiple([ - // todo remove userWorkspaceName field and add distinction to $workspaceListItems as $workspaceListItems->userWorkspace and $workspaceListItems->otherWorkspaces or something. - 'userWorkspaceName' => $this->workspaceService->getPersonalWorkspaceForUser($contentRepositoryId, $currentUser->getId())->workspaceName->value, 'workspaceListItems' => $workspaceListItems, 'flashMessages' => $this->controllerContext->getFlashMessageContainer()->getMessagesAndFlush(), 'sortAscending' => $sortAscending, @@ -422,10 +420,9 @@ public function updateAction( ) ); - $workspaceListItems = $this->getWorkspaceListItems($contentRepository, $currentUser); + $workspaceListItems = $this->getWorkspaceListItems($contentRepository); $this->view->assignMultiple([ - 'userWorkspaceName' => $this->workspaceService->getPersonalWorkspaceForUser($contentRepositoryId, $currentUser->getId())->workspaceName->value, 'workspaceListItems' => $workspaceListItems, ]); } @@ -433,9 +430,6 @@ public function updateAction( /** * Delete a workspace * - * TODO: Add force delete option to ignore unpublished nodes or dependent workspaces, the later should be rebased instead - * - * @param WorkspaceName $workspaceName A workspace to delete * @throws StopActionException */ public function deleteAction(WorkspaceName $workspaceName): void @@ -499,7 +493,7 @@ public function deleteAction(WorkspaceName $workspaceName): void ); $this->addFlashMessage($message, '', Message::SEVERITY_WARNING); $this->throwStatus(403, 'Workspace has unpublished nodes'); - // delete workspace on POST -> todo make this more FLOW-ig by possibly having a DeleteController with post() and get() _or_ by having deleteAction_post and deleteAction_get?? Or a separate action? + // delete workspace on POST -> TODO: Split this into 2 actions like the create or edit workflows } elseif ($this->request->getHttpRequest()->getMethod() === 'POST') { $this->workspaceService->deleteWorkspace($contentRepositoryId, $workspaceName); @@ -967,15 +961,11 @@ protected function renderContentChanges( $originalPropertyValue = ($originalNode?->getProperty($propertyName)); if ($changedPropertyValue === $originalPropertyValue) { - // TODO && !$changedNode->isRemoved() continue; } if (!is_object($originalPropertyValue) && !is_object($changedPropertyValue)) { $originalSlimmedDownContent = $this->renderSlimmedDownContent($originalPropertyValue); - // TODO $changedSlimmedDownContent = $changedNode->isRemoved() - // ? '' - // : $this->renderSlimmedDownContent($changedPropertyValue); $changedSlimmedDownContent = $this->renderSlimmedDownContent($changedPropertyValue); $diff = new Diff( @@ -1223,38 +1213,9 @@ public function getModuleLabel(string $id, array $arguments = [], mixed $quantit protected function getWorkspaceListItems( ContentRepository $contentRepository, - User $userWorkspaceOwner, ): WorkspaceListItems { $workspaceListItems = []; $allWorkspaces = $contentRepository->findWorkspaces(); - // todo this throws "No workspace is assigned to the user with id" for the case user logs first into workspace module before workspace exists!!! - $userWorkspace = $this->workspaceService->getPersonalWorkspaceForUser($contentRepository->id, $userWorkspaceOwner->getId()); - $userWorkspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepository->id, $userWorkspace->workspaceName); - $workspaceRoleAssignments = $this->workspaceService->getWorkspaceRoleAssignments( - $contentRepository->id, - $userWorkspace->workspaceName - ); - $userWorkspacesPermissions = $this->authorizationService->getWorkspacePermissions( - $contentRepository->id, - $userWorkspace->workspaceName, - $this->securityContext->getRoles(), - $userWorkspaceOwner->getId() - ); - - // add user workspace first - $workspaceListItems[$userWorkspace->workspaceName->value] = new WorkspaceListItem( - $userWorkspace->workspaceName->value, - $userWorkspaceMetadata->classification->value, - $userWorkspace->status->value, - $userWorkspaceMetadata->title->value, - $userWorkspaceMetadata->description->value, - $userWorkspace->baseWorkspaceName?->value, - $this->computePendingChanges($userWorkspace, $contentRepository), - !$allWorkspaces->getDependantWorkspaces($userWorkspace->workspaceName)->isEmpty(), - $userWorkspaceOwner->getLabel(), - $userWorkspacesPermissions, - $workspaceRoleAssignments, - ); // add other, accessible workspaces foreach ($allWorkspaces as $workspace) { @@ -1272,7 +1233,6 @@ protected function getWorkspaceListItems( continue; } - // TODO use owner/WorkspaceRoleAssignment? // TODO: If user is allowed to edit child workspace, we need to at least show the parent workspaces in the list if ($workspacesPermissions->read === false) { continue; diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion index 8b40bf735fd..0f5a3e25a9d 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion @@ -1,6 +1,4 @@ Neos.Workspace.Ui.WorkspaceController.index = Neos.Fusion:Component { - /// string - userWorkspaceName = ${userWorkspaceName} /// Neos\Workspace\Ui\ViewModel\WorkspaceListItems workspaceListItems = ${workspaceListItems} /// array @@ -26,7 +24,6 @@ Neos.Workspace.Ui.WorkspaceController.index = Neos.Fusion:Component {
` diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion index 6fce74c637d..0b94bcfa8bb 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion @@ -2,8 +2,6 @@ # Renders a list of workspaces # prototype(Neos.Workspace.Ui:Component.WorkspaceTable) < prototype(Neos.Fusion:Component) { - /// string - userWorkspaceName = null /// \Neos\Workspace\Ui\ViewModel\WorkspaceListItems workspaceListItems = ${{}} /// string @@ -48,7 +46,6 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTable) < prototype(Neos.Fusion:Co diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion index 338f6883e2b..287e3402f2e 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTableRow.fusion @@ -2,8 +2,6 @@ # Renders a single workspace list item # prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion:Component) { - /// string - userWorkspaceName = null /// Neos\Workspace\Ui\ViewModel\WorkspaceListItem workspaceListItem = ${[]} /// integer @@ -76,7 +74,7 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTableRow) < prototype(Neos.Fusion 1}/> {props.workspaceListItem.title} - + ({private.i18n.id('workspaces.workspace.userWorkspace')}) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTreeNode.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTreeNode.fusion index 5a2e8f169b4..da573e40134 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTreeNode.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTreeNode.fusion @@ -2,8 +2,6 @@ # Renders a single workspace list item and its subworkspaces # prototype(Neos.Workspace.Ui:Component.WorkspaceTreeNode) < prototype(Neos.Fusion:Component) { - /// string - userWorkspaceName = null /// Neos\Workspace\Ui\ViewModel\WorkspaceListItem workspaceListItem = null /// Neos\Workspace\Ui\ViewModel\WorkspaceListItems @@ -18,14 +16,12 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTreeNode) < prototype(Neos.Fusion renderer = afx` Date: Fri, 20 Dec 2024 17:11:37 +0100 Subject: [PATCH 107/122] TASK: Remove unfinished CR switch functionality --- .../Controller/WorkspaceController.php | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index f61c98ad7ea..0d940d823ff 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -139,25 +139,10 @@ public function indexAction(string $sortBy = 'title', bool $sortAscending = true throw new \RuntimeException('No user authenticated', 1718308216); } - $contentRepositoryIds = $this->contentRepositoryRegistry->getContentRepositoryIds(); - $numberOfContentRepositories = $contentRepositoryIds->count(); - if ($numberOfContentRepositories === 0) { - throw new \RuntimeException('No content repository configured', 1718296290); - } - if ($this->request->hasArgument('contentRepositoryId')) { - $contentRepositoryIdArgument = $this->request->getArgument('contentRepositoryId'); - assert(is_string($contentRepositoryIdArgument)); - $contentRepositoryId = ContentRepositoryId::fromString($contentRepositoryIdArgument); - } else { - $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; - } - $this->view->assign('contentRepositoryIds', $contentRepositoryIds); - $this->view->assign('contentRepositoryId', $contentRepositoryId->value); - $this->view->assign('displayContentRepositorySelector', $numberOfContentRepositories > 1); - + $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); - $workspaceListItems = $this->getWorkspaceListItems($contentRepository); + $workspaceListItems = $this->getWorkspaceListItems($contentRepository); if ($sortBy === 'title') { $workspaceListItems = $workspaceListItems->sortByTitle($sortAscending); } From 36e6dafb42dc6e7103142c3e4a086303010b84a2 Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Fri, 20 Dec 2024 17:15:49 +0100 Subject: [PATCH 108/122] TASK: Remove user stories --- .../Documentation/UserStories.rst | 87 ------------------- Neos.Workspace.Ui/Readme.rst | 5 -- 2 files changed, 92 deletions(-) delete mode 100644 Neos.Workspace.Ui/Documentation/UserStories.rst diff --git a/Neos.Workspace.Ui/Documentation/UserStories.rst b/Neos.Workspace.Ui/Documentation/UserStories.rst deleted file mode 100644 index 788e829b0bd..00000000000 --- a/Neos.Workspace.Ui/Documentation/UserStories.rst +++ /dev/null @@ -1,87 +0,0 @@ ----------------------------------------- -The workspace module feature description ----------------------------------------- - -This document describes the user stories for the workspace management module in Neos CMS. -If new features are added to the module, they should be described as user stories in this document. -If features are removed or changed, the user stories should be updated accordingly. -The user stories should be written in a way that is understandable to non-technical users. - -List workspaces ---------------- - -As an editor, I want to see a list of workspaces, so that I can see which workspaces are available and what their status is. - -The list should show the following information for each workspace: - -* The workspace title / name -* A description of the workspace to explain its purpose -* The last modification date of its content (TODO) -* The creator of the workspace (TODO) -* The status of the workspace (e.g. "Published", "Stale", "Outdated") -* The number of pending changes in the workspace and their type (e.g. "New content", "Modified content", "Deleted content") -* A list of actions that can be performed on the workspace (e.g. "Review", "Edit", "Delete") - -As an administrator or workspace manager I want to be able to see which workspaces are actively used, so that I can clean up unused or stale workspaces. -The list should therefore be sortable by the last modification date of the workspace and stale workspaces visually highlighted. - -Additional requirements: -######################## - -* The list should work well with 100 workspaces -* The list should be able to show nested workspaces with up to 4 levels of nesting - - -Create a new workspace ----------------------- - -As an editor, I want to be able to create a new workspace, so that I can work on changes without affecting the live site. - -When creating a new workspace, I should be able to specify the following information: - -* The title of the workspace -* A description of the workspace to explain its purpose -* The parent workspace that the new workspace should be based on -* The initial visibility of the workspace (e.g. "Shared", "Private") - -Advanced configuration can be done after the workspace has been created. - -Edit a workspace ----------------- - -As an editor, I want to be able to edit the properties of a workspace, -so that I can update its title, description, parent workspace, and visibility. - -When editing a workspace, I should be able to change the following information: - -* The title of the workspace -* A description of the workspace to explain its purpose -* The parent workspace that the workspace should be based on -* The visibility of the workspace (e.g. "Shared", "Private") and its access control list (ACL) - -Changing the base workspace -########################### - -When changing the parent workspace, the user should be able to choose from a list of possible workspaces. - -* If the edited workspace has sub-workspaces (shared, personal or private) that are based on the edited workspace, -the user should be warned that the changes will affect the sub-workspaces as well. The names of the affected workspaces -should be displayed if possible. -* If the user has no access to the sub-workspaces, the user see the number of affected workspaces. - -Review a workspace ------------------- - -As an editor, I want to be able to review the changes in a workspace, so that I can see what has been changed -and decide whether to publish the changes to the live site. - -Delete a workspace ------------------- - -As an editor, I want to be able to delete a workspace, so that I can clean up unused or stale workspaces. - -When deleting a workspace, the user should be warned that the action cannot be undone and that all changes in the workspace will be lost. -The number of pending changes in the workspace should be displayed to help the user decide whether to delete the workspace. -When the workspace has sub-workspaces, the user should be warned that the sub-workspaces will be deleted as well. -The names of the affected workspaces should be displayed if possible. -If the user has no access to the sub-workspaces, the user see the number of affected workspaces. diff --git a/Neos.Workspace.Ui/Readme.rst b/Neos.Workspace.Ui/Readme.rst index a3d0dea86e3..65935b43e5b 100644 --- a/Neos.Workspace.Ui/Readme.rst +++ b/Neos.Workspace.Ui/Readme.rst @@ -7,11 +7,6 @@ The Neos CMS workspace management module If you want to use Neos, please have a look at the `Neos documentation `_ -User stories ------------- - -See :file:`Documentation/UserStories.rst`. - Contribute ---------- From 775713cab505ba432795c1a9f3ce26a4979ae0ac Mon Sep 17 00:00:00 2001 From: Sebastian Helzle Date: Fri, 20 Dec 2024 17:16:17 +0100 Subject: [PATCH 109/122] TASK: Solve code style issue --- .../Classes/Controller/WorkspaceController.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 0d940d823ff..9be0334ab73 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -706,9 +706,13 @@ protected function computePendingChanges(Workspace $selectedWorkspace, ContentRe { $changesCount = ['new' => 0, 'changed' => 0, 'removed' => 0]; foreach($this->getChangesFromWorkspace($selectedWorkspace, $contentRepository) as $change) { - if($change->deleted) $changesCount['removed']++; - elseif($change->created) $changesCount['new']++; - else $changesCount['changed']++; + if ($change->deleted) { + $changesCount['removed']++; + } elseif ($change->created) { + $changesCount['new']++; + } else { + $changesCount['changed']++; + } } return new PendingChanges(new: $changesCount['new'], changed: $changesCount['changed'], removed:$changesCount['removed']); } From c4eb48fb00f72f5f88af74f7368c6a2d13e477d1 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Mon, 6 Jan 2025 11:53:20 +0100 Subject: [PATCH 110/122] BUGFIX: Handle NodeAggregateNameWasChanged or NodeAggregateTypeWasChanged changes This hack originally made in 4fe6b983a32bf559c6a78f5e74038c4d9de16a78 got lost due to parallel work and is herby reintroduced --- .../Classes/Controller/WorkspaceController.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 9be0334ab73..4f3035efa95 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -16,6 +16,7 @@ use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\Dimension\ContentDimensionId; +use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTag; use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Exception\WorkspaceAlreadyExists; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Exception\WorkspaceRebaseFailed; @@ -23,7 +24,6 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\Projection\ContentGraph\Nodes; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; -use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Node\NodeAddress; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; @@ -44,7 +44,6 @@ use Neos\Media\Domain\Model\AssetInterface; use Neos\Media\Domain\Model\ImageInterface; use Neos\Neos\Controller\Module\AbstractModuleController; -use Neos\Neos\Domain\Model\User; use Neos\Neos\Domain\Model\WorkspaceClassification; use Neos\Neos\Domain\Model\WorkspaceDescription; use Neos\Neos\Domain\Model\WorkspaceRole; @@ -725,11 +724,13 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos { $siteChanges = []; $changes = $this->getChangesFromWorkspace($selectedWorkspace, $contentRepository); + + // TODO hack for the case $change->originDimensionSpacePoint is NULL so we can fetch a subgraph still. This is the case for changes NodeAggregateNameWasChanged or NodeAggregateTypeWasChanged + $dimensionSpacePoints = iterator_to_array($contentRepository->getVariationGraph()->getDimensionSpacePoints()); + /** @var DimensionSpacePoint $arbitraryDimensionSpacePoint */ + $arbitraryDimensionSpacePoint = reset($dimensionSpacePoints); + foreach ($changes as $change) { - if ($change->originDimensionSpacePoint === null) { - // todo implement support for change node type!!! Because originDimensionSpacePoint is null currently for that case. - continue; - } $workspaceName = $selectedWorkspace->workspaceName; if ($change->deleted) { // If we deleted a node, there is no way for us to anymore find the deleted node in the ContentStream @@ -741,7 +742,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos $workspaceName = $baseWorkspace->workspaceName; } $subgraph = $contentRepository->getContentGraph($workspaceName)->getSubgraph( - $change->originDimensionSpacePoint->toDimensionSpacePoint(), + $change->originDimensionSpacePoint?->toDimensionSpacePoint() ?? $arbitraryDimensionSpacePoint, VisibilityConstraints::withoutRestrictions() ); @@ -837,7 +838,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos $nodeAddress = NodeAddress::create( $contentRepository->id, $selectedWorkspace->workspaceName, - $change->originDimensionSpacePoint->toDimensionSpacePoint(), + $change->originDimensionSpacePoint?->toDimensionSpacePoint() ?? $arbitraryDimensionSpacePoint, $change->nodeAggregateId ); $nodeType = $contentRepository->getNodeTypeManager()->getNodeType($node->nodeTypeName); From 0e17338d5206b99b848b3d50e107cc3ebe23628a Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Mon, 6 Jan 2025 12:14:28 +0100 Subject: [PATCH 111/122] TASK: Simplify code and remove `ContentChange` class --- .../Classes/ViewModel/ContentChangeItem.php | 8 +++++-- .../ContentChanges/AssetContentChange.php | 2 +- .../ContentChanges/ContentChange.php | 22 ------------------- .../ContentChanges/DateTimeContentChange.php | 2 +- .../ContentChanges/ImageContentChange.php | 2 +- .../ContentChanges/TagContentChange.php | 2 +- .../ContentChanges/TextContentChange.php | 2 +- 7 files changed, 11 insertions(+), 29 deletions(-) delete mode 100644 Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/ContentChange.php diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ContentChangeItem.php b/Neos.Workspace.Ui/Classes/ViewModel/ContentChangeItem.php index 0551451d37b..ccc1c6cabdd 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ContentChangeItem.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ContentChangeItem.php @@ -15,14 +15,18 @@ namespace Neos\Workspace\Ui\ViewModel; use Neos\Flow\Annotations as Flow; -use Neos\Workspace\Ui\ViewModel\ContentChanges\ContentChange; +use Neos\Workspace\Ui\ViewModel\ContentChanges\AssetContentChange; +use Neos\Workspace\Ui\ViewModel\ContentChanges\DateTimeContentChange; +use Neos\Workspace\Ui\ViewModel\ContentChanges\ImageContentChange; +use Neos\Workspace\Ui\ViewModel\ContentChanges\TagContentChange; +use Neos\Workspace\Ui\ViewModel\ContentChanges\TextContentChange; #[Flow\Proxy(false)] readonly class ContentChangeItem { public function __construct( public ContentChangeProperties $properties, - public ContentChange $changes, + public AssetContentChange|DateTimeContentChange|ImageContentChange|TagContentChange|TextContentChange $changes, ) { } } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/AssetContentChange.php b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/AssetContentChange.php index a2d92f33425..eebf482505d 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/AssetContentChange.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/AssetContentChange.php @@ -18,7 +18,7 @@ use Neos\Media\Domain\Model\AssetInterface; #[Flow\Proxy(false)] -final readonly class AssetContentChange extends ContentChange +final readonly class AssetContentChange { public function __construct( public ?AssetInterface $original, diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/ContentChange.php b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/ContentChange.php deleted file mode 100644 index 8a45a458293..00000000000 --- a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/ContentChange.php +++ /dev/null @@ -1,22 +0,0 @@ - $addedTags diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TextContentChange.php b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TextContentChange.php index bfbabf6135c..c6f7661d39a 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TextContentChange.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TextContentChange.php @@ -17,7 +17,7 @@ use Neos\Flow\Annotations as Flow; #[Flow\Proxy(false)] -final readonly class TextContentChange extends ContentChange +final readonly class TextContentChange { /** @param array $diff */ public function __construct( From 18c386099db368d297a2a07c4c12633316504ee4 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Mon, 6 Jan 2025 12:19:43 +0100 Subject: [PATCH 112/122] TASK: Declare all php classes in `Neos.Workspace.Ui` as internal like in the Neos.Ui --- Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php | 2 ++ Neos.Workspace.Ui/Classes/Mvc/HtmxRequestPattern.php | 3 +++ .../Classes/Mvc/HttpHeaderFlashMessageStorage.php | 3 +++ Neos.Workspace.Ui/Classes/ViewModel/ChangeItem.php | 3 +++ Neos.Workspace.Ui/Classes/ViewModel/ChangeItems.php | 1 + Neos.Workspace.Ui/Classes/ViewModel/ContentChangeItem.php | 3 +++ Neos.Workspace.Ui/Classes/ViewModel/ContentChangeItems.php | 1 + .../Classes/ViewModel/ContentChangeProperties.php | 3 +++ .../Classes/ViewModel/ContentChanges/AssetContentChange.php | 3 +++ .../Classes/ViewModel/ContentChanges/DateTimeContentChange.php | 3 +++ .../Classes/ViewModel/ContentChanges/ImageContentChange.php | 3 +++ .../Classes/ViewModel/ContentChanges/TagContentChange.php | 3 +++ .../Classes/ViewModel/ContentChanges/TextContentChange.php | 3 +++ Neos.Workspace.Ui/Classes/ViewModel/DocumentChangeItem.php | 3 +++ Neos.Workspace.Ui/Classes/ViewModel/DocumentItem.php | 3 +++ Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php | 3 +++ Neos.Workspace.Ui/Classes/ViewModel/PendingChanges.php | 3 +++ Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php | 3 +++ Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItems.php | 1 + 19 files changed, 50 insertions(+) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 4f3035efa95..0ad2f88a345 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -81,6 +81,8 @@ /** * The Neos Workspace module controller + * + * @internal for communication within the Workspace UI only */ #[Flow\Scope('singleton')] class WorkspaceController extends AbstractModuleController diff --git a/Neos.Workspace.Ui/Classes/Mvc/HtmxRequestPattern.php b/Neos.Workspace.Ui/Classes/Mvc/HtmxRequestPattern.php index 356c6b994e7..f85bae459d0 100644 --- a/Neos.Workspace.Ui/Classes/Mvc/HtmxRequestPattern.php +++ b/Neos.Workspace.Ui/Classes/Mvc/HtmxRequestPattern.php @@ -17,6 +17,9 @@ use Neos\Flow\Mvc\ActionRequest; use Neos\Flow\Security\RequestPatternInterface; +/** + * @internal for communication within the Workspace UI only + */ final class HtmxRequestPattern implements RequestPatternInterface { public function matchRequest(ActionRequest $request): bool diff --git a/Neos.Workspace.Ui/Classes/Mvc/HttpHeaderFlashMessageStorage.php b/Neos.Workspace.Ui/Classes/Mvc/HttpHeaderFlashMessageStorage.php index 715dfe1d738..4a7690bfd22 100644 --- a/Neos.Workspace.Ui/Classes/Mvc/HttpHeaderFlashMessageStorage.php +++ b/Neos.Workspace.Ui/Classes/Mvc/HttpHeaderFlashMessageStorage.php @@ -20,6 +20,9 @@ use Psr\Http\Message\ResponseInterface as HttpResponseInterface; use Psr\Http\Message\ServerRequestInterface as HttpRequestInterface; +/** + * @internal for communication within the Workspace UI only + */ final class HttpHeaderFlashMessageStorage implements FlashMessageStorageInterface { private FlashMessageContainer|null $flashMessageContainer = null; diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ChangeItem.php b/Neos.Workspace.Ui/Classes/ViewModel/ChangeItem.php index f644f0ec410..ec962f36eb8 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ChangeItem.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ChangeItem.php @@ -16,6 +16,9 @@ use Neos\Flow\Annotations as Flow; +/** + * @internal for communication within the Workspace UI only + */ #[Flow\Proxy(false)] final readonly class ChangeItem { diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ChangeItems.php b/Neos.Workspace.Ui/Classes/ViewModel/ChangeItems.php index 97c70e7eab9..4c6f685df6a 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ChangeItems.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ChangeItems.php @@ -18,6 +18,7 @@ /** * @implements \IteratorAggregate + * @internal for communication within the Workspace UI only */ #[Flow\Proxy(false)] final readonly class ChangeItems implements \IteratorAggregate, \Countable diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ContentChangeItem.php b/Neos.Workspace.Ui/Classes/ViewModel/ContentChangeItem.php index ccc1c6cabdd..9e55703f668 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ContentChangeItem.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ContentChangeItem.php @@ -21,6 +21,9 @@ use Neos\Workspace\Ui\ViewModel\ContentChanges\TagContentChange; use Neos\Workspace\Ui\ViewModel\ContentChanges\TextContentChange; +/** + * @internal for communication within the Workspace UI only + */ #[Flow\Proxy(false)] readonly class ContentChangeItem { diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ContentChangeItems.php b/Neos.Workspace.Ui/Classes/ViewModel/ContentChangeItems.php index 67d0c6f5a2d..2c4cf1b6c61 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ContentChangeItems.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ContentChangeItems.php @@ -18,6 +18,7 @@ /** * @implements \IteratorAggregate + * @internal for communication within the Workspace UI only */ #[Flow\Proxy(false)] final readonly class ContentChangeItems implements \IteratorAggregate, \Countable diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ContentChangeProperties.php b/Neos.Workspace.Ui/Classes/ViewModel/ContentChangeProperties.php index 1b543ba029a..33ebb1fadc8 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ContentChangeProperties.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ContentChangeProperties.php @@ -16,6 +16,9 @@ use Neos\Flow\Annotations as Flow; +/** + * @internal for communication within the Workspace UI only + */ #[Flow\Proxy(false)] readonly class ContentChangeProperties { diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/AssetContentChange.php b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/AssetContentChange.php index eebf482505d..14d8a894c75 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/AssetContentChange.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/AssetContentChange.php @@ -17,6 +17,9 @@ use Neos\Flow\Annotations as Flow; use Neos\Media\Domain\Model\AssetInterface; +/** + * @internal for communication within the Workspace UI only + */ #[Flow\Proxy(false)] final readonly class AssetContentChange { diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/DateTimeContentChange.php b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/DateTimeContentChange.php index 31e39093650..4e19ef68986 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/DateTimeContentChange.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/DateTimeContentChange.php @@ -16,6 +16,9 @@ use Neos\Flow\Annotations as Flow; +/** + * @internal for communication within the Workspace UI only + */ #[Flow\Proxy(false)] final readonly class DateTimeContentChange { diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/ImageContentChange.php b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/ImageContentChange.php index e473850b93b..08e17bdaffe 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/ImageContentChange.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/ImageContentChange.php @@ -17,6 +17,9 @@ use Neos\Flow\Annotations as Flow; use Neos\Media\Domain\Model\ImageInterface; +/** + * @internal for communication within the Workspace UI only + */ #[Flow\Proxy(false)] final readonly class ImageContentChange { diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TagContentChange.php b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TagContentChange.php index ba0a0970830..3be57585bf5 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TagContentChange.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TagContentChange.php @@ -16,6 +16,9 @@ use Neos\Flow\Annotations as Flow; +/** + * @internal for communication within the Workspace UI only + */ #[Flow\Proxy(false)] final readonly class TagContentChange { diff --git a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TextContentChange.php b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TextContentChange.php index c6f7661d39a..35e4e3d5de9 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TextContentChange.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/ContentChanges/TextContentChange.php @@ -16,6 +16,9 @@ use Neos\Flow\Annotations as Flow; +/** + * @internal for communication within the Workspace UI only + */ #[Flow\Proxy(false)] final readonly class TextContentChange { diff --git a/Neos.Workspace.Ui/Classes/ViewModel/DocumentChangeItem.php b/Neos.Workspace.Ui/Classes/ViewModel/DocumentChangeItem.php index 9c84c484bde..be1a7514414 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/DocumentChangeItem.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/DocumentChangeItem.php @@ -16,6 +16,9 @@ use Neos\Flow\Annotations as Flow; +/** + * @internal for communication within the Workspace UI only + */ #[Flow\Proxy(false)] readonly class DocumentChangeItem { diff --git a/Neos.Workspace.Ui/Classes/ViewModel/DocumentItem.php b/Neos.Workspace.Ui/Classes/ViewModel/DocumentItem.php index ef1983c0a69..2e1ef38f48e 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/DocumentItem.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/DocumentItem.php @@ -16,6 +16,9 @@ use Neos\Flow\Annotations as Flow; +/** + * @internal for communication within the Workspace UI only + */ #[Flow\Proxy(false)] readonly class DocumentItem { diff --git a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php index 977937c5c46..471e9840538 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/EditWorkspaceFormData.php @@ -19,6 +19,9 @@ use Neos\Neos\Domain\Model\WorkspaceDescription; use Neos\Neos\Domain\Model\WorkspaceTitle; +/** + * @internal for communication within the Workspace UI only + */ #[Flow\Proxy(false)] final readonly class EditWorkspaceFormData { diff --git a/Neos.Workspace.Ui/Classes/ViewModel/PendingChanges.php b/Neos.Workspace.Ui/Classes/ViewModel/PendingChanges.php index b6aafaa8843..4e6dfad1fcf 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/PendingChanges.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/PendingChanges.php @@ -16,6 +16,9 @@ use Neos\Flow\Annotations as Flow; +/** + * @internal for communication within the Workspace UI only + */ #[Flow\Proxy(false)] final readonly class PendingChanges { diff --git a/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php b/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php index a731f0ad506..b3f11d0cc65 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItem.php @@ -20,6 +20,9 @@ use Neos\Neos\Domain\Model\WorkspaceRole; use Neos\Neos\Domain\Model\WorkspaceRoleAssignments; +/** + * @internal for communication within the Workspace UI only + */ #[Flow\Proxy(false)] final readonly class WorkspaceListItem { diff --git a/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItems.php b/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItems.php index 8f476e1fcab..626794cee9e 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItems.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/WorkspaceListItems.php @@ -18,6 +18,7 @@ /** * @implements \IteratorAggregate + * @internal for communication within the Workspace UI only */ #[Flow\Proxy(false)] final readonly class WorkspaceListItems implements \IteratorAggregate, \Countable From 5f6af7ac8fdac7aa879e12b5b4b82f28a28875a1 Mon Sep 17 00:00:00 2001 From: pKallert Date: Tue, 7 Jan 2025 06:59:00 +0100 Subject: [PATCH 113/122] Fix: Remove ToDo --- Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 0ad2f88a345..00a65248626 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -807,8 +807,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos ) ) ); - - //ToDo: Consider dimensions + if(!isset($siteChanges[$siteNodeName]['documents'][$documentPath]['document'])) { $documentNodeAddress = NodeAddress::create( $contentRepository->id, From b55b75a7cbfbe4bc1e0fa2e2307f7f94cf3edb78 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Wed, 8 Jan 2025 13:07:00 +0100 Subject: [PATCH 114/122] TASK: Refine translation of property labels Overhauls b830ee8a2d88be1d6cc81c41667c85f1ddb5dcdd to use the translation helper to avoid duplicating code. This is done also in other places in php already. --- .../Controller/WorkspaceController.php | 35 ++++--------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 00a65248626..11315703e3b 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -34,6 +34,7 @@ use Neos\Diff\Renderer\Html\HtmlArrayRenderer; use Neos\Error\Messages\Message; use Neos\Flow\Annotations as Flow; +use Neos\Flow\I18n\EelHelper\TranslationHelper; use Neos\Flow\I18n\Translator; use Neos\Flow\Mvc\Exception\StopActionException; use Neos\Flow\Package\PackageManager; @@ -807,7 +808,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos ) ) ); - + if(!isset($siteChanges[$siteNodeName]['documents'][$documentPath]['document'])) { $documentNodeAddress = NodeAddress::create( $contentRepository->id, @@ -1059,35 +1060,13 @@ protected function renderSlimmedDownContent(mixed $propertyValue): string protected function getPropertyLabel(string $propertyName, Node $changedNode): string { $properties = $this->getNodeType($changedNode)->getProperties(); - if ( - !isset($properties[$propertyName]) - || !isset($properties[$propertyName]['ui']['label']) - ) { + $label = $properties[$propertyName]['ui']['label'] ?? null; + if ($label === null) { return $propertyName; } - $packageKey = 'Neos.Neos'; - $source = 'Main'; - $id = $properties[$propertyName]['ui']['label']; - $idParts = explode(':', $id, 3); - switch (count($idParts)) { - case 2: - $packageKey = $idParts[0]; - $id = $idParts[1]; - break; - case 3: - $packageKey = $idParts[0]; - $source = str_replace('.', '/', $idParts[1]); - $id = $idParts[2]; - break; - } - return $this->translator->translateById( - $id, - [], - null, - null, - $source, - $packageKey - ) ?: $properties[$propertyName]['ui']['label']; + + // hack, we use the eel helper here to support the shorthand syntax: PackageKey:Source:trans-unit-id + return (new TranslationHelper())->translate($label) ?: $label; } /** From 8a94d89d74ca2eac4f137871728435706fd19b59 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Wed, 8 Jan 2025 13:28:17 +0100 Subject: [PATCH 115/122] BUGFIX: Allow to edit workspaces if they contain changes Because of changes the base workspace will not be selectable > You cannot change the base workspace of workspace with unpublished changes. And thus also not part of the payload which crashes with a bad request (400): > Required argument "baseWorkspace" is not set. Instead, we make this parameter optional. Additionally, we ensure that the base workspace is different before issuing any change. --- .../Classes/Controller/WorkspaceController.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 11315703e3b..ef22bfeadb0 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -319,15 +319,15 @@ public function editAction(WorkspaceName $workspaceName): void * @param WorkspaceName $workspaceName The name of the workspace that is being updated * @param WorkspaceTitle $title Human friendly title of the workspace, for example "Christmas Campaign" * @param WorkspaceDescription $description A description explaining the purpose of the new workspace - * @param WorkspaceName $baseWorkspace The base workspace to rebase this workspace onto if modified * @param string $visibility Allow other editors to collaborate on this workspace if set to "shared" + * @param WorkspaceName|null $baseWorkspace The base workspace to rebase this workspace onto if modified */ public function updateAction( WorkspaceName $workspaceName, WorkspaceTitle $title, WorkspaceDescription $description, - WorkspaceName $baseWorkspace, string $visibility, + WorkspaceName|null $baseWorkspace = null, ): void { $currentUser = $this->userService->getCurrentUser(); if ($currentUser === null) { @@ -393,12 +393,14 @@ public function updateAction( ); } - // Update Base Workspace - $this->workspacePublishingService->changeBaseWorkspace( - $contentRepositoryId, - $workspaceName, - $baseWorkspace - ); + if ($baseWorkspace !== null && !$workspace->baseWorkspaceName->equals($baseWorkspace)) { + // Update Base Workspace + $this->workspacePublishingService->changeBaseWorkspace( + $contentRepositoryId, + $workspaceName, + $baseWorkspace + ); + } $this->addFlashMessage( $this->getModuleLabel( From 65683d86a599d8e5176c507438cbac2908749c05 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Wed, 8 Jan 2025 14:51:17 +0100 Subject: [PATCH 116/122] TASK: Use match to validate $visibility is correct --- .../Controller/WorkspaceController.php | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index ef22bfeadb0..50172276d74 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -231,10 +231,13 @@ public function createAction( $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; $workspaceName = $this->workspaceService->getUniqueWorkspaceName($contentRepositoryId, $title->value); + $assignments = match($visibility) { + 'shared' => WorkspaceRoleAssignments::createForSharedWorkspace($currentUser->getId()), + 'private' => WorkspaceRoleAssignments::createForPrivateWorkspace($currentUser->getId()), + default => throw new \RuntimeException(sprintf('Invalid visibility %s given', $visibility), 1736343542) + }; + try { - $assignments = $visibility === 'shared' ? - WorkspaceRoleAssignments::createForSharedWorkspace($currentUser->getId()) : - WorkspaceRoleAssignments::createForPrivateWorkspace($currentUser->getId()); $this->workspaceService->createSharedWorkspace( $contentRepositoryId, $workspaceName, @@ -374,26 +377,25 @@ public function updateAction( 'Neos.Neos:AbstractEditor', WorkspaceRole::COLLABORATOR, ); - if ($visibility === 'shared') { - if (!$workspaceRoleAssignments->contains($sharedRoleAssignment)) { - $this->workspaceService->assignWorkspaceRole( - $contentRepositoryId, - $workspaceName, - WorkspaceRoleAssignment::createForGroup( - 'Neos.Neos:AbstractEditor', - WorkspaceRole::COLLABORATOR, - ) - ); - } - } elseif ($visibility === 'private' && $workspaceRoleAssignments->contains($sharedRoleAssignment)) { - $this->workspaceService->unassignWorkspaceRole( + + match($visibility) { + 'shared' => !$workspaceRoleAssignments->contains($sharedRoleAssignment) && $this->workspaceService->assignWorkspaceRole( + $contentRepositoryId, + $workspaceName, + WorkspaceRoleAssignment::createForGroup( + 'Neos.Neos:AbstractEditor', + WorkspaceRole::COLLABORATOR, + ) + ), + 'private' => $workspaceRoleAssignments->contains($sharedRoleAssignment) && $this->workspaceService->unassignWorkspaceRole( $contentRepositoryId, $workspaceName, WorkspaceRoleSubject::createForGroup('Neos.Neos:AbstractEditor'), - ); - } + ), + default => throw new \RuntimeException(sprintf('Invalid visibility %s given', $visibility), 1736339457) + }; - if ($baseWorkspace !== null && !$workspace->baseWorkspaceName->equals($baseWorkspace)) { + if ($baseWorkspace !== null && $workspace->baseWorkspaceName?->equals($baseWorkspace) === false) { // Update Base Workspace $this->workspacePublishingService->changeBaseWorkspace( $contentRepositoryId, From 6b046c22f3033c8e8675c9aca68ebaba26a90b6e Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Wed, 8 Jan 2025 16:12:25 +0100 Subject: [PATCH 117/122] TASK: Use value object for `sorting` to simplify codeflow --- .../Controller/WorkspaceController.php | 17 ++++--- .../Classes/ViewModel/Sorting.php | 47 +++++++++++++++++++ .../Features/Workspace/Actions/Index.fusion | 9 ++-- .../Components/WorkspaceTable.fusion | 13 +++-- 4 files changed, 67 insertions(+), 19 deletions(-) create mode 100644 Neos.Workspace.Ui/Classes/ViewModel/Sorting.php diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 50172276d74..ed144bfc762 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -77,6 +77,7 @@ use Neos\Workspace\Ui\ViewModel\DocumentItem; use Neos\Workspace\Ui\ViewModel\EditWorkspaceFormData; use Neos\Workspace\Ui\ViewModel\PendingChanges; +use Neos\Workspace\Ui\ViewModel\Sorting; use Neos\Workspace\Ui\ViewModel\WorkspaceListItem; use Neos\Workspace\Ui\ViewModel\WorkspaceListItems; @@ -134,8 +135,13 @@ class WorkspaceController extends AbstractModuleController /** * Display a list of unpublished content */ - public function indexAction(string $sortBy = 'title', bool $sortAscending = true): void + public function indexAction(Sorting|null $sorting = null): void { + $sorting ??= new Sorting( + sortBy: 'title', + sortAscending: true + ); + $currentUser = $this->userService->getCurrentUser(); if ($currentUser === null) { throw new \RuntimeException('No user authenticated', 1718308216); @@ -145,15 +151,14 @@ public function indexAction(string $sortBy = 'title', bool $sortAscending = true $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); $workspaceListItems = $this->getWorkspaceListItems($contentRepository); - if ($sortBy === 'title') { - $workspaceListItems = $workspaceListItems->sortByTitle($sortAscending); - } + $workspaceListItems = match($sorting->sortBy) { + 'title' => $workspaceListItems->sortByTitle($sorting->sortAscending), + }; $this->view->assignMultiple([ 'workspaceListItems' => $workspaceListItems, 'flashMessages' => $this->controllerContext->getFlashMessageContainer()->getMessagesAndFlush(), - 'sortAscending' => $sortAscending, - 'sortBy' => $sortBy, + 'sorting' => $sorting, ]); } diff --git a/Neos.Workspace.Ui/Classes/ViewModel/Sorting.php b/Neos.Workspace.Ui/Classes/ViewModel/Sorting.php new file mode 100644 index 00000000000..1077eac3074 --- /dev/null +++ b/Neos.Workspace.Ui/Classes/ViewModel/Sorting.php @@ -0,0 +1,47 @@ +sortBy, + sortAscending: !$this->sortAscending + ); + } + + public function jsonSerialize(): mixed + { + return get_object_vars($this); + } + + public function allowsCallOfMethod($methodName) + { + return in_array($methodName, ['withInvertedSorting', 'jsonSerialize'], true); + } +} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion index 0f5a3e25a9d..fc1bf0db025 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Index.fusion @@ -3,10 +3,8 @@ Neos.Workspace.Ui.WorkspaceController.index = Neos.Fusion:Component { workspaceListItems = ${workspaceListItems} /// array flashMessages = ${flashMessages} - /// string - sortBy = ${sortBy} - /// bool - sortAscending = ${sortAscending} + /// Neos\Workspace\Ui\ViewModel\Sorting + sorting = ${sorting} newAction = Neos.Fusion:UriBuilder { action = 'new' @@ -25,8 +23,7 @@ Neos.Workspace.Ui.WorkspaceController.index = Neos.Fusion:Component {
diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion index 0b94bcfa8bb..8149a1fe820 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion @@ -3,11 +3,9 @@ # prototype(Neos.Workspace.Ui:Component.WorkspaceTable) < prototype(Neos.Fusion:Component) { /// \Neos\Workspace\Ui\ViewModel\WorkspaceListItems - workspaceListItems = ${{}} - /// string - sortBy = 'title' - /// bool - sortAscending = true + workspaceListItems = null + /// Neos\Workspace\Ui\ViewModel\Sorting + sorting = null @private { i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')} @@ -15,7 +13,8 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTable) < prototype(Neos.Fusion:Co action = 'index' format = 'html' arguments { - sortAscending = ${!props.sortAscending} + // Todo hack convert to array manually, flows routing chokes on value objects: Tried to convert an object of type "Neos\Workspace\Ui\ViewModel\Sorting" to an identity array, but it is unknown to the Persistence Manager. + sorting = ${sorting.withInvertedSorting().jsonSerialize()} } } } @@ -35,7 +34,7 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTable) < prototype(Neos.Fusion:Co hx-swap="outerHTML" > {private.i18n.id('workspaces.workspace.title').translate()} - + {private.i18n.id('workspaces.workspace.description')} From bfb52da796b4d0ec499dd449258975de5389a9b3 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Wed, 8 Jan 2025 17:18:40 +0100 Subject: [PATCH 118/122] BUGFIX: Fix sorting glitch because the hot swapped WorkspaceTable on update was not sorted ASC by title instead the order was slightly different ... neither ASC nor DESC. to ensure we dont have to deal with that we remove the update response by just using `forward()` letting the index action handle everything correctly --- .../Classes/Controller/WorkspaceController.php | 6 +----- .../Fusion/Features/Workspace/Actions/New.fusion | 16 +++++----------- .../Features/Workspace/Actions/Update.fusion | 13 ------------- .../Fusion/Features/Workspace/Modals/Edit.fusion | 3 +++ 4 files changed, 9 insertions(+), 29 deletions(-) delete mode 100644 Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Update.fusion diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index ed144bfc762..c5c8023b60a 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -416,11 +416,7 @@ public function updateAction( ) ); - $workspaceListItems = $this->getWorkspaceListItems($contentRepository); - - $this->view->assignMultiple([ - 'workspaceListItems' => $workspaceListItems, - ]); + $this->forward('index'); } /** diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/New.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/New.fusion index e74e2f9aa9a..04854a99cdd 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/New.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/New.fusion @@ -2,15 +2,9 @@ Neos.Workspace.Ui.WorkspaceController.new = Neos.Fusion:Component { /// array baseWorkspaceOptions = ${baseWorkspaceOptions} - renderer = Neos.Fusion:Match { - @subject = ${request.httpRequest.method} - # Render the create modal - @default = afx` - - ` - # Empty template for the delete response as the payload is contained in the HTTP headers - POST = '' - } + renderer = afx` + + ` } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Update.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Update.fusion deleted file mode 100644 index d1faf1f9fa6..00000000000 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Actions/Update.fusion +++ /dev/null @@ -1,13 +0,0 @@ -## -# Update response after a workspace has been edited via the edit modal -# -Neos.Workspace.Ui.WorkspaceController.update = Neos.Fusion:Component { - /// Neos\Workspace\Ui\ViewModel\WorkspaceListItems - workspaceListItems = ${workspaceListItems} - - renderer = afx` - - ` -} diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Edit.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Edit.fusion index a156231c01c..9bb88339344 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Edit.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Modals/Edit.fusion @@ -55,6 +55,9 @@ prototype(Neos.Workspace.Ui:Component.Modal.Edit) < prototype(Neos.Fusion:Compon attributes.hx-post={form.getTarget()} attributes.hx-disabled-elt="find button" attributes.hx-on--after-request={'document.getElementById("' + private.popoverId + '").hidePopover()'} + attributes.hx-select="#workspace-module-content" + attributes.hx-target="#workspace-module-content" + attributes.hx-swap="outerHTML" >
From a7bfcf83d24b38d86cfa4855fa49caa6d37922c9 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Wed, 8 Jan 2025 17:45:26 +0100 Subject: [PATCH 119/122] TASK: Simplify initial workspace list rendering for the live case --- .../Features/Workspace/Components/WorkspaceTable.fusion | 1 - .../Workspace/Components/WorkspaceTreeNode.fusion | 9 ++++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion index 8149a1fe820..44c7bf6fff9 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion @@ -45,7 +45,6 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTable) < prototype(Neos.Fusion:Co diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTreeNode.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTreeNode.fusion index da573e40134..076cb303e5c 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTreeNode.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTreeNode.fusion @@ -2,23 +2,22 @@ # Renders a single workspace list item and its subworkspaces # prototype(Neos.Workspace.Ui:Component.WorkspaceTreeNode) < prototype(Neos.Fusion:Component) { - /// Neos\Workspace\Ui\ViewModel\WorkspaceListItem + /// Neos\Workspace\Ui\ViewModel\WorkspaceListItem|null workspaceListItem = null /// Neos\Workspace\Ui\ViewModel\WorkspaceListItems - workspaceListItems = ${{}} + workspaceListItems = null /// int level = 0 @private { - workspaceName = ${props.workspaceListItem ? props.workspaceListItem.name : 'live'} - baseWorkspaceListItems = ${Array.filter(props.workspaceListItems, (workspaceListItem) => workspaceListItem.baseWorkspaceName == private.workspaceName)} + baseWorkspaceListItems = ${Array.filter(props.workspaceListItems, (workspaceListItem) => workspaceListItem.baseWorkspaceName == (props.workspaceListItem.name || 'live'))} } renderer = afx` Date: Wed, 8 Jan 2025 17:53:13 +0100 Subject: [PATCH 120/122] TASK: happy new year phpstan!!! --- Neos.Workspace.Ui/Classes/ViewModel/Sorting.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Neos.Workspace.Ui/Classes/ViewModel/Sorting.php b/Neos.Workspace.Ui/Classes/ViewModel/Sorting.php index 1077eac3074..c9ec1a85b45 100644 --- a/Neos.Workspace.Ui/Classes/ViewModel/Sorting.php +++ b/Neos.Workspace.Ui/Classes/ViewModel/Sorting.php @@ -11,6 +11,9 @@ final readonly class Sorting implements \JsonSerializable, ProtectedContextAwareInterface { public function __construct( + /** + * @phpstan-var 'title' + */ public string $sortBy, public bool $sortAscending ) { @@ -19,6 +22,9 @@ public function __construct( } } + /** + * @param array $array + */ public static function fromArray(array $array): self { return new self( From a7d6cc117b17596a662664fe9d417e0a08b577ee Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Wed, 8 Jan 2025 19:35:08 +0100 Subject: [PATCH 121/122] TASK: simplify workspace tree node fusion integration --- .../Controller/WorkspaceController.php | 34 ++++++++++--------- .../Components/WorkspaceTable.fusion | 1 + .../Components/WorkspaceTreeNode.fusion | 9 +++-- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index c5c8023b60a..daed6f3ac2c 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -219,7 +219,7 @@ public function newAction(): void $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId; $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); - $this->view->assign('baseWorkspaceOptions', $this->prepareBaseWorkspaceOptions($contentRepository)); + $this->view->assign('baseWorkspaceOptions', $this->prepareBaseWorkspaceOptions($contentRepository, null)); } public function createAction( @@ -1104,39 +1104,41 @@ protected function postProcessDiffArray(array &$diffArray): void } /** - * Creates an array of workspace names and their respective titles which are possible base workspaces for other - * workspaces. - * If $excludedWorkspace is set, this workspace and all its base workspaces will be excluded from the list of returned workspaces + * Creates an array of workspace names and their respective titles which are possible base workspaces + * + * If $editedWorkspace is set, this workspace and all its nested workspaces will be excluded from the list of returned workspaces * * @return array */ protected function prepareBaseWorkspaceOptions( ContentRepository $contentRepository, - WorkspaceName $excludedWorkspace = null, + WorkspaceName|null $editedWorkspaceName ): array { $user = $this->userService->getCurrentUser(); $baseWorkspaceOptions = []; $workspaces = $contentRepository->findWorkspaces(); foreach ($workspaces as $workspace) { - if ( - $excludedWorkspace !== null) { - if ($workspace->workspaceName->equals($excludedWorkspace)) { + if ($editedWorkspaceName !== null) { + if ($workspace->workspaceName->equals($editedWorkspaceName)) { continue; } - if ( $workspaces->getBaseWorkspaces($workspace->workspaceName)->get( - $excludedWorkspace - ) !== null) { + if ($workspaces->getBaseWorkspaces($workspace->workspaceName)->get($editedWorkspaceName) !== null) { continue; } } - $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepository->id, $workspace->workspaceName); + $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata( + $contentRepository->id, + $workspace->workspaceName + ); if (!in_array($workspaceMetadata->classification, [WorkspaceClassification::SHARED, WorkspaceClassification::ROOT], true)) { continue; } - if ($user === null) { - continue; - } - $permissions = $this->authorizationService->getWorkspacePermissions($contentRepository->id, $workspace->workspaceName, $this->securityContext->getRoles(), $user->getId()); + $permissions = $this->authorizationService->getWorkspacePermissions( + $contentRepository->id, + $workspace->workspaceName, + $this->securityContext->getRoles(), + $user?->getId() + ); if (!$permissions->read) { continue; } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion index 44c7bf6fff9..d6314f39bed 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion @@ -46,6 +46,7 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTable) < prototype(Neos.Fusion:Co entryWorkspace.baseWorkspaceName == 'live')} /> diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTreeNode.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTreeNode.fusion index 076cb303e5c..78a5c2cccc2 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTreeNode.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTreeNode.fusion @@ -6,23 +6,22 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTreeNode) < prototype(Neos.Fusion workspaceListItem = null /// Neos\Workspace\Ui\ViewModel\WorkspaceListItems workspaceListItems = null + // array + baseWorkspaceListItems = null /// int level = 0 - @private { - baseWorkspaceListItems = ${Array.filter(props.workspaceListItems, (workspaceListItem) => workspaceListItem.baseWorkspaceName == (props.workspaceListItem.name || 'live'))} - } - renderer = afx` - + workspace.baseWorkspaceName == workspaceListItem.name)} level={props.level + 1} /> From f3fe1d08a61d0bbd78eedea1b4d15f1179358bea Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Wed, 8 Jan 2025 19:42:27 +0100 Subject: [PATCH 122/122] BUGFIX: Show workspaces in overview if their parent workspace is not accessible we do this by checking if a workspaces base workspace does not appear in the list of workspaces. This is also true for the live workspace as its not part of the list so the `entryWorkspace.baseWorkspaceName == 'live'` check is now obsolete but it shows better what we are trying to do here for the normal case :D Also the dropdown of base workspaces must always show the current option even if it would not be re changeable in the current permission set from the current user. --- .../Classes/Controller/WorkspaceController.php | 11 ++++++++++- .../Workspace/Components/WorkspaceTable.fusion | 5 ++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index daed6f3ac2c..eb1c909dd17 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -1117,6 +1117,16 @@ protected function prepareBaseWorkspaceOptions( $user = $this->userService->getCurrentUser(); $baseWorkspaceOptions = []; $workspaces = $contentRepository->findWorkspaces(); + $editedWorkspace = $editedWorkspaceName ? $workspaces->get($editedWorkspaceName) : null; + if ($editedWorkspace?->baseWorkspaceName !== null) { + // ensure that the current base workspace is always part of the list even if permissions are not granted + $workspaceMetadata = $this->workspaceService->getWorkspaceMetadata( + $contentRepository->id, + $editedWorkspace->baseWorkspaceName + ); + $baseWorkspaceOptions[$editedWorkspace->baseWorkspaceName->value] = $workspaceMetadata->title->value; + } + foreach ($workspaces as $workspace) { if ($editedWorkspaceName !== null) { if ($workspace->workspaceName->equals($editedWorkspaceName)) { @@ -1210,7 +1220,6 @@ protected function getWorkspaceListItems( continue; } - // TODO: If user is allowed to edit child workspace, we need to at least show the parent workspaces in the list if ($workspacesPermissions->read === false) { continue; } diff --git a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion index d6314f39bed..89d2838df27 100644 --- a/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion +++ b/Neos.Workspace.Ui/Resources/Private/Fusion/Features/Workspace/Components/WorkspaceTable.fusion @@ -46,7 +46,10 @@ prototype(Neos.Workspace.Ui:Component.WorkspaceTable) < prototype(Neos.Fusion:Co entryWorkspace.baseWorkspaceName == 'live')} + baseWorkspaceListItems={Array.filter(props.workspaceListItems, (entryWorkspace) => + entryWorkspace.baseWorkspaceName == 'live' + || !Array.some(props.workspaceListItems, (workspace) => entryWorkspace.baseWorkspaceName == workspace.name) + )} />