Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix: Prevent publishing of readonly cultures #2298

Merged
merged 10 commits into from
Sep 17, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ describe('UmbSelectionManager', () => {
it('has a clearSelection method', () => {
expect(manager).to.have.property('clearSelection').that.is.a('function');
});

it('has a setAllowLimitation method', () => {
expect(manager).to.have.property('setAllowLimitation').that.is.a('function');
});
});
});

Expand Down Expand Up @@ -150,6 +154,15 @@ describe('UmbSelectionManager', () => {
manager.select('3');
expect(manager.getSelection()).to.deep.equal([]);
});

it('can not select an item if it does not pass the allow function', () => {
manager.setAllowLimitation((item) => item !== '2');
expect(() => manager.select('2')).to.throw();
expect(manager.getSelection()).to.deep.equal([]);

manager.select('1');
expect(manager.getSelection()).to.deep.equal(['1']);
});
});

describe('Deselect', () => {
Expand Down
15 changes: 15 additions & 0 deletions src/packages/core/utils/selection-manager/selection.manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export class UmbSelectionManager<ValueType extends string | null = string | null
#multiple = new UmbBooleanState(false);
public readonly multiple = this.#multiple.asObservable();

// eslint-disable-next-line @typescript-eslint/no-unused-vars
#allow = (unique: ValueType) => true;

constructor(host: UmbControllerHost) {
super(host);
}
Expand Down Expand Up @@ -109,6 +112,9 @@ export class UmbSelectionManager<ValueType extends string | null = string | null
public select(unique: ValueType) {
if (this.getSelectable() === false) return;
if (this.isSelected(unique)) return;
if (this.#allow(unique) === false) {
throw new Error('The item is now allowed to be selected');
}
const newSelection = this.getMultiple() ? [...this.getSelection(), unique] : [unique];
this.#selection.setValue(newSelection);
this.getHostElement().dispatchEvent(new UmbSelectedEvent(unique));
Expand Down Expand Up @@ -146,4 +152,13 @@ export class UmbSelectionManager<ValueType extends string | null = string | null
if (this.getSelectable() === false) return;
this.#selection.setValue([]);
}

/**
* Sets a function that determines if an item is selectable or not.
* @param compareFn A function that determines if an item is selectable or not.
* @memberof UmbSelectionManager
*/
public setAllowLimitation(compareFn: (unique: ValueType) => boolean): void {
this.#allow = compareFn;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement<
this.#selectionManager.setMultiple(true);
this.#selectionManager.setSelectable(true);

const pickableFilter = this.data?.pickableFilter;

if (pickableFilter) {
this.#selectionManager.setAllowLimitation((unique) => {
const option = this.data?.options.find((o) => o.unique === unique);
return option ? pickableFilter(option) : true;
});
}

// Only display variants that are relevant to pick from, i.e. variants that are draft or published with pending changes:
this._options =
this.data?.options.filter(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../../types.js';
import { css, customElement, html, nothing, property, repeat, state } from '@umbraco-cms/backoffice/external/lit';
import {
css,
customElement,
html,
nothing,
property,
repeat,
state,
type PropertyValues,
} from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import type { UmbSelectionManager } from '@umbraco-cms/backoffice/utils';
Expand Down Expand Up @@ -37,6 +46,17 @@ export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement {
@property({ attribute: false })
public pickableFilter?: (item: UmbDocumentVariantOptionModel) => boolean;

protected override updated(_changedProperties: PropertyValues): void {
super.updated(_changedProperties);

if (this.selectionManager && this.pickableFilter) {
this.#selectionManager.setAllowLimitation((unique) => {
const option = this.variantLanguageOptions.find((o) => o.unique === unique);
return option ? this.pickableFilter!(option) : true;
});
}
}

override render() {
return this.variantLanguageOptions.length
? repeat(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -536,11 +536,15 @@ export class UmbDocumentWorkspaceContext
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);

return {
options,
selected: selected.map((x) => x.toString()).filter((v, i, a) => a.indexOf(v) === i),
selected: writable,
};
}

Expand Down
Loading