diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs index 2d27479a95..518b769830 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs @@ -151,6 +151,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected override void PersistDeletedItem(ILanguage entity) { + //we need to validate that we can delete this language + if (entity.IsDefaultVariantLanguage) + throw new InvalidOperationException($"Cannot delete the default language ({entity.IsoCode})"); + + var count = Database.ExecuteScalar(Sql().SelectCount().From()); + if (count == 1) + throw new InvalidOperationException($"Cannot delete the default language ({entity.IsoCode})"); + base.PersistDeletedItem(entity); //Clear the cache entries that exist by key/iso diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/language.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/language.resource.js new file mode 100644 index 0000000000..526e0386c0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/resources/language.resource.js @@ -0,0 +1,32 @@ +/** + * @ngdoc service + * @name umbraco.resources.languageResource + * @description Handles retrieving and updating language data + **/ +function languageResource($q, $http, umbRequestHelper) { + return { + + getAll: function (id, alias) { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "languageApiBaseUrl", + "GetAllLanguages")), + "Failed to get languages"); + }, + + deleteById: function (id) { + + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "languageApiBaseUrl", + "DeleteLanguage", + { id: id })), + "Failed to delete item " + id); + } + }; +} + +angular.module('umbraco.resources').factory('languageResource', languageResource); diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js index f787fdcb19..452f6bfd41 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function LanguagesEditController($timeout, $location, $routeParams, notificationsService, localizationService) { + function LanguagesEditController($timeout, $location, $routeParams, notificationsService, localizationService, languageResource) { var vm = this; diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js b/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js index 9d9d2a2313..f7d42c6801 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function LanguagesOverviewController($timeout, $location, notificationsService, localizationService) { + function LanguagesOverviewController($timeout, $location, notificationsService, localizationService, languageResource) { var vm = this; @@ -32,55 +32,10 @@ vm.page.name = vm.labels.languages; }); - $timeout(function () { - - vm.languages = [ - { - "id": 1, - "cultureDisplayName": "English (United States)", - "culture": "en-US", - "isDefault": true, - "isMandatory": true - }, - { - "id": 2, - "cultureDisplayName": "Danish", - "culture": "da-DK", - "isDefault": false, - "isMandatory": true - }, - { - "id": 3, - "cultureDisplayName": "Spanish (Spain)", - "culture": "es-ES", - "isDefault": false, - "isMandatory": false - }, - { - "id": 4, - "cultureDisplayName": "French (France)", - "culture": "fr-FR", - "isDefault": false, - "isMandatory": false - }, - { - "id": 5, - "cultureDisplayName": "German (Germany)", - "culture": "de-DE", - "isDefault": false, - "isMandatory": true - } - ]; - + languageResource.getAll().then(function(languages) { + vm.languages = languages; vm.loading = false; - - }, 1000); - - /* - $timeout(function () { - navigationService.syncTree({ tree: "languages", path: "-1" }); }); - */ } function addLanguage() { @@ -97,10 +52,21 @@ var confirmed = confirm("Are you sure you want to delete " + language.cultureDisplayName + "?"); if(confirmed) { language.deleteButtonState = "busy"; - $timeout(function(){ + + languageResource.deleteById(language.id).then(function () { var index = vm.languages.indexOf(language); vm.languages.splice(index, 1); - }, 1000); + }, function (err) { + language.deleteButtonState = "error"; + + //show any notifications + if (angular.isArray(err.data.notifications)) { + for (var i = 0; i < err.data.notifications.length; i++) { + notificationsService.showNotification(err.data.notifications[i]); + } + } + }); + } event.preventDefault() event.stopPropagation(); diff --git a/src/Umbraco.Web/Composing/CompositionRoots/WebMappingProfilesCompositionRoot.cs b/src/Umbraco.Web/Composing/CompositionRoots/WebMappingProfilesCompositionRoot.cs index 77ded444b6..0c73e64ff2 100644 --- a/src/Umbraco.Web/Composing/CompositionRoots/WebMappingProfilesCompositionRoot.cs +++ b/src/Umbraco.Web/Composing/CompositionRoots/WebMappingProfilesCompositionRoot.cs @@ -23,6 +23,7 @@ namespace Umbraco.Web.Composing.CompositionRoots container.Register(); container.Register(); container.Register(); + container.Register(); } } } diff --git a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs index 713d386140..a0cf6c3767 100644 --- a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs @@ -285,6 +285,10 @@ namespace Umbraco.Web.Editors { "backOfficeAssetsApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( controller => controller.GetSupportedMomentLocales()) + }, + { + "languageApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + controller => controller.GetAllLanguages()) } } }, diff --git a/src/Umbraco.Web/Editors/LanguageController.cs b/src/Umbraco.Web/Editors/LanguageController.cs index c6e3dcfb2e..870e5e4b34 100644 --- a/src/Umbraco.Web/Editors/LanguageController.cs +++ b/src/Umbraco.Web/Editors/LanguageController.cs @@ -5,9 +5,12 @@ using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; +using AutoMapper; 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 { @@ -24,8 +27,7 @@ namespace Umbraco.Web.Editors [HttpGet] public IEnumerable GetAllCultures() { - return CultureInfo.GetCultures(CultureTypes.AllCultures) - .Select(x => new Culture {IsoCode = x.Name, Name = x.DisplayName}); + return Mapper.Map>(CultureInfo.GetCultures(CultureTypes.AllCultures)); } /// @@ -33,18 +35,10 @@ namespace Umbraco.Web.Editors /// /// [HttpGet] - public IEnumerable GetAllLanguages() + public IEnumerable GetAllLanguages() { var allLanguages = Services.LocalizationService.GetAllLanguages(); - - return allLanguages.Select(x => new Language - { - Id = x.Id, - IsoCode = x.IsoCode, - Name = x.CultureInfo.DisplayName, - IsDefaultVariantLanguage = x.IsDefaultVariantLanguage, - Mandatory = x.Mandatory - }); + return Mapper.Map>(allLanguages); } /// @@ -52,28 +46,29 @@ namespace Umbraco.Web.Editors /// [HttpDelete] [HttpPost] - public void DeleteLanguage(int id) + public IHttpActionResult DeleteLanguage(int id) { var language = Services.LocalizationService.GetLanguageById(id); - if (language == null) - { - throw new EntityNotFoundException(id, $"Could not find language by id: '{id}'."); - } + if (language == null) return NotFound(); - if (language.IsDefaultVariantLanguage) + var totalLangs = Services.LocalizationService.GetAllLanguages().Count(); + + if (language.IsDefaultVariantLanguage || totalLangs == 1) { - var message = $"Language with id '{id}' is currently set to 'default' and can not be deleted."; - throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, message)); + 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(); } /// /// Saves a bulk set of languages with default/mandatory settings and returns the full set of languages configured. /// [HttpPost] - public IEnumerable SaveLanguages(IEnumerable languages) + public IEnumerable SaveLanguages(IEnumerable languages) { foreach (var l in languages) { @@ -90,4 +85,4 @@ namespace Umbraco.Web.Editors return GetAllLanguages(); } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Models/ContentEditing/Culture.cs b/src/Umbraco.Web/Models/ContentEditing/Culture.cs new file mode 100644 index 0000000000..c5087e3aaa --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/Culture.cs @@ -0,0 +1,15 @@ +using System.Runtime.Serialization; + +namespace Umbraco.Web.Models.ContentEditing +{ + + [DataContract(Name = "culture", Namespace = "")] + public class Culture + { + [DataMember(Name = "culture")] + public string IsoCode { get; set; } + + [DataMember(Name = "cultureDisplayName")] + public string Name { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/ContentEditing/LanguageDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/LanguageDisplay.cs new file mode 100644 index 0000000000..5d082bb136 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/LanguageDisplay.cs @@ -0,0 +1,23 @@ +using System.Runtime.Serialization; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "language", Namespace = "")] + public class LanguageDisplay + { + [DataMember(Name = "id")] + public int Id { get; set; } + + [DataMember(Name = "culture")] + public string IsoCode { get; set; } + + [DataMember(Name = "cultureDisplayName")] + public string Name { get; set; } + + [DataMember(Name = "isDefault")] + public bool IsDefaultVariantLanguage { get; set; } + + [DataMember(Name = "isMandatory")] + public bool Mandatory { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/Culture.cs b/src/Umbraco.Web/Models/Culture.cs deleted file mode 100644 index 16ebc6bc2a..0000000000 --- a/src/Umbraco.Web/Models/Culture.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Umbraco.Web.Models -{ - public class Culture - { - public string IsoCode { get; set; } - public string Name { get; set; } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Language.cs b/src/Umbraco.Web/Models/Language.cs deleted file mode 100644 index 4fdf418eef..0000000000 --- a/src/Umbraco.Web/Models/Language.cs +++ /dev/null @@ -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; } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/LanguageMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/LanguageMapperProfile.cs new file mode 100644 index 0000000000..956854fbc4 --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/LanguageMapperProfile.cs @@ -0,0 +1,20 @@ +using System.Globalization; +using AutoMapper; +using Umbraco.Core.Models; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Models.Mapping +{ + internal class LanguageMapperProfile : Profile + { + public LanguageMapperProfile() + { + CreateMap() + .ForMember(l => l.Name, expression => expression.MapFrom(x => x.CultureInfo.DisplayName)); + + CreateMap() + .ForMember(c => c.Name, expression => expression.MapFrom(x => x.DisplayName)) + .ForMember(c => c.IsoCode, expression => expression.MapFrom(x => x.Name)); + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 2163a2e078..f381df3ff9 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -218,6 +218,7 @@ + @@ -241,14 +242,15 @@ - - + + +