diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts index c02baea98e..ece6df083a 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts @@ -442,10 +442,10 @@ export default { stay: 'Bliv', discardChanges: 'Kassér ændringer', unsavedChanges: 'Kassér ugemte ændringer', - unsavedChangesWarning: - 'Er du sikker på du vil navigere væk fra denne side? - du har ikke-gemte\n ændringer\n ', + unsavedChangesWarning: 'Er du sikker på du vil navigere væk fra denne side? - du har ikke-gemte ændringer', confirmListViewPublish: 'Udgivelse vil gøre de valgte sider synlige på sitet.', confirmListViewUnpublish: 'Afpublicering vil fjerne de valgte sider og deres undersider fra sitet.', + confirmPublish: 'Udgivelse vil gøre denne side og alle dets publicerede undersider synlige på websitet.', confirmUnpublish: 'Afpublicering vil fjerne denne side og alle dets undersider fra websitet.', doctypeChangeWarning: 'Du har ikke-gemte ændringer. Hvis du ændrer dokumenttype, kasseres ændringerne.\n ', }, diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts index 2107e98504..89c1abdcef 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts @@ -467,6 +467,7 @@ export default { unsavedChangesWarning: 'Are you sure you want to navigate away from this page? You have unsaved changes', confirmListViewPublish: 'Publishing will make the selected items visible on the site.', confirmListViewUnpublish: 'Unpublishing will remove the selected items and all their descendants from the site.', + confirmPublish: 'Publishing will make this page and all its published descendants visible on the site.', confirmUnpublish: 'Unpublishing will remove this page and all its descendants from the site.', doctypeChangeWarning: 'You have unsaved changes. Making changes to the Document Type will discard the changes.', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/entity-action/publish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/entity-action/publish.action.ts index 2c70cc80a5..f5c79e95ee 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/entity-action/publish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/entity-action/publish.action.ts @@ -62,30 +62,6 @@ export class UmbPublishDocumentEntityAction extends UmbEntityActionBase { }), ); - const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); - if (!actionEventContext) throw new Error('The action event context is missing'); - const event = new UmbRequestReloadStructureForEntityEvent({ - unique: this.args.unique, - entityType: this.args.entityType, - }); - - // If the document has only one variant, we can skip the modal and publish directly: - if (options.length === 1) { - const variantId = UmbVariantId.Create(documentData.variants[0]); - const publishingRepository = new UmbDocumentPublishingRepository(this._host); - const { error } = await publishingRepository.publish(this.args.unique, [{ variantId }]); - if (!error) { - notificationContext?.peek('positive', { - data: { - headline: localize.term('speechBubbles_editContentPublishedHeader'), - message: localize.term('speechBubbles_editContentPublishedText'), - }, - }); - } - actionEventContext.dispatchEvent(event); - return; - } - // Figure out the default selections // TODO: Missing features to pre-select the variant that fits with the variant-id of the tree/collection? (Again only relevant if the action is executed from a Tree or Collection) [NL] const selection: Array = []; @@ -99,6 +75,7 @@ export class UmbPublishDocumentEntityAction extends UmbEntityActionBase { const result = await umbOpenModal(this, UMB_DOCUMENT_PUBLISH_MODAL, { data: { + confirmLabel: '#actions_publish', options, pickableFilter: (option) => { if (!option.culture) return false; @@ -125,19 +102,39 @@ export class UmbPublishDocumentEntityAction extends UmbEntityActionBase { } if (!error) { - const documentVariants = documentData.variants.filter((variant) => result.selection.includes(variant.culture!)); - notificationContext?.peek('positive', { - data: { - headline: localize.term('speechBubbles_editContentPublishedHeader'), - message: localize.term( - 'speechBubbles_editVariantPublishedText', - localize.list(documentVariants.map((v) => v.culture ?? v.name)), - ), - }, - }); + // If the content is invariant, we need to show a different notification + const isInvariant = options.length === 1 && options[0].culture === null; + + if (isInvariant) { + notificationContext?.peek('positive', { + data: { + headline: localize.term('speechBubbles_editContentPublishedHeader'), + message: localize.term('speechBubbles_editContentPublishedText'), + }, + }); + } else { + const documentVariants = documentData.variants.filter((variant) => + result.selection.includes(variant.culture!), + ); + notificationContext?.peek('positive', { + data: { + headline: localize.term('speechBubbles_editContentPublishedHeader'), + message: localize.term( + 'speechBubbles_editVariantPublishedText', + localize.list(documentVariants.map((v) => v.culture ?? v.name)), + ), + }, + }); + } } - actionEventContext.dispatchEvent(event); + const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + const event = new UmbRequestReloadStructureForEntityEvent({ + unique: this.args.unique, + entityType: this.args.entityType, + }); + + actionEventContext?.dispatchEvent(event); } } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/modal/document-publish-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/modal/document-publish-modal.element.ts index 0ffd7f83dc..44a93f0d04 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/modal/document-publish-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/modal/document-publish-modal.element.ts @@ -1,7 +1,7 @@ import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../../../types.js'; import { isNotPublishedMandatory } from '../../utils.js'; import type { UmbDocumentPublishModalData, UmbDocumentPublishModalValue } from './document-publish-modal.token.js'; -import { css, customElement, html, state } from '@umbraco-cms/backoffice/external/lit'; +import { css, customElement, html, state, when } from '@umbraco-cms/backoffice/external/lit'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; @@ -21,6 +21,12 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement< @state() _hasNotSelectedMandatory?: boolean; + @state() + _hasInvalidSelection = true; + + @state() + _isInvariant = false; + #pickableFilter = (option: UmbDocumentVariantOptionModel) => { if (!option.variant || option.variant.state === UmbDocumentVariantState.NOT_CREATED) { return false; @@ -29,6 +35,13 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement< }; override firstUpdated() { + // If invariant, don't display the variant selection component. + if (this.data?.options.length === 1 && this.data.options[0].culture === null) { + this._isInvariant = true; + this._hasInvalidSelection = false; + return; + } + this.#configureSelectionManager(); } @@ -79,7 +92,7 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement< } #submit() { - this.value = { selection: this.#selectionManager.getSelection() }; + this.value = { selection: this._isInvariant ? ['invariant'] : this.#selectionManager.getSelection() }; this.modalContext?.submit(); } @@ -89,19 +102,31 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement< override render() { return html` -

- Which variants would you like to publish? + ${when( + !this._isInvariant, + () => + html`

+ Which variants would you like to publish? +

+ `, + )} + +

+ + Publishing will make this page and all its published descendants visible on the site. +

-
}), ); - const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); - if (!actionEventContext) throw new Error('The action event context is missing'); - const event = new UmbRequestReloadStructureForEntityEvent({ - unique: this.args.unique, - entityType: this.args.entityType, - }); - // Figure out the default selections // TODO: Missing features to pre-select the variant that fits with the variant-id of the tree/collection? (Again only relevant if the action is executed from a Tree or Collection) [NL] const selection: Array = []; @@ -135,7 +128,13 @@ export class UmbUnpublishDocumentEntityAction extends UmbEntityActionBase }); } - actionEventContext.dispatchEvent(event); + const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + const event = new UmbRequestReloadStructureForEntityEvent({ + unique: this.args.unique, + entityType: this.args.entityType, + }); + + actionEventContext?.dispatchEvent(event); } } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/modal/document-unpublish-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/modal/document-unpublish-modal.element.ts index 46a3a29556..7a1ef74b8f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/modal/document-unpublish-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/modal/document-unpublish-modal.element.ts @@ -5,7 +5,7 @@ import type { UmbDocumentUnpublishModalData, UmbDocumentUnpublishModalValue, } from './document-unpublish-modal.token.js'; -import { css, customElement, html, nothing, state } from '@umbraco-cms/backoffice/external/lit'; +import { css, customElement, html, nothing, state, when } from '@umbraco-cms/backoffice/external/lit'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; @@ -65,7 +65,7 @@ export class UmbDocumentUnpublishModalElement extends UmbModalBaseElement< this.#configureReferences(); // If invariant, don't display the variant selection component. - if (this.data?.options.length === 1 && this.data.options[0].unique === 'invariant') { + if (this.data?.options.length === 1 && this.data.options[0].culture === null) { this._isInvariant = true; this._hasInvalidSelection = false; return; @@ -150,20 +150,21 @@ export class UmbDocumentUnpublishModalElement extends UmbModalBaseElement< override render() { return html` - ${!this._isInvariant - ? html` -

- - Select the languages to unpublish. Unpublishing a mandatory language will unpublish all languages. - -

- - ` - : nothing} + ${when( + !this._isInvariant, + () => html` +

+ + Select the languages to unpublish. Unpublishing a mandatory language will unpublish all languages. + +

+ + `, + )}

diff --git a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json index 644ab33631..88ff1443eb 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json +++ b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json @@ -7,8 +7,8 @@ "name": "acceptancetest", "hasInstallScript": true, "dependencies": { - "@umbraco/json-models-builders": "^2.0.31", - "@umbraco/playwright-testhelpers": "^16.0.5", + "@umbraco/json-models-builders": "^2.0.33", + "@umbraco/playwright-testhelpers": "^16.0.7", "camelize": "^1.0.0", "dotenv": "^16.3.1", "node-fetch": "^2.6.7" @@ -58,32 +58,22 @@ } }, "node_modules/@umbraco/json-models-builders": { - "version": "2.0.32", - "resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-2.0.32.tgz", - "integrity": "sha512-Aw7yBu8ePNxdjS7Q61j5KPFsiOS+IGCYxBX0H4KWbjXTdvL/PsB98KiqbDHHKFnp0fF1b2ffwJAI6jmvnxPBzg==", - "license": "MIT", + "version": "2.0.33", + "resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-2.0.33.tgz", + "integrity": "sha512-FAQxQIHoY6PGxWuodp4LSQxNufnOiqnaRNmtG8Ejn01r9lJJdA27CJKJ0bCs8U0W3cdN+Z+j7jBLd8H025/THw==", "dependencies": { "camelize": "^1.0.1" } }, "node_modules/@umbraco/playwright-testhelpers": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-16.0.5.tgz", - "integrity": "sha512-QRZPBCSMqXWp3VnmSsetSdk00Un+Tocd0pa2uOSmBFiOn6+9+6ea5WXWZakMIdVi/Esr44TZs8Zotm3eAgiqfQ==", + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-16.0.7.tgz", + "integrity": "sha512-mb6i3lxz3M+PXko8wkX7dcRNU669CTroRX10O0EyXLQWmLXHOzUCOcdSiAjaVGczv9Ht0fmjXBL8BtU93bIZlA==", "dependencies": { - "@umbraco/json-models-builders": "2.0.31", + "@umbraco/json-models-builders": "2.0.33", "node-fetch": "^2.6.7" } }, - "node_modules/@umbraco/playwright-testhelpers/node_modules/@umbraco/json-models-builders": { - "version": "2.0.31", - "resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-2.0.31.tgz", - "integrity": "sha512-RGulJazaSjp6ZfHYACXUKDQuF055oXx+Kk3Q7/+PyfGZWRngT7V799Mal1vnrG7W9EDEX9Up+AP22O/alYj3Gg==", - "license": "MIT", - "dependencies": { - "camelize": "^1.0.1" - } - }, "node_modules/async": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", diff --git a/tests/Umbraco.Tests.AcceptanceTest/package.json b/tests/Umbraco.Tests.AcceptanceTest/package.json index 830fee98b4..94e5f3dc0b 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/package.json +++ b/tests/Umbraco.Tests.AcceptanceTest/package.json @@ -20,8 +20,8 @@ "typescript": "^4.8.3" }, "dependencies": { - "@umbraco/json-models-builders": "^2.0.31", - "@umbraco/playwright-testhelpers": "^16.0.5", + "@umbraco/json-models-builders": "^2.0.33", + "@umbraco/playwright-testhelpers": "^16.0.7", "camelize": "^1.0.0", "dotenv": "^16.3.1", "node-fetch": "^2.6.7" diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/ChildrenContent.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/ChildrenContent.spec.ts index cefa34bb94..1a4e1495f7 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/ChildrenContent.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/ChildrenContent.spec.ts @@ -1,4 +1,4 @@ -import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {ConstantHelper, NotificationConstantHelper, test} from '@umbraco/playwright-testhelpers'; import {expect} from "@playwright/test"; let documentTypeId = ''; @@ -103,15 +103,15 @@ test('cannot publish child if the parent is not published', async ({umbracoApi, await umbracoUi.content.clickCaretButtonForContentName(contentName); await umbracoUi.content.clickActionsMenuForContent(childContentName); await umbracoUi.content.clickPublishButton(); + await umbracoUi.content.clickConfirmToPublishButton(); // Assert - //await umbracoUi.content.isSuccessNotificationVisible(); - await umbracoUi.content.isErrorNotificationVisible(false); + await umbracoUi.content.doesErrorNotificationHaveText(NotificationConstantHelper.error.parentNotPublished); const contentData = await umbracoApi.document.getByName(childContentName); expect(contentData.variants[0].state).toBe('Draft'); }); -test('can publish with descendants', {tag: '@smoke'}, async ({umbracoApi, umbracoUi}) => { +test('can publish content with child node', {tag: '@smoke'}, async ({umbracoApi, umbracoUi}) => { // Arrange childDocumentTypeId = await umbracoApi.documentType.createDefaultDocumentType(childDocumentTypeName); documentTypeId = await umbracoApi.documentType.createDocumentTypeWithAllowedChildNode(documentTypeName, childDocumentTypeId); @@ -123,12 +123,13 @@ test('can publish with descendants', {tag: '@smoke'}, async ({umbracoApi, umbrac // Act await umbracoUi.content.clickActionsMenuForContent(contentName); await umbracoUi.content.clickPublishButton(); + await umbracoUi.content.clickConfirmToPublishButton(); // Assert - await umbracoUi.content.isSuccessNotificationVisible(); + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published); await umbracoUi.content.isErrorNotificationVisible(false); const contentData = await umbracoApi.document.getByName(contentName); expect(contentData.variants[0].state).toBe('Published'); - const childContentData = await umbracoApi.document.getByName(contentName); - expect(childContentData.variants[0].state).toBe('Published'); + const childContentData = await umbracoApi.document.getByName(childContentName); + expect(childContentData.variants[0].state).toBe('Draft'); }); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/Content.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/Content.spec.ts index ebf1951219..4bd1f27409 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/Content.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/Content.spec.ts @@ -128,7 +128,7 @@ test('can update content', {tag: '@smoke'}, async ({umbracoApi, umbracoUi}) => { expect(updatedContentData.values[0].value).toBe(contentText); }); -test('can publish content', async ({umbracoApi, umbracoUi}) => { +test('can publish invariant content node', async ({umbracoApi, umbracoUi}) => { // Arrange const dataTypeData = await umbracoApi.dataType.getByName(dataTypeName); documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, dataTypeName, dataTypeData.id); @@ -139,6 +139,7 @@ test('can publish content', async ({umbracoApi, umbracoUi}) => { // Act await umbracoUi.content.clickActionsMenuForContent(contentName); await umbracoUi.content.clickPublishButton(); + await umbracoUi.content.clickConfirmToPublishButton(); // Assert await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published); @@ -165,3 +166,22 @@ test('can unpublish content', {tag: '@smoke'}, async ({umbracoApi, umbracoUi}) = const contentData = await umbracoApi.document.getByName(contentName); expect(contentData.variants[0].state).toBe('Draft'); }); + +test('can publish variant content node', async ({umbracoApi, umbracoUi}) => { + // Arrange + const dataTypeData = await umbracoApi.dataType.getByName(dataTypeName); + documentTypeId = await umbracoApi.documentType.createVariantDocumentTypeWithInvariantPropertyEditor(documentTypeName, dataTypeName, dataTypeData.id); + await umbracoApi.document.createDocumentWithEnglishCultureAndTextContent(contentName, documentTypeId, contentText, dataTypeName); + await umbracoUi.goToBackOffice(); + await umbracoUi.content.goToSection(ConstantHelper.sections.content); + + // Act + await umbracoUi.content.clickActionsMenuForContent(contentName); + await umbracoUi.content.clickPublishButton(); + await umbracoUi.content.clickConfirmToPublishButton(); + + // Assert + await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published); + const contentData = await umbracoApi.document.getByName(contentName); + expect(contentData.variants[0].state).toBe('Published'); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/RedirectManagement.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/RedirectManagement.spec.ts index 62d92f7b55..ee154c1cdb 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/RedirectManagement.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/RedirectManagement.spec.ts @@ -20,6 +20,7 @@ test.beforeEach(async ({umbracoApi, umbracoUi}) => { await umbracoUi.content.goToSection(ConstantHelper.sections.content); await umbracoUi.content.clickActionsMenuForContent(contentName); await umbracoUi.content.clickPublishButton(); + await umbracoUi.content.clickConfirmToPublishButton(); }); test.afterEach(async ({umbracoApi}) => { diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Users/Permissions/UserGroup/DefaultPermissionsInContent.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Users/Permissions/UserGroup/DefaultPermissionsInContent.spec.ts index 8d822e8d43..a103a6f7e4 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Users/Permissions/UserGroup/DefaultPermissionsInContent.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Users/Permissions/UserGroup/DefaultPermissionsInContent.spec.ts @@ -248,6 +248,7 @@ test('can publish content with publish permission enabled', async ({umbracoApi, // Act await umbracoUi.content.clickActionsMenuForContent(rootDocumentName); await umbracoUi.content.clickPublishButton(); + await umbracoUi.content.clickConfirmToPublishButton(); // Assert await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);