2018-07-06 15:23:21 +02:00
|
|
|
|
using System.Collections.Generic;
|
2018-03-26 10:55:49 +02:00
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
using System.Linq;
|
2018-03-27 16:33:28 +01:00
|
|
|
|
using System.Net;
|
|
|
|
|
|
using System.Net.Http;
|
2018-03-26 10:55:49 +02:00
|
|
|
|
using System.Web.Http;
|
2018-03-29 23:48:54 +11:00
|
|
|
|
using AutoMapper;
|
2018-03-30 01:20:40 +11:00
|
|
|
|
using Umbraco.Core;
|
2018-04-04 01:59:51 +10:00
|
|
|
|
using Umbraco.Core.Models;
|
2018-03-26 10:55:49 +02:00
|
|
|
|
using Umbraco.Web.Mvc;
|
2018-03-29 23:48:54 +11:00
|
|
|
|
using Umbraco.Web.WebApi;
|
2018-05-10 19:16:46 +10:00
|
|
|
|
using Umbraco.Web.WebApi.Filters;
|
2018-04-04 01:59:51 +10:00
|
|
|
|
using Language = Umbraco.Web.Models.ContentEditing.Language;
|
2018-03-26 10:55:49 +02:00
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Web.Editors
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Backoffice controller supporting the dashboard for language administration.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[PluginController("UmbracoApi")]
|
2018-03-30 01:20:40 +11:00
|
|
|
|
[PrefixlessBodyModelValidator]
|
2018-03-26 10:55:49 +02:00
|
|
|
|
public class LanguageController : UmbracoAuthorizedJsonController
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Returns all cultures available for creating languages.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
[HttpGet]
|
2018-03-30 01:20:40 +11:00
|
|
|
|
public IDictionary<string, string> GetAllCultures()
|
2018-03-26 10:55:49 +02:00
|
|
|
|
{
|
2018-03-30 01:20:40 +11:00
|
|
|
|
return
|
|
|
|
|
|
CultureInfo.GetCultures(CultureTypes.AllCultures)
|
|
|
|
|
|
.Where(x => !x.Name.IsNullOrWhiteSpace())
|
|
|
|
|
|
.OrderBy(x => x.DisplayName).ToDictionary(x => x.Name, x => x.DisplayName);
|
2018-03-26 10:55:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Returns all currently configured languages.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
[HttpGet]
|
2018-03-30 01:20:40 +11:00
|
|
|
|
public IEnumerable<Language> GetAllLanguages()
|
2018-03-26 10:55:49 +02:00
|
|
|
|
{
|
2018-04-04 01:59:51 +10:00
|
|
|
|
var allLanguages = Services.LocalizationService.GetAllLanguages();
|
2018-03-30 01:20:40 +11:00
|
|
|
|
|
2018-04-04 01:59:51 +10:00
|
|
|
|
return Mapper.Map<IEnumerable<ILanguage>, IEnumerable<Language>>(allLanguages);
|
2018-03-26 10:55:49 +02:00
|
|
|
|
}
|
2018-03-30 01:20:40 +11:00
|
|
|
|
|
|
|
|
|
|
[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)
|
2018-03-30 21:01:24 +01:00
|
|
|
|
{
|
2018-03-30 01:20:40 +11:00
|
|
|
|
model.IsDefaultVariantLanguage = true;
|
2018-03-30 21:01:24 +01:00
|
|
|
|
model.Mandatory = true;
|
|
|
|
|
|
}
|
2018-03-30 01:20:40 +11:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-26 10:55:49 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Deletes a language with a given ID
|
|
|
|
|
|
/// </summary>
|
2018-05-10 19:16:46 +10:00
|
|
|
|
[UmbracoTreeAuthorize(Core.Constants.Trees.Languages)]
|
2018-03-26 10:55:49 +02:00
|
|
|
|
[HttpDelete]
|
|
|
|
|
|
[HttpPost]
|
2018-03-29 23:48:54 +11:00
|
|
|
|
public IHttpActionResult DeleteLanguage(int id)
|
2018-03-26 10:55:49 +02:00
|
|
|
|
{
|
|
|
|
|
|
var language = Services.LocalizationService.GetLanguageById(id);
|
2018-03-29 23:48:54 +11:00
|
|
|
|
if (language == null) return NotFound();
|
|
|
|
|
|
|
|
|
|
|
|
var totalLangs = Services.LocalizationService.GetAllLanguages().Count();
|
2018-03-27 16:33:28 +01:00
|
|
|
|
|
2018-03-29 23:48:54 +11:00
|
|
|
|
if (language.IsDefaultVariantLanguage || totalLangs == 1)
|
2018-03-27 16:33:28 +01:00
|
|
|
|
{
|
2018-03-29 23:48:54 +11:00
|
|
|
|
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));
|
2018-03-27 16:33:28 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-26 10:55:49 +02:00
|
|
|
|
Services.LocalizationService.Delete(language);
|
2018-03-29 23:48:54 +11:00
|
|
|
|
|
|
|
|
|
|
return Ok();
|
2018-03-26 10:55:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2018-03-30 01:20:40 +11:00
|
|
|
|
/// Creates or saves a language
|
2018-03-26 10:55:49 +02:00
|
|
|
|
/// </summary>
|
2018-05-10 19:16:46 +10:00
|
|
|
|
[UmbracoTreeAuthorize(Core.Constants.Trees.Languages)]
|
2018-03-26 10:55:49 +02:00
|
|
|
|
[HttpPost]
|
2018-03-30 01:20:40 +11:00
|
|
|
|
public Language SaveLanguage(Language language)
|
2018-03-26 10:55:49 +02:00
|
|
|
|
{
|
2018-03-30 01:20:40 +11:00
|
|
|
|
if (!ModelState.IsValid)
|
|
|
|
|
|
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
|
|
|
|
|
|
|
|
|
|
|
|
var found = Services.LocalizationService.GetLanguageByIsoCode(language.IsoCode);
|
|
|
|
|
|
|
|
|
|
|
|
if (found != null && language.Id != found.Id)
|
2018-03-26 10:55:49 +02:00
|
|
|
|
{
|
2018-03-30 01:20:40 +11:00
|
|
|
|
//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));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (found == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
CultureInfo culture;
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
culture = CultureInfo.GetCultureInfo(language.IsoCode);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (CultureNotFoundException)
|
2018-03-26 10:55:49 +02:00
|
|
|
|
{
|
2018-03-30 01:20:40 +11:00
|
|
|
|
ModelState.AddModelError("IsoCode", "No Culture found with name " + language.IsoCode);
|
|
|
|
|
|
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
|
2018-03-26 10:55:49 +02:00
|
|
|
|
}
|
2018-03-30 01:20:40 +11:00
|
|
|
|
|
|
|
|
|
|
//create it
|
2018-07-06 15:23:21 +02:00
|
|
|
|
var newLang = new Core.Models.Language(culture.Name)
|
2018-03-30 01:20:40 +11:00
|
|
|
|
{
|
|
|
|
|
|
CultureName = culture.DisplayName,
|
|
|
|
|
|
IsDefaultVariantLanguage = language.IsDefaultVariantLanguage,
|
2018-07-06 15:23:21 +02:00
|
|
|
|
Mandatory = language.Mandatory,
|
2018-03-30 01:20:40 +11:00
|
|
|
|
};
|
2018-07-06 15:23:21 +02:00
|
|
|
|
|
|
|
|
|
|
AssociateFallbackLanguage(language, newLang);
|
2018-03-30 01:20:40 +11:00
|
|
|
|
Services.LocalizationService.Save(newLang);
|
|
|
|
|
|
return Mapper.Map<Language>(newLang);
|
2018-03-26 10:55:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-30 01:20:40 +11:00
|
|
|
|
found.Mandatory = language.Mandatory;
|
|
|
|
|
|
found.IsDefaultVariantLanguage = language.IsDefaultVariantLanguage;
|
2018-07-06 15:23:21 +02:00
|
|
|
|
AssociateFallbackLanguage(language, found);
|
2018-07-06 15:51:13 +02:00
|
|
|
|
|
|
|
|
|
|
if (UpdatedFallbackLanguageCreatesCircularPath(found))
|
|
|
|
|
|
{
|
|
|
|
|
|
ModelState.AddModelError("FallbackLanguage", "The selected fall back language '" + found.FallbackLanguage.CultureName + "' would create a circular path.");
|
|
|
|
|
|
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-30 01:20:40 +11:00
|
|
|
|
Services.LocalizationService.Save(found);
|
|
|
|
|
|
return Mapper.Map<Language>(found);
|
2018-03-26 10:55:49 +02:00
|
|
|
|
}
|
2018-07-06 15:23:21 +02:00
|
|
|
|
|
|
|
|
|
|
private static void AssociateFallbackLanguage(Language submittedLanguage, ILanguage languageToCreateOrUpdate)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (submittedLanguage.FallbackLanguage == null)
|
|
|
|
|
|
{
|
2018-07-06 15:51:13 +02:00
|
|
|
|
languageToCreateOrUpdate.FallbackLanguage = null;
|
2018-07-06 15:23:21 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var fallbackLanguageCulture = CultureInfo.GetCultureInfo(submittedLanguage.FallbackLanguage.IsoCode);
|
|
|
|
|
|
languageToCreateOrUpdate.FallbackLanguage = new Core.Models.Language(fallbackLanguageCulture.Name)
|
|
|
|
|
|
{
|
|
|
|
|
|
Id = submittedLanguage.FallbackLanguage.Id,
|
|
|
|
|
|
CultureName = fallbackLanguageCulture.DisplayName
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
2018-07-06 15:51:13 +02:00
|
|
|
|
|
|
|
|
|
|
private bool UpdatedFallbackLanguageCreatesCircularPath(ILanguage language)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (language.FallbackLanguage == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var languages = Services.LocalizationService.GetAllLanguages().ToArray();
|
|
|
|
|
|
var fallbackLanguage = language.FallbackLanguage;
|
|
|
|
|
|
while (fallbackLanguage != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (fallbackLanguage.Id == language.Id)
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fallbackLanguage = languages.Single(x => x.Id == fallbackLanguage.Id).FallbackLanguage;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2018-03-26 10:55:49 +02:00
|
|
|
|
}
|
2018-03-29 23:48:54 +11:00
|
|
|
|
}
|