Skip to content

Commit 5d986e4

Browse files
committed
Merge remote-tracking branch 'origin/main' into v17/dev
2 parents 51575e5 + 3f0428c commit 5d986e4

File tree

12 files changed

+425
-25
lines changed

12 files changed

+425
-25
lines changed

src/Umbraco.Infrastructure/BackgroundJobs/RecurringBackgroundJobHostedServiceRunner.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ public async Task StartAsync(CancellationToken cancellationToken)
3737
_logger.LogDebug("Creating background hosted service for {job}", jobName);
3838
IHostedService hostedService = _jobFactory(job);
3939

40-
_logger.LogInformation("Starting background hosted service for {job}", jobName);
40+
_logger.LogInformation("Starting a background hosted service for {job} with a delay of {delay}, running every {period}", jobName, job.Delay, job.Period);
41+
4142
await hostedService.StartAsync(cancellationToken).ConfigureAwait(false);
4243

4344
_hostedServices.Add(new NamedServiceJob(jobName, hostedService));

src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ protected virtual void DefinePlan()
128128
To<V_16_3_0.AddRichTextEditorCapabilities>("{A917FCBC-C378-4A08-A36C-220C581A6581}");
129129
To<V_16_3_0.MigrateMediaTypeLabelProperties>("{FB7073AF-DFAF-4AC1-800D-91F9BD5B5238}");
130130

131+
// To 16.4.0
132+
To<V_16_4_0.CreateMissingTabs>("{6A7D3B80-8B64-4E41-A7C0-02EC39336E97}");
133+
131134
// To 17.0.0
132135
To<V_17_0_0.AddGuidsToAuditEntries>("{17D5F6CA-CEB8-462A-AF86-4B9C3BF91CF1}");
133136
To<V_17_0_0.MigrateCheckboxListDataTypesAndPropertyData>("{EB1E50B7-CD5E-4B6B-B307-36237DD2C506}");
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
using Microsoft.Extensions.Logging;
2+
using NPoco;
3+
using Umbraco.Cms.Infrastructure.Persistence;
4+
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
5+
using Umbraco.Extensions;
6+
7+
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_16_4_0;
8+
9+
/// <summary>
10+
/// Creates missing tabs on content types when a tab is referenced by both a composition and the content type's own groups.
11+
/// </summary>
12+
/// <remarks>
13+
/// In v13, if a tab had groups in both a composition and the content type, the tab might not exist on the content type itself.
14+
/// Newer versions require such tabs to also exist directly on the content type. This migration ensures those tabs are created.
15+
/// </remarks>
16+
[Obsolete("Remove in Umbraco 18.")]
17+
public class CreateMissingTabs : AsyncMigrationBase
18+
{
19+
private readonly ILogger<CreateMissingTabs> _logger;
20+
21+
/// <summary>
22+
/// Initializes a new instance of the <see cref="CreateMissingTabs"/> class.
23+
/// </summary>
24+
public CreateMissingTabs(IMigrationContext context, ILogger<CreateMissingTabs> logger)
25+
: base(context) => _logger = logger;
26+
27+
/// <inheritdoc/>
28+
protected override async Task MigrateAsync()
29+
{
30+
await ExecuteMigration(Database, _logger);
31+
Context.Complete();
32+
}
33+
34+
/// <summary>
35+
/// Performs the migration to create missing tabs.
36+
/// </summary>
37+
/// <remarks>
38+
/// Extracted into an internal static method to support integration testing.
39+
/// </remarks>
40+
internal static async Task ExecuteMigration(IUmbracoDatabase database, ILogger logger)
41+
{
42+
// 1. Find all property groups (type 0) and extract their tab alias (the part before the first '/').
43+
// This helps identify which groups are referencing tabs.
44+
Sql<ISqlContext> groupsSql = database.SqlContext.Sql()
45+
.SelectDistinct<PropertyTypeGroupDto>("g", pt => pt.ContentTypeNodeId)
46+
.AndSelect(GetTabAliasQuery(database.DatabaseType, "g.alias") + " AS tabAlias")
47+
.From<PropertyTypeDto>(alias: "p")
48+
.InnerJoin<PropertyTypeGroupDto>(alias: "g").On<PropertyTypeDto, PropertyTypeGroupDto>(
49+
(pt, ptg) => pt.PropertyTypeGroupId == ptg.Id && pt.ContentTypeId == ptg.ContentTypeNodeId,
50+
aliasLeft: "p",
51+
"g")
52+
.Where<PropertyTypeGroupDto>(x => x.Type == 0, alias: "g")
53+
.Where(CheckIfContainsTabAliasQuery(database.DatabaseType, "g.alias"));
54+
55+
// 2. Get all existing tabs (type 1) for all content types.
56+
Sql<ISqlContext> tabsSql = database.SqlContext.Sql()
57+
.Select<PropertyTypeGroupDto>("g2", g => g.UniqueId, g => g.ContentTypeNodeId, g => g.Alias)
58+
.From<PropertyTypeGroupDto>(alias: "g2")
59+
.Where<PropertyTypeGroupDto>(x => x.Type == 1, alias: "g2");
60+
61+
// 3. Identify groups that reference a tab alias which does not exist as a tab for their content type.
62+
// These are the "missing tabs" that need to be created.
63+
Sql<ISqlContext> missingTabsSql = database.SqlContext.Sql()
64+
.Select<PropertyTypeGroupDto>("groups", g => g.ContentTypeNodeId)
65+
.AndSelect("groups.tabAlias")
66+
.From()
67+
.AppendSubQuery(groupsSql, "groups")
68+
.LeftJoin(tabsSql, "tabs")
69+
.On("groups.ContentTypeNodeId = tabs.ContentTypeNodeId AND tabs.alias = groups.tabAlias")
70+
.WhereNull<PropertyTypeGroupDto>(ptg => ptg.UniqueId, "tabs");
71+
72+
// 4. For each missing tab, find the corresponding tab details (text, alias, sort order)
73+
// from the parent content type (composition) that already has this tab.
74+
Sql<ISqlContext> missingTabsWithDetailsSql = database.SqlContext.Sql()
75+
.Select<PropertyTypeGroupDto>("missingTabs", ptg => ptg.ContentTypeNodeId)
76+
.AndSelect<PropertyTypeGroupDto>("tg", ptg => ptg.Alias)
77+
.AndSelect("MIN(text) AS text", "MIN(sortorder) AS sortOrder")
78+
.From()
79+
.AppendSubQuery(missingTabsSql, "missingTabs")
80+
.InnerJoin<ContentType2ContentTypeDto>(alias: "ct2ct")
81+
.On<PropertyTypeGroupDto, ContentType2ContentTypeDto>(
82+
(ptg, ct2Ct) => ptg.ContentTypeNodeId == ct2Ct.ChildId,
83+
"missingTabs",
84+
"ct2ct")
85+
.InnerJoin<PropertyTypeGroupDto>(alias: "tg")
86+
.On<ContentType2ContentTypeDto, PropertyTypeGroupDto>(
87+
(ct2Ct, ptg) => ct2Ct.ParentId == ptg.ContentTypeNodeId,
88+
"ct2ct",
89+
"tg")
90+
.Append("AND tg.alias = missingTabs.tabAlias")
91+
.GroupBy<PropertyTypeGroupDto>("missingTabs", ptg => ptg.ContentTypeNodeId)
92+
.AndBy<PropertyTypeGroupDto>("tg", ptg => ptg.Alias);
93+
94+
List<MissingTabWithDetails> missingTabsWithDetails =
95+
await database.FetchAsync<MissingTabWithDetails>(missingTabsWithDetailsSql);
96+
97+
// 5. Create and insert new tab records for each missing tab, using the details from the parent/composition.
98+
IEnumerable<PropertyTypeGroupDto> newTabs = missingTabsWithDetails
99+
.Select(missingTabWithDetails => new PropertyTypeGroupDto
100+
{
101+
UniqueId = Guid.CreateVersion7(),
102+
ContentTypeNodeId = missingTabWithDetails.ContentTypeNodeId,
103+
Type = 1,
104+
Text = missingTabWithDetails.Text,
105+
Alias = missingTabWithDetails.Alias,
106+
SortOrder = missingTabWithDetails.SortOrder,
107+
});
108+
await database.InsertBatchAsync(newTabs);
109+
110+
logger.LogInformation(
111+
"Created {MissingTabCount} tab records to migrate property group information for content types derived from compositions.",
112+
missingTabsWithDetails.Count);
113+
}
114+
115+
private static string GetTabAliasQuery(DatabaseType databaseType, string columnName) =>
116+
databaseType == DatabaseType.SQLite
117+
? $"substr({columnName}, 1, INSTR({columnName},'/') - 1)"
118+
: $"SUBSTRING({columnName}, 1, CHARINDEX('/', {columnName}) - 1)";
119+
120+
private static string CheckIfContainsTabAliasQuery(DatabaseType databaseType, string columnName) =>
121+
databaseType == DatabaseType.SQLite
122+
? $"INSTR({columnName}, '/') > 0"
123+
: $"CHARINDEX('/', {columnName}) > 0";
124+
125+
private class MissingTabWithDetails
126+
{
127+
public required int ContentTypeNodeId { get; set; }
128+
129+
public required string Alias { get; set; }
130+
131+
public required string Text { get; set; }
132+
133+
public required int SortOrder { get; set; }
134+
}
135+
}

src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,26 @@ public static Sql<ISqlContext> GroupBy<TDto>(this Sql<ISqlContext> sql, params E
441441
return sql.GroupBy(columns);
442442
}
443443

444+
/// <summary>
445+
/// Appends a GROUP BY clause to the Sql statement.
446+
/// </summary>
447+
/// <typeparam name="TDto">The type of the Dto.</typeparam>
448+
/// <param name="sql">The Sql statement.</param>
449+
/// <param name="tableAlias">A table alias.</param>
450+
/// <param name="fields">Expression specifying the fields.</param>
451+
/// <returns>The Sql statement.</returns>
452+
public static Sql<ISqlContext> GroupBy<TDto>(
453+
this Sql<ISqlContext> sql,
454+
string tableAlias,
455+
params Expression<Func<TDto, object?>>[] fields)
456+
{
457+
ISqlSyntaxProvider sqlSyntax = sql.SqlContext.SqlSyntax;
458+
var columns = fields.Length == 0
459+
? sql.GetColumns<TDto>(withAlias: false)
460+
: fields.Select(x => sqlSyntax.GetFieldName(x, tableAlias)).ToArray();
461+
return sql.GroupBy(columns);
462+
}
463+
444464
/// <summary>
445465
/// Appends more ORDER BY or GROUP BY fields to the Sql statement.
446466
/// </summary>
@@ -596,7 +616,8 @@ public static Sql<ISqlContext>.SqlJoinClause<ISqlContext> LeftJoin(this Sql<ISql
596616
join += " " + sql.SqlContext.SqlSyntax.GetQuotedTableName(alias);
597617
}
598618

599-
return sql.LeftJoin(join);
619+
sql.Append("LEFT JOIN " + join, nestedSelect.Arguments);
620+
return new Sql<ISqlContext>.SqlJoinClause<ISqlContext>(sql);
600621
}
601622

602623
/// <summary>
@@ -907,6 +928,29 @@ public static Sql<ISqlContext> SelectDistinct<TDto>(this Sql<ISqlContext> sql, p
907928
return sql;
908929
}
909930

931+
/// <summary>
932+
/// Creates a SELECT DISTINCT Sql statement.
933+
/// </summary>
934+
/// <typeparam name="TDto">The type of the DTO to select.</typeparam>
935+
/// <param name="sql">The origin sql.</param>
936+
/// <param name="tableAlias">A table alias.</param>
937+
/// <param name="fields">Expressions indicating the columns to select.</param>
938+
/// <returns>The Sql statement.</returns>
939+
/// <remarks>
940+
/// <para>If <paramref name="fields"/> is empty, all columns are selected.</para>
941+
/// </remarks>
942+
public static Sql<ISqlContext> SelectDistinct<TDto>(this Sql<ISqlContext> sql, string tableAlias, params Expression<Func<TDto, object?>>[] fields)
943+
{
944+
if (sql == null)
945+
{
946+
throw new ArgumentNullException(nameof(sql));
947+
}
948+
949+
var columns = sql.GetColumns(tableAlias: tableAlias, columnExpressions: fields);
950+
sql.Append("SELECT DISTINCT " + string.Join(", ", columns));
951+
return sql;
952+
}
953+
910954
public static Sql<ISqlContext> SelectDistinct(this Sql<ISqlContext> sql, params object[] columns)
911955
{
912956
sql.Append("SELECT DISTINCT " + string.Join(", ", columns));

src/Umbraco.Web.UI.Client/src/assets/lang/da.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2454,26 +2454,27 @@ export default {
24542454
headlineEditorAppearance: 'Redigerings udseende',
24552455
headlineDataModels: 'Data modeller',
24562456
headlineCatalogueAppearance: 'Katalog udseende',
2457-
labelBackgroundColor: 'Baggrunds farve',
2458-
labelIconColor: 'Ikon farve',
2457+
labelBackgroundColor: 'Baggrundsfarve',
2458+
labelIconColor: 'Ikonfarve',
24592459
labelContentElementType: 'Indholds model',
24602460
labelLabelTemplate: 'Label',
24612461
labelCustomView: 'Speciel visning',
24622462
labelCustomViewInfoTitle: 'Vis speciel visning beskrivelsen',
24632463
labelCustomViewDescription:
2464-
'Overskrift hvordan denne block præsenteres i backoffice interfacet. Vælg en\n .html fil der indeholder din præsensation.\n ',
2464+
'Overskrift hvordan denne blok præsenteres i backoffice interfacet. Vælg en\n .html fil der indeholder din præsensation.\n ',
24652465
labelSettingsElementType: 'Indstillings model',
24662466
labelEditorSize: 'Rederings lagets størrelse',
24672467
addCustomView: 'Tilføj speciel visning',
2468-
addSettingsElementType: 'Tilføj instillinger',
2468+
addSettingsElementType: 'Tilføj indstillinger',
24692469
confirmDeleteBlockTitle: 'Slet %0',
24702470
confirmDeleteBlockMessage: 'Er du sikker på at du vil slette indholdet <strong>%0%</strong>?',
2471+
confirmDeleteBlockTypeTitle: 'Slet konfiguration for blokken?',
24712472
confirmDeleteBlockTypeMessage: 'Er du sikker på at du vil slette konfigurationen <strong>%0%</strong>?',
24722473
confirmDeleteBlockTypeNotice:
24732474
'Indholdet vil stadigt eksistere, men redigering af dette indhold vil ikke\n være muligt. Indholdet vil blive vist som ikke understøttet indhold.\n ',
24742475
confirmDeleteBlockGroupTitle: 'Slet gruppe?',
24752476
confirmDeleteBlockGroupMessage:
2476-
'Er du sikker på at du vil slette gruppen <strong>%0%</strong> og blok konfigurationer?',
2477+
'Er du sikker på at du vil slette gruppen <strong>%0%</strong>?',
24772478
confirmDeleteBlockGroupNotice:
24782479
'Indholdet af gruppens blokke vil stadigt eksistere, men redigering af dette indhold vil ikke\n være muligt. Indholdet vil blive vist som ikke understøttet indhold.\n ',
24792480
blockConfigurationOverlayTitle: "Konfiguration af '%0%'",
@@ -2508,7 +2509,7 @@ export default {
25082509
areaAllowedBlocks: 'Tilladte blok-typer',
25092510
areaAllowedBlocksHelp:
25102511
'Vælg de blok-typer, der er tilladt i dette område, og evt. også hvor mange af hver type, redaktørerne skal tilføje til området.',
2511-
areaAllowedBlocksEmpty: 'Når denne er tom er alle block-typer tilladt for områder tilladt.',
2512+
areaAllowedBlocksEmpty: 'Når denne er tom er alle blok-typer tilladt for områder tilladt.',
25122513
confirmDeleteBlockAreaMessage: 'Er du sikker på, at du vil slette dette område?',
25132514
confirmDeleteBlockAreaNotice: 'Alle blokke, der er oprettet i dette område, vil blive slettet.',
25142515
layoutOptions: 'Layout-opsætning',
@@ -2541,9 +2542,9 @@ export default {
25412542
areaCreateLabelTitle: 'Tilføj indhold label',
25422543
areaCreateLabelHelp: 'Overskriv labellen for tilføj indholds knappen i dette område.',
25432544
showSizeOptions: 'Tilføj skalerings muligheder',
2544-
addBlockType: 'Tilføj Blok',
2545+
addBlockType: 'Tilføj blok',
25452546
addBlockGroup: 'Tilføj gruppe',
2546-
pickSpecificAllowance: 'Tilføj gruppe eller Blok',
2547+
pickSpecificAllowance: 'Tilføj gruppe eller blok',
25472548
allowanceMinimum: 'Sæt minimum krav',
25482549
allowanceMaximum: 'Sæt maksimum krav',
25492550
block: 'Blok',
@@ -2554,23 +2555,23 @@ export default {
25542555
headlineAllowance: 'Tilladelser',
25552556
getSampleHeadline: 'Installer demo konfiguration',
25562557
getSampleDescription:
2557-
'Dette tilføjer basale og hjælper dig til at komme igang med Block Grid Editor.<br/>Dette indeholder Blokke for Overskrift, Beriget-Tekst, Billede og To-Koloners-Layout.',
2558+
'Dette tilføjer basale og hjælper dig til at komme igang med Block Grid Editor.<br/>Dette indeholder blokke for Overskrift, Beriget-Tekst, Billede og To-Koloners-Layout.',
25582559
getSampleButton: 'Installer',
25592560
actionEnterSortMode: 'Sortings tilstand',
25602561
actionExitSortMode: 'Afslut sortings tilstand',
2561-
areaAliasIsNotUnique: 'Dette område alias skal være unikt sammenlignet med andre områder af denne Blok.',
2562+
areaAliasIsNotUnique: 'Dette område alias skal være unikt sammenlignet med andre områder af denne blok.',
25622563
configureArea: 'Konfigurer område',
25632564
deleteArea: 'Slet område',
25642565
addColumnSpanOption: 'Tilføj mulighed for %0% koloner',
25652566
allowBlockInAreas: 'Allow in areas',
25662567
allowBlockInAreasHelp:
25672568
'Make this block available by default within the areas of other Blocks (unless explicit permissions are set for these areas).',
25682569
createThisFor: (name: string, variantName: string) =>
2569-
variantName ? `Opret ${name} for ${variantName}` : `Create ${name}`,
2570-
insertBlock: 'Indsæt Block',
2570+
variantName ? `Opret ${name} for ${variantName}` : `Opret ${name}`,
2571+
insertBlock: 'Indsæt blok',
25712572
labelInlineMode: 'Indsæt på linje med tekst',
25722573
notExposedLabel: 'ikke oprettet',
2573-
notExposedDescription: 'Denne Block er endnu ikke oprettet for denne variant',
2574+
notExposedDescription: 'Denne blok er endnu ikke oprettet for denne variant',
25742575
unsupportedBlockName: 'Ugyldigt indhold',
25752576
unsupportedBlockDescription:
25762577
'Dette indhold er ikke længere understøttet. Hvis du mangler dette indhold bør du kontakte din administrator. Ellers bør du slette dette indhold.',

src/Umbraco.Web.UI.Client/src/assets/lang/en.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2600,12 +2600,13 @@ export default {
26002600
addSettingsElementType: 'Add settings',
26012601
confirmDeleteBlockTitle: 'Delete %0%?',
26022602
confirmDeleteBlockMessage: 'Are you sure you want to delete this %0%?',
2603+
confirmDeleteBlockTypeTitle: 'Delete block configuration?',
26032604
confirmDeleteBlockTypeMessage: 'Are you sure you want to delete the block configuration <strong>%0%</strong>?',
26042605
confirmDeleteBlockTypeNotice:
26052606
'The content of this block will still be present, editing of this content will no longer be available and will be shown as unsupported content.',
26062607
confirmDeleteBlockGroupTitle: 'Delete group?',
26072608
confirmDeleteBlockGroupMessage:
2608-
'Are you sure you want to delete group <strong>%0%</strong> and all the Block configurations of this?',
2609+
'Are you sure you want to delete group <strong>%0%</strong>?',
26092610
confirmDeleteBlockGroupNotice:
26102611
'The content of these Blocks will still be present, editing of this content will no longer be available and will be shown as unsupported content.',
26112612
blockConfigurationOverlayTitle: "Configuration of '%0%'",

src/Umbraco.Web.UI.Client/src/assets/lang/nb.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,8 +1220,7 @@ export default {
12201220
confirmCancelBlockCreationMessage: 'Er du sikker på at du vil avbryte opprettelsen?',
12211221
confirmDeleteBlockAreaMessage: 'Er du sikker på at du vil slette dette området?',
12221222
confirmDeleteBlockAreaNotice: 'Alle blokker som for øyeblikket er opprettet i dette området, vil bli slettet.',
1223-
confirmDeleteBlockGroupMessage:
1224-
'Er du sikker på at du vil slette gruppen <strong>%0%</strong> og alle blokk-konfigurasjonene for denne?',
1223+
confirmDeleteBlockGroupMessage: 'Er du sikker på at du vil slette gruppen <strong>%0%</strong>?',
12251224
confirmDeleteBlockGroupNotice:
12261225
'Innholdet i disse blokkene vil fortsatt være tilstede, men redigering av dette innholdet\n vil ikke lenger være tilgjengelig og vil bli vist som ikke støttet innhold.\n ',
12271226
confirmDeleteBlockMessage: 'Er du sikker på at du vil slette innholdet <strong>%0%</strong>?',

src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-type-configuration/property-editor-ui-block-grid-type-configuration.element.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ export class UmbPropertyEditorUIBlockGridTypeConfigurationElement
182182
const groupName = this.#blockGroups?.find((group) => group.key === groupKey)?.name ?? '';
183183
await umbConfirmModal(this, {
184184
headline: '#blockEditor_confirmDeleteBlockGroupTitle',
185-
content: this.localize.term('#blockEditor_confirmDeleteBlockGroupMessage', [groupName]),
185+
content: this.localize.term('blockEditor_confirmDeleteBlockGroupMessage', [groupName]),
186186
color: 'danger',
187187
confirmLabel: '#general_delete',
188188
});

src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/input-block-type/input-block-type.element.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,9 @@ export class UmbInputBlockTypeElement<
150150
const contentType = store.getItems([item.contentElementTypeKey]);
151151
await umbConfirmModal(this, {
152152
color: 'danger',
153-
headline: `Remove ${contentType[0]?.name}?`,
154-
// TODO: Translations: [NL]
155-
content: 'Are you sure you want to remove this Block Type Configuration?',
156-
confirmLabel: 'Remove',
153+
headline: '#blockEditor_confirmDeleteBlockTypeTitle',
154+
content: this.localize.term('blockEditor_confirmDeleteBlockTypeMessage', [contentType[0]?.name]),
155+
confirmLabel: '#general_remove',
157156
});
158157
this.deleteItem(item.contentElementTypeKey);
159158
}

src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/upload-field/Umbraco.UploadField.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const manifest: ManifestPropertyEditorSchema = {
1212
alias: 'fileExtensions',
1313
label: 'Accepted file extensions',
1414
description:
15-
'Insert one extension per line, for example `.jpg`.\n\nYou can also use mime types, for example `image/*` or `application/pdf`.',
15+
'Insert one extension per line, for example `jpg`.\n\nYou can also use mime types, for example `image/*` or `application/pdf`.',
1616
propertyEditorUiAlias: 'Umb.PropertyEditorUi.AcceptedUploadTypes',
1717
},
1818
],

0 commit comments

Comments
 (0)