Merge remote-tracking branch 'oqtane/dev' into dev
This commit is contained in:
commit
6bc1507114
|
@ -64,6 +64,12 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="validation" HelpText="Optionally provide a regular expression (RegExp) for validating the value entered" ResourceKey="Validation">Validation: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="validation" class="form-control" @bind="@_validation" maxlength="200" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="private" HelpText="Should this profile item be visible to all users?" ResourceKey="Private">Private? </Label>
|
||||
<div class="col-sm-9">
|
||||
|
@ -97,6 +103,7 @@
|
|||
private string _maxlength = "0";
|
||||
private string _defaultvalue = string.Empty;
|
||||
private string _options = string.Empty;
|
||||
private string _validation = string.Empty;
|
||||
private string _isrequired = "False";
|
||||
private string _isprivate = "False";
|
||||
private string createdby;
|
||||
|
@ -126,6 +133,7 @@
|
|||
_maxlength = profile.MaxLength.ToString();
|
||||
_defaultvalue = profile.DefaultValue;
|
||||
_options = profile.Options;
|
||||
_validation = profile.Validation;
|
||||
_isrequired = profile.IsRequired.ToString();
|
||||
_isprivate = profile.IsPrivate.ToString();
|
||||
createdby = profile.CreatedBy;
|
||||
|
@ -169,6 +177,7 @@
|
|||
profile.MaxLength = int.Parse(_maxlength);
|
||||
profile.DefaultValue = _defaultvalue;
|
||||
profile.Options = _options;
|
||||
profile.Validation = _validation;
|
||||
profile.IsRequired = (_isrequired == null ? false : Boolean.Parse(_isrequired));
|
||||
profile.IsPrivate = (_isprivate == null ? false : Boolean.Parse(_isprivate));
|
||||
if (_profileid != -1)
|
||||
|
|
|
@ -145,9 +145,9 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="enabledSSl" HelpText="Specify if SSL is required for your SMTP server" ResourceKey="UseSsl">SSL Enabled: </Label>
|
||||
<Label Class="col-sm-3" For="smtpssl" HelpText="Specify if SSL is required for your SMTP server" ResourceKey="UseSsl">SSL Enabled: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="enabledSSl" class="form-select" @bind="@_smtpssl" >
|
||||
<select id="smtpssl" class="form-select" @bind="@_smtpssl" >
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
|
@ -183,7 +183,16 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="smtpenabled" HelpText="Specify if SMTP is enabled for this site" ResourceKey="SMTPEnabled">Enabled? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="smtpenabled" class="form-select" @bind="@_smtpenabled">
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="retention" HelpText="Number of days of notifications to retain" ResourceKey="Retention">Retention (Days): </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="retention" class="form-control" @bind="@_retention" />
|
||||
|
@ -354,6 +363,7 @@
|
|||
private string _togglesmtppassword = string.Empty;
|
||||
private string _smtpsender = string.Empty;
|
||||
private string _smtprelay = "False";
|
||||
private string _smtpenabled = "True";
|
||||
private string _retention = string.Empty;
|
||||
private string _pwaisenabled;
|
||||
private int _pwaappiconfileid = -1;
|
||||
|
@ -434,6 +444,7 @@
|
|||
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
||||
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
|
||||
_smtprelay = SettingService.GetSetting(settings, "SMTPRelay", "False");
|
||||
_smtpenabled = SettingService.GetSetting(settings, "SMTPEnabled", "True");
|
||||
_retention = SettingService.GetSetting(settings, "NotificationRetention", "30");
|
||||
|
||||
// aliases
|
||||
|
@ -600,7 +611,8 @@
|
|||
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPRelay", _smtprelay, true);
|
||||
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPEnabled", _smtpenabled, true);
|
||||
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||
|
||||
await logger.LogInformation("Site Settings Saved {Site}", site);
|
||||
|
@ -612,8 +624,8 @@
|
|||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success);
|
||||
await interop.ScrollTo(0, 0, "smooth");
|
||||
}
|
||||
await ScrollToPageTop();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -681,9 +693,8 @@
|
|||
|
||||
await NotificationService.AddNotificationAsync(new Notification(PageState.Site.SiteId, PageState.User, PageState.Site.Name + " SMTP Configuration Test", "SMTP Server Is Configured Correctly."));
|
||||
AddModuleMessage(Localizer["Info.Smtp.SaveSettings"], MessageType.Info);
|
||||
var interop = new Interop(JSRuntime);
|
||||
await interop.ScrollTo(0, 0, "smooth");
|
||||
}
|
||||
await ScrollToPageTop();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Testing SMTP Configuration");
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="name" HelpText="The name of the module" ResourceKey="Name">Name: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="name" class="form-control" @bind="@_name" disabled />
|
||||
<input id="name" class="form-control" @bind="@_name" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
|
@ -89,32 +89,32 @@
|
|||
private string _themeName = "";
|
||||
private string _isenabled;
|
||||
private string _name;
|
||||
private string _version;
|
||||
private string _packagename;
|
||||
private string _owner = "";
|
||||
private string _url = "";
|
||||
private string _contact = "";
|
||||
private string _license = "";
|
||||
private string _version;
|
||||
private string _packagename;
|
||||
private string _owner = "";
|
||||
private string _url = "";
|
||||
private string _contact = "";
|
||||
private string _license = "";
|
||||
private string _createdby;
|
||||
private DateTime _createdon;
|
||||
private string _modifiedby;
|
||||
private DateTime _modifiedon;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_themeId = Int32.Parse(PageState.QueryString["id"]);
|
||||
var theme = await ThemeService.GetThemeAsync(_themeId, ModuleState.SiteId);
|
||||
if (theme != null)
|
||||
{
|
||||
_name = theme.Name;
|
||||
if (theme != null)
|
||||
{
|
||||
_name = theme.Name;
|
||||
_isenabled =theme.IsEnabled.ToString();
|
||||
_version = theme.Version;
|
||||
_packagename = theme.PackageName;
|
||||
_owner = theme.Owner;
|
||||
_packagename = theme.PackageName;
|
||||
_owner = theme.Owner;
|
||||
_url = theme.Url;
|
||||
_contact = theme.Contact;
|
||||
_license = theme.License;
|
||||
|
@ -142,6 +142,7 @@
|
|||
try
|
||||
{
|
||||
var theme = await ThemeService.GetThemeAsync(_themeId, ModuleState.SiteId);
|
||||
theme.Name = _name;
|
||||
theme.IsEnabled = (_isenabled == null ? true : bool.Parse(_isenabled));
|
||||
await ThemeService.UpdateThemeAsync(theme);
|
||||
await logger.LogInformation("Theme Saved {Theme}", theme);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@namespace Oqtane.Modules.Admin.UserProfile
|
||||
@using System.Text.RegularExpressions;
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IUserService UserService
|
||||
|
@ -227,140 +228,140 @@ else
|
|||
<br /><br />
|
||||
|
||||
@code {
|
||||
private string username = string.Empty;
|
||||
private string _password = string.Empty;
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private string confirm = string.Empty;
|
||||
private bool allowtwofactor = false;
|
||||
private string twofactor = "False";
|
||||
private string email = string.Empty;
|
||||
private string displayname = string.Empty;
|
||||
private FileManager filemanager;
|
||||
private int folderid = -1;
|
||||
private int photofileid = -1;
|
||||
private File photo = null;
|
||||
private List<Profile> profiles;
|
||||
private Dictionary<string, string> settings;
|
||||
private string category = string.Empty;
|
||||
private string filter = "to";
|
||||
private List<Notification> notifications;
|
||||
private string username = string.Empty;
|
||||
private string _password = string.Empty;
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private string confirm = string.Empty;
|
||||
private bool allowtwofactor = false;
|
||||
private string twofactor = "False";
|
||||
private string email = string.Empty;
|
||||
private string displayname = string.Empty;
|
||||
private FileManager filemanager;
|
||||
private int folderid = -1;
|
||||
private int photofileid = -1;
|
||||
private File photo = null;
|
||||
private List<Profile> profiles;
|
||||
private Dictionary<string, string> settings;
|
||||
private string category = string.Empty;
|
||||
private string filter = "to";
|
||||
private List<Notification> notifications;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
|
||||
if (PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:TwoFactor"]))
|
||||
{
|
||||
allowtwofactor = (PageState.Site.Settings["LoginOptions:TwoFactor"] == "true");
|
||||
}
|
||||
if (PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:TwoFactor"]))
|
||||
{
|
||||
allowtwofactor = (PageState.Site.Settings["LoginOptions:TwoFactor"] == "true");
|
||||
}
|
||||
|
||||
if (PageState.User != null)
|
||||
{
|
||||
username = PageState.User.Username;
|
||||
twofactor = PageState.User.TwoFactorRequired.ToString();
|
||||
email = PageState.User.Email;
|
||||
displayname = PageState.User.DisplayName;
|
||||
if (PageState.User != null)
|
||||
{
|
||||
username = PageState.User.Username;
|
||||
twofactor = PageState.User.TwoFactorRequired.ToString();
|
||||
email = PageState.User.Email;
|
||||
displayname = PageState.User.DisplayName;
|
||||
|
||||
// get user folder
|
||||
var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath);
|
||||
if (folder != null)
|
||||
{
|
||||
folderid = folder.FolderId;
|
||||
}
|
||||
// get user folder
|
||||
var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath);
|
||||
if (folder != null)
|
||||
{
|
||||
folderid = folder.FolderId;
|
||||
}
|
||||
|
||||
if (PageState.User.PhotoFileId != null)
|
||||
{
|
||||
photofileid = PageState.User.PhotoFileId.Value;
|
||||
photo = await FileService.GetFileAsync(photofileid);
|
||||
}
|
||||
else
|
||||
{
|
||||
photofileid = -1;
|
||||
photo = null;
|
||||
}
|
||||
if (PageState.User.PhotoFileId != null)
|
||||
{
|
||||
photofileid = PageState.User.PhotoFileId.Value;
|
||||
photo = await FileService.GetFileAsync(photofileid);
|
||||
}
|
||||
else
|
||||
{
|
||||
photofileid = -1;
|
||||
photo = null;
|
||||
}
|
||||
|
||||
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
|
||||
settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
|
||||
settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||
|
||||
await LoadNotificationsAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.User.NoLogIn"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading User Profile {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Profile.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
await LoadNotificationsAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.User.NoLogIn"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading User Profile {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Profile.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadNotificationsAsync()
|
||||
{
|
||||
notifications = await NotificationService.GetNotificationsAsync(PageState.Site.SiteId, filter, PageState.User.UserId);
|
||||
notifications = notifications.Where(item => item.DeletedBy != PageState.User.Username).ToList();
|
||||
}
|
||||
private async Task LoadNotificationsAsync()
|
||||
{
|
||||
notifications = await NotificationService.GetNotificationsAsync(PageState.Site.SiteId, filter, PageState.User.UserId);
|
||||
notifications = notifications.Where(item => item.DeletedBy != PageState.User.Username).ToList();
|
||||
}
|
||||
|
||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||
{
|
||||
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
||||
if (value.Contains("]"))
|
||||
{
|
||||
value = value.Substring(value.IndexOf("]") + 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||
{
|
||||
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
||||
if (value.Contains("]"))
|
||||
{
|
||||
value = value.Substring(value.IndexOf("]") + 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private async Task Save()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
||||
{
|
||||
if (_password == confirm)
|
||||
{
|
||||
var user = PageState.User;
|
||||
user.Username = username;
|
||||
user.Password = _password;
|
||||
user.TwoFactorRequired = bool.Parse(twofactor);
|
||||
user.Email = email;
|
||||
user.DisplayName = (displayname == string.Empty ? username : displayname);
|
||||
user.PhotoFileId = filemanager.GetFileId();
|
||||
if (user.PhotoFileId == -1)
|
||||
{
|
||||
user.PhotoFileId = null;
|
||||
}
|
||||
if (user.PhotoFileId != null)
|
||||
{
|
||||
photofileid = user.PhotoFileId.Value;
|
||||
photo = await FileService.GetFileAsync(photofileid);
|
||||
}
|
||||
else
|
||||
{
|
||||
photofileid = -1;
|
||||
photo = null;
|
||||
}
|
||||
private async Task Save()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
||||
{
|
||||
if (_password == confirm)
|
||||
{
|
||||
var user = PageState.User;
|
||||
user.Username = username;
|
||||
user.Password = _password;
|
||||
user.TwoFactorRequired = bool.Parse(twofactor);
|
||||
user.Email = email;
|
||||
user.DisplayName = (displayname == string.Empty ? username : displayname);
|
||||
user.PhotoFileId = filemanager.GetFileId();
|
||||
if (user.PhotoFileId == -1)
|
||||
{
|
||||
user.PhotoFileId = null;
|
||||
}
|
||||
if (user.PhotoFileId != null)
|
||||
{
|
||||
photofileid = user.PhotoFileId.Value;
|
||||
photo = await FileService.GetFileAsync(photofileid);
|
||||
}
|
||||
else
|
||||
{
|
||||
photofileid = -1;
|
||||
photo = null;
|
||||
}
|
||||
|
||||
user = await UserService.UpdateUserAsync(user);
|
||||
if (user != null)
|
||||
{
|
||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||
await logger.LogInformation("User Profile Saved");
|
||||
user = await UserService.UpdateUserAsync(user);
|
||||
if (user != null)
|
||||
{
|
||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||
await logger.LogInformation("User Profile Saved");
|
||||
|
||||
AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success);
|
||||
StateHasChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success);
|
||||
StateHasChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Password.Invalid"], MessageType.Warning);
|
||||
|
@ -370,6 +371,8 @@ else
|
|||
{
|
||||
AddModuleMessage(Localizer["Message.Required.ProfileInfo"], MessageType.Warning);
|
||||
}
|
||||
|
||||
await ScrollToPageTop();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -389,10 +392,15 @@ else
|
|||
}
|
||||
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
{
|
||||
if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
|
||||
if (valid == true && profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
|
||||
{
|
||||
valid = false;
|
||||
}
|
||||
if (valid == true && !string.IsNullOrEmpty(profile.Validation))
|
||||
{
|
||||
Regex regex = new Regex(profile.Validation);
|
||||
valid = regex.Match(SettingService.GetSetting(settings, profile.Name, string.Empty)).Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@namespace Oqtane.Modules.Admin.Users
|
||||
@using System.Text.RegularExpressions;
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IUserService UserService
|
||||
|
@ -95,8 +96,8 @@
|
|||
@code {
|
||||
private string username = string.Empty;
|
||||
private string _password = string.Empty;
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private string confirm = string.Empty;
|
||||
private string email = string.Empty;
|
||||
private string displayname = string.Empty;
|
||||
|
@ -121,15 +122,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||
{
|
||||
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
||||
if (value.Contains("]"))
|
||||
{
|
||||
value = value.Substring(value.IndexOf("]") + 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||
{
|
||||
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
||||
if (value.Contains("]"))
|
||||
{
|
||||
value = value.Substring(value.IndexOf("]") + 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private async Task SaveUser()
|
||||
{
|
||||
|
@ -195,9 +196,17 @@
|
|||
{
|
||||
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
|
||||
}
|
||||
if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
|
||||
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
{
|
||||
valid = false;
|
||||
if (valid == true && profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
|
||||
{
|
||||
valid = false;
|
||||
}
|
||||
if (valid == true && !string.IsNullOrEmpty(profile.Validation))
|
||||
{
|
||||
Regex regex = new Regex(profile.Validation);
|
||||
valid = regex.Match(SettingService.GetSetting(settings, profile.Name, string.Empty)).Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@namespace Oqtane.Modules.Admin.Users
|
||||
@using System.Text.RegularExpressions;
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IUserService UserService
|
||||
|
@ -148,124 +149,124 @@ else
|
|||
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon" DeletedBy="@deletedby" DeletedOn="@deletedon"></AuditInfo>
|
||||
|
||||
@code {
|
||||
private int userid;
|
||||
private string username = string.Empty;
|
||||
private string _password = string.Empty;
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private string confirm = string.Empty;
|
||||
private string email = string.Empty;
|
||||
private string displayname = string.Empty;
|
||||
private FileManager filemanager;
|
||||
private int photofileid = -1;
|
||||
private File photo = null;
|
||||
private string isdeleted;
|
||||
private string lastlogin;
|
||||
private string lastipaddress;
|
||||
private int userid;
|
||||
private string username = string.Empty;
|
||||
private string _password = string.Empty;
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private string confirm = string.Empty;
|
||||
private string email = string.Empty;
|
||||
private string displayname = string.Empty;
|
||||
private FileManager filemanager;
|
||||
private int photofileid = -1;
|
||||
private File photo = null;
|
||||
private string isdeleted;
|
||||
private string lastlogin;
|
||||
private string lastipaddress;
|
||||
|
||||
private List<Profile> profiles;
|
||||
private Dictionary<string, string> settings;
|
||||
private string category = string.Empty;
|
||||
private List<Profile> profiles;
|
||||
private Dictionary<string, string> settings;
|
||||
private string category = string.Empty;
|
||||
|
||||
private string createdby;
|
||||
private DateTime createdon;
|
||||
private string modifiedby;
|
||||
private DateTime modifiedon;
|
||||
private string deletedby;
|
||||
private DateTime? deletedon;
|
||||
private string createdby;
|
||||
private DateTime createdon;
|
||||
private string modifiedby;
|
||||
private DateTime modifiedon;
|
||||
private string deletedby;
|
||||
private DateTime? deletedon;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PageState.QueryString.ContainsKey("id"))
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
|
||||
userid = Int32.Parse(PageState.QueryString["id"]);
|
||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
username = user.Username;
|
||||
email = user.Email;
|
||||
displayname = user.DisplayName;
|
||||
if (user.PhotoFileId != null)
|
||||
{
|
||||
photofileid = user.PhotoFileId.Value;
|
||||
photo = await FileService.GetFileAsync(photofileid);
|
||||
}
|
||||
else
|
||||
{
|
||||
photofileid = -1;
|
||||
photo = null;
|
||||
}
|
||||
isdeleted = user.IsDeleted.ToString();
|
||||
lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn);
|
||||
lastipaddress = user.LastIPAddress;
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PageState.QueryString.ContainsKey("id"))
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
|
||||
userid = Int32.Parse(PageState.QueryString["id"]);
|
||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
username = user.Username;
|
||||
email = user.Email;
|
||||
displayname = user.DisplayName;
|
||||
if (user.PhotoFileId != null)
|
||||
{
|
||||
photofileid = user.PhotoFileId.Value;
|
||||
photo = await FileService.GetFileAsync(photofileid);
|
||||
}
|
||||
else
|
||||
{
|
||||
photofileid = -1;
|
||||
photo = null;
|
||||
}
|
||||
isdeleted = user.IsDeleted.ToString();
|
||||
lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn);
|
||||
lastipaddress = user.LastIPAddress;
|
||||
|
||||
settings = await SettingService.GetUserSettingsAsync(user.UserId);
|
||||
createdby = user.CreatedBy;
|
||||
createdon = user.CreatedOn;
|
||||
modifiedby = user.ModifiedBy;
|
||||
modifiedon = user.ModifiedOn;
|
||||
deletedby = user.DeletedBy;
|
||||
deletedon = user.DeletedOn;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading User {UserId} {Error}", userid, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.User.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
settings = await SettingService.GetUserSettingsAsync(user.UserId);
|
||||
createdby = user.CreatedBy;
|
||||
createdon = user.CreatedOn;
|
||||
modifiedby = user.ModifiedBy;
|
||||
modifiedon = user.ModifiedOn;
|
||||
deletedby = user.DeletedBy;
|
||||
deletedon = user.DeletedOn;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading User {UserId} {Error}", userid, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.User.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||
{
|
||||
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
||||
if (value.Contains("]"))
|
||||
{
|
||||
value = value.Substring(value.IndexOf("]") + 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||
{
|
||||
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
||||
if (value.Contains("]"))
|
||||
{
|
||||
value = value.Substring(value.IndexOf("]") + 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private async Task SaveUser()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
||||
{
|
||||
if (_password == confirm)
|
||||
{
|
||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = username;
|
||||
user.Password = _password;
|
||||
user.Email = email;
|
||||
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
||||
user.PhotoFileId = null;
|
||||
user.PhotoFileId = filemanager.GetFileId();
|
||||
if (user.PhotoFileId == -1)
|
||||
{
|
||||
user.PhotoFileId = null;
|
||||
}
|
||||
private async Task SaveUser()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
||||
{
|
||||
if (_password == confirm)
|
||||
{
|
||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = username;
|
||||
user.Password = _password;
|
||||
user.Email = email;
|
||||
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
||||
user.PhotoFileId = null;
|
||||
user.PhotoFileId = filemanager.GetFileId();
|
||||
if (user.PhotoFileId == -1)
|
||||
{
|
||||
user.PhotoFileId = null;
|
||||
}
|
||||
|
||||
user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted));
|
||||
user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted));
|
||||
|
||||
user = await UserService.UpdateUserAsync(user);
|
||||
if (user != null)
|
||||
{
|
||||
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
|
||||
await logger.LogInformation("User Saved {User}", user);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error);
|
||||
}
|
||||
user = await UserService.UpdateUserAsync(user);
|
||||
if (user != null)
|
||||
{
|
||||
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
|
||||
await logger.LogInformation("User Saved {User}", user);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -293,9 +294,17 @@ else
|
|||
{
|
||||
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
|
||||
}
|
||||
if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
|
||||
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
{
|
||||
valid = false;
|
||||
if (valid == true && profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
|
||||
{
|
||||
valid = false;
|
||||
}
|
||||
if (valid == true && !string.IsNullOrEmpty(profile.Validation))
|
||||
{
|
||||
Regex regex = new Regex(profile.Validation);
|
||||
valid = regex.Match(SettingService.GetSetting(settings, profile.Name, string.Empty)).Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
|
|
|
@ -53,7 +53,6 @@
|
|||
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" },
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.bubble.css" },
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.snow.css" }
|
||||
};
|
||||
|
|
|
@ -15,11 +15,6 @@
|
|||
}
|
||||
|
||||
@code {
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
|
||||
};
|
||||
|
||||
private string content = "";
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using Oqtane.Documentation;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Modules.HtmlText
|
||||
{
|
||||
|
@ -13,7 +15,11 @@ namespace Oqtane.Modules.HtmlText
|
|||
Version = "1.0.1",
|
||||
ServerManagerType = "Oqtane.Modules.HtmlText.Manager.HtmlTextManager, Oqtane.Server",
|
||||
ReleaseVersions = "1.0.0,1.0.1",
|
||||
SettingsType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"
|
||||
SettingsType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client",
|
||||
Resources = new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Module.css" }
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -291,6 +291,12 @@ namespace Oqtane.Modules
|
|||
}
|
||||
}
|
||||
|
||||
public async Task ScrollToPageTop()
|
||||
{
|
||||
var interop = new Interop(JSRuntime);
|
||||
await interop.ScrollTo(0, 0, "smooth");
|
||||
}
|
||||
|
||||
// logging methods
|
||||
public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args)
|
||||
{
|
||||
|
|
|
@ -183,4 +183,10 @@
|
|||
<data name="Private.Text" xml:space="preserve">
|
||||
<value>Private? </value>
|
||||
</data>
|
||||
<data name="Validation.HelpText" xml:space="preserve">
|
||||
<value>Optionally provide a regular expression (RegExp) for validating the value entered</value>
|
||||
</data>
|
||||
<data name="Validation.Text" xml:space="preserve">
|
||||
<value>Validation:</value>
|
||||
</data>
|
||||
</root>
|
|
@ -369,4 +369,10 @@
|
|||
<data name="PageContent.Heading" xml:space="preserve">
|
||||
<value>Page Content</value>
|
||||
</data>
|
||||
<data name="SMTPEnabled.HelpText" xml:space="preserve">
|
||||
<value>Specify if SMTP is enabled for this site</value>
|
||||
</data>
|
||||
<data name="SMTPEnabled.Text" xml:space="preserve">
|
||||
<value>Enabled?</value>
|
||||
</data>
|
||||
</root>
|
|
@ -1,3 +1,4 @@
|
|||
@using System.Net
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@inherits ThemeControlBase
|
||||
@inject NavigationManager NavigationManager
|
||||
|
@ -501,7 +502,7 @@
|
|||
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.AdminDashboardModule);
|
||||
if (module != null)
|
||||
{
|
||||
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, module.ModuleId, "Index", ""));
|
||||
NavigationManager.NavigateTo(EditUrl("admin", module.ModuleId, "Index", "returnurl=" + WebUtility.UrlEncode(PageState.Route.PathAndQuery)));
|
||||
}
|
||||
break;
|
||||
case "Add":
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using Oqtane.Documentation;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Themes.OqtaneTheme
|
||||
{
|
||||
|
@ -11,7 +13,13 @@ namespace Oqtane.Themes.OqtaneTheme
|
|||
Name = "Oqtane Theme",
|
||||
Version = "1.0.0",
|
||||
ThemeSettingsType = "Oqtane.Themes.OqtaneTheme.ThemeSettings, Oqtane.Client",
|
||||
ContainerSettingsType = "Oqtane.Themes.OqtaneTheme.ContainerSettings, Oqtane.Client"
|
||||
ContainerSettingsType = "Oqtane.Themes.OqtaneTheme.ContainerSettings, Oqtane.Client",
|
||||
Resources = new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.2.0/cyborg/bootstrap.min.css", Integrity = "sha512-d6pZJl/sNcj0GFkp4kTjXtPE14deuUsOqFQtxkj0KyBJQl+4e0qsEyuIDcNqrYuGoauAW3sWyDCQp49mhF4Syw==", CrossOrigin = "anonymous" },
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Theme.css" },
|
||||
new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/js/bootstrap.bundle.min.js", Integrity = "sha512-9GacT4119eY3AcosfWtHMsT5JyZudrexyEVzTBWV3viP/YfB9e2pEy3N7WXL3SV6ASXpTU0vzzSxsbfsuUH4sQ==", CrossOrigin = "anonymous" }
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,14 +110,6 @@
|
|||
|
||||
public override string Panes => PaneNames.Default + ",Top Full Width,Top 100%,Left 50%,Right 50%,Left 33%,Center 33%,Right 33%,Left Outer 25%,Left Inner 25%,Right Inner 25%,Right Outer 25%,Left 25%,Center 50%,Right 25%,Left Sidebar 66%,Right Sidebar 33%,Left Sidebar 33%,Right Sidebar 66%,Bottom 100%,Bottom Full Width,Footer";
|
||||
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
// obtained from https://cdnjs.com/libraries
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.2.0/cyborg/bootstrap.min.css", Integrity = "sha512-d6pZJl/sNcj0GFkp4kTjXtPE14deuUsOqFQtxkj0KyBJQl+4e0qsEyuIDcNqrYuGoauAW3sWyDCQp49mhF4Syw==", CrossOrigin = "anonymous" },
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
|
||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/js/bootstrap.bundle.min.js", Integrity = "sha512-9GacT4119eY3AcosfWtHMsT5JyZudrexyEVzTBWV3viP/YfB9e2pEy3N7WXL3SV6ASXpTU0vzzSxsbfsuUH4sQ==", CrossOrigin = "anonymous" }
|
||||
};
|
||||
|
||||
private bool _login = true;
|
||||
private bool _register = true;
|
||||
private bool _footer = false;
|
||||
|
|
|
@ -531,10 +531,13 @@
|
|||
{
|
||||
foreach (var resource in resources)
|
||||
{
|
||||
if (!resource.Url.Contains("://") && resource.Url.StartsWith("~/"))
|
||||
if (resource.Url.StartsWith("~"))
|
||||
{
|
||||
// create local path
|
||||
resource.Url = resource.Url.Replace("~", alias.BaseUrl + "/" + type + "/" + name);
|
||||
resource.Url = resource.Url.Replace("~", "/" + type + "/" + name + "/").Replace("//", "/");
|
||||
}
|
||||
if (!resource.Url.Contains("://") && alias.BaseUrl != "" && !resource.Url.StartsWith(alias.BaseUrl))
|
||||
{
|
||||
resource.Url = alias.BaseUrl + resource.Url;
|
||||
}
|
||||
|
||||
// ensure resource does not exist already
|
||||
|
|
|
@ -96,6 +96,30 @@
|
|||
{
|
||||
await InjectScripts(PageState.Page.BodyContent, ResourceLocation.Body);
|
||||
}
|
||||
|
||||
if (PageState.Page.Resources != null && PageState.Page.Resources.Exists(item => item.ResourceType == ResourceType.Script))
|
||||
{
|
||||
var interop = new Interop(JSRuntime);
|
||||
var scripts = new List<object>();
|
||||
var inline = 0;
|
||||
foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level != ResourceLevel.Site))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(resource.Url))
|
||||
{
|
||||
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module, location = resource.Location.ToString().ToLower() });
|
||||
}
|
||||
else
|
||||
{
|
||||
inline += 1;
|
||||
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Content, resource.Location.ToString().ToLower());
|
||||
}
|
||||
}
|
||||
if (scripts.Any())
|
||||
{
|
||||
await interop.IncludeScripts(scripts.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,8 +33,10 @@ namespace Oqtane.Controllers
|
|||
private readonly IHttpContextAccessor _accessor;
|
||||
private readonly IAliasRepository _aliases;
|
||||
private readonly ILogger<InstallationController> _filelogger;
|
||||
private readonly ITenantManager _tenantManager;
|
||||
private readonly ServerStateManager _serverState;
|
||||
|
||||
public InstallationController(IConfigManager configManager, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache, IHttpContextAccessor accessor, IAliasRepository aliases, ILogger<InstallationController> filelogger)
|
||||
public InstallationController(IConfigManager configManager, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache, IHttpContextAccessor accessor, IAliasRepository aliases, ILogger<InstallationController> filelogger, ITenantManager tenantManager, ServerStateManager serverState)
|
||||
{
|
||||
_configManager = configManager;
|
||||
_installationManager = installationManager;
|
||||
|
@ -44,6 +46,8 @@ namespace Oqtane.Controllers
|
|||
_accessor = accessor;
|
||||
_aliases = aliases;
|
||||
_filelogger = filelogger;
|
||||
_tenantManager = tenantManager;
|
||||
_serverState = serverState;
|
||||
}
|
||||
|
||||
// POST api/<controller>
|
||||
|
@ -115,7 +119,9 @@ namespace Oqtane.Controllers
|
|||
|
||||
private List<ClientAssembly> GetAssemblyList()
|
||||
{
|
||||
return _cache.GetOrCreate("assemblieslist", entry =>
|
||||
int siteId = _tenantManager.GetAlias().SiteId;
|
||||
|
||||
return _cache.GetOrCreate($"assemblieslist:{siteId}", entry =>
|
||||
{
|
||||
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
var assemblyList = new List<ClientAssembly>();
|
||||
|
@ -126,78 +132,43 @@ namespace Oqtane.Controllers
|
|||
{
|
||||
hashfilename = false;
|
||||
}
|
||||
|
||||
// get list of assemblies which should be downloaded to client
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneClientAssemblies();
|
||||
var list = assemblies.Select(a => a.GetName().Name).ToList();
|
||||
|
||||
// populate assemblies
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
// get site assemblies which should be downloaded to client
|
||||
var assemblies = _serverState.GetServerState(siteId).Assemblies;
|
||||
|
||||
// populate assembly list
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
assemblyList.Add(new ClientAssembly(Path.Combine(binFolder, list[i] + ".dll"), hashfilename));
|
||||
if (assembly != Constants.ClientId)
|
||||
{
|
||||
var filepath = Path.Combine(binFolder, assembly) + ".dll";
|
||||
if (System.IO.File.Exists(filepath))
|
||||
{
|
||||
assemblyList.Add(new ClientAssembly(Path.Combine(binFolder, assembly + ".dll"), hashfilename));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// insert satellite assemblies at beginning of list
|
||||
foreach (var culture in _localizationManager.GetInstalledCultures())
|
||||
{
|
||||
var assembliesFolderPath = Path.Combine(binFolder, culture);
|
||||
if (culture == Constants.DefaultCulture)
|
||||
if (culture != Constants.DefaultCulture)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Directory.Exists(assembliesFolderPath))
|
||||
{
|
||||
foreach (var resourceFile in Directory.EnumerateFiles(assembliesFolderPath))
|
||||
var assembliesFolderPath = Path.Combine(binFolder, culture);
|
||||
if (Directory.Exists(assembliesFolderPath))
|
||||
{
|
||||
assemblyList.Insert(0, new ClientAssembly(resourceFile, hashfilename));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_filelogger.LogError(Utilities.LogMessage(this, $"The Satellite Assembly Folder For {culture} Does Not Exist"));
|
||||
}
|
||||
}
|
||||
|
||||
// insert module and theme dependencies at beginning of list
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModule))))
|
||||
{
|
||||
var instance = Activator.CreateInstance(type) as IModule;
|
||||
foreach (string name in instance.ModuleDefinition.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Reverse())
|
||||
{
|
||||
var filepath = Path.Combine(binFolder, name.ToLower().EndsWith(".dll") ? name : name + ".dll");
|
||||
if (System.IO.File.Exists(filepath))
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
if (!assemblyList.Exists(item => item.FilePath == filepath))
|
||||
var filepath = Path.Combine(assembliesFolderPath, assembly) + ".resources.dll";
|
||||
if (System.IO.File.Exists(filepath))
|
||||
{
|
||||
assemblyList.Insert(0, new ClientAssembly(filepath, hashfilename));
|
||||
assemblyList.Insert(0, new ClientAssembly(Path.Combine(assembliesFolderPath, assembly + ".resources.dll"), hashfilename));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_filelogger.LogError(Utilities.LogMessage(this, $"Module {instance.ModuleDefinition.ModuleDefinitionName} Dependency {name}.dll Does Not Exist"));
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(ITheme))))
|
||||
{
|
||||
var instance = Activator.CreateInstance(type) as ITheme;
|
||||
foreach (string name in instance.Theme.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Reverse())
|
||||
else
|
||||
{
|
||||
var filepath = Path.Combine(binFolder, name.ToLower().EndsWith(".dll") ? name : name + ".dll");
|
||||
if (System.IO.File.Exists(filepath))
|
||||
{
|
||||
if (!assemblyList.Exists(item => item.FilePath == filepath))
|
||||
{
|
||||
assemblyList.Insert(0, new ClientAssembly(filepath, hashfilename));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_filelogger.LogError(Utilities.LogMessage(this, $"Theme {instance.Theme.ThemeName} Dependency {name}.dll Does Not Exist"));
|
||||
}
|
||||
_filelogger.LogError(Utilities.LogMessage(this, $"The Satellite Assembly Folder For {culture} Does Not Exist"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -240,21 +211,24 @@ namespace Oqtane.Controllers
|
|||
{
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
if (System.IO.File.Exists(assembly.FilePath))
|
||||
if (Path.GetFileNameWithoutExtension(assembly.FilePath) != Constants.ClientId)
|
||||
{
|
||||
using (var filestream = new FileStream(assembly.FilePath, FileMode.Open, FileAccess.Read))
|
||||
using (var entrystream = archive.CreateEntry(assembly.HashedName).Open())
|
||||
if (System.IO.File.Exists(assembly.FilePath))
|
||||
{
|
||||
filestream.CopyTo(entrystream);
|
||||
using (var filestream = new FileStream(assembly.FilePath, FileMode.Open, FileAccess.Read))
|
||||
using (var entrystream = archive.CreateEntry(assembly.HashedName).Open())
|
||||
{
|
||||
filestream.CopyTo(entrystream);
|
||||
}
|
||||
}
|
||||
}
|
||||
var pdb = assembly.FilePath.Replace(".dll", ".pdb");
|
||||
if (System.IO.File.Exists(pdb))
|
||||
{
|
||||
using (var filestream = new FileStream(pdb, FileMode.Open, FileAccess.Read))
|
||||
using (var entrystream = archive.CreateEntry(assembly.HashedName.Replace(".dll", ".pdb")).Open())
|
||||
var pdb = assembly.FilePath.Replace(".dll", ".pdb");
|
||||
if (System.IO.File.Exists(pdb))
|
||||
{
|
||||
filestream.CopyTo(entrystream);
|
||||
using (var filestream = new FileStream(pdb, FileMode.Open, FileAccess.Read))
|
||||
using (var entrystream = archive.CreateEntry(assembly.HashedName.Replace(".dll", ".pdb")).Open())
|
||||
{
|
||||
filestream.CopyTo(entrystream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -200,6 +200,8 @@ namespace Oqtane.Controllers
|
|||
case EntityNames.Tenant:
|
||||
case EntityNames.ModuleDefinition:
|
||||
case EntityNames.Host:
|
||||
case EntityNames.Job:
|
||||
case EntityNames.Theme:
|
||||
if (permissionName == PermissionNames.Edit)
|
||||
{
|
||||
authorized = User.IsInRole(RoleNames.Host);
|
||||
|
@ -262,6 +264,8 @@ namespace Oqtane.Controllers
|
|||
case EntityNames.Tenant:
|
||||
case EntityNames.ModuleDefinition:
|
||||
case EntityNames.Host:
|
||||
case EntityNames.Job:
|
||||
case EntityNames.Theme:
|
||||
filter = !User.IsInRole(RoleNames.Host);
|
||||
break;
|
||||
case EntityNames.Site:
|
||||
|
|
|
@ -61,6 +61,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
services.AddSingleton<ILoggerProvider, FileLoggerProvider>();
|
||||
services.AddSingleton<AutoValidateAntiforgeryTokenFilter>();
|
||||
services.AddSingleton<IAuthorizationPolicyProvider, AuthorizationPolicyProvider>();
|
||||
services.AddSingleton<ServerStateManager>();
|
||||
return services;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,119 +42,127 @@ namespace Oqtane.Infrastructure
|
|||
// get site settings
|
||||
List<Setting> sitesettings = settingRepository.GetSettings(EntityNames.Site, site.SiteId).ToList();
|
||||
Dictionary<string, string> settings = GetSettings(sitesettings);
|
||||
if (settings.ContainsKey("SMTPHost") && settings["SMTPHost"] != "" &&
|
||||
settings.ContainsKey("SMTPPort") && settings["SMTPPort"] != "" &&
|
||||
settings.ContainsKey("SMTPSSL") && settings["SMTPSSL"] != "" &&
|
||||
settings.ContainsKey("SMTPSender") && settings["SMTPSender"] != "")
|
||||
if (!settings.ContainsKey("SMTPEnabled") || settings["SMTPEnabled"] == "True")
|
||||
{
|
||||
// construct SMTP Client
|
||||
var client = new SmtpClient()
|
||||
if (settings.ContainsKey("SMTPHost") && settings["SMTPHost"] != "" &&
|
||||
settings.ContainsKey("SMTPPort") && settings["SMTPPort"] != "" &&
|
||||
settings.ContainsKey("SMTPSSL") && settings["SMTPSSL"] != "" &&
|
||||
settings.ContainsKey("SMTPSender") && settings["SMTPSender"] != "")
|
||||
{
|
||||
DeliveryMethod = SmtpDeliveryMethod.Network,
|
||||
UseDefaultCredentials = false,
|
||||
Host = settings["SMTPHost"],
|
||||
Port = int.Parse(settings["SMTPPort"]),
|
||||
EnableSsl = bool.Parse(settings["SMTPSSL"])
|
||||
};
|
||||
if (settings["SMTPUsername"] != "" && settings["SMTPPassword"] != "")
|
||||
{
|
||||
client.Credentials = new NetworkCredential(settings["SMTPUsername"], settings["SMTPPassword"]);
|
||||
}
|
||||
|
||||
// iterate through undelivered notifications
|
||||
int sent = 0;
|
||||
List<Notification> notifications = notificationRepository.GetNotifications(site.SiteId, -1, -1).ToList();
|
||||
foreach (Notification notification in notifications)
|
||||
{
|
||||
// get sender and receiver information from user object if not provided
|
||||
if ((string.IsNullOrEmpty(notification.FromEmail) || string.IsNullOrEmpty(notification.FromDisplayName)) && notification.FromUserId != null)
|
||||
// construct SMTP Client
|
||||
var client = new SmtpClient()
|
||||
{
|
||||
var user = userRepository.GetUser(notification.FromUserId.Value);
|
||||
if (user != null)
|
||||
{
|
||||
notification.FromEmail = (string.IsNullOrEmpty(notification.FromEmail)) ? user.Email : notification.FromEmail;
|
||||
notification.FromDisplayName = (string.IsNullOrEmpty(notification.FromDisplayName)) ? user.DisplayName : notification.FromDisplayName;
|
||||
}
|
||||
}
|
||||
if ((string.IsNullOrEmpty(notification.ToEmail) || string.IsNullOrEmpty(notification.ToDisplayName)) && notification.ToUserId != null)
|
||||
DeliveryMethod = SmtpDeliveryMethod.Network,
|
||||
UseDefaultCredentials = false,
|
||||
Host = settings["SMTPHost"],
|
||||
Port = int.Parse(settings["SMTPPort"]),
|
||||
EnableSsl = bool.Parse(settings["SMTPSSL"])
|
||||
};
|
||||
if (settings["SMTPUsername"] != "" && settings["SMTPPassword"] != "")
|
||||
{
|
||||
var user = userRepository.GetUser(notification.ToUserId.Value);
|
||||
if (user != null)
|
||||
{
|
||||
notification.ToEmail = (string.IsNullOrEmpty(notification.ToEmail)) ? user.Email : notification.ToEmail;
|
||||
notification.ToDisplayName = (string.IsNullOrEmpty(notification.ToDisplayName)) ? user.DisplayName : notification.ToDisplayName;
|
||||
}
|
||||
client.Credentials = new NetworkCredential(settings["SMTPUsername"], settings["SMTPPassword"]);
|
||||
}
|
||||
|
||||
// validate recipient
|
||||
if (string.IsNullOrEmpty(notification.ToEmail))
|
||||
// iterate through undelivered notifications
|
||||
int sent = 0;
|
||||
List<Notification> notifications = notificationRepository.GetNotifications(site.SiteId, -1, -1).ToList();
|
||||
foreach (Notification notification in notifications)
|
||||
{
|
||||
log += "Recipient Missing For NotificationId: " + notification.NotificationId + "<br />";
|
||||
notification.IsDeleted = true;
|
||||
notificationRepository.UpdateNotification(notification);
|
||||
}
|
||||
else
|
||||
{
|
||||
MailMessage mailMessage = new MailMessage();
|
||||
|
||||
// sender
|
||||
if (settings.ContainsKey("SMTPRelay") && settings["SMTPRelay"] == "True" && !string.IsNullOrEmpty(notification.FromEmail))
|
||||
// get sender and receiver information from user object if not provided
|
||||
if ((string.IsNullOrEmpty(notification.FromEmail) || string.IsNullOrEmpty(notification.FromDisplayName)) && notification.FromUserId != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(notification.FromDisplayName))
|
||||
var user = userRepository.GetUser(notification.FromUserId.Value);
|
||||
if (user != null)
|
||||
{
|
||||
mailMessage.From = new MailAddress(notification.FromEmail, notification.FromDisplayName);
|
||||
notification.FromEmail = (string.IsNullOrEmpty(notification.FromEmail)) ? user.Email : notification.FromEmail;
|
||||
notification.FromDisplayName = (string.IsNullOrEmpty(notification.FromDisplayName)) ? user.DisplayName : notification.FromDisplayName;
|
||||
}
|
||||
}
|
||||
if ((string.IsNullOrEmpty(notification.ToEmail) || string.IsNullOrEmpty(notification.ToDisplayName)) && notification.ToUserId != null)
|
||||
{
|
||||
var user = userRepository.GetUser(notification.ToUserId.Value);
|
||||
if (user != null)
|
||||
{
|
||||
notification.ToEmail = (string.IsNullOrEmpty(notification.ToEmail)) ? user.Email : notification.ToEmail;
|
||||
notification.ToDisplayName = (string.IsNullOrEmpty(notification.ToDisplayName)) ? user.DisplayName : notification.ToDisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
// validate recipient
|
||||
if (string.IsNullOrEmpty(notification.ToEmail))
|
||||
{
|
||||
log += "Recipient Missing For NotificationId: " + notification.NotificationId + "<br />";
|
||||
notification.IsDeleted = true;
|
||||
notificationRepository.UpdateNotification(notification);
|
||||
}
|
||||
else
|
||||
{
|
||||
MailMessage mailMessage = new MailMessage();
|
||||
|
||||
// sender
|
||||
if (settings.ContainsKey("SMTPRelay") && settings["SMTPRelay"] == "True" && !string.IsNullOrEmpty(notification.FromEmail))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(notification.FromDisplayName))
|
||||
{
|
||||
mailMessage.From = new MailAddress(notification.FromEmail, notification.FromDisplayName);
|
||||
}
|
||||
else
|
||||
{
|
||||
mailMessage.From = new MailAddress(notification.FromEmail);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mailMessage.From = new MailAddress(notification.FromEmail);
|
||||
mailMessage.From = new MailAddress(settings["SMTPSender"], (!string.IsNullOrEmpty(notification.FromDisplayName)) ? notification.FromDisplayName : site.Name);
|
||||
}
|
||||
|
||||
// recipient
|
||||
if (!string.IsNullOrEmpty(notification.ToDisplayName))
|
||||
{
|
||||
mailMessage.To.Add(new MailAddress(notification.ToEmail, notification.ToDisplayName));
|
||||
}
|
||||
else
|
||||
{
|
||||
mailMessage.To.Add(new MailAddress(notification.ToEmail));
|
||||
}
|
||||
|
||||
// subject
|
||||
mailMessage.Subject = notification.Subject;
|
||||
|
||||
//body
|
||||
mailMessage.Body = notification.Body;
|
||||
|
||||
// encoding
|
||||
mailMessage.SubjectEncoding = System.Text.Encoding.UTF8;
|
||||
mailMessage.BodyEncoding = System.Text.Encoding.UTF8;
|
||||
mailMessage.IsBodyHtml = true;
|
||||
|
||||
// send mail
|
||||
try
|
||||
{
|
||||
client.Send(mailMessage);
|
||||
sent++;
|
||||
notification.IsDelivered = true;
|
||||
notification.DeliveredOn = DateTime.UtcNow;
|
||||
notificationRepository.UpdateNotification(notification);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// error
|
||||
log += ex.Message + "<br />";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mailMessage.From = new MailAddress(settings["SMTPSender"], (!string.IsNullOrEmpty(notification.FromDisplayName)) ? notification.FromDisplayName : site.Name);
|
||||
}
|
||||
|
||||
// recipient
|
||||
if (!string.IsNullOrEmpty(notification.ToDisplayName))
|
||||
{
|
||||
mailMessage.To.Add(new MailAddress(notification.ToEmail, notification.ToDisplayName));
|
||||
}
|
||||
else
|
||||
{
|
||||
mailMessage.To.Add(new MailAddress(notification.ToEmail));
|
||||
}
|
||||
|
||||
// subject
|
||||
mailMessage.Subject = notification.Subject;
|
||||
|
||||
//body
|
||||
mailMessage.Body = notification.Body;
|
||||
|
||||
// encoding
|
||||
mailMessage.SubjectEncoding = System.Text.Encoding.UTF8;
|
||||
mailMessage.BodyEncoding = System.Text.Encoding.UTF8;
|
||||
|
||||
// send mail
|
||||
try
|
||||
{
|
||||
client.Send(mailMessage);
|
||||
sent++;
|
||||
notification.IsDelivered = true;
|
||||
notification.DeliveredOn = DateTime.UtcNow;
|
||||
notificationRepository.UpdateNotification(notification);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// error
|
||||
log += ex.Message + "<br />";
|
||||
}
|
||||
}
|
||||
log += "Notifications Delivered: " + sent + "<br />";
|
||||
}
|
||||
else
|
||||
{
|
||||
log += "SMTP Not Configured Properly In Site Settings - Host, Port, SSL, And Sender Are All Required" + "<br />";
|
||||
}
|
||||
log += "Notifications Delivered: " + sent + "<br />";
|
||||
}
|
||||
else
|
||||
{
|
||||
log += "SMTP Not Configured Properly In Site Settings - Host, Port, SSL, And Sender Are All Required" + "<br />";
|
||||
log += "SMTP Disabled In Site Settings" + "<br />";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
12
Oqtane.Server/Infrastructure/ServerState.cs
Normal file
12
Oqtane.Server/Infrastructure/ServerState.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System.Collections.Generic;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Infrastructure
|
||||
{
|
||||
public class ServerState
|
||||
{
|
||||
public int SiteId { get; set; }
|
||||
public List<string> Assemblies { get; set; } = new List<string>();
|
||||
public List<Resource>Scripts { get; set; } = new List<Resource>();
|
||||
}
|
||||
}
|
49
Oqtane.Server/Infrastructure/ServerStateManager.cs
Normal file
49
Oqtane.Server/Infrastructure/ServerStateManager.cs
Normal file
|
@ -0,0 +1,49 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Infrastructure
|
||||
{
|
||||
// singleton
|
||||
public class ServerStateManager
|
||||
{
|
||||
private List<ServerState> _serverStates { get; set; }
|
||||
|
||||
public ServerStateManager()
|
||||
{
|
||||
_serverStates = new List<ServerState>();
|
||||
}
|
||||
|
||||
public ServerState GetServerState(int siteId)
|
||||
{
|
||||
var serverState = _serverStates.FirstOrDefault(item => item.SiteId == siteId);
|
||||
if (serverState == null)
|
||||
{
|
||||
serverState = new ServerState();
|
||||
serverState.SiteId = siteId;
|
||||
serverState.Assemblies = new List<string>();
|
||||
serverState.Scripts = new List<Resource>();
|
||||
return serverState;
|
||||
}
|
||||
else
|
||||
{
|
||||
return serverState;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetServerState(int siteId, ServerState serverState)
|
||||
{
|
||||
var serverstate = _serverStates.FirstOrDefault(item => item.SiteId == siteId);
|
||||
if (serverstate == null)
|
||||
{
|
||||
serverState.SiteId = siteId;
|
||||
_serverStates.Add(serverState);
|
||||
}
|
||||
else
|
||||
{
|
||||
serverstate.Assemblies = serverState.Assemblies;
|
||||
serverstate.Scripts = serverState.Scripts;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
28
Oqtane.Server/Migrations/Master/04000002_AddThemeName.cs
Normal file
28
Oqtane.Server/Migrations/Master/04000002_AddThemeName.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Oqtane.Databases.Interfaces;
|
||||
using Oqtane.Migrations.EntityBuilders;
|
||||
using Oqtane.Repository;
|
||||
|
||||
namespace Oqtane.Migrations.Master
|
||||
{
|
||||
[DbContext(typeof(MasterDBContext))]
|
||||
[Migration("Master.04.00.00.02")]
|
||||
public class AddThemeName : MultiDatabaseMigration
|
||||
{
|
||||
public AddThemeName(IDatabase database) : base(database)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
var themeEntityBuilder = new ThemeEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||
themeEntityBuilder.AddStringColumn("Name", 200, true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// not implemented
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Oqtane.Databases.Interfaces;
|
||||
using Oqtane.Migrations.EntityBuilders;
|
||||
using Oqtane.Repository;
|
||||
|
||||
namespace Oqtane.Migrations.Tenant
|
||||
{
|
||||
[DbContext(typeof(TenantDBContext))]
|
||||
[Migration("Tenant.04.00.00.03")]
|
||||
public class AddProfileValidation : MultiDatabaseMigration
|
||||
{
|
||||
public AddProfileValidation(IDatabase database) : base(database)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
var profileEntityBuilder = new ProfileEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||
profileEntityBuilder.AddStringColumn("Validation", 200, true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// not implemented
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,9 +36,10 @@ namespace Oqtane.Pages
|
|||
private readonly IVisitorRepository _visitors;
|
||||
private readonly IAliasRepository _aliases;
|
||||
private readonly ISettingRepository _settings;
|
||||
private readonly ServerStateManager _serverState;
|
||||
private readonly ILogManager _logger;
|
||||
|
||||
public HostModel(IConfigManager configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, IJwtManager jwtManager, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases, ISettingRepository settings, ILogManager logger)
|
||||
public HostModel(IConfigManager configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, IJwtManager jwtManager, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases, ISettingRepository settings, ServerStateManager serverState, ILogManager logger)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_tenantManager = tenantManager;
|
||||
|
@ -52,6 +53,7 @@ namespace Oqtane.Pages
|
|||
_visitors = visitors;
|
||||
_aliases = aliases;
|
||||
_settings = settings;
|
||||
_serverState = serverState;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
@ -117,33 +119,11 @@ namespace Oqtane.Pages
|
|||
{
|
||||
Runtime = site.Runtime;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(site.RenderMode))
|
||||
if (!string.IsNullOrEmpty(site.RenderMode))
|
||||
{
|
||||
RenderMode = site.RenderMode;
|
||||
}
|
||||
if (Runtime == "Server")
|
||||
{
|
||||
ReconnectScript = CreateReconnectScript();
|
||||
}
|
||||
if (site.PwaIsEnabled && site.PwaAppIconFileId != null && site.PwaSplashIconFileId != null)
|
||||
{
|
||||
PWAScript = CreatePWAScript(alias, site, route);
|
||||
}
|
||||
// site level scripts
|
||||
HeadResources += ParseScripts(site.HeadContent);
|
||||
BodyResources += ParseScripts(site.BodyContent);
|
||||
|
||||
// get jwt token for downstream APIs
|
||||
if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
var sitesettings = HttpContext.GetSiteSettings();
|
||||
var secret = sitesettings.GetValue("JwtOptions:Secret", "");
|
||||
if (!string.IsNullOrEmpty(secret))
|
||||
{
|
||||
AuthorizationToken = _jwtManager.GenerateToken(alias, (ClaimsIdentity)User.Identity, secret, sitesettings.GetValue("JwtOptions:Issuer", ""), sitesettings.GetValue("JwtOptions:Audience", ""), int.Parse(sitesettings.GetValue("JwtOptions:Lifetime", "20")));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (site.VisitorTracking)
|
||||
{
|
||||
TrackVisitor(site.SiteId);
|
||||
|
@ -172,11 +152,33 @@ namespace Oqtane.Pages
|
|||
}
|
||||
}
|
||||
|
||||
// include global resources
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||
foreach (Assembly assembly in assemblies)
|
||||
// get jwt token for downstream APIs
|
||||
if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
ProcessHostResources(assembly, alias);
|
||||
var sitesettings = HttpContext.GetSiteSettings();
|
||||
var secret = sitesettings.GetValue("JwtOptions:Secret", "");
|
||||
if (!string.IsNullOrEmpty(secret))
|
||||
{
|
||||
AuthorizationToken = _jwtManager.GenerateToken(alias, (ClaimsIdentity)User.Identity, secret, sitesettings.GetValue("JwtOptions:Issuer", ""), sitesettings.GetValue("JwtOptions:Audience", ""), int.Parse(sitesettings.GetValue("JwtOptions:Lifetime", "20")));
|
||||
}
|
||||
}
|
||||
|
||||
// inject scripts
|
||||
if (Runtime == "Server")
|
||||
{
|
||||
ReconnectScript = CreateReconnectScript();
|
||||
}
|
||||
if (site.PwaIsEnabled && site.PwaAppIconFileId != null && site.PwaSplashIconFileId != null)
|
||||
{
|
||||
PWAScript = CreatePWAScript(alias, site, route);
|
||||
}
|
||||
HeadResources += ParseScripts(site.HeadContent);
|
||||
BodyResources += ParseScripts(site.BodyContent);
|
||||
_sites.InitializeSite(site.SiteId); // populates server state
|
||||
var scripts = _serverState.GetServerState(site.SiteId).Scripts;
|
||||
foreach (var script in scripts)
|
||||
{
|
||||
AddScript(script, alias);
|
||||
}
|
||||
|
||||
// set culture if not specified
|
||||
|
@ -409,20 +411,6 @@ namespace Oqtane.Pages
|
|||
"</script>";
|
||||
}
|
||||
|
||||
private void ProcessHostResources(Assembly assembly, Alias alias)
|
||||
{
|
||||
var types = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IHostResources)));
|
||||
foreach (var type in types)
|
||||
{
|
||||
var obj = Activator.CreateInstance(type) as IHostResources;
|
||||
foreach (var resource in obj.Resources)
|
||||
{
|
||||
resource.Level = ResourceLevel.App;
|
||||
ProcessResource(resource, 0, alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string ParseScripts(string headcontent)
|
||||
{
|
||||
// iterate scripts
|
||||
|
@ -439,60 +427,39 @@ namespace Oqtane.Pages
|
|||
return scripts;
|
||||
}
|
||||
|
||||
private void ProcessResource(Resource resource, int count, Alias alias)
|
||||
private void AddScript(Resource resource, Alias alias)
|
||||
{
|
||||
var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url;
|
||||
switch (resource.ResourceType)
|
||||
var script = CreateScript(resource, alias);
|
||||
if (resource.Location == Shared.ResourceLocation.Head)
|
||||
{
|
||||
case ResourceType.Stylesheet:
|
||||
if (!HeadResources.Contains(url, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string id = "";
|
||||
if (resource.Level == ResourceLevel.Page)
|
||||
{
|
||||
id = "id=\"app-stylesheet-" + resource.Level.ToString().ToLower() + "-" + DateTime.UtcNow.ToString("yyyyMMddHHmmssfff") + "-" + count.ToString("00") + "\" ";
|
||||
}
|
||||
HeadResources += "<link " + id + "rel=\"stylesheet\" href=\"" + url + "\"" + CrossOrigin(resource.CrossOrigin) + Integrity(resource.Integrity) + " type=\"text/css\"/>" + Environment.NewLine;
|
||||
}
|
||||
break;
|
||||
case ResourceType.Script:
|
||||
if (resource.Location == Shared.ResourceLocation.Body)
|
||||
{
|
||||
if (!BodyResources.Contains(url, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
BodyResources += "<script src=\"" + url + "\"" + CrossOrigin(resource.CrossOrigin) + Integrity(resource.Integrity) + "></script>" + Environment.NewLine;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!HeadResources.Contains(resource.Url, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
HeadResources += "<script src=\"" + url + "\"" + CrossOrigin(resource.CrossOrigin) + Integrity(resource.Integrity) + "></script>" + Environment.NewLine;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
private string CrossOrigin(string crossorigin)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(crossorigin))
|
||||
{
|
||||
return " crossorigin=\"" + crossorigin + "\"";
|
||||
if (!HeadResources.Contains(script))
|
||||
{
|
||||
HeadResources += script + Environment.NewLine;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
if (!BodyResources.Contains(script))
|
||||
{
|
||||
BodyResources += script + Environment.NewLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
private string Integrity(string integrity)
|
||||
|
||||
private string CreateScript(Resource resource, Alias alias)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(integrity))
|
||||
if (!string.IsNullOrEmpty(resource.Url))
|
||||
{
|
||||
return " integrity=\"" + integrity + "\"";
|
||||
var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url;
|
||||
return "<script src=\"" + url + "\"" +
|
||||
((!string.IsNullOrEmpty(resource.CrossOrigin)) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") +
|
||||
((!string.IsNullOrEmpty(resource.Integrity)) ? " integrity=\"" + resource.Integrity + "\"" : "") +
|
||||
"></script>";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
// inline script
|
||||
return "<script>" + resource.Content + "</script>";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace Oqtane.Repository
|
|||
Site GetSite(int siteId);
|
||||
Site GetSite(int siteId, bool tracking);
|
||||
void DeleteSite(int siteId);
|
||||
void InitializeSite(int siteId);
|
||||
void CreatePages(Site site, List<PageTemplate> pageTemplates);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,14 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Xml;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.Themes;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
{
|
||||
|
@ -20,15 +22,17 @@ namespace Oqtane.Repository
|
|||
private readonly IPermissionRepository _permissions;
|
||||
private readonly ITenantManager _tenants;
|
||||
private readonly ISettingRepository _settings;
|
||||
private readonly ServerStateManager _serverState;
|
||||
private readonly string settingprefix = "SiteEnabled:";
|
||||
|
||||
public ModuleDefinitionRepository(MasterDBContext context, IMemoryCache cache, IPermissionRepository permissions, ITenantManager tenants, ISettingRepository settings)
|
||||
public ModuleDefinitionRepository(MasterDBContext context, IMemoryCache cache, IPermissionRepository permissions, ITenantManager tenants, ISettingRepository settings, ServerStateManager serverState)
|
||||
{
|
||||
_db = context;
|
||||
_cache = cache;
|
||||
_permissions = permissions;
|
||||
_tenants = tenants;
|
||||
_settings = settings;
|
||||
_serverState = serverState;
|
||||
}
|
||||
|
||||
public IEnumerable<ModuleDefinition> GetModuleDefinitions()
|
||||
|
@ -182,6 +186,7 @@ namespace Oqtane.Repository
|
|||
var settings = _settings.GetSettings(EntityNames.ModuleDefinition).ToList();
|
||||
|
||||
// populate module definition site settings and permissions
|
||||
var serverState = _serverState.GetServerState(siteId);
|
||||
foreach (ModuleDefinition moduledefinition in ModuleDefinitions)
|
||||
{
|
||||
moduledefinition.SiteId = siteId;
|
||||
|
@ -196,6 +201,36 @@ namespace Oqtane.Repository
|
|||
moduledefinition.IsEnabled = moduledefinition.IsAutoEnabled;
|
||||
}
|
||||
|
||||
if (moduledefinition.IsEnabled)
|
||||
{
|
||||
// build list of assemblies for site
|
||||
if (!serverState.Assemblies.Contains(moduledefinition.AssemblyName))
|
||||
{
|
||||
serverState.Assemblies.Add(moduledefinition.AssemblyName);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(moduledefinition.Dependencies))
|
||||
{
|
||||
foreach (var assembly in moduledefinition.Dependencies.Replace(".dll", "").Split(',', StringSplitOptions.RemoveEmptyEntries).Reverse())
|
||||
{
|
||||
if (!serverState.Assemblies.Contains(assembly))
|
||||
{
|
||||
serverState.Assemblies.Insert(0, assembly);
|
||||
}
|
||||
}
|
||||
}
|
||||
// build list of scripts for site
|
||||
if (moduledefinition.Resources != null)
|
||||
{
|
||||
foreach (var resource in moduledefinition.Resources.Where(item => item.Level == ResourceLevel.Site))
|
||||
{
|
||||
if (!serverState.Scripts.Contains(resource))
|
||||
{
|
||||
serverState.Scripts.Add(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (permissions.Count == 0)
|
||||
{
|
||||
// no module definition permissions exist for this site
|
||||
|
@ -216,6 +251,7 @@ namespace Oqtane.Repository
|
|||
}
|
||||
}
|
||||
}
|
||||
_serverState.SetServerState(siteId, serverState);
|
||||
|
||||
// clean up any orphaned permissions
|
||||
var ids = new HashSet<int>(ModuleDefinitions.Select(item => item.ModuleDefinitionId));
|
||||
|
@ -295,6 +331,16 @@ namespace Oqtane.Repository
|
|||
moduledefinition.ModuleDefinitionName = qualifiedModuleType;
|
||||
moduledefinition.ControlTypeTemplate = modulecontroltype.Namespace + "." + Constants.ActionToken + ", " + modulecontroltype.Assembly.GetName().Name;
|
||||
moduledefinition.AssemblyName = assembly.GetName().Name;
|
||||
if (moduledefinition.Resources != null)
|
||||
{
|
||||
foreach (var resource in moduledefinition.Resources)
|
||||
{
|
||||
if (resource.Url.StartsWith("~"))
|
||||
{
|
||||
resource.Url = resource.Url.Replace("~", "/Modules/" + Utilities.GetTypeName(moduledefinition.ModuleDefinitionName) + "/").Replace("//", "/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
moduledefinition.IsPortable = false;
|
||||
if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType))
|
||||
|
@ -313,10 +359,21 @@ namespace Oqtane.Repository
|
|||
|
||||
if (moduledefinition.Categories == "Admin")
|
||||
{
|
||||
moduledefinition.PermissionList = new List<Permission>
|
||||
var shortName = moduledefinition.ModuleDefinitionName.Replace("Oqtane.Modules.Admin.", "").Replace(", Oqtane.Client", "");
|
||||
if (Constants.DefaultHostModuleTypes.Contains(shortName))
|
||||
{
|
||||
new Permission(PermissionNames.Utilize, RoleNames.Admin, true)
|
||||
};
|
||||
moduledefinition.PermissionList = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.Utilize, RoleNames.Host, true)
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
moduledefinition.PermissionList = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.Utilize, RoleNames.Admin, true)
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -24,11 +24,12 @@ namespace Oqtane.Repository
|
|||
private readonly IModuleRepository _moduleRepository;
|
||||
private readonly IPageModuleRepository _pageModuleRepository;
|
||||
private readonly IModuleDefinitionRepository _moduleDefinitionRepository;
|
||||
private readonly IThemeRepository _themeRepository;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IConfigurationRoot _config;
|
||||
|
||||
public SiteRepository(TenantDBContext context, IRoleRepository roleRepository, IProfileRepository profileRepository, IFolderRepository folderRepository, IPageRepository pageRepository,
|
||||
IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IServiceProvider serviceProvider,
|
||||
IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IThemeRepository themeRepository, IServiceProvider serviceProvider,
|
||||
IConfigurationRoot config)
|
||||
{
|
||||
_db = context;
|
||||
|
@ -39,6 +40,7 @@ namespace Oqtane.Repository
|
|||
_moduleRepository = moduleRepository;
|
||||
_pageModuleRepository = pageModuleRepository;
|
||||
_moduleDefinitionRepository = moduleDefinitionRepository;
|
||||
_themeRepository = themeRepository;
|
||||
_serviceProvider = serviceProvider;
|
||||
_config = config;
|
||||
}
|
||||
|
@ -88,6 +90,12 @@ namespace Oqtane.Repository
|
|||
_db.SaveChanges();
|
||||
}
|
||||
|
||||
public void InitializeSite(int siteId)
|
||||
{
|
||||
_moduleDefinitionRepository.GetModuleDefinitions(siteId);
|
||||
_themeRepository.GetThemes();
|
||||
}
|
||||
|
||||
private void CreateSite(Site site)
|
||||
{
|
||||
// create default entities for site
|
||||
|
|
|
@ -12,6 +12,7 @@ using Oqtane.Models;
|
|||
using Oqtane.Shared;
|
||||
using Oqtane.Themes;
|
||||
using System.Reflection.Metadata;
|
||||
using Oqtane.Migrations.Master;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
{
|
||||
|
@ -21,14 +22,16 @@ namespace Oqtane.Repository
|
|||
private readonly IMemoryCache _cache;
|
||||
private readonly ITenantManager _tenants;
|
||||
private readonly ISettingRepository _settings;
|
||||
private readonly ServerStateManager _serverState;
|
||||
private readonly string settingprefix = "SiteEnabled:";
|
||||
|
||||
public ThemeRepository(MasterDBContext context, IMemoryCache cache, ITenantManager tenants, ISettingRepository settings)
|
||||
public ThemeRepository(MasterDBContext context, IMemoryCache cache, ITenantManager tenants, ISettingRepository settings, ServerStateManager serverState)
|
||||
{
|
||||
_db = context;
|
||||
_cache = cache;
|
||||
_tenants = tenants;
|
||||
_settings = settings;
|
||||
_serverState = serverState;
|
||||
}
|
||||
|
||||
public IEnumerable<Theme> GetThemes()
|
||||
|
@ -123,6 +126,12 @@ namespace Oqtane.Repository
|
|||
}
|
||||
else
|
||||
{
|
||||
// override user customizable property values
|
||||
Theme.Name = (!string.IsNullOrEmpty(theme.Name)) ? theme.Name : Theme.Name;
|
||||
foreach (var themecontrol in Theme.Themes)
|
||||
{
|
||||
themecontrol.Name = Theme.Name + " - " + themecontrol.Name;
|
||||
}
|
||||
// remove theme from list as it is already synced
|
||||
themes.Remove(theme);
|
||||
}
|
||||
|
@ -147,6 +156,7 @@ namespace Oqtane.Repository
|
|||
var settings = _settings.GetSettings(EntityNames.Theme).ToList();
|
||||
|
||||
// populate theme site settings
|
||||
var serverState = _serverState.GetServerState(siteId);
|
||||
foreach (Theme theme in Themes)
|
||||
{
|
||||
theme.SiteId = siteId;
|
||||
|
@ -160,7 +170,38 @@ namespace Oqtane.Repository
|
|||
{
|
||||
theme.IsEnabled = theme.IsAutoEnabled;
|
||||
}
|
||||
|
||||
if (theme.IsEnabled)
|
||||
{
|
||||
// build list of assemblies for site
|
||||
if (!serverState.Assemblies.Contains(theme.AssemblyName))
|
||||
{
|
||||
serverState.Assemblies.Add(theme.AssemblyName);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(theme.Dependencies))
|
||||
{
|
||||
foreach (var assembly in theme.Dependencies.Replace(".dll", "").Split(',', StringSplitOptions.RemoveEmptyEntries).Reverse())
|
||||
{
|
||||
if (!serverState.Assemblies.Contains(assembly))
|
||||
{
|
||||
serverState.Assemblies.Insert(0, assembly);
|
||||
}
|
||||
}
|
||||
}
|
||||
// build list of scripts for site
|
||||
if (theme.Resources != null)
|
||||
{
|
||||
foreach (var resource in theme.Resources.Where(item => item.Level == ResourceLevel.Site))
|
||||
{
|
||||
if (!serverState.Scripts.Contains(resource))
|
||||
{
|
||||
serverState.Scripts.Add(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_serverState.SetServerState(siteId, serverState);
|
||||
}
|
||||
|
||||
return Themes;
|
||||
|
@ -225,12 +266,22 @@ namespace Oqtane.Repository
|
|||
Version = new Version(1, 0, 0).ToString()
|
||||
};
|
||||
}
|
||||
|
||||
// set internal properties
|
||||
theme.ThemeName = qualifiedThemeType;
|
||||
theme.Themes = new List<ThemeControl>();
|
||||
theme.Containers = new List<ThemeControl>();
|
||||
theme.AssemblyName = assembly.FullName.Split(",")[0];
|
||||
|
||||
if (theme.Resources != null)
|
||||
{
|
||||
foreach (var resource in theme.Resources)
|
||||
{
|
||||
if (resource.Url.StartsWith("~"))
|
||||
{
|
||||
resource.Url = resource.Url.Replace("~", "/Themes/" + Utilities.GetTypeName(theme.ThemeName) + "/").Replace("//", "/");
|
||||
}
|
||||
}
|
||||
}
|
||||
Debug.WriteLine($"Oqtane Info: Registering Theme {theme.ThemeName}");
|
||||
themes.Add(theme);
|
||||
index = themes.FindIndex(item => item.ThemeName == qualifiedThemeType);
|
||||
|
@ -242,7 +293,7 @@ namespace Oqtane.Repository
|
|||
new ThemeControl
|
||||
{
|
||||
TypeName = themeControlType.FullName + ", " + themeControlType.Assembly.GetName().Name,
|
||||
Name = theme.Name + " - " + ((string.IsNullOrEmpty(themecontrolobject.Name)) ? Utilities.GetTypeNameLastSegment(themeControlType.FullName, 0) : themecontrolobject.Name),
|
||||
Name = ((string.IsNullOrEmpty(themecontrolobject.Name)) ? Utilities.GetTypeNameLastSegment(themeControlType.FullName, 0) : themecontrolobject.Name),
|
||||
Thumbnail = themecontrolobject.Thumbnail,
|
||||
Panes = themecontrolobject.Panes
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ namespace Oqtane.Shared
|
|||
public enum ResourceLevel
|
||||
{
|
||||
App,
|
||||
Site,
|
||||
Page,
|
||||
Module
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace Oqtane.Models
|
|||
public string ModuleDefinitionName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nice name to show in admin / edit dialogs.
|
||||
/// Friendly name to show in UI
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
|
|
|
@ -68,5 +68,10 @@ namespace Oqtane.Models
|
|||
/// This gives possible values for dropdown input fields.
|
||||
/// </summary>
|
||||
public string Options { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional RegExp validation expression
|
||||
/// </summary>
|
||||
public string Validation { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,10 +35,12 @@ namespace Oqtane.Models
|
|||
/// </summary>
|
||||
public string ThemeName { get; set; }
|
||||
|
||||
// additional ITheme properties
|
||||
[NotMapped]
|
||||
/// <summary>
|
||||
/// Friendly name to show in UI
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
// additional ITheme properties
|
||||
[NotMapped]
|
||||
public string Version { get; set; }
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
using System;
|
||||
using Oqtane.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Oqtane.Shared
|
||||
{
|
||||
|
@ -39,6 +42,8 @@ namespace Oqtane.Shared
|
|||
|
||||
public const string DefaultSiteTemplate = "Oqtane.SiteTemplates.DefaultSiteTemplate, Oqtane.Server";
|
||||
|
||||
public static readonly string[] DefaultHostModuleTypes = new[] { "Upgrade", "Themes", "SystemInfo", "Sql", "Sites", "ModuleDefinitions", "Logs", "Jobs", "ModuleCreator" };
|
||||
|
||||
public const string FileUrl = "/files/";
|
||||
public const string ImageUrl = "/api/file/image/";
|
||||
public const int UserFolderCapacity = 20; // megabytes
|
||||
|
|
|
@ -48,7 +48,6 @@ This project is open source, and therefore is a work in progress...
|
|||
Backlog (TBD)
|
||||
- [ ] Azure Autoscale support (ie. web farm)
|
||||
- [ ] Routable Modules (ie. declarative configuration)
|
||||
- [ ] Client Assembly Loading / Site
|
||||
- [ ] Folder Providers
|
||||
- [ ] Generative AI Integration
|
||||
|
||||
|
@ -56,7 +55,9 @@ Backlog (TBD)
|
|||
- [ ] Migration to .NET 8
|
||||
|
||||
4.0.0 (Q2 2023)
|
||||
- [ ] Migration to .NET 7
|
||||
- [x] Migration to .NET 7
|
||||
- [x] Improved JavaScript, CSS, and Meta support
|
||||
- [x] Optimized Client Assembly Loading
|
||||
|
||||
[3.4.3](https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.3) ( May 3, 2023 )
|
||||
- [x] Stabilization improvements
|
||||
|
|
Loading…
Reference in New Issue
Block a user