From b39670716c2247cdba0d2a327eba58720f633285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 3 Oct 2024 20:12:28 +0200 Subject: [PATCH] publish modal, require not published mandatory languages --- .../document-publish-modal.element.ts | 31 ++++++++++++++++--- ...ocument-variant-language-picker.element.ts | 29 +++++++++++------ .../documents/documents/modals/utils.ts | 14 +++++++++ .../workspace/document-workspace.context.ts | 28 +++++++++-------- 4 files changed, 75 insertions(+), 27 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/utils.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts index ce6e111fa3..6fd010b295 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts @@ -1,4 +1,5 @@ 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 { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; @@ -17,6 +18,9 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement< @state() _options: Array = []; + @state() + _hasNotSelectedMandatory?: boolean; + override firstUpdated() { this.#configureSelectionManager(); } @@ -25,10 +29,10 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement< this.#selectionManager.setMultiple(true); this.#selectionManager.setSelectable(true); - // Only display variants that are relevant to pick from, i.e. variants that are draft or published with pending changes: + // Only display variants that are relevant to pick from, i.e. variants that are draft, not-published-mandatory or published with pending changes: this._options = this.data?.options.filter( - (option) => option.variant && option.variant.state !== UmbDocumentVariantState.NOT_CREATED, + (option) => isNotPublishedMandatory(option) || option.variant?.state !== UmbDocumentVariantState.NOT_CREATED, ) ?? []; let selected = this.value?.selection ?? []; @@ -36,14 +40,29 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement< // Filter selection based on options: selected = selected.filter((s) => this._options.some((o) => o.unique === s)); - this.#selectionManager.setSelection(selected); - // Additionally select mandatory languages: + // [NL]: I think for now lets make it an active choice to select the languages. If you just made them, they would be selected. So it just to underline the act of actually selecting these languages. + /* this._options.forEach((variant) => { if (variant.language?.isMandatory) { - this.#selectionManager.select(variant.unique); + selected.push(variant.unique); } }); + */ + + this.#selectionManager.setSelection(selected); + + this.observe( + this.#selectionManager.selection, + (selection: Array) => { + if (!this._options && !selection) return; + + //Getting not published mandatory options — the options that are mandatory and not currently published. + const missingMandatoryOptions = this._options.filter(isNotPublishedMandatory); + this._hasNotSelectedMandatory = missingMandatoryOptions.some((option) => !selection.includes(option.unique)); + }, + 'observeSelection', + ); } #submit() { @@ -63,6 +82,7 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement<
@@ -71,6 +91,7 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement< label="${this.localize.term('buttons_saveAndPublish')}" look="primary" color="positive" + ?disabled=${this._hasNotSelectedMandatory} @click=${this.#submit}>
`; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts index ccfd6c36d4..49d878e16c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts @@ -25,7 +25,7 @@ export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement { this.#selectionManager = value; this.observe( this.selectionManager.selection, - async (selection) => { + (selection) => { this._selection = selection; }, '_selectionManager', @@ -46,6 +46,14 @@ export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement { @property({ attribute: false }) public pickableFilter?: (item: UmbDocumentVariantOptionModel) => boolean; + /** + * A filter function that determines if an item should be highlighted as a must select. + * @memberof UmbDocumentVariantLanguagePickerElement + * @returns {boolean} - True if the item is pickableFilter, false otherwise. + */ + @property({ attribute: false }) + public requiredFilter?: (item: UmbDocumentVariantOptionModel) => boolean; + protected override updated(_changedProperties: PropertyValues): void { super.updated(_changedProperties); @@ -71,25 +79,28 @@ export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement { #renderItem(option: UmbDocumentVariantOptionModel) { const pickable = this.pickableFilter ? this.pickableFilter(option) : () => true; + const selected = this._selection.includes(option.unique); + const mustSelect = (!selected && this.requiredFilter?.(option)) ?? false; return html` this.selectionManager.select(option.unique)} @deselected=${() => this.selectionManager.deselect(option.unique)} - ?selected=${this._selection.includes(option.unique)}> + ?selected=${selected}> - ${UmbDocumentVariantLanguagePickerElement.renderLabel(option)} + ${this.renderLabel(option, mustSelect)} `; } - static renderLabel(option: UmbDocumentVariantOptionModel) { + renderLabel(option: UmbDocumentVariantOptionModel, mustSelect: boolean) { return html`
${option.language.name}
${UmbDocumentVariantLanguagePickerElement.renderVariantStatus(option)}
- ${option.language.isMandatory && option.variant?.state !== UmbDocumentVariantState.PUBLISHED + ${option.language.isMandatory && mustSelect ? html`
Mandatory language
` @@ -106,17 +117,17 @@ export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement { case UmbDocumentVariantState.DRAFT: return html`Draft`; case UmbDocumentVariantState.NOT_CREATED: - return html`Not created`; default: - return nothing; + return html`Not created`; } } static override styles = [ UmbTextStyles, css` - #subtitle { - margin-top: 0; + .required { + color: var(--uui-color-danger); + --uui-menu-item-color-hover: var(--uui-color-danger-emphasis); } .label { padding: 0.5rem 0; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/utils.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/utils.ts new file mode 100644 index 0000000000..c5457fe1b9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/utils.ts @@ -0,0 +1,14 @@ +import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../types.js'; + +/** + * @function isNotPublishedMandatory + * @param {UmbDocumentVariantOptionModel} option - the option to check. + * @returns {boolean} boolean + */ +export function isNotPublishedMandatory(option: UmbDocumentVariantOptionModel): boolean { + return ( + option.language.isMandatory && + option.variant?.state !== UmbDocumentVariantState.PUBLISHED && + option.variant?.state !== UmbDocumentVariantState.PUBLISHED_PENDING_CHANGES + ); +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index a082d780ae..17a2d3e0ce 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -489,23 +489,21 @@ export class UmbDocumentWorkspaceContext }; async #determineVariantOptions() { - const activeVariants = this.splitView.getActiveVariants(); - - const activeVariantIds = activeVariants.map((activeVariant) => UmbVariantId.Create(activeVariant)); - // TODO: We need to filter the selected array, so it only contains one of each variantId. [NL] - const changedVariantIds = this.#data.getChangedVariants(); - const selected = activeVariantIds.concat(changedVariantIds); - // Selected can contain entries that are not part of the options, therefor the modal filters selection based on options. - - const readOnlyCultures = this.readOnlyState.getStates().map((s) => s.variantId.culture); - const selectedCultures = selected.map((x) => x.toString()).filter((v, i, a) => a.indexOf(v) === i); - const writable = selectedCultures.filter((x) => readOnlyCultures.includes(x) === false); - const options = await firstValueFrom(this.variantOptions); + const activeVariants = this.splitView.getActiveVariants(); + const activeVariantIds = activeVariants.map((activeVariant) => UmbVariantId.Create(activeVariant)); + const changedVariantIds = this.#data.getChangedVariants(); + const selectedVariantIds = activeVariantIds.concat(changedVariantIds); + + // Selected can contain entries that are not part of the options, therefor the modal filters selection based on options. + const readOnlyCultures = this.readOnlyState.getStates().map((s) => s.variantId.culture); + let selected = selectedVariantIds.map((x) => x.toString()).filter((v, i, a) => a.indexOf(v) === i); + selected = selected.filter((x) => readOnlyCultures.includes(x) === false); + return { options, - selected: writable, + selected, }; } @@ -787,6 +785,8 @@ export class UmbDocumentWorkspaceContext if (!result?.selection.length) return; + // TODO: Validate content & Save changes for the selected variants? — Or be clear that changes are not part of this action. [NL] + // Map to the correct format for the API (UmbDocumentVariantPublishModel) const variants = result?.selection.map((x) => ({ @@ -833,6 +833,8 @@ export class UmbDocumentWorkspaceContext if (!variantIds.length) return; + // TODO: Validate content & Save changes for the selected variants? — Or be clear that changes are not part of this action. [NL] + const unique = this.getUnique(); if (!unique) throw new Error('Unique is missing'); await this.publishingRepository.publishWithDescendants(