diff --git a/.gitignore b/.gitignore index 29b72c2a..8b66a5f3 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ msbuild.binlog .vscode/ *.binlog *.nupkg +*.zip *.idea @@ -17,3 +18,7 @@ Oqtane.Server/Data/*.db /Oqtane.Server/Properties/PublishProfiles/FolderProfile.pubxml Oqtane.Server/Content +Oqtane.Server/Packages +Oqtane.Server/wwwroot/Content +Oqtane.Server/wwwroot/Packages/*.log + diff --git a/Oqtane.Client/App.razor b/Oqtane.Client/App.razor index ca4b25bd..405bbd3a 100644 --- a/Oqtane.Client/App.razor +++ b/Oqtane.Client/App.razor @@ -1,4 +1,6 @@ @inject IInstallationService InstallationService +@inject IJSRuntime JSRuntime +@inject SiteState SiteState @if (_initialized) { @@ -20,21 +22,28 @@ {
@_installation.Message -
+ } } } @code { - private Installation _installation; - private bool _initialized; + private bool _initialized = false; + private Installation _installation = new Installation { Success = false, Message = "" }; private PageState PageState { get; set; } - protected override async Task OnParametersSetAsync() + protected override async Task OnAfterRenderAsync(bool firstRender) { - _installation = await InstallationService.IsInstalled(); - _initialized = true; + if (firstRender && !_initialized) + { + var interop = new Interop(JSRuntime); + SiteState.AntiForgeryToken = await interop.GetElementByName(Constants.RequestVerificationToken); + _installation = await InstallationService.IsInstalled(); + SiteState.Alias = _installation.Alias; + _initialized = true; + StateHasChanged(); + } } private void ChangeState(PageState pageState) diff --git a/Oqtane.Client/Installer/Controls/LocalDBConfig.razor b/Oqtane.Client/Installer/Controls/LocalDBConfig.razor new file mode 100644 index 00000000..0785fe04 --- /dev/null +++ b/Oqtane.Client/Installer/Controls/LocalDBConfig.razor @@ -0,0 +1,43 @@ +@namespace Oqtane.Installer.Controls +@implements Oqtane.Interfaces.IDatabaseConfigControl +@inject IStringLocalizer Localizer + +@{ + foreach (var field in _connectionStringFields) + { + var fieldId = field.Name.ToLowerInvariant(); + field.Value = field.Value.Replace("{{Date}}", DateTime.UtcNow.ToString("yyyyMMddHHmm")); + + + + + + + + + + } +} + +@code { + private readonly List _connectionStringFields = new() + { + new() {Name = "Server", FriendlyName = "Server", Value = "(LocalDb)\\MSSQLLocalDB", HelpText="Enter the database server"}, + new() {Name = "Database", FriendlyName = "Database", Value = "Oqtane-{{Date}}", HelpText="Enter the name of the database"} + }; + + public string GetConnectionString() + { + var connectionString = String.Empty; + + var server = _connectionStringFields[0].Value; + var database = _connectionStringFields[1].Value; + + if (!String.IsNullOrEmpty(server) && !String.IsNullOrEmpty(database)) + { + connectionString = $"Data Source={server};AttachDbFilename=|DataDirectory|\\{database}.mdf;Initial Catalog={database};Integrated Security=SSPI;"; + } + + return connectionString; + } +} \ No newline at end of file diff --git a/Oqtane.Client/Installer/Controls/MySQLConfig.razor b/Oqtane.Client/Installer/Controls/MySQLConfig.razor new file mode 100644 index 00000000..7bd1f512 --- /dev/null +++ b/Oqtane.Client/Installer/Controls/MySQLConfig.razor @@ -0,0 +1,54 @@ +@namespace Oqtane.Installer.Controls +@implements Oqtane.Interfaces.IDatabaseConfigControl +@inject IStringLocalizer Localizer + +@{ + foreach (var field in _connectionStringFields) + { + var fieldId = field.Name.ToLowerInvariant(); + var fieldType = (field.Name == "Pwd") ? "password" : "text"; + field.Value = field.Value.Replace("{{Date}}", DateTime.UtcNow.ToString("yyyyMMddHHmm")); + + + + + + + + + + } +} + +@code { + private readonly List _connectionStringFields = new() + { + new() {Name = "Server", FriendlyName = "Server", Value = "127.0.0.1", HelpText="Enter the database server"}, + new() {Name = "Port", FriendlyName = "Port", Value = "3306", HelpText="Enter the port used to connect to the server"}, + new() {Name = "Database", FriendlyName = "Database", Value = "Oqtane-{{Date}}", HelpText="Enter the name of the database"}, + new() {Name = "Uid", FriendlyName = "User Id", Value = "", HelpText="Enter the username to use for the database"}, + new() {Name = "Pwd", FriendlyName = "Password", Value = "", HelpText="Enter the password to use for the database"} + }; + + public string GetConnectionString() + { + var connectionString = String.Empty; + + var server = _connectionStringFields[0].Value; + var port = _connectionStringFields[1].Value; + var database = _connectionStringFields[2].Value; + var userId = _connectionStringFields[3].Value; + var password = _connectionStringFields[4].Value; + + if (!String.IsNullOrEmpty(server) && !String.IsNullOrEmpty(database) && !String.IsNullOrEmpty(userId) && !String.IsNullOrEmpty(password)) + { + connectionString = $"Server={server};Database={database};Uid={userId};Pwd={password};"; + } + + if (!String.IsNullOrEmpty(port)) + { + connectionString += $"Port={port};"; + } + return connectionString; + } +} \ No newline at end of file diff --git a/Oqtane.Client/Installer/Controls/PostgreSQLConfig.razor b/Oqtane.Client/Installer/Controls/PostgreSQLConfig.razor new file mode 100644 index 00000000..ac46bb69 --- /dev/null +++ b/Oqtane.Client/Installer/Controls/PostgreSQLConfig.razor @@ -0,0 +1,95 @@ +@namespace Oqtane.Installer.Controls +@implements Oqtane.Interfaces.IDatabaseConfigControl +@inject IStringLocalizer Localizer + +@{ + foreach (var field in _connectionStringFields) + { + var fieldId = field.Name.ToLowerInvariant(); + if (field.Name != "IntegratedSecurity") + { + var isVisible = ""; + var fieldType = (field.Name == "Pwd") ? "password" : "text"; + if ((field.Name == "Uid" || field.Name == "Pwd") ) + { + var intSecurityField = _connectionStringFields.Single(f => f.Name == "IntegratedSecurity"); + if (intSecurityField != null) + { + isVisible = (Convert.ToBoolean(intSecurityField.Value)) ? "display: none;" : ""; + } + } + + field.Value = field.Value.Replace("{{Date}}", DateTime.UtcNow.ToString("yyyyMMddHHmm")); + + + + + + + + + + } + else + { + + + + + + + + + } + } +} + +@code { + private readonly List _connectionStringFields = new() + { + new() {Name = "Server", FriendlyName = "Server", Value = "127.0.0.1", HelpText="Enter the database server"}, + new() {Name = "Port", FriendlyName = "Port", Value = "5432", HelpText="Enter the port used to connect to the server"}, + new() {Name = "Database", FriendlyName = "Database", Value = "Oqtane-{{Date}}", HelpText="Enter the name of the database"}, + new() {Name = "IntegratedSecurity", FriendlyName = "Integrated Security", Value = "true", HelpText="Select if you want integrated security or not"}, + new() {Name = "Uid", FriendlyName = "User Id", Value = "", HelpText="Enter the username to use for the database"}, + new() {Name = "Pwd", FriendlyName = "Password", Value = "", HelpText="Enter the password to use for the database"} + }; + + public string GetConnectionString() + { + var connectionString = String.Empty; + + var server = _connectionStringFields[0].Value; + var port = _connectionStringFields[1].Value; + var database = _connectionStringFields[2].Value; + var integratedSecurity = Boolean.Parse(_connectionStringFields[3].Value); + var userId = _connectionStringFields[4].Value; + var password = _connectionStringFields[5].Value; + + if (!String.IsNullOrEmpty(server) && !String.IsNullOrEmpty(database) && !String.IsNullOrEmpty(port)) + { + connectionString = $"Server={server};Port={port};Database={database};"; + } + + if (integratedSecurity) + { + connectionString += "Integrated Security=true;"; + } + else + { + if (!String.IsNullOrEmpty(userId) && !String.IsNullOrEmpty(password)) + { + connectionString += $"User ID={userId};Password={password};"; + } + else + { + connectionString = String.Empty; + } + } + + return connectionString; + } +} \ No newline at end of file diff --git a/Oqtane.Client/Installer/Controls/SqlServerConfig.razor b/Oqtane.Client/Installer/Controls/SqlServerConfig.razor new file mode 100644 index 00000000..302959f9 --- /dev/null +++ b/Oqtane.Client/Installer/Controls/SqlServerConfig.razor @@ -0,0 +1,94 @@ +@namespace Oqtane.Installer.Controls +@implements Oqtane.Interfaces.IDatabaseConfigControl +@inject IStringLocalizer Localizer + +@{ + foreach (var field in _connectionStringFields) + { + var fieldId = field.Name.ToLowerInvariant(); + if (field.Name != "IntegratedSecurity") + { + var isVisible = ""; + var fieldType = (field.Name == "Pwd") ? "password" : "text"; + if ((field.Name == "Uid" || field.Name == "Pwd") ) + { + var intSecurityField = _connectionStringFields.Single(f => f.Name == "IntegratedSecurity"); + if (intSecurityField != null) + { + isVisible = (Convert.ToBoolean(intSecurityField.Value)) ? "display: none;" : ""; + } + } + + field.Value = field.Value.Replace("{{Date}}", DateTime.UtcNow.ToString("yyyyMMddHHmm")); + + + + + + + + + + } + else + { + + + + + + + + + } + } +} + +@code { + private readonly List _connectionStringFields = new() + { + new() {Name = "Server", FriendlyName = "Server", Value = ".", HelpText="Enter the database server"}, + new() {Name = "Database", FriendlyName = "Database", Value = "Oqtane-{{Date}}", HelpText="Enter the name of the database"}, + new() {Name = "IntegratedSecurity", FriendlyName = "Integrated Security", Value = "true", HelpText="Select if you want integrated security or not"}, + new() {Name = "Uid", FriendlyName = "User Id", Value = "", HelpText="Enter the username to use for the database"}, + new() {Name = "Pwd", FriendlyName = "Password", Value = "", HelpText="Enter the password to use for the database"} + }; + + public string GetConnectionString() + { + var connectionString = String.Empty; + + var server = _connectionStringFields[0].Value; + var database = _connectionStringFields[1].Value; + var integratedSecurity = Boolean.Parse(_connectionStringFields[2].Value); + var userId = _connectionStringFields[3].Value; + var password = _connectionStringFields[4].Value; + + if (!String.IsNullOrEmpty(server) && !String.IsNullOrEmpty(database)) + { + connectionString = $"Data Source={server};Initial Catalog={database};"; + } + + if (integratedSecurity) + { + connectionString += "Integrated Security=SSPI;"; + } + else + { + if (!String.IsNullOrEmpty(userId) && !String.IsNullOrEmpty(password)) + { + connectionString += $"User ID={userId};Password={password};"; + } + else + { + connectionString = String.Empty; + } + } + + return connectionString; + } + +} \ No newline at end of file diff --git a/Oqtane.Client/Installer/Controls/SqliteConfig.razor b/Oqtane.Client/Installer/Controls/SqliteConfig.razor new file mode 100644 index 00000000..e0445c7c --- /dev/null +++ b/Oqtane.Client/Installer/Controls/SqliteConfig.razor @@ -0,0 +1,41 @@ +@namespace Oqtane.Installer.Controls +@implements Oqtane.Interfaces.IDatabaseConfigControl +@inject IStringLocalizer Localizer + +@{ + foreach (var field in _connectionStringFields) + { + var fieldId = field.Name.ToLowerInvariant(); + field.Value = field.Value.Replace("{{Date}}", DateTime.UtcNow.ToString("yyyyMMddHHmm")); + + + + + + + + + + } +} + +@code { + private readonly List _connectionStringFields = new() + { + new() {Name = "Server", FriendlyName = "File Name", Value = "Oqtane-{{Date}}.db", HelpText="Enter the file name to use for the database"} + }; + + public string GetConnectionString() + { + var connectionstring = String.Empty; + + var server = _connectionStringFields[0].Value; + + if (!String.IsNullOrEmpty(server)) + { + connectionstring = $"Data Source={server};"; + } + + return connectionstring; + } +} \ No newline at end of file diff --git a/Oqtane.Client/UI/Installer.razor b/Oqtane.Client/Installer/Installer.razor similarity index 55% rename from Oqtane.Client/UI/Installer.razor rename to Oqtane.Client/Installer/Installer.razor index 3c013711..c90c3bc9 100644 --- a/Oqtane.Client/UI/Installer.razor +++ b/Oqtane.Client/Installer/Installer.razor @@ -1,13 +1,12 @@ +@namespace Oqtane.Installer @using Oqtane.Interfaces -@using System.Reflection -@namespace Oqtane.UI @inject NavigationManager NavigationManager @inject IInstallationService InstallationService @inject ISiteService SiteService @inject IUserService UserService +@inject IDatabaseService DatabaseService @inject IJSRuntime JSRuntime @inject IStringLocalizer Localizer -@inject IEnumerable Databases
@@ -24,63 +23,33 @@ - + - + @if (_databases != null) + { + foreach (var database in _databases) { - + if (database.IsDefault) + { + + } + else + { + + } } } - @{ - _selectedDatabase = Databases.Single(d => d.Name == _databaseType); - foreach (var field in _selectedDatabase.ConnectionStringFields) - { - if (field.Name != "IntegratedSecurity") + @{ + if (_databaseConfigType != null) { - var isVisible = ""; - var fieldType = (field.Name == "Pwd") ? "password" : "text"; - if ((field.Name == "Uid" || field.Name == "Pwd") && _selectedDatabase.Name != "MySQL" ) - { - var intSecurityField = _selectedDatabase.ConnectionStringFields.Single(f => f.Name == "IntegratedSecurity"); - if (intSecurityField != null) - { - isVisible = (Convert.ToBoolean(intSecurityField.Value)) ? "display: none;" : ""; - } - } - - field.Value = field.Value.Replace("{{Date}}", DateTime.UtcNow.ToString("yyyyMMddHHmm")); - - - - - - - - - - } - else - { - - - - - - - - + @DatabaseConfigComponent; } } - }
@@ -90,31 +59,31 @@ - + - + - + - + - + - + - + @@ -135,8 +104,12 @@
@code { - private IOqtaneDatabase _selectedDatabase; - private string _databaseType = "LocalDB"; + private List _databases; + private string _databaseName = "LocalDB"; + private Type _databaseConfigType; + private object _databaseConfig; + private RenderFragment DatabaseConfigComponent { get; set; } + private string _hostUsername = UserNames.Host; private string _hostPassword = string.Empty; private string _confirmPassword = string.Empty; @@ -144,6 +117,41 @@ private string _message = string.Empty; private string _loadingDisplay = "display: none;"; + protected override async Task OnInitializedAsync() + { + _databases = await DatabaseService.GetDatabasesAsync(); + LoadDatabaseConfigComponent(); + } + + private void DatabaseChanged(ChangeEventArgs eventArgs) + { + try + { + _databaseName = (string)eventArgs.Value; + + LoadDatabaseConfigComponent(); + } + catch + { + _message = Localizer["Error loading Database Configuration Control"]; + } + } + + private void LoadDatabaseConfigComponent() + { + var database = _databases.SingleOrDefault(d => d.Name == _databaseName); + if (database != null) + { + _databaseConfigType = Type.GetType(database.ControlType); + DatabaseConfigComponent = builder => + { + builder.OpenComponent(0, _databaseConfigType); + builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); }); + builder.CloseComponent(); + }; + } + } + protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) @@ -155,7 +163,11 @@ private async Task Install() { - var connectionString = _selectedDatabase.BuildConnectionString(); + var connectionString = String.Empty; + if (_databaseConfig is IDatabaseConfigControl databaseConfigControl) + { + connectionString = databaseConfigControl.GetConnectionString(); + } if (connectionString != "" && _hostUsername != "" && _hostPassword.Length >= 6 && _hostPassword == _confirmPassword && _hostEmail != "") { @@ -163,10 +175,12 @@ StateHasChanged(); Uri uri = new Uri(NavigationManager.Uri); + + var database = _databases.SingleOrDefault(d => d.Name == _databaseName); var config = new InstallConfig { - DatabaseType = _databaseType, + DatabaseType = database.DBType, ConnectionString = connectionString, Aliases = uri.Authority, HostEmail = _hostEmail, diff --git a/Oqtane.Client/Localization/SharedResources.cs b/Oqtane.Client/Localization/SharedResources.cs new file mode 100644 index 00000000..77cb9d36 --- /dev/null +++ b/Oqtane.Client/Localization/SharedResources.cs @@ -0,0 +1,47 @@ +namespace Oqtane +{ + public class SharedResources + { + public static readonly string UserLogin = "User Login"; + + public static readonly string UserRegistration = "User Registration"; + + public static readonly string PasswordReset = "Password Reset"; + + public static readonly string UserProfile = "User Profile"; + + public static readonly string AdminDashboard = "Admin Dashboard"; + + public static readonly string SiteSettings = "Site Settings"; + + public static readonly string PageManagement = "Page Management"; + + public static readonly string UserManagement = "User Management"; + + public static readonly string ProfileManagement = "Profile Management"; + + public static readonly string RoleManagement = "Role Management"; + + public static readonly string FileManagement = "File Management"; + + public static readonly string RecycleBin = "Recycle Bin"; + + public static readonly string EventLog = "Event Log"; + + public static readonly string SiteManagement = "Site Management"; + + public static readonly string ModuleManagement = "Module Management"; + + public static readonly string ThemeManagement = "Theme Management"; + + public static readonly string LanguageManagement = "Language Management"; + + public static readonly string ScheduledJobs = "Scheduled Jobs"; + + public static readonly string SqlManagement = "Sql Management"; + + public static readonly string SystemInfo = "System Info"; + + public static readonly string SystemUpdate = "System Update"; + } +} diff --git a/Oqtane.Client/Modules/Admin/Dashboard/Index.razor b/Oqtane.Client/Modules/Admin/Dashboard/Index.razor index 650eaee6..0c7cd842 100644 --- a/Oqtane.Client/Modules/Admin/Dashboard/Index.razor +++ b/Oqtane.Client/Modules/Admin/Dashboard/Index.razor @@ -2,6 +2,7 @@ @inherits ModuleBase @inject IPageService PageService @inject IUserService UserService +@inject IStringLocalizer Localizer
@foreach (var p in _pages) @@ -11,7 +12,7 @@ string url = NavigateUrl(p.Path);
-

@p.Name +

@Localizer[p.Name]
} @@ -20,7 +21,7 @@ @code { private List _pages; - + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; protected override void OnInitialized() diff --git a/Oqtane.Client/Modules/Admin/Files/Edit.razor b/Oqtane.Client/Modules/Admin/Files/Edit.razor index b2ac3b64..64694717 100644 --- a/Oqtane.Client/Modules/Admin/Files/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Files/Edit.razor @@ -33,6 +33,24 @@ + + + + + + @if (PageState.QueryString.ContainsKey("id")) + { + + } + else + { + + } + + @@ -43,10 +61,12 @@ @if (!_isSystem) { + @((MarkupString)" ") } @Localizer["Cancel"] @if (!_isSystem && PageState.QueryString.ContainsKey("id")) { + @((MarkupString)" ") }
@@ -60,8 +80,9 @@ @code { private List _folders; private int _folderId = -1; - private string _name; private int _parentId = -1; + private string _name; + private string _type = FolderTypes.Private; private bool _isSystem; private string _permissions = string.Empty; private string _createdBy; @@ -91,6 +112,7 @@ { _parentId = folder.ParentId ?? -1; _name = folder.Name; + _type = folder.Type; _isSystem = folder.IsSystem; _permissions = folder.Permissions; _createdBy = folder.CreatedBy; @@ -150,6 +172,7 @@ } folder.Name = _name; + folder.Type = _type; folder.IsSystem = _isSystem; folder.Permissions = _permissionGrid.GetPermissions(); diff --git a/Oqtane.Client/Modules/Admin/Files/Index.razor b/Oqtane.Client/Modules/Admin/Files/Index.razor index 58990804..bd670d21 100644 --- a/Oqtane.Client/Modules/Admin/Files/Index.razor +++ b/Oqtane.Client/Modules/Admin/Files/Index.razor @@ -39,7 +39,7 @@ - @context.Name + @context.Name @context.ModifiedOn @context.Extension.ToUpper() @Localizer["File"] @string.Format("{0:0.00}", ((decimal)context.Size / 1000)) KB diff --git a/Oqtane.Client/Modules/Admin/Languages/Add.razor b/Oqtane.Client/Modules/Admin/Languages/Add.razor index 7efbcf85..aecb372c 100644 --- a/Oqtane.Client/Modules/Admin/Languages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Languages/Add.razor @@ -5,6 +5,7 @@ @inject NavigationManager NavigationManager @inject ILocalizationService LocalizationService @inject ILanguageService LanguageService +@inject IPackageService PackageService @inject IStringLocalizer Localizer @if (_supportedCultures == null) @@ -13,53 +14,114 @@ } else { - @if (_supportedCultures?.Count() > 1) - { - - - - - - - - - -
- - - -
- - - -
- - } - @Localizer["Cancel"] + + + @if (_availableCultures.Count() == 0) + { + + } + else + { + + + + + + + + + +
+ + + +
+ + + +
+ + } + @Localizer["Cancel"] +
+ + @if (_packages != null && _packages.Count > 0) + { + + +
+ @Localizer["Name"] + @Localizer["Version"] + +
+ + @context.Name + @context.Version + + + + +
+ + @Localizer["Cancel"] + } + else + { + + } +
+ + + + + + +
+ + + +
+ + @Localizer["Cancel"] +
+
} @code { private string _code = string.Empty; private string _isDefault = "False"; + private string _message; + private IEnumerable _supportedCultures; + private IEnumerable _availableCultures; + private List _packages; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; - private IEnumerable _supportedCultures; - protected override async Task OnParametersSetAsync() { + var languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId); + var languagesCodes = languages.Select(l => l.Code).ToList(); + _supportedCultures = await LocalizationService.GetCulturesAsync(); - if (_supportedCultures.Count() <= 1) + _availableCultures = _supportedCultures + .Where(c => !c.Name.Equals(Constants.DefaultCulture) && !languagesCodes.Contains(c.Name)); + _packages = await PackageService.GetPackagesAsync("language"); + + if (_supportedCultures.Count() == 1) { - AddModuleMessage(Localizer["The Only Supported Culture That Has Been Defined Is English"], MessageType.Warning); + _message = Localizer["The Only Installed Language Is English"]; + } + else if (_availableCultures.Count() == 0) + { + _message = Localizer["All The Installed Languages Have Been Added."]; } } @@ -93,6 +155,35 @@ else } } + private async Task InstallLanguages() + { + try + { + await PackageService.InstallPackagesAsync(); + AddModuleMessage(Localizer["Language Packages Installed Successfully. You Must Restart Your Application To Apply These Changes.", NavigateUrl("admin/system")], MessageType.Success); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Installing Language Package"); + } + } + + private async Task DownloadLanguage(string packageid, string version) + { + try + { + await PackageService.DownloadPackageAsync(packageid, version, "Packages"); + await logger.LogInformation("Language Paclage {Name} {Version} Downloaded Successfully", packageid, version); + AddModuleMessage(Localizer["Language Package Downloaded Successfully. Click Install To Complete Installation."], MessageType.Success); + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Downloading Language Package {Name} {Version}", packageid, version); + AddModuleMessage(Localizer["Error Downloading Language Package"], MessageType.Error); + } + } + private async Task SetCultureAsync(string culture) { if (culture != CultureInfo.CurrentUICulture.Name) diff --git a/Oqtane.Client/Modules/Admin/Languages/Index.razor b/Oqtane.Client/Modules/Admin/Languages/Index.razor index 748c875b..eef0c6af 100644 --- a/Oqtane.Client/Modules/Admin/Languages/Index.razor +++ b/Oqtane.Client/Modules/Admin/Languages/Index.razor @@ -1,6 +1,8 @@ @namespace Oqtane.Modules.Admin.Languages @inherits ModuleBase @inject ILanguageService LanguageService +@inject ILocalizationService LocalizationService +@inject IPackageService PackageService @inject IStringLocalizer Localizer @if (_languages == null) @@ -17,24 +19,43 @@ else @Localizer["Name"] @Localizer["Code"] @Localizer["Default?"] +   - + @context.Name @context.Code + + @if (UpgradeAvailable(context.Code)) + { + + } + } @code { private List _languages; + private List _packages; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; protected override async Task OnParametersSetAsync() { _languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId); + + var cultures = await LocalizationService.GetCulturesAsync(); + 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)) + { + _packages = await PackageService.GetPackagesAsync("language"); + } } private async Task DeleteLanguage(Language language) @@ -53,4 +74,38 @@ else AddModuleMessage(Localizer["Error Deleting Language"], MessageType.Error); } } + + private bool UpgradeAvailable(string code) + { + var upgradeavailable = false; + if (_packages != null) + { + var package = _packages.Where(item => item.PackageId == (Constants.PackageId + ".Client." + code)).FirstOrDefault(); + if (package != null) + { + upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) > 0); + } + + } + return upgradeavailable; + } + + private async Task DownloadLanguage(string code) + { + try + { + if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) + { + await PackageService.DownloadPackageAsync(Constants.PackageId + ".Client." + code, Constants.Version, "Packages"); + await logger.LogInformation("Language Package Downloaded {Code} {Version}", code, Constants.Version); + await PackageService.InstallPackagesAsync(); + AddModuleMessage(Localizer["Language Package Installed Successfully. You Must Restart Your Application To Apply These Changes.", NavigateUrl("admin/system")], MessageType.Success); + } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Downloading Language Package {Code} {Version} {Error}", code, Constants.Version, ex.Message); + AddModuleMessage(Localizer["Error Downloading Language Package"], MessageType.Error); + } + } } diff --git a/Oqtane.Client/Modules/Admin/Login/Index.razor b/Oqtane.Client/Modules/Admin/Login/Index.razor index 86ec7b0a..45f4e246 100644 --- a/Oqtane.Client/Modules/Admin/Login/Index.razor +++ b/Oqtane.Client/Modules/Admin/Login/Index.razor @@ -3,13 +3,20 @@ @inject NavigationManager NavigationManager @inject IUserService UserService @inject IServiceProvider ServiceProvider +@inject SiteState SiteState @inject IStringLocalizer Localizer @if (_message != string.Empty) { } - + + + ... + + + +