Merge branch 'main' into v14/fix/dictionary-item-misleading-description-when-name-not-set

This commit is contained in:
Niels Lyngsø
2024-09-09 11:51:40 +02:00
committed by GitHub
19 changed files with 286 additions and 142 deletions

View File

@@ -20,6 +20,14 @@ import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/rou
import { pathFolderName } from '@umbraco-cms/backoffice/utils';
import type { UmbNumberRangeValueType } from '@umbraco-cms/backoffice/models';
interface UmbBlockGridAreaTypeInvalidRuleType {
groupKey?: string;
key?: string;
name: string;
amount: number;
minRequirement: number;
maxRequirement: number;
}
export class UmbBlockGridEntriesContext
extends UmbBlockEntriesContext<
typeof UMB_BLOCK_GRID_MANAGER_CONTEXT,
@@ -455,14 +463,7 @@ export class UmbBlockGridEntriesContext
}
}
#invalidBlockTypeLimits?: Array<{
groupKey?: string;
key?: string;
name: string;
amount: number;
minRequirement: number;
maxRequirement: number;
}>;
#invalidBlockTypeLimits?: Array<UmbBlockGridAreaTypeInvalidRuleType>;
getInvalidBlockTypeLimits() {
return this.#invalidBlockTypeLimits ?? [];
@@ -476,57 +477,58 @@ export class UmbBlockGridEntriesContext
const layoutEntries = this._layoutEntries.getValue();
this.#invalidBlockTypeLimits = [];
this.#invalidBlockTypeLimits = this.#areaType.specifiedAllowance
.map((rule) => {
const minAllowed = rule.minAllowed || 0;
const maxAllowed = rule.maxAllowed || 0;
const hasInvalidRules = this.#areaType.specifiedAllowance.some((rule) => {
const minAllowed = rule.minAllowed || 0;
const maxAllowed = rule.maxAllowed || 0;
// For block groups:
if (rule.groupKey) {
const groupElementTypeKeys =
this._manager
?.getBlockTypes()
.filter((blockType) => blockType.groupKey === rule.groupKey && blockType.allowInAreas === true)
.map((x) => x.contentElementTypeKey) ?? [];
const groupAmount = layoutEntries.filter((entry) => {
const contentTypeKey = this._manager!.getContentTypeKeyOfContentUdi(entry.contentUdi);
return contentTypeKey ? groupElementTypeKeys.indexOf(contentTypeKey) !== -1 : false;
}).length;
// For block groups:
if (rule.groupKey) {
const groupElementTypeKeys =
this._manager
?.getBlockTypes()
.filter((blockType) => blockType.groupKey === rule.groupKey && blockType.allowInAreas === true)
.map((x) => x.contentElementTypeKey) ?? [];
const groupAmount = layoutEntries.filter((entry) => {
const contentTypeKey = this._manager!.getContentTypeKeyOf(entry.contentUdi);
return contentTypeKey ? groupElementTypeKeys.indexOf(contentTypeKey) !== -1 : false;
}).length;
if (groupAmount < minAllowed || (maxAllowed > 0 && groupAmount > maxAllowed)) {
this.#invalidBlockTypeLimits!.push({
groupKey: rule.groupKey,
name: this._manager!.getBlockGroupName(rule.groupKey) ?? '?',
amount: groupAmount,
minRequirement: minAllowed,
maxRequirement: maxAllowed,
});
return true;
if (groupAmount < minAllowed || (maxAllowed > 0 && groupAmount > maxAllowed)) {
return {
groupKey: rule.groupKey,
name: this._manager!.getBlockGroupName(rule.groupKey) ?? '?',
amount: groupAmount,
minRequirement: minAllowed,
maxRequirement: maxAllowed,
};
}
return undefined;
}
}
// For specific elementTypes:
else if (rule.elementTypeKey) {
const amount = layoutEntries.filter((entry) => {
const contentTypeKey = this._manager!.getContentOf(entry.contentUdi)?.contentTypeKey;
return contentTypeKey === rule.elementTypeKey;
}).length;
if (amount < minAllowed || (maxAllowed > 0 ? amount > maxAllowed : false)) {
this.#invalidBlockTypeLimits!.push({
key: rule.elementTypeKey,
name: this._manager!.getContentTypeNameOf(rule.elementTypeKey) ?? '?',
amount: amount,
minRequirement: minAllowed,
maxRequirement: maxAllowed,
});
return true;
// For specific elementTypes:
else if (rule.elementTypeKey) {
const amount = layoutEntries.filter((entry) => {
const contentTypeKey = this._manager!.getContentOf(entry.contentUdi)?.contentTypeKey;
return contentTypeKey === rule.elementTypeKey;
}).length;
if (amount < minAllowed || (maxAllowed > 0 ? amount > maxAllowed : false)) {
return {
key: rule.elementTypeKey,
name: this._manager!.getContentTypeNameOf(rule.elementTypeKey) ?? '?',
amount: amount,
minRequirement: minAllowed,
maxRequirement: maxAllowed,
};
}
return undefined;
}
}
// Lets fail cause the rule was bad.
console.error('Invalid block type limit rule.', rule);
return false;
});
// Lets fail cause the rule was bad.
console.error('Invalid block type limit rule.', rule);
return undefined;
})
.filter((x) => x !== undefined) as Array<UmbBlockGridAreaTypeInvalidRuleType>;
const hasInvalidRules = this.#invalidBlockTypeLimits.length > 0;
return hasInvalidRules === false;
}

View File

@@ -14,7 +14,7 @@ export const UMB_BLOCK_GRID_TYPE_WORKSPACE_MODAL = new UmbModalToken<
type: 'sidebar',
size: 'large',
},
data: { entityType: UMB_BLOCK_GRID_TYPE, preset: { allowAtRoot: true } },
data: { entityType: UMB_BLOCK_GRID_TYPE, preset: { allowAtRoot: true, allowInAreas: true } },
},
// Recast the type, so the entityType data prop is not required:
) as UmbModalToken<Omit<UmbWorkspaceModalData, 'entityType' | 'preset'>, UmbWorkspaceModalValue>;

View File

@@ -48,6 +48,15 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper
}
private _contentUdi?: string | undefined;
/**
* Sets the element to readonly mode, meaning value cannot be changed but still able to read and select its content.
* @type {boolean}
* @attr
* @default false
*/
@property({ type: Boolean, reflect: true })
public readonly = false;
#context = new UmbBlockListEntryContext(this);
@state()
@@ -252,33 +261,7 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper
>${this._inlineEditingMode ? this.#renderInlineBlock() : this.#renderRefBlock()}</umb-extension-slot
>
<uui-action-bar>
${this._showContentEdit && this._workspaceEditContentPath
? html`<uui-button
label="edit"
look="secondary"
color=${this._contentInvalid ? 'danger' : ''}
href=${this._workspaceEditContentPath}>
<uui-icon name="icon-edit"></uui-icon>
${this._contentInvalid
? html`<uui-badge attention color="danger" label="Invalid content">!</uui-badge>`
: nothing}
</uui-button>`
: nothing}
${this._hasSettings && this._workspaceEditSettingsPath
? html`<uui-button
label="Edit settings"
look="secondary"
color=${this._settingsInvalid ? 'danger' : ''}
href=${this._workspaceEditSettingsPath}>
<uui-icon name="icon-settings"></uui-icon>
${this._settingsInvalid
? html`<uui-badge attention color="danger" label="Invalid settings">!</uui-badge>`
: nothing}
</uui-button>`
: nothing}
<uui-button label="delete" look="secondary" @click=${() => this.#context.requestDelete()}>
<uui-icon name="icon-remove"></uui-icon>
</uui-button>
${this.#renderEditContentAction()} ${this.#renderEditSettingsAction()} ${this.#renderDeleteAction()}
</uui-action-bar>
${!this._showContentEdit && this._contentInvalid
? html`<uui-badge attention color="danger" label="Invalid content">!</uui-badge>`
@@ -286,6 +269,45 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper
`;
}
#renderEditContentAction() {
return html` ${this._showContentEdit && this._workspaceEditContentPath
? html`<uui-button
label="edit"
look="secondary"
color=${this._contentInvalid ? 'danger' : ''}
href=${this._workspaceEditContentPath}>
<uui-icon name="icon-edit"></uui-icon>
${this._contentInvalid
? html`<uui-badge attention color="danger" label="Invalid content">!</uui-badge>`
: nothing}
</uui-button>`
: nothing}`;
}
#renderEditSettingsAction() {
return html`
${this._hasSettings && this._workspaceEditSettingsPath
? html`<uui-button
label="Edit settings"
look="secondary"
color=${this._settingsInvalid ? 'danger' : ''}
href=${this._workspaceEditSettingsPath}>
<uui-icon name="icon-settings"></uui-icon>
${this._settingsInvalid
? html`<uui-badge attention color="danger" label="Invalid settings">!</uui-badge>`
: nothing}
</uui-button>`
: nothing}
`;
}
#renderDeleteAction() {
if (this.readonly) return nothing;
return html` <uui-button label="delete" look="secondary" @click=${() => this.#context.requestDelete()}>
<uui-icon name="icon-remove"></uui-icon>
</uui-button>`;
}
override render() {
return this.#renderBlock();
}

View File

@@ -14,6 +14,7 @@ export const manifests: Array<ManifestTypes> = [
propertyEditorSchemaAlias: UMB_BLOCK_LIST_PROPERTY_EDITOR_ALIAS,
icon: 'icon-thumbnail-list',
group: 'lists',
supportsReadOnly: true,
settings: {
properties: [
{

View File

@@ -4,7 +4,7 @@ import type { UmbBlockListLayoutModel, UmbBlockListValueModel } from '../../type
import type { UmbBlockListEntryElement } from '../../components/block-list-entry/index.js';
import { UMB_BLOCK_LIST_PROPERTY_EDITOR_ALIAS } from './manifests.js';
import { UmbLitElement, umbDestroyOnDisconnect } from '@umbraco-cms/backoffice/lit-element';
import { html, customElement, property, state, repeat, css } from '@umbraco-cms/backoffice/external/lit';
import { html, customElement, property, state, repeat, css, nothing } from '@umbraco-cms/backoffice/external/lit';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import type { UmbPropertyEditorUiElement, UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry';
import {
@@ -113,6 +113,27 @@ export class UmbPropertyEditorUIBlockListElement
}
}
/**
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
* @type {boolean}
* @attr
* @default false
*/
@property({ type: Boolean, reflect: true })
public get readonly() {
return this.#readonly;
}
public set readonly(value) {
this.#readonly = value;
if (this.#readonly) {
this.#sorter.disable();
} else {
this.#sorter.enable();
}
}
#readonly = false;
@state()
private _limitMin?: number;
@state()
@@ -205,6 +226,32 @@ export class UmbPropertyEditorUIBlockListElement
}
override render() {
return html` ${repeat(
this._layouts,
(x) => x.contentUdi,
(layoutEntry, index) => html`
${this.#renderInlineCreateButton(index)}
<umb-block-list-entry
.contentUdi=${layoutEntry.contentUdi}
.layout=${layoutEntry}
?readonly=${this.readonly}
${umbDestroyOnDisconnect()}>
</umb-block-list-entry>
`,
)}
<uui-button-group> ${this.#renderCreateButton()} ${this.#renderPasteButton()} </uui-button-group>`;
}
#renderInlineCreateButton(index: number) {
if (this.readonly) return nothing;
return html`<uui-button-inline-create
label=${this._createButtonLabel}
href=${this._catalogueRouteBuilder?.({ view: 'create', index: index }) ?? ''}></uui-button-inline-create>`;
}
#renderCreateButton() {
if (this.readonly) return nothing;
let createPath: string | undefined;
if (this._blocks?.length === 1) {
const elementKey = this._blocks[0].contentElementTypeKey;
@@ -213,28 +260,22 @@ export class UmbPropertyEditorUIBlockListElement
} else {
createPath = this._catalogueRouteBuilder?.({ view: 'create', index: -1 });
}
return html` ${repeat(
this._layouts,
(x) => x.contentUdi,
(layoutEntry, index) =>
html`<uui-button-inline-create
label=${this._createButtonLabel}
href=${this._catalogueRouteBuilder?.({ view: 'create', index: index }) ?? ''}></uui-button-inline-create>
<umb-block-list-entry
.contentUdi=${layoutEntry.contentUdi}
.layout=${layoutEntry}
${umbDestroyOnDisconnect()}>
</umb-block-list-entry> `,
)}
<uui-button-group>
<uui-button look="placeholder" label=${this._createButtonLabel} href=${createPath ?? ''}></uui-button>
<uui-button
label=${this.localize.term('content_createFromClipboard')}
look="placeholder"
href=${this._catalogueRouteBuilder?.({ view: 'clipboard', index: -1 }) ?? ''}>
<uui-icon name="icon-paste-in"></uui-icon>
</uui-button>
</uui-button-group>`;
return html`
<uui-button look="placeholder" label=${this._createButtonLabel} href=${createPath ?? ''}></uui-button>
`;
}
#renderPasteButton() {
if (this.readonly) return nothing;
return html`
<uui-button
label=${this.localize.term('content_createFromClipboard')}
look="placeholder"
href=${this._catalogueRouteBuilder?.({ view: 'clipboard', index: -1 }) ?? ''}>
<uui-icon name="icon-paste-in"></uui-icon>
</uui-button>
`;
}
static override styles = [

View File

@@ -139,6 +139,9 @@ export abstract class UmbBlockManagerContext<
this.#contentTypes.appendOne(data);
}
getContentTypeKeyOfContentUdi(contentUdi: string) {
return this.getContentOf(contentUdi)?.contentTypeKey;
}
contentTypeOf(contentTypeKey: string) {
return this.#contentTypes.asObservablePart((source) => source.find((x) => x.unique === contentTypeKey));
}
@@ -148,9 +151,6 @@ export abstract class UmbBlockManagerContext<
getContentTypeNameOf(contentTypeKey: string) {
return this.#contentTypes.getValue().find((x) => x.unique === contentTypeKey)?.name;
}
getContentTypeKeyOf(contentTypeKey: string) {
return this.#contentTypes.getValue().find((x) => x.unique === contentTypeKey)?.unique;
}
getContentTypeHasProperties(contentTypeKey: string) {
const properties = this.#contentTypes.getValue().find((x) => x.unique === contentTypeKey)?.properties;
return properties ? properties.length > 0 : false;

View File

@@ -1,5 +1,6 @@
import { UMB_BLOCK_ELEMENT_PROPERTY_DATASET_CONTEXT } from './block-element-property-dataset.context-token.js';
import type { UmbBlockElementManager } from './block-element-manager.js';
import { UMB_BLOCK_WORKSPACE_CONTEXT } from './block-workspace.context-token.js';
import type { UmbPropertyDatasetContext } from '@umbraco-cms/backoffice/property';
import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
@@ -36,6 +37,16 @@ export class UmbBlockElementPropertyDatasetContext extends UmbControllerBase imp
super(host, UMB_PROPERTY_DATASET_CONTEXT.toString());
this.#elementManager = elementManager;
this.consumeContext(UMB_BLOCK_WORKSPACE_CONTEXT, (workspace) => {
this.observe(
workspace.readOnlyState.isOn,
(value) => {
this.#currentVariantCultureIsReadOnly.setValue(value);
},
'umbObserveReadOnlyStates',
);
});
this.provideContext(UMB_BLOCK_ELEMENT_PROPERTY_DATASET_CONTEXT, this);
}

View File

@@ -10,7 +10,7 @@ import { UmbClassState, UmbObjectState, UmbStringState } from '@umbraco-cms/back
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type { ManifestWorkspace } from '@umbraco-cms/backoffice/extension-registry';
import { UMB_MODAL_CONTEXT, type UmbModalContext } from '@umbraco-cms/backoffice/modal';
import { decodeFilePath } from '@umbraco-cms/backoffice/utils';
import { decodeFilePath, UmbReadOnlyVariantStateManager } from '@umbraco-cms/backoffice/utils';
import {
UMB_BLOCK_ENTRIES_CONTEXT,
UMB_BLOCK_MANAGER_CONTEXT,
@@ -60,6 +60,8 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
#variantId = new UmbClassState<UmbVariantId | undefined>(undefined);
readonly variantId = this.#variantId.asObservable();
public readonly readOnlyState = new UmbReadOnlyVariantStateManager(this);
constructor(host: UmbControllerHost, workspaceArgs: { manifest: ManifestWorkspace }) {
super(host, workspaceArgs.manifest.alias);
const manifest = workspaceArgs.manifest;
@@ -92,6 +94,25 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
this.observe(context.variantId, (variantId) => {
this.#variantId.setValue(variantId);
});
// If the current property is readonly all inner block content should also be readonly.
this.observe(context.isReadOnly, (isReadOnly) => {
const unique = 'UMB_PROPERTY_CONTEXT';
const variantId = this.#variantId.getValue();
if (variantId === undefined) return;
if (isReadOnly) {
const state = {
unique,
variantId,
message: '',
};
this.readOnlyState?.addState(state);
} else {
this.readOnlyState?.removeState(unique);
}
});
});
this.observe(this.variantId, (variantId) => {

View File

@@ -76,6 +76,15 @@ export class UmbStateManager<StateType extends UmbState = UmbState> extends UmbC
this._states.setValue(this._states.getValue().filter((x) => !uniques.includes(x.unique)));
}
/**
* Get all states from the state manager
* @returns {StateType[]} {StateType[]} All states in the state manager
* @memberof UmbStateManager
*/
getStates() {
return this._states.getValue();
}
/**
* Clear all states from the state manager
* @memberof UmbStateManager

View File

@@ -165,6 +165,9 @@ export function UmbFormControlMixin<
this._internals = this.attachInternals();
this.addEventListener('blur', () => {
/*if (e.composedPath().some((x) => x === this)) {
return;
}*/
this.pristine = false;
this.checkValidity();
});

View File

@@ -101,7 +101,7 @@ export class UmbInputDataTypeElement extends UmbFormControlMixin(UmbLitElement,
label="Select Property Editor"
look="placeholder"
color="default"
@focus=${() => {
@blur=${() => {
this.pristine = false;
}}
.href=${this._createRoute}></uui-button>

View File

@@ -62,7 +62,8 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement<
</p>
<umb-document-variant-language-picker
.selectionManager=${this.#selectionManager}
.variantLanguageOptions=${this._options}></umb-document-variant-language-picker>
.variantLanguageOptions=${this._options}
.pickableFilter=${this.data?.pickableFilter}></umb-document-variant-language-picker>
<p><umb-localize key="content_variantsWillBeSaved">All new variants will be saved.</umb-localize></p>

View File

@@ -82,7 +82,8 @@ export class UmbDocumentPublishWithDescendantsModalElement extends UmbModalBaseE
<umb-document-variant-language-picker
.selectionManager=${this.#selectionManager}
.variantLanguageOptions=${this._options}></umb-document-variant-language-picker>
.variantLanguageOptions=${this._options}
.pickableFilter=${this.data?.pickableFilter}></umb-document-variant-language-picker>
<uui-form-layout-item>
<uui-toggle

View File

@@ -53,7 +53,8 @@ export class UmbDocumentSaveModalElement extends UmbModalBaseElement<
<umb-document-variant-language-picker
.selectionManager=${this.#selectionManager}
.variantLanguageOptions=${this._options}></umb-document-variant-language-picker>
.variantLanguageOptions=${this._options}
.pickableFilter=${this.data?.pickableFilter}></umb-document-variant-language-picker>
<div slot="actions">
<uui-button label=${this.localize.term('general_close')} @click=${this.#close}></uui-button>

View File

@@ -101,21 +101,28 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement<
return repeat(
this._options,
(option) => option.unique,
(option) => html`
<uui-menu-item
selectable
label=${option.variant?.name ?? option.language.name}
@selected=${() => this.#selectionManager.select(option.unique)}
@deselected=${() => this.#selectionManager.deselect(option.unique)}
?selected=${this.#isSelected(option.unique)}>
<uui-icon slot="icon" name="icon-globe"></uui-icon>
${UmbDocumentVariantLanguagePickerElement.renderLabel(option)}
</uui-menu-item>
${when(this.#isSelected(option.unique), () => this.#renderPublishDateInput(option))}
`,
(option) => this.#renderItem(option),
);
}
#renderItem(option: UmbDocumentVariantOptionModel) {
const pickable = this.data?.pickableFilter ? this.data.pickableFilter(option) : () => true;
return html`
<uui-menu-item
?selectable=${pickable}
?disabled=${!pickable}
label=${option.variant?.name ?? option.language.name}
@selected=${() => this.#selectionManager.select(option.unique)}
@deselected=${() => this.#selectionManager.deselect(option.unique)}
?selected=${this.#isSelected(option.unique)}>
<uui-icon slot="icon" name="icon-globe"></uui-icon>
${UmbDocumentVariantLanguagePickerElement.renderLabel(option)}
</uui-menu-item>
${when(this.#isSelected(option.unique), () => this.#renderPublishDateInput(option))}
`;
}
#renderPublishDateInput(option: UmbDocumentVariantOptionModel) {
return html`<div class="publish-date">
<uui-form-layout-item>

View File

@@ -29,33 +29,45 @@ export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement {
@state()
_selection: Array<string> = [];
/**
* A filter function that determines if an item is pickableFilter or not.
* @memberof UmbDocumentVariantLanguagePickerElement
* @returns {boolean} - True if the item is pickableFilter, false otherwise.
*/
@property({ attribute: false })
public pickableFilter?: (item: UmbDocumentVariantOptionModel) => boolean;
override render() {
return this.variantLanguageOptions.length
? repeat(
this.variantLanguageOptions,
(option) => option.unique,
(option) => html`
<uui-menu-item
selectable
label=${option.variant?.name ?? option.language.name}
@selected=${() => this.selectionManager.select(option.unique)}
@deselected=${() => this.selectionManager.deselect(option.unique)}
?selected=${this._selection.includes(option.unique)}>
<uui-icon slot="icon" name="icon-globe"></uui-icon>
${UmbDocumentVariantLanguagePickerElement.renderLabel(option)}
</uui-menu-item>
`,
(option) => html` ${this.#renderItem(option)} `,
)
: html`<uui-box>
<umb-localize key="content_noVariantsToProcess">There are no available variants</umb-localize>
</uui-box>`;
}
#renderItem(option: UmbDocumentVariantOptionModel) {
const pickable = this.pickableFilter ? this.pickableFilter(option) : () => true;
return html`
<uui-menu-item
?selectable=${pickable}
?disabled=${!pickable}
label=${option.variant?.name ?? option.language.name}
@selected=${() => this.selectionManager.select(option.unique)}
@deselected=${() => this.selectionManager.deselect(option.unique)}
?selected=${this._selection.includes(option.unique)}>
<uui-icon slot="icon" name="icon-globe"></uui-icon>
${UmbDocumentVariantLanguagePickerElement.renderLabel(option)}
</uui-menu-item>
`;
}
static renderLabel(option: UmbDocumentVariantOptionModel) {
return html`<div class="label" slot="label">
<strong>
${option.variant?.segment ? option.variant.segment + ' - ' : ''}${option.variant?.name ?? option.language.name}
</strong>
<strong> ${option.language.name} </strong>
<div class="label-status">${UmbDocumentVariantLanguagePickerElement.renderVariantStatus(option)}</div>
${option.language.isMandatory && option.variant?.state !== UmbDocumentVariantState.PUBLISHED
? html`<div class="label-status">

View File

@@ -2,6 +2,7 @@ import type { UmbDocumentVariantOptionModel } from '../types.js';
export interface UmbDocumentVariantPickerData {
options: Array<UmbDocumentVariantOptionModel>;
pickableFilter?: (variantOption: UmbDocumentVariantOptionModel) => boolean;
}
export interface UmbDocumentVariantPickerValue {

View File

@@ -101,7 +101,8 @@ export class UmbDocumentUnpublishModalElement extends UmbModalBaseElement<
<umb-document-variant-language-picker
.selectionManager=${this.#selectionManager}
.variantLanguageOptions=${this._options}></umb-document-variant-language-picker>
.variantLanguageOptions=${this._options}
.pickableFilter=${this.data?.pickableFilter}></umb-document-variant-language-picker>
<p>
<umb-localize key="prompt_confirmUnpublish">

View File

@@ -25,7 +25,7 @@ import {
} from '../paths.js';
import { UMB_DOCUMENTS_SECTION_PATH } from '../../section/paths.js';
import { UmbDocumentPreviewRepository } from '../repository/preview/index.js';
import { sortVariants } from '../utils.js';
import { UMB_DOCUMENT_WORKSPACE_ALIAS } from './manifests.js';
import { UmbEntityContext } from '@umbraco-cms/backoffice/entity';
import { UMB_INVARIANT_CULTURE, UmbVariantId } from '@umbraco-cms/backoffice/variant';
@@ -68,6 +68,7 @@ import type { UmbContentWorkspaceContext } from '@umbraco-cms/backoffice/content
import type { UmbDocumentTypeDetailModel } from '@umbraco-cms/backoffice/document-type';
import { UmbIsTrashedEntityContext } from '@umbraco-cms/backoffice/recycle-bin';
import { UmbReadOnlyVariantStateManager } from '@umbraco-cms/backoffice/utils';
import { sortVariants } from '../utils.js';
type EntityType = UmbDocumentDetailModel;
export class UmbDocumentWorkspaceContext
@@ -104,7 +105,7 @@ export class UmbDocumentWorkspaceContext
/*#blueprint = new UmbObjectState<UmbDocumentBlueprintDetailModel | undefined>(undefined);
public readonly blueprint = this.#blueprint.asObservable();*/
readOnlyState = new UmbReadOnlyVariantStateManager(this);
public readOnlyState = new UmbReadOnlyVariantStateManager(this);
public isLoaded() {
return this.#getDataPromise;
@@ -637,6 +638,11 @@ export class UmbDocumentWorkspaceContext
}
}
#readOnlyLanguageVariantsFilter = (option: UmbDocumentVariantOptionModel) => {
const readOnlyCultures = this.readOnlyState.getStates().map((s) => s.variantId.culture);
return readOnlyCultures.includes(option.culture) === false;
};
async #handleSaveAndPreview() {
const unique = this.getUnique();
if (!unique) throw new Error('Unique is missing');
@@ -681,6 +687,7 @@ export class UmbDocumentWorkspaceContext
.open(this, UMB_DOCUMENT_PUBLISH_MODAL, {
data: {
options,
pickableFilter: this.#readOnlyLanguageVariantsFilter,
},
value: { selection: selected },
})
@@ -786,6 +793,7 @@ export class UmbDocumentWorkspaceContext
.open(this, UMB_DOCUMENT_SAVE_MODAL, {
data: {
options,
pickableFilter: this.#readOnlyLanguageVariantsFilter,
},
value: { selection: selected },
})
@@ -833,6 +841,7 @@ export class UmbDocumentWorkspaceContext
.open(this, UMB_DOCUMENT_SCHEDULE_MODAL, {
data: {
options,
pickableFilter: this.#readOnlyLanguageVariantsFilter,
},
value: { selection: selected.map((unique) => ({ unique, schedule: {} })) },
})
@@ -873,6 +882,7 @@ export class UmbDocumentWorkspaceContext
.open(this, UMB_DOCUMENT_PUBLISH_WITH_DESCENDANTS_MODAL, {
data: {
options,
pickableFilter: this.#readOnlyLanguageVariantsFilter,
},
value: { selection: selected },
})