Fix merge

This commit is contained in:
Stephan
2018-09-13 14:59:45 +02:00
parent 33af498f88
commit 25c87d76b8
8 changed files with 78 additions and 53 deletions

View File

@@ -232,7 +232,7 @@ namespace Umbraco.Core.Migrations.Install
private void CreateLanguageData()
{
_database.Insert(Constants.DatabaseSchema.Tables.Language, "id", false, new LanguageDto { Id = 1, IsoCode = "en-US", CultureName = "English (United States)", IsDefaultVariantLanguage = true });
_database.Insert(Constants.DatabaseSchema.Tables.Language, "id", false, new LanguageDto { Id = 1, IsoCode = "en-US", CultureName = "English (United States)", IsDefault = true });
}
private void CreateContentChildTypeData()

View File

@@ -118,17 +118,18 @@ namespace Umbraco.Core.Migrations.Upgrade
Chain<IncreaseLanguageIsoCodeColumnLength>("{EB34B5DC-BB87-4005-985E-D983EA496C38}"); // from 7.12.0
Chain<RenameTrueFalseField>("{517CE9EA-36D7-472A-BF4B-A0D6FB1B8F89}"); // from 7.12.0
Chain<SetDefaultTagsStorageType>("{BBD99901-1545-40E4-8A5A-D7A675C7D2F2}"); // from 7.12.0
//Chain<UpdateDefaultMandatoryLanguage>("{2C87AA47-D1BC-4ECB-8A73-2D8D1046C27F}"); // stephan added that one = merge conflict, remove
Chain<FallbackLanguage>("{8B14CEBD-EE47-4AAD-A841-93551D917F11}"); // add andy's after others, with a new target state
From("{CF51B39B-9B9A-4740-BB7C-EAF606A7BFBF}") // and provide a path out of andy's
.CopyChain("{39E5B1F7-A50B-437E-B768-1723AEC45B65}", "{BBD99901-1545-40E4-8A5A-D7A675C7D2F2}", "{8B14CEBD-EE47-4AAD-A841-93551D917F11}"); // to final
.CopyChain("{39E5B1F7-A50B-437E-B768-1723AEC45B65}", "{BBD99901-1545-40E4-8A5A-D7A675C7D2F2}", "{8B14CEBD-EE47-4AAD-A841-93551D917F11}"); // to next
// resume at {8B14CEBD-EE47-4AAD-A841-93551D917F11} ...
Chain<UpdateDefaultMandatoryLanguage>("{guid1}"); // add stephan's after others, with a new target state
Chain<UpdateDefaultMandatoryLanguage>("{5F4597F4-A4E0-4AFE-90B5-6D2F896830EB}"); // add stephan's after others, with a new target state
From("{2C87AA47-D1BC-4ECB-8A73-2D8D1046C27F}") // and provide a path out of stephan's
.Chain<FallbackLanguage>("{guid1}");
#error is this OK?
.Chain<FallbackLanguage>("{5F4597F4-A4E0-4AFE-90B5-6D2F896830EB}"); // to next
// resume at {5F4597F4-A4E0-4AFE-90B5-6D2F896830EB} ...
//FINAL

View File

@@ -26,7 +26,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
var defaultId = int.MaxValue;
foreach (var dto in dtos)
{
if (dto.IsDefaultVariantLanguage)
if (dto.IsDefault)
{
defaultId = dto.Id;
break;
@@ -38,8 +38,8 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
// update, so that language with that id is now default and mandatory
var updateDefault = Sql()
.Update<LanguageDto>(u => u
.Set(x => x.IsDefaultVariantLanguage, true)
.Set(x => x.Mandatory, true))
.Set(x => x.IsDefault, true)
.Set(x => x.IsMandatory, true))
.Where<LanguageDto>(x => x.Id == defaultId);
Database.Execute(updateDefault);

View File

@@ -39,7 +39,7 @@ namespace Umbraco.Core.Persistence.Dtos
/// </summary>
[Column("isDefaultVariantLang")]
[Constraint(Default = "0")]
public bool IsDefaultVariantLanguage { get; set; }
public bool IsDefault { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the language is mandatory.

View File

@@ -12,7 +12,7 @@ namespace Umbraco.Core.Persistence.Factories
{
CultureName = dto.CultureName,
Id = dto.Id,
IsDefault = dto.IsDefaultVariantLanguage,
IsDefault = dto.IsDefault,
IsMandatory = dto.IsMandatory,
FallbackLanguageId = dto.FallbackLanguageId
};
@@ -28,7 +28,7 @@ namespace Umbraco.Core.Persistence.Factories
{
CultureName = entity.CultureName,
IsoCode = entity.IsoCode,
IsDefaultVariantLanguage = entity.IsDefault,
IsDefault = entity.IsDefault,
IsMandatory = entity.IsMandatory,
FallbackLanguageId = entity.FallbackLanguageId
};

View File

@@ -136,9 +136,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// set all other entities to non-default
// safe (no race cond) because the service locks languages
var setAllDefaultToFalse = Sql()
.Update<LanguageDto>(u => u.Set(x => x.IsDefaultVariantLanguage, false));
.Update<LanguageDto>(u => u.Set(x => x.IsDefault, false));
Database.Execute(setAllDefaultToFalse);
}
// fallback cycles are detected at service level
// insert
var dto = LanguageFactory.BuildDto(entity);
var id = Convert.ToInt32(Database.Insert(dto));
@@ -178,6 +181,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
throw new InvalidOperationException($"Cannot save the default language ({entity.IsoCode}) as non-default. Make another language the default language instead.");
}
// fallback cycles are detected at service level
// update
var dto = LanguageFactory.BuildDto(entity);
Database.Update(dto);
@@ -199,8 +204,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
throw new InvalidOperationException($"Cannot delete the default language ({entity.IsoCode}).");
// We need to remove any references to the language if it's being used as a fall-back from other ones
#error rewirte
Database.Execute(Sql().Update<LanguageDto>(u => u.Set(x => x.FallbackLanguageId, null)).Where<LanguageDto>(x => x.FallbackLanguageId == entity.Id));
var clearFallbackLanguage = Sql()
.Update<LanguageDto>(u => u
.Set(x => x.FallbackLanguageId, null))
.Where<LanguageDto>(x => x.FallbackLanguageId == entity.Id);
Database.Execute(clearFallbackLanguage);
// delete
base.PersistDeletedItem(entity);

View File

@@ -363,6 +363,16 @@ namespace Umbraco.Core.Services.Implement
// write-lock languages to guard against race conds when dealing with default language
scope.WriteLock(Constants.Locks.Languages);
// look for cycles - within write-lock
if (language.FallbackLanguageId.HasValue)
{
var languages = _languageRepository.GetMany().ToDictionary(x => x.Id, x => x);
if (!languages.ContainsKey(language.FallbackLanguageId.Value))
throw new InvalidOperationException($"Cannot save language {language.IsoCode} with fallback id={language.FallbackLanguageId.Value} which is not a valid language id.");
if (CreatesCycle(language, languages))
throw new InvalidOperationException($"Cannot save language {language.IsoCode} with fallback {languages[language.FallbackLanguageId.Value].IsoCode} as it would create a fallback cycle.");
}
var saveEventArgs = new SaveEventArgs<ILanguage>(language);
if (scope.Events.DispatchCancelable(SavingLanguage, this, saveEventArgs))
{
@@ -380,6 +390,20 @@ namespace Umbraco.Core.Services.Implement
}
}
private bool CreatesCycle(ILanguage language, IDictionary<int, ILanguage> languages)
{
// a new language is not referenced yet, so cannot be part of a cycle
if (!language.HasIdentity) return false;
var id = language.FallbackLanguageId;
while (true) // assuming languages does not already contains a cycle, this must end
{
if (!id.HasValue) return false; // no fallback means no cycle
if (id.Value == language.Id) return true; // back to language = cycle!
id = languages[id.Value].FallbackLanguageId; // else keep chaining
}
}
/// <summary>
/// Deletes a <see cref="ILanguage"/> by removing it (but not its usages) from the db
/// </summary>

View File

@@ -77,11 +77,8 @@ namespace Umbraco.Web.Editors
throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse(message));
}
if (langs.Any(x => x.FallbackLanguageId.HasValue && x.FallbackLanguageId.Value == language.Id))
{
var message = $"Language '{language.CultureName}' is defined as a fall-back language for one or more other languages, and so cannot be deleted.";
throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse(message));
}
// service is happy deleting a language that's fallback for another language,
// will just remove it - so no need to check here
Services.LocalizationService.Delete(language);
@@ -121,7 +118,7 @@ namespace Umbraco.Web.Editors
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
}
//create it
// create it (creating a new language cannot create a fallback cycle)
var newLang = new Core.Models.Language(culture.Name)
{
CultureName = culture.DisplayName,
@@ -147,46 +144,40 @@ namespace Umbraco.Web.Editors
existing.IsDefault = language.IsDefault;
existing.FallbackLanguageId = language.FallbackLanguageId;
string selectedFallbackLanguageCultureName;
if (DoesUpdatedFallbackLanguageCreateACircularPath(existing, out selectedFallbackLanguageCultureName))
// modifying an existing language can create a fallback, verify
// note that the service will check again, dealing with race conds
if (existing.FallbackLanguageId.HasValue)
{
ModelState.AddModelError("FallbackLanguage", "The selected fall back language '" + selectedFallbackLanguageCultureName + "' would create a circular path.");
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
var languages = Services.LocalizationService.GetAllLanguages().ToDictionary(x => x.Id, x => x);
if (!languages.ContainsKey(existing.FallbackLanguageId.Value))
{
ModelState.AddModelError("FallbackLanguage", "The selected fall back language does not exist.");
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
}
if (CreatesCycle(existing, languages))
{
ModelState.AddModelError("FallbackLanguage", $"The selected fall back language {languages[existing.FallbackLanguageId.Value].IsoCode} would create a circular path.");
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
}
}
Services.LocalizationService.Save(existing);
return Mapper.Map<Language>(existing);
}
private bool DoesUpdatedFallbackLanguageCreateACircularPath(ILanguage language, out string selectedFallbackLanguageCultureName)
{
if (language.FallbackLanguageId.HasValue == false)
{
selectedFallbackLanguageCultureName = string.Empty;
return false;
}
var languages = Services.LocalizationService.GetAllLanguages().ToArray();
var fallbackLanguageId = language.FallbackLanguageId;
while (fallbackLanguageId.HasValue)
{
if (fallbackLanguageId.Value == language.Id)
{
// We've found the current language in the path of fall back languages, so we have a circular path.
selectedFallbackLanguageCultureName = GetLanguageFromCollectionById(languages, fallbackLanguageId.Value).CultureName;
return true;
}
fallbackLanguageId = GetLanguageFromCollectionById(languages, fallbackLanguageId.Value).FallbackLanguageId;
}
selectedFallbackLanguageCultureName = string.Empty;
return false;
}
private static ILanguage GetLanguageFromCollectionById(IEnumerable<ILanguage> languages, int id)
// see LocalizationService
private bool CreatesCycle(ILanguage language, IDictionary<int, ILanguage> languages)
{
return languages.Single(x => x.Id == id);
// a new language is not referenced yet, so cannot be part of a cycle
if (!language.HasIdentity) return false;
var id = language.FallbackLanguageId;
while (true) // assuming languages does not already contains a cycle, this must end
{
if (!id.HasValue) return false; // no fallback means no cycle
if (id.Value == language.Id) return true; // back to language = cycle!
id = languages[id.Value].FallbackLanguageId; // else keep chaining
}
}
}
}