Merge remote-tracking branch 'origin/v11/dev' into release/11.4

This commit is contained in:
Nikolaj
2023-05-03 10:03:47 +02:00
17 changed files with 260 additions and 55 deletions

View File

@@ -1,9 +1,10 @@
using System;
using System.Threading.Tasks;
using System.ComponentModel;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Infrastructure.ModelsBuilder;
using Umbraco.Cms.Infrastructure.ModelsBuilder.Building;
using Umbraco.Extensions;
@@ -14,12 +15,37 @@ public class BuildModelsBuilderController : ModelsBuilderControllerBase
{
private ModelsBuilderSettings _modelsBuilderSettings;
private readonly ModelsGenerationError _mbErrors;
private readonly ModelsGenerator _modelGenerator;
private readonly IModelsGenerator _modelGenerator;
// TODO: Remove in v13
private readonly ModelsGenerator? _concreteModelGenerator;
[Obsolete("This constructor is obsolete and will be removed in v13. Use the constructor with IModelsGenerator instead.")]
[Browsable(false)]
public BuildModelsBuilderController(
IOptionsMonitor<ModelsBuilderSettings> modelsBuilderSettings,
ModelsGenerationError mbErrors,
ModelsGenerator modelGenerator)
: this(modelsBuilderSettings, mbErrors, StaticServiceProvider.Instance.GetRequiredService<IModelsGenerator>())
{
}
[Obsolete("This constructor is obsolete and will be removed in v13. Use the constructor with only IModelsGenerator instead.")]
[Browsable(false)]
public BuildModelsBuilderController(
IOptionsMonitor<ModelsBuilderSettings> modelsBuilderSettings,
ModelsGenerationError mbErrors,
ModelsGenerator concreteModelGenerator,
IModelsGenerator modelGenerator)
: this(modelsBuilderSettings, mbErrors, modelGenerator)
{
}
[ActivatorUtilitiesConstructor]
public BuildModelsBuilderController(
IOptionsMonitor<ModelsBuilderSettings> modelsBuilderSettings,
ModelsGenerationError mbErrors,
IModelsGenerator modelGenerator)
{
_mbErrors = mbErrors;
_modelGenerator = modelGenerator;

View File

@@ -2232,7 +2232,7 @@ Mange hilsner fra Umbraco robotten
<key alias="allowedBlockColumnsHelp">Vælg de forskellige antal kolonner denne blok må optage i layoutet. Dette forhindre ikke blokken i at optræde i et mindre område.</key>
<key alias="allowedBlockRows">TIlgængelige række-størrelser</key>
<key alias="allowedBlockRowsHelp">Vælg hvor mange rækker denne blok på optage i layoutet.</key>
<key alias="allowBlockInRoot">Tillad på rodniveay</key>
<key alias="allowBlockInRoot">Tillad på rodniveau</key>
<key alias="allowBlockInRootHelp">Gør denne blok tilgængelig i layoutets rodniveau. Hvis dette ikke er valgt, kan denne blok kun bruges inden for andre blokkes definerede områder.</key>
<key alias="areas">Blok-områder</key>
<key alias="areasLayoutColumns">Layout-kolonner</key>
@@ -2240,7 +2240,7 @@ Mange hilsner fra Umbraco robotten
<key alias="areasConfigurations">Opsætning af områder</key>
<key alias="areasConfigurationsHelp">Hvis det skal være muligt at indsætte nye blokke indeni denne blok, skal der oprettes ét eller flere områder til at indsætte de nye blokke i.</key>
<key alias="invalidDropPosition">Ikke tilladt placering.</key>
<key alias="defaultLayoutStylesheet">Standart layout stylesheet</key>
<key alias="defaultLayoutStylesheet">Standardlayout stylesheet</key>
<key alias="confirmPasteDisallowedNestedBlockHeadline">Ikke tilladt indhold blev afvist</key>
<key alias="confirmPasteDisallowedNestedBlockMessage">
<![CDATA[Det indsatte indhold bestod af ikke tilladt del-indhold, disse dele er blevet afvist. Vil du beholde det resterene alligevel?]]></key>

View File

@@ -134,7 +134,7 @@
<key alias="saveToPublish">Spara och skicka för godkännande</key>
<key alias="schedulePublish">Schemaläggning</key>
<key alias="select">Välj</key>
<key alias="saveAndPreview">Förhandsgranska</key>
<key alias="saveAndPreview">Spara och förhandsgranska</key>
<key alias="showPageDisabled">Förhandsgranskning är avstängt på grund av att det inte finns någon mall tilldelad</key>
<key alias="somethingElse">Gör något annat</key>
<key alias="styleChoose">Välj stil</key>

View File

@@ -13,6 +13,24 @@ public class MemberBasic : ContentItemBasic<ContentPropertyBasic>
[DataMember(Name = "email")]
public string? Email { get; set; }
[DataMember(Name = "failedPasswordAttempts")]
public int FailedPasswordAttempts { get; set; }
[DataMember(Name = "approved")]
public bool Approved { get; set; }
[DataMember(Name = "lockedOut")]
public bool LockedOut { get; set; }
[DataMember(Name = "lastLockoutDate")]
public DateTime? LastLockoutDate { get; set; }
[DataMember(Name = "lastLoginDate")]
public DateTime? LastLoginDate { get; set; }
[DataMember(Name = "lastPasswordChangeDate")]
public DateTime? LastPasswordChangeDate { get; set; }
[DataMember(Name = "properties")]
public override IEnumerable<ContentPropertyBasic> Properties
{

View File

@@ -67,5 +67,8 @@ public class UmbracoPlan : MigrationPlan
// To 11.3.0
To<V_11_3_0.AddDomainSortOrder>("{BB3889ED-E2DE-49F2-8F71-5FD8616A2661}");
// To 11.4.0
To<V_11_4_0.AlterKeyValueDataType>("{FFB6B9B0-F1A8-45E9-9CD7-25700577D1CA}");
}
}

View File

@@ -0,0 +1,37 @@
using System.Reflection;
using NPoco;
using Umbraco.Cms.Infrastructure.Persistence;
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_11_4_0;
public class AlterKeyValueDataType : MigrationBase
{
private readonly IMigrationContext _context;
public AlterKeyValueDataType(IMigrationContext context)
: base(context) => _context = context;
protected override void Migrate()
{
// SQLite doesn't need this upgrade
if (_context.Database.DatabaseType.IsSqlite())
{
return;
}
string tableName = KeyValueDto.TableName;
string colName = "value";
// Determine the current datatype of the column within the database
string colDataType = Database.ExecuteScalar<string>($"SELECT TOP(1) CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS" +
$" WHERE TABLE_NAME = '{tableName}' AND COLUMN_NAME = '{colName}'");
// 255 is the old length, -1 indicate MAX length
if (colDataType == "255")
{
// Upgrade to MAX length
Database.Execute($"ALTER TABLE {tableName} ALTER COLUMN {colName} nvarchar(MAX)");
}
}
}

View File

@@ -1,7 +1,10 @@
using System.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Runtime;
@@ -26,7 +29,35 @@ public sealed class AutoModelsNotificationHandler : INotificationHandler<Umbraco
private readonly ILogger<AutoModelsNotificationHandler> _logger;
private readonly IMainDom _mainDom;
private readonly ModelsGenerationError _mbErrors;
private readonly ModelsGenerator _modelGenerator;
private readonly IModelsGenerator _modelGenerator;
// TODO: Remove in v13
private readonly ModelsGenerator? _concreteModelGenerator;
[Obsolete("This constructor is obsolete and will be removed in v13. Use the constructor with IModelsGenerator instead.")]
[Browsable(false)]
public AutoModelsNotificationHandler(
ILogger<AutoModelsNotificationHandler> logger,
IOptionsMonitor<ModelsBuilderSettings> config,
ModelsGenerator modelGenerator,
ModelsGenerationError mbErrors,
IMainDom mainDom)
: this(logger, config, StaticServiceProvider.Instance.GetRequiredService<IModelsGenerator>(), mbErrors, mainDom)
{
}
[Obsolete("This constructor is obsolete and will be removed in v13. Use the constructor with only IModelsGenerator instead.")]
[Browsable(false)]
public AutoModelsNotificationHandler(
ILogger<AutoModelsNotificationHandler> logger,
IOptionsMonitor<ModelsBuilderSettings> config,
ModelsGenerator concreteModelGenerator,
IModelsGenerator modelGenerator,
ModelsGenerationError mbErrors,
IMainDom mainDom)
: this(logger, config, modelGenerator, mbErrors, mainDom)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AutoModelsNotificationHandler" /> class.
@@ -34,7 +65,7 @@ public sealed class AutoModelsNotificationHandler : INotificationHandler<Umbraco
public AutoModelsNotificationHandler(
ILogger<AutoModelsNotificationHandler> logger,
IOptionsMonitor<ModelsBuilderSettings> config,
ModelsGenerator modelGenerator,
IModelsGenerator modelGenerator,
ModelsGenerationError mbErrors,
IMainDom mainDom)
{

View File

@@ -0,0 +1,7 @@
namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building
{
public interface IModelsGenerator
{
void GenerateModels();
}
}

View File

@@ -6,7 +6,7 @@ using Umbraco.Extensions;
namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building;
public class ModelsGenerator
public class ModelsGenerator : IModelsGenerator
{
private readonly IHostingEnvironment _hostingEnvironment;
private readonly OutOfDateModelsStatus _outOfDateModels;

View File

@@ -11,12 +11,15 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos;
[ExplicitColumns]
internal class KeyValueDto
{
public const string TableName = Constants.DatabaseSchema.Tables.KeyValue;
[Column("key")]
[Length(256)]
[PrimaryKeyColumn(AutoIncrement = false, Clustered = true)]
public string Key { get; set; } = null!;
[Column("value")]
[SpecialDbType(SpecialDbTypes.NVARCHARMAX)]
[NullSetting(NullSetting = NullSettings.Null)]
public string? Value { get; set; }

View File

@@ -280,6 +280,36 @@ public class MemberRepository : ContentRepositoryBase<int, IMember, MemberReposi
return SqlSyntax.GetFieldName<ContentTypeDto>(x => x.Alias);
}
if (ordering.OrderBy.InvariantEquals("failedPasswordAttempts"))
{
return SqlSyntax.GetFieldName<MemberDto>(x => x.FailedPasswordAttempts);
}
if (ordering.OrderBy.InvariantEquals("approved"))
{
return SqlSyntax.GetFieldName<MemberDto>(x => x.IsApproved);
}
if (ordering.OrderBy.InvariantEquals("lockedOut"))
{
return SqlSyntax.GetFieldName<MemberDto>(x => x.IsLockedOut);
}
if (ordering.OrderBy.InvariantEquals("lastLockoutDate"))
{
return SqlSyntax.GetFieldName<MemberDto>(x => x.LastLockoutDate);
}
if (ordering.OrderBy.InvariantEquals("lastLoginDate"))
{
return SqlSyntax.GetFieldName<MemberDto>(x => x.LastLoginDate);
}
if (ordering.OrderBy.InvariantEquals("lastPasswordChangeDate"))
{
return SqlSyntax.GetFieldName<MemberDto>(x => x.LastPasswordChangeDate);
}
return base.ApplySystemOrdering(ref sql, ordering);
}

View File

@@ -88,6 +88,12 @@ public class MemberMapDefinition : IMapDefinition
target.Udi = Udi.Create(Constants.UdiEntityType.Member, source.Key);
target.UpdateDate = source.UpdateDate;
target.Username = source.Username;
target.FailedPasswordAttempts = source.FailedPasswordAttempts;
target.Approved = source.IsApproved;
target.LockedOut = source.IsLockedOut;
target.LastLockoutDate = source.LastLockoutDate;
target.LastLoginDate = source.LastLoginDate;
target.LastPasswordChangeDate = source.LastPasswordChangeDate;
}
// Umbraco.Code.MapAll -Icon -Trashed -ParentId -Alias

View File

@@ -1,9 +1,12 @@
using System.ComponentModel;
using System.Runtime.Serialization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Infrastructure.ModelsBuilder;
using Umbraco.Cms.Infrastructure.ModelsBuilder.Building;
using Umbraco.Cms.Web.BackOffice.Controllers;
@@ -33,11 +36,40 @@ public class ModelsBuilderDashboardController : UmbracoAuthorizedJsonController
private readonly ModelsBuilderSettings _config;
private readonly DashboardReport _dashboardReport;
private readonly ModelsGenerationError _mbErrors;
private readonly ModelsGenerator _modelGenerator;
private readonly IModelsGenerator _modelGenerator;
private readonly OutOfDateModelsStatus _outOfDateModels;
public ModelsBuilderDashboardController(IOptions<ModelsBuilderSettings> config, ModelsGenerator modelsGenerator,
OutOfDateModelsStatus outOfDateModels, ModelsGenerationError mbErrors)
// TODO: Remove in v13
private readonly ModelsGenerator? _concreteModelGenerator;
[Obsolete("This constructor is obsolete and will be removed in v13. Use the constructor with IModelsGenerator instead.")]
[Browsable(false)]
public ModelsBuilderDashboardController(
IOptions<ModelsBuilderSettings> config,
ModelsGenerator modelsGenerator,
OutOfDateModelsStatus outOfDateModels,
ModelsGenerationError mbErrors)
: this(config, StaticServiceProvider.Instance.GetRequiredService<IModelsGenerator>(), outOfDateModels, mbErrors)
{
}
[Obsolete("This constructor is obsolete and will be removed in v13. Use the constructor with only IModelsGenerator instead.")]
[Browsable(false)]
public ModelsBuilderDashboardController(
IOptions<ModelsBuilderSettings> config,
ModelsGenerator concreteModelGenerator,
IModelsGenerator modelsGenerator,
OutOfDateModelsStatus outOfDateModels,
ModelsGenerationError mbErrors)
: this(config, modelsGenerator, outOfDateModels, mbErrors)
{
}
[ActivatorUtilitiesConstructor]
public ModelsBuilderDashboardController(IOptions<ModelsBuilderSettings> config,
IModelsGenerator modelsGenerator,
OutOfDateModelsStatus outOfDateModels,
ModelsGenerationError mbErrors)
{
_config = config.Value;
_modelGenerator = modelsGenerator;

View File

@@ -133,6 +133,8 @@ public static class UmbracoBuilderDependencyInjectionExtensions
builder.Services.TryAddSingleton<IModelsBuilderDashboardProvider, NoopModelsBuilderDashboardProvider>();
// Register required services for ModelsBuilderDashboardController
builder.Services.AddSingleton<IModelsGenerator, ModelsGenerator>();
// TODO: Remove in v13 - this is only here in case someone is already using this generator directly
builder.Services.AddSingleton<ModelsGenerator>();
builder.Services.AddSingleton<OutOfDateModelsStatus>();
builder.Services.AddSingleton<ModelsGenerationError>();

View File

@@ -10,42 +10,26 @@ function includePropsPreValsController($rootScope, $scope, localizationService,
$scope.propertyAliases = [];
$scope.selectedField = null;
$scope.systemFields = [
{ value: "sortOrder" },
{ value: "updateDate" },
{ value: "updater" },
{ value: "createDate" },
{ value: "owner" },
{ value: "published"},
{ value: "contentTypeAlias" },
{ value: "email" },
{ value: "username" }
{ value: "sortOrder", localizedKey: "general_sort" },
{ value: "updateDate", localizedKey: "content_updateDate" },
{ value: "updater", localizedKey: "content_updatedBy" },
{ value: "createDate", localizedKey: "content_createDate" },
{ value: "owner", localizedKey: "content_createBy" },
{ value: "published", localizedKey: "content_isPublished" },
{ value: "contentTypeAlias", localizedKey: "content_documentType" },
{ value: "email", localizedKey: "general_email" },
{ value: "username", localizedKey: "general_username" },
{ value: "failedPasswordAttempts", localizedKey: "user_failedPasswordAttempts" },
{ value: "approved", localizedKey: "user_stateApproved" },
{ value: "lockedOut", localizedKey: "user_stateLockedOut" },
{ value: "lastLockoutDate", localizedKey: "user_lastLockoutDate" },
{ value: "lastLoginDate", localizedKey: "user_lastLogin" },
{ value: "lastPasswordChangeDate", localizedKey: "user_lastPasswordChangeDate" }
];
$scope.getLocalizedKey = function(alias) {
switch (alias) {
case "name":
return "general_name";
case "sortOrder":
return "general_sort";
case "updateDate":
return "content_updateDate";
case "updater":
return "content_updatedBy";
case "createDate":
return "content_createDate";
case "owner":
return "content_createBy";
case "published":
return "content_isPublished";
case "contentTypeAlias":
//NOTE: This will just be 'Document' type even if it's for media/members since this is just a pre-val editor and we don't have a key for 'Content Type Alias'
return "content_documentType";
case "email":
return "general_email";
case "username":
return "general_username";
}
return alias;
$scope.getLocalizedKey = function (alias) {
const translationKey = $scope.systemFields.find(x => x.value === alias)?.localizedKey;
return translationKey !== undefined ? translationKey : alias;
}
$scope.changeField = function () {
@@ -65,16 +49,18 @@ function includePropsPreValsController($rootScope, $scope, localizationService,
e.name = v;
switch (e.value) {
case "published":
case "updater":
e.name += " (Content only)";
break;
case "published":
e.name += " (Content only)";
break;
case "email":
e.name += " (Members only)";
break;
case "username":
case "email":
case "failedPasswordAttempts":
case "approved":
case "lockedOut":
case "lastLockoutDate":
case "lastLoginDate":
case "lastPasswordChangeDate":
e.name += " (Members only)";
break;
}
@@ -174,4 +160,4 @@ function includePropsPreValsController($rootScope, $scope, localizationService,
}
angular.module("umbraco").controller("Umbraco.PrevalueEditors.IncludePropertiesListViewController", includePropsPreValsController);
angular.module("umbraco").controller("Umbraco.PrevalueEditors.IncludePropertiesListViewController", includePropsPreValsController);

View File

@@ -213,7 +213,13 @@ function listViewController($scope, $interpolate, $routeParams, $injector, $time
e.alias === "email" ||
e.alias === "updateDate" ||
e.alias === "createDate" ||
e.alias === "contentTypeAlias";
e.alias === "contentTypeAlias" ||
e.alias === "failedPasswordAttempts" ||
e.alias === "approved" ||
e.alias === "lockedOut" ||
e.alias === "lastLockoutDate" ||
e.alias === "lastLoginDate" ||
e.alias === "lastPasswordChangeDate";
}
if (e.isSystem) {
@@ -815,6 +821,18 @@ function listViewController($scope, $interpolate, $routeParams, $injector, $time
return "general_email";
case "username":
return "general_username";
case "failedPasswordAttempts":
return "user_failedPasswordAttempts";
case "approved":
return "user_stateApproved";
case "lockedOut":
return "user_stateLockedOut";
case "lastLockoutDate":
return "user_lastLockoutDate";
case "lastLoginDate":
return "user_lastLogin";
case "lastPasswordChangeDate":
return "user_lastPasswordChangeDate";
}
return alias;
}

View File

@@ -53,7 +53,13 @@ function sortByPreValsController($rootScope, $scope, localizationService, editor
{ value: "ContentTypeAlias", key: "content_documentType" },
{ value: "Published", key: "content_isPublished" },
{ value: "Email", key: "general_email" },
{ value: "Username", key: "general_username" }
{ value: "Username", key: "general_username" },
{ value: "failedPasswordAttempts", key: "user_failedPasswordAttempts" },
{ value: "approved", key: "user_stateApproved" },
{ value: "lockedOut", key: "user_stateLockedOut" },
{ value: "lastLockoutDate", key: "user_lastLockoutDate" },
{ value: "lastLoginDate", key: "user_lastLogin" },
{ value: "lastPasswordChangeDate", key: "user_lastPasswordChangeDate" }
];
_.each(systemFields, function (e) {
localizationService.localize(e.key).then(function (v) {