Compare commits

..

5 Commits
v6.1.2 ... lel

85 changed files with 505 additions and 815 deletions

2
.deployment Normal file
View File

@ -0,0 +1,2 @@
[config]
project = Oqtane.Server/Oqtane.Server.csproj

View File

@ -0,0 +1,25 @@
name: build-docker-imge
on:
- push
jobs:
build:
name: Build the docker container
runs-on: ubuntu-latest
steps:
- name: "Git clone"
run: git clone ${{ gitea.server_url }}/${{ gitea.repository }}.git .
- name: "Git checkout"
run: git checkout "${{ gitea.sha }}"
- uses: aevea/action-kaniko@master
name: Run Kaniko to build our api docker container.
with:
image: kocoded/oqtane.framework
tag: ${{ git.workflow_sha }}
tag_with_latest: github.ref == 'refs/heads/master'
registry: git.kocoder.xyz
username: ${{ secrets.CI_RUNNER_USER }}
password: ${{ secrets.CI_RUNNER_TOKEN }}
build_file: Dockerfile
target: deploy

24
Dockerfile Normal file
View File

@ -0,0 +1,24 @@
# Build
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /source
COPY --link . .
RUN dotnet restore /source/Oqtane.sln
RUN dotnet build "/source/Oqtane.sln" -c Release -o /source/build/
# Publish
FROM build AS publish
RUN dotnet publish "Oqtane.Server/Oqtane.Server.csproj" -c Release -o /source/publish/
# Deploy
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS deploy
WORKDIR /app
COPY --from=publish /source/publish/ /app/
ENTRYPOINT ["dotnet", "Oqtane.Server.dll"]

View File

@ -1,119 +0,0 @@
@namespace Oqtane.Installer.Controls
@implements Oqtane.Interfaces.IDatabaseConfigControl
@inject IStringLocalizer<SqlServerConfig> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="server" HelpText="Enter the database server name. This might include a port number as well if you are using a cloud service." ResourceKey="Server">Server:</Label>
<div class="col-sm-9">
<input id="server" type="text" class="form-control" @bind="@_server" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="database" HelpText="Enter the name of the database" ResourceKey="Database">Database:</Label>
<div class="col-sm-9">
<input id="database" type="text" class="form-control" @bind="@_database" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="security" HelpText="Select your security method" ResourceKey="Security">Security:</Label>
<div class="col-sm-9">
<select id="security" class="form-select custom-select" @bind="@_security">
<option value="integrated" selected>@Localizer["Integrated"]</option>
<option value="custom">@Localizer["Custom"]</option>
</select>
</div>
</div>
@if (_security == "custom")
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="uid" HelpText="Enter the username to use for the database" ResourceKey="Uid">User Id:</Label>
<div class="col-sm-9">
<input id="uid" type="text" class="form-control" @bind="@_uid" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
<div class="col-sm-9">
<div class="input-group">
<input id="pwd" type="@_passwordType" class="form-control" @bind="@_pwd" autocomplete="new-password" />
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglePassword</button>
</div>
</div>
</div>
}
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="encryption" HelpText="Specify if you are using an encrypted database connection. It is highly recommended to use encryption in a production environment." ResourceKey="Encryption">Encryption:</Label>
<div class="col-sm-9">
<select id="encryption" class="form-select custom-select" @bind="@_encryption">
<option value="true">@SharedLocalizer["True"]</option>
<option value="false">@SharedLocalizer["False"]</option>
</select>
</div>
</div>
@if (_encryption == "true")
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="trustservercertificate" HelpText="Specify the type of certificate you are using for encryption. Verifiable is equivalent to False. Self Signed is equivalent to True." ResourceKey="TrustServerCertificate">Certificate:</Label>
<div class="col-sm-9">
<select id="encryption" class="form-select custom-select" @bind="@_trustservercertificate">
<option value="true">@Localizer["Self Signed"]</option>
<option value="false">@Localizer["Verifiable"]</option>
</select>
</div>
</div>
}
@code {
private string _server = "tcp:{SQL Server Name}.database.windows.net,1433";
private string _database = "{SQL Database Name}";
private string _security = "custom";
private string _uid = "{SQL Administrator Login}";
private string _pwd = String.Empty;
private string _passwordType = "password";
private string _togglePassword = string.Empty;
private string _encryption = "true";
private string _trustservercertificate = "false";
protected override void OnInitialized()
{
_togglePassword = SharedLocalizer["ShowPassword"];
}
public string GetConnectionString()
{
var connectionString = String.Empty;
if (!String.IsNullOrEmpty(_server) && !String.IsNullOrEmpty(_database))
{
connectionString = $"Data Source={_server};Initial Catalog={_database};";
}
if (_security == "integrated")
{
connectionString += "Integrated Security=SSPI;";
}
else
{
connectionString += $"User ID={_uid};Password={_pwd};";
}
connectionString += $"Encrypt={_encryption};";
connectionString += $"TrustServerCertificate={_trustservercertificate};";
return connectionString;
}
private void TogglePassword()
{
if (_passwordType == "password")
{
_passwordType = "text";
_togglePassword = SharedLocalizer["HidePassword"];
}
else
{
_passwordType = "password";
_togglePassword = SharedLocalizer["ShowPassword"];
}
}
}

View File

@ -4,7 +4,7 @@
@inject IStringLocalizer<SharedResources> SharedLocalizer
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="server" HelpText="Enter the database server name. This might include a port number as well if you are using a cloud service." ResourceKey="Server">Server:</Label>
<Label Class="col-sm-3" For="server" HelpText="Enter the database server name. This might include a port number as well if you are using a cloud service (ie. servername.database.windows.net,1433) " ResourceKey="Server">Server:</Label>
<div class="col-sm-9">
<input id="server" type="text" class="form-control" @bind="@_server" />
</div>

View File

@ -15,7 +15,7 @@
<div class="row">
<div class="mx-auto text-center">
<img src="oqtane-black.png" />
<div style="font-weight: bold">@SharedLocalizer["Version"] @Constants.Version (.NET @Environment.Version.Major)</div>
<div style="font-weight: bold">@SharedLocalizer["Version"] @Constants.Version (.NET 9)</div>
</div>
</div>
<hr class="app-rule" />

View File

@ -45,7 +45,7 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="retention" HelpText="Number of log entries to retain for this job" ResourceKey="RetentionLog">Retention Log (Items): </Label>
<div class="col-sm-9">
<input id="retention" type="number" min="0" step ="1" class="form-control" @bind="@_retentionHistory" maxlength="3" required />
<input id="retention" type="number" min="0" step ="1" class="form-control" @bind="@_retentionHistory" maxlength="4" required />
</div>
</div>
<div class="row mb-1 align-items-center">

View File

@ -44,12 +44,14 @@ else
private async Task GetJobLogs()
{
var jobId = -1;
_jobLogs = await JobLogService.GetJobLogsAsync();
if (PageState.QueryString.ContainsKey("id"))
{
jobId = int.Parse(PageState.QueryString["id"]);
_jobLogs = _jobLogs.Where(item => item.JobId == Int32.Parse(PageState.QueryString["id"])).ToList();
}
_jobLogs = await JobLogService.GetJobLogsAsync(jobId);
_jobLogs = _jobLogs.OrderByDescending(item => item.JobLogId).ToList();
}
private string DisplayStatus(bool isExecuting, bool? succeeded)

View File

@ -73,7 +73,7 @@
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="url" HelpText="The url of the module" ResourceKey="Url">Url: </Label>
<Label Class="col-sm-3" For="url" HelpText="The reference url of the module" ResourceKey="ReferenceUrl">Reference Url: </Label>
<div class="col-sm-9">
<input id="url" class="form-control" @bind="@_url" disabled />
</div>

View File

@ -13,32 +13,32 @@
}
else
{
<div class="container">
<div class="row mb-3 align-items-center">
<div class="col-sm-6">
<ActionLink Action="Add" Text="Install Module" ResourceKey="InstallModule" />
<ActionLink Action="Create" Text="Create Module" ResourceKey="CreateModule" Class="btn btn-secondary ps-2" />
<button type="button" class="btn btn-secondary pw-2" @onclick="@Synchronize">@Localizer["Synchronize"]</button>
</div>
<div class="col-sm-6">
<select class="form-select" @onchange="(e => CategoryChanged(e))">
@foreach (var category in _categories)
{
if (category == _category)
{
<option value="@category" selected>@category @Localizer["Modules"]</option>
}
else
{
<option value="@category">@category @Localizer["Modules"]</option>
}
}
</select>
</div>
</div>
</div>
<Pager Items="@_moduleDefinitions">
<div class="container">
<div class="row mb-3 align-items-center">
<div class="col-sm-6">
<ActionLink Action="Add" Text="Install Module" ResourceKey="InstallModule" />
@((MarkupString)"&nbsp;")
<ActionLink Action="Create" Text="Create Module" ResourceKey="CreateModule" Class="btn btn-secondary" />
</div>
<div class="col-sm-6">
<select class="form-select" @onchange="(e => CategoryChanged(e))">
@foreach (var category in _categories)
{
if (category == _category)
{
<option value="@category" selected>@category @Localizer["Modules"]</option>
}
else
{
<option value="@category">@category @Localizer["Modules"]</option>
}
}
</select>
</div>
</div>
</div>
<Pager Items="@_moduleDefinitions">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
@ -61,17 +61,17 @@ else
<td>@context.Name</td>
<td>@context.Version</td>
<td>
@if (context.IsEnabled)
{
<span>@SharedLocalizer["Yes"]</span>
}
else
{
<span>@SharedLocalizer["No"]</span>
}
@if (context.IsEnabled)
{
<span>@SharedLocalizer["Yes"]</span>
}
else
{
<span>@SharedLocalizer["No"]</span>
}
</td>
<td>
@if (context.AssemblyName == Constants.ClientId || _modules.Where(m => m.ModuleDefinition?.ModuleDefinitionId == context.ModuleDefinitionId).FirstOrDefault() != null)
@if (context.AssemblyName == Constants.ClientId || _modules.Where(m => m.ModuleDefinition?.ModuleDefinitionId == context.ModuleDefinitionId).FirstOrDefault() != null)
{
<span>@SharedLocalizer["Yes"]</span>
}
@ -87,9 +87,9 @@ else
@((MarkupString)PurchaseLink(context.PackageName))
</td>
<td>
@{
var version = UpgradeAvailable(context.PackageName, context.Version);
}
@{
var version = UpgradeAvailable(context.PackageName, context.Version);
}
@if (version != context.Version)
{
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadModule(context.PackageName, version))>@SharedLocalizer["Upgrade"]</button>
@ -153,10 +153,10 @@ else
link = "<a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</a>";
}
}
}
}
return link;
}
}
}
return link;
}
private string SupportLink(string packagename, string version)
{
@ -172,75 +172,52 @@ else
return link;
}
private string UpgradeAvailable(string packagename, string version)
{
if (!string.IsNullOrEmpty(packagename) && _packages != null)
{
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
if (package != null && Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0)
{
return package.Version;
}
}
return version;
}
private string UpgradeAvailable(string packagename, string version)
{
if (!string.IsNullOrEmpty(packagename) && _packages != null)
{
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
if (package != null && Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0)
{
return package.Version;
}
}
return version;
}
private async Task DownloadModule(string packagename, string version)
{
try
{
await PackageService.DownloadPackageAsync(packagename, version);
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", packagename, version);
AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version} {Error}", packagename, version, ex.Message);
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
}
}
private async Task DownloadModule(string packagename, string version)
{
try
{
await PackageService.DownloadPackageAsync(packagename, version);
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", packagename, version);
AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version} {Error}", packagename, version, ex.Message);
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
}
}
private async Task DeleteModule(ModuleDefinition moduleDefinition)
{
try
{
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
AddModuleMessage(Localizer["Success.Module.Delete"], MessageType.Success);
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting Module {ModuleDefinition} {Error}", moduleDefinition, ex.Message);
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
}
}
private async Task DeleteModule(ModuleDefinition moduleDefinition)
{
try
{
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
AddModuleMessage(Localizer["Success.Module.Delete"], MessageType.Success);
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting Module {ModuleDefinition} {Error}", moduleDefinition, ex.Message);
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
}
}
private async Task CategoryChanged(ChangeEventArgs e)
{
_category = (string)e.Value;
private async Task CategoryChanged(ChangeEventArgs e)
{
_category = (string)e.Value;
await LoadModuleDefinitions();
}
private async Task Synchronize()
{
try
{
ShowProgressIndicator();
foreach (var moduleDefinition in _moduleDefinitions)
{
if (!string.IsNullOrEmpty(moduleDefinition.PackageName) && !_packages.Any(item => item.PackageId == moduleDefinition.PackageName))
{
var package = await PackageService.GetPackageAsync(moduleDefinition.PackageName, moduleDefinition.Version, false);
}
}
HideProgressIndicator();
AddModuleMessage(Localizer["Success.Module.Synchronize"], MessageType.Success);
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Synchronizing Modules {Error}", ex.Message);
AddModuleMessage(Localizer["Error.Module.Synchronize"], MessageType.Error);
}
}
}
}

View File

@ -56,6 +56,19 @@
<input id="displayname" class="form-control" @bind="@_displayname" maxlength="50" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="subject" HelpText="Deine Studien- oder Ausbildungsrichtung" ResourceKey="Subject">Fachrichtung:</Label>
<div class="col-sm-9">
<input id="subject" class="form-control" @bind="@_subject" maxlength="100" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="graduationYear" HelpText="Dein voraussichtlicher Abschlussjahrgang" ResourceKey="GraduationYear">Abschlussjahrgang:</Label>
<div class="col-sm-9">
<input id="graduationYear" type="number" class="form-control" @bind="@_graduationYear" min="1950" max="2100" step="1" required />
</div>
</div>
</div>
<br />
<button type="button" class="btn btn-primary" @onclick="Register">@Localizer["Register"]</button>
@ -89,6 +102,9 @@ else
private string _displayname = string.Empty;
private bool _userCreated = false;
private bool _allowsitelogin = true;
private string _subject = string.Empty;
private int _graduationYear;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
@ -124,7 +140,10 @@ else
Password = _password,
Email = _email,
DisplayName = (_displayname == string.Empty ? _username : _displayname),
PhotoFileId = null
PhotoFileId = null,
Subject = _subject,
GraduationYear = _graduationYear
};
user = await UserService.AddUserAsync(user);

View File

@ -55,7 +55,7 @@
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="url" HelpText="The url of the theme" ResourceKey="Url">Url: </Label>
<Label Class="col-sm-3" For="url" HelpText="The reference url of the theme" ResourceKey="ReferenceUrl">Reference Url: </Label>
<div class="col-sm-9">
<input id="url" class="form-control" @bind="@_url" disabled />
</div>

View File

@ -15,8 +15,8 @@
else
{
<ActionLink Action="Add" Text="Install Theme" ResourceKey="InstallTheme" />
<ActionLink Action="Create" Text="Create Theme" ResourceKey="CreateTheme" Class="btn btn-secondary ps-2" />
<button type="button" class="btn btn-secondary pw-2" @onclick="@Synchronize">@Localizer["Synchronize"]</button>
@((MarkupString)"&nbsp;")
<ActionLink Action="Create" Text="Create Theme" ResourceKey="CreateTheme" Class="btn btn-secondary" />
<Pager Items="@_themes">
<Header>
@ -173,27 +173,4 @@ else
AddModuleMessage(Localizer["Error.Theme.Delete"], MessageType.Error);
}
}
private async Task Synchronize()
{
try
{
ShowProgressIndicator();
foreach (var theme in _themes)
{
if (!string.IsNullOrEmpty(theme.PackageName) && !_packages.Any(item => item.PackageId == theme.PackageName))
{
await PackageService.GetPackageAsync(theme.PackageName, theme.Version, false);
}
}
HideProgressIndicator();
AddModuleMessage(Localizer["Success.Theme.Synchronize"], MessageType.Success);
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Synchronizing Themes {Error}", ex.Message);
AddModuleMessage(Localizer["Error.Theme.Synchronize"], MessageType.Error);
}
}
}

View File

@ -13,21 +13,9 @@
<TabPanel Name="Download" ResourceKey="Download">
@if (_package != null && _upgradeavailable)
{
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" HelpText="Specify if you want to backup files during the upgrade process. Disabling this option will result in a better experience in some environments." ResourceKey="Backup">Backup Files? </Label>
<div class="col-sm-9">
<select id="backup" class="form-select" @bind="@_backup">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
</div>
<ModuleMessage Type="MessageType.Info" Message="Select The Download Button To Download The Framework Upgrade Package And Then Select Upgrade"></ModuleMessage>
<button type="button" class="btn btn-primary" @onclick=@(async () => await Download(Constants.PackageId, @_package.Version))>@SharedLocalizer["Download"] @_package.Version</button>
<button type="button" class="btn btn-success" @onclick="Upgrade">@SharedLocalizer["Upgrade"]</button>
<br /><br />
<ModuleMessage Type="MessageType.Info" Message="Select The Download Button To Download The Framework Upgrade Package And Then Select Upgrade"></ModuleMessage>
}
else
{
@ -35,6 +23,7 @@
}
</TabPanel>
<TabPanel Name="Upload" ResourceKey="Upload">
<ModuleMessage Type="MessageType.Info" Message=@Localizer["MessageUpgrade.Text"]></ModuleMessage>
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" HelpText="Upload A Framework Package And Then Select Upgrade" ResourceKey="Framework">Framework: </Label>
@ -42,19 +31,8 @@
<FileManager Folder="@Constants.PackagesFolder" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" HelpText="Specify if you want to backup files during the upgrade process. Disabling this option will result in a better experience in some environments." ResourceKey="Backup">Backup Files? </Label>
<div class="col-sm-9">
<select id="backup" class="form-select" @bind="@_backup">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
</div>
<button type="button" class="btn btn-success" @onclick="Upgrade">@SharedLocalizer["Upgrade"]</button>
<br /><br />
<ModuleMessage Type="MessageType.Info" Message=@Localizer["MessageUpgrade.Text"]></ModuleMessage>
</TabPanel>
</TabStrip>
}
@ -63,7 +41,6 @@
private bool _initialized = false;
private Package _package;
private bool _upgradeavailable = false;
private string _backup = "True";
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
@ -109,7 +86,7 @@
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 10);
await InstallationService.Upgrade(bool.Parse(_backup));
await InstallationService.Upgrade();
}
catch (Exception ex)
{

View File

@ -421,18 +421,6 @@ else
</select>
</div>
</div>
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="allowhostrole" HelpText="Indicate if host roles are supported from the identity provider. Please use caution with this option as it allows the host user to administrate every site within your installation." ResourceKey="AllowHostRole">Allow Host Role?</Label>
<div class="col-sm-9">
<select id="allowhostrole" class="form-select" @bind="@_allowhostrole" required>
<option value="true">@SharedLocalizer["Yes"]</option>
<option value="false">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
}
}
</Section>
<Section Name="Token" Heading="Token Settings" ResourceKey="TokenSettings">
@ -532,7 +520,6 @@ else
private string _domainfilter;
private string _createusers;
private string _verifyusers;
private string _allowhostrole;
private string _secret;
private string _secrettype = "password";
@ -615,7 +602,6 @@ else
_domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", "");
_createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true");
_verifyusers = SettingService.GetSetting(settings, "ExternalLogin:VerifyUsers", "true");
_allowhostrole = SettingService.GetSetting(settings, "ExternalLogin:AllowHostRole", "false");
}
private async Task LoadUsersAsync(bool load)
@ -719,7 +705,6 @@ else
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);
settings = SettingService.SetSetting(settings, "ExternalLogin:VerifyUsers", _verifyusers, true);
settings = SettingService.SetSetting(settings, "ExternalLogin:AllowHostRole", _allowhostrole, true);
settings = SettingService.SetSetting(settings, "JwtOptions:Secret", _secret, true);
settings = SettingService.SetSetting(settings, "JwtOptions:Issuer", _issuer, true);

View File

@ -100,18 +100,6 @@ else
<br />
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
</TabPanel>
<TabPanel Name="Robots" Heading="Robots.txt" ResourceKey="Robots">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="robots" HelpText="Specify your robots.txt instructions to provide bots with guidance on which parts of your site should be indexed" ResourceKey="Robots">Instructions: </Label>
<div class="col-sm-9">
<textarea id="robots" class="form-control" @bind="@_robots" rows="3"></textarea>
</div>
</div>
</div>
<br />
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
</TabPanel>
</TabStrip>
}
@ -125,7 +113,6 @@ else
private string _filter = "";
private int _retention = 30;
private string _correlation = "true";
private string _robots = "";
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
@ -152,8 +139,7 @@ else
_filter = SettingService.GetSetting(settings, "VisitorFilter", Constants.DefaultVisitorFilter);
_retention = int.Parse(SettingService.GetSetting(settings, "VisitorRetention", "30"));
_correlation = SettingService.GetSetting(settings, "VisitorCorrelation", "true");
_robots = SettingService.GetSetting(settings, "Robots", "");
}
}
private async void TypeChanged(ChangeEventArgs e)
{
@ -205,7 +191,6 @@ else
settings = SettingService.SetSetting(settings, "VisitorFilter", _filter, true);
settings = SettingService.SetSetting(settings, "VisitorRetention", _retention.ToString(), true);
settings = SettingService.SetSetting(settings, "VisitorCorrelation", _correlation, true);
settings = SettingService.SetSetting(settings, "Robots", _robots, true);
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);

View File

@ -163,13 +163,6 @@
[Parameter]
public EventCallback<int> OnUpload { get; set; } // optional - executes a method in the calling component when a file is uploaded
[Parameter]
public EventCallback<int> OnSelectFolder { get; set; } // optional - executes a method in the calling component when a folder is selected
[Parameter]
public EventCallback<int> OnSelectFile { get; set; } // optional - executes a method in the calling component when a file is selected
[Obsolete("Use OnSelectFile instead.")]
[Parameter]
public EventCallback<int> OnSelect { get; set; } // optional - executes a method in the calling component when a file is selected
@ -307,8 +300,6 @@
FileId = -1;
_file = null;
_image = string.Empty;
await OnSelectFolder.InvokeAsync(FolderId);
StateHasChanged();
}
catch (Exception ex)
@ -324,10 +315,7 @@
_message = string.Empty;
FileId = int.Parse((string)e.Value);
await SetImage();
#pragma warning disable CS0618
await OnSelect.InvokeAsync(FileId);
#pragma warning restore CS0618
await OnSelectFile.InvokeAsync(FileId);
StateHasChanged();
}
@ -450,10 +438,7 @@
{
FileId = file.FileId;
await SetImage();
#pragma warning disable CS0618
await OnSelect.InvokeAsync(FileId);
#pragma warning restore CS0618
await OnSelectFile.InvokeAsync(FileId);
await OnUpload.InvokeAsync(FileId);
}
await GetFiles();
@ -504,10 +489,7 @@
await GetFiles();
FileId = -1;
await SetImage();
#pragma warning disable CS0618
await OnSelect.InvokeAsync(FileId);
#pragma warning restore CS0618
await OnSelectFile.InvokeAsync(FileId);
StateHasChanged();
}
catch (Exception ex)

View File

@ -60,7 +60,7 @@
@foreach (User user in _users)
{
<tr>
<td>@user.DisplayName (@user.Username)</td>
<td>@user.DisplayName</td>
@foreach (var permissionname in _permissionnames)
{
<td style="text-align: center; width: 1px;">
@ -270,8 +270,8 @@
private async Task<Dictionary<string, string>> GetUsers(string filter)
{
var users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
return users.Where(item => item.User.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase) || item.User.Username.Contains(filter, StringComparison.OrdinalIgnoreCase))
.ToDictionary(item => item.UserId.ToString(), item => item.User.DisplayName + " (" + item.User.Username + ")");
return users.Where(item => item.User.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase))
.ToDictionary(item => item.UserId.ToString(), item => item.User.DisplayName);
}
private async Task AddUser()

View File

@ -4,7 +4,7 @@
<TargetFramework>net9.0</TargetFramework>
<OutputType>Exe</OutputType>
<Configurations>Debug;Release</Configurations>
<Version>6.1.2</Version>
<Version>6.1.1</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -12,7 +12,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane</RootNamespace>
@ -22,22 +22,16 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Oqtane.Shared\Oqtane.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="Installer\Controls\AzureSqlConfig.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
</ItemGroup>
<PropertyGroup>
<PublishTrimmed>false</PublishTrimmed>
<BlazorEnableCompression>false</BlazorEnableCompression>

View File

@ -121,7 +121,7 @@
<value>Server:</value>
</data>
<data name="Server.HelpText" xml:space="preserve">
<value>Enter the database server name. This might include a port number as well if you are using a cloud service.</value>
<value>Enter the database server name. This might include a port number as well if you are using a cloud service (ie. servername.database.windows.net,1433) </value>
</data>
<data name="Database.Text" xml:space="preserve">
<value>Database:</value>

View File

@ -147,8 +147,8 @@
<data name="Owner.HelpText" xml:space="preserve">
<value>The owner or creator of the module</value>
</data>
<data name="Url.HelpText" xml:space="preserve">
<value>The url of the module</value>
<data name="ReferenceUrl.HelpText" xml:space="preserve">
<value>The reference url of the module</value>
</data>
<data name="Contact.HelpText" xml:space="preserve">
<value>The contact for the module</value>
@ -171,8 +171,8 @@
<data name="Owner.Text" xml:space="preserve">
<value>Owner: </value>
</data>
<data name="Url.Text" xml:space="preserve">
<value>Url: </value>
<data name="ReferenceUrl.Text" xml:space="preserve">
<value>Reference Url: </value>
</data>
<data name="Contact.Text" xml:space="preserve">
<value>Contact: </value>

View File

@ -159,13 +159,4 @@
<data name="Enabled" xml:space="preserve">
<value>Enabled?</value>
</data>
<data name="Synchronize" xml:space="preserve">
<value>Synchronize</value>
</data>
<data name="Success.Module.Synchronize" xml:space="preserve">
<value>Modules Have Been Successfully Synchronized With The Marketplace</value>
</data>
<data name="Error.Module.Synchronize" xml:space="preserve">
<value>Error Synchronizing Modules With The Marketplace</value>
</data>
</root>

View File

@ -132,8 +132,8 @@
<data name="Owner.Text" xml:space="preserve">
<value>Owner: </value>
</data>
<data name="Url.Text" xml:space="preserve">
<value>Url: </value>
<data name="ReferenceUrl.Text" xml:space="preserve">
<value>Reference Url: </value>
</data>
<data name="Contact.Text" xml:space="preserve">
<value>Contact: </value>
@ -153,8 +153,8 @@
<data name="Owner.HelpText" xml:space="preserve">
<value>The owner or creator of the theme</value>
</data>
<data name="Url.HelpText" xml:space="preserve">
<value>The url of the theme</value>
<data name="ReferenceUrl.HelpText" xml:space="preserve">
<value>The reference url of the theme</value>
</data>
<data name="Contact.HelpText" xml:space="preserve">
<value>The contact for the theme</value>

View File

@ -159,13 +159,4 @@
<data name="Assign" xml:space="preserve">
<value>Assign</value>
</data>
<data name="Synchronize" xml:space="preserve">
<value>Synchronize</value>
</data>
<data name="Success.Theme.Synchronize" xml:space="preserve">
<value>Themes Have Been Successfully Synchronized With The Marketplace</value>
</data>
<data name="Error.Theme.Synchronize" xml:space="preserve">
<value>Error Synchronizing Themes With The Marketplace</value>
</data>
</root>

View File

@ -151,12 +151,6 @@
<value>You Cannot Perform A System Update In A Development Environment</value>
</data>
<data name="Disclaimer.Text" xml:space="preserve">
<value>Please Note That The System Update Capability Is A Simplified Upgrade Process Intended For Small To Medium Sized Installations. For Larger Enterprise Installations You Will Want To Use A Manual Upgrade Process.</value>
</data>
<data name="Backup.Text" xml:space="preserve">
<value>Backup Files?</value>
</data>
<data name="Backup.HelpText" xml:space="preserve">
<value>Specify if you want to backup files during the upgrade process. Disabling this option will result in a better experience in some environments.</value>
<value>Please Note That The System Update Capability Is A Simplified Upgrade Process Intended For Small To Medium Sized Installations. For Larger Enterprise Installations You Will Want To Use A Manual Upgrade Process. Also Note That The System Update Capability Is Not Recommended When Using Microsoft Azure Due To Environmental Limitations.</value>
</data>
</root>

View File

@ -513,10 +513,4 @@
<data name="LogoutEverywhere.HelpText" xml:space="preserve">
<value>Do you want users to be logged out of every active session on any device, or only their current session?</value>
</data>
<data name="AllowHostRole.Text" xml:space="preserve">
<value>Allow Host Role?</value>
</data>
<data name="AllowHostRole.HelpText" xml:space="preserve">
<value>Indicate if host roles are supported from the identity provider. Please use caution with this option as it allows the host user to administrate every site within your installation.</value>
</data>
</root>

View File

@ -198,13 +198,4 @@
<data name="Duration.Text" xml:space="preserve">
<value>Session Duration:</value>
</data>
<data name="Robots.Heading" xml:space="preserve">
<value>Robots.txt</value>
</data>
<data name="Robots.Text" xml:space="preserve">
<value>Instructions:</value>
</data>
<data name="Robots.HelpText" xml:space="preserve">
<value>Specify your robots.txt instructions to provide bots with guidance on which parts of your site should be indexed</value>
</data>
</root>

View File

@ -47,9 +47,9 @@ namespace Oqtane.Services
return await PostJsonAsync<InstallConfig,Installation>(ApiUrl, config);
}
public async Task<Installation> Upgrade(bool backup)
public async Task<Installation> Upgrade()
{
return await GetJsonAsync<Installation>($"{ApiUrl}/upgrade/?backup={backup}");
return await GetJsonAsync<Installation>($"{ApiUrl}/upgrade");
}
public async Task RestartAsync()

View File

@ -26,9 +26,8 @@ namespace Oqtane.Services
/// <summary>
/// Starts the upgrade process
/// </summary>
/// <param name="backup">indicates if files should be backed up during upgrade</param>
/// <returns>internal status/message object</returns>
Task<Installation> Upgrade(bool backup);
Task<Installation> Upgrade();
/// <summary>
/// Restarts the installation

View File

@ -10,11 +10,10 @@ namespace Oqtane.Services
public interface IJobLogService
{
/// <summary>
/// Return a list of <see cref="JobLog"/> entries
/// Return a list of all <see cref="JobLog"/> entries
/// </summary>
/// <param name="jobId"></param>
/// <returns></returns>
Task<List<JobLog>> GetJobLogsAsync(int jobId);
Task<List<JobLog>> GetJobLogsAsync();
/// <summary>
/// Return a <see cref="JobLog"/> entry for the given Id

View File

@ -15,9 +15,10 @@ namespace Oqtane.Services
private string Apiurl => CreateApiUrl("JobLog");
public async Task<List<JobLog>> GetJobLogsAsync(int jobId)
public async Task<List<JobLog>> GetJobLogsAsync()
{
return await GetJsonAsync<List<JobLog>>($"{Apiurl}?jobid={jobId}");
List<JobLog> joblogs = await GetJsonAsync<List<JobLog>>(Apiurl);
return joblogs.OrderBy(item => item.StartDate).ToList();
}
public async Task<JobLog> GetJobLogAsync(int jobLogId)

View File

@ -6,7 +6,8 @@
@if (_enabled && !Hidden)
{
<div class="gdpr-consent-bar bg-light text-dark @(_showBanner ? "px-0 py-3 pt-5 pt-sm-3 pe-sm-5 ps-sm-3" : "p-0") fixed-bottom">
<div class="gdpr-consent-bar bg-light text-dark @(_showBanner ? "p-3" : "p-0") pe-5 fixed-bottom">
<form method="post" @formname="CookieConsentForm" @onsubmit="async () => await AcceptPolicy()" data-enhance>
@if (_showBanner)
{

View File

@ -2,18 +2,19 @@
@using System.Net
@inherits ThemeControlBase
@inject IStringLocalizer<UserProfile> Localizer
@inject NavigationManager NavigationManager
<span class="app-profile">
@if (PageState.User != null)
{
<a href="@_profileurl" class="@CssClass">@PageState.User.Username</a>
<a href="@NavigateUrl("profile", "returnurl=" + _returnurl)" class="@CssClass">@PageState.User.Username</a>
}
else
{
@if (ShowRegister && PageState.Site.AllowRegistration)
{
<a href="@_registerurl" class="@CssClass">@Localizer["Register"]</a>
<a href="@NavigateUrl("register", "returnurl=" + _returnurl)" class="@CssClass">@Localizer["Register"]</a>
}
}
</span>
@ -22,50 +23,23 @@
[Parameter]
public bool ShowRegister { get; set; }
[Parameter]
public string CssClass { get; set; } = "btn btn-primary";
[Parameter]
public string RegisterUrl { get; set; } // optional parameter to specify a custom registration url
[Parameter]
public string ProfileUrl { get; set; } // optional parameter to specify a custom user profile url
private string _registerurl = "";
private string _profileurl = "";
private string _returnurl = "";
protected override void OnParametersSet()
{
if (!PageState.QueryString.ContainsKey("returnurl"))
{
// remember current url
_returnurl = WebUtility.UrlEncode(PageState.Route.PathAndQuery);
}
else
{
// use existing value
_returnurl = PageState.QueryString["returnurl"];
}
if (!string.IsNullOrEmpty(RegisterUrl))
{
_registerurl = RegisterUrl + "?returnurl=" + (RegisterUrl.Contains("://") ? WebUtility.UrlEncode(PageState.Route.RootUrl) + _returnurl : _returnurl);
}
else
{
_registerurl = NavigateUrl("register", "returnurl=" + _returnurl);
}
if (!string.IsNullOrEmpty(ProfileUrl))
{
_registerurl = ProfileUrl + "?returnurl=" + (ProfileUrl.Contains("://") ? WebUtility.UrlEncode(PageState.Route.RootUrl) + _returnurl : _returnurl);
}
else
{
_registerurl = NavigateUrl("profile", "returnurl=" + _returnurl);
}
{
if (!PageState.QueryString.ContainsKey("returnurl"))
{
// remember current url
_returnurl = WebUtility.UrlEncode(PageState.Route.PathAndQuery);
}
else
{
// use existing value
_returnurl = PageState.QueryString["returnurl"];
}
}
}

View File

@ -623,7 +623,7 @@
}
// ensure resource does not exist already
if (!pageresources.Exists(item => Utilities.GetUrlPath(item.Url).ToLower() == Utilities.GetUrlPath(resource.Url).ToLower()))
if (!pageresources.Exists(item => item.Url.ToLower() == resource.Url.ToLower()))
{
pageresources.Add(resource.Clone(level, name, fingerprint));
}

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Version>6.1.2</Version>
<Version>6.1.1</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
@ -42,7 +42,7 @@
</ItemGroup>
<ItemGroup>
<MySQLFiles Include="$(OutputPath)Oqtane.Database.MySQL.dll;$(OutputPath)Oqtane.Database.MySQL.pdb;$(OutputPath)Pomelo.EntityFrameworkCore.MySql.dll;$(OutputPath)MySqlConnector.dll;$(OutputPath)MySql.Data.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net9.0\%(Filename)%(Extension)" />
<MySQLFiles Include="$(OutputPath)Oqtane.Database.MySQL.dll;$(OutputPath)Oqtane.Database.MySQL.pdb;$(OutputPath)Pomelo.EntityFrameworkCore.MySql.dll;$(OutputPath)MySql.Data.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net9.0\%(Filename)%(Extension)" />
</ItemGroup>
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(MySQLFiles)" Outputs="@(MySQLFiles->'%(DestinationPath)')">

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Version>6.1.2</Version>
<Version>6.1.1</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
@ -34,7 +34,7 @@
<ItemGroup>
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
</ItemGroup>

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Version>6.1.2</Version>
<Version>6.1.1</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
@ -33,7 +33,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.3" />
</ItemGroup>
<ItemGroup>

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Version>6.1.2</Version>
<Version>6.1.1</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
@ -33,7 +33,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.3" />
</ItemGroup>
<ItemGroup>

View File

@ -6,7 +6,7 @@
<!-- <TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks> -->
<!-- <TargetFrameworks>$(TargetFrameworks);net9.0-tizen</TargetFrameworks> -->
<OutputType>Exe</OutputType>
<Version>6.1.2</Version>
<Version>6.1.1</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -14,7 +14,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane.Maui</RootNamespace>
@ -30,7 +30,7 @@
<ApplicationId>com.oqtane.maui</ApplicationId>
<!-- Versions -->
<ApplicationDisplayVersion>6.1.2</ApplicationDisplayVersion>
<ApplicationDisplayVersion>6.1.1</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>
<!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
@ -67,14 +67,14 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.4" />
<PackageReference Include="System.Net.Http.Json" Version="9.0.4" />
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.50" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="9.0.50" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.50" />
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
<PackageReference Include="System.Net.Http.Json" Version="9.0.3" />
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.40" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="9.0.40" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.40" />
</ItemGroup>
<ItemGroup>

View File

@ -2,7 +2,7 @@
<package>
<metadata>
<id>Oqtane.Client</id>
<version>6.1.2</version>
<version>6.1.1</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</releaseNotes>
<readme>readme.md</readme>
<icon>icon.png</icon>
<tags>oqtane</tags>

View File

@ -2,7 +2,7 @@
<package>
<metadata>
<id>Oqtane.Framework</id>
<version>6.1.2</version>
<version>6.1.1</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@ -11,8 +11,8 @@
<copyright>.NET Foundation</copyright>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v6.1.2/Oqtane.Framework.6.1.2.Upgrade.zip</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</releaseNotes>
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v6.1.1/Oqtane.Framework.6.1.1.Upgrade.zip</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</releaseNotes>
<readme>readme.md</readme>
<icon>icon.png</icon>
<tags>oqtane framework</tags>

View File

@ -2,7 +2,7 @@
<package>
<metadata>
<id>Oqtane.Server</id>
<version>6.1.2</version>
<version>6.1.1</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</releaseNotes>
<readme>readme.md</readme>
<icon>icon.png</icon>
<tags>oqtane</tags>

View File

@ -2,7 +2,7 @@
<package>
<metadata>
<id>Oqtane.Shared</id>
<version>6.1.2</version>
<version>6.1.1</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</releaseNotes>
<readme>readme.md</readme>
<icon>icon.png</icon>
<tags>oqtane</tags>

View File

@ -2,7 +2,7 @@
<package>
<metadata>
<id>Oqtane.Updater</id>
<version>6.1.2</version>
<version>6.1.1</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</releaseNotes>
<readme>readme.md</readme>
<icon>icon.png</icon>
<tags>oqtane</tags>

View File

@ -1 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.2.Install.zip" -Force
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.1.Install.zip" -Force

View File

@ -1 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.2.Upgrade.zip" -Force
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.1.Upgrade.zip" -Force

View File

@ -764,7 +764,7 @@
}
// ensure resource does not exist already
if (!pageresources.Exists(item => Utilities.GetUrlPath(item.Url).ToLower() == Utilities.GetUrlPath(resource.Url).ToLower()))
if (!pageresources.Exists(item => item.Url.ToLower() == resource.Url.ToLower()))
{
pageresources.Add(resource.Clone(level, name, fingerprint));
}

View File

@ -23,7 +23,6 @@ using System.IO.Compression;
using Oqtane.Services;
using Microsoft.Extensions.Primitives;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.Net.Http.Headers;
// ReSharper disable StringIndexOfIsCultureSpecific.1
@ -736,10 +735,6 @@ namespace Oqtane.Controllers
}
if (!string.IsNullOrEmpty(imagepath))
{
if (!string.IsNullOrEmpty(file.Folder.CacheControl))
{
HttpContext.Response.Headers.Append(HeaderNames.CacheControl, value: file.Folder.CacheControl);
}
return PhysicalFile(imagepath, file.GetMimeType());
}
else

View File

@ -280,14 +280,9 @@ namespace Oqtane.Controllers
var folder = _folders.GetFolder(id, false);
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, folder.SiteId, EntityNames.Folder, id, PermissionNames.Edit))
{
var folderPath = _folders.GetFolderPath(folder);
if (Directory.Exists(folderPath))
if (Directory.Exists(_folders.GetFolderPath(folder)))
{
foreach (var filePath in Directory.GetFiles(folderPath))
{
System.IO.File.Delete(filePath);
}
Directory.Delete(folderPath);
Directory.Delete(_folders.GetFolderPath(folder));
}
_folders.DeleteFolder(id);
_syncManager.AddSyncEvent(_alias, EntityNames.Folder, folder.FolderId, SyncEventActions.Delete);

View File

@ -88,10 +88,10 @@ namespace Oqtane.Controllers
[HttpGet("upgrade")]
[Authorize(Roles = RoleNames.Host)]
public Installation Upgrade(string backup)
public Installation Upgrade()
{
var installation = new Installation { Success = true, Message = "" };
_installationManager.UpgradeFramework(bool.Parse(backup));
_installationManager.UpgradeFramework();
return installation;
}

View File

@ -17,12 +17,12 @@ namespace Oqtane.Controllers
_jobLogs = jobLogs;
}
// GET: api/<controller>?jobid=x
// GET: api/<controller>
[HttpGet]
[Authorize(Roles = RoleNames.Host)]
public IEnumerable<JobLog> Get(string jobid)
public IEnumerable<JobLog> Get()
{
return _jobLogs.GetJobLogs(int.Parse(jobid));
return _jobLogs.GetJobLogs();
}
// GET api/<controller>/5

View File

@ -90,26 +90,33 @@ namespace Oqtane.Controllers
package = await GetJson<Package>(client, url + $"/api/registry/package/?id={_configManager.GetInstallationId()}&package={packageid}&version={version}&download={download}&email={WebUtility.UrlEncode(GetPackageRegistryEmail())}");
}
if (package != null && bool.Parse(install))
if (package != null)
{
using (var httpClient = new HttpClient())
if (bool.Parse(install))
{
var folder = Path.Combine(_environment.ContentRootPath, Constants.PackagesFolder);
var response = await httpClient.GetAsync(package.PackageUrl).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
using (var httpClient = new HttpClient())
{
string filename = packageid + "." + version + ".nupkg";
using (var fileStream = new FileStream(Path.Combine(Constants.PackagesFolder, filename), FileMode.Create, FileAccess.Write, FileShare.None))
var folder = Path.Combine(_environment.ContentRootPath, Constants.PackagesFolder);
var response = await httpClient.GetAsync(package.PackageUrl).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
string filename = packageid + "." + version + ".nupkg";
using (var fileStream = new FileStream(Path.Combine(Constants.PackagesFolder, filename), FileMode.Create, FileAccess.Write, FileShare.None))
{
await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Create, "Could Not Download {PackageUrl}", package.PackageUrl);
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Create, "Could Not Download {PackageUrl}", package.PackageUrl);
}
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Create, "Package {PackageId}.{Version} Is Not Registered In The Marketplace", packageid, version);
}
}
return package;
}

View File

@ -532,9 +532,8 @@ namespace Oqtane.Extensions
// external roles
if (claimsPrincipal.Claims.Any(item => item.Type == httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", "")))
{
var _roles = httpContext.RequestServices.GetRequiredService<IRoleRepository>();
var allowhostrole = bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:AllowHostRole", "false"));
var roles = _roles.GetRoles(user.SiteId, allowhostrole).ToList();
var _roles = httpContext.RequestServices.GetRequiredService<IRoleRepository>();
var roles = _roles.GetRoles(user.SiteId).ToList(); // global roles excluded ie. host users cannot be added/deleted
var mappings = httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimMappings", "").Split(',');
foreach (var claim in claimsPrincipal.Claims.Where(item => item.Type == httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", "")))
@ -584,9 +583,8 @@ namespace Oqtane.Extensions
}
}
var host = userRoles.FirstOrDefault(item => item.Role.Name == RoleNames.Host);
var registered = userRoles.FirstOrDefault(item => item.Role.Name == RoleNames.Registered);
if (!user.IsDeleted && (host != null || registered != null && Utilities.IsEffectiveAndNotExpired(registered.EffectiveDate, registered.ExpiryDate)))
var userrole = userRoles.FirstOrDefault(item => item.Role.Name == RoleNames.Registered);
if (!user.IsDeleted && userrole != null && Utilities.IsEffectiveAndNotExpired(userrole.EffectiveDate, userrole.ExpiryDate))
{
// update user
user.LastLoginOn = DateTime.UtcNow;

View File

@ -731,7 +731,6 @@ namespace Oqtane.Infrastructure
{
_configManager.AddOrUpdateSetting($"{SettingKeys.DatabaseSection}:{SettingKeys.DatabaseTypeKey}", Constants.DefaultDBType, true);
}
if (!_configManager.GetSection(SettingKeys.AvailableDatabasesSection).Exists())
{
string databases = "[";
@ -743,19 +742,6 @@ namespace Oqtane.Infrastructure
databases += "]";
_configManager.AddOrUpdateSetting(SettingKeys.AvailableDatabasesSection, databases, true);
}
var availabledatabases = _configManager.GetSection(SettingKeys.AvailableDatabasesSection).GetChildren();
if (!availabledatabases.Any(item => item.GetSection("Name").Value == "Azure SQL"))
{
// Azure SQL added in 6.1.2
string databases = "[";
foreach (var database in availabledatabases)
{
databases += "{ " + $"\"Name\": \"{database["Name"]}\", \"ControlType\": \"{database["ControlType"]}\", \"DBTYpe\": \"{database["DBType"]}\"" + " },";
}
databases += "{ \"Name\": \"Azure SQL\", \"ControlType\": \"Oqtane.Installer.Controls.AzureSqlConfig, Oqtane.Client\", \"DBTYpe\": \"Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Database.SqlServer\" }";
databases += "]";
_configManager.AddOrUpdateSetting(SettingKeys.AvailableDatabasesSection, databases, true);
}
}
}
}

View File

@ -380,7 +380,7 @@ namespace Oqtane.Infrastructure
File.WriteAllText(assemblyLogPath, JsonSerializer.Serialize(assemblies, new JsonSerializerOptions { WriteIndented = true }));
}
public async Task UpgradeFramework(bool backup)
public async Task UpgradeFramework()
{
string folder = Path.Combine(_environment.ContentRootPath, Constants.PackagesFolder);
if (Directory.Exists(folder))
@ -448,14 +448,14 @@ namespace Oqtane.Infrastructure
// install Oqtane.Upgrade zip package
if (File.Exists(upgradepackage))
{
FinishUpgrade(backup);
FinishUpgrade();
}
}
}
}
}
private void FinishUpgrade(bool backup)
private void FinishUpgrade()
{
// check if updater application exists
string Updater = Constants.UpdaterPackageId + ".dll";
@ -469,7 +469,7 @@ namespace Oqtane.Infrastructure
{
WorkingDirectory = folder,
FileName = "dotnet",
Arguments = Path.Combine(folder, Updater) + " \"" + _environment.ContentRootPath + "\" \"" + _environment.WebRootPath + "\" \"" + backup.ToString() + "\"",
Arguments = Path.Combine(folder, Updater) + " \"" + _environment.ContentRootPath + "\" \"" + _environment.WebRootPath + "\"",
UseShellExecute = false,
ErrorDialog = false,
CreateNoWindow = true,

View File

@ -7,7 +7,7 @@ namespace Oqtane.Infrastructure
void InstallPackages();
bool UninstallPackage(string PackageName);
int RegisterAssemblies();
Task UpgradeFramework(bool backup);
Task UpgradeFramework();
void RestartApplication();
}
}

View File

@ -46,8 +46,6 @@ namespace Oqtane.Infrastructure
protected async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.Yield(); // required so that this method does not block startup
while (!stoppingToken.IsCancellationRequested)
{
using (var scope = _serviceScopeFactory.CreateScope())
@ -172,7 +170,8 @@ namespace Oqtane.Infrastructure
jobs.UpdateJob(job);
// trim the job log
List<JobLog> logs = jobLogs.GetJobLogs(job.JobId).ToList();
List<JobLog> logs = jobLogs.GetJobLogs().Where(item => item.JobId == job.JobId)
.OrderByDescending(item => item.JobLogId).ToList();
for (int i = logs.Count; i > job.RetentionHistory; i--)
{
jobLogs.DeleteJobLog(logs[i - 1].JobLogId);

View File

@ -130,12 +130,7 @@ namespace Oqtane.Infrastructure
mailMessage.Subject = notification.Subject;
//body
mailMessage.Body = notification.Body;
if (!mailMessage.Body.Contains("<") || !mailMessage.Body.Contains(">"))
{
// plain text messages should convert line breaks to HTML tags to preserve formatting
mailMessage.Body = mailMessage.Body.Replace("\n", "<br />");
}
mailMessage.Body = notification.Body.Replace("\n", "<br />");
// encoding
mailMessage.SubjectEncoding = System.Text.Encoding.UTF8;

View File

@ -74,21 +74,8 @@ namespace Oqtane.Infrastructure
// handle robots.txt root request (does not support subfolder aliases)
if (context.Request.Path.StartsWithSegments("/robots.txt") && string.IsNullOrEmpty(alias.Path))
{
string robots = "";
if (sitesettings.ContainsKey("Robots") && !string.IsNullOrEmpty(sitesettings["Robots"]))
{
robots = sitesettings["Robots"];
}
else
{
// allow all user agents by default
robots = $"User-agent: *";
}
if (!robots.ToLower().Contains("Sitemap:"))
{
// add sitemap if not specified
robots += $"\n\nSitemap: {context.Request.Scheme}://{alias.Name}/sitemap.xml";
}
// allow all user agents and specify site map
var robots = $"User-agent: *\n\nSitemap: {context.Request.Scheme}://{alias.Name}/sitemap.xml";
context.Response.ContentType = "text/plain";
await context.Response.WriteAsync(robots);
return;

View File

@ -1,6 +1,7 @@
using System.Collections.Generic;
using Microsoft.Extensions.Localization;
using Oqtane.Documentation;
using Oqtane.Infrastructure;
using Oqtane.Models;
using Oqtane.Shared;
@ -181,7 +182,6 @@ namespace Oqtane.Infrastructure.SiteTemplates
Name = "Privacy",
Parent = "",
Path = "privacy",
Order = seed + 11,
Icon = Icons.Eye,
IsNavigation = false,
IsPersonalizable = false,
@ -212,7 +212,6 @@ namespace Oqtane.Infrastructure.SiteTemplates
Name = "Terms",
Parent = "",
Path = "terms",
Order = seed + 13,
Icon = Icons.List,
IsNavigation = false,
IsPersonalizable = false,
@ -243,7 +242,7 @@ namespace Oqtane.Infrastructure.SiteTemplates
Name = "Not Found",
Parent = "",
Path = "404",
Order = seed + 15,
Order = seed + 11,
Icon = Icons.X,
IsNavigation = false,
IsPersonalizable = false,

View File

@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Oqtane.Documentation;
using Oqtane.Infrastructure;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Shared;

View File

@ -1,5 +1,6 @@
using System.Collections.Generic;
using Oqtane.Documentation;
using Oqtane.Infrastructure;
using Oqtane.Models;
using Oqtane.Shared;

View File

@ -473,7 +473,6 @@ namespace Oqtane.Infrastructure
Name = "Privacy",
Parent = "",
Path = "privacy",
Order = 1011,
Icon = Icons.Eye,
IsNavigation = false,
IsPersonalizable = false,
@ -503,7 +502,6 @@ namespace Oqtane.Infrastructure
Name = "Terms",
Parent = "",
Path = "terms",
Order = 1013,
Icon = Icons.List,
IsNavigation = false,
IsPersonalizable = false,

View File

@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Configurations>Debug;Release</Configurations>
<Version>6.1.2</Version>
<Version>6.1.1</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -11,7 +11,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane</RootNamespace>
@ -34,21 +34,21 @@
<EmbeddedResource Include="Scripts\MigrateTenant.sql" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.3" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.4">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.4" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.3" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="9.0.3" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.11" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7" />
<PackageReference Include="HtmlAgilityPack" Version="1.12.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.3.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Oqtane.Client\Oqtane.Client.csproj" />

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Oqtane.Models;
namespace Oqtane.Repository
@ -6,7 +6,6 @@ namespace Oqtane.Repository
public interface IJobLogRepository
{
IEnumerable<JobLog> GetJobLogs();
IEnumerable<JobLog> GetJobLogs(int jobId);
JobLog AddJobLog(JobLog jobLog);
JobLog UpdateJobLog(JobLog jobLog);
JobLog GetJobLog(int jobLogId);

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Oqtane.Models;
@ -15,17 +15,9 @@ namespace Oqtane.Repository
}
public IEnumerable<JobLog> GetJobLogs()
{
return GetJobLogs(-1);
}
public IEnumerable<JobLog> GetJobLogs(int jobId)
{
return _db.JobLog
.AsNoTracking()
.Where(item => item.JobId == jobId || jobId == -1)
.Include(item => item.Job) // eager load jobs
.OrderByDescending(item => item.JobLogId)
.ToList();
}

View File

@ -61,9 +61,6 @@ namespace Oqtane.Repository
public UserRole AddUserRole(UserRole userRole)
{
userRole.EffectiveDate = userRole.EffectiveDate.HasValue ? DateTime.SpecifyKind(userRole.EffectiveDate.Value, DateTimeKind.Utc) : userRole.EffectiveDate;
userRole.ExpiryDate = userRole.ExpiryDate.HasValue ? DateTime.SpecifyKind(userRole.ExpiryDate.Value, DateTimeKind.Utc) : userRole.ExpiryDate;
using var db = _dbContextFactory.CreateDbContext();
db.UserRole.Add(userRole);
db.SaveChanges();
@ -87,9 +84,6 @@ namespace Oqtane.Repository
public UserRole UpdateUserRole(UserRole userRole)
{
userRole.EffectiveDate = userRole.EffectiveDate.HasValue ? DateTime.SpecifyKind(userRole.EffectiveDate.Value, DateTimeKind.Utc) : userRole.EffectiveDate;
userRole.ExpiryDate = userRole.ExpiryDate.HasValue ? DateTime.SpecifyKind(userRole.ExpiryDate.Value, DateTimeKind.Utc) : userRole.ExpiryDate;
using var db = _dbContextFactory.CreateDbContext();
db.Entry(userRole).State = EntityState.Modified;
db.SaveChanges();

View File

@ -70,6 +70,7 @@ namespace Oqtane
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddOptions<List<Database>>().Bind(Configuration.GetSection(SettingKeys.AvailableDatabasesSection));
services.Configure<HostOptions>(opts => opts.ShutdownTimeout = TimeSpan.FromSeconds(10)); // increase from default of 5 seconds
// register scoped core services
services.AddScoped<IAuthorizationHandler, PermissionHandler>()

View File

@ -2,10 +2,10 @@
"RenderMode": "Interactive",
"Runtime": "Server",
"Database": {
"DefaultDBType": ""
"DefaultDBType": "Oqtane.Database.Sqlite.SqliteDatabase, Oqtane.Database.Sqlite"
},
"ConnectionStrings": {
"DefaultConnection": ""
"DefaultConnection": "Data Source=Oqtane-202507152139.db;"
},
"Installation": {
"DefaultAlias": "",
@ -54,5 +54,9 @@
"LogLevel": {
"Default": "Information"
}
}
},
"InstallationId": "e0b3aaed-a33d-4756-bd08-a83403d0c437",
"InstallationVersion": "6.1.1",
"InstallationDate": "202507152140",
"PackageRegistryEmail": "ismail.hasimoglu@edu.szu.at"
}

View File

@ -13,11 +13,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.4" />
<PackageReference Include="System.Net.Http.Json" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.3" />
<PackageReference Include="System.Net.Http.Json" Version="9.0.3" />
</ItemGroup>
<ItemGroup>

View File

@ -19,10 +19,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
</ItemGroup>
<ItemGroup>

View File

@ -13,9 +13,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
</ItemGroup>
<ItemGroup>

View File

@ -4,4 +4,4 @@ set ProjectName=%2
del "*.nupkg"
"..\..\[RootFolder]\oqtane.package\nuget.exe" pack %ProjectName%.nuspec -Properties targetframework=%TargetFramework%;projectname=%ProjectName%
XCOPY "*.nupkg" "..\..\[RootFolder]\Oqtane.Server\Packages\" /Y
XCOPY "*.nupkg" "..\..\[RootFolder]\Oqtane.Server\wwwroot\Packages\" /Y

View File

@ -32,6 +32,10 @@ namespace Oqtane.Models
/// <summary>
/// Reference to a <see cref="File"/> containing the users photo.
/// </summary>
public string Subject { get; set; }
public int GraduationYear { get; set; }
public int? PhotoFileId { get; set; }
/// <summary>

View File

@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Configurations>Debug;Release</Configurations>
<Version>6.1.2</Version>
<Version>6.1.1</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -11,7 +11,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane</RootNamespace>
@ -19,11 +19,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.3" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="System.Text.Json" Version="9.0.4" />
<PackageReference Include="System.Text.Json" Version="9.0.3" />
</ItemGroup>
</Project>

View File

@ -4,8 +4,8 @@ namespace Oqtane.Shared
{
public class Constants
{
public static readonly string Version = "6.1.2";
public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2,2.1.0,2.2.0,2.3.0,2.3.1,3.0.0,3.0.1,3.0.2,3.0.3,3.1.0,3.1.1,3.1.2,3.1.3,3.1.4,3.2.0,3.2.1,3.3.0,3.3.1,3.4.0,3.4.1,3.4.2,3.4.3,4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,5.0.0,5.0.1,5.0.2,5.0.3,5.1.0,5.1.1,5.1.2,5.2.0,5.2.1,5.2.2,5.2.3,5.2.4,6.0.0,6.0.1,6.1.0,6.1.1,6.1.2";
public static readonly string Version = "6.1.1";
public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2,2.1.0,2.2.0,2.3.0,2.3.1,3.0.0,3.0.1,3.0.2,3.0.3,3.1.0,3.1.1,3.1.2,3.1.3,3.1.4,3.2.0,3.2.1,3.3.0,3.3.1,3.4.0,3.4.1,3.4.2,3.4.3,4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,5.0.0,5.0.1,5.0.2,5.0.3,5.1.0,5.1.1,5.1.2,5.2.0,5.2.1,5.2.2,5.2.3,5.2.4,6.0.0,6.0.1,6.1.0,6.1.1";
public const string PackageId = "Oqtane.Framework";
public const string ClientId = "Oqtane.Client";
public const string UpdaterPackageId = "Oqtane.Updater";

View File

@ -121,17 +121,6 @@ namespace Oqtane.Shared
return $"{alias?.BaseUrl}{url}{Constants.ImageUrl}{fileId}/{width}/{height}/{mode}/{position}/{background}/{rotate}/{recreate}";
}
public static string ImageUrl(Alias alias, string folderpath, string filename, int width, int height, string mode, string position, string background, int rotate, string format, bool recreate)
{
var aliasUrl = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : "";
mode = string.IsNullOrEmpty(mode) ? "crop" : mode;
position = string.IsNullOrEmpty(position) ? "center" : position;
background = string.IsNullOrEmpty(background) ? "transparent" : background;
format = string.IsNullOrEmpty(format) ? "png" : format;
var querystring = $"?width={width}&height={height}&mode={mode}&position={position}&background={background}&rotate={rotate}&format={format}&recreate={recreate}";
return $"{alias?.BaseUrl}{aliasUrl}{Constants.FileUrl}{folderpath.Replace("\\", "/")}{filename}{querystring}";
}
public static string TenantUrl(Alias alias, string url)
{
url = (!url.StartsWith("/")) ? "/" + url : url;
@ -491,15 +480,6 @@ namespace Oqtane.Shared
return querystring;
}
public static string GetUrlPath(string url)
{
if (url.Contains("?"))
{
url = url.Substring(0, url.IndexOf("?"));
}
return url;
}
public static string LogMessage(object @class, string message)
{
return $"[{@class.GetType()}] {message}";

View File

@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<OutputType>Exe</OutputType>
<Version>6.1.2</Version>
<Version>6.1.1</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -11,7 +11,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane</RootNamespace>

View File

@ -15,19 +15,17 @@ namespace Oqtane.Updater
/// <param name="args"></param>
static void Main(string[] args)
{
// requires 3 arguments - the ContentRootPath, the WebRootPath of the site, and a backup flag
// requires 2 arguments - the ContentRootPath and the WebRootPath of the site
// for testing purposes you can uncomment and modify the logic below
//Array.Resize(ref args, 3);
//Array.Resize(ref args, 2);
//args[0] = @"C:\yourpath\oqtane.framework\Oqtane.Server";
//args[1] = @"C:\yourpath\oqtane.framework\Oqtane.Server\wwwroot";
//args[2] = @"true";
if (args.Length == 3)
if (args.Length == 2)
{
string contentrootfolder = args[0];
string webrootfolder = args[1];
bool backup = bool.Parse(args[2]);
string deployfolder = Path.Combine(contentrootfolder, "Packages");
string backupfolder = Path.Combine(contentrootfolder, "Backup");
@ -51,7 +49,6 @@ namespace Oqtane.Updater
WriteLog(logFilePath, "Upgrade Process Started: " + DateTime.UtcNow.ToString() + Environment.NewLine);
WriteLog(logFilePath, "ContentRootPath: " + contentrootfolder + Environment.NewLine);
WriteLog(logFilePath, "WebRootPath: " + webrootfolder + Environment.NewLine);
if (packagename != "" && File.Exists(Path.Combine(webrootfolder, "app_offline.bak")))
{
WriteLog(logFilePath, "Located Upgrade Package: " + packagename + Environment.NewLine);
@ -77,12 +74,12 @@ namespace Oqtane.Updater
}
bool success = true;
if (backup)
// ensure files are not locked
if (CanAccessFiles(files))
{
UpdateOfflineContent(offlineFilePath, offlineTemplate, 10, "Preparing Backup Folder");
WriteLog(logFilePath, "Preparing Backup Folder: " + backupfolder + Environment.NewLine);
try
{
// clear out backup folder
@ -115,28 +112,8 @@ namespace Oqtane.Updater
{
Directory.CreateDirectory(Path.GetDirectoryName(filename));
}
try
{
// try optimistically to backup the file
File.Copy(file, filename);
WriteLog(logFilePath, "Copy File: " + filename + Environment.NewLine);
}
catch
{
// if the file is locked, wait until it is unlocked
if (CanAccessFile(file))
{
File.Copy(file, filename);
WriteLog(logFilePath, "Copy File: " + filename + Environment.NewLine);
}
else
{
// file could not be backed up, upgrade unsuccessful
success = false;
WriteLog(logFilePath, "Error Backing Up Files" + Environment.NewLine);
}
}
File.Copy(file, filename);
WriteLog(logFilePath, "Copy File: " + filename + Environment.NewLine);
}
}
catch (Exception ex)
@ -148,20 +125,17 @@ namespace Oqtane.Updater
}
}
}
}
// extract files
if (success)
{
UpdateOfflineContent(offlineFilePath, offlineTemplate, 50, "Extracting Files From Upgrade Package");
WriteLog(logFilePath, "Extracting Files From Upgrade Package..." + Environment.NewLine);
try
// extract files
if (success)
{
using (ZipArchive archive = ZipFile.OpenRead(packagename))
UpdateOfflineContent(offlineFilePath, offlineTemplate, 50, "Extracting Files From Upgrade Package");
WriteLog(logFilePath, "Extracting Files From Upgrade Package..." + Environment.NewLine);
try
{
foreach (ZipArchiveEntry entry in archive.Entries)
using (ZipArchive archive = ZipFile.OpenRead(packagename))
{
if (success)
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (!string.IsNullOrEmpty(entry.Name))
{
@ -170,42 +144,20 @@ namespace Oqtane.Updater
{
Directory.CreateDirectory(Path.GetDirectoryName(filename));
}
try
{
// try optimistically to extract the file
entry.ExtractToFile(filename, true);
WriteLog(logFilePath, "Exact File: " + filename + Environment.NewLine);
}
catch
{
// if the file is locked, wait until it is unlocked
if (CanAccessFile(filename))
{
entry.ExtractToFile(filename, true);
WriteLog(logFilePath, "Exact File: " + filename + Environment.NewLine);
}
else
{
// file could not be extracted, upgrade unsuccessful
success = false;
WriteLog(logFilePath, "Error Extracting Files From Upgrade Package" + Environment.NewLine);
}
}
entry.ExtractToFile(filename, true);
WriteLog(logFilePath, "Exact File: " + filename + Environment.NewLine);
}
}
}
}
}
catch (Exception ex)
{
success = false;
WriteLog(logFilePath, "Error Extracting Files From Upgrade Package: " + ex.Message + Environment.NewLine);
}
catch (Exception ex)
{
success = false;
UpdateOfflineContent(offlineFilePath, offlineTemplate, 95, "Error Extracting Files From Upgrade Package", "bg-danger");
WriteLog(logFilePath, "Error Extracting Files From Upgrade Package: " + ex.Message + Environment.NewLine);
}
if (success)
{
if (backup)
if (success)
{
UpdateOfflineContent(offlineFilePath, offlineTemplate, 90, "Removing Backup Folder");
WriteLog(logFilePath, "Removing Backup Folder..." + Environment.NewLine);
@ -222,12 +174,7 @@ namespace Oqtane.Updater
WriteLog(logFilePath, "Error Removing Backup Folder: " + ex.Message + Environment.NewLine);
}
}
}
else
{
UpdateOfflineContent(offlineFilePath, offlineTemplate, 95, "Error Extracting Files From Upgrade Package", "bg-danger");
if (backup)
else
{
UpdateOfflineContent(offlineFilePath, offlineTemplate, 50, "Upgrade Failed, Restoring Files From Backup Folder", "bg-warning");
WriteLog(logFilePath, "Restoring Files From Backup Folder..." + Environment.NewLine);
@ -254,14 +201,18 @@ namespace Oqtane.Updater
}
}
}
else
{
UpdateOfflineContent(offlineFilePath, offlineTemplate, 95, "Upgrade Failed: Could Not Backup Files", "bg-danger");
WriteLog(logFilePath, "Upgrade Failed: Could Not Backup Files" + Environment.NewLine);
}
}
else
{
UpdateOfflineContent(offlineFilePath, offlineTemplate, 95, "Upgrade Failed: Could Not Backup Files", "bg-danger");
WriteLog(logFilePath, "Upgrade Failed: Could Not Backup Files" + Environment.NewLine);
UpdateOfflineContent(offlineFilePath, offlineTemplate, 95, "Upgrade Failed: Some Files Are Locked By The Hosting Environment", "bg-danger");
WriteLog(logFilePath, "Upgrade Failed: Some Files Are Locked By The Hosting Environment" + Environment.NewLine);
}
UpdateOfflineContent(offlineFilePath, offlineTemplate, 100, "Upgrade Process Finished, Reloading", success ? "" : "bg-danger");
Thread.Sleep(3000); //wait for 3 seconds to complete the upgrade process.
// bring the app back online
@ -289,45 +240,55 @@ namespace Oqtane.Updater
}
}
private static bool CanAccessFile(string filepath)
private static bool CanAccessFiles(List<string> files)
{
// ensure file is not locked by another process
// ensure files are not locked by another process
// the IIS ShutdownTimeLimit defines the duration for app shutdown (default is 90 seconds)
// websockets can delay application shutdown (ie. Blazor Server)
int retries = 60;
int sleep = 2;
int attempts = 0;
bool canAccess = true;
FileStream stream = null;
bool locked = true;
while (attempts < retries && locked)
int i = 0;
while (i < (files.Count - 1) && canAccess)
{
try
{
if (File.Exists(filepath))
{
stream = File.Open(filepath, FileMode.Open, FileAccess.Read, FileShare.None);
locked = false;
}
else
{
locked = false;
}
}
catch // file is locked by another process
{
Thread.Sleep(sleep * 1000); // wait
}
finally
{
stream?.Close();
}
attempts += 1;
}
if (locked)
{
Console.WriteLine("File Locked: " + filepath);
}
string filepath = files[i];
int attempts = 0;
bool locked = true;
return !locked;
while (attempts < retries && locked)
{
try
{
if (File.Exists(filepath))
{
stream = File.Open(filepath, FileMode.Open, FileAccess.Read, FileShare.None);
locked = false;
}
else
{
locked = false;
}
}
catch // file is locked by another process
{
Thread.Sleep(sleep * 1000); // wait
}
finally
{
stream?.Close();
}
attempts += 1;
}
if (locked)
{
canAccess = false;
Console.WriteLine("File Locked: " + filepath);
}
i += 1;
}
return canAccess;
}
private static void UpdateOfflineContent(string filePath, string contentTemplate, int progress, string status, string progressClass = "")

View File

@ -1,4 +1,4 @@
# Oqtane Framework
# Oqtane Framewo
![Oqtane](https://github.com/oqtane/framework/blob/master/oqtane.png?raw=true "Oqtane")
@ -14,14 +14,6 @@ Oqtane is being developed based on some fundamental principles which are outline
[6.1.1](https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1) was released on March 12, 2025 and is a maintenance release including 46 pull requests by 4 different contributors, pushing the total number of project commits all-time to over 6400. The Oqtane framework continues to evolve at a rapid pace to meet the needs of .NET developers.
# Try It Now!
Microsoft's Public Cloud (requires an Azure account)
[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Foqtane%2Foqtane.framework%2Fdev%2Fazuredeploy.json)
A free ASP.NET hosting account. No hidden fees. No credit card required.
[![Deploy to MonsterASP.NET](https://www.oqtane.org/files/Public/MonsterASPNET.png)](https://www.monsterasp.net/)
# Getting Started (Version 6.1.1)
**Installing using source code from the Dev/Master branch:**

View File

@ -2,24 +2,6 @@
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.2",
"parameters": {
"sqlServerName": {
"type": "string",
"metadata": {
"description": "The SQL Server name (must be unique). If you have an existing SQL Server in your resource group, you can specify its name and include its login credentials below."
}
},
"sqlAdministratorLogin": {
"type": "string",
"metadata": {
"description": "The Administrator login username for the SQL Server specified above"
}
},
"sqlAdministratorLoginPassword": {
"type": "securestring",
"metadata": {
"description": "The Administrator login password for the SQL Server specified above"
}
},
"sqlDatabaseEditionTierDtuCapacity": {
"type": "string",
"defaultValue": "Basic-Basic-5-2",
@ -44,19 +26,37 @@
"GeneralPurpose-GP_S_Gen5_2-2-250"
],
"metadata": {
"description": "The SQL Database Configuration (Edition-Tier-DTU-Capacity)"
"description": "Describes the database Edition, Tier, Dtu, Gigabytes (Edition-Tier-Dtu-Gigabytes)"
}
},
"sqlServerName": {
"type": "string",
"metadata": {
"description": "The name of the sql server. It has to be unique."
}
},
"sqlDatabaseName": {
"type": "string",
"metadata": {
"description": "The SQL Database name (must be unique)"
"description": "The name of the sql database. It has to be unique."
}
},
"sqlAdministratorLogin": {
"type": "string",
"metadata": {
"description": "The admin user of the SQL Server."
}
},
"sqlAdministratorLoginPassword": {
"type": "securestring",
"metadata": {
"description": "The password of the admin user of the SQL Server."
}
},
"BlazorWebsiteName": {
"type": "string",
"metadata": {
"description": "The App Service name for the Blazor Website (must be unique). If you use the same name as the SQL Database specified above, it will make it easier to associate the resources later."
"description": "The name of the website. It has to be unique."
}
},
"BlazorSKU": {
@ -77,12 +77,28 @@
],
"defaultValue": "B1",
"metadata": {
"description": "The App Service SKU for the Blazor Website"
"description": "The SKU for the App Service Plan"
}
},
"BlazorSKUCapacity": {
"type": "int",
"defaultValue": 1,
"maxValue": 3,
"minValue": 1,
"metadata": {
"description": "Describes plan's instance count"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
}
},
"variables": {
"hostingPlanName": "[concat('Oqtane-HostingPlan-', uniqueString(resourceGroup().id))]",
"hostingPlanName": "[concat('Oqtane-hostingplan-', uniqueString(resourceGroup().id))]",
"databaseCollation": "SQL_Latin1_General_CP1_CI_AS",
"databaseEditionTierDtuCapacity": "[split(parameters('sqlDatabaseEditionTierDtuCapacity'),'-')]",
"databaseEdition": "[variables('databaseEditionTierDtuCapacity')[0]]",
@ -99,9 +115,9 @@
// ------------------------------------------------------
{
"type": "Microsoft.Sql/servers",
"apiVersion": "2022-05-01-preview",
"apiVersion": "2022-05-01-preview", // Updated API version
"name": "[parameters('sqlServerName')]",
"location": "[resourceGroup().location]",
"location": "[parameters('location')]",
"tags": {
"displayName": "SQL Server"
},
@ -116,9 +132,9 @@
// ------------------------------------------------------
{
"type": "Microsoft.Sql/servers/databases",
"apiVersion": "2022-05-01-preview",
"apiVersion": "2022-05-01-preview", // Updated API version
"name": "[format('{0}/{1}', parameters('sqlServerName'), parameters('sqlDatabaseName'))]",
"location": "[resourceGroup().location]",
"location": "[parameters('location')]",
"tags": {
"displayName": "Database"
},
@ -153,11 +169,11 @@
]
},
// ------------------------------------------------------
// Firewall Rule
// Firewall Rule (renamed to 'AllowAllMicrosoftAzureIps')
// ------------------------------------------------------
{
"type": "Microsoft.Sql/servers/firewallRules",
"apiVersion": "2022-05-01-preview",
"apiVersion": "2022-05-01-preview", // Updated API version
"name": "[format('{0}/{1}', parameters('sqlServerName'), 'AllowAllMicrosoftAzureIps')]",
"properties": {
"endIpAddress": "0.0.0.0",
@ -172,15 +188,16 @@
// ------------------------------------------------------
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2022-03-01",
"apiVersion": "2022-03-01", // Updated API version
"name": "[variables('hostingPlanName')]",
"location": "[resourceGroup().location]",
"location": "[parameters('location')]",
"tags": {
"displayName": "Blazor"
},
"sku": {
"name": "[parameters('BlazorSKU')]",
"capacity": "1"
// If you want to auto-map to certain "tier" strings, you can do so. Here we just set the capacity:
"capacity": "[parameters('BlazorSKUCapacity')]"
},
"properties": {
"name": "[variables('hostingPlanName')]",
@ -192,11 +209,13 @@
// Web App
// ------------------------------------------------------
{
"type": "Microsoft.Web/sites",
"apiVersion": "2022-03-01",
"apiVersion": "2022-03-01", // Updated API version
"name": "[parameters('BlazorWebsiteName')]",
"kind": "app",
"location": "[resourceGroup().location]",
"type": "Microsoft.Web/sites",
"location": "[parameters('location')]",
"dependsOn": [
"[variables('hostingPlanName')]"
],
"tags": {
"[concat('hidden-related:', resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName')))]": "empty",
"displayName": "Website"
@ -205,26 +224,44 @@
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
"siteConfig": {
"webSocketsEnabled": true,
// Updated .NET version "v9.0" from second snippet
"netFrameworkVersion": "v9.0"
}
},
"dependsOn": [
"[variables('hostingPlanName')]"
],
"resources": [
// --------------------------------------------------
// ZIP Deploy
// Source Control for your Web App
// --------------------------------------------------
{
"type": "Microsoft.Web/sites/extensions",
"apiVersion": "2024-04-01",
"name": "[concat(parameters('BlazorWebsiteName'), '/ZipDeploy')]",
"properties": {
"packageUri": "https://github.com/oqtane/oqtane.framework/releases/download/v6.1.1/Oqtane.Framework.6.1.1.Install.zip"
},
"type": "sourcecontrols",
"apiVersion": "2022-03-01",
"name": "web",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', parameters('BlazorWebsiteName'))]"
]
],
"properties": {
"repoUrl": "https://github.com/oqtane/oqtane.framework.git",
"branch": "master",
"isManualIntegration": true
}
},
// --------------------------------------------------
// Connection Strings (to use FQDN)
// --------------------------------------------------
{
"type": "config",
"apiVersion": "2022-03-01",
"name": "connectionstrings",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', parameters('BlazorWebsiteName'))]"
],
"properties": {
"DefaultConnection": {
"value": "[concat('Data Source=tcp:', reference(resourceId('Microsoft.Sql/servers', parameters('sqlServerName'))).fullyQualifiedDomainName, ',1433;Initial Catalog=', parameters('sqlDatabaseName'), ';User Id=', parameters('sqlAdministratorLogin'), '@', reference(resourceId('Microsoft.Sql/servers', parameters('sqlServerName'))).fullyQualifiedDomainName, ';Password=', parameters('sqlAdministratorLoginPassword'), ';')]",
"type": "SQLAzure"
}
}
}
]
}