Skip to content

WIP: Language sortorder #19515

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

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/Umbraco.Core/Models/ILanguage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,12 @@ public interface ILanguage : IEntity, IRememberBeingDirty
/// </remarks>
[DataMember]
public string? FallbackIsoCode { get; set; }

/// <summary>
/// Gets or sets the sort order.
/// </summary>
/// <value>
/// The sort order.
/// </value>
int SortOrder { get; set; }
}
23 changes: 16 additions & 7 deletions src/Umbraco.Core/Models/Language.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class Language : EntityBase, ILanguage
private bool _isDefaultVariantLanguage;
private string _isoCode;
private bool _mandatory;
private int _sortOrder;

/// <summary>
/// Initializes a new instance of the <see cref="Language" /> class.
Expand All @@ -28,14 +29,14 @@ public Language(string isoCode, string cultureName)
_cultureName = cultureName ?? throw new ArgumentNullException(nameof(cultureName));
}

/// <inheritdoc />
[DataMember]
public string IsoCode
/// <inheritdoc />
[DataMember]
public string IsoCode
{
get => _isoCode;
set
{
get => _isoCode;
set
{
ArgumentNullException.ThrowIfNull(value);
ArgumentNullException.ThrowIfNull(value);

SetPropertyValueAndDetectChanges(value, ref _isoCode!, nameof(IsoCode));
}
Expand Down Expand Up @@ -78,4 +79,12 @@ public string? FallbackIsoCode
get => _fallbackLanguageIsoCode;
set => SetPropertyValueAndDetectChanges(value, ref _fallbackLanguageIsoCode, nameof(FallbackIsoCode));
}

/// <inheritdoc />
[DataMember]
public int SortOrder
{
get => _sortOrder;
set => SetPropertyValueAndDetectChanges(value, ref _sortOrder, nameof(SortOrder));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,5 +119,6 @@ protected virtual void DefinePlan()
// To 16.0.0
To<V_16_0_0.MigrateTinyMceToTiptap>("{C6681435-584F-4BC8-BB8D-BC853966AF0B}");
To<V_16_0_0.AddDocumentPropertyPermissions>("{D1568C33-A697-455F-8D16-48060CB954A1}");
To<V_16_1_0.AddSortOrderToLanguage>("{B1F2A6D3-4C8B-4E9F-8A5C-7D1B2E3F5A6B}");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
using NPoco;
using Umbraco.Cms.Core;
using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations;
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
using Umbraco.Cms.Infrastructure.Scoping;

namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_16_1_0;

public class AddSortOrderToLanguage : UnscopedMigrationBase
{
private const string NewColumnName = "sortOrder";
private readonly IScopeProvider _scopeProvider;

public AddSortOrderToLanguage(IMigrationContext context, IScopeProvider scopeProvider)
: base(context)
=> _scopeProvider = scopeProvider;

protected override void Migrate()
{
// If the new column already exists we'll do nothing.
if (ColumnExists(Constants.DatabaseSchema.Tables.Language, NewColumnName))
{
Context.Complete();
return;
}

using IScope scope = _scopeProvider.CreateScope();
using IDisposable notificationSuppression = scope.Notifications.Suppress();
ScopeDatabase(scope);

// SQL server can simply add the column, but for SQLite this won't work,
// so we'll have to create a new table and copy over data.
if (DatabaseType != DatabaseType.SQLite)
{
MigrateSqlServer();
Context.Complete();
return;
}

MigrateSqlite();
Context.Complete();

}

private void MigrateSqlServer()
{
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList();
AddColumnIfNotExists<LanguageDto>(columns, NewColumnName);
}

private void MigrateSqlite()
{
/*
* We commit the initial transaction started by the scope. This is required in order to disable the foreign keys.
* We then begin a new transaction, this transaction will be committed or rolled back by the scope, like normal.
* We don't have to worry about re-enabling the foreign keys, since these are enabled by default every time a connection is established.
*
* Ideally we'd want to do this with the unscoped database we get, however, this cannot be done,
* since our scoped database cannot share a connection with the unscoped database, so a new one will be created, which enables the foreign keys.
* Similarly we cannot use Database.CompleteTransaction(); since this also closes the connection,
* so starting a new transaction would re-enable foreign keys.
*/
Database.Execute("COMMIT;");
Database.Execute("PRAGMA foreign_keys=off;");
Database.Execute("BEGIN TRANSACTION;");

IEnumerable<LanguageDto> languages = Database.Fetch<OldLanguageDto>().Select(x => new LanguageDto
{
Id = x.Id,
IsoCode = x.IsoCode,
CultureName = x.CultureName,
IsDefault = x.IsDefault,
IsMandatory = x.IsMandatory,
FallbackLanguageId = x.FallbackLanguageId,
SortOrder = 0
});

Delete.Table(Constants.DatabaseSchema.Tables.Language).Do();
Create.Table<LanguageDto>().Do();

foreach (LanguageDto language in languages)
{
Database.Insert(Constants.DatabaseSchema.Tables.Language, "id", false, language);
}
}

[TableName(TableName)]
[PrimaryKey("id")]
[ExplicitColumns]
internal class OldLanguageDto
{
public const string TableName = Constants.DatabaseSchema.Tables.Language;

// Public constants to bind properties between DTOs
public const string IsoCodeColumnName = "languageISOCode";

/// <summary>
/// Gets or sets the identifier of the language.
/// </summary>
[Column("id")]
[PrimaryKeyColumn(IdentitySeed = 2)]
public short Id { get; set; }

/// <summary>
/// Gets or sets the ISO code of the language.
/// </summary>
[Column(IsoCodeColumnName)]
[Index(IndexTypes.UniqueNonClustered)]
[NullSetting(NullSetting = NullSettings.Null)]
[Length(14)]
public string? IsoCode { get; set; }

/// <summary>
/// Gets or sets the culture name of the language.
/// </summary>
[Column("languageCultureName")]
[NullSetting(NullSetting = NullSettings.Null)]
[Length(100)]
public string? CultureName { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the language is the default language.
/// </summary>
[Column("isDefaultVariantLang")]
[Constraint(Default = "0")]
public bool IsDefault { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the language is mandatory.
/// </summary>
[Column("mandatory")]
[Constraint(Default = "0")]
public bool IsMandatory { get; set; }

/// <summary>
/// Gets or sets the identifier of a fallback language.
/// </summary>
[Column("fallbackLanguageId")]
[ForeignKey(typeof(LanguageDto), Column = "id")]
[Index(IndexTypes.NonClustered)]
[NullSetting(NullSetting = NullSettings.Null)]
public int? FallbackLanguageId { get; set; }
}
}
3 changes: 3 additions & 0 deletions src/Umbraco.Infrastructure/Persistence/Dtos/DomainDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ internal class DomainDto
[ResultColumn("languageISOCode")]
public string IsoCode { get; set; } = null!;

/// <summary>
/// Gets or sets the sort order of the domain.
/// </summary>
[Column("sortOrder")]
public int SortOrder { get; set; }
}
6 changes: 6 additions & 0 deletions src/Umbraco.Infrastructure/Persistence/Dtos/LanguageDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,10 @@ internal class LanguageDto
[Index(IndexTypes.NonClustered)]
[NullSetting(NullSetting = NullSettings.Null)]
public int? FallbackLanguageId { get; set; }

/// <summary>
/// Gets or sets the sort order of the language.
/// </summary>
[Column("sortOrder")]
public int SortOrder { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public static ILanguage BuildEntity(LanguageDto dto, string? fallbackIsoCode)
Id = dto.Id,
IsDefault = dto.IsDefault,
IsMandatory = dto.IsMandatory,
FallbackIsoCode = fallbackIsoCode
SortOrder = dto.SortOrder,
FallbackIsoCode = fallbackIsoCode,
};

// Reset dirty initial properties
Expand All @@ -40,6 +41,7 @@ public static LanguageDto BuildDto(ILanguage entity, int? fallbackLanguageId)
CultureName = entity.CultureName,
IsDefault = entity.IsDefault,
IsMandatory = entity.IsMandatory,
SortOrder = entity.SortOrder,
FallbackLanguageId = fallbackLanguageId
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ protected override void DefineMaps()
DefineMap<Language, LanguageDto>(nameof(Language.Id), nameof(LanguageDto.Id));
DefineMap<Language, LanguageDto>(nameof(Language.IsoCode), nameof(LanguageDto.IsoCode));
DefineMap<Language, LanguageDto>(nameof(Language.CultureName), nameof(LanguageDto.CultureName));
DefineMap<Language, LanguageDto>(nameof(Language.SortOrder), nameof(LanguageDto.SortOrder));
}
}
Loading