Model, retrieval, mapping and display for fall back language editing

This commit is contained in:
AndyButland
2018-07-05 16:00:53 +02:00
parent 2bae3e2eda
commit 592de8bebc
12 changed files with 100 additions and 5 deletions

View File

@@ -0,0 +1,24 @@
using System.Linq;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
{
/// <summary>
/// Adds a new, self-joined field to umbracoLanguages to hold the fall-back language for
/// a given language.
/// </summary>
public class FallbackLanguage : MigrationBase
{
public FallbackLanguage(IMigrationContext context)
: base(context)
{ }
public override void Migrate()
{
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
if (columns.Any(x => x.TableName.InvariantEquals(Constants.DatabaseSchema.Tables.Language) && x.ColumnName.InvariantEquals("fallbackLanguageId")) == false)
AddColumn<LanguageDto>("fallbackLanguageId");
}
}
}

View File

@@ -33,5 +33,11 @@ namespace Umbraco.Core.Models
/// If true, a variant node cannot be published unless this language variant is created
/// </summary>
bool Mandatory { get; set; }
/// <summary>
/// Defines the fallback language that can be used in multi-lingual scenarios to provide
/// content if the requested language does not have it published.
/// </summary>
ILanguage FallbackLanguage { get; set; }
}
}

View File

@@ -19,6 +19,7 @@ namespace Umbraco.Core.Models
private string _cultureName;
private bool _isDefaultVariantLanguage;
private bool _mandatory;
private ILanguage _fallbackLanguage;
public Language(string isoCode)
{
@@ -32,6 +33,7 @@ namespace Umbraco.Core.Models
public readonly PropertyInfo CultureNameSelector = ExpressionHelper.GetPropertyInfo<Language, string>(x => x.CultureName);
public readonly PropertyInfo IsDefaultVariantLanguageSelector = ExpressionHelper.GetPropertyInfo<Language, bool>(x => x.IsDefaultVariantLanguage);
public readonly PropertyInfo MandatorySelector = ExpressionHelper.GetPropertyInfo<Language, bool>(x => x.Mandatory);
public readonly PropertyInfo FallbackLanguageSelector = ExpressionHelper.GetPropertyInfo<Language, ILanguage>(x => x.FallbackLanguage);
}
/// <summary>
@@ -71,5 +73,11 @@ namespace Umbraco.Core.Models
get => _mandatory;
set => SetPropertyValueAndDetectChanges(value, ref _mandatory, Ps.Value.MandatorySelector);
}
public ILanguage FallbackLanguage
{
get => _fallbackLanguage;
set => SetPropertyValueAndDetectChanges(value, ref _fallbackLanguage, Ps.Value.FallbackLanguageSelector);
}
}
}

View File

@@ -38,5 +38,14 @@ namespace Umbraco.Core.Persistence.Dtos
[Column("mandatory")]
[Constraint(Default = "0")]
public bool Mandatory { get; set; }
/// <summary>
/// Defines the fallback language that can be used in multi-lingual scenarios to provide
/// content if the requested language does not have it published.
/// </summary>
[Column("fallbackLanguageId")]
[ForeignKey(typeof(LanguageDto), Column = "id")]
[Index(IndexTypes.NonClustered)]
public int? FallbackLanguageId { get; set; }
}
}

View File

@@ -52,7 +52,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
sql.OrderBy<LanguageDto>(dto => dto.Id);
// get languages
var languages = Database.Fetch<LanguageDto>(sql).Select(ConvertFromDto).ToList();
var dtos = Database.Fetch<LanguageDto>(sql);
var languages = dtos.Select(ConvertFromDto).ToList();
PopulateFallbackLanguages(dtos, languages);
// initialize the code-id map
lock (_codeIdMap)
@@ -74,7 +76,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<ILanguage>(sqlClause, query);
var sql = translator.Translate();
return Database.Fetch<LanguageDto>(sql).Select(ConvertFromDto);
var dtos = Database.Fetch<LanguageDto>(sql);
var languages = dtos.Select(ConvertFromDto).ToList();
PopulateFallbackLanguages(dtos, languages);
return languages;
}
#endregion
@@ -199,7 +204,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
var entity = LanguageFactory.BuildEntity(dto);
return entity;
}
private static void PopulateFallbackLanguages(List<LanguageDto> dtos, IList<ILanguage> languages)
{
foreach (var dto in dtos.Where(x => x.FallbackLanguageId.HasValue))
{
var language = languages.Single(x => x.Id == dto.Id);
// ReSharper disable once PossibleInvalidOperationException (DTOs with fallback languages have already been filtered in the loop condition)
language.FallbackLanguage = languages.Single(x => x.Id == dto.FallbackLanguageId.Value);
}
}
public ILanguage GetByIsoCode(string isoCode)
{
TypedCachePolicy.GetAllCached(PerformGetAll); // ensure cache is populated, in a non-expensive way

View File

@@ -334,6 +334,7 @@
<Compile Include="Migrations\Upgrade\V_8_0_0\RefactorMacroColumns.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\SuperZero.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\TagsMigration.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\FallbackLanguage.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\UserForeignKeys.cs" />
<Compile Include="Models\AuditEntry.cs" />
<Compile Include="Models\Consent.cs" />

View File

@@ -56,6 +56,20 @@
});
$scope.properties = {
fallbackLanguage: {
alias: "fallbackLanguage",
description: "To allow multi-lingual content to fall back to another language if not present in the requested language, select it here.",
label: "Fall back language"
}
};
vm.loading = true;
languageResource.getAll().then(function (languages) {
vm.availableLanguages = languages;
vm.loading = false;
});
if(!$routeParams.create) {
vm.loading = true;

View File

@@ -14,11 +14,11 @@
hide-alias="true">
</umb-editor-header>
<umb-editor-container>
<umb-editor-container class="form-horizontal">
<umb-load-indicator ng-if="vm.loading"></umb-load-indicator>
<umb-box ng-if="!vm.loading" class="block-form">
<umb-box ng-if="!vm.loading">
<umb-box-content>
<umb-control-group label="@general_language" ng-if="vm.availableCultures">
@@ -64,6 +64,17 @@
</umb-control-group>
<umb-property property="properties.fallbackLanguage">
<div>
<select name="fallbackLanguage"
ng-model="vm.language.fallbackLanguage.id"
required
ng-options="l.id as l.name for l in vm.availableLanguages">
<option value="">No fall back language</option>
</select>
</div>
</umb-property>
</umb-box-content>
</umb-box>

View File

@@ -22,12 +22,14 @@
"treeHeaders_languages",
"general_mandatory",
"general_default",
"languages_fallsbackToLabel"
];
localizationService.localizeMany(labelKeys).then(function (values) {
vm.labels.languages = values[0];
vm.labels.mandatory = values[1];
vm.labels.general = values[2];
vm.labels.fallsbackTo = values[3];
// set page name
vm.page.name = vm.labels.languages;
});

View File

@@ -36,6 +36,7 @@
<span ng-if="language.isDefault">- {{vm.labels.general}}</span>
</td>
<td><span ng-if="language.isMandatory">{{vm.labels.mandatory}}</span></td>
<td><span ng-if="language.fallbackLanguage">{{vm.labels.fallsbackTo}}: {{language.fallbackLanguage.name}}</span></td>
<td style="text-align: right;">
<umb-button
ng-if="!language.isDefault"

View File

@@ -1661,6 +1661,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="defaultLanguage">Default language</key>
<key alias="defaultLanguageHelp">An Umbraco site can only have one default langugae set.</key>
<key alias="changingDefaultLanguageWarning">Switching default language may result in default content missing.</key>
<key alias="fallsbackToLabel">Falls back to</key>
</area>
<area alias="modelsBuilder">

View File

@@ -24,5 +24,8 @@ namespace Umbraco.Web.Models.ContentEditing
[DataMember(Name = "isMandatory")]
public bool Mandatory { get; set; }
[DataMember(Name = "fallbackLanguage")]
public Language FallbackLanguage { get; set; }
}
}