Added version to Language Management, improved framework performance by loading languages into PageState, include all supported cultures and allow Administrator to add any language to a site regardless of translation availability, fix translation upgrade issue
This commit is contained in:
parent
6012275c7b
commit
f97a6a2bee
|
@ -19,15 +19,17 @@ else
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
<th>@Localizer["Code"]</th>
|
<th>@Localizer["Code"]</th>
|
||||||
|
<th>@Localizer["Translation"]</th>
|
||||||
<th>@Localizer["Default"]</th>
|
<th>@Localizer["Default"]</th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionDialog Header="Delete Language" Message="@string.Format(Localizer["Confirm.Language.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteLanguage(context))" Disabled="@((context.IsDefault && _languages.Count > 2) || context.Code == Constants.DefaultCulture)" ResourceKey="DeleteLanguage" /></td>
|
<td><ActionDialog Header="Delete Language" Message="@string.Format(Localizer["Confirm.Language.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteLanguage(context))" Disabled="@((context.IsDefault && _languages.Count > 2) || context.Code == Constants.DefaultCulture)" ResourceKey="DeleteLanguage" /></td>
|
||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
<td>@context.Code</td>
|
<td>@context.Code</td>
|
||||||
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
|
<td>@context.Version</td>
|
||||||
<td>
|
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
|
||||||
|
<td>
|
||||||
@if (UpgradeAvailable(context.Code))
|
@if (UpgradeAvailable(context.Code))
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadLanguage(context.Code))>@SharedLocalizer["Upgrade"]</button>
|
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadLanguage(context.Code))>@SharedLocalizer["Upgrade"]</button>
|
||||||
|
@ -50,9 +52,6 @@ else
|
||||||
var cultures = await LocalizationService.GetCulturesAsync();
|
var cultures = await LocalizationService.GetCulturesAsync();
|
||||||
var culture = cultures.First(c => c.Name.Equals(Constants.DefaultCulture));
|
var culture = cultures.First(c => c.Name.Equals(Constants.DefaultCulture));
|
||||||
|
|
||||||
// Adds English as default language
|
|
||||||
_languages.Insert(0, new Language { Name = culture.DisplayName, Code = culture.Name, IsDefault = !_languages.Any(l => l.IsDefault) });
|
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
_packages = await PackageService.GetPackagesAsync("translation");
|
_packages = await PackageService.GetPackagesAsync("translation");
|
||||||
|
@ -81,7 +80,7 @@ else
|
||||||
var upgradeavailable = false;
|
var upgradeavailable = false;
|
||||||
if (_packages != null)
|
if (_packages != null)
|
||||||
{
|
{
|
||||||
var package = _packages.Where(item => item.PackageId == (Constants.PackageId + ".Client." + code)).FirstOrDefault();
|
var package = _packages.Where(item => item.PackageId == (Constants.ClientAssemblyName + "." + code)).FirstOrDefault();
|
||||||
if (package != null)
|
if (package != null)
|
||||||
{
|
{
|
||||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) == 0);
|
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) == 0);
|
||||||
|
|
|
@ -144,4 +144,7 @@
|
||||||
<data name="DeleteLanguage.Text" xml:space="preserve">
|
<data name="DeleteLanguage.Text" xml:space="preserve">
|
||||||
<value>Delete</value>
|
<value>Delete</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Translation" xml:space="preserve">
|
||||||
|
<value>Translation</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -17,6 +17,14 @@ namespace Oqtane.Services
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<List<Language>> GetLanguagesAsync(int siteId);
|
Task<List<Language>> GetLanguagesAsync(int siteId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a list of all available languages for the given <see cref="Site" /> and client assembly
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="siteId"></param>
|
||||||
|
/// <param name="clientAssemblyName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<List<Language>> GetLanguagesAsync(int siteId, string clientAssemblyName);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the given language
|
/// Returns the given language
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -17,18 +17,27 @@ namespace Oqtane.Services
|
||||||
|
|
||||||
public async Task<List<Language>> GetLanguagesAsync(int siteId)
|
public async Task<List<Language>> GetLanguagesAsync(int siteId)
|
||||||
{
|
{
|
||||||
var languages = await GetJsonAsync<List<Language>>($"{Apiurl}?siteid={siteId}");
|
return await GetLanguagesAsync(siteId, "");
|
||||||
|
}
|
||||||
|
|
||||||
return languages?.OrderBy(l => l.Name).ToList() ?? Enumerable.Empty<Language>().ToList();
|
public async Task<List<Language>> GetLanguagesAsync(int siteId, string clientAssemblyName)
|
||||||
|
{
|
||||||
|
return await GetJsonAsync<List<Language>>($"{Apiurl}?siteid={siteId}&clientassemblyname={clientAssemblyName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Language> GetLanguageAsync(int languageId)
|
public async Task<Language> GetLanguageAsync(int languageId)
|
||||||
=> await GetJsonAsync<Language>($"{Apiurl}/{languageId}");
|
{
|
||||||
|
return await GetJsonAsync<Language>($"{Apiurl}/{languageId}");
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Language> AddLanguageAsync(Language language)
|
public async Task<Language> AddLanguageAsync(Language language)
|
||||||
=> await PostJsonAsync<Language>(Apiurl, language);
|
{
|
||||||
|
return await PostJsonAsync<Language>(Apiurl, language);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task DeleteLanguageAsync(int languageId)
|
public async Task DeleteLanguageAsync(int languageId)
|
||||||
=> await DeleteAsync($"{Apiurl}/{languageId}");
|
{
|
||||||
|
await DeleteAsync($"{Apiurl}/{languageId}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,13 +24,9 @@
|
||||||
@code{
|
@code{
|
||||||
private IEnumerable<Culture> _supportedCultures;
|
private IEnumerable<Culture> _supportedCultures;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
var languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
|
var languages = PageState.Languages;
|
||||||
var defaultCulture = CultureInfo.GetCultureInfo(Constants.DefaultCulture);
|
|
||||||
|
|
||||||
languages.Add(new Language { Code = defaultCulture.Name, Name = defaultCulture.DisplayName });
|
|
||||||
|
|
||||||
_supportedCultures = languages.Select(l => new Culture { Name = l.Code, DisplayName = l.Name });
|
_supportedCultures = languages.Select(l => new Culture { Name = l.Code, DisplayName = l.Name });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace Oqtane.UI
|
||||||
{
|
{
|
||||||
public Alias Alias { get; set; }
|
public Alias Alias { get; set; }
|
||||||
public Site Site { get; set; }
|
public Site Site { get; set; }
|
||||||
|
public List<Language> Languages { get; set; }
|
||||||
public List<Page> Pages { get; set; }
|
public List<Page> Pages { get; set; }
|
||||||
public Page Page { get; set; }
|
public Page Page { get; set; }
|
||||||
public User User { get; set; }
|
public User User { get; set; }
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
@inject INavigationInterception NavigationInterception
|
@inject INavigationInterception NavigationInterception
|
||||||
@inject ISyncService SyncService
|
@inject ISyncService SyncService
|
||||||
@inject ISiteService SiteService
|
@inject ISiteService SiteService
|
||||||
|
@inject ILanguageService LanguageService
|
||||||
@inject IPageService PageService
|
@inject IPageService PageService
|
||||||
@inject IUserService UserService
|
@inject IUserService UserService
|
||||||
@inject IModuleService ModuleService
|
@inject IModuleService ModuleService
|
||||||
|
@ -70,6 +71,7 @@
|
||||||
private async Task Refresh()
|
private async Task Refresh()
|
||||||
{
|
{
|
||||||
Site site;
|
Site site;
|
||||||
|
List<Language> languages;
|
||||||
List<Page> pages;
|
List<Page> pages;
|
||||||
Page page;
|
Page page;
|
||||||
User user = null;
|
User user = null;
|
||||||
|
@ -102,7 +104,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the refresh parameter is used to refresh the client-side PageState
|
// the refresh parameter is used to refresh the client-side PageState
|
||||||
if (querystring.ContainsKey("refresh"))
|
if (querystring.ContainsKey("refresh"))
|
||||||
{
|
{
|
||||||
|
@ -173,11 +175,13 @@
|
||||||
|
|
||||||
if (PageState == null || refresh == UI.Refresh.Site)
|
if (PageState == null || refresh == UI.Refresh.Site)
|
||||||
{
|
{
|
||||||
|
languages = await LanguageService.GetLanguagesAsync(site.SiteId);
|
||||||
pages = await PageService.GetPagesAsync(site.SiteId);
|
pages = await PageService.GetPagesAsync(site.SiteId);
|
||||||
pages = pages.Where(item => !item.IsDeleted).ToList();
|
pages = pages.Where(item => !item.IsDeleted).ToList();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
languages = PageState.Languages;
|
||||||
pages = PageState.Pages;
|
pages = PageState.Pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,6 +234,7 @@
|
||||||
{
|
{
|
||||||
Alias = SiteState.Alias,
|
Alias = SiteState.Alias,
|
||||||
Site = site,
|
Site = site,
|
||||||
|
Languages = languages,
|
||||||
Pages = pages,
|
Pages = pages,
|
||||||
Page = page,
|
Page = page,
|
||||||
User = user,
|
User = user,
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Reflection;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Oqtane.Enums;
|
using Oqtane.Enums;
|
||||||
|
@ -7,6 +9,9 @@ using Oqtane.Infrastructure;
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using Oqtane.Repository;
|
using Oqtane.Repository;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
namespace Oqtane.Controllers
|
namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
|
@ -14,23 +19,40 @@ namespace Oqtane.Controllers
|
||||||
public class LanguageController : Controller
|
public class LanguageController : Controller
|
||||||
{
|
{
|
||||||
private readonly ILanguageRepository _languages;
|
private readonly ILanguageRepository _languages;
|
||||||
|
private readonly ISyncManager _syncManager;
|
||||||
private readonly ILogManager _logger;
|
private readonly ILogManager _logger;
|
||||||
private readonly Alias _alias;
|
private readonly Alias _alias;
|
||||||
|
|
||||||
public LanguageController(ILanguageRepository language, ILogManager logger, ITenantManager tenantManager)
|
public LanguageController(ILanguageRepository language, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
|
||||||
{
|
{
|
||||||
_languages = language;
|
_languages = language;
|
||||||
|
_syncManager = syncManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_alias = tenantManager.GetAlias();
|
_alias = tenantManager.GetAlias();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public IEnumerable<Language> Get(string siteid)
|
public IEnumerable<Language> Get(string siteid, string clientassemblyname)
|
||||||
{
|
{
|
||||||
int SiteId;
|
int SiteId;
|
||||||
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
|
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
|
||||||
{
|
{
|
||||||
return _languages.GetLanguages(SiteId);
|
if (string.IsNullOrEmpty(clientassemblyname))
|
||||||
|
{
|
||||||
|
clientassemblyname = Constants.ClientAssemblyName;
|
||||||
|
}
|
||||||
|
var languages = _languages.GetLanguages(SiteId).ToList();
|
||||||
|
foreach (var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), clientassemblyname + ".resources.dll", SearchOption.AllDirectories))
|
||||||
|
{
|
||||||
|
var code = Path.GetFileName(Path.GetDirectoryName(file));
|
||||||
|
if (languages.Any(item => item.Code == code))
|
||||||
|
{
|
||||||
|
languages.Single(item => item.Code == code).Version = FileVersionInfo.GetVersionInfo(file).FileVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var defaultCulture = CultureInfo.GetCultureInfo(Constants.DefaultCulture);
|
||||||
|
languages.Add(new Language { Code = defaultCulture.Name, Name = defaultCulture.DisplayName, Version = Constants.Version, IsDefault = !languages.Any(l => l.IsDefault) });
|
||||||
|
return languages.OrderBy(item => item.Name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -63,6 +85,7 @@ namespace Oqtane.Controllers
|
||||||
if (ModelState.IsValid && language.SiteId == _alias.SiteId)
|
if (ModelState.IsValid && language.SiteId == _alias.SiteId)
|
||||||
{
|
{
|
||||||
language = _languages.AddLanguage(language);
|
language = _languages.AddLanguage(language);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Language Added {Language}", language);
|
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Language Added {Language}", language);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -82,6 +105,7 @@ namespace Oqtane.Controllers
|
||||||
if (language != null && language.SiteId == _alias.SiteId)
|
if (language != null && language.SiteId == _alias.SiteId)
|
||||||
{
|
{
|
||||||
_languages.DeleteLanguage(id);
|
_languages.DeleteLanguage(id);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Language Deleted {LanguageId}", id);
|
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Language Deleted {LanguageId}", id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -89,7 +113,6 @@ namespace Oqtane.Controllers
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Language Delete Attempt {LanguageId}", id);
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Language Delete Attempt {LanguageId}", id);
|
||||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
@ -21,19 +22,20 @@ namespace Oqtane.Infrastructure
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetDefaultCulture()
|
public string GetDefaultCulture()
|
||||||
=> String.IsNullOrEmpty(_localizationOptions.DefaultCulture)
|
{
|
||||||
? DefaultCulture
|
if (string.IsNullOrEmpty(_localizationOptions.DefaultCulture))
|
||||||
: _localizationOptions.DefaultCulture;
|
{
|
||||||
|
return DefaultCulture;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return _localizationOptions.DefaultCulture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string[] GetSupportedCultures()
|
public string[] GetSupportedCultures()
|
||||||
{
|
{
|
||||||
var cultures = new List<string>(DefaultSupportedCultures);
|
return CultureInfo.GetCultures(CultureTypes.AllCultures).Select(item => item.Name).OrderBy(c => c).ToArray();
|
||||||
foreach(var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Oqtane.Client.resources.dll", SearchOption.AllDirectories))
|
|
||||||
{
|
|
||||||
cultures.Add(Path.GetFileName(Path.GetDirectoryName(file)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return cultures.OrderBy(c => c).ToArray();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,13 +13,16 @@ namespace Oqtane.Repository
|
||||||
_db = context;
|
_db = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Language> GetLanguages(int siteId) => _db.Language.Where(l => l.SiteId == siteId);
|
public IEnumerable<Language> GetLanguages(int siteId)
|
||||||
|
{
|
||||||
|
return _db.Language.Where(l => l.SiteId == siteId);
|
||||||
|
}
|
||||||
|
|
||||||
public Language AddLanguage(Language language)
|
public Language AddLanguage(Language language)
|
||||||
{
|
{
|
||||||
if (language.IsDefault)
|
if (language.IsDefault)
|
||||||
{
|
{
|
||||||
// Ensure all other languages are not set to current
|
// Ensure all other languages are not set to default
|
||||||
_db.Language
|
_db.Language
|
||||||
.Where(l => l.SiteId == language.SiteId)
|
.Where(l => l.SiteId == language.SiteId)
|
||||||
.ToList()
|
.ToList()
|
||||||
|
@ -32,7 +35,10 @@ namespace Oqtane.Repository
|
||||||
return language;
|
return language;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Language GetLanguage(int languageId) => _db.Language.Find(languageId);
|
public Language GetLanguage(int languageId)
|
||||||
|
{
|
||||||
|
return _db.Language.Find(languageId);
|
||||||
|
}
|
||||||
|
|
||||||
public void DeleteLanguage(int languageId)
|
public void DeleteLanguage(int languageId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
namespace Oqtane.Models
|
namespace Oqtane.Models
|
||||||
{
|
{
|
||||||
|
@ -34,6 +35,12 @@ namespace Oqtane.Models
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsDefault { get; set; }
|
public bool IsDefault { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
/// <summary>
|
||||||
|
/// Version of the satellite assembly
|
||||||
|
/// </summary>
|
||||||
|
public string Version { get; set; }
|
||||||
|
|
||||||
#region IAuditable Properties
|
#region IAuditable Properties
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
|
@ -9,6 +9,7 @@ namespace Oqtane.Shared
|
||||||
public const string PackageId = "Oqtane.Framework";
|
public const string PackageId = "Oqtane.Framework";
|
||||||
public const string UpdaterPackageId = "Oqtane.Updater";
|
public const string UpdaterPackageId = "Oqtane.Updater";
|
||||||
public const string PackageRegistryUrl = "https://www.oqtane.net";
|
public const string PackageRegistryUrl = "https://www.oqtane.net";
|
||||||
|
public const string ClientAssemblyName = "Oqtane.Client";
|
||||||
|
|
||||||
public const string DefaultDBType = "Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Database.SqlServer";
|
public const string DefaultDBType = "Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Database.SqlServer";
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user