Compare commits

..

108 Commits

Author SHA1 Message Date
2e116489c8 Merge pull request #3208 from oqtane/master
Merge pull request #3207 from oqtane/dev
2023-08-29 13:54:50 -04:00
a94daa1216 Merge pull request #3207 from oqtane/dev
4.0.3 Release
2023-08-29 13:54:31 -04:00
ad57d665cc Merge pull request #3205 from sbwalker/dev
filter deleted pages and modules on the server
2023-08-28 17:15:45 -04:00
db2c42f0f4 filter deleted pages and modules on the server 2023-08-28 17:15:31 -04:00
b713cf86c7 Merge pull request #3204 from sbwalker/dev
fix page template update logic
2023-08-28 16:12:47 -04:00
11d02ccf44 fix page template update logic 2023-08-28 16:12:32 -04:00
54f3c8beac Merge pull request #3203 from sbwalker/dev
documentation for ReleaseVersions property
2023-08-28 15:27:47 -04:00
2ed51bf1f3 documentation for ReleaseVersions property 2023-08-28 15:27:33 -04:00
9f859f024c Merge pull request #3202 from sbwalker/dev
improve solution related to module installation issue where ModuleDefinition Version property was not being populated correctly (#3175)
2023-08-28 15:20:29 -04:00
d47175df8a improve solution related to module installation issue where ModuleDefinition Version property was not being populated correctly (#3175) 2023-08-28 15:20:09 -04:00
d6a80d36b1 Merge pull request #3200 from sbwalker/dev
abstract namespace specification to template.json so that module and theme templates can use their own naming conventions
2023-08-28 09:03:26 -04:00
a857e3f31e abstract namespace specification to template.json so that module and theme templates can use their own naming conventions 2023-08-28 09:03:05 -04:00
918dfb05cd Merge pull request #3199 from sbwalker/dev
prepare for 4.0.3 release
2023-08-28 07:58:49 -04:00
f22e0c4384 prepare for 4.0.3 release 2023-08-28 07:58:33 -04:00
daa08626ae Merge pull request #3198 from sbwalker/dev
support ~ notation for license urls
2023-08-28 07:49:22 -04:00
45592e6acd support ~ notation for license urls 2023-08-28 07:49:06 -04:00
6944eb3392 Merge pull request #3197 from sbwalker/dev
allow module or theme License property to be a Url
2023-08-27 08:57:50 -04:00
85f20aca58 allow module or theme License property to be a Url 2023-08-27 08:57:30 -04:00
ca8d8b3587 Merge pull request #3196 from sbwalker/dev
move password requirement localization back to Client project
2023-08-27 08:43:43 -04:00
74921a0686 move password requirement localization back to Client project 2023-08-27 08:43:23 -04:00
be4296f288 Merge pull request #3195 from sbwalker/dev
include only distinct items which have package names
2023-08-25 17:44:39 -04:00
836de56276 include only distinct items which have package names 2023-08-25 17:44:27 -04:00
ef60ac3836 Merge pull request #3194 from sbwalker/dev
optimize package update queries
2023-08-25 15:42:58 -04:00
bdd1ba05e8 optimize package update queries 2023-08-25 15:42:45 -04:00
f0a22d5517 Merge pull request #3193 from sbwalker/dev
remove Module Creator in favor of using Create Module in Module Management
2023-08-25 13:49:08 -04:00
417c8d7874 remove Module Creator in favor of using Create Module in Module Management 2023-08-25 13:48:51 -04:00
5a111cdb2a Merge pull request #3192 from sbwalker/dev
fix #3174 - display accurate password complexity requirements (this is now implemented in registration, user profiles, and user management - add/edit)
2023-08-25 13:31:22 -04:00
ef2f779f71 fix #3174 - display accurate password complexity requirements (this is now implemented in registration, user profiles, and user management - add/edit) 2023-08-25 13:31:02 -04:00
f0fd0db7bb Merge pull request #3191 from sbwalker/dev
simplified method names
2023-08-25 12:30:01 -04:00
b4ab45d2e7 simplified method names 2023-08-25 12:29:46 -04:00
73c4bcee30 Merge pull request #3185 from leigh-pointer/SchedularTimeControls
Fix for Schedular Allows incorrect Time format #3184
2023-08-25 10:54:13 -04:00
d4daf098e1 Merge pull request #3190 from sbwalker/dev
resolve #3189 - make path a querystring parameter
2023-08-25 09:49:22 -04:00
95de1fff69 resolve #3189 - make path a querystring parameter 2023-08-25 09:49:07 -04:00
96480b4382 Override and 2 new functions 2023-08-25 12:09:16 +02:00
14ae553557 Merge pull request #3188 from sbwalker/dev
remove unecessary NotMapped attribute
2023-08-24 16:42:30 -04:00
4de809e275 remove unecessary NotMapped attribute 2023-08-24 16:41:58 -04:00
a383382529 Merge pull request #3187 from sbwalker/dev
include PackageRegistryUrl in System Info
2023-08-24 16:39:51 -04:00
d2b3061ed9 include PackageRegistryUrl in System Info 2023-08-24 16:39:38 -04:00
073b10929a Fix for Schedular Allows incorrect Time format #3184
Modified code to accept correct time types.
2023-08-24 18:37:33 +02:00
6a2cfcab34 Merge pull request #3183 from leigh-pointer/IconResources
Update IconRescources
2023-08-24 11:38:09 -04:00
00c85ae7d3 Update IconResources.resx 2023-08-24 17:03:24 +02:00
d1b1e88389 Update IconRescources 2023-08-24 15:46:24 +02:00
5679ff5df9 Merge pull request #3182 from sbwalker/dev
add refresh button to module and theme installation page
2023-08-24 09:33:46 -04:00
3f28b39da0 add refresh button to module and theme installation page 2023-08-24 09:33:32 -04:00
ee21536742 Merge pull request #3181 from sbwalker/dev
reverse InputList Dictionary usage
2023-08-24 08:50:45 -04:00
6fafeedeb9 reverse InputList Dictionary usage 2023-08-24 08:50:33 -04:00
d86c53858e Merge pull request #3180 from sbwalker/dev
added defensive logic to resolve regression issue caused by #3175
2023-08-24 08:10:06 -04:00
0a22f80942 added defensive logic to resolve regression issue caused by #3175 2023-08-24 08:09:53 -04:00
fb247197e8 Merge pull request #3179 from sbwalker/dev
Allow InputList component to be localizable and support multiple instances on a page. Implement icon localization in Page Add/Edit using IconResources.resx
2023-08-23 16:43:36 -04:00
261ed05fa3 Allow InputList component to be localizable and support multiple instances on a page. Implement icon localization in Page Add/Edit using IconResources.resx 2023-08-23 16:43:14 -04:00
f5ce48a7c2 Merge pull request #3178 from sbwalker/dev
move icon loading reflection logic to server
2023-08-23 15:25:54 -04:00
82d128974c move icon loading reflection logic to server 2023-08-23 15:25:39 -04:00
34ae731959 Merge pull request #3169 from leigh-pointer/InputList
Add InputList control to select values from a dictionary of string
2023-08-23 14:29:38 -04:00
030a92d048 Merge pull request #3171 from vnetonline/fix-3163
[FIX] Added the ability to clear the Message in control panel
2023-08-23 14:29:28 -04:00
a327358b7a Merge pull request #3177 from sbwalker/dev
add support for background color padding during image resizing
2023-08-23 10:05:46 -04:00
9bd078d3e9 add support for background color padding during image resizing 2023-08-23 10:05:27 -04:00
412405e22c Merge pull request #3176 from sbwalker/dev
improve UX for Extend license option
2023-08-23 09:39:04 -04:00
2b20b34a74 improve UX for Extend license option 2023-08-23 09:38:48 -04:00
f269a07463 Merge pull request #3175 from sbwalker/dev
resolve module installation issue where ModuleDefinition Version property was not being populated correctly
2023-08-23 09:24:56 -04:00
d9948e8ee0 resolve module installation issue where ModuleDefinition Version property was not being populated correctly 2023-08-23 09:24:36 -04:00
ddb2fe4b03 Update Edit.razor
Moved Icon to the end of control
2023-08-23 07:49:50 +02:00
471f7184fb Added Icon Preview
Added an icon preview to the icon selecting control
2023-08-23 07:47:35 +02:00
92ea5da358 [FIX] Added the ability to clear the Message in control panel on open or close
This fix is related to issue #3163
2023-08-23 10:13:16 +10:00
09f1d3ca15 Add InputList control to select values from a dictionary of string
The control accepts a dictionary of string that can be searched using an input control.
Pages Add and Edit have implemented the control to render the list of Icons found in the Oqtane Shared namespace.
2023-08-22 09:50:59 +02:00
3729b8eac2 Merge pull request #3161 from rcpacheco/rcp_show_passwd_dbs
Update database installers to show/hide passwd
2023-08-21 11:02:29 -04:00
ca5f345414 Update database installers to show/hide passwd 2023-08-18 13:25:32 -06:00
15cf1e8a53 Merge pull request #3160 from sbwalker/dev
fix #3157 - provide support for asynchronous scheduled jobs
2023-08-18 13:34:52 -04:00
da74b0ece1 fix #3157 - provide support for asynchronous scheduled jobs 2023-08-18 13:34:38 -04:00
f50fe6f22f Merge pull request #3158 from sbwalker/dev
add support for * ImageSizes for folders
2023-08-18 10:55:25 -04:00
542eec2a9e add support for * ImageSizes for folders 2023-08-18 10:55:10 -04:00
4c5460fc9e Merge pull request #3156 from sbwalker/dev
migrate LocalizerFactory logic from SiteRouter to ModuleTitle component
2023-08-17 08:23:30 -04:00
394b8f1ce6 migrate LocalizerFactory logic from SiteRouter to ModuleTitle component 2023-08-17 08:23:17 -04:00
abeeda1a2b Merge pull request #3155 from sbwalker/dev
rollback #3125 and localize module component Title using LocalizerFactory
2023-08-17 07:56:58 -04:00
9e6ea3f486 rollback #3125 and localize module component Title using LocalizerFactory 2023-08-17 07:56:39 -04:00
2777a0946c Merge pull request #3154 from vnetonline/enhance-expando-object
[ENHANCE] - Change to ExpandoObject instead of an Anonymous Object
2023-08-17 07:47:57 -04:00
7f43c2659a Merge pull request #3153 from leigh-pointer/UserSort
Update the TH with link-primary Decoration classes
2023-08-17 07:46:39 -04:00
a4fa11c881 [ENHANCE] - Change to ExpandoObject instead of an Anonymous Object
Anonymous Object are not able to be used across assemblies however ExpandoObject is refer to #3145
2023-08-17 10:45:38 +10:00
8230e51c05 Update the TH with link-primary Decoration classes
This emphasizes that the header is clickable.
2023-08-16 19:09:12 +02:00
6e62d4791c Update the TH with link-primary Decoration classes
This emphasizes that the header is clickable.
2023-08-16 19:06:59 +02:00
ce8abdb8cd Merge pull request #3152 from sbwalker/dev
modify column size to prevent text wrapping
2023-08-16 12:25:53 -04:00
941bb7edaa modify column size to prevent text wrapping 2023-08-16 12:25:39 -04:00
1acbdf8b9e Merge pull request #3150 from alikoli/dev
Fix missing translations part 3
2023-08-15 15:51:58 -04:00
530804c847 Merge pull request #3151 from sbwalker/dev
In FileManager fix the id handling for the progressinfo and progressbar and include OnSelect events on Upload and Delete. Add missing backend implementation for AddFileAsync
2023-08-15 15:51:32 -04:00
b00b426e9c fix the id handling for the progressinfo and progressbar, include OnSelect events on Upload and Delete, add missing backend implementation for AddFileAsync 2023-08-15 15:50:35 -04:00
128f1753c0 Remove resx backups 2023-08-15 16:17:23 +02:00
1922629d27 Merge branch 'oqtane:dev' into dev 2023-08-15 16:12:21 +02:00
283a03a7cc Fix missing translations part 3 2023-08-15 16:09:59 +02:00
cdab26a97b Merge pull request #3146 from HonesDK/dev
Fixed localization in AuditInfo
2023-08-15 08:03:29 -04:00
5d3311c46b Merge pull request #3147 from sbwalker/dev
Notification job must convert \n to <br /> now that IsNodyHtml is set to True
2023-08-14 17:03:43 -04:00
7cbc21671b Notification job must convert \n to <br /> now that IsNodyHtml is set to True 2023-08-14 17:03:21 -04:00
d14d820e25 Merge branch 'dev' of https://github.com/HonesDK/oqtane.framework into dev 2023-08-14 16:54:41 +02:00
b567d02df2 Fixed localization in AuditInfo 2023-08-14 16:52:30 +02:00
dbb58541f2 Revert "Fixed modified by localization, and added DateTimeFormat to localization"
This reverts commit 4a8bcc6f71.
2023-08-14 16:51:21 +02:00
5a42caf4f8 Merge pull request #3143 from sbwalker/dev
add ability to get user based on username or email address
2023-08-13 08:35:24 -04:00
c2acd010ce add ability to get user based on username or email address 2023-08-13 08:35:03 -04:00
4a8bcc6f71 Fixed modified by localization, and added DateTimeFormat to localization 2023-08-13 10:11:29 +02:00
89e4dc7a08 Merge pull request #3141 from sbwalker/dev
change "price" to "from" to reflect multiple options
2023-08-12 10:31:56 -04:00
c344eedb12 change "price" to "from" to reflect multiple options 2023-08-12 10:31:44 -04:00
0aeb1a62b9 Merge pull request #3137 from sbwalker/dev
fix #3134 improve parsing of headcontent to handle space delimiters
2023-08-11 15:53:43 -04:00
316e0f5a68 fix #3134 improve parsing of headcontent to handle space delimiters 2023-08-11 15:53:32 -04:00
6f9314f76f Merge pull request #3133 from alikoli/dev
Fix missing translations part 2
2023-08-11 15:33:21 -04:00
6ef21795ba fix heading 2023-08-10 02:13:31 +02:00
e44855493e fix heading 2023-08-10 02:12:36 +02:00
101a3c8af5 Revert launchSettings 2023-08-10 02:03:46 +02:00
8fbbbce4ec Fix missing translations part 2 2023-08-10 01:52:46 +02:00
1de8bb850f Update README.md 2023-08-09 08:54:45 -04:00
25d09186fe Update README.md 2023-08-09 08:34:35 -04:00
9552b7c6f9 Update README.md 2023-08-09 08:34:13 -04:00
124 changed files with 1898 additions and 801 deletions

View File

@ -0,0 +1,14 @@
namespace Oqtane
{
/// <summary>
/// Dummy class used to collect shared resource strings for this application
/// </summary>
/// <remarks>
/// This class is mostly used with IStringLocalizer and IHtmlLocalizer interfaces.
/// The class must reside at the project root.
/// </remarks>
public class IconResources
{
}
}

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Installer.Controls
@implements Oqtane.Interfaces.IDatabaseConfigControl
@inject IStringLocalizer<SharedResources> SharedLocalizer
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="server" HelpText="Enter the database server" ResourceKey="Server">Server:</Label>
@ -28,7 +29,10 @@
<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">
<input id="pwd" type="password" class="form-control" @bind="@_pwd" autocomplete="new-password" />
<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>
@ -38,6 +42,13 @@
private string _database = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm");
private string _uid = String.Empty;
private string _pwd = String.Empty;
private string _passwordType = "password";
private string _togglePassword = string.Empty;
protected override void OnInitialized()
{
_togglePassword = SharedLocalizer["ShowPassword"];
}
public string GetConnectionString()
{
@ -55,4 +66,18 @@
return connectionString;
}
private void TogglePassword()
{
if (_passwordType == "password")
{
_passwordType = "text";
_togglePassword = SharedLocalizer["HidePassword"];
}
else
{
_passwordType = "password";
_togglePassword = SharedLocalizer["ShowPassword"];
}
}
}

View File

@ -1,6 +1,7 @@
@namespace Oqtane.Installer.Controls
@implements Oqtane.Interfaces.IDatabaseConfigControl
@inject IStringLocalizer<PostgreSQLConfig> 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" ResourceKey="Server">Server:</Label>
@ -40,7 +41,10 @@
<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">
<input id="pwd" type="password" class="form-control" @bind="@_pwd" autocomplete="new-password" />
<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>
}
@ -52,6 +56,13 @@
private string _security = "integrated";
private string _uid = String.Empty;
private string _pwd = String.Empty;
private string _passwordType = "password";
private string _togglePassword = string.Empty;
protected override void OnInitialized()
{
_togglePassword = SharedLocalizer["ShowPassword"];
}
public string GetConnectionString()
{
@ -80,4 +91,18 @@
return connectionString;
}
private void TogglePassword()
{
if (_passwordType == "password")
{
_passwordType = "text";
_togglePassword = SharedLocalizer["HidePassword"];
}
else
{
_passwordType = "password";
_togglePassword = SharedLocalizer["ShowPassword"];
}
}
}

View File

@ -35,7 +35,10 @@
<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">
<input id="pwd" type="password" class="form-control" @bind="@_pwd" autocomplete="new-password" />
<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>
}
@ -67,9 +70,16 @@
private string _security = "integrated";
private string _uid = String.Empty;
private string _pwd = String.Empty;
private string _passwordType = "password";
private string _togglePassword = string.Empty;
private string _encryption = "false";
private string _trustservercertificate = "false";
protected override void OnInitialized()
{
_togglePassword = SharedLocalizer["ShowPassword"];
}
public string GetConnectionString()
{
var connectionString = String.Empty;
@ -92,4 +102,18 @@
return connectionString;
}
private void TogglePassword()
{
if (_passwordType == "password")
{
_passwordType = "text";
_togglePassword = SharedLocalizer["HidePassword"];
}
else
{
_passwordType = "password";
_togglePassword = SharedLocalizer["ShowPassword"];
}
}
}

View File

@ -66,12 +66,6 @@
public override string Title => "File Management";
protected override void OnParametersSet()
{
base.OnParametersSet();
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
}
protected override async Task OnInitializedAsync()
{
try

View File

@ -48,7 +48,7 @@
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="imagesizes" HelpText="Enter a list of image sizes which can be generated dynamically from uploaded images (ie. 200x200,x200,200x)" ResourceKey="ImageSizes">Image Sizes: </Label>
<Label Class="col-sm-3" For="imagesizes" HelpText="Enter a list of image sizes which can be generated dynamically from uploaded images (ie. 200x200,400x400). Use * to indicate the folder supports all image sizes." ResourceKey="ImageSizes">Image Sizes: </Label>
<div class="col-sm-9">
<input id="imagesizes" class="form-control" @bind="@_imagesizes" maxlength="512" />
</div>
@ -112,11 +112,6 @@
public override string Title => "Folder Management";
protected override void OnParametersSet()
{
base.OnParametersSet();
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
}
protected override async Task OnInitializedAsync()
{
try

View File

@ -56,7 +56,7 @@
<input id="starting" type="date" class="form-control" @bind="@_startDate" />
</div>
<div class="col">
<input id="starting" type="text" class="form-control" placeholder="hh:mm" @bind="@_startTime" />
<input id="starting" type="time" class="form-control" placeholder="hh:mm" @bind="@_startTime" />
</div>
</div>
</div>
@ -69,7 +69,7 @@
<input id="ending" type="date" class="form-control" @bind="@_endDate" />
</div>
<div class="col">
<input id="ending" type="text" class="form-control" placeholder="hh:mm" @bind="@_endTime" />
<input id="ending" type="time" class="form-control" placeholder="hh:mm" @bind="@_endTime" />
</div>
</div>
</div>
@ -82,7 +82,7 @@
<input id="next" type="date" class="form-control" @bind="@_nextDate" />
</div>
<div class="col">
<input id="next" type="text" class="form-control" placeholder="hh:mm" @bind="@_nextTime" />
<input id="next" type="time" class="form-control" placeholder="hh:mm" @bind="@_nextTime" />
</div>
</div>
</div>
@ -106,12 +106,12 @@
private string _interval = string.Empty;
private string _frequency = string.Empty;
private DateTime? _startDate = null;
private string _startTime = string.Empty;
private DateTime? _startTime = null;
private DateTime? _endDate = null;
private string _endTime = string.Empty;
private DateTime? _endTime = null;
private string _retentionHistory = string.Empty;
private DateTime? _nextDate = null;
private string _nextTime = string.Empty;
private DateTime? _nextTime = null;
private string createdby;
private DateTime createdon;
private string modifiedby;
@ -132,10 +132,13 @@
_isEnabled = job.IsEnabled.ToString();
_interval = job.Interval.ToString();
_frequency = job.Frequency;
(_startDate, _startTime) = Utilities.UtcAsLocalDateAndTime(job.StartDate);
(_endDate, _endTime) = Utilities.UtcAsLocalDateAndTime(job.EndDate);
_startDate = Utilities.UtcAsLocalDate(job.StartDate);
_startTime = Utilities.UtcAsLocalDateTime(job.StartDate);
_endDate = Utilities.UtcAsLocalDate(job.EndDate);
_endTime = Utilities.UtcAsLocalDateTime(job.EndDate);
_retentionHistory = job.RetentionHistory.ToString();
(_nextDate, _nextTime) = Utilities.UtcAsLocalDateAndTime(job.NextExecution);
_nextDate = Utilities.UtcAsLocalDate(job.NextExecution);
_nextTime = Utilities.UtcAsLocalDateTime(job.NextExecution);
createdby = job.CreatedBy;
createdon = job.CreatedOn;
modifiedby = job.ModifiedBy;

View File

@ -1,203 +0,0 @@
@namespace Oqtane.Modules.Admin.ModuleCreator
@inherits ModuleBase
@using System.Text.RegularExpressions
@inject NavigationManager NavigationManager
@inject IModuleDefinitionService ModuleDefinitionService
@inject IModuleService ModuleService
@inject ISettingService SettingService
@inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@if (string.IsNullOrEmpty(_moduledefinitionname) && _templates != null)
{
<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="owner" HelpText="Enter the name of the organization who is developing this module. It should not contain spaces or punctuation." ResourceKey="OwnerName">Owner Name: </Label>
<div class="col-sm-9">
<input id="owner" class="form-control" @bind="@_owner" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="module" HelpText="Enter a name for this module. It should not contain spaces or punctuation." ResourceKey="ModuleName">Module Name: </Label>
<div class="col-sm-9">
<input id="module" class="form-control" @bind="@_module" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="description" HelpText="Enter a short description for the module" ResourceKey="Description">Description: </Label>
<div class="col-sm-9">
<textarea id="description" class="form-control" @bind="@_description" rows="3" ></textarea>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="template" HelpText="Select a module template. Templates are located in the wwwroot/Modules/Templates folder on the server." ResourceKey="Template">Template: </Label>
<div class="col-sm-9">
<select id="template" class="form-select" @onchange="(e => TemplateChanged(e))" required>
<option value="-">&lt;@Localizer["Template.Select"]&gt;</option>
@foreach (Template template in _templates)
{
<option value="@template.Name">@template.Title</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
<div class="col-sm-9">
<select id="reference" class="form-select" @bind="@_reference" required>
@foreach (string version in _versions)
{
if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0)
{
<option value="@(version)">@(version)</option>
}
}
<option value="local">@SharedLocalizer["LocalVersion"]</option>
</select>
</div>
</div>
@if (!string.IsNullOrEmpty(_location))
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="location" HelpText="Location where the module will be created" ResourceKey="Location">Location: </Label>
<div class="col-sm-9">
<input id="module" class="form-control" @bind="@_location" readonly />
</div>
</div>
}
</div>
</form>
<button type="button" class="btn btn-success" @onclick="CreateModule">@Localizer["Module.Create"]</button>
}
else
{
<button type="button" class="btn btn-success" @onclick="ActivateModule">@Localizer["Module.Activate"]</button>
}
@code {
private ElementReference form;
private bool validated = false;
private string _moduledefinitionname = string.Empty;
private string _owner = string.Empty;
private string _module = string.Empty;
private string _description = string.Empty;
private List<Template> _templates;
private string _template = "-";
private string[] _versions;
private string _reference = "local";
private string _minversion = "2.0.0";
private string _location = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override void OnInitialized()
{
_moduledefinitionname = SettingService.GetSetting(ModuleState.Settings, "ModuleDefinitionName", "");
if (string.IsNullOrEmpty(_moduledefinitionname))
{
AddModuleMessage(Localizer["Info.Module.Creator"], MessageType.Info);
}
else
{
AddModuleMessage(Localizer["Info.Module.Activate"], MessageType.Info);
}
}
protected override async Task OnParametersSetAsync()
{
try
{
_templates = await ModuleDefinitionService.GetModuleDefinitionTemplatesAsync();
_versions = Constants.ReleaseVersions.Split(',').Where(item => Version.Parse(item).CompareTo(Version.Parse("2.0.0")) >= 0).ToArray();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Module Creator");
}
}
private async Task CreateModule()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{
try
{
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
{
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
settings = SettingService.SetSetting(settings, "ModuleDefinitionName", moduleDefinition.ModuleDefinitionName);
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
GetLocation();
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success);
}
else
{
AddModuleMessage(Localizer["Message.Require.ValidName"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Creating Module");
}
}
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
private async Task ActivateModule()
{
try
{
if (!string.IsNullOrEmpty(_moduledefinitionname))
{
Module module = await ModuleService.GetModuleAsync(ModuleState.ModuleId);
module.ModuleDefinitionName = _moduledefinitionname;
await ModuleService.UpdateModuleAsync(module);
NavigationManager.NavigateTo(NavigateUrl(), true);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Activating Module");
}
}
private bool IsValid(string name)
{
// must contain letters, underscores and digits and first character must be letter or underscore
return !string.IsNullOrEmpty(name) && name.ToLower() != "module" && !name.ToLower().Contains("oqtane") && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
}
private void TemplateChanged(ChangeEventArgs e)
{
_template = (string)e.Value;
_minversion = "2.0.0";
if (_template != "-")
{
var template = _templates.FirstOrDefault(item => item.Name == _template);
_minversion = template.Version;
}
GetLocation();
}
private void GetLocation()
{
_location = string.Empty;
if (_owner != "" && _module != "" && _template != "-")
{
var template = _templates.FirstOrDefault(item => item.Name == _template);
_location = template.Location + _owner + ".Module." + _module;
}
StateHasChanged();
}
}

View File

@ -1,17 +0,0 @@
using Oqtane.Documentation;
using Oqtane.Models;
namespace Oqtane.Modules.Admin.ModuleCreator
{
[PrivateApi("Mark this as private, since it's not very useful in the public docs")]
public class ModuleInfo : IModule
{
public ModuleDefinition ModuleDefinition => new ModuleDefinition
{
Name = "Module Creator",
Description = "Enables software developers to quickly create modules by automating many of the initial module creation tasks",
Version = "1.0.0",
Categories = "Developer"
};
}
}

View File

@ -8,7 +8,7 @@
@inject IStringLocalizer<SharedResources> SharedLocalizer
<TabStrip>
<TabPanel Name="Download" ResourceKey="Download">
<TabPanel Name="Download" ResourceKey="Download" Heading="Download">
<div class="row justify-content-center mb-3">
<div class="text-center">
<div class="form-check form-check-inline">
@ -28,6 +28,7 @@
<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>
<button type="button" class="btn btn-primary ms-2" @onclick="Refresh"><span class="@Icons.Reload" aria-hidden="true"></span></button>
</div>
</div>
</div>
@ -56,22 +57,22 @@
</select>
</div>
</div>
<Pager Format="Grid" Items="@_packages" DisplayPages="1" PageSize="9" Toolbar="Both" Class="container-fluid px-0" RowClass="row g-0" ColumnClass="col-lg-4 col-md-6">
<Pager Format="Grid" Items="@_packages" DisplayPages="1" PageSize="9" Toolbar="Both" Class="container-fluid px-0" RowClass="row g-0" ColumnClass="col-lg-4 col-md-6" CurrentPage="@_page.ToString()" OnPageChange="OnPageChange">
<Row>
<div class="m-2 p-2 d-flex justify-content-center">
<div class="container-fluid px-0">
<div class="row g-0">
<div class="col-6">
@if (context.LogoFileId != null)
<div class="row g-0 mb-2">
<div class="col-4">
@if (context.LogoUrl != null)
{
<img src="@GetLogo(context.LogoFileId.Value)" class="img-fluid" alt="@context.Name" />
<img src="@context.LogoUrl" class="img-fluid" alt="@context.Name" />
}
else
{
<img src="/package.png" class="img-fluid" alt="@context.Name" />
}
</div>
<div class="col-6 text-end">
<div class="col-8 text-end">
<small>@SharedLocalizer["Search.Version"]:</small> <strong>@context.Version</strong>
<br /><small>@SharedLocalizer["Search.Downloads"]:</small> <strong>@(String.Format("{0:n0}", context.Downloads))</strong>
<br /><small>@SharedLocalizer["Search.Released"]:</small> <strong>@context.ReleaseDate.ToString("MM/dd/yyyy")</strong>
@ -88,7 +89,7 @@
@(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br />
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
{
<small>@SharedLocalizer["Search.Price"]:</small> <strong>@context.Price.Value.ToString("$#,##0.00")</strong>
<small>@SharedLocalizer["From"]:</small> <strong>@context.Price.Value.ToString("$#,##0.00")</strong>
@((MarkupString)(context.TrialPeriod > 0 ? " <strong>(" + context.TrialPeriod + " Day Trial)</strong>" : ""))
}
<br />
@ -96,7 +97,7 @@
{
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
}
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl) && string.IsNullOrEmpty(context.PackageUrl))
{
<a class="btn btn-success ms-2" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@SharedLocalizer["Buy"]</a>
}
@ -162,6 +163,7 @@
@code {
private bool _initialized = false;
private int _page = 1;
private List<Package> _packages;
private string _price = "free";
private string _sort = "popularity";
@ -208,13 +210,6 @@
HideProgressIndicator();
}
private string GetLogo(int fileid)
{
var url = ImageUrl(fileid, 100, 100);
url = (!string.IsNullOrEmpty(PageState.Alias.Path)) ? url.Substring(PageState.Alias.Path.Length + 1) : url;
return Constants.PackageRegistryUrl + url;
}
private async void PriceChanged(string price)
{
_price = price;
@ -223,28 +218,25 @@
}
private async Task Search()
{
try
{
await LoadModuleDefinitions();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error On Search");
}
}
private async Task Reset()
{
try
{
_page = 1;
_search = "";
await LoadModuleDefinitions();
}
catch (Exception ex)
private async Task Refresh()
{
await logger.LogError(ex, "Error On Reset");
await LoadModuleDefinitions();
}
private void OnPageChange(int page)
{
_page = page;
}
private async void SortChanged(ChangeEventArgs e)

View File

@ -115,7 +115,8 @@
{
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
{
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
var template = _templates.FirstOrDefault(item => item.Name == _template);
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference, ModuleDefinitionName = template.Namespace };
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
GetLocation();
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success);
@ -160,8 +161,14 @@
if (_owner != "" && _module != "" && _template != "-")
{
var template = _templates.FirstOrDefault(item => item.Name == _template);
_location = template.Location + _owner + "." + _module;
if (!string.IsNullOrEmpty(template.Namespace))
{
_location = template.Location + template.Namespace.Replace("[Owner]", _owner).Replace("[Module]", _module);
}
else
{
_location = template.Location + _owner + ".Module." + _module;
}
}
StateHasChanged();
}

View File

@ -12,7 +12,7 @@
@if (_initialized)
{
<TabStrip>
<TabPanel Name="Definition" ResourceKey="Definition">
<TabPanel Name="Definition" ResourceKey="Definition" Heading="Definition">
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container">
<div class="row mb-1 align-items-center">
@ -85,13 +85,14 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="license" HelpText="The module license terms" ResourceKey="License">License: </Label>
<div class="col-sm-9">
@if (_license.StartsWith("http") || _license.StartsWith("/") || _license.StartsWith("~"))
{
<a href="@_license.Replace("~", PageState?.Alias.BaseUrl + "/Modules/" + Utilities.GetTypeName(_moduledefinitionname))" class="btn btn-info" style="text-decoration: none !important" target="_new">@Localizer["View License"]</a>
}
else
{
<textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="runtimes" HelpText="The Blazor runtimes which this module supports" ResourceKey="Runtimes">Runtimes: </Label>
<div class="col-sm-9">
<input id="runtimes" class="form-control" @bind="@_runtimes" disabled />
}
</div>
</div>
</div>
@ -103,7 +104,7 @@
<br />
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
</TabPanel>
<TabPanel Name="Permissions" ResourceKey="Permissions">
<TabPanel Name="Permissions" ResourceKey="Permissions" Heading="Permissions">
<div class="container">
<div class="row mb-1 align-items-center">
<PermissionGrid EntityName="@EntityNames.ModuleDefinition" PermissionNames="@PermissionNames.Utilize" PermissionList="@_permissions" @ref="_permissionGrid" />
@ -113,7 +114,7 @@
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
</TabPanel>
<TabPanel Name="Translations" ResourceKey="Translations">
<TabPanel Name="Translations" ResourceKey="Translations" Heading="Translations">
@if (_languages != null && _languages.Count > 0)
{
<Pager Items="@_languages">
@ -216,7 +217,6 @@
private string _url = "";
private string _contact = "";
private string _license = "";
private string _runtimes = "";
private List<Permission> _permissions = null;
private string _createdby;
private DateTime _createdon;
@ -252,7 +252,6 @@
_url = moduleDefinition.Url;
_contact = moduleDefinition.Contact;
_license = moduleDefinition.License;
_runtimes = moduleDefinition.Runtimes;
_permissions = moduleDefinition.PermissionList;
_createdby = moduleDefinition.CreatedBy;
_createdon = moduleDefinition.CreatedOn;

View File

@ -37,7 +37,7 @@ else
</div>
</div>
<Pager Items="@_moduleDefinitions.Where(item => item.Categories.Contains(_category))">
<Pager Items="@_moduleDefinitions">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
@ -99,6 +99,7 @@ else
}
@code {
private List<ModuleDefinition> _allModuleDefinitions;
private List<ModuleDefinition> _moduleDefinitions;
private List<Package> _packages;
private List<string> _categories = new List<string>();
@ -110,9 +111,9 @@ else
{
try
{
_moduleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
_packages = await PackageService.GetPackagesAsync("module");
_categories = _moduleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
_allModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
_categories = _allModuleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
await LoadModuleDefinitions();
}
catch (Exception ex)
{
@ -124,6 +125,13 @@ else
}
}
private async Task LoadModuleDefinitions()
{
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(_category)).ToList();
var list = _moduleDefinitions.Where(item => !string.IsNullOrEmpty(item.PackageName)).Select(item => item.PackageName).Distinct().ToList();
_packages = await PackageService.GetPackagesAsync(list);
}
private string PurchaseLink(string packagename)
{
string link = "";
@ -134,10 +142,13 @@ else
{
if (package.ExpiryDate != null && package.ExpiryDate.Value.Date != DateTime.MaxValue.Date)
{
link += "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span><br />";
if (!string.IsNullOrEmpty(package.PaymentUrl))
if (string.IsNullOrEmpty(package.PaymentUrl))
{
link += "&nbsp;&nbsp;<a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + SharedLocalizer["Extend"] + "</a>";
link = "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span>";
}
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>";
}
}
}
@ -202,9 +213,9 @@ else
}
}
private void CategoryChanged(ChangeEventArgs e)
private async Task CategoryChanged(ChangeEventArgs e)
{
_category = (string)e.Value;
StateHasChanged();
await LoadModuleDefinitions();
}
}

View File

@ -22,12 +22,6 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Title => "Export Content";
protected override void OnParametersSet()
{
base.OnParametersSet();
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
}
private async Task ExportModule()
{
try

View File

@ -28,11 +28,6 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Title => "Import Content";
protected override void OnParametersSet()
{
base.OnParametersSet();
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
}
private async Task ImportModule()
{
validated = true;

View File

@ -130,11 +130,6 @@
private string modifiedby;
private DateTime modifiedon;
protected override void OnParametersSet()
{
base.OnParametersSet();
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
}
protected override void OnInitialized()
{
_module = ModuleState.ModuleDefinition.Name;

View File

@ -3,6 +3,7 @@
@inject NavigationManager NavigationManager
@inject IPageService PageService
@inject IThemeService ThemeService
@inject ISystemService SystemService
@inject IStringLocalizer<Add> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@ -111,8 +112,11 @@
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="icon" HelpText="Optionally provide an icon class name for this page which will be displayed in the site navigation" ResourceKey="Icon">Icon: </Label>
<div class="col-sm-9">
<input id="icon" class="form-control" @bind="@_icon" />
<div class="col-sm-8">
<InputList Value="@_icon" ValueChanged="IconChanged" DataList="@_icons" ResourceKey="Icon" ResourceType="@_iconresources" />
</div>
<div class="col-sm-1">
<i class="@_icon"></i>
</div>
</div>
<div class="row mb-1 align-items-center">
@ -227,6 +231,8 @@
private RenderFragment ThemeSettingsComponent { get; set; }
private bool _refresh = false;
protected Page _parent = null;
protected Dictionary<string, string> _icons;
private string _iconresources = "";
protected override async Task OnInitializedAsync()
{
@ -241,6 +247,8 @@
_parentid = _parent.PageId.ToString();
}
}
_icons = await SystemService.GetIconsAsync();
_iconresources = typeof(IconResources).FullName;
// if admin or user has edit access to parent page
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin) || (_parent != null && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, _parent.PermissionList)))
@ -482,4 +490,8 @@
NavigationManager.NavigateTo(NavigateUrl());
}
}
private void IconChanged(string NewIcon)
{
_icon = NewIcon;
}
}

View File

@ -5,6 +5,7 @@
@inject IPageService PageService
@inject IPageModuleService PageModuleService
@inject IThemeService ThemeService
@inject ISystemService SystemService
@inject IStringLocalizer<Edit> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@ -123,8 +124,11 @@
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="icon" HelpText="Optionally provide an icon class name for this page which will be displayed in the site navigation" ResourceKey="Icon">Icon: </Label>
<div class="col-sm-9">
<input id="icon" class="form-control" @bind="@_icon" maxlength="50" />
<div class="col-sm-8">
<InputList Value="@_icon" ValueChanged="IconChanged" DataList="@_icons" ResourceKey="Icon" ResourceType="@_iconresources" />
</div>
<div class="col-sm-1">
<i class="@_icon"></i>
</div>
</div>
<div class="row mb-1 align-items-center">
@ -312,6 +316,8 @@
private bool _refresh = false;
protected Page _page = null;
protected Page _parent = null;
protected Dictionary<string, string> _icons;
private string _iconresources = "";
protected override async Task OnInitializedAsync()
{
@ -320,6 +326,8 @@
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
_pageId = Int32.Parse(PageState.QueryString["id"]);
_page = await PageService.GetPageAsync(_pageId);
_icons = await SystemService.GetIconsAsync();
_iconresources = Utilities.GetFullTypeName(typeof(IconResources).AssemblyQualifiedName);
if (_page != null && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, _page.PermissionList))
{
@ -660,4 +668,9 @@
}
}
private void IconChanged(string NewIcon)
{
_icon = NewIcon;
}
}

View File

@ -16,7 +16,7 @@
<ModuleMessage Message="@Localizer["Info.Registration.Exists"]" Type="MessageType.Info" />
</Authorized>
<NotAuthorized>
<ModuleMessage Message="@_passwordconstruction" Type="MessageType.Info" />
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container">
<div class="row mb-1 align-items-center">
@ -69,6 +69,7 @@ else
}
@code {
private string _passwordrequirements;
private string _username = string.Empty;
private ElementReference form;
private bool validated = false;
@ -79,44 +80,16 @@ else
private string _email = string.Empty;
private string _displayname = string.Empty;
//Password construction
private string _minimumlength;
private string _uniquecharacters;
private bool _requiredigit;
private bool _requireupper;
private bool _requirelower;
private bool _requirepunctuation;
private string _passwordconstruction;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
protected override async Task OnInitializedAsync()
{
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
_minimumlength = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredLength", "6");
_uniquecharacters = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", "1");
_requiredigit = bool.Parse(SettingService.GetSetting(settings, "IdentityOptions:Password:RequireDigit", "true"));
_requireupper = bool.Parse(SettingService.GetSetting(settings, "IdentityOptions:Password:RequireUppercase", "true"));
_requirelower = bool.Parse(SettingService.GetSetting(settings, "IdentityOptions:Password:RequireLowercase", "true"));
_requirepunctuation = bool.Parse(SettingService.GetSetting(settings, "IdentityOptions:Password:RequireNonAlphanumeric", "true"));
// Replace the placeholders with the actual values of the variables
string digitRequirement = _requiredigit ? Localizer["Password.DigitRequirement"] + ", " : "";
string uppercaseRequirement = _requireupper ? Localizer["Password.UppercaseRequirement"] + ", " : "";
string lowercaseRequirement = _requirelower ? Localizer["Password.LowercaseRequirement"] + ", " : "";
string punctuationRequirement = _requirepunctuation ? Localizer["Password.PunctuationRequirement"] + ", " : "";
// Replace the placeholders with the actual values of the variables
string passwordValidationCriteriaTemplate = Localizer["Password.ValidationCriteria"];
_passwordconstruction = Localizer["Info.Registration.InvalidEmail"] + ". " + string.Format(passwordValidationCriteriaTemplate,
_minimumlength, _uniquecharacters, digitRequirement, uppercaseRequirement, lowercaseRequirement, punctuationRequirement);
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
}
protected override void OnParametersSet()
{
_togglepassword = SharedLocalizer["ShowPassword"];
}
private async Task Register()

View File

@ -257,7 +257,7 @@
<td>
@if (_aliasid == -1)
{
<ActionDialog Action="Delete" OnClick="@(async () => await DeleteAlias(context))" ResourceKey="DeleteModule" Class="btn btn-danger" Header="Delete Alias" Message="@string.Format(Localizer["Confirm.Alias.Delete", context.Name])" />
<ActionDialog Action="Delete" OnClick="@(async () => await DeleteAlias(context))" ResourceKey="DeleteAlias" Class="btn btn-danger" Header="Delete Alias" Message="@string.Format(Localizer["Confirm.Alias.Delete", context.Name])" />
}
</td>
<td>@context.Name</td>

View File

@ -133,12 +133,9 @@
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="packageservice" HelpText="Specify If The Package Service Is Enabled For Installing Modules, Themes, And Translations" ResourceKey="PackageService">Enable Package Service? </Label>
<Label Class="col-sm-3" For="packageregistryurl" HelpText="Specify The Package Manager Service For Installing Modules, Themes, And Translations. If This Field Is Blank It Means The Package Manager Service Is Disabled For This Installation." ResourceKey="PackageManager">Package Manager: </Label>
<div class="col-sm-9">
<select id="packageservice" class="form-select" @bind="@_packageservice">
<option value="true">@SharedLocalizer["True"]</option>
<option value="false">@SharedLocalizer["False"]</option>
</select>
<input id="packageregistryurl" class="form-control" @bind="@_packageregistryurl" />
</div>
</div>
</div>
@ -182,7 +179,7 @@
private string _logginglevel = string.Empty;
private string _notificationlevel = string.Empty;
private string _swagger = string.Empty;
private string _packageservice = string.Empty;
private string _packageregistryurl = string.Empty;
private string _log = string.Empty;
@ -213,7 +210,7 @@
_logginglevel = systeminfo["Logging:LogLevel:Default"].ToString();
_notificationlevel = systeminfo["Logging:LogLevel:Notify"].ToString();
_swagger = systeminfo["UseSwagger"].ToString();
_packageservice = systeminfo["PackageService"].ToString();
_packageregistryurl = systeminfo["PackageRegistryUrl"].ToString();
}
systeminfo = await SystemService.GetSystemInfoAsync("log");
@ -232,7 +229,7 @@
settings.Add("Logging:LogLevel:Default", _logginglevel);
settings.Add("Logging:LogLevel:Notify", _notificationlevel);
settings.Add("UseSwagger", _swagger);
settings.Add("PackageService", _packageservice);
settings.Add("PackageRegistryUrl", _packageregistryurl);
await SystemService.UpdateSystemInfoAsync(settings);
AddModuleMessage(Localizer["Success.UpdateConfig.Restart"], MessageType.Success);
}

View File

@ -24,10 +24,11 @@
<div class="row justify-content-center mb-3">
<div class="col">
<div class="input-group">
<span class="input-group-text">Product</span>
<span class="input-group-text">@Localizer["Product"]</span>
<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>
<button type="button" class="btn btn-primary ms-2" @onclick="Refresh"><span class="@Icons.Reload" aria-hidden="true"></span></button>
</div>
</div>
</div>
@ -60,18 +61,18 @@
<Row>
<div class="m-2 p-2 d-flex justify-content-center">
<div class="container-fluid px-0">
<div class="row g-0">
<div class="col-6">
@if (context.LogoFileId != null)
<div class="row g-0 mb-2">
<div class="col-4">
@if (context.LogoUrl != null)
{
<img src="@GetLogo(context.LogoFileId.Value)" class="img-fluid" alt="@context.Name" />
<img src="@context.LogoUrl" class="img-fluid" alt="@context.Name" />
}
else
{
<img src="/package.png" class="img-fluid" alt="@context.Name" />
}
</div>
<div class="col-6 text-end">
<div class="col-8 text-end">
<small>@SharedLocalizer["Search.Version"]:</small> <strong>@context.Version</strong>
<br /><small>@SharedLocalizer["Search.Downloads"]:</small> <strong>@(String.Format("{0:n0}", context.Downloads))</strong>
<br /><small>@SharedLocalizer["Search.Released"]:</small> <strong>@context.ReleaseDate.ToString("MM/dd/yyyy")</strong>
@ -90,7 +91,7 @@
@(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br />
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
{
<small>@SharedLocalizer["Search.Price"]:</small> <strong>@context.Price.Value.ToString("$#,##0.00")</strong>
<small>@SharedLocalizer["From"]:</small> <strong>@context.Price.Value.ToString("$#,##0.00")</strong>
@((MarkupString)(context.TrialPeriod > 0 ? " <strong>(" + context.TrialPeriod + " Day Trial)</strong>" : ""))
}
<br />
@ -98,7 +99,7 @@
{
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
}
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl) && string.IsNullOrEmpty(context.PackageUrl))
{
<a class="btn btn-success ms-2" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@SharedLocalizer["Buy"]</a>
}
@ -164,6 +165,7 @@
@code {
private bool _initialized = false;
private int _page = 1;
private List<Package> _packages;
private string _price = "free";
private string _sort = "popularity";
@ -210,13 +212,6 @@
HideProgressIndicator();
}
private string GetLogo(int fileid)
{
var url = ImageUrl(fileid, 100, 100);
url = (!string.IsNullOrEmpty(PageState.Alias.Path)) ? url.Substring(PageState.Alias.Path.Length + 1) : url;
return Constants.PackageRegistryUrl + url;
}
private async void PriceChanged(string price)
{
_price = price;
@ -225,28 +220,25 @@
}
private async Task Search()
{
try
{
await LoadThemes();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error On Search");
}
}
private async Task Reset()
{
try
{
_page = 1;
_search = "";
await LoadThemes();
}
catch (Exception ex)
private async Task Refresh()
{
await logger.LogError(ex, "Error On Reset");
await LoadThemes();
}
private void OnPageChange(int page)
{
_page = page;
}
private async void SortChanged(ChangeEventArgs e)

View File

@ -102,7 +102,8 @@
{
if (IsValid(_owner) && IsValid(_theme) && _owner != _theme && _template != "-")
{
var theme = new Theme { Owner = _owner, Name = _theme, Template = _template, Version = _reference };
var template = _templates.FirstOrDefault(item => item.Name == _template);
var theme = new Theme { Owner = _owner, Name = _theme, Template = _template, Version = _reference, ThemeName = template.Namespace };
theme = await ThemeService.CreateThemeAsync(theme);
GetLocation();
AddModuleMessage(string.Format(Localizer["Success.Theme.Create"], NavigateUrl("admin/system")), MessageType.Success);
@ -142,8 +143,14 @@
if (_owner != "" && _theme != "" && _template != "-")
{
var template = _templates.FirstOrDefault(item => item.Name == _template);
if (!string.IsNullOrEmpty(template.Namespace))
{
_location = template.Location + template.Namespace.Replace("[Owner]", _owner).Replace("[Theme]", _theme);
}
else
{
_location = template.Location + _owner + ".Theme." + _theme;
}
}
StateHasChanged();
}

View File

@ -68,7 +68,14 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="license" HelpText="The license of the theme" ResourceKey="License">License: </Label>
<div class="col-sm-9">
@if (_license.StartsWith("http") || _license.StartsWith("/") || _license.StartsWith("~"))
{
<a href="@_license.Replace("~", PageState?.Alias.BaseUrl + "/Themes/" + Utilities.GetTypeName(_themeName))" class="btn btn-info" style="text-decoration: none !important" target="_new">@Localizer["View License"]</a>
}
else
{
<textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea>
}
</div>
</div>
</div>

View File

@ -29,7 +29,7 @@ else
<th>&nbsp;</th>
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ThemeId.ToString())" ResourceKey="EditModule" /></td>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ThemeId.ToString())" ResourceKey="EditTheme" /></td>
<td>
@if (context.AssemblyName != Constants.ClientId)
{
@ -79,7 +79,8 @@ else
try
{
_themes = await ThemeService.GetThemesAsync();
_packages = await PackageService.GetPackagesAsync("theme");
var list = _themes.Where(item => !string.IsNullOrEmpty(item.PackageName)).Select(item => item.PackageName).Distinct().ToList();
_packages = await PackageService.GetPackagesAsync(list);
}
catch (Exception ex)
{
@ -101,10 +102,13 @@ else
{
if (package.ExpiryDate != null && package.ExpiryDate.Value.Date != DateTime.MaxValue.Date)
{
link += "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span><br />";
if (!string.IsNullOrEmpty(package.PaymentUrl))
if (string.IsNullOrEmpty(package.PaymentUrl))
{
link += "&nbsp;&nbsp;<a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + SharedLocalizer["Extend"] + "</a>";
link = "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span>";
}
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>";
}
}
}

View File

@ -42,12 +42,6 @@
public override string Title => "Send Notification";
protected override void OnParametersSet()
{
base.OnParametersSet();
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
}
private async Task Send()
{
try

View File

@ -23,6 +23,7 @@ else
<TabPanel Name="Identity" ResourceKey="Identity">
@if (profiles != null && settings != null)
{
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="username" HelpText="Your username. Note that this field can not be modified." ResourceKey="Username"></Label>
@ -267,6 +268,7 @@ else
<br /><br />
@code {
private string _passwordrequirements;
private string username = string.Empty;
private string _password = string.Empty;
private string _passwordtype = "password";
@ -293,6 +295,8 @@ else
{
try
{
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
_togglepassword = SharedLocalizer["ShowPassword"];
if (PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:TwoFactor"]))

View File

@ -110,11 +110,6 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
public override string Title => "View Notification";
protected override void OnParametersSet()
{
base.OnParametersSet();
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
}
protected override async Task OnInitializedAsync()
{
try

View File

@ -12,6 +12,7 @@
<TabPanel Name="Identity" ResourceKey="Identity">
@if (profiles != null)
{
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="username" HelpText="A unique username for a user. Note that this field can not be modified once it is saved." ResourceKey="Username"></Label>
@ -94,6 +95,7 @@
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
@code {
private string _passwordrequirements;
private string username = string.Empty;
private string _password = string.Empty;
private string _passwordtype = "password";
@ -111,6 +113,7 @@
{
try
{
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
_togglepassword = SharedLocalizer["ShowPassword"];
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
settings = new Dictionary<string, string>();

View File

@ -21,6 +21,7 @@ else
<TabPanel Name="Identity" ResourceKey="Identity">
@if (profiles != null)
{
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="username" HelpText="The unique username for a user. Note that this field can not be modified." ResourceKey="Username"></Label>
@ -149,6 +150,7 @@ else
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon" DeletedBy="@deletedby" DeletedOn="@deletedon"></AuditInfo>
@code {
private string _passwordrequirements;
private int userid;
private string username = string.Empty;
private string _password = string.Empty;
@ -183,6 +185,7 @@ else
{
if (PageState.QueryString.ContainsKey("id"))
{
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
_togglepassword = SharedLocalizer["ShowPassword"];
profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
userid = Int32.Parse(PageState.QueryString["id"]);

View File

@ -35,9 +35,9 @@ else
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th class="app-sort-th" @onclick="@(() => SortTable("Username"))">@Localizer["Username"]<i class="@(SetSortIcon("Username"))"></i></th>
<th class="app-sort-th" @onclick="@(() => SortTable("DisplayName"))">@Localizer["Name"]<i class="@(SetSortIcon("DisplayName"))"></i></th>
<th class="app-sort-th" @onclick="@(() => SortTable("LastLoginOn"))">@Localizer["LastLoginOn"]<i class="@(SetSortIcon("LastLoginOn"))"></i></th>
<th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("Username"))">@Localizer["Username"]<i class="@(SetSortIcon("Username"))"></i></th>
<th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("DisplayName"))">@Localizer["Name"]<i class="@(SetSortIcon("DisplayName"))"></i></th>
<th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("LastLoginOn"))">@Localizer["LastLoginOn"]<i class="@(SetSortIcon("LastLoginOn"))"></i></th>
</Header>
<Row>
<td>

View File

@ -1,6 +1,7 @@
@namespace Oqtane.Modules.Controls
@using System.Text.Json
@inherits LocalizableComponent
@inject IStringLocalizer<SharedResources> SharedLocalizer
@if (_visible)
{
@ -20,7 +21,7 @@
{
<button type="button" class="@Class" @onclick="Confirm">@((MarkupString)_iconSpan) @Text</button>
}
<button type="button" class="btn btn-secondary" @onclick="DisplayModal">@Localize("Cancel")</button>
<button type="button" class="btn btn-secondary" @onclick="DisplayModal">@SharedLocalizer["Cancel"]</button>
</div>
</div>
</div>

View File

@ -64,12 +64,12 @@
if (!String.IsNullOrEmpty(ModifiedBy))
{
_text += $" {Localizer["by"]} <b>{ModifiedBy}</b>";
_text += $" {Localizer["By"]} <b>{ModifiedBy}</b>";
}
if (ModifiedOn != null)
{
_text += $" {Localizer["on"]} <b>{ModifiedOn.Value.ToString(DateTimeFormat)}</b>";
_text += $" {Localizer["On"]} <b>{ModifiedOn.Value.ToString(DateTimeFormat)}</b>";
}
_text += "</p>";

View File

@ -157,6 +157,15 @@
[Parameter]
public EventCallback<int> OnDelete { get; set; } // optional - executes a method in the calling component when a file is deleted
protected override void OnInitialized()
{
// create unique id for component
_guid = Guid.NewGuid().ToString("N");
_fileinputid = "FileInput_" + _guid;
_progressinfoid = "ProgressInfo_" + _guid;
_progressbarid = "ProgressBar_" + _guid;
}
protected override async Task OnParametersSetAsync()
{
// packages folder is a framework folder for uploading installable nuget packages
@ -223,12 +232,6 @@
await GetFiles();
// create unique id for component
_guid = Guid.NewGuid().ToString("N");
_fileinputid = "FileInput_" + _guid;
_progressinfoid = "ProgressInfo_" + _guid;
_progressbarid = "ProgressBar_" + _guid;
_initialized = true;
}
@ -398,8 +401,8 @@
// reset progress indicators
if (ShowProgress)
{
await interop.SetElementAttribute(_guid + "ProgressInfo", "style", "display: none;");
await interop.SetElementAttribute(_guid + "ProgressBar", "style", "display: none;");
await interop.SetElementAttribute(_progressinfoid, "style", "display: none;");
await interop.SetElementAttribute(_progressbarid, "style", "display: none;");
}
else
{
@ -435,6 +438,7 @@
{
FileId = file.FileId;
await SetImage();
await OnSelect.InvokeAsync(FileId);
await OnUpload.InvokeAsync(FileId);
}
await GetFiles();
@ -481,6 +485,7 @@
await GetFiles();
FileId = -1;
await SetImage();
await OnSelect.InvokeAsync(FileId);
StateHasChanged();
}
catch (Exception ex)

View File

@ -0,0 +1,50 @@
@namespace Oqtane.Modules.Controls
@inherits LocalizableComponent
<input type="text" value="@Value" list="@_id" class="form-control" @onchange="(e => OnChange(e))" />
<datalist id="@_id" value="@Value">
@foreach(var kvp in DataList)
{
if (!string.IsNullOrEmpty(kvp.Value))
{
<option value="@kvp.Key">@Localize(kvp.Value, kvp.Value)</option>
}
else
{
<option value="@kvp.Key">@Localize(kvp.Key, kvp.Key)</option>
}
}
</datalist>
@code {
private string _id;
[Parameter]
public string Value { get; set; }
[EditorRequired]
[Parameter]
public Dictionary<string, string> DataList { get; set; }
[EditorRequired]
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
protected override void OnInitialized()
{
// create unique id for component
_id = "DataList_" + Guid.NewGuid().ToString("N");
}
protected void OnChange(ChangeEventArgs e)
{
if (!string.IsNullOrEmpty(e.Value.ToString()))
{
Value = e.Value.ToString();
if (ValueChanged.HasDelegate)
{
ValueChanged.InvokeAsync(Value);
}
}
}
}

View File

@ -5,7 +5,7 @@
<div class="row" style="margin-bottom: 50px;">
<div class="col">
<TabStrip>
<TabPanel Name="Rich" Heading="Rich Text Editor">
<TabPanel Name="Rich" Heading="Rich Text Editor" ResourceKey="RichTextEditor">
@if (_richfilemanager)
{
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />

View File

@ -68,12 +68,6 @@
private List<Models.HtmlText> _htmltexts;
private string _view = "";
protected override void OnParametersSet()
{
base.OnParametersSet();
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
}
protected override async Task OnInitializedAsync()
{
try

View File

@ -9,6 +9,7 @@ using Oqtane.UI;
using System.Collections.Generic;
using Microsoft.JSInterop;
using System.Linq;
using System.Dynamic;
namespace Oqtane.Modules
{
@ -280,13 +281,17 @@ namespace Oqtane.Modules
public void SetModuleTitle(string title)
{
var obj = new { PageModuleId = ModuleState.PageModuleId, Title = title };
dynamic obj = new ExpandoObject();
obj.PageModuleId = ModuleState.PageModuleId;
obj.Title = title;
SiteState.Properties.ModuleTitle = obj;
}
public void SetModuleVisibility(bool visible)
{
var obj = new { PageModuleId = ModuleState.PageModuleId, Visible = visible };
dynamic obj = new ExpandoObject();
obj.PageModuleId = ModuleState.PageModuleId;
obj.Visible = visible;
SiteState.Properties.ModuleVisibility = obj;
}

View File

@ -4,7 +4,7 @@
<TargetFramework>net7.0</TargetFramework>
<OutputType>Exe</OutputType>
<Configurations>Debug;Release</Configurations>
<Version>4.0.2</Version>
<Version>4.0.3</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/v4.0.2</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.3</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane</RootNamespace>

View File

@ -0,0 +1,789 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Icon.AccountLogin" xml:space="preserve">
<value>Account Login</value>
</data>
<data name="Icon.AccountLogout" xml:space="preserve">
<value>Account Logout</value>
</data>
<data name="Icon.ActionRedo" xml:space="preserve">
<value>Action Redo</value>
</data>
<data name="Icon.ActionUndo" xml:space="preserve">
<value>Action Undo</value>
</data>
<data name="Icon.AlignCenter" xml:space="preserve">
<value>Align Center</value>
</data>
<data name="Icon.AlignLeft" xml:space="preserve">
<value>Align Left</value>
</data>
<data name="Icon.AlignRight" xml:space="preserve">
<value>Align Right</value>
</data>
<data name="Icon.Aperture" xml:space="preserve">
<value>Aperture</value>
</data>
<data name="Icon.ArrowBottom" xml:space="preserve">
<value>Arrow Bottom</value>
</data>
<data name="Icon.ArrowCircleBottom" xml:space="preserve">
<value>Arrow Circle Bottom</value>
</data>
<data name="Icon.ArrowCircleLeft" xml:space="preserve">
<value>Arrow Circle Left</value>
</data>
<data name="Icon.ArrowCircleRight" xml:space="preserve">
<value>Arrow Circle Right</value>
</data>
<data name="Icon.ArrowCircleTop" xml:space="preserve">
<value>Arrow Circle Top</value>
</data>
<data name="Icon.ArrowLeft" xml:space="preserve">
<value>Arrow Left</value>
</data>
<data name="Icon.ArrowRight" xml:space="preserve">
<value>Arrow Right</value>
</data>
<data name="Icon.ArrowThickBottom" xml:space="preserve">
<value>Arrow Thick Bottom</value>
</data>
<data name="Icon.ArrowThickLeft" xml:space="preserve">
<value>Arrow Thick Left</value>
</data>
<data name="Icon.ArrowThickRight" xml:space="preserve">
<value>Arrow Thick Right</value>
</data>
<data name="Icon.ArrowThickTop" xml:space="preserve">
<value>Arrow Thick Top</value>
</data>
<data name="Icon.ArrowTop" xml:space="preserve">
<value>Arrow Top</value>
</data>
<data name="Icon.AudioSpectrum" xml:space="preserve">
<value>Audio Spectrum</value>
</data>
<data name="Icon.Audio" xml:space="preserve">
<value>Audio</value>
</data>
<data name="Icon.Badge" xml:space="preserve">
<value>Badge</value>
</data>
<data name="Icon.Ban" xml:space="preserve">
<value>Ban</value>
</data>
<data name="Icon.BarChart" xml:space="preserve">
<value>Bar Chart</value>
</data>
<data name="Icon.Basket" xml:space="preserve">
<value>Basket</value>
</data>
<data name="Icon.BatteryEmpty" xml:space="preserve">
<value>Battery Empty</value>
</data>
<data name="Icon.BatteryFull" xml:space="preserve">
<value>Battery Full</value>
</data>
<data name="Icon.Beaker" xml:space="preserve">
<value>Beaker</value>
</data>
<data name="Icon.Bell" xml:space="preserve">
<value>Bell</value>
</data>
<data name="Icon.Bluetooth" xml:space="preserve">
<value>Bluetooth</value>
</data>
<data name="Icon.Bold" xml:space="preserve">
<value>Bold</value>
</data>
<data name="Icon.Bolt" xml:space="preserve">
<value>Bolt</value>
</data>
<data name="Icon.Book" xml:space="preserve">
<value>Book</value>
</data>
<data name="Icon.Bookmark" xml:space="preserve">
<value>Bookmark</value>
</data>
<data name="Icon.Box" xml:space="preserve">
<value>Box</value>
</data>
<data name="Icon.Briefcase" xml:space="preserve">
<value>Briefcase</value>
</data>
<data name="Icon.BritishPound" xml:space="preserve">
<value>British Pound</value>
</data>
<data name="Icon.Browser" xml:space="preserve">
<value>Browser</value>
</data>
<data name="Icon.Brush" xml:space="preserve">
<value>Brush</value>
</data>
<data name="Icon.Bug" xml:space="preserve">
<value>Bug</value>
</data>
<data name="Icon.Bullhorn" xml:space="preserve">
<value>Bullhorn</value>
</data>
<data name="Icon.Calculator" xml:space="preserve">
<value>Calculator</value>
</data>
<data name="Icon.Calendar" xml:space="preserve">
<value>Calendar</value>
</data>
<data name="Icon.CameraSlr" xml:space="preserve">
<value>Camera Slr</value>
</data>
<data name="Icon.CaretBottom" xml:space="preserve">
<value>Caret Bottom</value>
</data>
<data name="Icon.CaretLeft" xml:space="preserve">
<value>Caret Left</value>
</data>
<data name="Icon.CaretRight" xml:space="preserve">
<value>Caret Right</value>
</data>
<data name="Icon.CaretTop" xml:space="preserve">
<value>Caret Top</value>
</data>
<data name="Icon.Cart" xml:space="preserve">
<value>Cart</value>
</data>
<data name="Icon.Chat" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Icon.Check" xml:space="preserve">
<value>Check</value>
</data>
<data name="Icon.ChevronBottom" xml:space="preserve">
<value>Chevron Bottom</value>
</data>
<data name="Icon.ChevronLeft" xml:space="preserve">
<value>Chevron Left</value>
</data>
<data name="Icon.ChevronRight" xml:space="preserve">
<value>Chevron Right</value>
</data>
<data name="Icon.ChevronTop" xml:space="preserve">
<value>Chevron Top</value>
</data>
<data name="Icon.CircleCheck" xml:space="preserve">
<value>Circle Check</value>
</data>
<data name="Icon.CircleX" xml:space="preserve">
<value>Circle X</value>
</data>
<data name="Icon.Clipboard" xml:space="preserve">
<value>Clipboard</value>
</data>
<data name="Icon.Clock" xml:space="preserve">
<value>Clock</value>
</data>
<data name="Icon.CloudDownload" xml:space="preserve">
<value>Cloud Download</value>
</data>
<data name="Icon.CloudUpload" xml:space="preserve">
<value>Cloud Upload</value>
</data>
<data name="Icon.Cloud" xml:space="preserve">
<value>Cloud</value>
</data>
<data name="Icon.Cloudy" xml:space="preserve">
<value>Cloudy</value>
</data>
<data name="Icon.Code" xml:space="preserve">
<value>Code</value>
</data>
<data name="Icon.Cog" xml:space="preserve">
<value>Cog</value>
</data>
<data name="Icon.CollapseDown" xml:space="preserve">
<value>Collapse Down</value>
</data>
<data name="Icon.CollapseLeft" xml:space="preserve">
<value>Collapse Left</value>
</data>
<data name="Icon.CollapseRight" xml:space="preserve">
<value>Collapse Right</value>
</data>
<data name="Icon.CollapseUp" xml:space="preserve">
<value>Collapse Up</value>
</data>
<data name="Icon.Command" xml:space="preserve">
<value>Command</value>
</data>
<data name="Icon.CommentSquare" xml:space="preserve">
<value>Comment Square</value>
</data>
<data name="Icon.Compass" xml:space="preserve">
<value>Compass</value>
</data>
<data name="Icon.Contrast" xml:space="preserve">
<value>Contrast</value>
</data>
<data name="Icon.Copywriting" xml:space="preserve">
<value>Copywriting</value>
</data>
<data name="Icon.CreditCard" xml:space="preserve">
<value>Credit Card</value>
</data>
<data name="Icon.Crop" xml:space="preserve">
<value>Crop</value>
</data>
<data name="Icon.Dashboard" xml:space="preserve">
<value>Dashboard</value>
</data>
<data name="Icon.DataTransferDownload" xml:space="preserve">
<value>Data Transfer Download</value>
</data>
<data name="Icon.DataTransferUpload" xml:space="preserve">
<value>Data Transfer Upload</value>
</data>
<data name="Icon.Delete" xml:space="preserve">
<value>Delete</value>
</data>
<data name="Icon.Dial" xml:space="preserve">
<value>Dial</value>
</data>
<data name="Icon.Document" xml:space="preserve">
<value>Document</value>
</data>
<data name="Icon.Dollar" xml:space="preserve">
<value>Dollar</value>
</data>
<data name="Icon.DoubleQuoteSansLeft" xml:space="preserve">
<value>Double Quote Sans Left</value>
</data>
<data name="Icon.DoubleQuoteSansRight" xml:space="preserve">
<value>Double Quote Sans Right</value>
</data>
<data name="Icon.DoubleQuoteSerifLeft" xml:space="preserve">
<value>Double Quote Serif Left</value>
</data>
<data name="Icon.DoubleQuoteSerifRight" xml:space="preserve">
<value>Double Quote Serif Right</value>
</data>
<data name="Icon.Droplet" xml:space="preserve">
<value>Droplet</value>
</data>
<data name="Icon.Eject" xml:space="preserve">
<value>Eject</value>
</data>
<data name="Icon.Elevator" xml:space="preserve">
<value>Elevator</value>
</data>
<data name="Icon.Ellipses" xml:space="preserve">
<value>Ellipses</value>
</data>
<data name="Icon.EnvelopeClosed" xml:space="preserve">
<value>Envelope Closed</value>
</data>
<data name="Icon.EnvelopeOpen" xml:space="preserve">
<value>Envelope Open</value>
</data>
<data name="Icon.Euro" xml:space="preserve">
<value>Euro</value>
</data>
<data name="Icon.Excerpt" xml:space="preserve">
<value>Excerpt</value>
</data>
<data name="Icon.ExpandDown" xml:space="preserve">
<value>Expand Down</value>
</data>
<data name="Icon.ExpandLeft" xml:space="preserve">
<value>Expand Left</value>
</data>
<data name="Icon.ExpandRight" xml:space="preserve">
<value>Expand Right</value>
</data>
<data name="Icon.ExpandUp" xml:space="preserve">
<value>Expand Up</value>
</data>
<data name="Icon.ExternalLink" xml:space="preserve">
<value>External Link</value>
</data>
<data name="Icon.Eye" xml:space="preserve">
<value>Eye</value>
</data>
<data name="Icon.Eyedropper" xml:space="preserve">
<value>Eyedropper</value>
</data>
<data name="Icon.File" xml:space="preserve">
<value>File</value>
</data>
<data name="Icon.Fire" xml:space="preserve">
<value>Fire</value>
</data>
<data name="Icon.Flag" xml:space="preserve">
<value>Flag</value>
</data>
<data name="Icon.Flash" xml:space="preserve">
<value>Flash</value>
</data>
<data name="Icon.Folder" xml:space="preserve">
<value>Folder</value>
</data>
<data name="Icon.Fork" xml:space="preserve">
<value>Fork</value>
</data>
<data name="Icon.FullscreenEnter" xml:space="preserve">
<value>Fullscreen Enter</value>
</data>
<data name="Icon.FullscreenExit" xml:space="preserve">
<value>Fullscreen Exit</value>
</data>
<data name="Icon.Globe" xml:space="preserve">
<value>Globe</value>
</data>
<data name="Icon.Graph" xml:space="preserve">
<value>Graph</value>
</data>
<data name="Icon.GridFourUp" xml:space="preserve">
<value>Grid Four Up</value>
</data>
<data name="Icon.GridThreeUp" xml:space="preserve">
<value>Grid Three Up</value>
</data>
<data name="Icon.GridTwoUp" xml:space="preserve">
<value>Grid Two Up</value>
</data>
<data name="Icon.HardDrive" xml:space="preserve">
<value>Hard Drive</value>
</data>
<data name="Icon.Header" xml:space="preserve">
<value>Header</value>
</data>
<data name="Icon.Headphones" xml:space="preserve">
<value>Headphones</value>
</data>
<data name="Icon.Heart" xml:space="preserve">
<value>Heart</value>
</data>
<data name="Icon.Home" xml:space="preserve">
<value>Home</value>
</data>
<data name="Icon.Image" xml:space="preserve">
<value>Image</value>
</data>
<data name="Icon.Inbox" xml:space="preserve">
<value>Inbox</value>
</data>
<data name="Icon.Infinity" xml:space="preserve">
<value>Infinity</value>
</data>
<data name="Icon.Info" xml:space="preserve">
<value>Info</value>
</data>
<data name="Icon.Italic" xml:space="preserve">
<value>Italic</value>
</data>
<data name="Icon.JustifyCenter" xml:space="preserve">
<value>Justify Center</value>
</data>
<data name="Icon.JustifyLeft" xml:space="preserve">
<value>Justify Left</value>
</data>
<data name="Icon.JustifyRight" xml:space="preserve">
<value>Justify Right</value>
</data>
<data name="Icon.Key" xml:space="preserve">
<value>Key</value>
</data>
<data name="Icon.Laptop" xml:space="preserve">
<value>Laptop</value>
</data>
<data name="Icon.Layers" xml:space="preserve">
<value>Layers</value>
</data>
<data name="Icon.Lightbulb" xml:space="preserve">
<value>Lightbulb</value>
</data>
<data name="Icon.LinkBroken" xml:space="preserve">
<value>Link Broken</value>
</data>
<data name="Icon.LinkIntact" xml:space="preserve">
<value>Link Intact</value>
</data>
<data name="Icon.ListRich" xml:space="preserve">
<value>List Rich</value>
</data>
<data name="Icon.List" xml:space="preserve">
<value>List</value>
</data>
<data name="Icon.Location" xml:space="preserve">
<value>Location</value>
</data>
<data name="Icon.LockLocked" xml:space="preserve">
<value>Lock Locked</value>
</data>
<data name="Icon.LockUnlocked" xml:space="preserve">
<value>Lock Unlocked</value>
</data>
<data name="Icon.LoopCircular" xml:space="preserve">
<value>Loop Circular</value>
</data>
<data name="Icon.LoopSquare" xml:space="preserve">
<value>Loop Square</value>
</data>
<data name="Icon.Loop" xml:space="preserve">
<value>Loop</value>
</data>
<data name="Icon.MagnifyingGlass" xml:space="preserve">
<value>Magnifying Glass</value>
</data>
<data name="Icon.MapMarker" xml:space="preserve">
<value>Map Marker</value>
</data>
<data name="Icon.Map" xml:space="preserve">
<value>Map</value>
</data>
<data name="Icon.MediaPause" xml:space="preserve">
<value>Media Pause</value>
</data>
<data name="Icon.MediaPlay" xml:space="preserve">
<value>Media Play</value>
</data>
<data name="Icon.MediaRecord" xml:space="preserve">
<value>Media Record</value>
</data>
<data name="Icon.MediaSkipBackward" xml:space="preserve">
<value>Media Skip Backward</value>
</data>
<data name="Icon.MediaSkipForward" xml:space="preserve">
<value>Media Skip Forward</value>
</data>
<data name="Icon.MediaStepBackward" xml:space="preserve">
<value>Media Step Backward</value>
</data>
<data name="Icon.MediaStepForward" xml:space="preserve">
<value>Media Step Forward</value>
</data>
<data name="Icon.MediaStop" xml:space="preserve">
<value>Media Stop</value>
</data>
<data name="Icon.MedicalCross" xml:space="preserve">
<value>Medical Cross</value>
</data>
<data name="Icon.Menu" xml:space="preserve">
<value>Menu</value>
</data>
<data name="Icon.Microphone" xml:space="preserve">
<value>Microphone</value>
</data>
<data name="Icon.Minus" xml:space="preserve">
<value>Minus</value>
</data>
<data name="Icon.Monitor" xml:space="preserve">
<value>Monitor</value>
</data>
<data name="Icon.Moon" xml:space="preserve">
<value>Moon</value>
</data>
<data name="Icon.Move" xml:space="preserve">
<value>Move</value>
</data>
<data name="Icon.MusicalNote" xml:space="preserve">
<value>Musical Note</value>
</data>
<data name="Icon.Paperclip" xml:space="preserve">
<value>Paperclip</value>
</data>
<data name="Icon.Pencil" xml:space="preserve">
<value>Pencil</value>
</data>
<data name="Icon.People" xml:space="preserve">
<value>People</value>
</data>
<data name="Icon.Person" xml:space="preserve">
<value>Person</value>
</data>
<data name="Icon.Phone" xml:space="preserve">
<value>Phone</value>
</data>
<data name="Icon.PieChart" xml:space="preserve">
<value>Pie Chart</value>
</data>
<data name="Icon.Pin" xml:space="preserve">
<value>Pin</value>
</data>
<data name="Icon.PlayCircle" xml:space="preserve">
<value>Play Circle</value>
</data>
<data name="Icon.Plus" xml:space="preserve">
<value>Plus</value>
</data>
<data name="Icon.PowerStandby" xml:space="preserve">
<value>Power Standby</value>
</data>
<data name="Icon.Print" xml:space="preserve">
<value>Print</value>
</data>
<data name="Icon.Project" xml:space="preserve">
<value>Project</value>
</data>
<data name="Icon.Pulse" xml:space="preserve">
<value>Pulse</value>
</data>
<data name="Icon.PuzzlePiece" xml:space="preserve">
<value>Puzzle Piece</value>
</data>
<data name="Icon.QuestionMark" xml:space="preserve">
<value>Question Mark</value>
</data>
<data name="Icon.Rain" xml:space="preserve">
<value>Rain</value>
</data>
<data name="Icon.Random" xml:space="preserve">
<value>Random</value>
</data>
<data name="Icon.Reload" xml:space="preserve">
<value>Reload</value>
</data>
<data name="Icon.ResizeBoth" xml:space="preserve">
<value>Resize Both</value>
</data>
<data name="Icon.ResizeHeight" xml:space="preserve">
<value>Resize Height</value>
</data>
<data name="Icon.ResizeWidth" xml:space="preserve">
<value>Resize Width</value>
</data>
<data name="Icon.RssAlt" xml:space="preserve">
<value>Rss Alt</value>
</data>
<data name="Icon.Rss" xml:space="preserve">
<value>Rss</value>
</data>
<data name="Icon.Script" xml:space="preserve">
<value>Script</value>
</data>
<data name="Icon.ShareBoxed" xml:space="preserve">
<value>Share Boxed</value>
</data>
<data name="Icon.Share" xml:space="preserve">
<value>Share</value>
</data>
<data name="Icon.Shield" xml:space="preserve">
<value>Shield</value>
</data>
<data name="Icon.Signal" xml:space="preserve">
<value>Signal</value>
</data>
<data name="Icon.Signpost" xml:space="preserve">
<value>Signpost</value>
</data>
<data name="Icon.SortAscending" xml:space="preserve">
<value>Sort Ascending</value>
</data>
<data name="Icon.SortDescending" xml:space="preserve">
<value>Sort Descending</value>
</data>
<data name="Icon.Spreadsheet" xml:space="preserve">
<value>Spreadsheet</value>
</data>
<data name="Icon.Star" xml:space="preserve">
<value>Star</value>
</data>
<data name="Icon.Sun" xml:space="preserve">
<value>Sun</value>
</data>
<data name="Icon.Tablet" xml:space="preserve">
<value>Tablet</value>
</data>
<data name="Icon.Tag" xml:space="preserve">
<value>Tag</value>
</data>
<data name="Icon.Tags" xml:space="preserve">
<value>Tags</value>
</data>
<data name="Icon.Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="Icon.Task" xml:space="preserve">
<value>Task</value>
</data>
<data name="Icon.Terminal" xml:space="preserve">
<value>Terminal</value>
</data>
<data name="Icon.Text" xml:space="preserve">
<value>Text</value>
</data>
<data name="Icon.ThumbDown" xml:space="preserve">
<value>Thumb Down</value>
</data>
<data name="Icon.ThumbUp" xml:space="preserve">
<value>Thumb Up</value>
</data>
<data name="Icon.Timer" xml:space="preserve">
<value>Timer</value>
</data>
<data name="Icon.Transfer" xml:space="preserve">
<value>Transfer</value>
</data>
<data name="Icon.Trash" xml:space="preserve">
<value>Trash</value>
</data>
<data name="Icon.Underline" xml:space="preserve">
<value>Underline</value>
</data>
<data name="Icon.VerticalAlignBottom" xml:space="preserve">
<value>Vertical Align Bottom</value>
</data>
<data name="Icon.VerticalAlignCenter" xml:space="preserve">
<value>Vertical Align Center</value>
</data>
<data name="Icon.VerticalAlignTop" xml:space="preserve">
<value>Vertical Align Top</value>
</data>
<data name="Icon.Video" xml:space="preserve">
<value>Video</value>
</data>
<data name="Icon.VolumeHigh" xml:space="preserve">
<value>Volume High</value>
</data>
<data name="Icon.VolumeLow" xml:space="preserve">
<value>Volume Low</value>
</data>
<data name="Icon.VolumeOff" xml:space="preserve">
<value>Volume Off</value>
</data>
<data name="Icon.Warning" xml:space="preserve">
<value>Warning</value>
</data>
<data name="Icon.Wifi" xml:space="preserve">
<value>Wifi</value>
</data>
<data name="Icon.Wrench" xml:space="preserve">
<value>Wrench</value>
</data>
<data name="Icon.X" xml:space="preserve">
<value>X</value>
</data>
<data name="Icon.Yen" xml:space="preserve">
<value>Yen</value>
</data>
<data name="Icon.ZoomIn" xml:space="preserve">
<value>Zoom In</value>
</data>
<data name="Icon.ZoomOut" xml:space="preserve">
<value>Zoom Out</value>
</data>
</root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@ -150,7 +150,7 @@
<data name="Description.Text" xml:space="preserve">
<value>Description:</value>
</data>
<data name="ModuleTitle.Text" xml:space="preserve">
<data name="File Management" xml:space="preserve">
<value>File Management</value>
</data>
</root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@ -178,7 +178,7 @@
<value>Capacity:</value>
</data>
<data name="ImageSizes.HelpText" xml:space="preserve">
<value>Enter a list of image sizes which can be generated dynamically from uploaded images (ie. 200x200,x200,200x)</value>
<value>Enter a list of image sizes which can be generated dynamically from uploaded images (ie. 200x200,400x400). Use * to indicate the folder supports all image sizes.</value>
</data>
<data name="ImageSizes.Text" xml:space="preserve">
<value>Image Sizes:</value>
@ -192,7 +192,7 @@
<data name="Public" xml:space="preserve">
<value>Public</value>
</data>
<data name="ModuleTitle.Text" xml:space="preserve">
<data name="Folder Management" xml:space="preserve">
<value>Folder Management</value>
</data>
</root>

View File

@ -141,7 +141,7 @@
<data name="Upload.Heading" xml:space="preserve">
<value>Upload</value>
</data>
<data name="Product.Text" xml:space="preserve">
<data name="Product" xml:space="preserve">
<value>Product</value>
</data>
</root>

View File

@ -225,4 +225,7 @@
<data name="IsEnabled.Text" xml:space="preserve">
<value>Enabled?</value>
</data>
<data name="View License" xml:space="preserve">
<value>View License</value>
</data>
</root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@ -132,7 +132,7 @@
<data name="Success.Content.Export" xml:space="preserve">
<value>Content Exported Successfully</value>
</data>
<data name="ModuleTitle.Text" xml:space="preserve">
<data name="Export Content" xml:space="preserve">
<value>Export Content</value>
</data>
</root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@ -138,7 +138,7 @@
<data name="Message.Required.ImportContent" xml:space="preserve">
<value>You Must Enter Some Content To Import</value>
</data>
<data name="ModuleTitle.Text" xml:space="preserve">
<data name="Import Content" xml:space="preserve">
<value>Import Content</value>
</data>
</root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@ -156,7 +156,7 @@
<data name="Module.Text" xml:space="preserve">
<value>Module:</value>
</data>
<data name="ModuleTitle.Text" xml:space="preserve">
<data name="Module Settings" xml:space="preserve">
<value>Module Settings</value>
</data>
</root>

View File

@ -177,19 +177,4 @@
<data name="Username.Text" xml:space="preserve">
<value>Username:</value>
</data>
<data name="Password.ValidationCriteria" xml:space="preserve">
<value>Passwords Must Have A Minimum Length Of {0} Characters, Including At Least {1} Unique Character(s), {2}{3}{4}{5} To Satisfy Password Compexity Requirements For This Site.</value>
</data>
<data name="Password.DigitRequirement" xml:space="preserve">
<value>At Least One Digit</value>
</data>
<data name="Password.LowercaseRequirement" xml:space="preserve">
<value>At Least One Lowercase Letter</value>
</data>
<data name="Password.PunctuationRequirement" xml:space="preserve">
<value>At Least One Punctuation Mark</value>
</data>
<data name="Password.UppercaseRequirement" xml:space="preserve">
<value>At Least One Uppercase Letter</value>
</data>
</root>

View File

@ -312,6 +312,9 @@
<data name="Database.HelpText" xml:space="preserve">
<value>The type of database</value>
</data>
<data name="DeleteSite.Header" xml:space="preserve">
<value>Delete Site</value>
</data>
<data name="DeleteSite.Text" xml:space="preserve">
<value>Delete Site</value>
</data>
@ -381,4 +384,10 @@
<data name="Version.Text" xml:space="preserve">
<value>Version:</value>
</data>
<data name="DeleteAlias.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="DeleteAlias.Header" xml:space="preserve">
<value>Delete Alias</value>
</data>
</root>

View File

@ -219,11 +219,11 @@
<data name="Success.Register" xml:space="preserve">
<value>You Have Been Successfully Registered For Updates</value>
</data>
<data name="PackageService.HelpText" xml:space="preserve">
<value>Specify If The Package Service Is Enabled For Installing Modules, Themes, And Translations</value>
<data name="PackageManager.HelpText" xml:space="preserve">
<value>Specify The Package Manager Service For Installing Modules, Themes, And Translations. If This Field Is Blank It Means The Package Manager Service Is Disabled For This Installation.</value>
</data>
<data name="PackageService.Text" xml:space="preserve">
<value>Package Service Enabled?</value>
<data name="PackageManager.Text" xml:space="preserve">
<value>Package Manager:</value>
</data>
<data name="Swagger.HelpText" xml:space="preserve">
<value>Specify If Swagger Is Enabled For Your Server API</value>

View File

@ -141,4 +141,7 @@
<data name="Upload.Heading" xml:space="preserve">
<value>Upload</value>
</data>
<data name="Product" xml:space="preserve">
<value>Product</value>
</data>
</root>

View File

@ -177,4 +177,7 @@
<data name="IsEnabled.Text" xml:space="preserve">
<value>Enabled?</value>
</data>
<data name="View License" xml:space="preserve">
<value>View License</value>
</data>
</root>

View File

@ -138,6 +138,9 @@
<data name="DeleteTheme.Header" xml:space="preserve">
<value>Delete Theme</value>
</data>
<data name="DeleteTheme.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="CreateTheme.Text" xml:space="preserve">
<value>Create Theme</value>
</data>
@ -147,6 +150,9 @@
<data name="ViewTheme.Text" xml:space="preserve">
<value>View</value>
</data>
<data name="EditTheme.Text" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Enabled" xml:space="preserve">
<value>Enabled?</value>
</data>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@ -141,7 +141,7 @@
<data name="Subject.Text" xml:space="preserve">
<value>Subject: </value>
</data>
<data name="ModuleTitle.Text" xml:space="preserve">
<data name="Send Notification" xml:space="preserve">
<value>Send Notification</value>
</data>
</root>

View File

@ -228,4 +228,10 @@
<data name="Profile.Heading" xml:space="preserve">
<value>Profile</value>
</data>
<data name="ViewNotification.Text" xml:space="preserve">
<value>View</value>
</data>
<data name="DeleteNotification.Text" xml:space="preserve">
<value>Delete</value>
</data>
</root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@ -144,7 +144,7 @@
<data name="OriginalMessage" xml:space="preserve">
<value>Original Message</value>
</data>
<data name="ModuleTitle.Text" xml:space="preserve">
<data name="View Notification" xml:space="preserve">
<value>View Notification</value>
</data>
</root>

View File

@ -396,4 +396,10 @@
<data name="ProfileClaimTypes.Text" xml:space="preserve">
<value>User Profile Claims:</value>
</data>
<data name="Username" xml:space="preserve">
<value>User Name</value>
</data>
<data name="Name" xml:space="preserve">
<value>Name</value>
</data>
</root>

View File

@ -129,4 +129,25 @@
<data name="Message.Username.DontExist" xml:space="preserve">
<value>User Does Not Exist With Name Specified</value>
</data>
<data name="ModuleDefinition" xml:space="preserve">
<value>Module</value>
</data>
<data name="Page" xml:space="preserve">
<value>Page</value>
</data>
<data name="Folder" xml:space="preserve">
<value>Folder</value>
</data>
<data name="View" xml:space="preserve">
<value>View</value>
</data>
<data name="Edit" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Utilize" xml:space="preserve">
<value>Utilize</value>
</data>
<data name="Browse" xml:space="preserve">
<value>Browse</value>
</data>
</root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@ -156,7 +156,7 @@
<data name="Message.Content.Restored" xml:space="preserve">
<value>Version Restored</value>
</data>
<data name="ModuleTitle.Text" xml:space="preserve">
<data name="Edit Html/Text" xml:space="preserve">
<value>Edit Html/Text</value>
</data>
<data name="Restore.Header" xml:space="preserve">
@ -168,4 +168,16 @@
<data name="View.Text" xml:space="preserve">
<value>View</value>
</data>
<data name="Edit.Heading" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Versions.Heading" xml:space="preserve">
<value>Versions</value>
</data>
<data name="HtmlEditor.Heading" xml:space="preserve">
<value>Raw HTML Editor</value>
</data>
<data name="RichTextEditor.Heading" xml:space="preserve">
<value>Rich Text Editor</value>
</data>
</root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@ -120,6 +120,9 @@
<data name="Edit.Action" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Edit.Text" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Error.Content.Load" xml:space="preserve">
<value>An Error Occurred Loading Content</value>
</data>

View File

@ -405,4 +405,25 @@
<data name="Search.RecentlyReleased" xml:space="preserve">
<value>Recently Released</value>
</data>
<data name="From" xml:space="preserve">
<value>From</value>
</data>
<data name="To" xml:space="preserve">
<value>To</value>
</data>
<data name="Password.DigitRequirement" xml:space="preserve">
<value>At Least One Digit</value>
</data>
<data name="Password.LowercaseRequirement" xml:space="preserve">
<value>At Least One Lowercase Letter</value>
</data>
<data name="Password.PunctuationRequirement" xml:space="preserve">
<value>At Least One Punctuation Mark</value>
</data>
<data name="Password.UppercaseRequirement" xml:space="preserve">
<value>At Least One Uppercase Letter</value>
</data>
<data name="Password.ValidationCriteria" xml:space="preserve">
<value>Passwords Must Have A Minimum Length Of {0} Characters, Including At Least {1} Unique Character(s), {2}{3}{4}{5} To Satisfy Password Compexity Requirements For This Site.</value>
</data>
</root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@ -186,4 +186,7 @@
<data name="VisibilityView" xml:space="preserve">
<value>Same As Page</value>
</data>
<data name="Confirm.Page.Delete" xml:space="preserve">
<value>Are You Sure You Want To Delete This Page?</value>
</data>
</root>

View File

@ -29,7 +29,7 @@ namespace Oqtane.Services
public async Task<Folder> GetFolderAsync(int siteId, [NotNull] string folderPath)
{
var path = WebUtility.UrlEncode(folderPath);
return await GetJsonAsync<Folder>($"{ApiUrl}/{siteId}/{path}");
return await GetJsonAsync<Folder>($"{ApiUrl}/path/{siteId}/?path={path}");
}
public async Task<Folder> AddFolderAsync(Folder folder)

View File

@ -38,6 +38,13 @@ namespace Oqtane.Services
/// <returns></returns>
Task<List<Package>> GetPackagesAsync(string type, string search, string price, string package, string sort);
/// <summary>
/// Returns a list of packages matching the list of package names
/// </summary>
/// <param name="names"></param>
/// <returns></returns>
Task<List<Package>> GetPackagesAsync(List<string> packagenames);
/// <summary>
/// Returns a specific package
/// </summary>

View File

@ -9,13 +9,13 @@ namespace Oqtane.Services
public interface ISystemService
{
/// <summary>
/// returns a key-value directory with the current system configuration information
/// returns a key-value dictionary with the current system configuration information
/// </summary>
/// <returns></returns>
Task<Dictionary<string, object>> GetSystemInfoAsync();
/// <summary>
/// returns a key-value directory with the current system information - "environment" or "configuration"
/// returns a key-value dictionary with the current system information - "environment" or "configuration"
/// </summary>
/// <returns></returns>
Task<Dictionary<string, object>> GetSystemInfoAsync(string type);
@ -32,5 +32,11 @@ namespace Oqtane.Services
/// <param name="settings"></param>
/// <returns></returns>
Task UpdateSystemInfoAsync(Dictionary<string, object> settings);
/// <summary>
/// returns a key-value dictionary with default system icons
/// </summary>
/// <returns></returns>
Task<Dictionary<string, string>> GetIconsAsync();
}
}

View File

@ -1,4 +1,5 @@
using Oqtane.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Oqtane.Services
@ -16,7 +17,6 @@ namespace Oqtane.Services
/// <returns></returns>
Task<User> GetUserAsync(int userId, int siteId);
/// <summary>
/// Get a <see cref="User"/> of a specific site
/// </summary>
@ -25,6 +25,15 @@ namespace Oqtane.Services
/// <returns></returns>
Task<User> GetUserAsync(string username, int siteId);
/// <summary>
/// Get a <see cref="User"/> of a specific site
/// </summary>
/// <param name="username">Username / login of a <see cref="User"/></param>
/// <param name="email">email address of a <see cref="User"/></param>
/// <param name="siteId">ID of a <see cref="Site"/></param>
/// <returns></returns>
Task<User> GetUserAsync(string username, string email, int siteId);
/// <summary>
/// Save a user to the Database.
/// The <see cref="User"/> object contains all the information incl. what <see cref="Site"/> it belongs to.
@ -127,6 +136,11 @@ namespace Oqtane.Services
/// <returns></returns>
Task<User> LinkUserAsync(User user, string token, string type, string key, string name);
/// <summary>
/// Get password requirements for site
/// </summary>
/// <param name="siteId">ID of a <see cref="Site"/></param>
/// <returns></returns>
Task<string> GetPasswordRequirementsAsync(int siteId);
}
}

View File

@ -31,6 +31,11 @@ namespace Oqtane.Services
return await GetJsonAsync<List<Package>>($"{Apiurl}?type={type}&search={WebUtility.UrlEncode(search)}&price={price}&package={package}&sort={sort}");
}
public async Task<List<Package>> GetPackagesAsync(List<string> packagenames)
{
return await GetJsonAsync<List<Package>>($"{Apiurl}/list/?names={string.Join(",", packagenames)}");
}
public async Task<Package> GetPackageAsync(string packageId, string version)
{
return await PostJsonAsync<Package>($"{Apiurl}?packageid={packageId}&version={version}", null);

View File

@ -33,5 +33,10 @@ namespace Oqtane.Services
{
await PostJsonAsync(Apiurl, settings);
}
public async Task<Dictionary<string, string>> GetIconsAsync()
{
return await GetJsonAsync<Dictionary<string, string>>($"{Apiurl}/icons");
}
}
}

View File

@ -4,13 +4,20 @@ using System.Net.Http;
using System.Threading.Tasks;
using Oqtane.Documentation;
using System.Net;
using System.Collections.Generic;
using Microsoft.Extensions.Localization;
namespace Oqtane.Services
{
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
public class UserService : ServiceBase, IUserService
{
public UserService(HttpClient http, SiteState siteState) : base(http, siteState) { }
private readonly IStringLocalizer<SharedResources> _localizer;
public UserService(IStringLocalizer<SharedResources> localizer, HttpClient http, SiteState siteState) : base(http, siteState)
{
_localizer = localizer;
}
private string Apiurl => CreateApiUrl("User");
@ -21,7 +28,12 @@ namespace Oqtane.Services
public async Task<User> GetUserAsync(string username, int siteId)
{
return await GetJsonAsync<User>($"{Apiurl}/name/{username}?siteid={siteId}");
return await GetUserAsync(username, "", siteId);
}
public async Task<User> GetUserAsync(string username, string email, int siteId)
{
return await GetJsonAsync<User>($"{Apiurl}/name/{(!string.IsNullOrEmpty(username) ? username : "-")}/{(!string.IsNullOrEmpty(email) ? email : "-")}/?siteid={siteId}");
}
public async Task<User> AddUserAsync(User user)
@ -90,5 +102,26 @@ namespace Oqtane.Services
return await PostJsonAsync<User>($"{Apiurl}/link?token={token}&type={type}&key={key}&name={name}", user);
}
public async Task<string> GetPasswordRequirementsAsync(int siteId)
{
var requirements = await GetJsonAsync<Dictionary<string, string>>($"{Apiurl}/passwordrequirements/{siteId}");
var minimumlength = (requirements.ContainsKey("IdentityOptions:Password:RequiredLength")) ? requirements["IdentityOptions:Password:RequiredLength"] : "6";
var uniquecharacters = (requirements.ContainsKey("IdentityOptions:Password:RequiredUniqueChars")) ? requirements["IdentityOptions:Password:RequiredUniqueChars"] : "1";
var requiredigit = bool.Parse((requirements.ContainsKey("IdentityOptions:Password:RequireDigit")) ? requirements["IdentityOptions:Password:RequireDigit"] : "true");
var requireupper = bool.Parse((requirements.ContainsKey("IdentityOptions:Password:RequireUppercase")) ? requirements["IdentityOptions:Password:RequireUppercase"] : "true");
var requirelower = bool.Parse((requirements.ContainsKey("IdentityOptions:Password:RequireLowercase")) ? requirements["IdentityOptions:Password:RequireLowercase"] : "true");
var requirepunctuation = bool.Parse((requirements.ContainsKey("IdentityOptions:Password:RequireNonAlphanumeric")) ? requirements["IdentityOptions:Password:RequireNonAlphanumeric"] : "true");
// replace the placeholders with the setting values
string digitRequirement = requiredigit ? _localizer["Password.DigitRequirement"] + ", " : "";
string uppercaseRequirement = requireupper ? _localizer["Password.UppercaseRequirement"] + ", " : "";
string lowercaseRequirement = requirelower ? _localizer["Password.LowercaseRequirement"] + ", " : "";
string punctuationRequirement = requirepunctuation ? _localizer["Password.PunctuationRequirement"] + ", " : "";
string passwordValidationCriteriaTemplate = _localizer["Password.ValidationCriteria"];
// format requirements
return string.Format(passwordValidationCriteriaTemplate, minimumlength, uniquecharacters, digitRequirement, uppercaseRequirement, lowercaseRequirement, punctuationRequirement);
}
}
}

View File

@ -3,6 +3,7 @@
@inherits ContainerBase
@attribute [OqtaneIgnore]
@inject IStringLocalizer<SharedResources> SharedLocalizer
@inject IStringLocalizerFactory LocalizerFactory
<span class="app-moduletitle">
@((MarkupString)title)
@ -20,7 +21,8 @@
{
if (!string.IsNullOrEmpty(ModuleState.ControlTitle))
{
title = ModuleState.ControlTitle;
var localizer = LocalizerFactory.Create(ModuleState.ModuleType);
title = localizer[ModuleState.ControlTitle];
}
else
{

View File

@ -36,14 +36,14 @@
@if (_canViewAdminDashboard || UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
{
<button type="button" class="btn @ButtonClass ms-1" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel">
<button type="button" class="btn @ButtonClass ms-1" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel" @onclick="ClearMessage">
<span class="oi oi-cog"></span>
</button>
<div class="@ContainerClass" tabindex="-1" data-bs-scroll="true" data-bs-backdrop="true" id="offcanvasControlPanel" aria-labelledby="offcanvasScrollingLabel">
<div class="@HeaderClass">
<h5 id="offcanvasScrollingLabel" class="offcanvas-title">@Localizer["ControlPanel"]</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close" @onclick="ClearMessage"></button>
</div>
<div class="@BodyClass">
<div class="container-fluid">
@ -98,7 +98,7 @@
<button type="button" class="btn-close" aria-label="Close" @onclick="ConfirmDelete"></button>
</div>
<div class="modal-body">
<p>Are You Sure You Want To Delete This Page?</p>
<p>@Localizer["Confirm.Page.Delete"]</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" @onclick="DeletePage">@SharedLocalizer["Delete"]</button>
@ -647,4 +647,10 @@
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
}
private void ClearMessage()
{
Message = "";
}
}

View File

@ -28,11 +28,11 @@ namespace Oqtane.UI
public List<Page> Pages
{
get { return Site.Pages.Where(item => !item.IsDeleted).ToList(); }
get { return Site.Pages; }
}
public List<Module> Modules
{
get { return Site.Modules.Where(item => !item.IsDeleted).ToList(); }
get { return Site.Modules; }
}
public List<Language> Languages
{

View File

@ -387,6 +387,7 @@
private (Page Page, List<Module> Modules) ProcessModules(Page page, List<Module> modules, int moduleid, string action, string defaultcontainertype, Alias alias)
{
var paneindex = new Dictionary<string, int>();
foreach (Module module in modules)
{
// initialize module control properties
@ -397,7 +398,7 @@
module.PaneModuleIndex = -1;
module.PaneModuleCount = 0;
if ((module.PageId == page.PageId || module.ModuleId == moduleid))
if (module.PageId == page.PageId || module.ModuleId == moduleid)
{
var typename = Constants.ErrorModule;
@ -463,8 +464,8 @@
// additional metadata needed for admin components
if (module.ModuleId == moduleid && action != "")
{
module.SecurityAccessLevel = moduleobject.SecurityAccessLevel;
module.ControlTitle = moduleobject.Title;
module.SecurityAccessLevel = moduleobject.SecurityAccessLevel;
module.Actions = moduleobject.Actions;
module.UseAdminContainer = moduleobject.UseAdminContainer;
}

View File

@ -67,16 +67,19 @@
if (!string.IsNullOrEmpty(content))
{
// format head content, remove scripts, and filter duplicate elements
var elements = (">" + content.Replace("\n", "") + "<").Split("><");
foreach (var element in elements)
content = content.Replace("\n", "");
var index = content.IndexOf("<");
while (index >= 0)
{
if (!string.IsNullOrEmpty(element) && !element.ToLower().StartsWith("script"))
var element = content.Substring(index, content.IndexOf(">", index) - index + 1);
if (!string.IsNullOrEmpty(element) && !element.ToLower().StartsWith("<script"))
{
if (!headcontent.Contains("<" + element + ">"))
if (!headcontent.Contains(element))
{
headcontent += "<" + element + ">" + "\n";
headcontent += element + "\n";
}
}
index = content.IndexOf("<", index + 1);
}
}
return headcontent;

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Version>4.0.2</Version>
<Version>4.0.3</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/v4.0.2</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.3</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Database.MySQL</id>
<version>4.0.2</version>
<version>4.0.3</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane MySQL Provider</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/v4.0.2</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.3</releaseNotes>
<icon>icon.png</icon>
<tags>oqtane</tags>
</metadata>

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Version>4.0.2</Version>
<Version>4.0.3</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/v4.0.2</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.3</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Database.PostgreSQL</id>
<version>4.0.2</version>
<version>4.0.3</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane PostgreSQL Provider</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/v4.0.2</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.3</releaseNotes>
<icon>icon.png</icon>
<tags>oqtane</tags>
</metadata>

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Version>4.0.2</Version>
<Version>4.0.3</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/v4.0.2</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.3</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Database.SqlServer</id>
<version>4.0.2</version>
<version>4.0.3</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane SQL Server Provider</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/v4.0.2</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.3</releaseNotes>
<icon>icon.png</icon>
<tags>oqtane</tags>
</metadata>

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Version>4.0.2</Version>
<Version>4.0.3</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/v4.0.2</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.3</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Database.Sqlite</id>
<version>4.0.2</version>
<version>4.0.3</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane SQLite Provider</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/v4.0.2</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.3</releaseNotes>
<icon>icon.png</icon>
<tags>oqtane</tags>
</metadata>

View File

@ -6,7 +6,7 @@
<!-- <TargetFrameworks>net7.0-android;net7.0-ios;net7.0-maccatalyst</TargetFrameworks> -->
<!-- <TargetFrameworks>$(TargetFrameworks);net7.0-tizen</TargetFrameworks> -->
<OutputType>Exe</OutputType>
<Version>4.0.2</Version>
<Version>4.0.3</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/v4.0.2</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.3</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane.Maui</RootNamespace>
@ -31,7 +31,7 @@
<ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid>
<!-- Versions -->
<ApplicationDisplayVersion>4.0.2</ApplicationDisplayVersion>
<ApplicationDisplayVersion>4.0.3</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Client</id>
<version>4.0.2</version>
<version>4.0.3</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/v4.0.2</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.3</releaseNotes>
<icon>icon.png</icon>
<tags>oqtane</tags>
</metadata>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Framework</id>
<version>4.0.2</version>
<version>4.0.3</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/v4.0.2/Oqtane.Framework.4.0.2.Upgrade.zip</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</releaseNotes>
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v4.0.3/Oqtane.Framework.4.0.3.Upgrade.zip</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.3</releaseNotes>
<icon>icon.png</icon>
<tags>oqtane framework</tags>
</metadata>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Server</id>
<version>4.0.2</version>
<version>4.0.3</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/v4.0.2</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.3</releaseNotes>
<icon>icon.png</icon>
<tags>oqtane</tags>
</metadata>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Shared</id>
<version>4.0.2</version>
<version>4.0.3</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/v4.0.2</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.3</releaseNotes>
<icon>icon.png</icon>
<tags>oqtane</tags>
</metadata>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Updater</id>
<version>4.0.2</version>
<version>4.0.3</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/v4.0.2</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.3</releaseNotes>
<icon>icon.png</icon>
<tags>oqtane</tags>
</metadata>

View File

@ -1 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.2.Install.zip" -Force
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.3.Install.zip" -Force

View File

@ -1 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.2.Upgrade.zip" -Force
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.3.Upgrade.zip" -Force

View File

@ -159,6 +159,48 @@ namespace Oqtane.Controllers
}
}
// POST api/<controller>
[HttpPost]
[Authorize(Roles = RoleNames.Registered)]
public Models.File Post([FromBody] Models.File file)
{
var folder = _folders.GetFolder(file.FolderId);
if (ModelState.IsValid && folder != null && folder.SiteId == _alias.SiteId)
{
if (_userPermissions.IsAuthorized(User, folder.SiteId, EntityNames.Folder, file.FolderId, PermissionNames.Edit))
{
var filepath = _files.GetFilePath(file);
if (System.IO.File.Exists(filepath))
{
file = CreateFile(file.Name, folder.FolderId, filepath);
file = _files.AddFile(file);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Create);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "File Added {File}", file);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "File Does Not Exist At Path {FilePath}", filepath);
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
file = null;
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Post Attempt {File}", file);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
file = null;
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Post Attempt {File}", file);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
file = null;
}
return file;
}
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = RoleNames.Registered)]
@ -580,8 +622,9 @@ namespace Oqtane.Controllers
string imagepath = filepath.Replace(Path.GetExtension(filepath), "." + width.ToString() + "x" + height.ToString() + ".png");
if (!System.IO.File.Exists(imagepath) || bool.Parse(recreate))
{
if ((_userPermissions.IsAuthorized(User, PermissionNames.Edit, file.Folder.PermissionList) ||
!string.IsNullOrEmpty(file.Folder.ImageSizes) && file.Folder.ImageSizes.ToLower().Split(",").Contains(width.ToString() + "x" + height.ToString())))
// user has edit access to folder or folder supports the image size being created
if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, file.Folder.PermissionList) ||
(!string.IsNullOrEmpty(file.Folder.ImageSizes) && (file.Folder.ImageSizes == "*" || file.Folder.ImageSizes.ToLower().Split(",").Contains(width.ToString() + "x" + height.ToString()))))
{
imagepath = CreateImage(filepath, width, height, mode, position, background, rotate, imagepath);
}
@ -636,6 +679,25 @@ namespace Oqtane.Controllers
Enum.TryParse(mode, true, out ResizeMode resizemode);
Enum.TryParse(position, true, out AnchorPositionMode anchorpositionmode);
PngEncoder encoder;
if (background != "transparent")
{
image.Mutate(x => x
.AutoOrient() // auto orient the image
.Rotate(angle)
.Resize(new ResizeOptions
{
Mode = resizemode,
Position = anchorpositionmode,
Size = new Size(width, height),
PadColor = Color.ParseHex("#" + background)
}));
encoder = new PngEncoder();
}
else
{
image.Mutate(x => x
.AutoOrient() // auto orient the image
.Rotate(angle)
@ -646,19 +708,14 @@ namespace Oqtane.Controllers
Size = new Size(width, height)
}));
if (background != "transparent")
{
image.Mutate(x => x
.BackgroundColor(Color.ParseHex("#" + background)));
}
PngEncoder encoder = new PngEncoder
encoder = new PngEncoder
{
ColorType = PngColorType.RgbWithAlpha,
TransparentColorMode = PngTransparentColorMode.Preserve,
BitDepth = PngBitDepth.Bit8,
CompressionLevel = PngCompressionLevel.BestSpeed
};
}
image.Save(imagepath, encoder);
}

View File

@ -84,10 +84,11 @@ namespace Oqtane.Controllers
}
}
[HttpGet("{siteId}/{path}")]
// GET api/<controller>/path/x/?path=y
[HttpGet("path/{siteId}")]
public Folder GetByPath(int siteId, string path)
{
var folderPath = WebUtility.UrlDecode(path).Replace("\\", "/");
var folderPath = WebUtility.UrlDecode(path).Replace("\\", "/"); // handle legacy path format
folderPath = (folderPath == "/") ? "" : folderPath;
if (!folderPath.EndsWith("/") && folderPath != "")
{

View File

@ -7,9 +7,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Oqtane.Infrastructure;
using Oqtane.Models;
using Oqtane.Modules;
using Oqtane.Shared;
using Oqtane.Themes;
using Microsoft.Extensions.Caching.Memory;
using System.Net;
using Oqtane.Repository;
@ -243,13 +241,16 @@ namespace Oqtane.Controllers
private async Task RegisterContact(string email)
{
try
{
var url = _configManager.GetSetting("PackageRegistryUrl", Constants.PackageRegistryUrl);
if (!string.IsNullOrEmpty(url))
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Referer", HttpContext.Request.Scheme + "://" + HttpContext.Request.Host.Value);
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(Constants.PackageId, Constants.Version));
Uri uri = new Uri(Constants.PackageRegistryUrl + $"/api/registry/contact/?id={_configManager.GetInstallationId()}&email={WebUtility.UrlEncode(email)}");
var response = await client.GetAsync(uri).ConfigureAwait(false);
var response = await client.GetAsync(new Uri(url + $"/api/registry/contact/?id={_configManager.GetInstallationId()}&email={WebUtility.UrlEncode(email)}")).ConfigureAwait(false);
}
}
}
catch

View File

@ -127,17 +127,26 @@ namespace Oqtane.Controllers
DirectoryInfo rootFolder = Directory.GetParent(_environment.ContentRootPath);
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", moduleDefinition.Template, Path.DirectorySeparatorChar.ToString());
if (!string.IsNullOrEmpty(moduleDefinition.ModuleDefinitionName))
{
moduleDefinition.ModuleDefinitionName = moduleDefinition.ModuleDefinitionName.Replace("[Owner]", moduleDefinition.Owner).Replace("[Module]", moduleDefinition.Name);
}
else
{
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + ".Module." + moduleDefinition.Name;
}
if (moduleDefinition.Template.ToLower().Contains("internal"))
{
rootPath = Utilities.PathCombine(rootFolder.FullName, Path.DirectorySeparatorChar.ToString());
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + ".Module." + moduleDefinition.Name + ", Oqtane.Client";
moduleDefinition.ServerManagerType = moduleDefinition.Owner + ".Module." + moduleDefinition.Name + ".Manager." + moduleDefinition.Name + "Manager, Oqtane.Server";
moduleDefinition.ModuleDefinitionName = moduleDefinition.ModuleDefinitionName + ", Oqtane.Client";
moduleDefinition.ServerManagerType = moduleDefinition.ModuleDefinitionName + ".Manager." + moduleDefinition.Name + "Manager, Oqtane.Server";
}
else
{
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName, moduleDefinition.Owner + ".Module." + moduleDefinition.Name, Path.DirectorySeparatorChar.ToString());
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + ".Module." + moduleDefinition.Name + ", " + moduleDefinition.Owner + ".Module." + moduleDefinition.Name + ".Client.Oqtane";
moduleDefinition.ServerManagerType = moduleDefinition.Owner + ".Module." + moduleDefinition.Name + ".Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + ".Module." + moduleDefinition.Name + ".Server.Oqtane";
moduleDefinition.ModuleDefinitionName = moduleDefinition.ModuleDefinitionName + ", " + moduleDefinition.ModuleDefinitionName + ".Client.Oqtane";
moduleDefinition.ServerManagerType = moduleDefinition.ModuleDefinitionName + ".Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.ModuleDefinitionName + ".Server.Oqtane";
}
ProcessTemplatesRecursively(new DirectoryInfo(templatePath), rootPath, rootFolder.Name, templatePath, moduleDefinition);
@ -301,7 +310,7 @@ namespace Oqtane.Controllers
}
else
{
templates.Add(new Template { Name = name, Title = name, Type = "External", Version = "", Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString()) });
templates.Add(new Template { Name = name, Title = name, Type = "External", Version = "", Namespace = "", Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString()) });
}
}
}

View File

@ -38,13 +38,33 @@ namespace Oqtane.Controllers
{
// get packages
List<Package> packages = new List<Package>();
if (bool.Parse(_configManager.GetSetting("PackageService", "true")) == true)
var url = _configManager.GetSetting("PackageRegistryUrl", Constants.PackageRegistryUrl);
if (!string.IsNullOrEmpty(url))
{
using (var client = new HttpClient())
{
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}&price={price}&package={package}&sort={sort}");
packages = await GetJson<List<Package>>(client, url + $"/api/registry/packages/?id={_configManager.GetInstallationId()}&type={type.ToLower()}&version={Constants.Version}&search={search}&price={price}&package={package}&sort={sort}");
}
}
return packages;
}
// GET: api/<controller>/list/?names=x,y,z
[HttpGet("list")]
public async Task<IEnumerable<Package>> GetPackages(string names)
{
// get packages
List<Package> packages = new List<Package>();
var url = _configManager.GetSetting("PackageRegistryUrl", Constants.PackageRegistryUrl);
if (!string.IsNullOrEmpty(url) && !string.IsNullOrEmpty(names))
{
using (var client = new HttpClient())
{
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, url + $"/api/registry/list/?id={_configManager.GetInstallationId()}&version={Constants.Version}&list={names}");
}
}
return packages;
@ -56,14 +76,15 @@ namespace Oqtane.Controllers
{
// get package info
Package package = null;
if (bool.Parse(_configManager.GetSetting("PackageService", "true")) == true)
var url = _configManager.GetSetting("PackageRegistryUrl", Constants.PackageRegistryUrl);
if (!string.IsNullOrEmpty(url))
{
var download = (string.IsNullOrEmpty(folder)) ? "false" : "true";
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Referer", HttpContext.Request.Scheme + "://" + HttpContext.Request.Host.Value);
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(Constants.PackageId, Constants.Version));
package = await GetJson<Package>(client, Constants.PackageRegistryUrl + $"/api/registry/package/?id={_configManager.GetInstallationId()}&package={packageid}&version={version}&download={download}");
package = await GetJson<Package>(client, url + $"/api/registry/package/?id={_configManager.GetInstallationId()}&package={packageid}&version={version}&download={download}");
}
if (package != null)

View File

@ -91,7 +91,7 @@ namespace Oqtane.Controllers
site.Pages = new List<Page>();
foreach (Page page in _pages.GetPages(site.SiteId))
{
if (_userPermissions.IsAuthorized(User, PermissionNames.View, page.PermissionList))
if (!page.IsDeleted && _userPermissions.IsAuthorized(User, PermissionNames.View, page.PermissionList))
{
page.Settings = settings.Where(item => item.EntityId == page.PageId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.PermissionList))
@ -107,7 +107,7 @@ namespace Oqtane.Controllers
site.Modules = new List<Module>();
foreach (PageModule pagemodule in _pageModules.GetPageModules(site.SiteId))
{
if (_userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.PermissionList))
if (!pagemodule.IsDeleted && _userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.PermissionList))
{
Module module = new Module();
module.SiteId = pagemodule.Module.SiteId;

View File

@ -52,7 +52,7 @@ namespace Oqtane.Controllers
systeminfo.Add("Logging:LogLevel:Default", _configManager.GetSetting("Logging:LogLevel:Default", "Information"));
systeminfo.Add("Logging:LogLevel:Notify", _configManager.GetSetting("Logging:LogLevel:Notify", "Error"));
systeminfo.Add("UseSwagger", _configManager.GetSetting("UseSwagger", "true"));
systeminfo.Add("PackageService", _configManager.GetSetting("PackageService", "true"));
systeminfo.Add("PackageRegistryUrl", _configManager.GetSetting("PackageRegistryUrl", Constants.PackageRegistryUrl));
break;
case "log":
string log = "";
@ -74,7 +74,6 @@ namespace Oqtane.Controllers
return systeminfo;
}
// GET: api/<controller>
[HttpGet("{key}/{value}")]
[Authorize(Roles = RoleNames.Host)]
@ -94,6 +93,26 @@ namespace Oqtane.Controllers
}
}
// GET: api/<controller>/icons
[HttpGet("icons")]
public Dictionary<string, string> Get()
{
var icons = new Dictionary<string, string>();
// use reflection to get list of icons from Icons class
Type iconsType = typeof(Icons);
System.Reflection.FieldInfo[] fields = iconsType.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.GetField);
foreach (System.Reflection.FieldInfo field in fields)
{
if (field.FieldType == typeof(string))
{
icons.Add((string)field.GetValue(null), field.Name); // ie. ("oi oi-home", "Home")
}
}
return icons;
}
private void UpdateSetting(string key, object value)
{
switch (key.ToLower())

View File

@ -173,15 +173,24 @@ namespace Oqtane.Controllers
DirectoryInfo rootFolder = Directory.GetParent(_environment.ContentRootPath);
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Themes", "Templates", theme.Template, Path.DirectorySeparatorChar.ToString());
if (!string.IsNullOrEmpty(theme.ThemeName))
{
theme.ThemeName = theme.ThemeName.Replace("[Owner]", theme.Owner).Replace("[Theme]", theme.Name);
}
else
{
theme.ThemeName = theme.Owner + ".Theme." + theme.Name;
}
if (theme.Template.ToLower().Contains("internal"))
{
rootPath = Utilities.PathCombine(rootFolder.FullName, Path.DirectorySeparatorChar.ToString());
theme.ThemeName = theme.Owner + ".Theme." + theme.Name + ", Oqtane.Client";
theme.ThemeName = theme.ThemeName + ", Oqtane.Client";
}
else
{
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName, theme.Owner + ".Theme." + theme.Name, Path.DirectorySeparatorChar.ToString());
theme.ThemeName = theme.Owner + ".Theme." + theme.Name + ", " + theme.Owner + ".Theme." + theme.Name + ".Client.Oqtane";
theme.ThemeName = theme.ThemeName + ", " + theme.ThemeName + ".Client.Oqtane";
}
ProcessTemplatesRecursively(new DirectoryInfo(templatePath), rootPath, rootFolder.Name, templatePath, theme);

View File

@ -14,6 +14,7 @@ using Oqtane.Repository;
using Oqtane.Security;
using Oqtane.Extensions;
using Oqtane.Managers;
using System.Collections.Generic;
namespace Oqtane.Controllers
{
@ -61,13 +62,15 @@ namespace Oqtane.Controllers
}
}
// GET api/<controller>/name/x?siteid=x
[HttpGet("name/{name}")]
public User Get(string name, string siteid)
// GET api/<controller>/name/{name}/{email}?siteid=x
[HttpGet("name/{name}/{email}")]
public User Get(string name, string email, string siteid)
{
if (int.TryParse(siteid, out int SiteId) && SiteId == _tenantManager.GetAlias().SiteId)
{
User user = _userManager.GetUser(name, SiteId);
name = (name == "-") ? "" : name;
email = (email == "-") ? "" : email;
User user = _userManager.GetUser(name, email, SiteId);
if (user == null)
{
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
@ -76,7 +79,7 @@ namespace Oqtane.Controllers
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Get Attempt {Username} {SiteId}", name, siteid);
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Get Attempt {Username} {Email} {SiteId}", name, email, siteid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
@ -334,5 +337,23 @@ namespace Oqtane.Controllers
}
return user;
}
// GET api/<controller>/passwordrequirements/5
[HttpGet("passwordrequirements/{siteid}")]
public Dictionary<string, string> PasswordRequirements(int siteid)
{
var requirements = new Dictionary<string, string>();
var site = _sites.GetSite(siteid);
if (site != null && (site.AllowRegistration || User.IsInRole(RoleNames.Registered)))
{
// get password settings
var sitesettings = HttpContext.GetSiteSettings();
requirements = sitesettings.Where(item => item.Key.StartsWith("IdentityOptions:Password:"))
.ToDictionary(item => item.Key, item => item.Value);
}
return requirements;
}
}
}

View File

@ -23,9 +23,6 @@ namespace Oqtane.Infrastructure
_serviceScopeFactory = serviceScopeFactory;
}
// abstract method must be overridden
public abstract string ExecuteJob(IServiceProvider provider);
// public properties which can be overridden and are used during auto registration of job
public string Name { get; set; } = "";
public string Frequency { get; set; } = "d"; // day
@ -35,6 +32,17 @@ namespace Oqtane.Infrastructure
public int RetentionHistory { get; set; } = 10;
public bool IsEnabled { get; set; } = false;
// one of the following methods must be overridden
public virtual string ExecuteJob(IServiceProvider provider)
{
return "";
}
public virtual Task<string> ExecuteJobAsync(IServiceProvider provider)
{
return Task.FromResult(string.Empty);
}
protected async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.Yield(); // required so that this method does not block startup
@ -102,6 +110,7 @@ namespace Oqtane.Infrastructure
// set tenant and execute job
tenantManager.SetTenant(tenant.TenantId);
notes += ExecuteJob(scope.ServiceProvider);
notes += await ExecuteJobAsync(scope.ServiceProvider);
}
log.Notes = notes;
log.Succeeded = true;

Some files were not shown because too many files have changed in this diff Show More