Merge branch 'release/16.4' into v16/dev
This commit is contained in:
@@ -23,4 +23,11 @@ public class RichTextBlockValue : BlockValue<RichTextBlockLayoutItem>
|
||||
/// <inheritdoc />
|
||||
[JsonIgnore]
|
||||
public override string PropertyEditorAlias => Constants.PropertyEditors.Aliases.RichText;
|
||||
|
||||
/// <inheritdoc />
|
||||
#pragma warning disable CS0672 // Member overrides obsolete member
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
public override bool SupportsBlockLayoutAlias(string alias) => base.SupportsBlockLayoutAlias(alias) || alias.Equals("Umbraco.TinyMCE");
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
#pragma warning restore CS0672 // Member overrides obsolete member
|
||||
}
|
||||
|
||||
@@ -53,6 +53,12 @@ public interface IPublishedContentTypeFactory
|
||||
/// </summary>
|
||||
PublishedDataType GetDataType(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the internal data type cache.
|
||||
/// </summary>
|
||||
void ClearDataTypeCache()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the factory of datatype changes.
|
||||
/// </summary>
|
||||
|
||||
@@ -65,6 +65,22 @@ public class PublishedContentTypeFactory : IPublishedContentTypeFactory
|
||||
return dataType;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearDataTypeCache()
|
||||
{
|
||||
if (_publishedDataTypes is null)
|
||||
{
|
||||
// Not initialized yet, so skip and avoid lock
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_publishedDataTypesLocker)
|
||||
{
|
||||
// Clear cache (and let it lazy initialize again later)
|
||||
_publishedDataTypes = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void NotifyDataTypeChanges(params int[] ids)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Migrations;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
@@ -51,9 +52,12 @@ public class MigrationPlanExecutor : IMigrationPlanExecutor
|
||||
private readonly DistributedCache _distributedCache;
|
||||
private readonly IScopeAccessor _scopeAccessor;
|
||||
private readonly ICoreScopeProvider _scopeProvider;
|
||||
private readonly IPublishedContentTypeFactory _publishedContentTypeFactory;
|
||||
|
||||
private bool _rebuildCache;
|
||||
private bool _invalidateBackofficeUserAccess;
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 19.")]
|
||||
public MigrationPlanExecutor(
|
||||
ICoreScopeProvider scopeProvider,
|
||||
IScopeAccessor scopeAccessor,
|
||||
@@ -65,6 +69,33 @@ public class MigrationPlanExecutor : IMigrationPlanExecutor
|
||||
IKeyValueService keyValueService,
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
AppCaches appCaches)
|
||||
: this(
|
||||
scopeProvider,
|
||||
scopeAccessor,
|
||||
loggerFactory,
|
||||
migrationBuilder,
|
||||
databaseFactory,
|
||||
databaseCacheRebuilder,
|
||||
distributedCache,
|
||||
keyValueService,
|
||||
serviceScopeFactory,
|
||||
appCaches,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentTypeFactory>())
|
||||
{
|
||||
}
|
||||
|
||||
public MigrationPlanExecutor(
|
||||
ICoreScopeProvider scopeProvider,
|
||||
IScopeAccessor scopeAccessor,
|
||||
ILoggerFactory loggerFactory,
|
||||
IMigrationBuilder migrationBuilder,
|
||||
IUmbracoDatabaseFactory databaseFactory,
|
||||
IDatabaseCacheRebuilder databaseCacheRebuilder,
|
||||
DistributedCache distributedCache,
|
||||
IKeyValueService keyValueService,
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
AppCaches appCaches,
|
||||
IPublishedContentTypeFactory publishedContentTypeFactory)
|
||||
{
|
||||
_scopeProvider = scopeProvider;
|
||||
_scopeAccessor = scopeAccessor;
|
||||
@@ -76,6 +107,7 @@ public class MigrationPlanExecutor : IMigrationPlanExecutor
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
_appCaches = appCaches;
|
||||
_distributedCache = distributedCache;
|
||||
_publishedContentTypeFactory = publishedContentTypeFactory;
|
||||
_logger = _loggerFactory.CreateLogger<MigrationPlanExecutor>();
|
||||
}
|
||||
|
||||
@@ -301,6 +333,7 @@ public class MigrationPlanExecutor : IMigrationPlanExecutor
|
||||
_appCaches.IsolatedCaches.ClearAllCaches();
|
||||
await _databaseCacheRebuilder.RebuildAsync(false);
|
||||
_distributedCache.RefreshAllPublishedSnapshot();
|
||||
_publishedContentTypeFactory.ClearDataTypeCache();
|
||||
}
|
||||
|
||||
private async Task RevokeBackofficeTokens()
|
||||
|
||||
4
src/Umbraco.Web.UI.Client/package-lock.json
generated
4
src/Umbraco.Web.UI.Client/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@umbraco-cms/backoffice",
|
||||
"version": "16.4.0-rc",
|
||||
"version": "16.4.0-rc3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@umbraco-cms/backoffice",
|
||||
"version": "16.4.0-rc",
|
||||
"version": "16.4.0-rc3",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"./src/packages/*",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@umbraco-cms/backoffice",
|
||||
"license": "MIT",
|
||||
"version": "16.4.0-rc2",
|
||||
"version": "16.4.0-rc3",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": null,
|
||||
|
||||
@@ -2869,29 +2869,52 @@ export default {
|
||||
ar: 'العربية',
|
||||
bs: 'Bosanski',
|
||||
cs: 'Česky',
|
||||
'cs-cz': 'Česky (Czechia)',
|
||||
cy: 'Cymraeg',
|
||||
'cy-gb': 'Cymraeg (UK)',
|
||||
da: 'Dansk',
|
||||
'da-dk': 'Dansk (Danmark)',
|
||||
de: 'Deutsch',
|
||||
'de-de': 'Deutsch (Deutschland)',
|
||||
'de-ch': 'Deutsch (Schweiz)',
|
||||
en: 'English (UK)',
|
||||
'en-us': 'English (US)',
|
||||
es: 'Español',
|
||||
'es-es': 'Español (España)',
|
||||
fr: 'Français',
|
||||
he: 'Hebrew',
|
||||
'fr-fr': 'Français (France)',
|
||||
'fr-ch': 'Français (Suisse)',
|
||||
he: 'עברית',
|
||||
'he-il': 'עברית (ישראל)',
|
||||
hr: 'Hrvatski',
|
||||
'hr-hr': 'Hrvatski (Hrvatska)',
|
||||
it: 'Italiano',
|
||||
'it-it': 'Italiano (Italia)',
|
||||
'it-ch': 'Italiano (Svizzera)',
|
||||
ja: '日本語',
|
||||
'ja-jp': '日本語 (日本)',
|
||||
ko: '한국어',
|
||||
'ko-kr': '한국어 (한국)',
|
||||
nb: 'Norsk Bokmål',
|
||||
'nb-no': 'Norsk (Bokmål)',
|
||||
nl: 'Nederlands',
|
||||
'nl-nl': 'Nederlands (Nederland)',
|
||||
pl: 'Polski',
|
||||
'pl-pl': 'Polski (Polska)',
|
||||
pt: 'Português',
|
||||
'pt-br': 'Português (Brasil)',
|
||||
ro: 'Romana',
|
||||
ro: 'Română',
|
||||
'ro-ro': 'Română (România)',
|
||||
ru: 'Русский',
|
||||
'ru-ru': 'Русский (Россия)',
|
||||
sv: 'Svenska',
|
||||
'sv-se': 'Svenska (Sverige)',
|
||||
tr: 'Türkçe',
|
||||
'tr-tr': 'Türkçe (Türkiye Cumhuriyeti)',
|
||||
uk: 'Українська',
|
||||
'uk-ua': 'Українська (Україна)',
|
||||
zh: '中文',
|
||||
'zh-cn': '中文(简体,中国)',
|
||||
'zh-tw': '中文(正體,台灣)',
|
||||
vi: 'Tiếng Việt',
|
||||
},
|
||||
|
||||
@@ -2836,34 +2836,4 @@ export default {
|
||||
resetUrlMessage: 'Bạn có chắc chắn muốn đặt lại URL này không?',
|
||||
resetUrlLabel: 'Đặt lại',
|
||||
},
|
||||
uiCulture: {
|
||||
ar: 'العربية',
|
||||
bs: 'Bosanski',
|
||||
cs: 'Česky',
|
||||
cy: 'Cymraeg',
|
||||
da: 'Dansk',
|
||||
de: 'Deutsch',
|
||||
en: 'English (UK)',
|
||||
'en-us': 'English (US)',
|
||||
es: 'Español',
|
||||
fr: 'Français',
|
||||
he: 'Hebrew',
|
||||
hr: 'Hrvatski',
|
||||
it: 'Italiano',
|
||||
ja: '日本語',
|
||||
ko: '한국어',
|
||||
nb: 'Norsk Bokmål',
|
||||
nl: 'Nederlands',
|
||||
pl: 'Polski',
|
||||
pt: 'Português',
|
||||
'pt-br': 'Português (Brasil)',
|
||||
ro: 'Romana',
|
||||
ru: 'Русский',
|
||||
sv: 'Svenska',
|
||||
tr: 'Türkçe',
|
||||
uk: 'Українська',
|
||||
zh: '中文',
|
||||
'zh-tw': '中文(正體,台灣)',
|
||||
vi: 'Tiếng Việt',
|
||||
},
|
||||
} as UmbLocalizationDictionary;
|
||||
|
||||
@@ -17,7 +17,7 @@ import type {
|
||||
UmbPropertyEditorUiElement,
|
||||
UmbPropertyEditorConfigCollection,
|
||||
} from '@umbraco-cms/backoffice/property-editor';
|
||||
import { observeMultiple } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { jsonStringComparison, observeMultiple } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UMB_PROPERTY_CONTEXT, UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property';
|
||||
import { UmbFormControlMixin, UmbValidationContext } from '@umbraco-cms/backoffice/validation';
|
||||
import type { UmbBlockTypeGroup } from '@umbraco-cms/backoffice/block-type';
|
||||
@@ -181,15 +181,22 @@ export class UmbPropertyEditorUIBlockGridElement
|
||||
]).pipe(debounceTime(20)),
|
||||
([layouts, contents, settings, exposes]) => {
|
||||
if (layouts.length === 0) {
|
||||
if (this.value === undefined) {
|
||||
return;
|
||||
}
|
||||
super.value = undefined;
|
||||
} else {
|
||||
super.value = {
|
||||
const newValue = {
|
||||
...super.value,
|
||||
layout: { [UMB_BLOCK_GRID_PROPERTY_EDITOR_SCHEMA_ALIAS]: layouts },
|
||||
contentData: contents,
|
||||
settingsData: settings,
|
||||
expose: exposes,
|
||||
};
|
||||
if (jsonStringComparison(this.value, newValue)) {
|
||||
return;
|
||||
}
|
||||
super.value = newValue;
|
||||
}
|
||||
|
||||
// If we don't have a value set from the outside or an internal value, we don't want to set the value.
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
UmbFormControlMixin,
|
||||
UmbValidationContext,
|
||||
} from '@umbraco-cms/backoffice/validation';
|
||||
import { observeMultiple } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { jsonStringComparison, observeMultiple } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { debounceTime } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import { UMB_CONTENT_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/content';
|
||||
|
||||
@@ -339,15 +339,22 @@ export class UmbPropertyEditorUIBlockListElement
|
||||
]).pipe(debounceTime(20)),
|
||||
([layouts, contents, settings, exposes]) => {
|
||||
if (layouts.length === 0) {
|
||||
if (this.value === undefined) {
|
||||
return;
|
||||
}
|
||||
super.value = undefined;
|
||||
} else {
|
||||
super.value = {
|
||||
const newValue = {
|
||||
...super.value,
|
||||
layout: { [UMB_BLOCK_LIST_PROPERTY_EDITOR_SCHEMA_ALIAS]: layouts },
|
||||
contentData: contents,
|
||||
settingsData: settings,
|
||||
expose: exposes,
|
||||
};
|
||||
if (jsonStringComparison(this.value, newValue)) {
|
||||
return;
|
||||
}
|
||||
super.value = newValue;
|
||||
}
|
||||
|
||||
// If we don't have a value set from the outside or an internal value, we don't want to set the value.
|
||||
|
||||
@@ -4,281 +4,335 @@ export const manifests: Array<ManifestLocalization> = [
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.AR',
|
||||
weight: 100,
|
||||
name: 'Arabic Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'ar',
|
||||
},
|
||||
meta: { culture: 'ar' },
|
||||
js: () => import('../../../assets/lang/ar.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.BS',
|
||||
weight: 100,
|
||||
name: 'Bosnian Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'bs',
|
||||
},
|
||||
meta: { culture: 'bs' },
|
||||
js: () => import('../../../assets/lang/bs.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.CS',
|
||||
weight: 100,
|
||||
name: 'Czech Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'cs',
|
||||
},
|
||||
meta: { culture: 'cs' },
|
||||
js: () => import('../../../assets/lang/cs.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.CY',
|
||||
weight: 100,
|
||||
name: 'Welsh Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'cy',
|
||||
alias: 'Umb.Localization.CS_CZ',
|
||||
name: 'Czech (Czechia) Backoffice UI Localization',
|
||||
meta: { culture: 'cs-CZ' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.CY',
|
||||
name: 'Welsh Backoffice UI Localization',
|
||||
meta: { culture: 'cy' },
|
||||
js: () => import('../../../assets/lang/cy.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.DA',
|
||||
weight: 100,
|
||||
name: 'Danish Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'da',
|
||||
alias: 'Umb.Localization.CY_GB',
|
||||
name: 'Welsh (UK) Backoffice UI Localization',
|
||||
meta: { culture: 'cy-GB' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.DA',
|
||||
name: 'Danish Backoffice UI Localization',
|
||||
meta: { culture: 'da' },
|
||||
js: () => import('../../../assets/lang/da.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.DE',
|
||||
weight: 100,
|
||||
name: 'German Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'de',
|
||||
alias: 'Umb.Localization.DA-DK',
|
||||
name: 'Danish (Denmark) Backoffice UI Localization',
|
||||
meta: { culture: 'da-DK' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.DE',
|
||||
name: 'German Backoffice UI Localization',
|
||||
meta: { culture: 'de' },
|
||||
js: () => import('../../../assets/lang/de.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.EN',
|
||||
weight: 100,
|
||||
name: 'English (United Kingdom) Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'en',
|
||||
alias: 'Umb.Localization.DE_DE',
|
||||
name: 'German (Germany) Backoffice UI Localization',
|
||||
meta: { culture: 'de-DE' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.DE_CH',
|
||||
name: 'German (Switzerland) Backoffice UI Localization',
|
||||
meta: { culture: 'de-CH' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.EN',
|
||||
name: 'English (United Kingdom) Backoffice UI Localization',
|
||||
meta: { culture: 'en' },
|
||||
js: () => import('../../../assets/lang/en.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.EN_US',
|
||||
weight: 100,
|
||||
name: 'English (United States) Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'en-US',
|
||||
},
|
||||
meta: { culture: 'en-US' },
|
||||
js: () => import('../../../assets/lang/en-us.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.ES',
|
||||
weight: 100,
|
||||
name: 'Spanish Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'es',
|
||||
},
|
||||
meta: { culture: 'es' },
|
||||
js: () => import('../../../assets/lang/es.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.FR',
|
||||
weight: 100,
|
||||
name: 'French Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'fr',
|
||||
alias: 'Umb.Localization.ES_ES',
|
||||
name: 'Spanish (Spain) Backoffice UI Localization',
|
||||
meta: { culture: 'es-ES' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.FR',
|
||||
name: 'French Backoffice UI Localization',
|
||||
meta: { culture: 'fr' },
|
||||
js: () => import('../../../assets/lang/fr.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.HE',
|
||||
weight: 100,
|
||||
name: 'Hebrew Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'he',
|
||||
alias: 'Umb.Localization.FR_FR',
|
||||
name: 'French (France) Backoffice UI Localization',
|
||||
meta: { culture: 'fr-FR' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.FR_CH',
|
||||
name: 'French (Switzerland) Backoffice UI Localization',
|
||||
meta: { culture: 'fr-CH' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.HE',
|
||||
name: 'Hebrew Backoffice UI Localization',
|
||||
meta: { culture: 'he' },
|
||||
js: () => import('../../../assets/lang/he.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.HR',
|
||||
weight: 100,
|
||||
name: 'Croatian Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'hr',
|
||||
alias: 'Umb.Localization.HE_IL',
|
||||
name: 'Hebrew (Israel) Backoffice UI Localization',
|
||||
meta: { culture: 'he-IL' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.HR',
|
||||
name: 'Croatian Backoffice UI Localization',
|
||||
meta: { culture: 'hr' },
|
||||
js: () => import('../../../assets/lang/hr.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.IT',
|
||||
weight: 100,
|
||||
name: 'Italian Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'it',
|
||||
alias: 'Umb.Localization.HR_HR',
|
||||
name: 'Croatian (Croatia) Backoffice UI Localization',
|
||||
meta: { culture: 'hr-HR' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.IT',
|
||||
name: 'Italian Backoffice UI Localization',
|
||||
meta: { culture: 'it' },
|
||||
js: () => import('../../../assets/lang/it.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.JA',
|
||||
weight: 100,
|
||||
name: 'Japanese Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'ja',
|
||||
alias: 'Umb.Localization.IT_IT',
|
||||
name: 'Italian (Italy) Backoffice UI Localization',
|
||||
meta: { culture: 'it-IT' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.IT_CH',
|
||||
name: 'Italian (Switzerland) Backoffice UI Localization',
|
||||
meta: { culture: 'it-CH' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.JA',
|
||||
name: 'Japanese Backoffice UI Localization',
|
||||
meta: { culture: 'ja' },
|
||||
js: () => import('../../../assets/lang/ja.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.KO',
|
||||
weight: 100,
|
||||
name: 'Korean Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'ko',
|
||||
alias: 'Umb.Localization.JA_JP',
|
||||
name: 'Japanese (Japan) Backoffice UI Localization',
|
||||
meta: { culture: 'ja-JP' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.KO',
|
||||
name: 'Korean Backoffice UI Localization',
|
||||
meta: { culture: 'ko' },
|
||||
js: () => import('../../../assets/lang/ko.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.NB',
|
||||
weight: 100,
|
||||
name: 'Norwegian Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'nb',
|
||||
alias: 'Umb.Localization.KO_KR',
|
||||
name: 'Korean (Korea) Backoffice UI Localization',
|
||||
meta: { culture: 'ko-KR' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.NB',
|
||||
name: 'Norwegian Backoffice UI Localization',
|
||||
meta: { culture: 'nb' },
|
||||
js: () => import('../../../assets/lang/nb.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.NL',
|
||||
weight: 100,
|
||||
name: 'Dutch Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'nl',
|
||||
alias: 'Umb.Localization.NB_NO',
|
||||
name: 'Norwegian (Norway) Backoffice UI Localization',
|
||||
meta: { culture: 'nb-NO' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.NL',
|
||||
name: 'Dutch Backoffice UI Localization',
|
||||
meta: { culture: 'nl' },
|
||||
js: () => import('../../../assets/lang/nl.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.PL',
|
||||
weight: 100,
|
||||
name: 'Polish Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'pl',
|
||||
alias: 'Umb.Localization.NL_NL',
|
||||
name: 'Dutch (Netherlands) Backoffice UI Localization',
|
||||
meta: { culture: 'nl-NL' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.PL',
|
||||
name: 'Polish Backoffice UI Localization',
|
||||
meta: { culture: 'pl' },
|
||||
js: () => import('../../../assets/lang/pl.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.PT',
|
||||
weight: 100,
|
||||
name: 'Portuguese Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'pt',
|
||||
alias: 'Umb.Localization.PL_PL',
|
||||
name: 'Polish (Poland) Backoffice UI Localization',
|
||||
meta: { culture: 'pl-PL' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.PT',
|
||||
name: 'Portuguese Backoffice UI Localization',
|
||||
meta: { culture: 'pt' },
|
||||
js: () => import('../../../assets/lang/pt.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.PT_BR',
|
||||
weight: 100,
|
||||
name: 'Portuguese (Brazil) Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'pt-BR',
|
||||
},
|
||||
meta: { culture: 'pt-BR' },
|
||||
js: () => import('../../../assets/lang/pt-br.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.RO',
|
||||
weight: 100,
|
||||
name: 'Romanian Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'ro',
|
||||
},
|
||||
meta: { culture: 'ro' },
|
||||
js: () => import('../../../assets/lang/ro.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.RU',
|
||||
weight: 100,
|
||||
name: 'Russian Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'ru',
|
||||
alias: 'Umb.Localization.RO_RO',
|
||||
name: 'Romanian (Romania) Backoffice UI Localization',
|
||||
meta: { culture: 'ro-RO' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.RU',
|
||||
name: 'Russian Backoffice UI Localization',
|
||||
meta: { culture: 'ru' },
|
||||
js: () => import('../../../assets/lang/ru.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.SV',
|
||||
weight: 100,
|
||||
name: 'Swedish Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'sv',
|
||||
alias: 'Umb.Localization.RU_RU',
|
||||
name: 'Russian (Russia) Backoffice UI Localization',
|
||||
meta: { culture: 'ru-RU' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.SV',
|
||||
name: 'Swedish Backoffice UI Localization',
|
||||
meta: { culture: 'sv' },
|
||||
js: () => import('../../../assets/lang/sv.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.TR',
|
||||
weight: 100,
|
||||
name: 'Turkish Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'tr',
|
||||
alias: 'Umb.Localization.SV_SE',
|
||||
name: 'Swedish (Sweden) Backoffice UI Localization',
|
||||
meta: { culture: 'sv-SE' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.TR',
|
||||
name: 'Turkish Backoffice UI Localization',
|
||||
meta: { culture: 'tr' },
|
||||
js: () => import('../../../assets/lang/tr.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.UK',
|
||||
weight: 100,
|
||||
name: 'Ukrainian Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'uk',
|
||||
alias: 'Umb.Localization.TR_TR',
|
||||
name: 'Turkish (Türkiye) Backoffice UI Localization',
|
||||
meta: { culture: 'tr-TR' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.UK',
|
||||
name: 'Ukrainian Backoffice UI Localization',
|
||||
meta: { culture: 'uk' },
|
||||
js: () => import('../../../assets/lang/uk.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.ZH',
|
||||
weight: 100,
|
||||
name: 'Chinese Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'zh',
|
||||
alias: 'Umb.Localization.UK_UA',
|
||||
name: 'Ukrainian (Ukraine) Backoffice UI Localization',
|
||||
meta: { culture: 'uk-UA' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.ZH',
|
||||
name: 'Chinese Backoffice UI Localization',
|
||||
meta: { culture: 'zh' },
|
||||
js: () => import('../../../assets/lang/zh.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.ZH_TW',
|
||||
weight: 100,
|
||||
name: 'Chinese (Taiwan) Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'zh-TW',
|
||||
alias: 'Umb.Localization.ZH_CN',
|
||||
name: 'Chinese (Simplified, China) Backoffice UI Localization',
|
||||
meta: { culture: 'zh-CN' },
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.ZH_TW',
|
||||
name: 'Chinese (Taiwan) Backoffice UI Localization',
|
||||
meta: { culture: 'zh-TW' },
|
||||
js: () => import('../../../assets/lang/zh-tw.js'),
|
||||
},
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Umb.Localization.VI',
|
||||
weight: 100,
|
||||
name: 'Vietnamese Backoffice UI Localization',
|
||||
meta: {
|
||||
culture: 'vi',
|
||||
},
|
||||
meta: { culture: 'vi' },
|
||||
js: () => import('../../../assets/lang/vi.js'),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { UmbPropertyEditorRteValueType } from '../types.js';
|
||||
import { UMB_BLOCK_RTE_PROPERTY_EDITOR_SCHEMA_ALIAS } from '../constants.js';
|
||||
import { observeMultiple } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { jsonStringComparison, observeMultiple } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbBlockRteEntriesContext, UmbBlockRteManagerContext } from '@umbraco-cms/backoffice/block-rte';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
@@ -243,9 +243,12 @@ export abstract class UmbPropertyEditorUiRteElementBase
|
||||
([layouts, contents, settings, exposes]) => {
|
||||
if (layouts.length === 0) {
|
||||
if (super.value?.markup === undefined) {
|
||||
if (this.value === undefined) {
|
||||
return;
|
||||
}
|
||||
super.value = undefined;
|
||||
} else {
|
||||
super.value = {
|
||||
const newValue = {
|
||||
...super.value,
|
||||
blocks: {
|
||||
layout: {},
|
||||
@@ -254,9 +257,13 @@ export abstract class UmbPropertyEditorUiRteElementBase
|
||||
expose: [],
|
||||
},
|
||||
};
|
||||
if (jsonStringComparison(this.value, newValue)) {
|
||||
return;
|
||||
}
|
||||
super.value = newValue;
|
||||
}
|
||||
} else {
|
||||
super.value = {
|
||||
const newValue = {
|
||||
markup: this._markup,
|
||||
blocks: {
|
||||
layout: { [UMB_BLOCK_RTE_PROPERTY_EDITOR_SCHEMA_ALIAS]: layouts },
|
||||
@@ -265,6 +272,10 @@ export abstract class UmbPropertyEditorUiRteElementBase
|
||||
expose: exposes,
|
||||
},
|
||||
};
|
||||
if (jsonStringComparison(this.value, newValue)) {
|
||||
return;
|
||||
}
|
||||
super.value = newValue;
|
||||
}
|
||||
|
||||
// If we don't have a value set from the outside or an internal value, we don't want to set the value.
|
||||
|
||||
@@ -417,3 +417,22 @@ test('can add a variant block element with invariant RTE Tiptap in the content',
|
||||
await umbracoApi.documentType.ensureNameNotExists(customElementTypeName);
|
||||
await umbracoApi.language.ensureNameNotExists('Danish');
|
||||
});
|
||||
|
||||
// Tests regression issue: https://github.com/umbraco/Umbraco-CMS/issues/20680
|
||||
test('can move away from a content node with a block grid after making no changes without seeing discard unsaved changes', {tag: '@smoke'}, async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const customDataTypeId = await umbracoApi.dataType.createBlockGridWithPermissions(customDataTypeName, elementTypeId, true, true);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.documentType.goToSection(ConstantHelper.sections.settings);
|
||||
|
||||
// Assert
|
||||
// We do this to make sure that there is no discard changes button visible, if the discard changes was visible, we would not be able to go to the document type
|
||||
await umbracoUi.documentType.goToDocumentType(documentTypeName);
|
||||
});
|
||||
|
||||
|
||||
@@ -364,3 +364,21 @@ test('can add a variant block element with invariant RTE Tiptap in the content',
|
||||
await umbracoApi.documentType.ensureNameNotExists(customElementTypeName);
|
||||
await umbracoApi.language.ensureNameNotExists('Danish');
|
||||
});
|
||||
|
||||
// Tests regression issue: https://github.com/umbraco/Umbraco-CMS/issues/20680
|
||||
test('can move away from a content node with a block list after making no changes without seeing discard unsaved changes', {tag: '@smoke'}, async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const customDataTypeId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(customDataTypeName, elementTypeId);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.documentType.goToSection(ConstantHelper.sections.settings);
|
||||
|
||||
// Assert
|
||||
// We do this to make sure that there is no discard changes button visible, if the discard changes was visible, we would not be able to go to the document type
|
||||
await umbracoUi.documentType.goToDocumentType(documentTypeName);
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Configuration;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Migrations;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
@@ -38,6 +39,7 @@ internal sealed class AdvancedMigrationTests : UmbracoIntegrationTest
|
||||
private IServiceScopeFactory ServiceScopeFactory => GetRequiredService<IServiceScopeFactory>();
|
||||
private DistributedCache DistributedCache => GetRequiredService<DistributedCache>();
|
||||
private IDatabaseCacheRebuilder DatabaseCacheRebuilder => GetRequiredService<IDatabaseCacheRebuilder>();
|
||||
private IPublishedContentTypeFactory PublishedContentTypeFactory => GetRequiredService<IPublishedContentTypeFactory>();
|
||||
private IMigrationPlanExecutor MigrationPlanExecutor => new MigrationPlanExecutor(
|
||||
CoreScopeProvider,
|
||||
ScopeAccessor,
|
||||
@@ -48,7 +50,8 @@ internal sealed class AdvancedMigrationTests : UmbracoIntegrationTest
|
||||
DistributedCache,
|
||||
Mock.Of<IKeyValueService>(),
|
||||
ServiceScopeFactory,
|
||||
AppCaches.NoCache);
|
||||
AppCaches.NoCache,
|
||||
PublishedContentTypeFactory);
|
||||
|
||||
[Test]
|
||||
public async Task CreateTableOfTDtoAsync()
|
||||
|
||||
@@ -110,15 +110,16 @@ public class RichTextPropertyEditorHelperTests
|
||||
Assert.IsNull(value.Blocks);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Parse_Blocks_With_Both_Content_And_Settings()
|
||||
[TestCase(Constants.PropertyEditors.Aliases.RichText)]
|
||||
[TestCase("Umbraco.TinyMCE")]
|
||||
public void Can_Parse_Blocks_With_Both_Content_And_Settings(string propertyEditorAlias)
|
||||
{
|
||||
const string input = """
|
||||
string input = """
|
||||
{
|
||||
"markup": "<p>this is some markup</p><umb-rte-block data-content-key=\"36cc710a-d8a6-45d0-a07f-7bbd8742cf02\"><!--Umbraco-Block--></umb-rte-block>",
|
||||
"blocks": {
|
||||
"layout": {
|
||||
"Umbraco.RichText": [{
|
||||
"[PropertyEditorAlias]": [{
|
||||
"contentKey": "36cc710a-d8a6-45d0-a07f-7bbd8742cf02",
|
||||
"settingsKey": "d2eeef66-4111-42f4-a164-7a523eaffbc2"
|
||||
}
|
||||
@@ -143,6 +144,7 @@ public class RichTextPropertyEditorHelperTests
|
||||
}
|
||||
}
|
||||
""";
|
||||
input = input.Replace("[PropertyEditorAlias]", propertyEditorAlias);
|
||||
|
||||
var result = RichTextPropertyEditorHelper.TryParseRichTextEditorValue(input, JsonSerializer(), Logger(), out RichTextEditorValue? value);
|
||||
Assert.IsTrue(result);
|
||||
@@ -180,6 +182,12 @@ public class RichTextPropertyEditorHelperTests
|
||||
Assert.AreEqual("settingsPropertyAlias", settingsProperties.First().Alias);
|
||||
Assert.AreEqual("A settings property value", settingsProperties.First().Value);
|
||||
});
|
||||
|
||||
Assert.IsTrue(value.Blocks.Layout.ContainsKey(Constants.PropertyEditors.Aliases.RichText));
|
||||
var layout = value.Blocks.Layout[Constants.PropertyEditors.Aliases.RichText];
|
||||
Assert.AreEqual(1, layout.Count());
|
||||
Assert.AreEqual(Guid.Parse("36cc710a-d8a6-45d0-a07f-7bbd8742cf02"), layout.First().ContentKey);
|
||||
Assert.AreEqual(Guid.Parse("d2eeef66-4111-42f4-a164-7a523eaffbc2"), layout.First().SettingsKey);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -12,6 +12,7 @@ using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
@@ -85,7 +86,8 @@ public class MigrationPlanTests
|
||||
distributedCache,
|
||||
Mock.Of<IKeyValueService>(),
|
||||
Mock.Of<IServiceScopeFactory>(),
|
||||
appCaches);
|
||||
appCaches,
|
||||
Mock.Of<IPublishedContentTypeFactory>());
|
||||
|
||||
var plan = new MigrationPlan("default")
|
||||
.From(string.Empty)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json",
|
||||
"version": "16.4.0-rc2",
|
||||
"version": "16.4.0-rc3",
|
||||
"assemblyVersion": {
|
||||
"precision": "build"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user