add support for free/paid in module, theme, translation installation

This commit is contained in:
Shaun Walker 2021-08-13 15:56:22 -04:00
parent b19cbf54e0
commit ffe724b32d
9 changed files with 177 additions and 35 deletions

View File

@ -53,13 +53,16 @@ else
</TabPanel>
<TabPanel Name="Download" ResourceKey="Download" Security="SecurityAccessLevel.Host">
<ModuleMessage Type="MessageType.Info" Message="Download one or more translations from the list below. Once you are ready click Install to complete the installation."></ModuleMessage>
<div class="container">
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
<div class="row justify-content-center mb-3">
<div class="col-sm-6">
<div class="input-group">
<select id="price" class="form-select custom-select" @onchange="(e => PriceChanged(e))">
<option value="free">@SharedLocalizer["Free"]</option>
<option value="paid">@SharedLocalizer["Paid"]</option>
</select>
<input id="search" class="form-control" placeholder="@SharedLocalizer["Search.Hint"]" @bind="@_search" />
</div>
<div class="col-sm-3">
<button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>&nbsp;
<button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button>
</div>
</div>
@ -77,7 +80,7 @@ else
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp; @SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp;@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>&nbsp;&nbsp;|&nbsp;&nbsp;@SharedLocalizer["Search.Source"]: <strong>@context.PackageUrl</strong>
</td>
<td style="vertical-align: middle;">
<button type="button" class="btn btn-primary" @onclick=@(async () => await DownloadLanguage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetLanguage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
</td>
</Row>
</Pager>
@ -108,6 +111,38 @@ else
</TabStrip>
}
@if (_productname != "")
{
<div class="app-actiondialog">
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@_productname @Localizer["License Terms"]</h5>
<button type="button" class="btn-close" aria-label="Close" @onclick="HideModal"></button>
</div>
<div class="modal-body">
<p style="height: 200px; overflow-y: scroll;">
@if (!string.IsNullOrEmpty(_license))
{
@((MarkupString)_license)
}
else
{
@Localizer["License Not Specified"]
}
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" @onclick="DownloadPackage">@Localizer["Accept"]</button>
<button type="button" class="btn btn-secondary" @onclick="HideModal">@Localizer["Cancel"]</button>
</div>
</div>
</div>
</div>
</div>
}
@code {
private ElementReference form;
private bool validated = false;
@ -118,7 +153,12 @@ else
private IEnumerable<Culture> _supportedCultures;
private IEnumerable<Culture> _availableCultures;
private List<Package> _packages;
private string _price = "free";
private string _search = "";
private string _productname = "";
private string _license = "";
private string _packageid = "";
private string _version = "";
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
@ -144,9 +184,24 @@ else
private async Task LoadTranslations()
{
_packages = await PackageService.GetPackagesAsync("translation", _search);
_packages = await PackageService.GetPackagesAsync("translation", _search, _price);
}
private async void PriceChanged(ChangeEventArgs e)
{
try
{
_price = (string)e.Value;
_search = "";
await LoadTranslations();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error On PriceChanged");
}
}
private async Task Search()
{
try
@ -211,6 +266,51 @@ else
}
}
private void HideModal()
{
_productname = "";
_license = "";
StateHasChanged();
}
private async Task GetLanguage(string packageid, string version)
{
try
{
var package = await PackageService.GetPackageAsync(packageid, version);
if (package != null)
{
_productname = package.Name;
_license = package.License.Replace("\n", "<br />");
_packageid = package.PackageId;
_version = package.Version;
}
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packageid, version);
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
}
}
private async Task DownloadPackage()
{
try
{
await PackageService.DownloadPackageAsync(_packageid, _version, "Packages");
await logger.LogInformation("Language Package {Name} {Version} Downloaded Successfully", _packageid, _version);
AddModuleMessage(Localizer["Success.Language.Download"], MessageType.Success);
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Translation {Name} {Version}", _packageid, _version);
AddModuleMessage(Localizer["Error.Language.Download"], MessageType.Error);
}
}
private async Task InstallLanguages()
{
try
@ -224,22 +324,6 @@ else
}
}
private async Task DownloadLanguage(string packageid, string version)
{
try
{
await PackageService.DownloadPackageAsync(packageid, version, "Packages");
await logger.LogInformation("Language Paclage {Name} {Version} Downloaded Successfully", packageid, version);
AddModuleMessage(Localizer["Success.Language.Download"], MessageType.Success);
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Translation {Name} {Version}", packageid, version);
AddModuleMessage(Localizer["Error.Language.Download"], MessageType.Error);
}
}
private async Task SetCultureAsync(string culture)
{
if (culture != CultureInfo.CurrentUICulture.Name)

View File

@ -14,6 +14,10 @@
<div class="row justify-content-center mb-3">
<div class="col-sm-6">
<div class="input-group">
<select id="price" class="form-select custom-select" @onchange="(e => PriceChanged(e))">
<option value="free">@SharedLocalizer["Free"]</option>
<option value="paid">@SharedLocalizer["Paid"]</option>
</select>
<input id="search" class="form-control" placeholder="@SharedLocalizer["Search.Hint"]" @bind="@_search" />
<button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button>
@ -30,7 +34,11 @@
<td>
<h3 style="display: inline;"><a href="@context.ProductUrl" target="_new">@context.Name</a></h3>&nbsp;&nbsp;by:&nbsp;&nbsp;<strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br />
@(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br />
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp; @SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp;@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>&nbsp;&nbsp;|&nbsp;&nbsp;@SharedLocalizer["Search.Source"]: <strong>@context.PackageUrl</strong>
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Source"]: <strong>@context.PackageUrl</strong>&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Price"]: <strong>@((context.Price == 0) ? "FREE" : context.Price.ToString("$#,##0.00") )</strong>
</td>
<td style="vertical-align: middle;">
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
@ -96,6 +104,7 @@
@code {
private List<Package> _packages;
private string _price = "free";
private string _search = "";
private string _productname = "";
private string _license = "";
@ -120,7 +129,7 @@
private async Task LoadModuleDefinitions()
{
var moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
_packages = await PackageService.GetPackagesAsync("module", _search);
_packages = await PackageService.GetPackagesAsync("module", _search, _price);
if (_packages != null)
{
@ -134,6 +143,21 @@
}
}
private async void PriceChanged(ChangeEventArgs e)
{
try
{
_price = (string)e.Value;
_search = "";
await LoadModuleDefinitions();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error On PriceChanged");
}
}
private async Task Search()
{
try

View File

@ -8,7 +8,7 @@
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="content" HelpText="Enter the module content" ResourceKey="Content">Content: </Label>
<Label Class="col-sm-3" For="content" HelpText="Enter The Module Content To Import" ResourceKey="Content">Content: </Label>
<div class="col-sm-9">
<textarea id="content" class="form-control" @bind="@_content" rows="5" required></textarea>

View File

@ -14,6 +14,10 @@
<div class="row justify-content-center mb-3">
<div class="col-sm-6">
<div class="input-group">
<select id="price" class="form-select custom-select" @onchange="(e => PriceChanged(e))">
<option value="free">@SharedLocalizer["Free"]</option>
<option value="paid">@SharedLocalizer["Paid"]</option>
</select>
<input id="search" class="form-control" placeholder="@SharedLocalizer["Search.Hint"]" @bind="@_search" />
<button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button>
@ -97,6 +101,7 @@
@code {
private List<Package> _packages;
private string _price = "free";
private string _search = "";
private string _productname = "";
private string _license = "";
@ -121,7 +126,7 @@
private async Task LoadThemes()
{
var themes = await ThemeService.GetThemesAsync();
_packages = await PackageService.GetPackagesAsync("theme", _search);
_packages = await PackageService.GetPackagesAsync("theme", _search, _price);
if (_packages != null)
{
@ -135,6 +140,21 @@
}
}
private async void PriceChanged(ChangeEventArgs e)
{
try
{
_price = (string)e.Value;
_search = "";
await LoadThemes();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error On PriceChanged");
}
}
private async Task Search()
{
try

View File

@ -282,4 +282,13 @@
<data name="Message.InfoRequired" xml:space="preserve">
<value>Please Provide All Required Information</value>
</data>
<data name="Free" xml:space="preserve">
<value>Free</value>
</data>
<data name="Paid" xml:space="preserve">
<value>Paid</value>
</data>
<data name="Search.Price" xml:space="preserve">
<value>price</value>
</data>
</root>

View File

@ -7,7 +7,7 @@ namespace Oqtane.Services
public interface IPackageService
{
Task<List<Package>> GetPackagesAsync(string type);
Task<List<Package>> GetPackagesAsync(string type, string search);
Task<List<Package>> GetPackagesAsync(string type, string search, string price);
Task<Package> GetPackageAsync(string packageId, string version);
Task DownloadPackageAsync(string packageId, string version, string folder);
Task InstallPackagesAsync();

View File

@ -22,12 +22,12 @@ namespace Oqtane.Services
public async Task<List<Package>> GetPackagesAsync(string type)
{
return await GetPackagesAsync(type, "");
return await GetPackagesAsync(type, "", "");
}
public async Task<List<Package>> GetPackagesAsync(string type, string search)
public async Task<List<Package>> GetPackagesAsync(string type, string search, string price)
{
return await GetJsonAsync<List<Package>>($"{Apiurl}?type={type}&search={WebUtility.UrlEncode(search)}");
return await GetJsonAsync<List<Package>>($"{Apiurl}?type={type}&search={WebUtility.UrlEncode(search)}&price={price}");
}
public async Task<Package> GetPackageAsync(string packageId, string version)

View File

@ -32,10 +32,10 @@ namespace Oqtane.Controllers
_logger = logger;
}
// GET: api/<controller>?type=x&search=y
// GET: api/<controller>?type=x&search=y&license=z
[HttpGet]
[Authorize(Roles = RoleNames.Host)]
public async Task<IEnumerable<Package>> Get(string type, string search)
public async Task<IEnumerable<Package>> Get(string type, string search, string price)
{
// get packages
List<Package> packages = new List<Package>();
@ -45,7 +45,7 @@ namespace Oqtane.Controllers
{
client.DefaultRequestHeaders.Add("Referer", HttpContext.Request.Scheme + "://" + HttpContext.Request.Host.Value);
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(Constants.PackageId, Constants.Version));
packages = await GetJson<List<Package>>(client, Constants.PackageRegistryUrl + $"/api/registry/packages/?id={_configManager.GetInstallationId()}&type={type.ToLower()}&version={Constants.Version}&search={search}");
packages = await GetJson<List<Package>>(client, Constants.PackageRegistryUrl + $"/api/registry/packages/?id={_configManager.GetInstallationId()}&type={type.ToLower()}&version={Constants.Version}&search={search}&price={price}");
}
}
return packages;

View File

@ -66,5 +66,10 @@ namespace Oqtane.Models
/// Indicates if any known security vulnerabilities exist
/// </summary>
public int Vulnerabilities { get; set; }
/// <summary>
/// The price of the package
/// </summary>
public decimal Price { get; set; }
}
}