Merge branch origin/dev-v8 into dev-v8

This commit is contained in:
Stephan
2018-04-03 14:57:55 +02:00
44 changed files with 9633 additions and 262 deletions

View File

@@ -23,6 +23,7 @@ namespace Umbraco.Web.Composing.CompositionRoots
container.Register<TagMapperProfile>();
container.Register<TemplateMapperProfile>();
container.Register<UserMapperProfile>();
container.Register<LanguageMapperProfile>();
}
}
}

View File

@@ -285,6 +285,10 @@ namespace Umbraco.Web.Editors
{
"backOfficeAssetsApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<BackOfficeAssetsController>(
controller => controller.GetSupportedMomentLocales())
},
{
"languageApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<LanguageController>(
controller => controller.GetAllLanguages())
}
}
},

View File

@@ -1,10 +1,17 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using AutoMapper;
using Umbraco.Core;
using Umbraco.Core.Persistence;
using Umbraco.Web.Models;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
namespace Umbraco.Web.Editors
{
@@ -12,6 +19,7 @@ namespace Umbraco.Web.Editors
/// Backoffice controller supporting the dashboard for language administration.
/// </summary>
[PluginController("UmbracoApi")]
[PrefixlessBodyModelValidator]
public class LanguageController : UmbracoAuthorizedJsonController
{
/// <summary>
@@ -19,10 +27,12 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <returns></returns>
[HttpGet]
public IEnumerable<Culture> GetAllCultures()
public IDictionary<string, string> GetAllCultures()
{
return CultureInfo.GetCultures(CultureTypes.AllCultures)
.Select(x => new Culture {IsoCode = x.Name, Name = x.DisplayName});
return
CultureInfo.GetCultures(CultureTypes.AllCultures)
.Where(x => !x.Name.IsNullOrWhiteSpace())
.OrderBy(x => x.DisplayName).ToDictionary(x => x.Name, x => x.DisplayName);
}
/// <summary>
@@ -32,52 +42,125 @@ namespace Umbraco.Web.Editors
[HttpGet]
public IEnumerable<Language> GetAllLanguages()
{
var allLanguages = Services.LocalizationService.GetAllLanguages();
var allLanguages = Services.LocalizationService.GetAllLanguages().OrderBy(x => x.Id).ToList();
var langs = Mapper.Map<IEnumerable<Language>>(allLanguages).ToList();
return allLanguages.Select(x => new Language
//if there's only one language, by default it is the default
if (langs.Count == 1)
{
Id = x.Id,
IsoCode = x.IsoCode,
Name = x.CultureInfo.DisplayName,
IsDefaultVariantLanguage = x.IsDefaultVariantLanguage,
Mandatory = x.Mandatory
});
langs[0].IsDefaultVariantLanguage = true;
langs[0].Mandatory = true;
}
else if (allLanguages.All(x => !x.IsDefaultVariantLanguage))
{
//if no language has the default flag, then the defaul language is the one with the lowest id
langs[0].IsDefaultVariantLanguage = true;
langs[0].Mandatory = true;
}
return langs.OrderBy(x => x.Name);
}
[HttpGet]
public Language GetLanguage(int id)
{
var lang = Services.LocalizationService.GetLanguageById(id);
if (lang == null)
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
var model = Mapper.Map<Language>(lang);
//if there's only one language, by default it is the default
var allLangs = Services.LocalizationService.GetAllLanguages().OrderBy(x => x.Id).ToList();
if (!lang.IsDefaultVariantLanguage)
{
if (allLangs.Count == 1)
{
model.IsDefaultVariantLanguage = true;
model.Mandatory = true;
}
else if (allLangs.All(x => !x.IsDefaultVariantLanguage))
{
//if no language has the default flag, then the defaul language is the one with the lowest id
model.IsDefaultVariantLanguage = allLangs[0].Id == lang.Id;
model.Mandatory = allLangs[0].Id == lang.Id;
}
}
return model;
}
/// <summary>
/// Deletes a language with a given ID
/// </summary>
[HttpDelete]
[HttpPost]
public void DeleteLanguage(int id)
public IHttpActionResult DeleteLanguage(int id)
{
var language = Services.LocalizationService.GetLanguageById(id);
if (language == null)
if (language == null) return NotFound();
var totalLangs = Services.LocalizationService.GetAllLanguages().Count();
if (language.IsDefaultVariantLanguage || totalLangs == 1)
{
throw new EntityNotFoundException(id, $"Could not find language by id: '{id}'.");
var message = $"Language '{language.IsoCode}' is currently set to 'default' or it is the only installed language and can not be deleted.";
throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse(message));
}
Services.LocalizationService.Delete(language);
return Ok();
}
/// <summary>
/// Saves a bulk set of languages with default/mandatory settings and returns the full set of languages configured.
/// Creates or saves a language
/// </summary>
[HttpPost]
public IEnumerable<Language> SaveLanguages(IEnumerable<Language> languages)
public Language SaveLanguage(Language language)
{
foreach (var l in languages)
if (!ModelState.IsValid)
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
var found = Services.LocalizationService.GetLanguageByIsoCode(language.IsoCode);
if (found != null && language.Id != found.Id)
{
var language = Services.LocalizationService.GetLanguageByIsoCode(l.IsoCode);
if (language == null)
{
language = new Core.Models.Language(l.IsoCode);
}
language.Mandatory = l.Mandatory;
language.IsDefaultVariantLanguage = l.IsDefaultVariantLanguage;
Services.LocalizationService.Save(language);
//someone is trying to create a language that alraedy exist
ModelState.AddModelError("IsoCode", "The language " + language.IsoCode + " already exists");
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
}
return GetAllLanguages();
if (found == null)
{
CultureInfo culture;
try
{
culture = CultureInfo.GetCultureInfo(language.IsoCode);
}
catch (CultureNotFoundException)
{
ModelState.AddModelError("IsoCode", "No Culture found with name " + language.IsoCode);
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
}
//create it
var newLang = new Umbraco.Core.Models.Language(culture.Name)
{
CultureName = culture.DisplayName,
IsDefaultVariantLanguage = language.IsDefaultVariantLanguage,
Mandatory = language.Mandatory
};
Services.LocalizationService.Save(newLang);
return Mapper.Map<Language>(newLang);
}
found.Mandatory = language.Mandatory;
found.IsDefaultVariantLanguage = language.IsDefaultVariantLanguage;
Services.LocalizationService.Save(found);
return Mapper.Map<Language>(found);
}
}
}
}

View File

@@ -0,0 +1,28 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
namespace Umbraco.Web.Models.ContentEditing
{
[DataContract(Name = "language", Namespace = "")]
public class Language
{
[DataMember(Name = "id")]
public int Id { get; set; }
[DataMember(Name = "culture", IsRequired = true)]
[Required(AllowEmptyStrings = false)]
public string IsoCode { get; set; }
[DataMember(Name = "name")]
[ReadOnly(true)]
public string Name { get; set; }
[DataMember(Name = "isDefault")]
public bool IsDefaultVariantLanguage { get; set; }
[DataMember(Name = "isMandatory")]
public bool Mandatory { get; set; }
}
}

View File

@@ -1,8 +0,0 @@
namespace Umbraco.Web.Models
{
public class Culture
{
public string IsoCode { get; set; }
public string Name { get; set; }
}
}

View File

@@ -1,13 +0,0 @@
using System.Globalization;
namespace Umbraco.Web.Models
{
public class Language
{
public int Id { get; set; }
public string IsoCode { get; set; }
public string Name { get; set; }
public bool IsDefaultVariantLanguage { get; set; }
public bool Mandatory { get; set; }
}
}

View File

@@ -0,0 +1,17 @@
using System.Globalization;
using AutoMapper;
using Umbraco.Core.Models;
using Umbraco.Web.Models.ContentEditing;
using Language = Umbraco.Web.Models.ContentEditing.Language;
namespace Umbraco.Web.Models.Mapping
{
internal class LanguageMapperProfile : Profile
{
public LanguageMapperProfile()
{
CreateMap<ILanguage, Language>()
.ForMember(l => l.Name, expression => expression.MapFrom(x => x.CultureInfo.DisplayName));
}
}
}

View File

@@ -34,7 +34,7 @@ namespace Umbraco.Web.Trees
//this will load in a custom UI instead of the dashboard for the root node
root.RoutePath = string.Format("{0}/{1}/{2}", Constants.Applications.Settings, Constants.Trees.Languages, "overview");
root.Icon = "icon-flag-alt";
root.Icon = "icon-globe";
root.HasChildren = false;
root.MenuUrl = null;

View File

@@ -72,6 +72,9 @@
<PackageReference Include="Examine" Version="1.0.0-beta024" />
<PackageReference Include="HtmlAgilityPack" Version="1.7.2" />
<PackageReference Include="LightInject" Version="5.1.2" />
<PackageReference Include="Examine" Version="1.0.0-beta025" />
<PackageReference Include="HtmlAgilityPack" Version="1.5.1" />
<PackageReference Include="LightInject" Version="5.0.3" />
<PackageReference Include="LightInject.Annotation" Version="1.1.0" />
<PackageReference Include="LightInject.Mvc" Version="2.0.0" />
<PackageReference Include="LightInject.Web" Version="2.0.0" />
@@ -216,6 +219,7 @@
<Compile Include="Models\BackOfficeTourStep.cs" />
<Compile Include="Models\ContentEditing\AssignedContentPermissions.cs" />
<Compile Include="Models\ContentEditing\AssignedUserGroupPermissions.cs" />
<Compile Include="Models\ContentEditing\BackOfficePreview.cs" />
<Compile Include="Models\ContentEditing\CodeFileDisplay.cs" />
<Compile Include="Models\ContentEditing\ContentRedirectUrl.cs" />
<Compile Include="Media\ImageUrlProviderCollectionBuilder.cs" />
@@ -239,14 +243,14 @@
<Compile Include="Models\ContentEditing\UserInvite.cs" />
<Compile Include="Models\ContentEditing\UserProfile.cs" />
<Compile Include="Models\ContentEditing\UserSave.cs" />
<Compile Include="Models\Culture.cs" />
<Compile Include="Models\Language.cs" />
<Compile Include="Models\ContentEditing\Language.cs" />
<Compile Include="Models\Mapping\ActionButtonsResolver.cs" />
<Compile Include="Models\Mapping\AuditMapperProfile.cs" />
<Compile Include="Models\Mapping\AutoMapperExtensions.cs" />
<Compile Include="Models\Mapping\ContentChildOfListViewResolver.cs" />
<Compile Include="Models\Mapping\ContentUrlResolver.cs" />
<Compile Include="Models\Mapping\DefaultTemplateResolver.cs" />
<Compile Include="Models\Mapping\LanguageMapperProfile.cs" />
<Compile Include="Models\Mapping\MediaChildOfListViewResolver.cs" />
<Compile Include="Models\Mapping\CodeFileMapperProfile.cs" />
<Compile Include="Models\Mapping\ContentTreeNodeUrlResolver.cs" />