Merge pull request #5181 from sbwalker/dev

add ability to Synchronize local modules and themes with Marketplace
This commit is contained in:
Shaun Walker 2025-03-19 14:37:50 -04:00 committed by GitHub
commit 09f5e158dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 174 additions and 117 deletions

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 reference url of the module" ResourceKey="ReferenceUrl">Reference Url: </Label>
<Label Class="col-sm-3" For="url" HelpText="The url of the module" ResourceKey="Url">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" />
@((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">
<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">
<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,52 +172,75 @@ 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

@ -55,7 +55,7 @@
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="url" HelpText="The reference url of the theme" ResourceKey="ReferenceUrl">Reference Url: </Label>
<Label Class="col-sm-3" For="url" HelpText="The url of the theme" ResourceKey="Url">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" />
@((MarkupString)"&nbsp;")
<ActionLink Action="Create" Text="Create Theme" ResourceKey="CreateTheme" Class="btn btn-secondary" />
<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>
<Pager Items="@_themes">
<Header>
@ -173,4 +173,27 @@ 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

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

View File

@ -159,4 +159,13 @@
<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="ReferenceUrl.Text" xml:space="preserve">
<value>Reference Url: </value>
<data name="Url.Text" xml:space="preserve">
<value>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="ReferenceUrl.HelpText" xml:space="preserve">
<value>The reference url of the theme</value>
<data name="Url.HelpText" xml:space="preserve">
<value>The url of the theme</value>
</data>
<data name="Contact.HelpText" xml:space="preserve">
<value>The contact for the theme</value>

View File

@ -159,4 +159,13 @@
<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

@ -90,33 +90,26 @@ 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)
if (package != null && bool.Parse(install))
{
if (bool.Parse(install))
using (var httpClient = new HttpClient())
{
using (var httpClient = new HttpClient())
var folder = Path.Combine(_environment.ContentRootPath, Constants.PackagesFolder);
var response = await httpClient.GetAsync(package.PackageUrl).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
var folder = Path.Combine(_environment.ContentRootPath, Constants.PackagesFolder);
var response = await httpClient.GetAsync(package.PackageUrl).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
string filename = packageid + "." + version + ".nupkg";
using (var fileStream = new FileStream(Path.Combine(Constants.PackagesFolder, filename), FileMode.Create, FileAccess.Write, FileShare.None))
{
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);
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, "Package {PackageId}.{Version} Is Not Registered In The Marketplace", packageid, version);
}
}
return package;
}