-
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -99,7 +101,8 @@
{
margin-top: var(--padding);
- .ui-property + .ui-property
+ .ui-property + .ui-property,
+ .ui-split + .ui-property
{
margin-top: 0;
}
diff --git a/zero.Web/App/Resources/languages.js b/zero.Web/App/Resources/languages.js
new file mode 100644
index 00000000..49f89620
--- /dev/null
+++ b/zero.Web/App/Resources/languages.js
@@ -0,0 +1,34 @@
+import Axios from 'axios';
+
+export default {
+
+ // get language by id
+ getById(id)
+ {
+ return Axios.get('languages/getById', { params: { id } }).then(res => Promise.resolve(res.data));
+ },
+
+ // get all languages
+ getAll()
+ {
+ return Axios.get('languages/getAll').then(res => Promise.resolve(res.data));
+ },
+
+ // get all available cultures
+ getAllCultures()
+ {
+ return Axios.get('languages/getAllCultures').then(res => Promise.resolve(res.data));
+ },
+
+ // save a language
+ save(model)
+ {
+ return Axios.post('languages/save', model).then(res => Promise.resolve(res.data));
+ },
+
+ // deletes a language
+ delete(id)
+ {
+ return Axios.delete('languages/delete', { params: { id } }).then(res => Promise.resolve(res.data));
+ }
+};
\ No newline at end of file
diff --git a/zero.Web/App/pages/settings/routes.js b/zero.Web/App/pages/settings/routes.js
index f6140f55..776eb6fb 100644
--- a/zero.Web/App/pages/settings/routes.js
+++ b/zero.Web/App/pages/settings/routes.js
@@ -11,6 +11,7 @@ detailPages[zero.alias.settings.users] = [
];
detailPages[zero.alias.settings.countries] = 'country';
detailPages[zero.alias.settings.translations] = 'translations';
+detailPages[zero.alias.settings.languages] = 'language';
if (section)
{
diff --git a/zero.Web/Controllers/LanguagesController.cs b/zero.Web/Controllers/LanguagesController.cs
new file mode 100644
index 00000000..5b1e83d6
--- /dev/null
+++ b/zero.Web/Controllers/LanguagesController.cs
@@ -0,0 +1,95 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Options;
+using System.Globalization;
+using System.Linq;
+using System.Threading.Tasks;
+using zero.Core;
+using zero.Core.Api;
+using zero.Core.Entities;
+using zero.Core.Extensions;
+using zero.Core.Identity;
+using zero.Web.Mapper;
+using zero.Web.Models;
+
+namespace zero.Web.Controllers
+{
+ [ZeroAuthorize(Permissions.Settings.Languages, PermissionsValue.Read)]
+ public class LanguagesController : BackofficeController
+ {
+ private ILanguagesApi Api { get; set; }
+
+ private ZeroOptions Options { get; set; }
+
+
+ public LanguagesController(IZeroConfiguration config, ILanguagesApi api, IMapper mapper, IToken token, IOptionsMonitor
options) : base(config, mapper, token)
+ {
+ Api = api;
+ Options = options.CurrentValue;
+ }
+
+
+ ///
+ /// Get translation by id
+ ///
+ public IActionResult GetEmpty()
+ {
+ return Json(new LanguageEditModel());
+ }
+
+
+ ///
+ /// Get translation by id
+ ///
+ public async Task GetById([FromQuery] string id)
+ {
+ return await As(await Api.GetById(id));
+ }
+
+
+ ///
+ /// Get all translations
+ ///
+ public async Task GetAll([FromQuery] ListQuery query)
+ {
+ return await As(await Api.GetByQuery(query));
+ }
+
+
+ ///
+ /// Returns all cultures available for creating languages.
+ ///
+ public IActionResult GetAllCultures()
+ {
+ return Json(CultureInfo.GetCultures(CultureTypes.AllCultures)
+ .Where(x => !x.Name.IsNullOrWhiteSpace())
+ .Select(x => new CultureInfo(x.Name))
+ .OrderBy(x => x.DisplayName)
+ .Select(x => new
+ {
+ code = x.Name,
+ name = x.DisplayName
+ }));
+ }
+
+
+ ///
+ /// Save translation
+ ///
+ [ZeroAuthorize(Permissions.Settings.Languages, PermissionsValue.Write)]
+ public async Task Save([FromBody] LanguageEditModel model)
+ {
+ Language entity = await Mapper.Map(model, await Api.GetById(model.Id));
+ return await As(await Api.Save(entity));
+ }
+
+
+ ///
+ /// Deletes a translation
+ ///
+ [ZeroAuthorize(Permissions.Settings.Languages, PermissionsValue.Write)]
+ public async Task Delete([FromQuery] string id)
+ {
+ return await As(await Api.Delete(id));
+ }
+ }
+}
diff --git a/zero.Web/Controllers/TestController.cs b/zero.Web/Controllers/TestController.cs
index b8b7f021..708777a1 100644
--- a/zero.Web/Controllers/TestController.cs
+++ b/zero.Web/Controllers/TestController.cs
@@ -22,14 +22,17 @@ namespace zero.Web.Controllers
private ISpacesApi SpacesApi { get; set; }
+ private ILanguagesApi LanguagesApi { get; set; }
+
private SignInManager SignInManager;
private ZeroOptions Options;
- public TestController(IZeroConfiguration config, IAuthenticationApi api, ISpacesApi spacesApi, SignInManager signInManager, IOptionsMonitor options) : base(config)
+ public TestController(IZeroConfiguration config, IAuthenticationApi api, ISpacesApi spacesApi, ILanguagesApi languagesApi, SignInManager signInManager, IOptionsMonitor options) : base(config)
{
Api = api;
SpacesApi = spacesApi;
+ LanguagesApi = languagesApi;
SignInManager = signInManager;
Options = options.CurrentValue;
}
@@ -69,6 +72,21 @@ namespace zero.Web.Controllers
}
+ [HttpGet]
+ [ZeroAuthorize(false)]
+ public async Task AddLanguage()
+ {
+ return Json(await LanguagesApi.Save(new Language()
+ {
+
+ IsActive = true,
+ Name = "English",
+ Code = "en-US",
+ IsDefault = true
+ }));
+ }
+
+
[HttpGet]
[ZeroAuthorize(false)]
public async Task GetSpaceList()
diff --git a/zero.Web/Mapper/LanguageMapperConfig.cs b/zero.Web/Mapper/LanguageMapperConfig.cs
new file mode 100644
index 00000000..5c59df97
--- /dev/null
+++ b/zero.Web/Mapper/LanguageMapperConfig.cs
@@ -0,0 +1,42 @@
+using zero.Core.Entities;
+using zero.Web.Models;
+
+namespace zero.Web.Mapper
+{
+ public class LanguageMapperConfig : IMapperConfig
+ {
+ ///
+ public void Configure(IMapper config)
+ {
+ config.CreateMap((source, target) =>
+ {
+ target.Id = source.Id;
+ target.Name = source.Name;
+ target.IsActive = source.IsActive;
+ target.IsDefault = source.IsDefault;
+ target.InheritedLanguageId = source.InheritedLanguageId;
+ target.Code = source.Code;
+ target.CreatedDate = source.CreatedDate;
+ });
+
+ config.CreateMap((source, target) =>
+ {
+ target.Name = source.Name;
+ target.IsActive = source.IsActive;
+ target.IsDefault = source.IsDefault;
+ target.InheritedLanguageId = source.InheritedLanguageId;
+ target.Code = source.Code;
+ });
+
+ config.CreateMap((source, target) =>
+ {
+ target.Id = source.Id;
+ target.Name = source.Name;
+ target.IsActive = source.IsActive;
+ target.IsDefault = source.IsDefault;
+ target.InheritedLanguageId = source.InheritedLanguageId;
+ target.Code = source.Code;
+ });
+ }
+ }
+}
diff --git a/zero.Web/Models/CountryEditModel.cs b/zero.Web/Models/CountryEditModel.cs
index 56c371f4..a038f967 100644
--- a/zero.Web/Models/CountryEditModel.cs
+++ b/zero.Web/Models/CountryEditModel.cs
@@ -6,10 +6,6 @@ namespace zero.Web.Models
{
public string Name { get; set; }
- public bool IsActive { get; set; }
-
- public DateTimeOffset CreatedDate { get; set; }
-
public bool IsPreferred { get; set; }
public string Code { get; set; }
diff --git a/zero.Web/Models/EditModel.cs b/zero.Web/Models/EditModel.cs
index 81b32cb1..1f932a4d 100644
--- a/zero.Web/Models/EditModel.cs
+++ b/zero.Web/Models/EditModel.cs
@@ -10,11 +10,21 @@ namespace zero.Web.Models
///
public string Id { get; set; }
+ ///
+ /// Whether this entity is active
+ ///
+ public bool IsActive { get; set; }
+
///
/// Whether this entity can be edited or only viewed
///
public bool CanEdit { get; set; }
+ ///
+ /// Date of creation
+ ///
+ public DateTimeOffset CreatedDate { get; set; }
+
///
/// Meta data for the entity
///
diff --git a/zero.Web/Models/LanguageEditModel.cs b/zero.Web/Models/LanguageEditModel.cs
new file mode 100644
index 00000000..f5c3c3d7
--- /dev/null
+++ b/zero.Web/Models/LanguageEditModel.cs
@@ -0,0 +1,13 @@
+namespace zero.Web.Models
+{
+ public class LanguageEditModel : EditModel
+ {
+ public string Name { get; set; }
+
+ public string Code { get; set; }
+
+ public bool IsDefault { get; set; }
+
+ public string InheritedLanguageId { get; set; }
+ }
+}
diff --git a/zero.Web/Models/LanguageListModel.cs b/zero.Web/Models/LanguageListModel.cs
new file mode 100644
index 00000000..0b44e928
--- /dev/null
+++ b/zero.Web/Models/LanguageListModel.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace zero.Web.Models
+{
+ public class LanguageListModel : ListModel
+ {
+ public string Name { get; set; }
+
+ public string Code { get; set; }
+
+ public bool IsDefault { get; set; }
+
+ public bool IsActive { get; set; }
+
+ public string InheritedLanguageId { get; set; }
+ }
+}
diff --git a/zero.Web/Models/TranslationEditModel.cs b/zero.Web/Models/TranslationEditModel.cs
index 065ae22c..9360aac7 100644
--- a/zero.Web/Models/TranslationEditModel.cs
+++ b/zero.Web/Models/TranslationEditModel.cs
@@ -11,7 +11,5 @@ namespace zero.Web.Models
public string Value { get; set; }
public TranslationDisplay Display { get; set; }
-
- public DateTimeOffset CreatedDate { get; set; }
}
}
diff --git a/zero.Web/Models/UserEditModel.cs b/zero.Web/Models/UserEditModel.cs
index 169801fb..b366ec88 100644
--- a/zero.Web/Models/UserEditModel.cs
+++ b/zero.Web/Models/UserEditModel.cs
@@ -8,10 +8,6 @@ namespace zero.Web.Models
{
public string Name { get; set; }
- public bool IsActive { get; set; }
-
- public DateTimeOffset CreatedDate { get; set; }
-
public bool IsSuper { get; set; }
public string Email { get; set; }
diff --git a/zero.Web/Models/UserRoleEditModel.cs b/zero.Web/Models/UserRoleEditModel.cs
index 4d62d710..31065fea 100644
--- a/zero.Web/Models/UserRoleEditModel.cs
+++ b/zero.Web/Models/UserRoleEditModel.cs
@@ -8,10 +8,6 @@ namespace zero.Web.Models
{
public string Name { get; set; }
- public bool IsActive { get; set; }
-
- public DateTimeOffset CreatedDate { get; set; }
-
public string Description { get; set; }
public string Icon { get; set; }
diff --git a/zero.Web/Resources/Localization/zero.en-us.json b/zero.Web/Resources/Localization/zero.en-us.json
index 58c80bbc..cd0a2198 100644
--- a/zero.Web/Resources/Localization/zero.en-us.json
+++ b/zero.Web/Resources/Localization/zero.en-us.json
@@ -96,6 +96,10 @@
"name": "Users & Permissions",
"text": "Administration of backoffice users"
},
+ "languages": {
+ "name": "Languages",
+ "text": "Frontend languages"
+ },
"translations": {
"name": "Translations",
"text": "Frontend texts and translations"
@@ -223,6 +227,21 @@
}
},
+ "language": {
+ "list": "Languages",
+ "name": "Language",
+ "fields": {
+ "name": "Name",
+ "locale": "Locale",
+ "code": "ISO Code",
+ "code_text": "Code in ISO 3166-1 format",
+ "isDefault": "Default",
+ "isDefault_text": "Use as default language",
+ "inheritedLanguageId": "Inherit from",
+ "inheritedLanguageId_text": "To allow multi-lingual content to fall back to another language if not present in the requested language, select it here"
+ }
+ },
+
"translation": {
"list": "Translations",
"name": "Translation",
diff --git a/zero.Web/Sass/Canvas/_canvas.scss b/zero.Web/Sass/Canvas/_canvas.scss
index ae5c0a8e..9e615960 100644
--- a/zero.Web/Sass/Canvas/_canvas.scss
+++ b/zero.Web/Sass/Canvas/_canvas.scss
@@ -63,4 +63,10 @@ body
grid-gap: var(--padding);
align-items: stretch;
justify-content: stretch;
+
+ .ui-property + .ui-property
+ {
+ margin-top: 0;
+ padding-top: 0;
+ }
}
\ No newline at end of file
diff --git a/zero.Web/Sass/Core/_forms.scss b/zero.Web/Sass/Core/_forms.scss
index 77e0ab13..e837e1e4 100644
--- a/zero.Web/Sass/Core/_forms.scss
+++ b/zero.Web/Sass/Core/_forms.scss
@@ -7,7 +7,7 @@
input[type="text"], input[type="color"], input[type="date"], input[type="datetime"], input[type="email"], input[type="file"], input[type="month"], input[type="number"],
input[type="password"], input[type="range"], input[type="search"], input[type="tel"], input[type="time"], input[type="url"], input[type="week"],
-textarea
+textarea, .ui-native-select
{
@extend %font;
background: var(--color-bg-light);
@@ -40,4 +40,34 @@ textarea
{
resize: vertical;
min-height: 80px;
+}
+
+.ui-native-select
+{
+ position: relative;
+
+ select
+ {
+ @extend %font;
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ width: 100%;
+ padding-left: 12px;
+ -webkit-appearance: none;
+ border: none;
+ }
+
+ &:after
+ {
+ @extend %font-icon;
+ content: "\e842";
+ position: absolute;
+ right: 12px;
+ top: 0;
+ height: 42px;
+ line-height: 40px;
+ }
}
\ No newline at end of file
diff --git a/zero.Web/Startup.cs b/zero.Web/Startup.cs
index 148e302e..368e0811 100644
--- a/zero.Web/Startup.cs
+++ b/zero.Web/Startup.cs
@@ -162,6 +162,7 @@ namespace zero.Web
services.AddTransient();
services.AddTransient();
services.AddTransient();
+ services.AddTransient();
}
///
diff --git a/zero.Web/ZeroBuilder.cs b/zero.Web/ZeroBuilder.cs
index de587195..23f16404 100644
--- a/zero.Web/ZeroBuilder.cs
+++ b/zero.Web/ZeroBuilder.cs
@@ -32,6 +32,7 @@ namespace zero.Web
opts.Add();
opts.Add();
opts.Add();
+ opts.Add();
});
Services.AddIdentity(opts =>
@@ -102,8 +103,9 @@ namespace zero.Web
systemSettings.Add(Constants.Settings.Updates, "@settings.system.updates.name", "@settings.system.updates.text", "fth-check-circle");
systemSettings.Add(Constants.Settings.Applications, "@settings.system.applications.name", "@settings.system.applications.text", "fth-layers");
systemSettings.Add(Constants.Settings.Users, "@settings.system.users.name", "@settings.system.users.text", "fth-users");
- systemSettings.Add(Constants.Settings.Translations, "@settings.system.translations.name", "@settings.system.translations.text", "fth-type");
+ systemSettings.Add(Constants.Settings.Languages, "@settings.system.languages.name", "@settings.system.languages.text", "fth-globe");
systemSettings.Add(Constants.Settings.Countries, "@settings.system.countries.name", "@settings.system.countries.text", "fth-map-pin");
+ systemSettings.Add(Constants.Settings.Translations, "@settings.system.translations.name", "@settings.system.translations.text", "fth-type");
systemSettings.Add(Constants.Settings.Logging, "@settings.system.logs.name", "@settings.system.logs.text", "fth-file-text");
SettingsGroup pluginSettings = new SettingsGroup("@settings.groups.plugins");
@@ -122,8 +124,9 @@ namespace zero.Web
permissionSettings.Items.Add(new Permission(Permissions.Settings.Updates, "@settings.system.updates.name", "@settings.system.updates.text_default", PermissionValueType.ReadWrite));
permissionSettings.Items.Add(new Permission(Permissions.Settings.Applications, "@settings.system.applications.name", "@settings.system.applications.text", PermissionValueType.ReadWrite));
permissionSettings.Items.Add(new Permission(Permissions.Settings.Users, "@settings.system.users.name", "@settings.system.users.text", PermissionValueType.ReadWrite));
- permissionSettings.Items.Add(new Permission(Permissions.Settings.Translations, "@settings.system.translations.name", "@settings.system.translations.text", PermissionValueType.ReadWrite));
+ permissionSettings.Items.Add(new Permission(Permissions.Settings.Languages, "@settings.system.languages.name", "@settings.system.languages.text", PermissionValueType.ReadWrite));
permissionSettings.Items.Add(new Permission(Permissions.Settings.Countries, "@settings.system.countries.name", "@settings.system.countries.text", PermissionValueType.ReadWrite));
+ permissionSettings.Items.Add(new Permission(Permissions.Settings.Translations, "@settings.system.translations.name", "@settings.system.translations.text", PermissionValueType.ReadWrite));
permissionSettings.Items.Add(new Permission(Permissions.Settings.Logging, "@settings.system.logs.name", "@settings.system.logs.text", PermissionValueType.ReadWrite));
permissionSettings.Items.Add(new Permission(Permissions.Settings.Plugins, "@settings.plugins.installed.name", "@settings.plugins.installed.text_default", PermissionValueType.ReadWrite));
permissionSettings.Items.Add(new Permission(Permissions.Settings.CreatePlugin, "@settings.plugins.create.name", "@settings.plugins.create.text", PermissionValueType.ReadWrite));
diff --git a/zero.Web/ZeroVue.cs b/zero.Web/ZeroVue.cs
index 6ec2c4e0..f666dcd2 100644
--- a/zero.Web/ZeroVue.cs
+++ b/zero.Web/ZeroVue.cs
@@ -137,6 +137,7 @@ namespace zero.Web
settings.Add("applications", Constants.Settings.Applications);
settings.Add("countries", Constants.Settings.Countries);
settings.Add("logging", Constants.Settings.Logging);
+ settings.Add("languages", Constants.Settings.Languages);
settings.Add("translations", Constants.Settings.Translations);
settings.Add("updates", Constants.Settings.Updates);
settings.Add("users", Constants.Settings.Users);