diff --git a/src/Umbraco.Web.UI.Client/src/packages/language/app-language-select/app-language-select.element.ts b/src/Umbraco.Web.UI.Client/src/packages/language/app-language-select/app-language-select.element.ts index fe0a58b166..282deb16dc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/language/app-language-select/app-language-select.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/language/app-language-select/app-language-select.element.ts @@ -26,6 +26,9 @@ export class UmbAppLanguageSelectElement extends UmbLitElement { @state() private _appLanguage?: UmbLanguageDetailModel; + @state() + private _appLanguageIsReadOnly = false; + @state() private _isOpen = false; @@ -77,6 +80,10 @@ export class UmbAppLanguageSelectElement extends UmbLitElement { this.observe(this.#appLanguageContext.appLanguage, (language) => { this._appLanguage = language; }); + + this.observe(this.#appLanguageContext.appLanguageReadOnlyState.isReadOnly, (isReadOnly) => { + this._appLanguageIsReadOnly = isReadOnly; + }); } async #observeLanguages() { @@ -123,7 +130,11 @@ export class UmbAppLanguageSelectElement extends UmbLitElement { #renderTrigger() { return html``; } @@ -139,7 +150,7 @@ export class UmbAppLanguageSelectElement extends UmbLitElement { @click-label=${this.#onLabelClick} data-unique=${ifDefined(language.unique)} ?active=${language.unique === this._appLanguage?.unique}> - ${this.#renderReadOnlyTag(language.unique)} + ${this.#isLanguageReadOnly(language.unique) ? this.#renderReadOnlyTag(language.unique) : nothing} `, )} @@ -147,14 +158,14 @@ export class UmbAppLanguageSelectElement extends UmbLitElement { `; } - #isReadOnly(culture: string | null) { + #isLanguageReadOnly(culture?: string) { if (!culture) return false; return this._disallowedLanguages.find((language) => language.unique === culture) ? true : false; } - #renderReadOnlyTag(culture?: string | null) { + #renderReadOnlyTag(culture?: string) { if (!culture) return nothing; - return this.#isReadOnly(culture) ? html`Read-only` : nothing; + return html`Read-only`; } static override styles = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/language/global-contexts/app-language.context.ts b/src/Umbraco.Web.UI.Client/src/packages/language/global-contexts/app-language.context.ts index 6a0d4b7d8b..270858b99b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/language/global-contexts/app-language.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/language/global-contexts/app-language.context.ts @@ -6,30 +6,34 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; import { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth'; +import { UmbReadOnlyStateManager } from '@umbraco-cms/backoffice/utils'; +import { UMB_CURRENT_USER_CONTEXT } from '@umbraco-cms/backoffice/current-user'; // TODO: Make a store for the App Languages. // TODO: Implement default language end-point, in progress at backend team, so we can avoid getting all languages. export class UmbAppLanguageContext extends UmbContextBase implements UmbApi { - #languageCollectionRepository: UmbLanguageCollectionRepository; #languages = new UmbArrayState([], (x) => x.unique); - moreThanOneLanguage = this.#languages.asObservablePart((x) => x.length > 1); #appLanguage = new UmbObjectState(undefined); - appLanguage = this.#appLanguage.asObservable(); + public readonly appLanguage = this.#appLanguage.asObservable(); + public readonly appLanguageCulture = this.#appLanguage.asObservablePart((x) => x?.unique); - appLanguageCulture = this.#appLanguage.asObservablePart((x) => x?.unique); + public readonly appLanguageReadOnlyState = new UmbReadOnlyStateManager(this); - appDefaultLanguage = createObservablePart(this.#languages.asObservable(), (languages) => + public readonly appDefaultLanguage = createObservablePart(this.#languages.asObservable(), (languages) => languages.find((language) => language.isDefault), ); - getAppCulture() { - return this.#appLanguage.getValue()?.unique; - } + public readonly moreThanOneLanguage = this.#languages.asObservablePart((x) => x.length > 1); + + #languageCollectionRepository = new UmbLanguageCollectionRepository(this); + #currentUserAllowedLanguages: Array = []; + #currentUserHasAccessToAllLanguages = false; + + #readOnlyStateIdentifier = 'UMB_LANGUAGE_PERMISSION_'; constructor(host: UmbControllerHost) { super(host, UMB_APP_LANGUAGE_CONTEXT); - this.#languageCollectionRepository = new UmbLanguageCollectionRepository(this); // TODO: We need to ensure this request is called every time the user logs in, but this should be done somewhere across the app and not here [JOV] this.consumeContext(UMB_AUTH_CONTEXT, (authContext) => { @@ -38,12 +42,39 @@ export class UmbAppLanguageContext extends UmbContextBase this.#observeLanguages(); }); }); + + this.consumeContext(UMB_CURRENT_USER_CONTEXT, (context) => { + this.observe(context.languages, (languages) => { + this.#currentUserAllowedLanguages = languages || []; + this.#setIsReadOnly(); + }); + + this.observe(context.hasAccessToAllLanguages, (hasAccessToAllLanguages) => { + this.#currentUserHasAccessToAllLanguages = hasAccessToAllLanguages || false; + this.#setIsReadOnly(); + }); + }); + } + + getAppCulture() { + return this.#appLanguage.getValue()?.unique; } setLanguage(unique: string) { + const appLanguage = this.#appLanguage.getValue(); + + // clear the previous read-only state + if (appLanguage?.unique) { + this.appLanguageReadOnlyState.removeState(this.#readOnlyStateIdentifier + appLanguage.unique); + } + + // set the new language const languages = this.#languages.getValue(); const language = languages.find((x) => x.unique === unique); this.#appLanguage.update(language); + + // set the new read-only state + this.#setIsReadOnly(); } async #observeLanguages() { @@ -67,6 +98,30 @@ export class UmbAppLanguageContext extends UmbContextBase // in that case do we then need an endpoint to get the default language? if (!defaultLanguage?.unique) return; this.setLanguage(defaultLanguage.unique); + this.#setIsReadOnly(); + } + + #setIsReadOnly() { + const appLanguage = this.#appLanguage.getValue(); + + if (!appLanguage) { + this.appLanguageReadOnlyState.clear(); + return; + } + + const unique = this.#readOnlyStateIdentifier + appLanguage.unique; + this.appLanguageReadOnlyState.removeState(unique); + + const isReadOnly = !this.#currentUserAllowedLanguages.includes(appLanguage.unique); + + if (isReadOnly) { + const readOnlyState = { + unique, + message: 'You do not have permission to edit to this culture', + }; + + this.appLanguageReadOnlyState.addState(readOnlyState); + } } }